assert_util.h | assert_util.h | |||
---|---|---|---|---|
skipping to change at line 90 | skipping to change at line 90 | |||
}; | }; | |||
extern AssertionCount assertionCount; | extern AssertionCount assertionCount; | |||
class DBException : public std::exception { | class DBException : public std::exception { | |||
public: | public: | |||
virtual const char* what() const throw() = 0; | virtual const char* what() const throw() = 0; | |||
virtual string toString() const { | virtual string toString() const { | |||
return what(); | return what(); | |||
} | } | |||
virtual int getCode() = 0; | virtual int getCode() const = 0; | |||
operator string() const { return toString(); } | operator string() const { stringstream ss; ss << getCode() << " " < | |||
< what(); return ss.str(); } | ||||
}; | }; | |||
class AssertionException : public DBException { | class AssertionException : public DBException { | |||
public: | public: | |||
int code; | int code; | |||
string msg; | string msg; | |||
AssertionException() { code = 0; } | AssertionException() { code = 0; } | |||
virtual ~AssertionException() throw() { } | virtual ~AssertionException() throw() { } | |||
virtual bool severe() { | virtual bool severe() { | |||
return true; | return true; | |||
} | } | |||
virtual bool isUserAssertion() { | virtual bool isUserAssertion() { | |||
return false; | return false; | |||
} | } | |||
virtual int getCode(){ return code; } | virtual int getCode() const { return code; } | |||
virtual const char* what() const throw() { return msg.c_str(); } | virtual const char* what() const throw() { return msg.c_str(); } | |||
/* true if an interrupted exception - see KillCurrentOp */ | /* true if an interrupted exception - see KillCurrentOp */ | |||
bool interrupted() { | bool interrupted() { | |||
return code == 11600 || code == 11601; | return code == 11600 || 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 { | |||
skipping to change at line 168 | skipping to change at line 168 | |||
inline void msgasserted(int msgid, string msg) { msgasserted(msgid, msg .c_str()); } | inline void msgasserted(int msgid, string msg) { msgasserted(msgid, msg .c_str()); } | |||
#ifdef assert | #ifdef assert | |||
#undef assert | #undef assert | |||
#endif | #endif | |||
#define MONGO_assert(_Expression) (void)( (!!(_Expression)) || (mongo::asse rted(#_Expression, __FILE__, __LINE__), 0) ) | #define MONGO_assert(_Expression) (void)( (!!(_Expression)) || (mongo::asse rted(#_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 * / | |||
inline void uassert(unsigned msgid, string msg, bool expr) { | #define MONGO_uassert(msgid, msg, expr) (void)( (!!(expr)) || (mongo::uasse | |||
if( !expr ) uasserted(msgid, msg.c_str()); | rted(msgid, msg), 0) ) | |||
} | #define uassert MONGO_uassert | |||
template<class T> | ||||
inline void uassert(unsigned msgid, string msg, const T&t , bool expr) | ||||
{ | ||||
if( !expr ){ | ||||
stringstream ss; | ||||
ss << msg << " " << t.toString(); | ||||
uasserted(msgid, ss.str()); | ||||
} | ||||
} | ||||
inline void uassert(unsigned msgid, const char * msg, bool expr) { | ||||
if( !expr ) uasserted(msgid, msg); | ||||
} | ||||
/* 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)( (!!(_Expression)) || (mongo::was serted(#_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. | |||
*/ | */ | |||
inline void massert(unsigned msgid, string msg, bool expr) { | #define MONGO_massert(msgid, msg, expr) (void)( (!!(expr)) || (mongo::msgas | |||
if( !expr) msgasserted(msgid, msg.c_str()); | serted(msgid, msg), 0) ) | |||
} | #define massert MONGO_massert | |||
inline void massert(unsigned msgid, const char * msg, bool expr) { | ||||
if( !expr) msgasserted(msgid, msg); | ||||
} | ||||
/* 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 | |||
End of changes. 4 change blocks. | ||||
26 lines changed or deleted | 10 lines changed or added | |||
balance.h | balance.h | |||
---|---|---|---|---|
skipping to change at line 29 | skipping to change at line 29 | |||
#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" | |||
namespace mongo { | namespace mongo { | |||
class Balancer : public BackgroundJob { | class Balancer : public BackgroundJob { | |||
public: | public: | |||
string name() { return "Balancer"; } | ||||
Balancer(); | Balancer(); | |||
void run(); | void run(); | |||
private: | private: | |||
bool shouldIBalance( DBClientBase& conn ); | bool shouldIBalance( DBClientBase& conn ); | |||
void balance( DBClientBase& conn ); | ||||
void balance( DBClientBase& conn , const string& ns , const BSONObj | /** | |||
& data ); | * @return true if everything is ok | |||
*/ | ||||
bool checkOIDs(); | ||||
/** | ||||
* @return number of collections balanced | ||||
*/ | ||||
int balance( DBClientBase& conn ); | ||||
bool balance( DBClientBase& conn , const string& ns , const BSONObj | ||||
& data ); | ||||
void ping(); | void ping(); | |||
void ping( DBClientBase& conn ); | void ping( DBClientBase& conn ); | |||
BSONObj pickChunk( vector<BSONObj>& from, vector<BSONObj>& to ); | BSONObj pickChunk( vector<BSONObj>& from, vector<BSONObj>& to ); | |||
string _myid; | string _myid; | |||
time_t _started; | time_t _started; | |||
int _balancedLastTime; | ||||
}; | }; | |||
extern Balancer balancer; | extern Balancer balancer; | |||
} | } | |||
End of changes. 3 change blocks. | ||||
3 lines changed or deleted | 14 lines changed or added | |||
bson.h | bson.h | |||
---|---|---|---|---|
/* NOTE: This file is not quite ready to use yet. Work in progress. | /* NOTE: Standalone bson header for when not using MongoDB. | |||
include ../db/jsobj.h for now. | See also: bsondemo. | |||
This file, however, pulls in much less code. | MongoDB includes ../db/jsobj.h instead. This file, however, pulls in muc h less code / dependencies. | |||
*/ | */ | |||
/** @file bson.h | /** @file bson.h | |||
BSON classes | 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"); | |||
skipping to change at line 40 | skipping to change at line 40 | |||
"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 | |||
#include <iostream> | #include <iostream> | |||
#include <sstream> | #include <sstream> | |||
#include <boost/utility.hpp> | ||||
namespace bson { | namespace bson { | |||
class assertion : public std::exception { | class assertion : public std::exception { | |||
public: | public: | |||
virtual const char* what() const throw() { return "BsonAssertion"; } | virtual const char* what() const throw() { return "BsonAssertion"; } | |||
}; | }; | |||
} | } | |||
namespace mongo { | namespace mongo { | |||
#if !defined(assert) | #if !defined(assert) | |||
skipping to change at line 62 | skipping to change at line 63 | |||
std::cout << "assertion failure in bson library" << std::endl; | std::cout << "assertion failure in bson library" << std::endl; | |||
throw bson::assertion(); | throw bson::assertion(); | |||
} | } | |||
} | } | |||
#endif | #endif | |||
#if !defined(uassert) | #if !defined(uassert) | |||
inline void uassert(unsigned msgid, std::string msg, bool expr) { | inline void uassert(unsigned msgid, std::string msg, bool expr) { | |||
if( !expr ) | if( !expr ) | |||
throw bson::assertion(); | throw bson::assertion(); | |||
} | } | |||
inline void msgasserted(int msgid, const char *msg) { | ||||
throw bson::assertion(); | ||||
} | ||||
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(); | throw bson::assertion(); | |||
} | } | |||
} | } | |||
#endif | #endif | |||
} | } | |||
#include "../bson/bsontypes.h" | #include "../bson/bsontypes.h" | |||
End of changes. 4 change blocks. | ||||
3 lines changed or deleted | 7 lines changed or added | |||
bsonelement.h | bsonelement.h | |||
---|---|---|---|---|
skipping to change at line 343 | skipping to change at line 343 | |||
} | } | |||
bool operator<( const BSONElement& other ) const { | bool operator<( const BSONElement& other ) const { | |||
int x = (int)canonicalType() - (int)other.canonicalType(); | int x = (int)canonicalType() - (int)other.canonicalType(); | |||
if ( x < 0 ) return true; | if ( x < 0 ) return true; | |||
else if ( x > 0 ) return false; | else if ( x > 0 ) return false; | |||
return compareElementValues(*this,other) < 0; | return compareElementValues(*this,other) < 0; | |||
} | } | |||
// If maxLen is specified, don't scan more than maxLen bytes. | // If maxLen is specified, don't scan more than maxLen bytes. | |||
BSONElement(const char *d, int maxLen = -1) : data(d) { | explicit BSONElement(const char *d, int maxLen = -1) : data(d) { | |||
fieldNameSize_ = -1; | fieldNameSize_ = -1; | |||
if ( eoo() ) | if ( eoo() ) | |||
fieldNameSize_ = 0; | fieldNameSize_ = 0; | |||
else { | else { | |||
if ( maxLen != -1 ) { | if ( maxLen != -1 ) { | |||
int size = (int) strnlen( fieldName(), maxLen - 1 ); | int size = (int) strnlen( fieldName(), maxLen - 1 ); | |||
massert( 10333 , "Invalid field name", size != -1 ); | massert( 10333 , "Invalid field name", size != -1 ); | |||
fieldNameSize_ = size + 1; | fieldNameSize_ = size + 1; | |||
} | } | |||
} | } | |||
End of changes. 1 change blocks. | ||||
1 lines changed or deleted | 1 lines changed or added | |||
bsoninlines.h | bsoninlines.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 <map> | #include <map> | |||
#include "util/atomic_int.h" | #include "util/atomic_int.h" | |||
namespace mongo { | namespace mongo { | |||
inline BSONObjIterator BSONObj::begin() { | ||||
return BSONObjIterator(*this); | ||||
} | ||||
inline BSONObj BSONElement::embeddedObjectUserCheck() const { | inline BSONObj BSONElement::embeddedObjectUserCheck() const { | |||
uassert( 10065 , "invalid parameter: expected an object", isABSONO bj() ); | uassert( 10065 , "invalid parameter: expected an object", isABSONO bj() ); | |||
return BSONObj(value()); | return BSONObj(value()); | |||
} | } | |||
inline BSONObj BSONElement::embeddedObject() const { | inline BSONObj BSONElement::embeddedObject() const { | |||
assert( isABSONObj() ); | assert( isABSONObj() ); | |||
return BSONObj(value()); | return BSONObj(value()); | |||
} | } | |||
End of changes. 1 change blocks. | ||||
0 lines changed or deleted | 4 lines changed or added | |||
bsonmisc.h | bsonmisc.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/utility.hpp> | ||||
namespace mongo { | namespace mongo { | |||
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; | |||
} | } | |||
}; | }; | |||
End of changes. 1 change blocks. | ||||
2 lines changed or deleted | 0 lines changed or added | |||
bsonobj.h | bsonobj.h | |||
---|---|---|---|---|
skipping to change at line 304 | skipping to change at line 304 | |||
/** 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 ){ | bool operator==( const BSONObj& other ) const{ | |||
return woCompare( other ) == 0; | 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] } } | |||
skipping to change at line 349 | skipping to change at line 349 | |||
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; | |||
private: | ||||
friend class BSONObjIterator; | friend class BSONObjIterator; | |||
typedef BSONObjIterator iterator; | ||||
BSONObjIterator begin(); | ||||
private: | ||||
class Holder { | class Holder { | |||
public: | public: | |||
Holder( const char *objdata ) : | Holder( const char *objdata ) : | |||
_objdata( objdata ) { | _objdata( objdata ) { | |||
} | } | |||
~Holder() { | ~Holder() { | |||
free((void *)_objdata); | free((void *)_objdata); | |||
_objdata = 0; | _objdata = 0; | |||
} | } | |||
private: | private: | |||
const char *_objdata; | const char *_objdata; | |||
}; | }; | |||
const char *_objdata; | const char *_objdata; | |||
boost::shared_ptr< Holder > _holder; | boost::shared_ptr< Holder > _holder; | |||
void init(const char *data, bool ifree) { | void init(const char *data, bool ifree) { | |||
if ( ifree ) | if ( ifree ) | |||
_holder.reset( new Holder( data ) ); | _holder.reset( new Holder( data ) ); | |||
_objdata = data; | _objdata = data; | |||
if ( ! isValid() ){ | if ( ! isValid() ){ | |||
stringstream ss; | stringstream ss; | |||
ss << "Invalid BSONObj spec size: " << objsize(); | ss << "Invalid BSONObj spec size: " << objsize() << " (" << hex << objsize() << dec << ")"; | |||
try { | try { | |||
BSONElement e = firstElement(); | BSONElement e = firstElement(); | |||
ss << " first element:" << e.toString() << " "; | ss << " first element:" << e.toString() << " "; | |||
} | } | |||
catch ( ... ){} | catch ( ... ){} | |||
string s = ss.str(); | string s = ss.str(); | |||
massert( 10334 , s , 0 ); | massert( 10334 , s , 0 ); | |||
} | } | |||
} | } | |||
/* | ||||
#pragma pack(1) | ||||
static struct EmptyObject { | ||||
EmptyObject() { | ||||
len = 5; | ||||
jstype = EOO; | ||||
} | ||||
int len; | ||||
char jstype; | ||||
} emptyObject; | ||||
#pragma pack() | ||||
*/ | ||||
}; | }; | |||
ostream& operator<<( ostream &s, const BSONObj &o ); | ostream& operator<<( ostream &s, const BSONObj &o ); | |||
ostream& operator<<( ostream &s, const BSONElement &e ); | ostream& operator<<( ostream &s, const BSONElement &e ); | |||
struct BSONArray : BSONObj { | struct BSONArray : BSONObj { | |||
// Don't add anything other than forwarding constructors!!! | // Don't add anything other than forwarding constructors!!! | |||
BSONArray(): BSONObj() {} | BSONArray(): BSONObj() {} | |||
explicit BSONArray(const BSONObj& obj): BSONObj(obj) {} | explicit BSONArray(const BSONObj& obj): BSONObj(obj) {} | |||
}; | }; | |||
End of changes. 5 change blocks. | ||||
15 lines changed or deleted | 6 lines changed or added | |||
bsonobjbuilder.h | bsonobjbuilder.h | |||
---|---|---|---|---|
skipping to change at line 26 | skipping to change at line 26 | |||
* 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 <limits> | #include <limits> | |||
#include <cmath> | ||||
using namespace std; | ||||
namespace mongo { | namespace mongo { | |||
#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 | |||
/** 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. | |||
skipping to change at line 102 | skipping to change at line 104 | |||
the subobject's body */ | the subobject's body */ | |||
BufBuilder &subobjStart(const char *fieldName) { | BufBuilder &subobjStart(const char *fieldName) { | |||
_b.append((char) Object); | _b.append((char) Object); | |||
_b.append(fieldName); | _b.append(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 char *fieldName, BSONObj subObj) { | BSONObjBuilder& appendArray(const char *fieldName, const BSONObj &s ubObj) { | |||
_b.append((char) Array); | _b.append((char) Array); | |||
_b.append(fieldName); | _b.append(fieldName); | |||
_b.append((void *) subObj.objdata(), subObj.objsize()); | _b.append((void *) subObj.objdata(), subObj.objsize()); | |||
return *this; | return *this; | |||
} | } | |||
BSONObjBuilder& append(const char *fieldName, BSONArray arr) { | BSONObjBuilder& append(const char *fieldName, BSONArray arr) { | |||
return appendArray(fieldName, arr); | return appendArray(fieldName, arr); | |||
} | } | |||
/** add header for a new subarray and return bufbuilder for writing to | /** add header for a new subarray and return bufbuilder for writing to | |||
skipping to change at line 441 | skipping to change at line 443 | |||
} | } | |||
/** | /** | |||
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 string& field , int type ); | void appendMinForType( const string& field , int type ); | |||
void appendMaxForType( const string& field , int type ); | void appendMaxForType( const string& field , int type ); | |||
/** Append an array of values. */ | /** Append an array of values. */ | |||
template < class T > | template < class T > | |||
BSONObjBuilder& append( const char *fieldName, const vector< T >& v | BSONObjBuilder& append( const char *fieldName, const vector< T >& v | |||
als ) { | als ); | |||
BSONObjBuilder arrBuilder; | ||||
for ( unsigned int i = 0; i < vals.size(); ++i ) | ||||
arrBuilder.append( numStr( i ).c_str(), vals[ i ] ); | ||||
marshalArray( fieldName, arrBuilder.done() ); | ||||
return *this; | ||||
} | ||||
/* Append an array of ints | template < class T > | |||
void appendArray( const char *fieldName, const vector< int >& va | BSONObjBuilder& append( const char *fieldName, const list< T >& val | |||
ls ) { | s ); | |||
BSONObjBuilder arrBuilder; | ||||
for ( unsigned i = 0; i < vals.size(); ++i ) | ||||
arrBuilder.append( numStr( i ).c_str(), vals[ i ] ); | ||||
marshalArray( fieldName, arrBuilder.done() ); | ||||
}*/ | ||||
/** The returned BSONObj will free the buffer when it is finished. */ | /** The returned BSONObj will free the buffer when it is finished. */ | |||
BSONObj obj() { | BSONObj obj() { | |||
bool own = owned(); | bool own = owned(); | |||
massert( 10335 , "builder does not own memory", own ); | massert( 10335 , "builder does not own memory", own ); | |||
int l; | int l; | |||
return BSONObj(decouple(l), true); | return BSONObj(decouple(l), true); | |||
} | } | |||
/** Fetch the object we have built. | /** Fetch the object we have built. | |||
skipping to change at line 539 | skipping to change at line 529 | |||
massert( 10336 , "No subobject started", _s.subobjStarted() ); | massert( 10336 , "No subobject started", _s.subobjStarted() ); | |||
return _s << l; | return _s << l; | |||
} | } | |||
/** @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 ; | |||
private: | private: | |||
// Append the provided arr object as an array. | ||||
void marshalArray( const char *fieldName, const BSONObj &arr ) { | ||||
_b.append( (char) Array ); | ||||
_b.append( fieldName ); | ||||
_b.append( (void *) arr.objdata(), arr.objsize() ); | ||||
} | ||||
char* _done() { | char* _done() { | |||
_s.endField(); | _s.endField(); | |||
_b.append((char) EOO); | _b.append((char) EOO); | |||
char *data = _b.buf() + _offset; | char *data = _b.buf() + _offset; | |||
int size = _b.len() - _offset; | int size = _b.len() - _offset; | |||
*((int*)data) = size; | *((int*)data) = size; | |||
if ( _tracker ) | if ( _tracker ) | |||
_tracker->got( size ); | _tracker->got( size ); | |||
return data; | return data; | |||
} | } | |||
skipping to change at line 602 | skipping to change at line 585 | |||
BSONObj done() { return _b.done(); } | BSONObj done() { return _b.done(); } | |||
template <typename T> | template <typename T> | |||
BSONArrayBuilder& append(const char *name, const T& x){ | BSONArrayBuilder& append(const char *name, const T& x){ | |||
fill( name ); | fill( name ); | |||
append( x ); | append( x ); | |||
return *this; | return *this; | |||
} | } | |||
BufBuilder &subobjStart( const char *name ) { | BufBuilder &subobjStart( const char *name = "0" ) { | |||
fill( name ); | fill( name ); | |||
return _b.subobjStart( num().c_str() ); | return _b.subobjStart( num().c_str() ); | |||
} | } | |||
BufBuilder &subarrayStart( const char *name ) { | BufBuilder &subarrayStart( const char *name ) { | |||
fill( name ); | fill( name ); | |||
return _b.subarrayStart( num().c_str() ); | return _b.subarrayStart( num().c_str() ); | |||
} | } | |||
void appendArray( const char *name, BSONObj subObj ) { | void appendArray( const char *name, BSONObj subObj ) { | |||
skipping to change at line 647 | skipping to change at line 630 | |||
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 > | ||||
inline BSONObjBuilder& BSONObjBuilder::append( const char *fieldName, c | ||||
onst vector< T >& vals ) { | ||||
BSONObjBuilder arrBuilder; | ||||
for ( unsigned int i = 0; i < vals.size(); ++i ) | ||||
arrBuilder.append( numStr( i ).c_str(), vals[ i ] ); | ||||
appendArray( fieldName, arrBuilder.done() ); | ||||
return *this; | ||||
} | ||||
template < class T > | ||||
inline BSONObjBuilder& BSONObjBuilder::append( const char *fieldName, c | ||||
onst list< T >& vals ) { | ||||
BSONObjBuilder arrBuilder; | ||||
int n = 0; | ||||
for( typename list< T >::const_iterator i = vals.begin(); i != vals | ||||
.end(); i++ ) | ||||
arrBuilder.append( numStr(n++).c_str(), *i ); | ||||
appendArray( fieldName, arrBuilder.done() ); | ||||
return *this; | ||||
} | ||||
} | } | |||
End of changes. 7 change blocks. | ||||
25 lines changed or deleted | 31 lines changed or added | |||
bsonobjiterator.h | bsonobjiterator.h | |||
---|---|---|---|---|
skipping to change at line 63 | skipping to change at line 63 | |||
bool more(){ | bool more(){ | |||
return _pos < _theend && _pos[0]; | return _pos < _theend && _pos[0]; | |||
} | } | |||
/** @return the next element in the object. For the final element, element.eoo() will be true. */ | /** @return the next element in the object. For the final element, element.eoo() will be true. */ | |||
BSONElement next( bool checkEnd = false ) { | BSONElement next( bool checkEnd = false ) { | |||
assert( _pos < _theend ); | assert( _pos < _theend ); | |||
BSONElement e( _pos, checkEnd ? (int)(_theend - _pos) : -1 ); | BSONElement e( _pos, checkEnd ? (int)(_theend - _pos) : -1 ); | |||
_pos += e.size( checkEnd ? (int)(_theend - _pos) : -1 ); | _pos += e.size( checkEnd ? (int)(_theend - _pos) : -1 ); | |||
return e; | return e; | |||
} | } | |||
void operator++() { next(); } | ||||
void operator++(int) { next(); } | ||||
BSONElement operator*() { | ||||
assert( _pos < _theend ); | ||||
return BSONElement(_pos, -1); | ||||
} | ||||
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(){ | |||
End of changes. 1 change blocks. | ||||
0 lines changed or deleted | 9 lines changed or added | |||
btree.h | btree.h | |||
---|---|---|---|---|
skipping to change at line 190 | skipping to change at line 187 | |||
int n; // # of keys so far. | int n; // # of keys so far. | |||
int reserved; | int reserved; | |||
const _KeyNode& k(int i) const { | const _KeyNode& k(int i) const { | |||
return ((_KeyNode*)data)[i]; | return ((_KeyNode*)data)[i]; | |||
} | } | |||
_KeyNode& k(int i) { | _KeyNode& k(int i) { | |||
return ((_KeyNode*)data)[i]; | return ((_KeyNode*)data)[i]; | |||
} | } | |||
char data[4]; | char data[4]; | |||
}; | }; | |||
#pragma pack() | ||||
#pragma pack(1) | ||||
class BtreeBucket : public BucketBasics { | class BtreeBucket : public BucketBasics { | |||
friend class BtreeCursor; | friend class BtreeCursor; | |||
public: | public: | |||
void dump(); | void dump(); | |||
/* @return true if key exists in index | /* @return true if key exists in index | |||
order - indicates order of keys in the index. this is basically the index's key pattern, e.g.: | order - indicates order of keys in the index. this is basically the index's key pattern, e.g.: | |||
BSONObj order = ((IndexDetails&)idx).keyPattern(); | BSONObj order = ((IndexDetails&)idx).keyPattern(); | |||
likewise below in bt_insert() etc. | likewise below in bt_insert() etc. | |||
skipping to change at line 265 | skipping to change at line 264 | |||
DiskLoc lchild, DiskLoc rchild, IndexDetails&); | DiskLoc lchild, DiskLoc rchild, IndexDetails&); | |||
int _insert(DiskLoc thisLoc, DiskLoc recordLoc, | int _insert(DiskLoc thisLoc, DiskLoc recordLoc, | |||
const BSONObj& key, const Ordering &order, bool dupsAll owed, | const BSONObj& key, const Ordering &order, bool dupsAll owed, | |||
DiskLoc lChild, DiskLoc rChild, IndexDetails&); | DiskLoc lChild, DiskLoc rChild, IndexDetails&); | |||
bool find(const IndexDetails& idx, const BSONObj& key, DiskLoc reco rdLoc, const Ordering &order, int& pos, bool assertIfDup); | bool find(const IndexDetails& idx, const BSONObj& key, DiskLoc reco rdLoc, const Ordering &order, int& pos, bool assertIfDup); | |||
static void findLargestKey(const DiskLoc& thisLoc, DiskLoc& largest Loc, int& largestKey); | static void findLargestKey(const DiskLoc& thisLoc, DiskLoc& largest Loc, int& largestKey); | |||
public: | public: | |||
// simply builds and returns a dup key error message string | // simply builds and returns a dup key error message string | |||
static string dupKeyError( const IndexDetails& idx , const BSONObj& key ); | static string dupKeyError( const IndexDetails& idx , const BSONObj& key ); | |||
}; | }; | |||
#pragma pack() | ||||
class BtreeCursor : public Cursor { | class BtreeCursor : public Cursor { | |||
public: | public: | |||
BtreeCursor( NamespaceDetails *_d, int _idxNo, const IndexDetails&, const BSONObj &startKey, const BSONObj &endKey, bool endKeyInclusive, int direction ); | BtreeCursor( NamespaceDetails *_d, int _idxNo, const IndexDetails&, const BSONObj &startKey, const BSONObj &endKey, bool endKeyInclusive, int direction ); | |||
BtreeCursor( NamespaceDetails *_d, int _idxNo, const IndexDetails& _id, const BoundList &_bounds, int _direction ); | BtreeCursor( NamespaceDetails *_d, int _idxNo, const IndexDetails& _id, const BoundList &_bounds, int _direction ); | |||
~BtreeCursor(){ | ||||
} | ||||
virtual bool ok() { | virtual bool ok() { | |||
return !bucket.isNull(); | return !bucket.isNull(); | |||
} | } | |||
bool eof() { | bool eof() { | |||
return !ok(); | return !ok(); | |||
} | } | |||
virtual bool advance(); | virtual bool advance(); | |||
virtual void noteLocation(); // updates keyAtKeyOfs... | virtual void noteLocation(); // updates keyAtKeyOfs... | |||
virtual void checkLocation(); | virtual void checkLocation(); | |||
virtual bool supportGetMore() { return true; } | virtual bool supportGetMore() { return true; } | |||
/* 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. | |||
@return true if the loc has not been seen | @return true if the loc has not been seen | |||
*/ | */ | |||
set<DiskLoc> dups; | ||||
virtual bool getsetdup(DiskLoc loc) { | virtual bool getsetdup(DiskLoc loc) { | |||
if( multikey ) { | if( multikey ) { | |||
pair<set<DiskLoc>::iterator, bool> p = dups.insert(loc); | pair<set<DiskLoc>::iterator, bool> p = dups.insert(loc); | |||
return !p.second; | return !p.second; | |||
} | } | |||
return false; | return false; | |||
} | } | |||
_KeyNode& _currKeyNode() { | _KeyNode& _currKeyNode() { | |||
assert( !bucket.isNull() ); | assert( !bucket.isNull() ); | |||
skipping to change at line 345 | skipping to change at line 345 | |||
string s = string("BtreeCursor ") + indexDetails.indexName(); | string s = string("BtreeCursor ") + indexDetails.indexName(); | |||
if ( direction < 0 ) s += " reverse"; | if ( direction < 0 ) s += " reverse"; | |||
if ( bounds_.size() > 1 ) s += " multi"; | if ( bounds_.size() > 1 ) s += " multi"; | |||
return s; | return s; | |||
} | } | |||
BSONObj prettyKey( const BSONObj &key ) const { | BSONObj prettyKey( const BSONObj &key ) const { | |||
return key.replaceFieldNames( indexDetails.keyPattern() ).clien tReadable(); | return key.replaceFieldNames( indexDetails.keyPattern() ).clien tReadable(); | |||
} | } | |||
virtual BSONObj prettyIndexBounds() const { | virtual BSONArray prettyIndexBounds() const { | |||
BSONArrayBuilder ba; | BSONArrayBuilder ba; | |||
if ( bounds_.size() == 0 ) { | if ( bounds_.size() == 0 ) { | |||
ba << BSON_ARRAY( prettyKey( startKey ) << prettyKey( endKe y ) ); | ba << BSON_ARRAY( prettyKey( startKey ) << prettyKey( endKe y ) ); | |||
} else { | } else { | |||
for( BoundList::const_iterator i = bounds_.begin(); i != bo unds_.end(); ++i ) { | for( BoundList::const_iterator i = bounds_.begin(); i != bo unds_.end(); ++i ) { | |||
ba << BSON_ARRAY( prettyKey( i->first ) << prettyKey( i ->second ) ); | ba << BSON_ARRAY( prettyKey( i->first ) << prettyKey( i ->second ) ); | |||
} | } | |||
} | } | |||
return ba.arr(); | return ba.arr(); | |||
} | } | |||
void forgetEndKey() { endKey = BSONObj(); } | void forgetEndKey() { endKey = BSONObj(); } | |||
virtual CoveredIndexMatcher *matcher() const { return _matcher.get( | ||||
); } | ||||
virtual void setMatcher( auto_ptr< CoveredIndexMatcher > matcher ) | ||||
{ | ||||
_matcher = matcher; | ||||
} | ||||
private: | private: | |||
/* Our btrees may (rarely) have "unused" keys when items are delete d. | /* Our btrees may (rarely) have "unused" keys when items are delete d. | |||
Skip past them. | Skip past them. | |||
*/ | */ | |||
void skipUnusedKeys(); | void skipUnusedKeys(); | |||
/* Check if the current key is beyond endKey. */ | /* Check if the current key is beyond endKey. */ | |||
void checkEnd(); | void checkEnd(); | |||
// selective audits on construction | // selective audits on construction | |||
void audit(); | void audit(); | |||
// set initial bucket | // set initial bucket | |||
void init(); | void init(); | |||
// init start / end keys with a new range | // init start / end keys with a new range | |||
void initInterval(); | void initInterval(); | |||
friend class BtreeBucket; | friend class BtreeBucket; | |||
set<DiskLoc> dups; | ||||
NamespaceDetails *d; | NamespaceDetails *d; | |||
int idxNo; | int idxNo; | |||
BSONObj startKey; | BSONObj startKey; | |||
BSONObj endKey; | BSONObj endKey; | |||
bool endKeyInclusive_; | bool endKeyInclusive_; | |||
bool multikey; // note this must be updated every getmore batch in case someone added a multikey... | bool multikey; // note this must be updated every getmore batch in case someone added a multikey... | |||
const IndexDetails& indexDetails; | const IndexDetails& indexDetails; | |||
BSONObj order; | BSONObj order; | |||
DiskLoc bucket; | DiskLoc bucket; | |||
int keyOfs; | int keyOfs; | |||
int direction; // 1=fwd,-1=reverse | int direction; // 1=fwd,-1=reverse | |||
BSONObj keyAtKeyOfs; // so we can tell if things moved around on us between the query and the getMore call | BSONObj keyAtKeyOfs; // so we can tell if things moved around on us between the query and the getMore call | |||
DiskLoc locAtKeyOfs; | DiskLoc locAtKeyOfs; | |||
BoundList bounds_; | BoundList bounds_; | |||
unsigned boundIndex_; | unsigned boundIndex_; | |||
const IndexSpec& _spec; | const IndexSpec& _spec; | |||
auto_ptr< CoveredIndexMatcher > _matcher; | ||||
}; | }; | |||
#pragma pack() | ||||
inline bool IndexDetails::hasKey(const BSONObj& key) { | inline bool IndexDetails::hasKey(const BSONObj& key) { | |||
return head.btree()->exists(*this, head, key, Ordering::make(keyPat tern())); | return head.btree()->exists(*this, head, key, Ordering::make(keyPat tern())); | |||
} | } | |||
inline bool IndexDetails::wouldCreateDup(const BSONObj& key, DiskLoc se lf) { | inline bool IndexDetails::wouldCreateDup(const BSONObj& key, DiskLoc se lf) { | |||
return head.btree()->wouldCreateDup(*this, head, key, Ordering::mak e(keyPattern()), self); | return head.btree()->wouldCreateDup(*this, head, key, Ordering::mak e(keyPattern()), self); | |||
} | } | |||
/* build btree from the bottom up */ | /* build btree from the bottom up */ | |||
/* _ TODO dropDups */ | /* _ TODO dropDups */ | |||
class BtreeBuilder { | class BtreeBuilder { | |||
End of changes. 10 change blocks. | ||||
5 lines changed or deleted | 16 lines changed or added | |||
builder.h | builder.h | |||
---|---|---|---|---|
skipping to change at line 29 | skipping to change at line 29 | |||
#include <string> | #include <string> | |||
#include <string.h> | #include <string.h> | |||
#include <stdio.h> | #include <stdio.h> | |||
#include <boost/shared_ptr.hpp> | #include <boost/shared_ptr.hpp> | |||
namespace mongo { | namespace mongo { | |||
class StringBuilder; | class StringBuilder; | |||
void msgasserted(int msgid, const char *msg); | ||||
class BufBuilder { | class BufBuilder { | |||
public: | public: | |||
BufBuilder(int initsize = 512) : size(initsize) { | BufBuilder(int initsize = 512) : size(initsize) { | |||
if ( size > 0 ) { | if ( size > 0 ) { | |||
data = (char *) malloc(size); | data = (char *) malloc(size); | |||
assert(data != 0); | if( data == 0 ) | |||
msgasserted(10000, "out of memory BufBuilder"); | ||||
} else { | } else { | |||
data = 0; | data = 0; | |||
} | } | |||
l = 0; | l = 0; | |||
} | } | |||
~BufBuilder() { | ~BufBuilder() { | |||
kill(); | kill(); | |||
} | } | |||
void kill() { | void kill() { | |||
skipping to change at line 62 | skipping to change at line 65 | |||
l = 0; | l = 0; | |||
if ( maxSize && size > maxSize ){ | if ( maxSize && size > maxSize ){ | |||
free(data); | free(data); | |||
data = (char*)malloc(maxSize); | data = (char*)malloc(maxSize); | |||
size = maxSize; | size = maxSize; | |||
} | } | |||
} | } | |||
/* leave room for some stuff later */ | /* leave room for some stuff later */ | |||
void skip(int n) { | char* skip(int n) { return grow(n); } | |||
grow(n); | ||||
} | ||||
/* note this may be deallocated (realloced) if you keep writing. */ | /* note this may be deallocated (realloced) if you keep writing. */ | |||
char* buf() { | char* buf() { return data; } | |||
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() { | void decouple() { | |||
data = 0; | data = 0; | |||
} | } | |||
template<class T> void append(T j) { | template<class T> void append(T j) { | |||
*((T*)grow(sizeof(T))) = j; | *((T*)grow(sizeof(T))) = j; | |||
} | } | |||
void append(short j) { | void append(short j) { | |||
skipping to change at line 115 | skipping to change at line 115 | |||
} | } | |||
int len() const { | int len() const { | |||
return l; | return l; | |||
} | } | |||
void setlen( int newLen ){ | void setlen( int newLen ){ | |||
l = newLen; | l = newLen; | |||
} | } | |||
private: | ||||
/* returns the pre-grow write position */ | /* returns the pre-grow write position */ | |||
char* grow(int by) { | char* grow(int by) { | |||
int oldlen = l; | int oldlen = l; | |||
l += by; | l += by; | |||
if ( l > size ) { | if ( l > size ) { | |||
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; | |||
assert( a < 64 * 1024 * 1024 ); | if( a > 64 * 1024 * 1024 ) | |||
msgasserted(10000, "BufBuilder grow() > 64MB"); | ||||
data = (char *) realloc(data, a); | data = (char *) realloc(data, a); | |||
size= a; | size= a; | |||
} | } | |||
return data + oldlen; | return data + oldlen; | |||
} | } | |||
private: | ||||
char *data; | char *data; | |||
int l; | int l; | |||
int size; | int size; | |||
friend class StringBuilder; | friend class StringBuilder; | |||
}; | }; | |||
#if defined(_WIN32) | #if defined(_WIN32) | |||
#pragma warning( disable : 4996 ) | #pragma warning( disable : 4996 ) | |||
#endif | #endif | |||
End of changes. 7 change blocks. | ||||
9 lines changed or deleted | 10 lines changed or added | |||
chunk.h | chunk.h | |||
---|---|---|---|---|
skipping to change at line 32 | skipping to change at line 32 | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../pch.h" | #include "../pch.h" | |||
#include "../client/dbclient.h" | #include "../client/dbclient.h" | |||
#include "../client/model.h" | #include "../client/model.h" | |||
#include "../bson/util/atomic_int.h" | #include "../bson/util/atomic_int.h" | |||
#include "shardkey.h" | #include "shardkey.h" | |||
#include "shard.h" | #include "shard.h" | |||
#include "config.h" | ||||
namespace mongo { | namespace mongo { | |||
class DBConfig; | class DBConfig; | |||
class Chunk; | ||||
class ChunkRange; | ||||
class ChunkManager; | class ChunkManager; | |||
class ChunkRangeMangager; | ||||
class ChunkObjUnitTest; | class ChunkObjUnitTest; | |||
typedef unsigned long long ShardChunkVersion; | typedef unsigned long long ShardChunkVersion; | |||
typedef shared_ptr<Chunk> ChunkPtr; | ||||
// key is max for each Chunk or ChunkRange | ||||
typedef map<BSONObj,ChunkPtr,BSONObjCmp> ChunkMap; | ||||
typedef map<BSONObj,shared_ptr<ChunkRange>,BSONObjCmp> ChunkRangeMap; | ||||
/** | /** | |||
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 : public Model , boost::noncopyable { | class Chunk : public Model , boost::noncopyable { | |||
public: | public: | |||
skipping to change at line 83 | skipping to change at line 92 | |||
bool operator==(const Chunk& s) const; | bool operator==(const Chunk& s) const; | |||
bool operator!=(const Chunk& s) const{ | bool operator!=(const Chunk& s) const{ | |||
return ! ( *this == s ); | return ! ( *this == s ); | |||
} | } | |||
void getFilter( BSONObjBuilder& b ) const; | void getFilter( BSONObjBuilder& b ) const; | |||
BSONObj getFilter() const{ BSONObjBuilder b; getFilter( b ); return b.obj(); } | BSONObj getFilter() const{ BSONObjBuilder b; getFilter( b ); return b.obj(); } | |||
// if min/max key is pos/neg infinity | ||||
bool minIsInf() const; | ||||
bool maxIsInf() const; | ||||
BSONObj pickSplitPoint() const; | BSONObj pickSplitPoint() const; | |||
Chunk * split(); | ChunkPtr split(); | |||
Chunk * split( const BSONObj& middle ); | ChunkPtr split( const BSONObj& middle ); | |||
/** | /** | |||
* @return size of shard in bytes | * @return size of shard in bytes | |||
* talks to mongod to do this | * talks to mongod to do this | |||
*/ | */ | |||
long getPhysicalSize() const; | long getPhysicalSize() const; | |||
long countObjects( const BSONObj& filter = BSONObj() ) const; | long countObjects( const BSONObj& filter = BSONObj() ) const; | |||
/** | /** | |||
* if the amount of data written nears the max size of a shard | * if the amount of data written nears the max size of a shard | |||
* then we check the real size, and if its too big, we split | * then we check the real size, and if its too big, we split | |||
*/ | */ | |||
bool splitIfShould( long dataWritten ); | bool splitIfShould( long dataWritten ); | |||
/* | /* | |||
* moves either this shard or newShard if it makes sense too | * moves either this shard or newShard if it makes sense too | |||
* @return whether or not a shard was moved | * @return whether or not a shard was moved | |||
*/ | */ | |||
bool moveIfShould( Chunk * newShard = 0 ); | bool moveIfShould( ChunkPtr newShard = ChunkPtr() ); | |||
bool moveAndCommit( const Shard& to , string& errmsg ); | bool moveAndCommit( const Shard& to , string& errmsg ); | |||
virtual const char * getNS(){ return "config.chunks"; } | virtual const char * getNS(){ return "config.chunks"; } | |||
virtual void serialize(BSONObjBuilder& to); | virtual void serialize(BSONObjBuilder& to); | |||
virtual void unserialize(const BSONObj& from); | virtual void unserialize(const BSONObj& from); | |||
virtual string modelServer(); | virtual string modelServer(); | |||
void appendShortVersion( const char * name , BSONObjBuilder& b ); | void appendShortVersion( const char * name , BSONObjBuilder& b ); | |||
virtual void save( bool check=false ); | virtual void save( bool check=false ); | |||
void ensureIndex(); | void ensureIndex(); | |||
void _markModified(); | void _markModified(); | |||
static int MaxChunkSize; | static int MaxChunkSize; | |||
static string genID( const string& ns , const BSONObj& min ); | static string genID( const string& ns , const BSONObj& min ); | |||
const ChunkManager* getManager() const { return _manager; } | ||||
private: | private: | |||
// main shard info | // main shard info | |||
ChunkManager * _manager; | ChunkManager * _manager; | |||
ShardKeyPattern skey() const; | ShardKeyPattern skey() const; | |||
string _ns; | string _ns; | |||
BSONObj _min; | BSONObj _min; | |||
BSONObj _max; | BSONObj _max; | |||
Shard _shard; | Shard _shard; | |||
ShardChunkVersion _lastmod; | ShardChunkVersion _lastmod; | |||
bool _modified; | bool _modified; | |||
// transient stuff | // transient stuff | |||
long _dataWritten; | long _dataWritten; | |||
ChunkPtr _this; | ||||
// methods, etc.. | // methods, etc.. | |||
void _split( BSONObj& middle ); | void _split( BSONObj& middle ); | |||
friend class ChunkManager; | friend class ChunkManager; | |||
friend class ShardObjUnitTest; | friend class ShardObjUnitTest; | |||
}; | }; | |||
class ChunkRange{ | ||||
public: | ||||
const ChunkManager* getManager() const{ return _manager; } | ||||
Shard getShard() const{ return _shard; } | ||||
const BSONObj& getMin() const { return _min; } | ||||
const BSONObj& getMax() const { return _max; } | ||||
// clones of Chunk methods | ||||
bool contains(const BSONObj& obj) const; | ||||
void getFilter( BSONObjBuilder& b ) const; | ||||
BSONObj getFilter() const{ BSONObjBuilder b; getFilter( b ); return | ||||
b.obj(); } | ||||
long countObjects( const BSONObj& filter = BSONObj() ) const; | ||||
ChunkRange(ChunkMap::const_iterator begin, const ChunkMap::const_it | ||||
erator end) | ||||
: _manager(begin->second->getManager()) | ||||
, _shard(begin->second->getShard()) | ||||
, _min(begin->second->getMin()) | ||||
, _max(prior(end)->second->getMax()) | ||||
{ | ||||
assert( begin != end ); | ||||
DEV while (begin != end){ | ||||
assert(begin->second->getManager() == _manager); | ||||
assert(begin->second->getShard() == _shard); | ||||
++begin; | ||||
} | ||||
} | ||||
// Merge min and max (must be adjacent ranges) | ||||
ChunkRange(const ChunkRange& min, const ChunkRange& max) | ||||
: _manager(min.getManager()) | ||||
, _shard(min.getShard()) | ||||
, _min(min.getMin()) | ||||
, _max(max.getMax()) | ||||
{ | ||||
assert(min.getShard() == max.getShard()); | ||||
assert(min.getManager() == max.getManager()); | ||||
assert(min.getMax() == max.getMin()); | ||||
} | ||||
friend ostream& operator<<(ostream& out, const ChunkRange& cr){ | ||||
return (out << "ChunkRange(min=" << cr._min << ", max=" << cr._ | ||||
max << ", shard=" << cr._shard <<")"); | ||||
} | ||||
private: | ||||
const ChunkManager* _manager; | ||||
const Shard _shard; | ||||
const BSONObj _min; | ||||
const BSONObj _max; | ||||
}; | ||||
class ChunkRangeManager { | ||||
public: | ||||
const ChunkRangeMap& ranges() const { return _ranges; } | ||||
void clear() { _ranges.clear(); } | ||||
void reloadAll(const ChunkMap& chunks); | ||||
void reloadRange(const ChunkMap& chunks, const BSONObj& min, const | ||||
BSONObj& max); | ||||
// Slow operation -- wrap with DEV | ||||
void assertValid() const; | ||||
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); } | ||||
private: | ||||
// assumes nothing in this range exists in _ranges | ||||
void _insertRange(ChunkMap::const_iterator begin, const ChunkMap::c | ||||
onst_iterator end); | ||||
ChunkRangeMap _ranges; | ||||
}; | ||||
/* 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: | |||
ChunkManager( DBConfig * config , string ns , ShardKeyPattern patte rn , bool unique ); | ChunkManager( DBConfig * config , string ns , ShardKeyPattern patte rn , bool unique ); | |||
virtual ~ChunkManager(); | virtual ~ChunkManager(); | |||
string getns(){ | string getns() const { | |||
return _ns; | return _ns; | |||
} | } | |||
int numChunks(){ rwlock lk( _lock , false ); return _chunks.size(); } | int numChunks(){ rwlock lk( _lock , false ); return _chunks.size(); } | |||
Chunk* getChunk( int i ){ rwlock lk( _lock , false ); return _chunk s[i]; } | ChunkPtr getChunk( int i ){ rwlock lk( _lock , false ); return _chu nks[i]; } | |||
bool hasShardKey( const BSONObj& obj ); | bool hasShardKey( const BSONObj& obj ); | |||
Chunk& findChunk( const BSONObj& obj , bool retry = false ); | ChunkPtr findChunk( const BSONObj& obj , bool retry = false ); | |||
Chunk* findChunkOnServer( const Shard& shard ) const; | ChunkPtr findChunkOnServer( const Shard& shard ) const; | |||
ShardKeyPattern& getShardKey(){ return _key; } | ShardKeyPattern& getShardKey(){ return _key; } | |||
const ShardKeyPattern& getShardKey() const { return _key; } | ||||
bool isUnique(){ return _unique; } | bool isUnique(){ return _unique; } | |||
/** | /** | |||
* makes sure the shard index is on all servers | * makes sure the shard index is on all servers | |||
*/ | */ | |||
void ensureIndex(); | void ensureIndex(); | |||
/** | /** | |||
* @return number of Chunk added to the vector | * @return number of Chunk added to the vector | |||
*/ | */ | |||
int getChunksForQuery( vector<Chunk*>& chunks , const BSONObj& quer y ); | int getChunksForQuery( vector<shared_ptr<ChunkRange> >& chunks , co nst BSONObj& query ); | |||
/** | /** | |||
* @return number of Shards added to the set | * @return number of Shards added to the set | |||
*/ | */ | |||
int getShardsForQuery( set<Shard>& shards , const BSONObj& query ); | int getShardsForQuery( set<Shard>& shards , const BSONObj& query ); | |||
void getAllShards( set<Shard>& all ); | void getAllShards( set<Shard>& all ); | |||
void save(); | void save(); | |||
skipping to change at line 211 | skipping to change at line 303 | |||
ShardChunkVersion getVersion( const Shard& shard ) const; | ShardChunkVersion getVersion( const Shard& shard ) const; | |||
ShardChunkVersion getVersion() const; | ShardChunkVersion getVersion() 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(){ | |||
return _sequenceNumber; | return _sequenceNumber; | |||
} | } | |||
void drop(); | /** | |||
* @param me - so i don't get deleted before i'm done | ||||
*/ | ||||
void drop( ChunkManagerPtr me ); | ||||
private: | private: | |||
void _reload(); | void _reload(); | |||
void _load(); | void _load(); | |||
DBConfig * _config; | DBConfig * _config; | |||
string _ns; | string _ns; | |||
ShardKeyPattern _key; | ShardKeyPattern _key; | |||
bool _unique; | bool _unique; | |||
vector<Chunk*> _chunks; | vector<ChunkPtr> _chunks; | |||
map<string,unsigned long long> _maxMarkers; | map<string,unsigned long long> _maxMarkers; | |||
typedef map<BSONObj,Chunk*,BSONObjCmp> ChunkMap; | ChunkMap _chunkMap; | |||
ChunkMap _chunkMap; // max -> Chunk | ChunkRangeManager _chunkRanges; | |||
unsigned long long _sequenceNumber; | unsigned long long _sequenceNumber; | |||
RWLock _lock; | RWLock _lock; | |||
// This should only be called from Chunk after it has been migrated | ||||
void _migrationNotification(Chunk* c); | ||||
friend class Chunk; | friend class Chunk; | |||
friend class ChunkRangeManager; // only needed for CRM::assertValid () | ||||
static AtomicUInt NextSequenceNumber; | static AtomicUInt NextSequenceNumber; | |||
bool _isValid() const; | ||||
void _printChunks() const; | ||||
/** | /** | |||
* @return number of Chunk matching the query or -1 for all chunks. | * @return number of Chunk matching the query or -1 for all chunks. | |||
*/ | */ | |||
int _getChunksForQuery( vector<Chunk*>& chunks , const BSONObj& que ry ); | int _getChunksForQuery( vector<shared_ptr<ChunkRange> >& chunks , c onst BSONObj& query ); | |||
}; | }; | |||
// 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()); | |||
} | } | |||
bool operator()( const ptr<Chunk> l, const ptr<Chunk> r ) const { | ||||
return operator()(*l, *r); | ||||
} | ||||
bool operator()( const Chunk *l, const Chunk *r ) const { | // Also support ChunkRanges | |||
bool operator()( const ChunkRange &l, const ChunkRange &r ) const { | ||||
return _cmp(l.getMin(), r.getMin()); | ||||
} | ||||
bool operator()( const shared_ptr<ChunkRange> l, const shared_ptr<C | ||||
hunkRange> r ) const { | ||||
return operator()(*l, *r); | return operator()(*l, *r); | |||
} | } | |||
private: | private: | |||
BSONObjCmp _cmp; | BSONObjCmp _cmp; | |||
}; | }; | |||
} // namespace mongo | } // namespace mongo | |||
End of changes. 24 change blocks. | ||||
14 lines changed or deleted | 131 lines changed or added | |||
client.h | client.h | |||
---|---|---|---|---|
skipping to change at line 172 | skipping to change at line 172 | |||
bool _shutdown; | bool _shutdown; | |||
list<string> _tempCollections; | list<string> _tempCollections; | |||
const char *_desc; | const char *_desc; | |||
bool _god; | bool _god; | |||
AuthenticationInfo _ai; | AuthenticationInfo _ai; | |||
OpTime _lastOp; | OpTime _lastOp; | |||
BSONObj _handshake; | BSONObj _handshake; | |||
BSONObj _remoteId; | BSONObj _remoteId; | |||
public: | public: | |||
string clientAddress() const; | ||||
AuthenticationInfo * getAuthenticationInfo(){ return &_ai; } | AuthenticationInfo * getAuthenticationInfo(){ return &_ai; } | |||
bool isAdmin() { return _ai.isAuthorized( "admin" ); } | bool isAdmin() { return _ai.isAuthorized( "admin" ); } | |||
CurOp* curop() { return _curOp; } | CurOp* curop() { return _curOp; } | |||
Context* getContext(){ return _context; } | Context* getContext(){ return _context; } | |||
Database* database() { return _context ? _context->db() : 0; } | Database* database() { return _context ? _context->db() : 0; } | |||
const char *ns() { return _context->ns(); } | const char *ns() const { return _context->ns(); } | |||
const char *desc() const { return _desc; } | ||||
Client(const char *desc); | Client(const char *desc); | |||
~Client(); | ~Client(); | |||
const char *desc() const { return _desc; } | void addTempCollection( const string& ns ) { _tempCollections.push_ | |||
back( ns ); } | ||||
void addTempCollection( const string& ns ){ | void dropTempCollectionsInDB(const string db); | |||
_tempCollections.push_back( ns ); | void dropAllTempCollectionsInDB(const string db); | |||
} | ||||
void setLastOp( const OpTime& op ){ | void setLastOp( const OpTime& op ){ | |||
_lastOp = op; | _lastOp = op; | |||
} | } | |||
OpTime getLastOp() const { | OpTime getLastOp() const { | |||
return _lastOp; | return _lastOp; | |||
} | } | |||
void appendLastOp( BSONObjBuilder& b ){ | void appendLastOp( BSONObjBuilder& b ){ | |||
skipping to change at line 235 | skipping to change at line 232 | |||
}; | }; | |||
inline Client& cc() { | inline Client& cc() { | |||
return *currentClient.get(); | return *currentClient.get(); | |||
} | } | |||
/* each thread which does db operations has a Client object in TLS. | /* each thread which does db operations has a Client object in TLS. | |||
call this when your thread starts. | call this when your thread starts. | |||
*/ | */ | |||
inline void Client::initThread(const char *desc) { | inline void Client::initThread(const char *desc) { | |||
setThreadName(desc); | ||||
assert( currentClient.get() == 0 ); | assert( currentClient.get() == 0 ); | |||
currentClient.reset( new Client(desc) ); | currentClient.reset( new Client(desc) ); | |||
mongo::lastError.initThread(); | ||||
} | } | |||
inline Client::GodScope::GodScope(){ | 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; | |||
} | } | |||
End of changes. 7 change blocks. | ||||
9 lines changed or deleted | 9 lines changed or added | |||
clientcursor.h | clientcursor.h | |||
---|---|---|---|---|
skipping to change at line 42 | skipping to change at line 42 | |||
#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" | |||
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; | ||||
/* 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. | |||
*/ | */ | |||
typedef map<CursorId, ClientCursor*> CCById; | typedef map<CursorId, ClientCursor*> CCById; | |||
typedef multimap<DiskLoc, ClientCursor*> CCByLoc; | typedef multimap<DiskLoc, ClientCursor*> CCByLoc; | |||
extern BSONObj id_obj; | extern BSONObj id_obj; | |||
class ClientCursor { | class ClientCursor { | |||
skipping to change at line 105 | skipping to change at line 106 | |||
_c->_pinValue += 100; | _c->_pinValue += 100; | |||
} | } | |||
} | } | |||
~Pointer() { | ~Pointer() { | |||
release(); | release(); | |||
} | } | |||
}; | }; | |||
/*const*/ CursorId cursorid; | /*const*/ CursorId cursorid; | |||
string ns; | string ns; | |||
auto_ptr<CoveredIndexMatcher> matcher; | shared_ptr<Cursor> c; | |||
auto_ptr<Cursor> c; | ||||
int pos; // # objects into the curs or so far | int pos; // # objects into the curs or so far | |||
BSONObj query; | BSONObj query; | |||
int _queryOptions; | int _queryOptions; | |||
OpTime _slaveReadTill; | OpTime _slaveReadTill; | |||
ClientCursor(int queryOptions, auto_ptr<Cursor>& _c, const char *_n s) : | ClientCursor(int queryOptions, shared_ptr<Cursor>& _c, const char * _ns) : | |||
_idleAgeMillis(0), _pinValue(0), | _idleAgeMillis(0), _pinValue(0), | |||
_doingDeletes(false), | _doingDeletes(false), | |||
ns(_ns), c(_c), | ns(_ns), c(_c), | |||
pos(0), _queryOptions(queryOptions) | pos(0), _queryOptions(queryOptions) | |||
{ | { | |||
if( queryOptions & QueryOption_NoCursorTimeout ) | if( queryOptions & QueryOption_NoCursorTimeout ) | |||
noTimeout(); | noTimeout(); | |||
recursive_scoped_lock lock(ccmutex); | recursive_scoped_lock lock(ccmutex); | |||
cursorid = allocCursorId_inlock(); | cursorid = allocCursorId_inlock(); | |||
clientCursorsById.insert( make_pair(cursorid, this) ); | clientCursorsById.insert( make_pair(cursorid, this) ); | |||
} | } | |||
~ClientCursor(); | ~ClientCursor(); | |||
DiskLoc lastLoc() const { | DiskLoc lastLoc() const { | |||
return _lastLoc; | return _lastLoc; | |||
} | } | |||
shared_ptr< ParsedQuery > pq; | ||||
shared_ptr< FieldMatcher > fields; // which fields query wants retu rned | shared_ptr< FieldMatcher > fields; // which fields query wants retu rned | |||
Message originalMessage; // this is effectively an auto ptr for dat a the matcher points to | Message originalMessage; // this is effectively an auto ptr for dat a the matcher points to | |||
/* Get rid of cursors for namespaces that begin with nsprefix. | /* Get rid of cursors for namespaces that begin with nsprefix. | |||
Used by drop, dropIndexes, dropDatabase. | Used by drop, dropIndexes, dropDatabase. | |||
*/ | */ | |||
static void invalidate(const char *nsPrefix); | static void invalidate(const char *nsPrefix); | |||
/** | /** | |||
* do a dbtemprelease | * do a dbtemprelease | |||
* note: caller should check matcher.docMatcher().atomic() first an d not yield if atomic - | * note: caller should check matcher.docMatcher().atomic() first an d not yield if atomic - | |||
* we don't do herein as this->matcher (above) is only initia lized for true queries/getmore. | * we don't do herein as this->matcher (above) is only initia lized for true queries/getmore. | |||
* (ie not set for remote/update) | * (ie not set for remote/update) | |||
* @return if the cursor is still valid. | * @return if the cursor is still valid. | |||
* if false is returned, then this ClientCursor should be c 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(); | bool yield(); | |||
struct YieldLock { | struct YieldLock : boost::noncopyable { | |||
YieldLock( ClientCursor * cc ) | explicit YieldLock( ptr<ClientCursor> cc ) | |||
: _cc( cc ) , _id( cc->cursorid ) , _doingDeletes( cc->_doi ngDeletes ) { | : _cc( cc ) , _id( cc->cursorid ) , _doingDeletes( cc->_doi ngDeletes ) { | |||
cc->updateLocation(); | cc->updateLocation(); | |||
_unlock = new dbtempreleasecond(); | _unlock.reset(new dbtempreleasecond()); | |||
} | } | |||
~YieldLock(){ | ~YieldLock(){ | |||
assert( ! _unlock ); | assert( ! _unlock ); | |||
} | } | |||
bool stillOk(){ | bool stillOk(){ | |||
delete _unlock; | relock(); | |||
_unlock = 0; | ||||
if ( ClientCursor::find( _id , false ) == 0 ){ | if ( ClientCursor::find( _id , false ) == 0 ){ | |||
// i was deleted | // i was deleted | |||
return false; | return false; | |||
} | } | |||
_cc->_doingDeletes = _doingDeletes; | _cc->_doingDeletes = _doingDeletes; | |||
return true; | return true; | |||
} | } | |||
void relock(){ | ||||
_unlock.reset(); | ||||
} | ||||
private: | ||||
ClientCursor * _cc; | ClientCursor * _cc; | |||
CursorId _id; | CursorId _id; | |||
bool _doingDeletes; | bool _doingDeletes; | |||
dbtempreleasecond * _unlock; | scoped_ptr<dbtempreleasecond> _unlock; | |||
}; | }; | |||
YieldLock yieldHold(){ | ||||
return YieldLock( this ); | ||||
} | ||||
// --- some pass through helpers for Cursor --- | // --- some pass through helpers for Cursor --- | |||
BSONObj indexKeyPattern() { | BSONObj indexKeyPattern() { | |||
return c->indexKeyPattern(); | return c->indexKeyPattern(); | |||
} | } | |||
bool ok(){ | bool ok(){ | |||
return c->ok(); | return c->ok(); | |||
} | } | |||
bool advance(){ | bool advance(){ | |||
return c->advance(); | return c->advance(); | |||
} | } | |||
bool currentMatches(){ | bool currentMatches(){ | |||
if ( ! matcher.get() ) | if ( ! c->matcher() ) | |||
return true; | return true; | |||
return matcher->matchesCurrent( c.get() ); | return c->matcher()->matchesCurrent( c.get() ); | |||
} | } | |||
BSONObj current(){ | BSONObj current(){ | |||
return c->current(); | return c->current(); | |||
} | } | |||
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) { | |||
skipping to change at line 247 | skipping to change at line 248 | |||
} | } | |||
return false; | return false; | |||
} | } | |||
/* call when cursor's location changes so that we can update the | /* call when cursor's location changes so that we can update the | |||
cursorsbylocation map. if you are locked and internally iterati ng, only | cursorsbylocation map. if you are locked and internally iterati ng, only | |||
need to call when you are ready to "unlock". | need to call when you are ready to "unlock". | |||
*/ | */ | |||
void updateLocation(); | void updateLocation(); | |||
void cleanupByLocation(DiskLoc loc); | ||||
void mayUpgradeStorage() { | void mayUpgradeStorage() { | |||
/* if ( !ids_.get() ) | /* if ( !ids_.get() ) | |||
return; | return; | |||
stringstream ss; | stringstream ss; | |||
ss << ns << "." << cursorid; | ss << ns << "." << cursorid; | |||
ids_->mayUpgradeStorage( ss.str() );*/ | ids_->mayUpgradeStorage( ss.str() );*/ | |||
} | } | |||
/** | /** | |||
* @param millis amount of idle passed time since last call | * @param millis amount of idle passed time since last call | |||
skipping to change at line 293 | skipping to change at line 292 | |||
static unsigned byLocSize(); // just for diagnostics | static unsigned byLocSize(); // just for diagnostics | |||
static void informAboutToDeleteBucket(const DiskLoc& b); | static void informAboutToDeleteBucket(const DiskLoc& b); | |||
static void aboutToDelete(const DiskLoc& dl); | static void aboutToDelete(const DiskLoc& dl); | |||
}; | }; | |||
class ClientCursorMonitor : public BackgroundJob { | class ClientCursorMonitor : public BackgroundJob { | |||
public: | public: | |||
void run(); | void run(); | |||
string name() { return "ClientCursorMonitor"; } | ||||
}; | }; | |||
extern ClientCursorMonitor clientCursorMonitor; | extern ClientCursorMonitor clientCursorMonitor; | |||
} // namespace mongo | } // namespace mongo | |||
End of changes. 14 change blocks. | ||||
17 lines changed or deleted | 17 lines changed or added | |||
cmdline.h | cmdline.h | |||
---|---|---|---|---|
skipping to change at line 73 | skipping to change at line 73 | |||
*/ | */ | |||
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 ); | |||
}; | }; | |||
extern CmdLine cmdLine; | extern CmdLine cmdLine; | |||
void setupCoreSignals(); | ||||
} | } | |||
End of changes. 1 change blocks. | ||||
0 lines changed or deleted | 1 lines changed or added | |||
commands.h | commands.h | |||
---|---|---|---|---|
skipping to change at line 48 | skipping to change at line 48 | |||
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 char *ns, BSONObj& cmdObj, string& errmsg, B SONObjBuilder& result, bool fromRepl) = 0; | virtual bool run(const string& db, BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool fromRepl) = 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 { | |||
End of changes. 1 change blocks. | ||||
1 lines changed or deleted | 1 lines changed or added | |||
concurrency.h | concurrency.h | |||
---|---|---|---|---|
skipping to change at line 32 | skipping to change at line 32 | |||
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/locks.h" | #include "../util/concurrency/locks.h" | |||
namespace mongo { | namespace mongo { | |||
inline bool readLockSupported(){ | inline bool readLockSupported(){ | |||
return true; | return true; | |||
} | } | |||
string sayClientState(); | string sayClientState(); | |||
bool haveClient(); | bool haveClient(); | |||
skipping to change at line 207 | skipping to change at line 207 | |||
assert( s == -1 ); | assert( s == -1 ); | |||
_state.set(0); | _state.set(0); | |||
_m.unlock_shared(); | _m.unlock_shared(); | |||
} | } | |||
MutexInfo& info() { return _minfo; } | MutexInfo& info() { return _minfo; } | |||
}; | }; | |||
extern MongoMutex &dbMutex; | extern MongoMutex &dbMutex; | |||
void dbunlocking_write(); | inline void dbunlocking_write() { } | |||
void dbunlocking_read(); | inline void dbunlocking_read() { } | |||
struct writelock { | struct writelock { | |||
writelock(const string& ns) { | writelock(const string& ns) { | |||
dbMutex.lock(); | dbMutex.lock(); | |||
} | } | |||
~writelock() { | ~writelock() { | |||
DESTRUCTOR_GUARD( | DESTRUCTOR_GUARD( | |||
dbunlocking_write(); | dbunlocking_write(); | |||
dbMutex.unlock(); | dbMutex.unlock(); | |||
); | ); | |||
skipping to change at line 244 | skipping to change at line 244 | |||
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(); | dbunlocking_read(); | |||
dbMutex.unlock_shared(); | dbMutex.unlock_shared(); | |||
} | } | |||
} | } | |||
bool got(){ | bool got() const { return _got; } | |||
return _got; | private: | |||
} | ||||
bool _got; | bool _got; | |||
}; | }; | |||
struct readlocktryassert : public readlocktry { | ||||
readlocktryassert(const string& ns, int tryms) : | ||||
readlocktry(ns,tryms) { | ||||
uassert(13142, "timeout getting readlock", got()); | ||||
} | ||||
}; | ||||
/** assure we have at least a read lock - they key with this being | ||||
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: | ||||
int _prev; | int _prev; | |||
}; | }; | |||
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(); | |||
} | } | |||
skipping to change at line 289 | skipping to change at line 298 | |||
} else { | } else { | |||
dbunlocking_read(); | dbunlocking_read(); | |||
dbMutex.unlock_shared(); | dbMutex.unlock_shared(); | |||
} | } | |||
); | ); | |||
} | } | |||
/* this unlocks, does NOT upgrade. that works for our current usage */ | /* this unlocks, does NOT upgrade. that works for our current usage */ | |||
void releaseAndWriteLock(); | void releaseAndWriteLock(); | |||
}; | }; | |||
/* use writelock and readlock instead */ | /* use writelock and readlock instead */ | |||
struct dblock : public writelock { | struct dblock : public writelock { | |||
dblock() : writelock("") { } | dblock() : writelock("") { } | |||
~dblock() { | ||||
} | ||||
}; | }; | |||
// eliminate | // eliminate | |||
inline void assertInWriteLock() { dbMutex.assertWriteLocked(); } | inline void assertInWriteLock() { dbMutex.assertWriteLocked(); } | |||
} | } | |||
End of changes. 7 change blocks. | ||||
10 lines changed or deleted | 17 lines changed or added | |||
config.h | config.h | |||
---|---|---|---|---|
skipping to change at line 213 | skipping to change at line 213 | |||
"configure" can be used to override this default. */ | "configure" can be used to override this default. */ | |||
#define POSIX_MALLOC_THRESHOLD 10 | #define POSIX_MALLOC_THRESHOLD 10 | |||
/* Define to 1 if you have the ANSI C header files. */ | /* Define to 1 if you have the ANSI C header files. */ | |||
#define STDC_HEADERS 1 | #define STDC_HEADERS 1 | |||
/* Define to enable support for Unicode properties */ | /* Define to enable support for Unicode properties */ | |||
/* #undef SUPPORT_UCP */ | /* #undef SUPPORT_UCP */ | |||
/* Define to enable support for the UTF-8 Unicode encoding. */ | /* Define to enable support for the UTF-8 Unicode encoding. */ | |||
#if( !defined(SUPPORT_UTF8) ) | ||||
#define SUPPORT_UTF8 | #define SUPPORT_UTF8 | |||
#endif | ||||
/* Version number of package */ | /* Version number of package */ | |||
#define VERSION "7.4" | #define VERSION "7.4" | |||
/* Define to empty if `const' does not conform to ANSI C. */ | /* Define to empty if `const' does not conform to ANSI C. */ | |||
/* #undef const */ | /* #undef const */ | |||
/* Define to `unsigned int' if <sys/types.h> does not define. */ | /* Define to `unsigned int' if <sys/types.h> does not define. */ | |||
/* #undef size_t */ | /* #undef size_t */ | |||
End of changes. 2 change blocks. | ||||
0 lines changed or deleted | 2 lines changed or added | |||
connpool.h | connpool.h | |||
---|---|---|---|---|
skipping to change at line 26 | skipping to change at line 26 | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include <stack> | #include <stack> | |||
#include "dbclient.h" | #include "dbclient.h" | |||
#include "redef_macros.h" | #include "redef_macros.h" | |||
namespace mongo { | namespace mongo { | |||
class Shard; | ||||
struct PoolForHost { | struct PoolForHost { | |||
PoolForHost() | PoolForHost() | |||
: created(0){} | : created(0){} | |||
std::stack<DBClientBase*> pool; | std::stack<DBClientBase*> pool; | |||
long long created; | long long created; | |||
}; | }; | |||
class DBConnectionHook { | class DBConnectionHook { | |||
public: | public: | |||
virtual ~DBConnectionHook(){} | virtual ~DBConnectionHook(){} | |||
skipping to change at line 62 | skipping to change at line 62 | |||
{ | { | |||
ScopedDbConnection c("myserver"); | ScopedDbConnection c("myserver"); | |||
c.conn()... | c.conn()... | |||
} | } | |||
*/ | */ | |||
class DBConnectionPool { | class DBConnectionPool { | |||
mongo::mutex _mutex; | mongo::mutex _mutex; | |||
map<string,PoolForHost*> _pools; // servername -> pool | map<string,PoolForHost*> _pools; // servername -> pool | |||
list<DBConnectionHook*> _hooks; | list<DBConnectionHook*> _hooks; | |||
public: | ||||
DBConnectionPool() : _mutex("DBConnectionPool") { } | ||||
void onCreate( DBClientBase * conn ); | void onCreate( DBClientBase * conn ); | |||
void onHandedOut( DBClientBase * conn ); | void onHandedOut( DBClientBase * conn ); | |||
public: | ||||
void flush(); | void flush(); | |||
DBClientBase *get(const string& host); | DBClientBase *get(const string& host); | |||
void release(const string& host, DBClientBase *c) { | void release(const string& host, DBClientBase *c) { | |||
if ( c->isFailed() ){ | if ( c->isFailed() ){ | |||
delete c; | delete c; | |||
return; | return; | |||
} | } | |||
scoped_lock L(_mutex); | scoped_lock L(_mutex); | |||
_pools[host]->pool.push(c); | _pools[host]->pool.push(c); | |||
} | } | |||
skipping to change at line 119 | skipping to change at line 122 | |||
/** throws UserException if can't connect */ | /** throws UserException if can't connect */ | |||
ScopedDbConnection(const string& host) | ScopedDbConnection(const string& host) | |||
: _host(host), _conn( pool.get(host) ) { | : _host(host), _conn( pool.get(host) ) { | |||
} | } | |||
ScopedDbConnection(const string& host, DBClientBase* conn ) | ScopedDbConnection(const string& host, DBClientBase* conn ) | |||
: _host( host ) , _conn( conn ){ | : _host( host ) , _conn( conn ){ | |||
} | } | |||
ScopedDbConnection(const Shard& shard ); | ||||
ScopedDbConnection(const Shard* shard ); | ||||
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; | |||
} | } | |||
End of changes. 4 change blocks. | ||||
1 lines changed or deleted | 9 lines changed or added | |||
counters.h | counters.h | |||
---|---|---|---|---|
skipping to change at line 36 | skipping to change at line 36 | |||
/** | /** | |||
* 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(); | |||
int * getInsert(){ return _insert; } | AtomicUInt * getInsert(){ return _insert; } | |||
int * getQuery(){ return _query; } | AtomicUInt * getQuery(){ return _query; } | |||
int * getUpdate(){ return _update; } | AtomicUInt * getUpdate(){ return _update; } | |||
int * getDelete(){ return _delete; } | AtomicUInt * getDelete(){ return _delete; } | |||
int * getGetMore(){ return _getmore; } | AtomicUInt * getGetMore(){ return _getmore; } | |||
int * getCommand(){ return _command; } | AtomicUInt * getCommand(){ return _command; } | |||
void gotInsert(){ _insert[0]++; } | void gotInsert(){ _insert[0]++; } | |||
void gotQuery(){ _query[0]++; } | void gotQuery(){ _query[0]++; } | |||
void gotUpdate(){ _update[0]++; } | void gotUpdate(){ _update[0]++; } | |||
void gotDelete(){ _delete[0]++; } | void gotDelete(){ _delete[0]++; } | |||
void gotGetMore(){ _getmore[0]++; } | void gotGetMore(){ _getmore[0]++; } | |||
void gotCommand(){ _command[0]++; } | void gotCommand(){ _command[0]++; } | |||
void gotOp( int op , bool isCommand ); | void gotOp( int op , bool isCommand ); | |||
BSONObj& getObj(){ return _obj; } | BSONObj& getObj(){ return _obj; } | |||
private: | private: | |||
BSONObj _obj; | BSONObj _obj; | |||
int * _insert; | AtomicUInt * _insert; | |||
int * _query; | AtomicUInt * _query; | |||
int * _update; | AtomicUInt * _update; | |||
int * _delete; | AtomicUInt * _delete; | |||
int * _getmore; | AtomicUInt * _getmore; | |||
int * _command; | AtomicUInt * _command; | |||
}; | }; | |||
extern OpCounters globalOpCounters; | extern OpCounters globalOpCounters; | |||
class IndexCounters { | class IndexCounters { | |||
public: | public: | |||
IndexCounters(); | IndexCounters(); | |||
void btree( char * node ){ | void btree( char * node ){ | |||
if ( ! _memSupported ) | if ( ! _memSupported ) | |||
skipping to change at line 125 | skipping to change at line 125 | |||
long long _total_time; | long long _total_time; | |||
long long _flushes; | long long _flushes; | |||
int _last_time; | int _last_time; | |||
Date_t _last; | Date_t _last; | |||
}; | }; | |||
extern FlushCounters globalFlushCounters; | extern FlushCounters globalFlushCounters; | |||
class GenericCounter { | class GenericCounter { | |||
public: | public: | |||
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; | |||
}; | }; | |||
} | } | |||
End of changes. 3 change blocks. | ||||
12 lines changed or deleted | 13 lines changed or added | |||
curop.h | curop.h | |||
---|---|---|---|---|
skipping to change at line 244 | skipping to change at line 243 | |||
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 incPort = true ){ | string getRemoteString( bool includePort = true ){ | |||
return _remote.toString(incPort); | return _remote.toString(includePort); | |||
} | } | |||
ProgressMeter& setMessage( const char * msg , long long progressMet erTotal = 0 , int secondsBetween = 3 ){ | ProgressMeter& setMessage( const char * msg , long long progressMet erTotal = 0 , int secondsBetween = 3 ){ | |||
if ( progressMeterTotal ){ | if ( progressMeterTotal ){ | |||
if ( _progressMeter.isActive() ){ | if ( _progressMeter.isActive() ){ | |||
cout << "about to assert, old _message: " << _message < < 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; } | string getMessage() const { return _message; } | |||
ProgressMeter getProgressMeter() { return _progressMeter; } | ProgressMeter& getProgressMeter() { return _progressMeter; } | |||
friend class Client; | friend class Client; | |||
}; | }; | |||
/* 0 = ok | /* 0 = ok | |||
1 = kill current operation and reset this to 0 | 1 = kill current operation and reset this to 0 | |||
future: maybe use this as a "going away" thing on process terminatio n with a higher flag value | future: maybe use this as a "going away" thing on process terminatio n with a higher flag value | |||
*/ | */ | |||
extern class KillCurrentOp { | extern class KillCurrentOp { | |||
enum { Off, On, All } state; | enum { Off, On, All } state; | |||
End of changes. 3 change blocks. | ||||
4 lines changed or deleted | 4 lines changed or added | |||
cursor.h | cursor.h | |||
---|---|---|---|---|
skipping to change at line 23 | skipping to change at line 23 | |||
* 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 "diskloc.h" | #include "diskloc.h" | |||
#include "matcher.h" | ||||
namespace mongo { | namespace mongo { | |||
class Record; | class Record; | |||
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 { | class Cursor : boost::noncopyable { | |||
public: | public: | |||
virtual ~Cursor() {} | virtual ~Cursor() {} | |||
virtual bool ok() = 0; | virtual bool ok() = 0; | |||
bool eof() { | bool eof() { return !ok(); } | |||
return !ok(); | ||||
} | ||||
virtual Record* _current() = 0; | virtual Record* _current() = 0; | |||
virtual BSONObj current() = 0; | virtual BSONObj current() = 0; | |||
virtual DiskLoc currLoc() = 0; | virtual DiskLoc currLoc() = 0; | |||
virtual bool advance() = 0; /*true=ok*/ | virtual bool advance() = 0; /*true=ok*/ | |||
virtual BSONObj currKey() const { return BSONObj(); } | virtual BSONObj currKey() const { return BSONObj(); } | |||
// DiskLoc the cursor requires for continued operation. Before thi s | // DiskLoc the cursor requires for continued operation. Before thi s | |||
// DiskLoc is deleted, the cursor must be incremented or destroyed. | // DiskLoc is deleted, the cursor must be incremented or destroyed. | |||
virtual DiskLoc refLoc() = 0; | virtual DiskLoc refLoc() = 0; | |||
skipping to change at line 82 | skipping to change at line 82 | |||
/* 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 supportGetMore() = 0; | |||
virtual string toString() { | virtual string toString() { return "abstract?"; } | |||
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 | @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 | for backwards datafile compatibility. 'deep' can be eliminated next time we | |||
force a data file conversion. 7Jul09 | force a data file conversion. 7Jul09 | |||
*/ | */ | |||
virtual bool getsetdup(DiskLoc loc) = 0; | virtual bool getsetdup(DiskLoc loc) = 0; | |||
virtual BSONObj prettyIndexBounds() const { return BSONObj(); } | virtual BSONArray prettyIndexBounds() const { return BSONArray(); } | |||
virtual bool capped() const { return false; } | virtual bool capped() const { return false; } | |||
// The implementation may return different matchers depending on th | ||||
e | ||||
// position of the cursor. If matcher() is nonzero at the start, | ||||
// matcher() should be checked each time advance() is called. | ||||
virtual CoveredIndexMatcher *matcher() const { return 0; } | ||||
// A convenience function for setting the value of matcher() manual | ||||
ly | ||||
// so it may accessed later. Implementations which must generate | ||||
// their own matcher() should assert here. | ||||
virtual void setMatcher( auto_ptr< CoveredIndexMatcher > matcher ) | ||||
{ | ||||
massert( 13285, "manual matcher config not allowed", false ); | ||||
} | ||||
}; | }; | |||
// 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: | protected: | |||
DiskLoc curr, last; | DiskLoc curr, last; | |||
const AdvanceStrategy *s; | const AdvanceStrategy *s; | |||
private: | private: | |||
bool tailable_; | bool tailable_; | |||
auto_ptr< CoveredIndexMatcher > _matcher; | ||||
void init() { | void init() { | |||
tailable_ = false; | tailable_ = false; | |||
} | } | |||
public: | public: | |||
bool ok() { | bool ok() { | |||
return !curr.isNull(); | return !curr.isNull(); | |||
} | } | |||
Record* _current() { | Record* _current() { | |||
assert( ok() ); | assert( ok() ); | |||
return curr.rec(); | return curr.rec(); | |||
skipping to change at line 164 | skipping to change at line 174 | |||
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 supportGetMore() { return true; } | virtual bool supportGetMore() { return true; } | |||
virtual CoveredIndexMatcher *matcher() const { return _matcher.get( | ||||
); } | ||||
virtual void setMatcher( auto_ptr< CoveredIndexMatcher > matcher ) | ||||
{ | ||||
_matcher = matcher; | ||||
} | ||||
}; | }; | |||
/* 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"; | |||
} | } | |||
End of changes. 9 change blocks. | ||||
8 lines changed or deleted | 30 lines changed or added | |||
database.h | database.h | |||
---|---|---|---|---|
skipping to change at line 116 | skipping to change at line 116 | |||
files.pop_back(); | files.pop_back(); | |||
} | } | |||
} | } | |||
MongoDataFile* getFile( int n, int sizeNeeded = 0, bool preallocate Only = false ) { | MongoDataFile* getFile( int n, int sizeNeeded = 0, bool preallocate Only = false ) { | |||
assert(this); | assert(this); | |||
namespaceIndex.init(); | namespaceIndex.init(); | |||
if ( n < 0 || n >= DiskLoc::MaxFiles ) { | if ( n < 0 || n >= DiskLoc::MaxFiles ) { | |||
out() << "getFile(): n=" << n << endl; | out() << "getFile(): n=" << n << endl; | |||
#if !defined(_RECSTORE) | #if 0 | |||
if( n >= RecCache::Base && n <= RecCache::Base+1000 ) | if( n >= RecCache::Base && n <= RecCache::Base+1000 ) | |||
massert( 10294 , "getFile(): bad file number - using re cstore db w/nonrecstore db build?", false); | massert( 10294 , "getFile(): bad file number - using re cstore db w/nonrecstore db build?", false); | |||
#endif | #endif | |||
massert( 10295 , "getFile(): bad file number value (corrupt db?): run repair", false); | massert( 10295 , "getFile(): bad file number value (corrupt db?): run repair", false); | |||
} | } | |||
DEV { | DEV { | |||
if ( n > 100 ) | if ( n > 100 ) | |||
out() << "getFile(): n=" << n << "?" << endl; | out() << "getFile(): n=" << n << "?" << endl; | |||
} | } | |||
MongoDataFile* p = 0; | MongoDataFile* p = 0; | |||
skipping to change at line 156 | skipping to change at line 156 | |||
throw; | throw; | |||
} | } | |||
if ( preallocateOnly ) | if ( preallocateOnly ) | |||
delete p; | delete p; | |||
else | else | |||
files[n] = p; | files[n] = p; | |||
} | } | |||
return preallocateOnly ? 0 : p; | return preallocateOnly ? 0 : p; | |||
} | } | |||
MongoDataFile* addAFile( int sizeNeeded = 0, bool preallocateNextFi le = false ) { | MongoDataFile* addAFile( int sizeNeeded, bool preallocateNextFile ) { | |||
int n = (int) files.size(); | int n = (int) files.size(); | |||
MongoDataFile *ret = getFile( n, sizeNeeded ); | MongoDataFile *ret = getFile( n, sizeNeeded ); | |||
if ( preallocateNextFile ) | if ( preallocateNextFile ) | |||
preallocateAFile(); | preallocateAFile(); | |||
return ret; | return ret; | |||
} | } | |||
// safe to call this multiple times - the implementation will only preallocate one file | // safe to call this multiple times - the implementation will only preallocate one file | |||
void preallocateAFile() { | void preallocateAFile() { | |||
int n = (int) files.size(); | int n = (int) files.size(); | |||
getFile( n, 0, true ); | getFile( n, 0, true ); | |||
} | } | |||
MongoDataFile* suitableFile( int sizeNeeded ) { | MongoDataFile* suitableFile( int sizeNeeded, bool preallocate ) { | |||
MongoDataFile* f = newestFile(); | MongoDataFile* f = newestFile(); | |||
if ( !f ) { | ||||
f = addAFile( sizeNeeded, preallocate ); | ||||
} | ||||
for ( int i = 0; i < 8; i++ ) { | for ( int i = 0; i < 8; i++ ) { | |||
if ( f->getHeader()->unusedLength >= sizeNeeded ) | if ( f->getHeader()->unusedLength >= sizeNeeded ) | |||
break; | break; | |||
f = addAFile( sizeNeeded ); | f = addAFile( sizeNeeded, preallocate ); | |||
if ( f->getHeader()->fileLength >= MongoDataFile::maxSize() ) // this is as big as they get so might as well stop | if ( f->getHeader()->fileLength >= MongoDataFile::maxSize() ) // this is as big as they get so might as well stop | |||
break; | break; | |||
} | } | |||
return f; | return f; | |||
} | } | |||
Extent* allocExtent( const char *ns, int size, bool capped ) { | Extent* allocExtent( const char *ns, int size, bool capped ) { | |||
Extent *e = DataFileMgr::allocFromFreeList( ns, size, capped ); | Extent *e = DataFileMgr::allocFromFreeList( ns, size, capped ); | |||
if( e ) return e; | if( e ) return e; | |||
return suitableFile( size )->createExtent( ns, size, capped ); | return suitableFile( size, !capped )->createExtent( ns, size, c apped ); | |||
} | } | |||
MongoDataFile* newestFile() { | MongoDataFile* newestFile() { | |||
int n = (int) files.size(); | int n = (int) files.size(); | |||
if ( n > 0 ) n--; | if ( n > 0 ) { | |||
n--; | ||||
} else { | ||||
return 0; | ||||
} | ||||
return getFile(n); | return getFile(n); | |||
} | } | |||
/** | /** | |||
* @return true if success, false otherwise | * @return true if success, false otherwise | |||
*/ | */ | |||
bool setProfilingLevel( int newLevel , string& errmsg ); | bool setProfilingLevel( int newLevel , string& errmsg ); | |||
void finishInit(); | void finishInit(); | |||
static bool validDBName( const string& ns ); | ||||
vector<MongoDataFile*> files; | vector<MongoDataFile*> files; | |||
string name; // "alleyinsider" | string name; // "alleyinsider" | |||
string path; | string path; | |||
NamespaceIndex namespaceIndex; | NamespaceIndex namespaceIndex; | |||
int profile; // 0=off. | int profile; // 0=off. | |||
string profileName; // "alleyinsider.system.profile" | string profileName; // "alleyinsider.system.profile" | |||
int magic; // used for making sure the object is still loaded in me mory | int magic; // used for making sure the object is still loaded in me mory | |||
}; | }; | |||
} // namespace mongo | } // namespace mongo | |||
End of changes. 8 change blocks. | ||||
6 lines changed or deleted | 15 lines changed or added | |||
db.h | db.h | |||
---|---|---|---|---|
skipping to change at line 21 | skipping to change at line 21 | |||
* GNU Affero General Public License for more details. | * GNU Affero General Public License for more details. | |||
* | * | |||
* You should have received a copy of the GNU Affero General Public Licen se | * You should have received a copy of the GNU Affero General Public Licen se | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../pch.h" | #include "../pch.h" | |||
#include "../util/message.h" | #include "../util/message.h" | |||
#include "boost/version.hpp" | ||||
#include "concurrency.h" | #include "concurrency.h" | |||
#include "pdfile.h" | #include "pdfile.h" | |||
#include "client.h" | #include "client.h" | |||
namespace mongo { | namespace mongo { | |||
// void jniCallback(Message& m, Message& out); | // void jniCallback(Message& m, Message& out); | |||
/* Note the limit here is rather arbitrary and is simply a standard. ge nerally the code works | /* Note the limit here is rather arbitrary and is simply a standard. ge nerally the code works | |||
with any object that fits in ram. | with any object that fits in ram. | |||
skipping to change at line 139 | skipping to change at line 138 | |||
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 ); | ||||
uassert( 13280 , (string)"invalid db name: " + ns , Database::v | ||||
alidDBName( d ) ); | ||||
return d; | ||||
} | ||||
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; | |||
End of changes. 2 change blocks. | ||||
1 lines changed or deleted | 7 lines changed or added | |||
dbclient.h | dbclient.h | |||
---|---|---|---|---|
skipping to change at line 253 | skipping to change at line 253 | |||
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 */ | |||
int _logLevel; | int _logLevel; | |||
DBClientWithCommands() : _logLevel(0) { } | DBClientWithCommands() : _logLevel(0) { } | |||
/** helper function. run a simple command where the command | /** helper function. run a simple command where the command expres | |||
expression is simply | sion is simply | |||
{ command : 1 } | { command : 1 } | |||
@param info -- where to put result object. may be null if call er doesn't need that info | @param info -- where to put result object. may be null if call er doesn't need that info | |||
@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 exam ple, { ismaster : 1 } | |||
@param info the result object the database returns. Typically has { ok : ..., errmsg : ... } fields | @param info the result object the database returns. Typically has { 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 | Authentication is separate for each database on the server -- y | |||
server -- you may authenticate for any | 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 authenticat | The "admin" database is special and once authenticated provides | |||
ed provides access to all databases on the | access to all databases on the | |||
server. | server. | |||
@param digestPassword if password is plain text, set | @param digestPassword if password is plain text, set this to tr | |||
this to true. otherwise assumed to be pre-digested | 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 ); | unsigned long long count(const string &ns, const BSONObj& query = B SONObj(), int options=0 ); | |||
string createPasswordDigest( const string &username , const string &clearTextPassword ); | string createPasswordDigest( const string &username , const string &clearTextPassword ); | |||
End of changes. 5 change blocks. | ||||
15 lines changed or deleted | 15 lines changed or added | |||
dbclientcursor.h | dbclientcursor.h | |||
---|---|---|---|---|
skipping to change at line 28 | skipping to change at line 28 | |||
#pragma once | #pragma once | |||
#include "../pch.h" | #include "../pch.h" | |||
#include "../util/message.h" | #include "../util/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 ShardConnection; | ||||
/** Queries return a cursor object */ | /** Queries return a cursor object */ | |||
class DBClientCursor : boost::noncopyable { | class DBClientCursor : boost::noncopyable { | |||
public: | public: | |||
/** If true, safe to call next(). Requests more from server if necessary. */ | /** If true, safe to call next(). Requests more from server if necessary. */ | |||
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. | |||
skipping to change at line 61 | skipping to change at line 63 | |||
*/ | */ | |||
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(); | BSONElement e = o.firstElement(); | |||
if( strcmp(e.fieldName(), "$err") == 0 ) { | if( strcmp(e.fieldName(), "$err") == 0 ) { | |||
if( logLevel >= 5 ) | if( logLevel >= 5 ) | |||
log() << "nextSafe() error " << o.toString() << endl; | log() << "nextSafe() error " << o.toString() << endl; | |||
uassert(13106, "nextSafe() returns: " + o.toString(), false ); | uassert(13106, "nextSafe(): " + o.toString(), false); | |||
} | } | |||
return o; | return o; | |||
} | } | |||
/** | /** | |||
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() ){ | |||
skipping to change at line 142 | skipping to change at line 144 | |||
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( ScopedDbConnection * conn ); | void attach( ScopedDbConnection * conn ); | |||
void attach( ShardConnection * conn ); | ||||
private: | private: | |||
friend class DBClientBase; | friend class DBClientBase; | |||
bool init(); | bool init(); | |||
int nextBatchSize(); | int nextBatchSize(); | |||
DBConnector *connector; | DBConnector *connector; | |||
string ns; | string ns; | |||
BSONObj query; | BSONObj query; | |||
int nToReturn; | int nToReturn; | |||
bool haveLimit; | bool haveLimit; | |||
End of changes. 3 change blocks. | ||||
1 lines changed or deleted | 4 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 { | |||
class Cursor; | class Cursor; | |||
class CoveredIndexMatcher; | class CoveredIndexMatcher; | |||
class CursorIterator { | class CursorIterator { | |||
public: | public: | |||
CursorIterator( auto_ptr<Cursor> c , BSONObj filter = BSONObj() ); | CursorIterator( shared_ptr<Cursor> c , BSONObj filter = BSONObj() ) ; | |||
BSONObj next(); | BSONObj next(); | |||
bool hasNext(); | bool hasNext(); | |||
private: | private: | |||
void _advance(); | void _advance(); | |||
auto_ptr<Cursor> _cursor; | shared_ptr<Cursor> _cursor; | |||
auto_ptr<CoveredIndexMatcher> _matcher; | auto_ptr<CoveredIndexMatcher> _matcher; | |||
BSONObj _o; | 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. | |||
skipping to change at line 85 | skipping to change at line 85 | |||
/** | /** | |||
* @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 ); | |||
static auto_ptr<CursorIterator> find( const char *ns , BSONObj quer y = BSONObj() , bool requireIndex = false ); | static auto_ptr<CursorIterator> find( const char *ns , BSONObj quer y = BSONObj() , bool requireIndex = false ); | |||
/* Get/put the first object from a collection. Generally only usef | /** Get/put the first object from a collection. Generally only use | |||
ul if the collection | ful if the collection | |||
only ever has a single object -- which is a "singleton collectio | only ever has a single object -- which is a "singleton collecti | |||
n". | on". | |||
You do not need to set the database before calling. | You do not need to set the database before calling. | |||
Returns: true if object exists. | @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); | |||
/** You do not need to set the database before calling. | ||||
@return true if collection is empty. | ||||
*/ | ||||
static bool isEmpty(const char *ns); | ||||
/* Remove all objects from a collection. | /* Remove all objects from a collection. | |||
You do not need to set the database before calling. | You do not need to set the database before calling. | |||
*/ | */ | |||
static void emptyCollection(const char *ns); | static void emptyCollection(const char *ns); | |||
}; | }; | |||
class Database; | class Database; | |||
// manage a set using collection backed storage | // manage a set using collection backed storage | |||
End of changes. 6 change blocks. | ||||
8 lines changed or deleted | 13 lines changed or added | |||
dbmessage.h | dbmessage.h | |||
---|---|---|---|---|
skipping to change at line 83 | skipping to change at line 83 | |||
}; | }; | |||
#pragma pack() | #pragma pack() | |||
/* For the database/server protocol, these objects and functions encaps ulate | /* For the database/server protocol, these objects and functions encaps ulate | |||
the various messages transmitted over the connection. | the various messages transmitted over the connection. | |||
*/ | */ | |||
class DbMessage { | class DbMessage { | |||
public: | public: | |||
DbMessage(const Message& _m) : m(_m) { | DbMessage(const Message& _m) : m(_m) { | |||
theEnd = _m.data->_data + _m.data->dataLen(); | // for received messages, Message has only one buffer | |||
int *r = (int *) _m.data->_data; | theEnd = _m.singleData()->_data + _m.header()->dataLen(); | |||
int *r = (int *) _m.singleData()->_data; | ||||
reserved = *r; | reserved = *r; | |||
r++; | r++; | |||
data = (const char *) r; | data = (const char *) r; | |||
nextjsobj = data; | nextjsobj = data; | |||
} | } | |||
const char * getns() const { | const char * getns() const { | |||
return data; | return data; | |||
} | } | |||
void getns(Namespace& ns) const { | void getns(Namespace& ns) const { | |||
skipping to change at line 207 | skipping to change at line 208 | |||
/* parses the message into the above fields */ | /* parses the message into the above fields */ | |||
QueryMessage(DbMessage& d) { | QueryMessage(DbMessage& d) { | |||
ns = d.getns(); | ns = d.getns(); | |||
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().data->dataAsInt(); | queryOptions = d.msg().header()->dataAsInt(); | |||
} | } | |||
}; | }; | |||
} // namespace mongo | } // namespace mongo | |||
#include "../client/dbclient.h" | #include "../client/dbclient.h" | |||
namespace mongo { | namespace mongo { | |||
inline void replyToQuery(int queryResultFlags, | inline void replyToQuery(int queryResultFlags, | |||
skipping to change at line 235 | skipping to change at line 236 | |||
b.append(data, size); | b.append(data, size); | |||
QueryResult *qr = (QueryResult *) b.buf(); | QueryResult *qr = (QueryResult *) b.buf(); | |||
qr->_resultFlags() = queryResultFlags; | qr->_resultFlags() = queryResultFlags; | |||
qr->len = b.len(); | qr->len = b.len(); | |||
qr->setOperation(opReply); | qr->setOperation(opReply); | |||
qr->cursorId = cursorId; | qr->cursorId = cursorId; | |||
qr->startingFrom = startingFrom; | qr->startingFrom = startingFrom; | |||
qr->nReturned = nReturned; | qr->nReturned = nReturned; | |||
b.decouple(); | b.decouple(); | |||
Message resp(qr, true); | Message resp(qr, true); | |||
p->reply(requestMsg, resp, requestMsg.data->id); | p->reply(requestMsg, resp, requestMsg.header()->id); | |||
} | } | |||
} // namespace mongo | } // namespace mongo | |||
//#include "bsonobj.h" | //#include "bsonobj.h" | |||
#include "instance.h" | #include "instance.h" | |||
namespace mongo { | namespace mongo { | |||
/* object reply helper. */ | /* object reply helper. */ | |||
skipping to change at line 272 | skipping to change at line 273 | |||
QueryResult *qr = msgdata; | QueryResult *qr = msgdata; | |||
qr->_resultFlags() = queryResultFlags; | qr->_resultFlags() = queryResultFlags; | |||
qr->len = b.len(); | qr->len = b.len(); | |||
qr->setOperation(opReply); | qr->setOperation(opReply); | |||
qr->cursorId = 0; | qr->cursorId = 0; | |||
qr->startingFrom = 0; | qr->startingFrom = 0; | |||
qr->nReturned = 1; | qr->nReturned = 1; | |||
Message *resp = new Message(); | Message *resp = new Message(); | |||
resp->setData(msgdata, true); // transport will free | resp->setData(msgdata, true); // transport will free | |||
dbresponse.response = resp; | dbresponse.response = resp; | |||
dbresponse.responseTo = m.data->id; | dbresponse.responseTo = m.header()->id; | |||
} | } | |||
} // namespace mongo | } // namespace mongo | |||
End of changes. 4 change blocks. | ||||
5 lines changed or deleted | 6 lines changed or added | |||
diskloc.h | diskloc.h | |||
---|---|---|---|---|
skipping to change at line 25 | skipping to change at line 25 | |||
*/ | */ | |||
/* storage.h | /* storage.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 | |||
namespace mongo { | #include "jsobj.h" | |||
#pragma pack(1) | namespace mongo { | |||
class Record; | class Record; | |||
class DeletedRecord; | class DeletedRecord; | |||
class Extent; | class Extent; | |||
class BtreeBucket; | class BtreeBucket; | |||
class BSONObj; | ||||
class MongoDataFile; | class MongoDataFile; | |||
#pragma pack(1) | ||||
class DiskLoc { | class DiskLoc { | |||
int fileNo; /* this will be volume, file #, etc. */ | int fileNo; /* this will be volume, file #, etc. */ | |||
int ofs; | int ofs; | |||
public: | public: | |||
// Note: MaxFiles imposes a limit of about 32TB of data per process | // Note: MaxFiles imposes a limit of about 32TB of data per process | |||
enum SentinelValues { MaxFiles=16000, NullOfs = -1 }; | enum SentinelValues { MaxFiles=16000, NullOfs = -1 }; | |||
int a() const { | int a() const { | |||
return fileNo; | return fileNo; | |||
} | } | |||
skipping to change at line 90 | skipping to change at line 90 | |||
string toString() const { | string toString() const { | |||
if ( isNull() ) | if ( isNull() ) | |||
return "null"; | return "null"; | |||
stringstream ss; | stringstream ss; | |||
ss << hex << fileNo << ':' << ofs; | ss << hex << fileNo << ':' << ofs; | |||
return ss.str(); | return ss.str(); | |||
} | } | |||
operator string() const { return toString(); } | operator string() const { return toString(); } | |||
BSONObj toBSONObj() const { | ||||
return BSON( "file" << fileNo << "offset" << ofs ); | ||||
} | ||||
int& GETOFS() { | int& GETOFS() { | |||
return ofs; | 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; | fileNo=a; | |||
ofs=b; | ofs=b; | |||
} | } | |||
End of changes. 5 change blocks. | ||||
3 lines changed or deleted | 7 lines changed or added | |||
engine_java.h | engine_java.h | |||
---|---|---|---|---|
skipping to change at line 25 | skipping to change at line 25 | |||
* limitations under the License. | * limitations under the License. | |||
*/ | */ | |||
/* this file contains code to call into java (into the 10gen sandbox) from inside the database */ | /* this file contains code to call into java (into the 10gen sandbox) from inside the database */ | |||
#pragma once | #pragma once | |||
#include "../pch.h" | #include "../pch.h" | |||
#include <jni.h> | #include <jni.h> | |||
#include <boost/thread/tss.hpp> | ||||
#include <errno.h> | #include <errno.h> | |||
#include <sys/types.h> | #include <sys/types.h> | |||
#if !defined(_WIN32) | #if !defined(_WIN32) | |||
#include <dirent.h> | #include <dirent.h> | |||
#endif | #endif | |||
#include "../db/jsobj.h" | #include "../db/jsobj.h" | |||
#include "engine.h" | #include "engine.h" | |||
End of changes. 1 change blocks. | ||||
1 lines changed or deleted | 0 lines changed or added | |||
file.h | file.h | |||
---|---|---|---|---|
skipping to change at line 140 | skipping to change at line 140 | |||
if( is_open() ) ::close(fd); | if( is_open() ) ::close(fd); | |||
fd = -1; | fd = -1; | |||
} | } | |||
#ifndef O_NOATIME | #ifndef O_NOATIME | |||
#define O_NOATIME 0 | #define O_NOATIME 0 | |||
#define lseek64 lseek | #define lseek64 lseek | |||
#endif | #endif | |||
void open(const char *filename, bool readOnly=false ) { | void open(const char *filename, bool readOnly=false ) { | |||
fd = ::open(filename, O_CREAT | ( readOnly ? 0 : O_RDWR ) | O_NOATI | fd = ::open(filename, | |||
ME, S_IRUSR | S_IWUSR); | O_CREAT | ( readOnly ? 0 : ( O_RDWR | O_NOATIME ) ) , | |||
S_IRUSR | S_IWUSR); | ||||
if ( fd <= 0 ) { | if ( fd <= 0 ) { | |||
out() << "couldn't open " << filename << ' ' << errnoWithDescri ption() << endl; | out() << "couldn't open " << filename << ' ' << errnoWithDescri ption() << endl; | |||
return; | return; | |||
} | } | |||
_bad = false; | _bad = false; | |||
} | } | |||
void write(fileofs o, const char *data, unsigned len) { | void write(fileofs o, const char *data, unsigned len) { | |||
lseek64(fd, o, SEEK_SET); | lseek64(fd, o, SEEK_SET); | |||
err( ::write(fd, data, len) == (int) len ); | err( ::write(fd, data, len) == (int) len ); | |||
} | } | |||
End of changes. 1 change blocks. | ||||
2 lines changed or deleted | 3 lines changed or added | |||
file_allocator.h | file_allocator.h | |||
---|---|---|---|---|
skipping to change at line 41 | skipping to change at line 41 | |||
/* Handles allocation of contiguous files on disk. Allocation may be | /* Handles allocation of contiguous files on disk. Allocation may be | |||
requested asynchronously or synchronously. | requested asynchronously or synchronously. | |||
*/ | */ | |||
class FileAllocator { | class FileAllocator { | |||
/* The public functions may not be called concurrently. The alloca tion | /* The public functions may not be called concurrently. The alloca tion | |||
functions may be called multiple times per file, but only the fi rst | functions may be called multiple times per file, but only the fi rst | |||
size specified per file will be used. | size specified per file will be used. | |||
*/ | */ | |||
public: | public: | |||
#if !defined(_WIN32) | #if !defined(_WIN32) | |||
FileAllocator() : failed_() {} | FileAllocator() : pendingMutex_("FileAllocator"), failed_() {} | |||
#endif | #endif | |||
void start() { | void start() { | |||
#if !defined(_WIN32) | #if !defined(_WIN32) | |||
Runner r( *this ); | Runner r( *this ); | |||
boost::thread t( r ); | boost::thread t( r ); | |||
#endif | #endif | |||
} | } | |||
// May be called if file exists. If file exists, or its allocation has | // May be called if file exists. If file exists, or its allocation has | |||
// been requested, size is updated to match existing file size. | // been requested, size is updated to match existing file size. | |||
void requestAllocation( const string &name, long &size ) { | void requestAllocation( const string &name, long &size ) { | |||
End of changes. 1 change blocks. | ||||
1 lines changed or deleted | 1 lines changed or added | |||
framework.h | framework.h | |||
---|---|---|---|---|
skipping to change at line 35 | skipping to change at line 35 | |||
#define ASSERT_EXCEPTION(a,b) \ | #define ASSERT_EXCEPTION(a,b) \ | |||
try { \ | try { \ | |||
a; \ | a; \ | |||
mongo::regression::assert_fail( #a , __FILE__ , __LINE__ ); \ | mongo::regression::assert_fail( #a , __FILE__ , __LINE__ ); \ | |||
} catch ( b& ){ \ | } catch ( b& ){ \ | |||
mongo::regression::assert_pass(); \ | mongo::regression::assert_pass(); \ | |||
} | } | |||
#define ASSERT_EQUALS(a,b) (mongo::regression::MyAsserts( #a , #b , __FILE_ _ , __LINE__ ) ).ae( (a) , (b) ) | #define ASSERT_EQUALS(a,b) (mongo::regression::MyAsserts( #a , #b , __FILE_ _ , __LINE__ ) ).ae( (a) , (b) ) | |||
#define ASSERT_NOT_EQUALS(a,b) (mongo::regression::MyAsserts( #a , #b , __F | ||||
ILE__ , __LINE__ ) ).nae( (a) , (b) ) | ||||
#define ASSERT(x) (void)( (!(!(x))) ? mongo::regression::assert_pass() : mo ngo::regression::assert_fail( #x , __FILE__ , __LINE__ ) ) | #define ASSERT(x) (void)( (!(!(x))) ? mongo::regression::assert_pass() : mo ngo::regression::assert_fail( #x , __FILE__ , __LINE__ ) ) | |||
#define FAIL(x) mongo::regression::fail( #x , __FILE__ , __LINE__ ) | #define FAIL(x) mongo::regression::fail( #x , __FILE__ , __LINE__ ) | |||
#include "../db/instance.h" | #include "../db/instance.h" | |||
namespace mongo { | namespace mongo { | |||
namespace regression { | namespace regression { | |||
class Result; | class Result; | |||
skipping to change at line 162 | skipping to change at line 164 | |||
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> | ||||
void nae( A a , B b ){ | ||||
_gotAssert(); | ||||
if ( a != b ) | ||||
return; | ||||
printLocation(); | ||||
MyAssertionException * e = getBase(); | ||||
e->ss << a << " == " << b << endl; | ||||
log() << e->ss.str() << endl; | ||||
throw e; | ||||
} | ||||
void printLocation(); | void printLocation(); | |||
private: | private: | |||
void _gotAssert(); | void _gotAssert(); | |||
MyAssertionException * getBase(); | MyAssertionException * getBase(); | |||
string _aexp; | string _aexp; | |||
string _bexp; | string _bexp; | |||
End of changes. 2 change blocks. | ||||
0 lines changed or deleted | 17 lines changed or added | |||
goodies.h | goodies.h | |||
---|---|---|---|---|
// goodies.h | // @file goodies.h | |||
// miscellaneous junk | // miscellaneous junk | |||
/* 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 "../bson/util/misc.h" | #include "../bson/util/misc.h" | |||
#include "concurrency/mutex.h" | ||||
namespace mongo { | namespace mongo { | |||
void setThreadName(const char * name); | ||||
template<class T> | ||||
inline string ToString(const T& t) { | ||||
stringstream s; | ||||
s << t; | ||||
return s.str(); | ||||
} | ||||
#if !defined(_WIN32) && !defined(NOEXECINFO) && !defined(__freebsd__) && !d efined(__sun__) | #if !defined(_WIN32) && !defined(NOEXECINFO) && !defined(__freebsd__) && !d efined(__sun__) | |||
} // namespace mongo | } // namespace mongo | |||
#include <pthread.h> | #include <pthread.h> | |||
#include <execinfo.h> | #include <execinfo.h> | |||
namespace mongo { | namespace mongo { | |||
inline pthread_t GetCurrentThreadId() { | inline pthread_t GetCurrentThreadId() { | |||
skipping to change at line 138 | skipping to change at line 148 | |||
bool operator<=(WrappingInt r) { | bool operator<=(WrappingInt r) { | |||
// platform dependent | // platform dependent | |||
int df = (r.x - x); | int df = (r.x - x); | |||
return df >= 0; | return df >= 0; | |||
} | } | |||
bool operator>(WrappingInt r) { | bool operator>(WrappingInt r) { | |||
return !(r<=*this); | return !(r<=*this); | |||
} | } | |||
}; | }; | |||
} // namespace mongo | ||||
#include <ctime> | ||||
namespace mongo { | ||||
inline void time_t_to_Struct(time_t t, struct tm * buf , bool local = f alse ) { | inline void time_t_to_Struct(time_t t, struct tm * buf , bool local = f alse ) { | |||
#if defined(_WIN32) | #if defined(_WIN32) | |||
if ( local ) | if ( local ) | |||
localtime_s( buf , &t ); | localtime_s( buf , &t ); | |||
else | else | |||
gmtime_s(buf, &t); | gmtime_s(buf, &t); | |||
#else | #else | |||
if ( local ) | if ( local ) | |||
localtime_r(&t, buf); | localtime_r(&t, buf); | |||
else | else | |||
skipping to change at line 186 | skipping to change at line 190 | |||
#define MONGO_ctime _ctime_is_not_threadsafe_ | #define MONGO_ctime _ctime_is_not_threadsafe_ | |||
#define ctime MONGO_ctime | #define ctime MONGO_ctime | |||
#if defined(_WIN32) || defined(__sunos__) | #if defined(_WIN32) || defined(__sunos__) | |||
inline void sleepsecs(int s) { | inline void sleepsecs(int s) { | |||
boost::xtime xt; | boost::xtime xt; | |||
boost::xtime_get(&xt, boost::TIME_UTC); | boost::xtime_get(&xt, boost::TIME_UTC); | |||
xt.sec += s; | xt.sec += s; | |||
boost::thread::sleep(xt); | boost::thread::sleep(xt); | |||
} | } | |||
inline void sleepmillis(int s) { | inline void sleepmillis(long long s) { | |||
boost::xtime xt; | boost::xtime xt; | |||
boost::xtime_get(&xt, boost::TIME_UTC); | boost::xtime_get(&xt, boost::TIME_UTC); | |||
xt.sec += ( s / 1000 ); | xt.sec += (int)( s / 1000 ); | |||
xt.nsec += ( s % 1000 ) * 1000000; | xt.nsec += (int)(( s % 1000 ) * 1000000); | |||
if ( xt.nsec >= 1000000000 ) { | if ( xt.nsec >= 1000000000 ) { | |||
xt.nsec -= 1000000000; | xt.nsec -= 1000000000; | |||
xt.sec++; | xt.sec++; | |||
} | } | |||
boost::thread::sleep(xt); | boost::thread::sleep(xt); | |||
} | } | |||
inline void sleepmicros(int s) { | inline void sleepmicros(long long s) { | |||
if ( s <= 0 ) | if ( s <= 0 ) | |||
return; | return; | |||
boost::xtime xt; | boost::xtime xt; | |||
boost::xtime_get(&xt, boost::TIME_UTC); | boost::xtime_get(&xt, boost::TIME_UTC); | |||
xt.sec += ( s / 1000000 ); | xt.sec += (int)( s / 1000000 ); | |||
xt.nsec += ( s % 1000000 ) * 1000; | xt.nsec += (int)(( s % 1000000 ) * 1000); | |||
if ( xt.nsec >= 1000000000 ) { | if ( xt.nsec >= 1000000000 ) { | |||
xt.nsec -= 1000000000; | xt.nsec -= 1000000000; | |||
xt.sec++; | xt.sec++; | |||
} | } | |||
boost::thread::sleep(xt); | boost::thread::sleep(xt); | |||
} | } | |||
#else | #else | |||
inline void sleepsecs(int s) { | inline void sleepsecs(int s) { | |||
struct timespec t; | struct timespec t; | |||
t.tv_sec = s; | t.tv_sec = s; | |||
t.tv_nsec = 0; | t.tv_nsec = 0; | |||
if ( nanosleep( &t , 0 ) ){ | if ( nanosleep( &t , 0 ) ){ | |||
cout << "nanosleep failed" << endl; | cout << "nanosleep failed" << endl; | |||
} | } | |||
} | } | |||
inline void sleepmicros(int s) { | inline void sleepmicros(long long s) { | |||
if ( s <= 0 ) | if ( s <= 0 ) | |||
return; | return; | |||
struct timespec t; | struct timespec t; | |||
t.tv_sec = (int)(s / 1000000); | t.tv_sec = (int)(s / 1000000); | |||
t.tv_nsec = 1000 * ( s % 1000000 ); | t.tv_nsec = 1000 * ( s % 1000000 ); | |||
struct timespec out; | struct timespec out; | |||
if ( nanosleep( &t , &out ) ){ | if ( nanosleep( &t , &out ) ){ | |||
cout << "nanosleep failed" << endl; | cout << "nanosleep failed" << endl; | |||
} | } | |||
} | } | |||
inline void sleepmillis(int s) { | inline void sleepmillis(long long s) { | |||
sleepmicros( s * 1000 ); | sleepmicros( s * 1000 ); | |||
} | } | |||
#endif | #endif | |||
// note this wraps | // note this wraps | |||
inline int tdiff(unsigned told, unsigned tnew) { | inline int tdiff(unsigned told, unsigned tnew) { | |||
return WrappingInt::diff(tnew, told); | return WrappingInt::diff(tnew, told); | |||
} | } | |||
inline unsigned curTimeMillis() { | inline unsigned curTimeMillis() { | |||
boost::xtime xt; | boost::xtime xt; | |||
skipping to change at line 268 | skipping to change at line 272 | |||
} | } | |||
// measures up to 1024 seconds. or, 512 seconds with tdiff that is... | // measures up to 1024 seconds. or, 512 seconds with tdiff that is... | |||
inline unsigned curTimeMicros() { | inline unsigned curTimeMicros() { | |||
boost::xtime xt; | boost::xtime xt; | |||
boost::xtime_get(&xt, boost::TIME_UTC); | boost::xtime_get(&xt, boost::TIME_UTC); | |||
unsigned t = xt.nsec / 1000; | unsigned t = xt.nsec / 1000; | |||
unsigned secs = xt.sec % 1024; | unsigned secs = xt.sec % 1024; | |||
return secs*1000000 + t; | return secs*1000000 + t; | |||
} | } | |||
using namespace boost; | ||||
extern bool __destroyingStatics; | ||||
// 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 | ||||
// to true before the global static variables are destroyed. | ||||
class StaticObserver : boost::noncopyable { | ||||
public: | ||||
~StaticObserver() { __destroyingStatics = true; } | ||||
}; | ||||
// On pthread systems, it is an error to destroy a mutex while held. S | ||||
tatic global | ||||
// mutexes may be held upon shutdown in our implementation, and this wa | ||||
y we avoid | ||||
// destroying them. | ||||
class mutex : boost::noncopyable { | ||||
public: | ||||
mutex() { _m = new boost::mutex(); } | ||||
~mutex() { | ||||
if( !__destroyingStatics ) { | ||||
delete _m; | ||||
} | ||||
} | ||||
class scoped_lock : boost::noncopyable { | ||||
public: | ||||
scoped_lock( mongo::mutex &m ) : _l( m.boost() ) {} | ||||
boost::mutex::scoped_lock &boost() { return _l; } | ||||
private: | ||||
boost::mutex::scoped_lock _l; | ||||
}; | ||||
private: | ||||
boost::mutex &boost() { return *_m; } | ||||
boost::mutex *_m; | ||||
}; | ||||
typedef mongo::mutex::scoped_lock scoped_lock; | ||||
typedef boost::recursive_mutex::scoped_lock recursive_scoped_lock; | ||||
// simple scoped timer | // simple scoped timer | |||
class Timer { | class Timer { | |||
public: | public: | |||
Timer() { | Timer() { | |||
reset(); | reset(); | |||
} | } | |||
Timer( unsigned long long start ) { | Timer( unsigned long long start ) { | |||
old = start; | old = start; | |||
} | } | |||
skipping to change at line 368 | skipping to change at line 335 | |||
} | } | |||
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); | |||
if ( b > a ) return false; | if ( b > a ) return false; | |||
return strcmp(p + a - b, suffix) == 0; | return strcmp(p + a - b, suffix) == 0; | |||
} | } | |||
} // namespace mongo | ||||
#include "boost/detail/endian.hpp" | ||||
namespace mongo { | ||||
inline unsigned long swapEndian(unsigned long x) { | inline unsigned long swapEndian(unsigned long x) { | |||
return | return | |||
((x & 0xff) << 24) | | ((x & 0xff) << 24) | | |||
((x & 0xff00) << 8) | | ((x & 0xff00) << 8) | | |||
((x & 0xff0000) >> 8) | | ((x & 0xff0000) >> 8) | | |||
((x & 0xff000000) >> 24); | ((x & 0xff000000) >> 24); | |||
} | } | |||
#if defined(BOOST_LITTLE_ENDIAN) | #if defined(BOOST_LITTLE_ENDIAN) | |||
inline unsigned long fixEndian(unsigned long x) { | inline unsigned long fixEndian(unsigned long x) { | |||
skipping to change at line 442 | skipping to change at line 403 | |||
} | } | |||
v = new T(i); | v = new T(i); | |||
_val.reset( v ); | _val.reset( v ); | |||
} | } | |||
private: | private: | |||
T _default; | T _default; | |||
boost::thread_specific_ptr<T> _val; | boost::thread_specific_ptr<T> _val; | |||
}; | }; | |||
class ProgressMeter { | class ProgressMeter : boost::noncopyable { | |||
public: | public: | |||
ProgressMeter( long long total , int secondsBetween = 3 , int check Interval = 100 ){ | ProgressMeter( long long total , int secondsBetween = 3 , int check Interval = 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 ){ | void reset( long long total , int secondsBetween = 3 , int checkInt erval = 100 ){ | |||
skipping to change at line 509 | skipping to change at line 470 | |||
return _hits; | return _hits; | |||
} | } | |||
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 { | ||||
return this == &other; | ||||
} | ||||
private: | private: | |||
bool _active; | bool _active; | |||
long long _total; | long long _total; | |||
int _secondsBetween; | int _secondsBetween; | |||
int _checkInterval; | int _checkInterval; | |||
long long _done; | long long _done; | |||
long long _hits; | long long _hits; | |||
int _lastTime; | int _lastTime; | |||
}; | }; | |||
class ProgressMeterHolder : boost::noncopyable { | ||||
public: | ||||
ProgressMeterHolder( ProgressMeter& pm ) | ||||
: _pm( pm ){ | ||||
} | ||||
~ProgressMeterHolder(){ | ||||
_pm.finished(); | ||||
} | ||||
ProgressMeter* operator->(){ | ||||
return &_pm; | ||||
} | ||||
bool hit( int n = 1 ){ | ||||
return _pm.hit( n ); | ||||
} | ||||
void finished(){ | ||||
_pm.finished(); | ||||
} | ||||
bool operator==( const ProgressMeter& other ){ | ||||
return _pm == other; | ||||
} | ||||
private: | ||||
ProgressMeter& _pm; | ||||
}; | ||||
class TicketHolder { | class TicketHolder { | |||
public: | public: | |||
TicketHolder( int num ){ | 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; | |||
} | } | |||
skipping to change at line 713 | skipping to change at line 708 | |||
s1++; s2++; | s1++; s2++; | |||
} | } | |||
if ( *s1 ) | if ( *s1 ) | |||
return 1; | return 1; | |||
if ( *s2 ) | if ( *s2 ) | |||
return -1; | return -1; | |||
return 0; | return 0; | |||
} | } | |||
/** A generic pointer type for function arguments. | ||||
* It will convert from any pointer type except auto_ptr. | ||||
* Semantics are the same as passing the pointer returned from get() | ||||
* const ptr<T> => T * const | ||||
* ptr<const T> => T const * or const T* | ||||
*/ | ||||
template <typename T> | ||||
struct ptr{ | ||||
ptr() : _p(NULL) {} | ||||
// convert to ptr<T> | ||||
ptr(T* p) : _p(p) {} // needed for NULL | ||||
template<typename U> 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::scoped_ptr<U>& p) : _p(p.get( | ||||
)) {} | ||||
//template<typename U> ptr(const auto_ptr<U>& p) : _p(p.get()) {} | ||||
// assign to ptr<T> | ||||
ptr& operator= (T* p) { _p = p; return *this; } // needed for NULL | ||||
template<typename U> ptr& operator= (U* p) { _p = p; return *this; | ||||
} | ||||
template<typename U> ptr& operator= (const ptr<U>& p) { _p = p; ret | ||||
urn *this; } | ||||
template<typename U> ptr& operator= (const boost::shared_ptr<U>& p) | ||||
{ _p = p.get(); return *this; } | ||||
template<typename U> ptr& operator= (const boost::scoped_ptr<U>& p) | ||||
{ _p = p.get(); return *this; } | ||||
//template<typename U> ptr& operator= (const auto_ptr<U>& p) { _p = | ||||
p.get(); return *this; } | ||||
// use | ||||
T* operator->() const { return _p; } | ||||
T& operator*() const { return *_p; } | ||||
// convert from ptr<T> | ||||
operator T* () const { return _p; } | ||||
private: | ||||
T* _p; | ||||
}; | ||||
/** Hmmmm */ | ||||
using namespace boost; | ||||
} // namespace mongo | } // namespace mongo | |||
End of changes. 17 change blocks. | ||||
64 lines changed or deleted | 103 lines changed or added | |||
hashtab.h | hashtab.h | |||
---|---|---|---|---|
skipping to change at line 111 | skipping to change at line 111 | |||
HashTable(PTR buf, int buflen, const char *_name) : name(_name) { | HashTable(PTR 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; | |||
assert( sizeof(Node) == 628 ); | if ( sizeof(Node) != 628 ){ | |||
//out() << "HashTable() " << _name << " sizeof(node):" << sizeo | out() << "HashTable() " << _name << " sizeof(node):" << siz | |||
f(Node) << " n:" << n << endl; | eof(Node) << " n:" << n << " sizeof(Key): " << sizeof(Key) << " sizeof(Type | |||
):" << sizeof(Type) << endl; | ||||
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; | |||
} | } | |||
End of changes. 1 change blocks. | ||||
3 lines changed or deleted | 7 lines changed or added | |||
health.h | health.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 { | |||
/* throws */ | ||||
bool requestHeartbeat(string setname, string memberFullName, BSONObj& r | ||||
esult, int myConfigVersion, int& theirConfigVersion); | ||||
struct HealthOptions { | struct HealthOptions { | |||
HealthOptions() { | HealthOptions() { | |||
heartbeatSleepMillis = 2000; | heartbeatSleepMillis = 2000; | |||
heartbeatTimeoutMillis = 10000; | heartbeatTimeoutMillis = 10000; | |||
heartbeatConnRetries = 3; | heartbeatConnRetries = 3; | |||
} | } | |||
bool isDefault() const { | bool isDefault() const { | |||
return !( heartbeatSleepMillis != 2000 || heartbeatTimeoutMilli s != 10000 || heartbeatConnRetries != 3 ); | return !( heartbeatSleepMillis != 2000 || heartbeatTimeoutMilli s != 10000 || heartbeatConnRetries != 3 ); | |||
} | } | |||
End of changes. 1 change blocks. | ||||
0 lines changed or deleted | 4 lines changed or added | |||
hostandport.h | hostandport.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 "sock.h" | #include "sock.h" | |||
#include "../db/cmdline.h" | #include "../db/cmdline.h" | |||
#include "mongoutils/str.h" | ||||
namespace mongo { | namespace mongo { | |||
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) { } | |||
HostAndPort(string h, int p = -1) : _host(h), _port(p) { } | /** From a string hostname[:portnumber] | |||
Throws user assertion if bad config string or bad port #. | ||||
*/ | ||||
HostAndPort(string s); | ||||
/** @param p port number. -1 is ok to use default. */ | ||||
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); | |||
} | } | |||
static HostAndPort fromString(string s) { | ||||
const char *p = s.c_str(); | ||||
uassert(13110, "HostAndPort: bad config string", *p); | ||||
const char *colon = strchr(p, ':'); | ||||
HostAndPort m; | ||||
if( colon ) { | ||||
int port = atoi(colon+1); | ||||
uassert(13095, "HostAndPort: bad port #", port > 0); | ||||
return HostAndPort(string(p,colon-p),port); | ||||
} | ||||
// no port specified. | ||||
return HostAndPort(p); | ||||
} | ||||
bool operator<(const HostAndPort& r) const { return _host < r._host || (_host==r._host&&_port<r._port); } | bool operator<(const HostAndPort& r) const { return _host < r._host || (_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; | bool isSelf() const; | |||
bool isLocalHost() const; | bool isLocalHost() const; | |||
// @returns host:port | // @returns host:port | |||
string toString() const; | string toString() const; | |||
skipping to change at line 65 | skipping to change at line 60 | |||
/* 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; | bool isSelf() const; | |||
bool isLocalHost() const; | bool isLocalHost() const; | |||
// @returns host:port | // @returns host:port | |||
string toString() const; | string toString() const; | |||
string host() const { return _host; } | string host() const { return _host; } | |||
int port() const { return _port >= 0 ? _port : cmdLine.port; } | int port() const { return _port >= 0 ? _port : cmdLine.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 share a common starting prefix */ | /** returns true if strings seem to be the same hostname. | |||
inline bool sameStart(const char *p, const char *q) { | "nyc1" and "nyc1.acme.com" are treated as the same. | |||
while( 1 ) { | in fact "nyc1.foo.com" and "nyc1.acme.com" are treated the same - | |||
if( *p == 0 || *q == 0 ) | we oly look up to the first period. | |||
return true; | */ | |||
if( *p != *q ) | inline bool sameHostname(const string& a, const string& b) { | |||
break; | return str::before(a, '.') == str::before(b, '.'); | |||
p++; q++; | ||||
} | ||||
return false; | ||||
} | } | |||
inline bool HostAndPort::isSelf() const { | inline bool HostAndPort::isSelf() const { | |||
int p = _port == -1 ? CmdLine::DefaultDBPort : _port; | int p = _port == -1 ? CmdLine::DefaultDBPort : _port; | |||
if( p != cmdLine.port ) | if( p != cmdLine.port ) | |||
return false; | return false; | |||
assert( _host != "localhost" && _host != "127.0.0.1" ); | ||||
return sameStart(getHostName().c_str(), _host.c_str()); | return sameHostname(getHostName(), _host) || isLocalHost(); | |||
} | } | |||
inline string HostAndPort::toString() const { | inline string HostAndPort::toString() const { | |||
stringstream ss; | stringstream ss; | |||
ss << _host; | ss << _host; | |||
if( _port != -1 ) ss << ':' << _port; | if( _port != -1 ) ss << ':' << _port; | |||
return ss.str(); | return ss.str(); | |||
} | } | |||
inline bool HostAndPort::isLocalHost() const { | inline bool HostAndPort::isLocalHost() const { | |||
return _host == "localhost" || _host == "127.0.0.1"; | return _host == "localhost" || _host == "127.0.0.1" || _host == ":: | |||
1"; | ||||
} | ||||
inline HostAndPort::HostAndPort(string s) { | ||||
const char *p = s.c_str(); | ||||
uassert(13110, "HostAndPort: bad config string", *p); | ||||
const char *colon = strrchr(p, ':'); | ||||
if( colon ) { | ||||
int port = atoi(colon+1); | ||||
uassert(13095, "HostAndPort: bad port #", port > 0); | ||||
_host = string(p,colon-p); | ||||
_port = port; | ||||
} | ||||
else { | ||||
// no port specified. | ||||
_host = p; | ||||
_port = -1; | ||||
} | ||||
} | } | |||
} | } | |||
End of changes. 8 change blocks. | ||||
28 lines changed or deleted | 39 lines changed or added | |||
html.h | html.h | |||
---|---|---|---|---|
skipping to change at line 12 | skipping to change at line 12 | |||
#pragma once | #pragma once | |||
/* Things in the mongoutils namespace | /* Things in the mongoutils namespace | |||
(1) are not database specific, rather, true utilities | (1) are not database specific, rather, true utilities | |||
(2) are cross platform | (2) are cross platform | |||
(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 | |||
*/ | */ | |||
/* Copyright 2010 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. | ||||
*/ | ||||
#include <sstream> | #include <sstream> | |||
namespace mongoutils { | namespace mongoutils { | |||
namespace html { | namespace html { | |||
using namespace std; | using namespace std; | |||
inline string _end() { return "</body></html>"; } | ||||
inline string _table() { return "</table>\n\n"; } | ||||
inline string _tr() { return "</tr>\n"; } | ||||
inline string tr() { return "<tr>"; } | ||||
inline string tr(string a, string b) { | ||||
stringstream ss; | ||||
ss << "<tr><td>" << a << "</td><td>" << b << "</td></tr>\n"; | ||||
return ss.str(); | ||||
} | ||||
template <class T> | ||||
inline string td(T x) { | ||||
stringstream ss; | ||||
ss << "<td>" << x << "</td>"; | ||||
return ss.str(); | ||||
} | ||||
inline string td(string x) { | ||||
return "<td>" + x + "</td>"; | ||||
} | ||||
inline string table(const char *headers[] = 0, bool border = true) | ||||
{ | ||||
stringstream ss; | ||||
ss << "\n<table " | ||||
<< (border?"border=1 ":"") | ||||
<< "cellpadding=2 cellspacing=0>\n"; | ||||
if( headers ) { | ||||
ss << "<tr>"; | ||||
while( *headers ) { | ||||
ss << "<th>" << *headers << "</th>"; | ||||
headers++; | ||||
} | ||||
ss << "</tr>\n"; | ||||
} | ||||
return ss.str(); | ||||
} | ||||
inline string start(string title) { | ||||
stringstream ss; | ||||
ss << "<html><head>\n<title>"; | ||||
ss << title; | ||||
ss << "</title>\n"; | ||||
ss << "<style type=\"text/css\" media=\"screen\">" | ||||
"body { font-family: helvetica, arial, san-serif }\n" | ||||
"table { border-collapse:collapse; border-color:#999; margi | ||||
n-top:.5em }\n" | ||||
"th { background-color:#bbb; color:#000 }\n" | ||||
"td,th { padding:.25em }\n" | ||||
"</style>\n"; | ||||
ss << "</head>\n<body>\n"; | ||||
return ss.str(); | ||||
} | ||||
inline string red(string contentHtml, bool color=true) { | ||||
if( !color ) return contentHtml; | ||||
stringstream ss; | ||||
ss << "<span style=\"color:#A00;\">" << contentHtml << "</span> | ||||
"; | ||||
return ss.str(); | ||||
} | ||||
inline string blue(string contentHtml, bool color=true) { | ||||
if( !color ) return contentHtml; | ||||
stringstream ss; | ||||
ss << "<span style=\"color:#00A;\">" << contentHtml << "</span> | ||||
"; | ||||
return ss.str(); | ||||
} | ||||
inline string yellow(string contentHtml, bool color=true) { | ||||
if( !color ) return contentHtml; | ||||
stringstream ss; | ||||
ss << "<span style=\"color:#A80;\">" << contentHtml << "</span> | ||||
"; | ||||
return ss.str(); | ||||
} | ||||
inline string green(string contentHtml, bool color=true) { | ||||
if( !color ) return contentHtml; | ||||
stringstream ss; | ||||
ss << "<span style=\"color:#0A0;\">" << contentHtml << "</span> | ||||
"; | ||||
return ss.str(); | ||||
} | ||||
inline string p(string contentHtml) { | inline string p(string contentHtml) { | |||
stringstream ss; | stringstream ss; | |||
ss << "<p>" << contentHtml << "</p>\n"; | ss << "<p>" << contentHtml << "</p>\n"; | |||
return ss.str(); | return ss.str(); | |||
} | } | |||
inline string h2(string contentHtml) { | ||||
stringstream ss; | ||||
ss << "<h2>" << contentHtml << "</h2>\n"; | ||||
return ss.str(); | ||||
} | ||||
/* does NOT escape the strings. */ | /* does NOT escape the strings. */ | |||
inline string a(string href, string title="", string contentHtml = "") { | inline string a(string href, string title="", string contentHtml = "") { | |||
stringstream ss; | stringstream ss; | |||
ss << "<a"; | ss << "<a"; | |||
if( !href.empty() ) ss << " href=\"" << href << '"'; | if( !href.empty() ) ss << " href=\"" << href << '"'; | |||
if( !title.empty() ) ss << " title=\"" << title << '"'; | if( !title.empty() ) ss << " title=\"" << title << '"'; | |||
ss << '>'; | ss << '>'; | |||
if( !contentHtml.empty() ) { | if( !contentHtml.empty() ) { | |||
ss << contentHtml << "</a>"; | ss << contentHtml << "</a>"; | |||
} | } | |||
End of changes. 3 change blocks. | ||||
0 lines changed or deleted | 106 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 { | |||
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, BSONObjSetDefaultOrder &k eys ) const = 0; | |||
virtual auto_ptr<Cursor> newCursor( const BSONObj& query , const BS ONObj& 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; } | |||
End of changes. 2 change blocks. | ||||
1 lines changed or deleted | 2 lines changed or added | |||
instance.h | instance.h | |||
---|---|---|---|---|
skipping to change at line 40 | skipping to change at line 40 | |||
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) { } | 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; | |||
End of changes. 1 change blocks. | ||||
1 lines changed or deleted | 1 lines changed or added | |||
lasterror.h | lasterror.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/thread/tss.hpp> | ||||
#undef assert | ||||
#define assert MONGO_assert | ||||
namespace mongo { | namespace mongo { | |||
class BSONObjBuilder; | class BSONObjBuilder; | |||
class Message; | class Message; | |||
struct LastError { | struct LastError { | |||
int code; | int code; | |||
string msg; | string msg; | |||
enum UpdatedExistingType { NotUpdate, True, False } updatedExisting ; | enum UpdatedExistingType { NotUpdate, True, False } updatedExisting ; | |||
/* todo: nObjects should be 64 bit */ | /* todo: nObjects should be 64 bit */ | |||
long long nObjects; | long long nObjects; | |||
skipping to change at line 79 | skipping to change at line 75 | |||
extern class LastErrorHolder { | extern class LastErrorHolder { | |||
public: | public: | |||
LastErrorHolder() : _id( 0 ) {} | LastErrorHolder() : _id( 0 ) {} | |||
LastError * get( bool create = false ); | LastError * get( bool create = false ); | |||
LastError * _get( bool create = false ); // may return a disabled L astError | 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. */ | ||||
void initThread(); | ||||
/** | /** | |||
* id of 0 means should use thread local management | * id of 0 means should use thread local management | |||
*/ | */ | |||
void setID( int id ); | void setID( int id ); | |||
int getID(); | int getID(); | |||
void remove( int id ); | void remove( int id ); | |||
void release(); | void release(); | |||
/** when db receives a message/request, call this */ | /** when db receives a message/request, call this */ | |||
skipping to change at line 110 | skipping to change at line 109 | |||
time_t time; | time_t time; | |||
LastError *lerr; | LastError *lerr; | |||
}; | }; | |||
static mongo::mutex _idsmutex; | static mongo::mutex _idsmutex; | |||
map<int,Status> _ids; | map<int,Status> _ids; | |||
} lastError; | } lastError; | |||
inline void raiseError(int code , const char *msg) { | inline void raiseError(int code , const char *msg) { | |||
LastError *le = lastError.get(); | LastError *le = lastError.get(); | |||
if ( le == 0 ) { | if ( le == 0 ) { | |||
DEV log() << "warning: lastError==0 can't report:" << msg << '\ | /* might be intentional (non-user thread) */ | |||
n'; | DEV log() << "warning dev: lastError==0 won't report:" << msg < | |||
< endl; | ||||
} else if ( le->disabled ) { | } else if ( le->disabled ) { | |||
log() << "lastError disabled, can't report: " << msg << endl; | log() << "lastError disabled, can't report: " << msg << endl; | |||
} else { | } else { | |||
le->raiseError(code, msg); | le->raiseError(code, msg); | |||
} | } | |||
} | } | |||
inline void recordUpdate( bool updatedExisting, int nChanged ) { | inline void recordUpdate( bool updatedExisting, int nChanged ) { | |||
LastError *le = lastError.get(); | LastError *le = lastError.get(); | |||
if ( le ) | if ( le ) | |||
End of changes. 3 change blocks. | ||||
6 lines changed or deleted | 6 lines changed or added | |||
list.h | list.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 | |||
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 MemberInfo : public List1<MemberInfo>::Base { | struct Member : public List1<Member>::Base { | |||
const char *host; | const char *host; | |||
int port; | int port; | |||
}; | }; | |||
List1<MemberInfo> _members; | List1<Member> _members; | |||
_members.head()->next(); | _members.head()->next(); | |||
*/ | */ | |||
template<typename T> | template<typename T> | |||
class List1 : boost::noncopyable{ | class List1 : boost::noncopyable { | |||
public: | public: | |||
/* next() and head() return 0 at end of list */ | /* next() and head() return 0 at end of list */ | |||
List1() : _head(0), _m("List1"), _orphans(0) { } | ||||
class Base { | class Base { | |||
friend class List1; | friend class List1; | |||
T *_next; | T *_next; | |||
public: | public: | |||
T* next() const { return _next; } | T* next() const { return _next; } | |||
}; | }; | |||
T* head() const { return _head; } | T* head() const { return _head; } | |||
void push(T* t) { | void push(T* t) { | |||
boost::mutex::scoped_lock lk(_m); | scoped_lock lk(_m); | |||
t->_next = _head; | t->_next = _head; | |||
_head = t; | _head = t; } | |||
} | ||||
/* 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) { | |||
boost::mutex::scoped_lock lk(_m); | scoped_lock lk(_m); | |||
T *&prev = _head; | T *&prev = _head; | |||
T *n = prev; | T *n = prev; | |||
while( n != t ) { | while( n != t ) { | |||
prev = n->_next; | prev = n->_next; | |||
n = prev; | n = prev; | |||
} | } | |||
prev = t->_next; | prev = t->_next; | |||
if( ++_orphans > 500 ) | if( ++_orphans > 500 ) | |||
log() << "warning orphans=" << _orphans << '\n'; | log() << "warning orphans=" << _orphans << '\n'; | |||
} | } | |||
List1() : _head(0), _orphans(0) { } | ||||
private: | private: | |||
T *_head; | T *_head; | |||
boost::mutex _m; | mutex _m; | |||
int _orphans; | int _orphans; | |||
}; | }; | |||
}; | }; | |||
End of changes. 9 change blocks. | ||||
10 lines changed or deleted | 9 lines changed or added | |||
locks.h | locks.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/>. | |||
*/ | */ | |||
//#include "../pch.h" | //#include "../pch.h" | |||
#pragma once | #pragma once | |||
#if defined(_WIN32) | ||||
#if BOOST_VERSION >= 103500 | #if BOOST_VERSION >= 103500 | |||
#include <boost/thread/shared_mutex.hpp> | #define BOOST_RWLOCK | |||
#undef assert | ||||
#define assert MONGO_assert | ||||
#else | #else | |||
#error need boost >= 1.35 for windows | ||||
#endif | ||||
#define BOOST_RWLOCK | #if defined(_WIN32) | |||
#error need boost >= 1.35 for windows | ||||
#endif | ||||
#else | #include <pthread.h> | |||
#include <pthread.h> | #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 | #ifdef BOOST_RWLOCK | |||
class RWLock { | class RWLock { | |||
boost::shared_mutex _m; | boost::shared_mutex _m; | |||
public: | public: | |||
void lock(){ | void lock(){ | |||
End of changes. 7 change blocks. | ||||
10 lines changed or deleted | 10 lines changed or added | |||
log.h | log.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 <string.h> | #include <string.h> | |||
#include <errno.h> | #include <errno.h> | |||
#include "../bson/util/builder.h" | ||||
#ifndef _WIN32 | ||||
//#include <syslog.h> | ||||
#endif | ||||
namespace mongo { | namespace mongo { | |||
using boost::shared_ptr; | using boost::shared_ptr; | |||
// Utility interface for stringifying object only when val() called. | // Utility interface for stringifying object only when val() called. | |||
class LazyString { | class LazyString { | |||
public: | public: | |||
virtual ~LazyString() {} | virtual ~LazyString() {} | |||
virtual string val() const = 0; | virtual string val() const = 0; | |||
skipping to change at line 44 | skipping to change at line 49 | |||
// 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 (string)t_; } | virtual string val() const { return (string)t_; } | |||
private: | private: | |||
const T& t_; | const T& t_; | |||
}; | }; | |||
class Tee { | ||||
public: | ||||
virtual ~Tee(){} | ||||
virtual void write(const string& str) = 0; | ||||
}; | ||||
class Nullstream { | class Nullstream { | |||
public: | public: | |||
virtual Nullstream& operator<< (Tee* tee) { | ||||
return *this; | ||||
} | ||||
virtual ~Nullstream() {} | virtual ~Nullstream() {} | |||
virtual Nullstream& operator<<(const char *) { | virtual Nullstream& operator<<(const char *) { | |||
return *this; | return *this; | |||
} | } | |||
virtual Nullstream& operator<<(char *) { | virtual Nullstream& operator<<(char *) { | |||
return *this; | return *this; | |||
} | } | |||
virtual Nullstream& operator<<(char) { | virtual Nullstream& operator<<(char) { | |||
return *this; | return *this; | |||
} | } | |||
skipping to change at line 114 | skipping to change at line 128 | |||
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(){} | 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; | |||
public: | public: | |||
static int magicNumber(){ | static int magicNumber(){ | |||
return 1717; | return 1717; | |||
} | } | |||
void flush() { | void flush(Tee *t = 0) { | |||
// this ensures things are sane | // this ensures things are sane | |||
if ( doneSetup == 1717 ){ | if ( doneSetup == 1717 ) { | |||
BufBuilder b(512); | ||||
time_t_to_String( time(0) , b.grow(20) ); | ||||
b.append( ss.str() ); | ||||
const char *s = b.buf(); | ||||
scoped_lock lk(mutex); | scoped_lock lk(mutex); | |||
cout << ss.str(); | ||||
if( t ) t->write(s); | ||||
#ifndef _WIN32 | ||||
//syslog( LOG_INFO , "%s" , cc ); | ||||
#endif | ||||
cout << s; | ||||
cout.flush(); | cout.flush(); | |||
} | } | |||
ss.str(""); | _init(); | |||
} | } | |||
/** 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<<(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; } | |||
skipping to change at line 156 | skipping to change at line 180 | |||
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; | |||
} | } | |||
Nullstream& operator<< (Tee* tee) { | ||||
ss << '\n'; | ||||
flush(tee); | ||||
return *this; | ||||
} | ||||
Logstream& operator<< (ostream& ( *_endl )(ostream&)) { | Logstream& operator<< (ostream& ( *_endl )(ostream&)) { | |||
ss << '\n'; | ss << '\n'; | |||
flush(); | 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 > | template< class T > | |||
Nullstream& operator<<(const shared_ptr<T> p ){ | Nullstream& operator<<(const shared_ptr<T> p ){ | |||
T * t = p.get(); | T * t = p.get(); | |||
if ( ! t ) | if ( ! t ) | |||
*this << "null"; | *this << "null"; | |||
else | else | |||
*this << t; | *this << *t; | |||
return *this; | return *this; | |||
} | } | |||
Logstream& prolog() { | Logstream& prolog() { | |||
char now[64]; | ||||
time_t_to_String(time(0), now); | ||||
now[20] = 0; | ||||
ss << now; | ||||
return *this; | return *this; | |||
} | } | |||
private: | private: | |||
static thread_specific_ptr<Logstream> tsp; | static thread_specific_ptr<Logstream> tsp; | |||
Logstream(){ | ||||
_init(); | ||||
} | ||||
void _init(){ | ||||
ss.str(""); | ||||
} | ||||
public: | public: | |||
static Logstream& get() { | static Logstream& get() { | |||
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; | ||||
inline Nullstream& out( int level = 0 ) { | inline Nullstream& out( int level = 0 ) { | |||
if ( level > logLevel ) | if ( level > logLevel ) | |||
return nullstream; | return nullstream; | |||
return Logstream::get(); | return Logstream::get(); | |||
} | } | |||
/* 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(); | 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(); | |||
} | } | |||
inline Nullstream& log( int level = 0 ) { | /** logging which we may not want during unit tests runs. | |||
set tlogLevel to -1 to suppress tlog() output in a test program. */ | ||||
inline Nullstream& tlog( int level = 0 ) { | ||||
if ( level > tlogLevel || level > logLevel ) | ||||
return nullstream; | ||||
return Logstream::get().prolog(); | ||||
} | ||||
inline Nullstream& log( int level ) { | ||||
if ( level > logLevel ) | if ( level > logLevel ) | |||
return nullstream; | return nullstream; | |||
return Logstream::get().prolog(); | return Logstream::get().prolog(); | |||
} | } | |||
inline Nullstream& log() { | ||||
return Logstream::get().prolog(); | ||||
} | ||||
/* TODOCONCURRENCY */ | /* TODOCONCURRENCY */ | |||
inline ostream& stdcout() { | inline ostream& stdcout() { | |||
return cout; | return cout; | |||
} | } | |||
/* default impl returns "" -- mongod overrides */ | /* default impl returns "" -- mongod overrides */ | |||
extern const char * (*getcurns)(); | extern const char * (*getcurns)(); | |||
inline Nullstream& problem( int level = 0 ) { | inline Nullstream& problem( int level = 0 ) { | |||
if ( level > logLevel ) | if ( level > logLevel ) | |||
End of changes. 17 change blocks. | ||||
13 lines changed or deleted | 57 lines changed or added | |||
matcher.h | matcher.h | |||
---|---|---|---|---|
skipping to change at line 155 | skipping to change at line 155 | |||
bool keyMatch() const { return !all && !haveSize && !hasArray && !h aveNeg && _orMatchers.size() == 0; } | bool keyMatch() const { return !all && !haveSize && !hasArray && !h aveNeg && _orMatchers.size() == 0; } | |||
bool atomic() const { return _atomic; } | bool atomic() const { return _atomic; } | |||
bool hasType( BSONObj::MatchType type ) const; | bool hasType( BSONObj::MatchType type ) const; | |||
string toString() const { | string toString() const { | |||
return jsobj.toString(); | return jsobj.toString(); | |||
} | } | |||
// void popOr() { | ||||
// massert( 13261, "no or to pop", !_orMatchers.empty() ); | ||||
// _norMatchers.push_back( _orMatchers.front() ); | ||||
// _orMatchers.pop_front(); | ||||
// } | ||||
private: | private: | |||
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 parseOrNor( const BSONElement &e, bool subMatcher ); | |||
void parseOr( const BSONElement &e, bool subMatcher, vector< shared _ptr< Matcher > > &matchers ); | void parseOr( const BSONElement &e, bool subMatcher, list< shared_p tr< Matcher > > &matchers ); | |||
Where *where; // set if query uses $where | Where *where; // set if query uses $where | |||
BSONObj jsobj; // the query pattern. e.g., { name : "joe" } | BSONObj jsobj; // the query pattern. e.g., { name : "joe" } | |||
BSONObj constrainIndexKey_; | BSONObj constrainIndexKey_; | |||
vector<ElementMatcher> basics; | vector<ElementMatcher> basics; | |||
bool haveSize; | bool haveSize; | |||
bool all; | bool all; | |||
bool hasArray; | bool hasArray; | |||
bool haveNeg; | bool haveNeg; | |||
skipping to change at line 192 | skipping to change at line 198 | |||
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; | |||
vector< shared_ptr< Matcher > > _orMatchers; | list< shared_ptr< Matcher > > _orMatchers; | |||
vector< shared_ptr< Matcher > > _norMatchers; | list< shared_ptr< Matcher > > _norMatchers; | |||
friend class CoveredIndexMatcher; | friend class CoveredIndexMatcher; | |||
}; | }; | |||
// If match succeeds on index key, then attempt to match full document. | // If match succeeds on index key, then attempt to match full document. | |||
class CoveredIndexMatcher : boost::noncopyable { | class CoveredIndexMatcher : boost::noncopyable { | |||
public: | public: | |||
CoveredIndexMatcher(const BSONObj &pattern, const BSONObj &indexKey Pattern); | CoveredIndexMatcher(const BSONObj &pattern, const BSONObj &indexKey Pattern , bool 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 matches(const BSONObj &key, const DiskLoc &recLoc , MatchDetai ls * details = 0 ); | |||
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; } | |||
private: | private: | |||
Matcher _keyMatcher; | Matcher _keyMatcher; | |||
Matcher _docMatcher; | Matcher _docMatcher; | |||
bool _needRecord; | bool _needRecord; | |||
End of changes. 4 change blocks. | ||||
4 lines changed or deleted | 10 lines changed or added | |||
message.h | message.h | |||
---|---|---|---|---|
skipping to change at line 93 | skipping to change at line 93 | |||
bool call(Message& toSend, Message& response); | bool call(Message& toSend, Message& response); | |||
void say(Message& toSend, int responseTo = -1); | void say(Message& toSend, int responseTo = -1); | |||
void piggyBack( Message& toSend , int responseTo = -1 ); | void piggyBack( Message& toSend , int responseTo = -1 ); | |||
virtual unsigned remotePort() const; | virtual unsigned remotePort() const; | |||
virtual HostAndPort remote() const; | virtual HostAndPort remote() const; | |||
// send len or throw SocketException | // send len or throw SocketException | |||
void send( const char * data , int len, const char *context ); | 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 | // recv len or throw SocketException | |||
void recv( char * data , int len ); | void recv( char * data , int len ); | |||
int unsafe_recv( char *buf, int max ); | int unsafe_recv( char *buf, int max ); | |||
private: | private: | |||
int sock; | int sock; | |||
PiggyBackData * piggyBackData; | PiggyBackData * piggyBackData; | |||
public: | public: | |||
SockAddr farEnd; | SockAddr farEnd; | |||
int _timeout; | int _timeout; | |||
skipping to change at line 179 | skipping to change at line 181 | |||
}; | }; | |||
const int MsgDataHeaderSize = sizeof(MsgData) - 4; | const int MsgDataHeaderSize = sizeof(MsgData) - 4; | |||
inline int MsgData::dataLen() { | inline int MsgData::dataLen() { | |||
return len - MsgDataHeaderSize; | return len - MsgDataHeaderSize; | |||
} | } | |||
#pragma pack() | #pragma pack() | |||
class Message { | class Message { | |||
public: | public: | |||
Message() { | // we assume here that a vector with initial size 0 does no allocat | |||
data = 0; | ion (0 is the default, but wanted to make it explicit). | |||
freeIt = false; | Message() : _buf( 0 ), _data( 0 ), _freeIt( false ) {} | |||
} | Message( void * data , bool freeIt ) : | |||
Message( void * _data , bool _freeIt ) { | _buf( 0 ), _data( 0 ), _freeIt( false ) { | |||
data = (MsgData*)_data; | _setData( reinterpret_cast< MsgData* >( data ), freeIt ); | |||
freeIt = _freeIt; | ||||
}; | }; | |||
Message(Message& r) : _buf( 0 ), _data( 0 ), _freeIt( false ) { | ||||
*this = r; | ||||
} | ||||
~Message() { | ~Message() { | |||
reset(); | reset(); | |||
} | } | |||
SockAddr from; | SockAddr _from; | |||
MsgData *data; | ||||
int operation() const { | MsgData *header() const { | |||
return data->operation(); | assert( !empty() ); | |||
return _buf ? _buf : reinterpret_cast< MsgData* > ( _data[ 0 ]. | ||||
first ); | ||||
} | ||||
int operation() const { return header()->operation(); } | ||||
MsgData *singleData() const { | ||||
massert( 13273, "single data buffer expected", _buf ); | ||||
return header(); | ||||
} | } | |||
bool empty() const { return !_buf && _data.empty(); } | ||||
// concat multiple buffers - noop if <2 buffers already, otherwise | ||||
can be expensive copy | ||||
// can get rid of this if we make response handling smarter | ||||
void concat() { | ||||
if ( _buf || empty() ) { | ||||
return; | ||||
} | ||||
assert( _freeIt ); | ||||
int totalSize = 0; | ||||
for( vector< pair< char *, int > >::const_iterator i = _data.be | ||||
gin(); i != _data.end(); ++i ) { | ||||
totalSize += i->second; | ||||
} | ||||
char *buf = (char*)malloc( totalSize ); | ||||
char *p = buf; | ||||
for( vector< pair< char *, int > >::const_iterator i = _data.be | ||||
gin(); i != _data.end(); ++i ) { | ||||
memcpy( p, i->first, i->second ); | ||||
p += i->second; | ||||
} | ||||
reset(); | ||||
_setData( (MsgData*)buf, true ); | ||||
} | ||||
// vector swap() so this is fast | ||||
Message& operator=(Message& r) { | Message& operator=(Message& r) { | |||
assert( data == 0 ); | assert( empty() ); | |||
data = r.data; | assert( r._freeIt ); | |||
assert( r.freeIt ); | _buf = r._buf; | |||
r.freeIt = false; | r._buf = 0; | |||
r.data = 0; | if ( r._data.size() > 0 ) { | |||
freeIt = true; | _data.swap( r._data ); | |||
} | ||||
r._freeIt = false; | ||||
_freeIt = true; | ||||
return *this; | return *this; | |||
} | } | |||
void reset() { | void reset() { | |||
if ( freeIt && data ) | if ( _freeIt ) { | |||
free(data); | if ( _buf ) { | |||
data = 0; | free( _buf ); | |||
freeIt = false; | } | |||
for( vector< pair< char *, int > >::const_iterator i = _dat | ||||
a.begin(); i != _data.end(); ++i ) { | ||||
free(i->first); | ||||
} | ||||
} | ||||
_buf = 0; | ||||
_data.clear(); | ||||
_freeIt = false; | ||||
} | } | |||
void setData(MsgData *d, bool _freeIt) { | // use to add a buffer | |||
assert( data == 0 ); | // assumes message will free everything | |||
freeIt = _freeIt; | void appendData(char *d, int size) { | |||
data = d; | if ( size <= 0 ) { | |||
return; | ||||
} | ||||
if ( empty() ) { | ||||
MsgData *md = (MsgData*)d; | ||||
md->len = size; // can be updated later if more buffers add | ||||
ed | ||||
_setData( md, true ); | ||||
return; | ||||
} | ||||
assert( _freeIt ); | ||||
if ( _buf ) { | ||||
_data.push_back( make_pair( (char*)_buf, _buf->len ) ); | ||||
_buf = 0; | ||||
} | ||||
_data.push_back( make_pair( d, size ) ); | ||||
header()->len += size; | ||||
} | ||||
// use to set first buffer if empty | ||||
void setData(MsgData *d, bool freeIt) { | ||||
assert( empty() ); | ||||
_setData( d, freeIt ); | ||||
} | } | |||
void setData(int operation, const char *msgtxt) { | void setData(int operation, const char *msgtxt) { | |||
setData(operation, msgtxt, strlen(msgtxt)+1); | setData(operation, msgtxt, strlen(msgtxt)+1); | |||
} | } | |||
void setData(int operation, const char *msgdata, size_t len) { | void setData(int operation, const char *msgdata, size_t len) { | |||
assert(data == 0); | assert( empty() ); | |||
size_t dataLen = len + sizeof(MsgData) - 4; | size_t dataLen = len + sizeof(MsgData) - 4; | |||
MsgData *d = (MsgData *) malloc(dataLen); | MsgData *d = (MsgData *) malloc(dataLen); | |||
memcpy(d->_data, msgdata, len); | memcpy(d->_data, msgdata, len); | |||
d->len = fixEndian(dataLen); | d->len = fixEndian(dataLen); | |||
d->setOperation(operation); | d->setOperation(operation); | |||
freeIt= true; | _setData( d, true ); | |||
data = d; | ||||
} | } | |||
bool doIFreeIt() { | bool doIFreeIt() { | |||
return freeIt; | return _freeIt; | |||
} | ||||
void send( MessagingPort &p, const char *context ) { | ||||
if ( empty() ) { | ||||
return; | ||||
} | ||||
if ( _buf != 0 ) { | ||||
p.send( (char*)_buf, _buf->len, context ); | ||||
} else { | ||||
p.send( _data, context ); | ||||
} | ||||
} | } | |||
private: | private: | |||
bool freeIt; | void _setData( MsgData *d, bool freeIt ) { | |||
_freeIt = freeIt; | ||||
_buf = d; | ||||
} | ||||
// if just one buffer, keep it in _buf, otherwise keep a sequence o | ||||
f buffers in _data | ||||
MsgData * _buf; | ||||
// byte buffer(s) - the first must contain at least a full MsgData | ||||
unless using _buf for storage instead | ||||
vector< pair< char*, int > > _data; | ||||
bool _freeIt; | ||||
}; | }; | |||
class SocketException : public DBException { | class SocketException : public DBException { | |||
public: | public: | |||
virtual const char* what() const throw() { return "socket exception "; } | virtual const char* what() const throw() { return "socket exception "; } | |||
virtual int getCode(){ return 9001; } | virtual int getCode() const { return 9001; } | |||
}; | }; | |||
MSGID nextMessageId(); | MSGID nextMessageId(); | |||
void setClientId( int id ); | void setClientId( int id ); | |||
int getClientId(); | int getClientId(); | |||
extern TicketHolder connTicketHolder; | extern TicketHolder connTicketHolder; | |||
} // namespace mongo | } // namespace mongo | |||
End of changes. 14 change blocks. | ||||
31 lines changed or deleted | 124 lines changed or added | |||
misc.h | misc.h | |||
---|---|---|---|---|
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, 64, &t); | ctime_s(buf, 64, &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) ){ | ||||
char buf[32]; | ||||
#if defined(_WIN32) | ||||
ctime_s(buf, 64, &t); | ||||
#else | ||||
ctime_r(&t, buf); | ||||
#endif | ||||
buf[24] = 0; // don't want the \n | ||||
return buf; | ||||
} | ||||
struct Date_t { | struct Date_t { | |||
// TODO: make signed (and look for related TODO's) | // TODO: make signed (and look for related TODO's) | |||
unsigned long long millis; | unsigned long long millis; | |||
Date_t(): millis(0) {} | Date_t(): millis(0) {} | |||
Date_t(unsigned long long m): millis(m) {} | Date_t(unsigned long long m): millis(m) {} | |||
operator unsigned long long&() { return millis; } | operator unsigned long long&() { return millis; } | |||
operator const unsigned long long&() const { return millis; } | operator const unsigned long long&() const { return millis; } | |||
string toString() const { | string toString() const { | |||
char buf[64]; | char buf[64]; | |||
time_t_to_String(millis, buf); | time_t_to_String(millis, buf); | |||
End of changes. 1 change blocks. | ||||
0 lines changed or deleted | 11 lines changed or added | |||
mmap.h | mmap.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 | |||
namespace mongo { | namespace mongo { | |||
/* the administrative-ish stuff here */ | /* the administrative-ish stuff here */ | |||
class MongoFile { | class MongoFile : boost::noncopyable { | |||
protected: | protected: | |||
virtual void close() = 0; | virtual void close() = 0; | |||
virtual void flush(bool sync) = 0; | virtual void flush(bool sync) = 0; | |||
void created(); /* subclass must call after create */ | void created(); /* subclass must call after create */ | |||
void destroyed(); /* subclass must call in destructor */ | void destroyed(); /* subclass must call in destructor */ | |||
public: | public: | |||
virtual ~MongoFile() {} | ||||
virtual long length() = 0; | virtual long length() = 0; | |||
enum Options { | enum Options { | |||
SEQUENTIAL = 1 // hint - e.g. FILE_FLAG_SEQUENTIAL_SCAN on wind ows | SEQUENTIAL = 1 // hint - e.g. FILE_FLAG_SEQUENTIAL_SCAN on wind ows | |||
}; | }; | |||
virtual ~MongoFile() {} | ||||
static int flushAll( bool sync ); // returns n flushed | static int flushAll( bool sync ); // returns n flushed | |||
static long long totalMappedLength(); | static long long totalMappedLength(); | |||
static void closeAllFiles( stringstream &message ); | static void closeAllFiles( stringstream &message ); | |||
/* can be "overriden" if necessary */ | /* can be "overriden" if necessary */ | |||
static bool exists(boost::filesystem::path p) { | static bool exists(boost::filesystem::path p) { | |||
return boost::filesystem::exists(p); | return boost::filesystem::exists(p); | |||
} | } | |||
}; | }; | |||
class MFTemplate : public MongoFile { | /** template for what a new storage engine's class definition must impl | |||
ement | ||||
PRELIMINARY - subject to change. | ||||
*/ | ||||
class StorageContainerTemplate : public MongoFile { | ||||
protected: | protected: | |||
virtual void close(); | virtual void close(); | |||
virtual void flush(bool sync); | virtual void flush(bool sync); | |||
public: | public: | |||
virtual long length(); | virtual long length(); | |||
/** pointer to a range of space in this storage unit */ | ||||
class Pointer { | class Pointer { | |||
public: | 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); | void* at(int offset, int len); | |||
void grow(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; | bool isNull() const; | |||
}; | }; | |||
Pointer map( const char *filename ); | /** commit written() calls from above. */ | |||
Pointer map(const char *_filename, long &length, int options=0); | 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 { | |||
public: | public: | |||
class Pointer { | class Pointer { | |||
char *_base; | char *_base; | |||
public: | public: | |||
Pointer() : _base(0) { } | Pointer() : _base(0) { } | |||
Pointer(void *p) : _base((char*) p) { } | Pointer(void *p) : _base((char*) p) { } | |||
void* at(int offset, int maxLen) { return _base + offset; } | void* at(int offset, int maxLen) { return _base + offset; } | |||
skipping to change at line 88 | skipping to change at line 108 | |||
MemoryMappedFile(); | MemoryMappedFile(); | |||
~MemoryMappedFile() { | ~MemoryMappedFile() { | |||
destroyed(); | destroyed(); | |||
close(); | close(); | |||
} | } | |||
void close(); | void close(); | |||
// Throws exception if file doesn't exist. (dm may2010: not sure if this is always true?) | // Throws exception if file doesn't exist. (dm may2010: not sure if this is always true?) | |||
void* map( const char *filename ); | void* map( const char *filename ); | |||
/*Pointer pmap( const char *filename ) { | ||||
/*To replace map(): | ||||
Pointer open( const char *filename ) { | ||||
void *p = map(filename); | void *p = map(filename); | |||
uassert(13077, "couldn't open/map file", p); | uassert(13077, "couldn't open/map file", p); | |||
return Pointer(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. | |||
*/ | */ | |||
void* map(const char *filename, long &length, int options = 0 ); | void* map(const char *filename, long &length, int options = 0 ); | |||
skipping to change at line 116 | skipping to change at line 139 | |||
return len; | return len; | |||
} | } | |||
private: | private: | |||
static void updateLength( const char *filename, long &length ); | static void updateLength( const char *filename, long &length ); | |||
HANDLE fd; | HANDLE fd; | |||
HANDLE maphandle; | HANDLE maphandle; | |||
void *view; | void *view; | |||
long len; | long len; | |||
string _filename; | ||||
}; | }; | |||
void printMemInfo( const char * where ); | void printMemInfo( const char * where ); | |||
#include "ramstore.h" | #include "ramstore.h" | |||
//#define _RAMSTORE | //#define _RAMSTORE | |||
#if defined(_RAMSTORE) | #if defined(_RAMSTORE) | |||
typedef RamStoreFile MMF; | typedef RamStoreFile MMF; | |||
#else | #else | |||
End of changes. 10 change blocks. | ||||
8 lines changed or deleted | 38 lines changed or added | |||
namespace.h | namespace.h | |||
---|---|---|---|---|
skipping to change at line 30 | skipping to change at line 30 | |||
#include "../pch.h" | #include "../pch.h" | |||
#include "jsobj.h" | #include "jsobj.h" | |||
#include "queryutil.h" | #include "queryutil.h" | |||
#include "diskloc.h" | #include "diskloc.h" | |||
#include "../util/hashtab.h" | #include "../util/hashtab.h" | |||
#include "../util/mmap.h" | #include "../util/mmap.h" | |||
namespace mongo { | namespace mongo { | |||
class Cursor; | ||||
#pragma pack(1) | ||||
/* 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 | const int MaxDatabaseLen = 256; // max str len for the db name, includi ng null char | |||
// "database.a.b.c" -> "database" | // "database.a.b.c" -> "database" | |||
inline void nsToDatabase(const char *ns, char *database) { | inline void nsToDatabase(const char *ns, char *database) { | |||
const char *p = ns; | const char *p = ns; | |||
char *q = database; | char *q = database; | |||
while ( *p != '.' ) { | while ( *p != '.' ) { | |||
if ( *p == 0 ) | if ( *p == 0 ) | |||
skipping to change at line 58 | skipping to change at line 54 | |||
if (q-database>=MaxDatabaseLen) { | if (q-database>=MaxDatabaseLen) { | |||
log() << "nsToDatabase: ns too long. terminating, buf overrun c ondition" << endl; | log() << "nsToDatabase: ns too long. terminating, buf overrun c ondition" << endl; | |||
dbexit( EXIT_POSSIBLE_CORRUPTION ); | dbexit( EXIT_POSSIBLE_CORRUPTION ); | |||
} | } | |||
} | } | |||
inline string nsToDatabase(const char *ns) { | inline string nsToDatabase(const char *ns) { | |||
char buf[MaxDatabaseLen]; | char buf[MaxDatabaseLen]; | |||
nsToDatabase(ns, buf); | nsToDatabase(ns, buf); | |||
return 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") | |||
private: | private: | |||
skipping to change at line 87 | skipping to change at line 89 | |||
string ns() const { | string ns() const { | |||
return db + '.' + coll; | return db + '.' + coll; | |||
} | } | |||
bool isSystem() { | bool isSystem() { | |||
return strncmp(coll.c_str(), "system.", 7) == 0; | return strncmp(coll.c_str(), "system.", 7) == 0; | |||
} | } | |||
}; | }; | |||
#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 NamespaceD etails */ | |||
class Namespace { | class Namespace { | |||
public: | public: | |||
enum MaxNsLenValue { MaxNsLen = 128 }; | enum MaxNsLenValue { MaxNsLen = 128 }; | |||
Namespace(const char *ns) { | Namespace(const char *ns) { | |||
*this = ns; | *this = ns; | |||
} | } | |||
Namespace& operator=(const char *ns) { | Namespace& operator=(const char *ns) { | |||
uassert( 10080 , "ns name too long, max size is 128", strlen(ns ) < MaxNsLen); | uassert( 10080 , "ns name too long, max size is 128", strlen(ns ) < MaxNsLen); | |||
//memset(buf, 0, MaxNsLen); /* this is just to keep stuff clean in the files for easy dumping and reading */ | //memset(buf, 0, MaxNsLen); /* this is just to keep stuff clean in the files for easy dumping and reading */ | |||
strcpy_s(buf, MaxNsLen, ns); | strcpy_s(buf, MaxNsLen, ns); | |||
return *this; | return *this; | |||
} | } | |||
/* for more than 10 indexes -- see NamespaceDetails::Extra */ | /* for more than 10 indexes -- see NamespaceDetails::Extra */ | |||
string extraName() { | string extraName(int i) { | |||
string s = string(buf) + "$extra"; | char ex[] = "$extra"; | |||
massert( 10348 , "ns name too long", s.size() < MaxNsLen); | ex[5] += i; | |||
string s = string(buf) + ex; | ||||
massert( 10348 , "$extra: ns name too long", s.size() < MaxNsLe | ||||
n); | ||||
return s; | return s; | |||
} | } | |||
bool isExtra() const { | bool isExtra() const { | |||
const char *p = strstr(buf, "$extra"); | const char *p = strstr(buf, "$extr"); | |||
return p && p[6] == 0; //==0 important in case an index uses na | return p && p[5] && p[6] == 0; //==0 important in case an index | |||
me "$extra_1" for example | uses name "$extra_1" for example | |||
} | ||||
bool hasDollarSign() const { | ||||
return strstr( buf , "$" ) > 0; | ||||
} | ||||
void kill() { | ||||
buf[0] = 0x7f; | ||||
} | ||||
bool operator==(const char *r) { | ||||
return strcmp(buf, r) == 0; | ||||
} | ||||
bool operator==(const Namespace& r) { | ||||
return strcmp(buf, r.buf) == 0; | ||||
} | } | |||
bool hasDollarSign() const { return strchr( buf , '$' ) > 0; } | ||||
void kill() { buf[0] = 0x7f; } | ||||
bool operator==(const char *r) const { return strcmp(buf, r) == 0; | ||||
} | ||||
bool operator==(const Namespace& r) const { return strcmp(buf, r.bu | ||||
f) == 0; } | ||||
int hash() const { | int hash() const { | |||
unsigned x = 0; | unsigned x = 0; | |||
const char *p = buf; | const char *p = buf; | |||
while ( *p ) { | while ( *p ) { | |||
x = x * 131 + *p; | x = x * 131 + *p; | |||
p++; | p++; | |||
} | } | |||
return (x & 0x7fffffff) | 0x8000000; // must be > 0 | return (x & 0x7fffffff) | 0x8000000; // must be > 0 | |||
} | } | |||
skipping to change at line 153 | skipping to change at line 148 | |||
old = old.substr( 0 , old.find( "." ) ); | old = old.substr( 0 , old.find( "." ) ); | |||
return old + "." + local; | return old + "." + local; | |||
} | } | |||
operator string() const { | operator string() const { | |||
return (string)buf; | return (string)buf; | |||
} | } | |||
char buf[MaxNsLen]; | char buf[MaxNsLen]; | |||
}; | }; | |||
#pragma pack() | ||||
} | } // 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 | things like *.system.users */ | |||
things like *.system.users | ||||
*/ | ||||
bool legalClientSystemNS( const string& ns , bool write ); | bool legalClientSystemNS( const string& ns , bool write ); | |||
/* deleted lists -- linked lists of deleted records -- are placed in 'b uckets' of various sizes | /* deleted lists -- linked lists of deleted records -- are placed in 'b uckets' of various sizes | |||
so you can look for a deleterecord about the right size. | so you can look for a deleterecord about the right size. | |||
*/ | */ | |||
const int Buckets = 19; | const int Buckets = 19; | |||
const int MaxBucket = 18; | const int MaxBucket = 18; | |||
extern int bucketSizes[]; | extern int bucketSizes[]; | |||
#pragma pack(1) | ||||
/* this is the "header" for a collection that has all its details. in the .ns file. | /* this is the "header" for a collection that has all its details. in the .ns file. | |||
*/ | */ | |||
class NamespaceDetails { | class NamespaceDetails { | |||
friend class NamespaceIndex; | friend class NamespaceIndex; | |||
enum { NIndexesExtra = 30, | enum { NIndexesExtra = 30, | |||
NIndexesBase = 10 | NIndexesBase = 10 | |||
}; | }; | |||
struct Extra { | public: | |||
struct ExtraOld { | ||||
// note we could use this field for more chaining later, so don 't waste it: | // note we could use this field for more chaining later, so don 't waste it: | |||
unsigned long long reserved1; | unsigned long long reserved1; | |||
IndexDetails details[NIndexesExtra]; | IndexDetails details[NIndexesExtra]; | |||
unsigned reserved2; | unsigned reserved2; | |||
unsigned reserved3; | unsigned reserved3; | |||
}; | }; | |||
class Extra { | ||||
long long _next; | ||||
public: | ||||
IndexDetails details[NIndexesExtra]; | ||||
private: | ||||
unsigned reserved2; | ||||
unsigned reserved3; | ||||
Extra(const Extra&) { assert(false); } | ||||
Extra& operator=(const Extra& r) { assert(false); re | ||||
turn *this; } | ||||
public: | ||||
Extra() { } | ||||
long ofsFrom(NamespaceDetails *d) { | ||||
return ((char *) this) - ((char *) d); | ||||
} | ||||
void init() { memset(this, 0, sizeof(Extra)); } | ||||
Extra* next(NamespaceDetails *d) { | ||||
if( _next == 0 ) return 0; | ||||
return (Extra*) (((char *) d) + _next); | ||||
} | ||||
void setNext(long ofs) { _next = ofs; } | ||||
void copy(NamespaceDetails *d, const Extra& e) { | ||||
memcpy(this, &e, sizeof(Extra)); | ||||
_next = 0; | ||||
} | ||||
}; // Extra | ||||
Extra* extra() { | Extra* extra() { | |||
assert( extraOffset ); | if( extraOffset == 0 ) return 0; | |||
return (Extra *) (((char *) this) + extraOffset); | return (Extra *) (((char *) this) + extraOffset); | |||
} | } | |||
public: | public: | |||
/* add extra space for indexes when more than 10 */ | ||||
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 = 40 }; | enum { NIndexesMax = 64 }; | |||
BOOST_STATIC_ASSERT( NIndexesMax == NIndexesBase + NIndexesExtra ); | 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 ) { | NamespaceDetails( const DiskLoc &loc, bool _capped ); | |||
/* be sure to initialize new fields here -- doesn't default to | ||||
zeroes the way we use it */ | ||||
firstExtent = lastExtent = capExtent = loc; | ||||
datasize = nrecords = 0; | ||||
lastExtentSize = 0; | ||||
nIndexes = 0; | ||||
capped = _capped; | ||||
max = 0x7fffffff; | ||||
paddingFactor = 1.0; | ||||
flags = 0; | ||||
capFirstNewRecord = DiskLoc(); | ||||
// Signal that we are on first allocation iteration through ext | ||||
ents. | ||||
capFirstNewRecord.setInvalid(); | ||||
// For capped case, signal that we are doing initial extent all | ||||
ocation. | ||||
if ( capped ) | ||||
deletedList[ 1 ].setInvalid(); | ||||
assert( sizeof(dataFileVersion) == 2 ); | ||||
dataFileVersion = 0; | ||||
indexFileVersion = 0; | ||||
multiKeyIndexBits = 0; | ||||
reservedA = 0; | ||||
extraOffset = 0; | ||||
backgroundIndexBuildInProgress = 0; | ||||
memset(reserved, 0, sizeof(reserved)); | ||||
} | ||||
DiskLoc firstExtent; | DiskLoc firstExtent; | |||
DiskLoc lastExtent; | DiskLoc lastExtent; | |||
/* NOTE: capped collections override the meaning of deleted list. | /* NOTE: capped collections override the meaning of deleted list. | |||
deletedList[0] points to a list of free records (DeletedRe cord's) for all extents in | deletedList[0] points to a list of free records (DeletedRe cord's) for all extents in | |||
the namespace. | the namespace. | |||
deletedList[1] points to the last record in the prev exten t. When the "current extent" | deletedList[1] points to the last record in the prev exten t. When the "current extent" | |||
changes, this value is updated. !deletedList[1].isValid() when this value is not | changes, this value is updated. !deletedList[1].isValid() when this value is not | |||
yet computed. | yet computed. | |||
*/ | */ | |||
skipping to change at line 270 | skipping to change at line 276 | |||
private: | private: | |||
unsigned long long reservedA; | unsigned long long reservedA; | |||
long long extraOffset; // where the $extra info is located (bytes r elative to this) | long long extraOffset; // where the $extra info is located (bytes r elative to this) | |||
public: | public: | |||
int backgroundIndexBuildInProgress; // 1 if in prog | int backgroundIndexBuildInProgress; // 1 if in prog | |||
char reserved[76]; | char reserved[76]; | |||
/* when a background index build is in progress, we don't count the index in nIndexes until | /* 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 { | int nIndexesBeingBuilt() const { return nIndexes + backgroundIndexB | |||
return nIndexes + backgroundIndexBuildInProgress; | uildInProgress; } | |||
} | ||||
/* NOTE: be careful with flags. are we manipulating them in read l ocks? if so, | /* NOTE: be careful with flags. are we manipulating them in read l ocks? if so, | |||
this isn't thread safe. TODO | this isn't thread safe. TODO | |||
*/ | */ | |||
enum NamespaceFlags { | enum NamespaceFlags { | |||
Flag_HaveIdIndex = 1 << 0, // set when we have _id index (ONLY if ensureIdIndex was called -- 0 if that has never been called) | Flag_HaveIdIndex = 1 << 0, // set when we have _id index (ONLY if ensureIdIndex was called -- 0 if that has never been called) | |||
Flag_CappedDisallowDelete = 1 << 1 // set when deletes not allo wed during capped table allocation. | Flag_CappedDisallowDelete = 1 << 1 // set when deletes not allo wed during capped table allocation. | |||
}; | }; | |||
IndexDetails& idx(int idxNo) { | IndexDetails& idx(int idxNo) { | |||
if( idxNo < NIndexesBase ) | if( idxNo < NIndexesBase ) | |||
return _indexes[idxNo]; | return _indexes[idxNo]; | |||
return extra()->details[idxNo-NIndexesBase]; | Extra *e = extra(); | |||
massert(13282, "missing Extra", e); | ||||
int i = idxNo - NIndexesBase; | ||||
if( i >= NIndexesExtra ) { | ||||
e = e->next(this); | ||||
massert(13283, "missing Extra", e); | ||||
i -= NIndexesExtra; | ||||
} | ||||
return e->details[i]; | ||||
} | } | |||
IndexDetails& backgroundIdx() { | IndexDetails& backgroundIdx() { | |||
DEV assert(backgroundIndexBuildInProgress); | DEV assert(backgroundIndexBuildInProgress); | |||
return idx(nIndexes); | return idx(nIndexes); | |||
} | } | |||
class IndexIterator { | class IndexIterator { | |||
friend class NamespaceDetails; | friend class NamespaceDetails; | |||
int i; | int i; | |||
int n; | int n; | |||
NamespaceDetails *d; | NamespaceDetails *d; | |||
Extra *e; | ||||
IndexIterator(NamespaceDetails *_d) { | IndexIterator(NamespaceDetails *_d) { | |||
d = _d; | d = _d; | |||
i = 0; | i = 0; | |||
n = d->nIndexes; | n = d->nIndexes; | |||
if( n > NIndexesBase ) | ||||
e = d->extra(); | ||||
} | } | |||
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() { | IndexDetails& next() { return d->idx(i++); } | |||
int k = i; | }; // IndexIterator | |||
i++; | ||||
return k < NIndexesBase ? d->_indexes[k] : | ||||
e->details[k-10]; | ||||
} | ||||
}; | ||||
IndexIterator ii() { | IndexIterator ii() { return IndexIterator(this); } | |||
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(); | IndexIterator i = ii(); | |||
while( i.more() ) { | while( i.more() ) { | |||
if( &i.next() == &idx ) | if( &i.next() == &idx ) | |||
return i.pos()-1; | return i.pos()-1; | |||
} | } | |||
massert( 10349 , "E12000 idxNo fails", false); | massert( 10349 , "E12000 idxNo fails", false); | |||
skipping to change at line 353 | skipping to change at line 355 | |||
void clearIndexIsMultikey(int i) { | void clearIndexIsMultikey(int i) { | |||
dassert( i < NIndexesMax ); | dassert( i < NIndexesMax ); | |||
multiKeyIndexBits &= ~(((unsigned long long) 1) << i); | multiKeyIndexBits &= ~(((unsigned long long) 1) << i); | |||
} | } | |||
/* 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() { | void aboutToDeleteAnIndex() { flags &= ~Flag_HaveIdIndex; } | |||
flags &= ~Flag_HaveIdIndex; | ||||
} | ||||
void cappedDisallowDelete() { | void cappedDisallowDelete() { flags |= Flag_CappedDisallowDelete; } | |||
flags |= Flag_CappedDisallowDelete; | ||||
} | ||||
/* 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; | paddingFactor = x; | |||
} | } | |||
void paddingTooSmall() { | void paddingTooSmall() { | |||
skipping to change at line 422 | skipping to change at line 420 | |||
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); | |||
bool capLooped() const { return capped && capFirstNewRecord.isValid | ||||
bool capLooped() const { | (); } | |||
return capped && capFirstNewRecord.isValid(); | ||||
} | ||||
// Start from firstExtent by default. | // Start from firstExtent by default. | |||
DiskLoc firstRecord( const DiskLoc &startExtent = DiskLoc() ) const ; | DiskLoc firstRecord( const DiskLoc &startExtent = DiskLoc() ) const ; | |||
// Start from lastExtent by default. | // Start from lastExtent by default. | |||
DiskLoc lastRecord( const DiskLoc &startExtent = DiskLoc() ) const; | DiskLoc lastRecord( const DiskLoc &startExtent = DiskLoc() ) const; | |||
bool inCapExtent( const DiskLoc &dl ) const; | bool inCapExtent( const DiskLoc &dl ) const; | |||
void checkMigrate(); | void checkMigrate(); | |||
long long storageSize( int * numExtents = 0 ); | long long storageSize( int * numExtents = 0 ); | |||
private: | private: | |||
bool cappedMayDelete() const { | bool cappedMayDelete() const { return !( flags & Flag_CappedDisallo | |||
return !( flags & Flag_CappedDisallowDelete ); | wDelete ); } | |||
} | Extent *theCapExtent() const { return capExtent.ext(); } | |||
Extent *theCapExtent() const { | ||||
return capExtent.ext(); | ||||
} | ||||
void advanceCapExtent( const char *ns ); | void advanceCapExtent( const char *ns ); | |||
void maybeComplain( const char *ns, int len ) const; | void maybeComplain( const char *ns, int len ) const; | |||
DiskLoc __stdAlloc(int len); | DiskLoc __stdAlloc(int len); | |||
DiskLoc __capAlloc(int len); | DiskLoc __capAlloc(int len); | |||
DiskLoc _alloc(const char *ns, int len); | DiskLoc _alloc(const char *ns, int len); | |||
void compact(); // combine adjacent deleted records | void compact(); // combine adjacent deleted records | |||
DiskLoc &firstDeletedInCapExtent(); | DiskLoc &firstDeletedInCapExtent(); | |||
bool nextIsInCapExtent( const DiskLoc &dl ) const; | bool nextIsInCapExtent( const DiskLoc &dl ) const; | |||
}; | }; // NamespaceDetails | |||
#pragma pack() | #pragma pack() | |||
/* these are things we know / compute about a namespace that are transi | /* NamespaceDetailsTransient | |||
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 ); | ||||
/* 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(), _cll_enabled() { } | NamespaceDetailsTransient(const char *ns) : _ns(ns), _keysComputed( false), _qcWriteCount(), _cll_enabled() { } | |||
/* _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 */ | /* use get_w() when doing write operations */ | |||
skipping to change at line 583 | skipping to change at line 574 | |||
if ( t.get() == 0 ) | if ( t.get() == 0 ) | |||
t.reset( new NamespaceDetailsTransient(ns) ); | t.reset( new NamespaceDetailsTransient(ns) ); | |||
return *t; | return *t; | |||
} | } | |||
/* NamespaceIndex is the ".ns" file you see in the data directory. It is the "system catalog" | /* NamespaceIndex is the ".ns" file you see in the data directory. It is the "system catalog" | |||
if you will: at least the core parts. (Additional info in system.* collections.) | if you will: at least the core parts. (Additional info in system.* collections.) | |||
*/ | */ | |||
class NamespaceIndex { | class NamespaceIndex { | |||
friend class NamespaceCursor; | friend class NamespaceCursor; | |||
BOOST_STATIC_ASSERT( sizeof(NamespaceDetails::Extra) <= sizeof(Name | ||||
spaceDetails) ); | ||||
public: | ||||
public: | ||||
NamespaceIndex(const string &dir, const string &database) : | NamespaceIndex(const string &dir, const string &database) : | |||
ht( 0 ), | ht( 0 ), dir_( dir ), database_( database ) {} | |||
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 ); | |||
} | } | |||
skipping to change at line 614 | skipping to change at line 601 | |||
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; | |||
}*/ | }*/ | |||
/* extra space for indexes when more than 10 */ | ||||
NamespaceDetails::Extra* allocExtra(const char *ns) { | ||||
Namespace n(ns); | ||||
Namespace extra(n.extraName().c_str()); // throws userexception | ||||
if ns name too long | ||||
NamespaceDetails *d = details(ns); | ||||
massert( 10350 , "allocExtra: base ns missing?", d ); | ||||
assert( d->extraOffset == 0 ); | ||||
massert( 10351 , "allocExtra: extra already exists", ht->get(e | ||||
xtra) == 0 ); | ||||
NamespaceDetails::Extra temp; | ||||
memset(&temp, 0, sizeof(temp)); | ||||
uassert( 10082 , "allocExtra: too many namespaces/collections" | ||||
, ht->put(extra, (NamespaceDetails&) temp)); | ||||
NamespaceDetails::Extra *e = (NamespaceDetails::Extra *) ht->ge | ||||
t(extra); | ||||
d->extraOffset = ((char *) e) - ((char *) d); | ||||
assert( d->extra() == e ); | ||||
return e; | ||||
} | ||||
NamespaceDetails* details(const char *ns) { | NamespaceDetails* details(const char *ns) { | |||
if ( !ht ) | if ( !ht ) | |||
return 0; | return 0; | |||
Namespace n(ns); | Namespace n(ns); | |||
NamespaceDetails *d = ht->get(n); | NamespaceDetails *d = ht->get(n); | |||
if ( d ) | if ( d ) | |||
d->checkMigrate(); | d->checkMigrate(); | |||
return d; | return d; | |||
} | } | |||
void kill_ns(const char *ns) { | void kill_ns(const char *ns) { | |||
if ( !ht ) | if ( !ht ) | |||
return; | return; | |||
Namespace n(ns); | Namespace n(ns); | |||
ht->kill(n); | ht->kill(n); | |||
try { | for( int i = 0; i<=1; i++ ) { | |||
Namespace extra(n.extraName().c_str()); | try { | |||
ht->kill(extra); | Namespace extra(n.extraName(i).c_str()); | |||
ht->kill(extra); | ||||
} | ||||
catch(DBException&) { } | ||||
} | } | |||
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); | ||||
private: | private: | |||
boost::filesystem::path path() const; | boost::filesystem::path path() const; | |||
void maybeMkdir() const; | void maybeMkdir() const; | |||
MMF f; | MMF f; | |||
HashTable<Namespace,NamespaceDetails,MMF::Pointer> *ht; | HashTable<Namespace,NamespaceDetails,MMF::Pointer> *ht; | |||
string dir_; | string dir_; | |||
string database_; | string database_; | |||
}; | }; | |||
End of changes. 41 change blocks. | ||||
132 lines changed or deleted | 110 lines changed or added | |||
oid.h | oid.h | |||
---|---|---|---|---|
skipping to change at line 88 | skipping to change at line 87 | |||
s << a; | s << a; | |||
s.width( 8 ); | s.width( 8 ); | |||
s << b; | s << b; | |||
s << dec; | s << dec; | |||
*/ | */ | |||
return s.str(); | return s.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. */ | /** Set to the hex string value specified. */ | |||
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 ); | |||
End of changes. 1 change blocks. | ||||
0 lines changed or deleted | 1 lines changed or added | |||
oplog.h | oplog.h | |||
---|---|---|---|---|
skipping to change at line 65 | skipping to change at line 65 | |||
class FindingStartCursor { | class FindingStartCursor { | |||
public: | public: | |||
FindingStartCursor( const QueryPlan & qp ) : | FindingStartCursor( const QueryPlan & qp ) : | |||
_qp( qp ), | _qp( qp ), | |||
_findingStart( true ), | _findingStart( true ), | |||
_findingStartMode(), | _findingStartMode(), | |||
_findingStartTimer( 0 ), | _findingStartTimer( 0 ), | |||
_findingStartCursor( 0 ) | _findingStartCursor( 0 ) | |||
{ init(); } | { init(); } | |||
bool done() const { return !_findingStart; } | bool done() const { return !_findingStart; } | |||
auto_ptr< Cursor > cRelease() { return _c; } | shared_ptr<Cursor> cRelease() { return _c; } | |||
void next() { | void next() { | |||
if ( !_findingStartCursor || !_findingStartCursor->c->ok() ) { | if ( !_findingStartCursor || !_findingStartCursor->c->ok() ) { | |||
_findingStart = false; | _findingStart = false; | |||
_c = _qp.newCursor(); // on error, start from beginning | _c = _qp.newCursor(); // on error, start from beginning | |||
destroyClientCursor(); | destroyClientCursor(); | |||
return; | return; | |||
} | } | |||
switch( _findingStartMode ) { | switch( _findingStartMode ) { | |||
case Initial: { | case Initial: { | |||
if ( !_matcher->matches( _findingStartCursor->c->currKe y(), _findingStartCursor->c->currLoc() ) ) { | if ( !_matcher->matches( _findingStartCursor->c->currKe y(), _findingStartCursor->c->currLoc() ) ) { | |||
skipping to change at line 133 | skipping to change at line 133 | |||
} | } | |||
} | } | |||
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 * _findingStartCursor; | |||
auto_ptr< Cursor > _c; | shared_ptr<Cursor> _c; | |||
DiskLoc startLoc( const DiskLoc &rec ) { | DiskLoc startLoc( const DiskLoc &rec ) { | |||
Extent *e = rec.rec()->myExtent( rec ); | Extent *e = rec.rec()->myExtent( rec ); | |||
if ( e->myLoc != _qp.nsd()->capExtent ) | if ( !_qp.nsd()->capLooped() || ( e->myLoc != _qp.nsd()->capExt ent ) ) | |||
return e->firstRecord; | return e->firstRecord; | |||
// Likely we are on the fresh side of capExtent, so return firs t fresh record. | // 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 | // 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. | // doesn't matter if we start the extent scan with capFirstNewR ecord. | |||
return _qp.nsd()->capFirstNewRecord; | return _qp.nsd()->capFirstNewRecord; | |||
} | } | |||
// should never have an empty extent in the oplog, so don't worry a bout that case | ||||
DiskLoc prevLoc( const DiskLoc &rec ) { | DiskLoc prevLoc( const DiskLoc &rec ) { | |||
Extent *e = rec.rec()->myExtent( rec ); | Extent *e = rec.rec()->myExtent( rec ); | |||
if ( e->xprev.isNull() ) | if ( _qp.nsd()->capLooped() ) { | |||
e = _qp.nsd()->lastExtent.ext(); | if ( e->xprev.isNull() ) | |||
else | e = _qp.nsd()->lastExtent.ext(); | |||
e = e->xprev.ext(); | else | |||
if ( e->myLoc != _qp.nsd()->capExtent ) | e = e->xprev.ext(); | |||
return e->firstRecord; | 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 | return DiskLoc(); // reached beginning of collection | |||
} | } | |||
void createClientCursor( const DiskLoc &startLoc = DiskLoc() ) { | void createClientCursor( const DiskLoc &startLoc = DiskLoc() ) { | |||
auto_ptr<Cursor> c = _qp.newCursor( startLoc ); | shared_ptr<Cursor> c = _qp.newCursor( startLoc ); | |||
_findingStartCursor = new ClientCursor(QueryOption_NoCursorTime out, c, _qp.ns()); | _findingStartCursor = new ClientCursor(QueryOption_NoCursorTime out, c, _qp.ns()); | |||
} | } | |||
void destroyClientCursor() { | void destroyClientCursor() { | |||
if ( _findingStartCursor ) { | if ( _findingStartCursor ) { | |||
ClientCursor::erase( _findingStartCursor->cursorid ); | ClientCursor::erase( _findingStartCursor->cursorid ); | |||
_findingStartCursor = 0; | _findingStartCursor = 0; | |||
} | } | |||
} | } | |||
void maybeRelease() { | void maybeRelease() { | |||
RARELY { | RARELY { | |||
skipping to change at line 177 | skipping to change at line 185 | |||
_findingStartCursor->updateLocation(); | _findingStartCursor->updateLocation(); | |||
{ | { | |||
dbtemprelease t; | dbtemprelease t; | |||
} | } | |||
_findingStartCursor = ClientCursor::find( id, false ); | _findingStartCursor = ClientCursor::find( id, false ); | |||
} | } | |||
} | } | |||
void init() { | void init() { | |||
// Use a ClientCursor here so we can release db mutex while sca nning | // Use a ClientCursor here so we can release db mutex while sca nning | |||
// oplog (can take quite a while with large oplogs). | // oplog (can take quite a while with large oplogs). | |||
auto_ptr<Cursor> c = _qp.newReverseCursor(); | shared_ptr<Cursor> c = _qp.newReverseCursor(); | |||
_findingStartCursor = new ClientCursor(QueryOption_NoCursorTime out, c, _qp.ns()); | _findingStartCursor = new ClientCursor(QueryOption_NoCursorTime out, c, _qp.ns()); | |||
_findingStartTimer.reset(); | _findingStartTimer.reset(); | |||
_findingStartMode = Initial; | _findingStartMode = Initial; | |||
BSONElement tsElt = _qp.query()[ "ts" ]; | BSONElement tsElt = _qp.query()[ "ts" ]; | |||
massert( 13044, "no ts field in query", !tsElt.eoo() ); | massert( 13044, "no ts field in query", !tsElt.eoo() ); | |||
BSONObjBuilder b; | BSONObjBuilder b; | |||
b.append( tsElt ); | b.append( tsElt ); | |||
BSONObj tsQuery = b.obj(); | BSONObj tsQuery = b.obj(); | |||
_matcher.reset(new CoveredIndexMatcher(tsQuery, _qp.indexKey()) ); | _matcher.reset(new CoveredIndexMatcher(tsQuery, _qp.indexKey()) ); | |||
} | } | |||
End of changes. 7 change blocks. | ||||
11 lines changed or deleted | 19 lines changed or added | |||
optime.h | optime.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 "../db/concurrency.h" | #include "../db/concurrency.h" | |||
namespace mongo { | namespace mongo { | |||
void exitCleanly( int code ); | void exitCleanly( int code ); | |||
/* Operation sequence #. A combination of current second plus an ordin | ||||
al value. | ||||
*/ | ||||
struct ClockSkewException : public DBException { | struct ClockSkewException : public DBException { | |||
virtual const char* what() const throw() { return "clock skew excep tion"; } | virtual const char* what() const throw() { return "clock skew excep tion"; } | |||
virtual int getCode(){ return 20001; } | virtual int getCode() const { return 20001; } | |||
}; | }; | |||
/* 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; | |||
unsigned secs; | unsigned secs; | |||
static OpTime last; | static OpTime last; | |||
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 { | |||
End of changes. 3 change blocks. | ||||
4 lines changed or deleted | 4 lines changed or added | |||
parallel.h | parallel.h | |||
---|---|---|---|---|
skipping to change at line 32 | skipping to change at line 32 | |||
#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" | |||
namespace mongo { | namespace mongo { | |||
/** | /** | |||
* this is a cursor that works over a set of servers | ||||
* can be used in serial/paralellel as controlled by sub classes | ||||
*/ | ||||
class ClusteredCursor { | ||||
public: | ||||
ClusteredCursor( QueryMessage& q ); | ||||
ClusteredCursor( const string& ns , const BSONObj& q , int options= | ||||
0 , const BSONObj& fields=BSONObj() ); | ||||
virtual ~ClusteredCursor(); | ||||
virtual bool more() = 0; | ||||
virtual BSONObj next() = 0; | ||||
static BSONObj concatQuery( const BSONObj& query , const BSONObj& e | ||||
xtraFilter ); | ||||
virtual string type() const = 0; | ||||
protected: | ||||
auto_ptr<DBClientCursor> query( const string& server , int num = 0 | ||||
, BSONObj extraFilter = BSONObj() ); | ||||
static BSONObj _concatFilter( const BSONObj& filter , const BSONObj | ||||
& extraFilter ); | ||||
string _ns; | ||||
BSONObj _query; | ||||
int _options; | ||||
BSONObj _fields; | ||||
bool _done; | ||||
}; | ||||
/** | ||||
* 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() ) | |||
skipping to change at line 96 | skipping to change at line 66 | |||
operator string() const { | operator string() const { | |||
return toString(); | return toString(); | |||
} | } | |||
string _server; | string _server; | |||
BSONObj _extra; | BSONObj _extra; | |||
BSONObj _orderObject; | BSONObj _orderObject; | |||
}; | }; | |||
/** | ||||
* this is a cursor that works over a set of servers | ||||
* can be used in serial/paralellel as controlled by sub classes | ||||
*/ | ||||
class ClusteredCursor { | ||||
public: | ||||
ClusteredCursor( QueryMessage& q ); | ||||
ClusteredCursor( const string& ns , const BSONObj& q , int options= | ||||
0 , const BSONObj& fields=BSONObj() ); | ||||
virtual ~ClusteredCursor(); | ||||
virtual bool more() = 0; | ||||
virtual BSONObj next() = 0; | ||||
static BSONObj concatQuery( const BSONObj& query , const BSONObj& e | ||||
xtraFilter ); | ||||
virtual string type() const = 0; | ||||
virtual BSONObj explain(); | ||||
protected: | ||||
auto_ptr<DBClientCursor> query( const string& server , int num = 0 | ||||
, BSONObj extraFilter = BSONObj() ); | ||||
BSONObj explain( const string& server , BSONObj extraFilter = BSONO | ||||
bj() ); | ||||
static BSONObj _concatFilter( const BSONObj& filter , const BSONObj | ||||
& extraFilter ); | ||||
virtual void _explain( map< string,list<BSONObj> >& out ) = 0; | ||||
string _ns; | ||||
BSONObj _query; | ||||
int _options; | ||||
BSONObj _fields; | ||||
bool _done; | ||||
}; | ||||
class FilteringClientCursor { | class FilteringClientCursor { | |||
public: | public: | |||
FilteringClientCursor( const BSONObj filter = BSONObj() ); | FilteringClientCursor( const BSONObj filter = 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 ); | |||
bool more(); | bool more(); | |||
BSONObj next(); | BSONObj next(); | |||
skipping to change at line 180 | skipping to change at line 185 | |||
/** | /** | |||
* runs a query in serial across any number of servers | * runs a query in serial across any number of servers | |||
* returns all results from 1 server, then the next, etc... | * returns all results from 1 server, then the next, etc... | |||
*/ | */ | |||
class SerialServerClusteredCursor : public ClusteredCursor { | class SerialServerClusteredCursor : public ClusteredCursor { | |||
public: | public: | |||
SerialServerClusteredCursor( 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"; } | |||
private: | private: | |||
virtual void _explain( map< string,list<BSONObj> >& out ); | ||||
vector<ServerAndQuery> _servers; | vector<ServerAndQuery> _servers; | |||
unsigned _serverIndex; | unsigned _serverIndex; | |||
FilteringClientCursor _current; | FilteringClientCursor _current; | |||
int _needToSkip; | int _needToSkip; | |||
}; | }; | |||
/** | /** | |||
* runs a query in parellel across N servers | * runs a query in parellel across N servers | |||
skipping to change at line 205 | skipping to change at line 213 | |||
ParallelSortClusteredCursor( const set<ServerAndQuery>& servers , Q ueryMessage& q , const BSONObj& sortKey ); | ParallelSortClusteredCursor( const set<ServerAndQuery>& servers , Q ueryMessage& q , const BSONObj& sortKey ); | |||
ParallelSortClusteredCursor( const set<ServerAndQuery>& servers , c onst string& ns , | ParallelSortClusteredCursor( const set<ServerAndQuery>& servers , c onst string& ns , | |||
const Query& q , int options=0, const BSONObj& fields=BSONObj() ); | const Query& q , int options=0, const BSONObj& fields=BSONObj() ); | |||
virtual ~ParallelSortClusteredCursor(); | virtual ~ParallelSortClusteredCursor(); | |||
virtual bool more(); | virtual bool more(); | |||
virtual BSONObj next(); | virtual BSONObj next(); | |||
virtual string type() const { return "ParallelSort"; } | virtual string type() const { return "ParallelSort"; } | |||
private: | private: | |||
void _init(); | void _init(); | |||
virtual void _explain( map< string,list<BSONObj> >& out ); | ||||
int _numServers; | int _numServers; | |||
set<ServerAndQuery> _servers; | set<ServerAndQuery> _servers; | |||
BSONObj _sortKey; | BSONObj _sortKey; | |||
FilteringClientCursor * _cursors; | FilteringClientCursor * _cursors; | |||
int _needToSkip; | int _needToSkip; | |||
}; | }; | |||
/** | /** | |||
* tools for doing asynchronous operations | * tools for doing asynchronous operations | |||
End of changes. 5 change blocks. | ||||
34 lines changed or deleted | 45 lines changed or added | |||
pch.h | pch.h | |||
---|---|---|---|---|
skipping to change at line 30 | skipping to change at line 30 | |||
#pragma once | #pragma once | |||
#if defined(_WIN32) | #if defined(_WIN32) | |||
# define NOMINMAX | # define NOMINMAX | |||
# include <winsock2.h> //this must be included before the first windows.h include | # include <winsock2.h> //this must be included before the first windows.h include | |||
# include <ws2tcpip.h> | # include <ws2tcpip.h> | |||
# include <windows.h> | # include <windows.h> | |||
#endif | #endif | |||
#include <ctime> | ||||
#include <sstream> | ||||
#include <string> | ||||
#include <memory> | ||||
#include <string> | #include <string> | |||
#include <iostream> | ||||
#include <fstream> | ||||
#include <map> | ||||
#include <vector> | ||||
#include <set> | ||||
#include <stdio.h> | ||||
#include <stdlib.h> | ||||
#include <sstream> | ||||
#include <signal.h> | ||||
#include "targetver.h" | ||||
#include "time.h" | ||||
#include "string.h" | ||||
#include "limits.h" | ||||
#include <boost/any.hpp> | ||||
#include <boost/archive/iterators/transform_width.hpp> | ||||
#include <boost/filesystem/convenience.hpp> | ||||
#include <boost/filesystem/operations.hpp> | ||||
#include <boost/program_options.hpp> | ||||
#include <boost/shared_ptr.hpp> | ||||
#include <boost/smart_ptr.hpp> | ||||
#include "boost/bind.hpp" | ||||
#include "boost/function.hpp" | ||||
#include <boost/thread/tss.hpp> | ||||
#include "boost/detail/endian.hpp" | ||||
#define BOOST_SPIRIT_THREADSAFE | ||||
#include <boost/version.hpp> | ||||
#include <boost/tuple/tuple.hpp> | ||||
#include <boost/thread/thread.hpp> | ||||
#include <boost/thread/condition.hpp> | ||||
#include <boost/thread/recursive_mutex.hpp> | ||||
#include <boost/thread/xtime.hpp> | ||||
#undef assert | ||||
#define assert MONGO_assert | ||||
namespace mongo { | namespace mongo { | |||
using namespace std; | using namespace std; | |||
#if defined(_WIN32) | #if defined(_DEBUG) | |||
const bool debug=true; | const bool debug=true; | |||
#else | #else | |||
const bool debug=false; | const bool debug=false; | |||
#endif | #endif | |||
// pdfile versions | // pdfile versions | |||
const int VERSION = 4; | const int VERSION = 4; | |||
const int VERSION_MINOR = 5; | const int VERSION_MINOR = 5; | |||
// mongo version | // mongo version | |||
skipping to change at line 80 | skipping to change at line 119 | |||
/** | /** | |||
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 quiy | you should either use dbexit to shutdown cleanly, or ::exit to tell the system to quiy | |||
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 | |||
#include <memory> | ||||
#include <string> | ||||
#include <iostream> | ||||
#include <fstream> | ||||
#include <map> | ||||
#include <vector> | ||||
#include <set> | ||||
#include <stdio.h> | ||||
#include <stdlib.h> | ||||
#include <sstream> | ||||
#include <signal.h> | ||||
#include "targetver.h" | ||||
#include "time.h" | ||||
#include "string.h" | ||||
#include "limits.h" | ||||
///using namespace std; | ||||
#include <boost/archive/iterators/base64_from_binary.hpp> | ||||
#include <boost/archive/iterators/binary_from_base64.hpp> | ||||
#include <boost/archive/iterators/transform_width.hpp> | ||||
#include <boost/filesystem/convenience.hpp> | ||||
#include <boost/filesystem/operations.hpp> | ||||
#include <boost/program_options.hpp> | ||||
#include <boost/shared_ptr.hpp> | ||||
#include <boost/smart_ptr.hpp> | ||||
#define BOOST_SPIRIT_THREADSAFE | ||||
#include <boost/version.hpp> | ||||
#if BOOST_VERSION >= 103800 | ||||
#define BOOST_SPIRIT_USE_OLD_NAMESPACE | ||||
#include <boost/spirit/include/classic_core.hpp> | ||||
#include <boost/spirit/include/classic_loops.hpp> | ||||
#include <boost/spirit/include/classic_lists.hpp> | ||||
#else | ||||
#include <boost/spirit/core.hpp> | ||||
#include <boost/spirit/utility/loops.hpp> | ||||
#include <boost/spirit/utility/lists.hpp> | ||||
#endif | ||||
#include <boost/tuple/tuple.hpp> | ||||
#include <boost/thread/thread.hpp> | ||||
#include <boost/thread/condition.hpp> | ||||
#include <boost/thread/recursive_mutex.hpp> | ||||
#include <boost/thread/xtime.hpp> | ||||
#undef assert | ||||
#define assert MONGO_assert | ||||
namespace mongo { | namespace mongo { | |||
using namespace boost::filesystem; | using namespace boost::filesystem; | |||
void asserted(const char *msg, const char *file, unsigned line); | ||||
} | } | |||
#define MONGO_assert(_Expression) (void)( (!!(_Expression)) || (mongo::asse | ||||
rted(#_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 ); | |||
End of changes. 6 change blocks. | ||||
51 lines changed or deleted | 44 lines changed or added | |||
pdfile.h | pdfile.h | |||
---|---|---|---|---|
skipping to change at line 43 | skipping to change at line 43 | |||
#include "client.h" | #include "client.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(const char *ns); | void dropDatabase(string db); | |||
bool repairDatabase(const char *ns, string &errmsg, bool preserveCloned | bool repairDatabase(string db, string &errmsg, bool preserveClonedFiles | |||
FilesOnFailure = false, bool backupOriginalFiles = false); | 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 userCreateNS(const char *ns, BSONObj j, string& err, bool logForRe plication); | |||
auto_ptr<Cursor> findTableScan(const char *ns, const BSONObj& order, co nst DiskLoc &startLoc=DiskLoc()); | shared_ptr<Cursor> findTableScan(const char *ns, const BSONObj& order, const DiskLoc &startLoc=DiskLoc()); | |||
// -1 if library unavailable. | // -1 if library unavailable. | |||
boost::intmax_t freeSpace(); | boost::intmax_t freeSpace(); | |||
/*--------------------------------------------------------------------- */ | /*--------------------------------------------------------------------- */ | |||
class MongoDataFile { | class MongoDataFile { | |||
friend class DataFileMgr; | friend class DataFileMgr; | |||
friend class BasicCursor; | friend class BasicCursor; | |||
public: | public: | |||
skipping to change at line 121 | skipping to change at line 121 | |||
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). */ | /** @param obj both and in and out param -- insert can sometimes mo dify an object (such as add _id). */ | |||
DiskLoc insertWithObjMod(const char *ns, BSONObj &o, bool god = fal se); | DiskLoc insertWithObjMod(const char *ns, BSONObj &o, bool god = fal se); | |||
/** @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, const BSONElement &writeId = BSONElement(), bool mayAddIndex = true ); | |||
void deleteRecord(const char *ns, Record *todelete, const DiskLoc& dl, bool cappedOK = false, bool noWarn = false); | void deleteRecord(const char *ns, Record *todelete, const DiskLoc& dl, bool cappedOK = false, bool noWarn = false); | |||
static auto_ptr<Cursor> findAll(const char *ns, const DiskLoc &star tLoc = DiskLoc()); | static 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) ; | |||
End of changes. 3 change blocks. | ||||
5 lines changed or deleted | 5 lines changed or added | |||
query.h | query.h | |||
---|---|---|---|---|
skipping to change at line 117 | skipping to change at line 117 | |||
god - allow access to system namespaces | 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(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 , OpDebug& d ebug ); | UpdateResult _updateObjects(bool god, const char *ns, const BSONObj& up dateobj, BSONObj pattern, bool upsert, bool multi , bool logop , OpDebug& d ebug ); | |||
// If justOne is true, deletedId is set to the id of the deleted object . | // 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); | long long deleteObjects(const char *ns, BSONObj pattern, bool justOne, bool logop = false, bool god=false); | |||
long long runCount(const char *ns, const BSONObj& cmd, string& err); | long long runCount(const char *ns, const BSONObj& cmd, string& err); | |||
auto_ptr< QueryResult > runQuery(Message& m, QueryMessage& q, CurOp& cu rop ); | void runQuery(Message& m, QueryMessage& q, CurOp& curop, Message &resul t); | |||
/* 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; | |||
char p[2] = "0"; | char p[2] = "0"; | |||
while ( 1 ) { | while ( 1 ) { | |||
skipping to change at line 178 | skipping to change at line 178 | |||
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; } | |||
bool showDiskLoc() const { return _showDiskLoc; } | ||||
const BSONObj& getMin() const { return _min; } | const BSONObj& getMin() const { return _min; } | |||
const BSONObj& getMax() const { return _max; } | const BSONObj& getMax() const { return _max; } | |||
const BSONObj& getOrder() const { return _order; } | const BSONObj& getOrder() const { return _order; } | |||
const BSONElement& getHint() const { return _hint; } | const BSONElement& getHint() const { return _hint; } | |||
int getMaxScan() const { return _maxScan; } | int getMaxScan() const { return _maxScan; } | |||
bool couldBeCommand() const { | bool couldBeCommand() const { | |||
/* we assume you are using findOne() for running a cmd... */ | /* we assume you are using findOne() for running a cmd... */ | |||
return _ntoreturn == 1 && strstr( _ns , ".$cmd" ); | return _ntoreturn == 1 && strstr( _ns , ".$cmd" ); | |||
skipping to change at line 245 | skipping to change at line 246 | |||
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; | ||||
_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 || | |||
skipping to change at line 277 | skipping to change at line 279 | |||
else if ( strcmp( "$min" , name ) == 0 ) | else if ( strcmp( "$min" , name ) == 0 ) | |||
_min = e.embeddedObject(); | _min = e.embeddedObject(); | |||
else if ( strcmp( "$max" , name ) == 0 ) | else if ( strcmp( "$max" , name ) == 0 ) | |||
_max = e.embeddedObject(); | _max = e.embeddedObject(); | |||
else if ( strcmp( "$hint" , name ) == 0 ) | else if ( strcmp( "$hint" , name ) == 0 ) | |||
_hint = e; | _hint = e; | |||
else if ( strcmp( "$returnKey" , name ) == 0 ) | else if ( strcmp( "$returnKey" , name ) == 0 ) | |||
_returnKey = e.trueValue(); | _returnKey = e.trueValue(); | |||
else if ( strcmp( "$maxScan" , name ) == 0 ) | else if ( strcmp( "$maxScan" , name ) == 0 ) | |||
_maxScan = e.numberInt(); | _maxScan = e.numberInt(); | |||
else if ( strcmp( "$showDiskLoc" , name ) == 0 ) | ||||
_showDiskLoc = e.trueValue(); | ||||
} | } | |||
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() ); | |||
} | } | |||
} | } | |||
skipping to change at line 311 | skipping to change at line 315 | |||
int _options; | int _options; | |||
BSONObj _filter; | BSONObj _filter; | |||
shared_ptr< FieldMatcher > _fields; | shared_ptr< FieldMatcher > _fields; | |||
bool _wantMore; | bool _wantMore; | |||
bool _explain; | bool _explain; | |||
bool _snapshot; | bool _snapshot; | |||
bool _returnKey; | bool _returnKey; | |||
bool _showDiskLoc; | ||||
BSONObj _min; | BSONObj _min; | |||
BSONObj _max; | BSONObj _max; | |||
BSONElement _hint; | BSONElement _hint; | |||
BSONObj _order; | BSONObj _order; | |||
int _maxScan; | int _maxScan; | |||
}; | }; | |||
} // namespace mongo | } // namespace mongo | |||
#include "clientcursor.h" | #include "clientcursor.h" | |||
End of changes. 5 change blocks. | ||||
1 lines changed or deleted | 6 lines changed or added | |||
queryoptimizer.h | queryoptimizer.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 "cursor.h" | #include "cursor.h" | |||
#include "jsobj.h" | #include "jsobj.h" | |||
#include "queryutil.h" | #include "queryutil.h" | |||
#include "matcher.h" | ||||
namespace mongo { | namespace mongo { | |||
class IndexDetails; | class IndexDetails; | |||
class IndexType; | class IndexType; | |||
class QueryPlan : boost::noncopyable { | class QueryPlan : boost::noncopyable { | |||
public: | public: | |||
QueryPlan(NamespaceDetails *_d, | QueryPlan(NamespaceDetails *_d, | |||
int _idxNo, // -1 = no index | int _idxNo, // -1 = no index | |||
skipping to change at line 52 | skipping to change at line 53 | |||
/* ScanAndOrder processing will be required if true */ | /* ScanAndOrder processing will be required if true */ | |||
bool scanAndOrderRequired() const { return scanAndOrderRequired_; } | bool scanAndOrderRequired() const { return scanAndOrderRequired_; } | |||
/* When true, the index we are using has keys such that it can comp letely resolve the | /* When true, the index we are using has keys such that it can comp letely resolve the | |||
query expression to match by itself without ever checking the main object. | query expression to match by itself without ever checking the main 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 | /* If true, the startKey and endKey are unhelpful and the index ord er doesn't match the | |||
requested sort order */ | requested sort order */ | |||
bool unhelpful() const { return unhelpful_; } | bool unhelpful() const { return unhelpful_; } | |||
int direction() const { return direction_; } | int direction() const { return direction_; } | |||
auto_ptr< Cursor > newCursor( const DiskLoc &startLoc = DiskLoc() , | shared_ptr<Cursor> newCursor( const DiskLoc &startLoc = DiskLoc() , | |||
int numWanted=0 ) const; | int numWanted=0 ) const; | |||
auto_ptr< Cursor > newReverseCursor() const; | shared_ptr<Cursor> newReverseCursor() const; | |||
BSONObj indexKey() const; | BSONObj indexKey() const; | |||
const char *ns() const { return fbs_.ns(); } | const char *ns() const { return fbs_.ns(); } | |||
NamespaceDetails *nsd() const { return d; } | NamespaceDetails *nsd() const { return d; } | |||
BSONObj query() const { return fbs_.query(); } | BSONObj query() const { return fbs_.query(); } | |||
BSONObj simplifiedQuery( const BSONObj& fields = BSONObj() ) const { return fbs_.simplifiedQuery( fields ); } | BSONObj simplifiedQuery( const BSONObj& fields = BSONObj() ) const { return fbs_.simplifiedQuery( fields ); } | |||
const FieldRange &range( const char *fieldName ) const { return fbs _.range( fieldName ); } | const FieldRange &range( const char *fieldName ) const { return fbs _.range( fieldName ); } | |||
void registerSelf( long long nScanned ) const; | void registerSelf( long long nScanned ) const; | |||
// just for testing | // just for testing | |||
BoundList indexBounds() const { return indexBounds_; } | BoundList indexBounds() const { return indexBounds_; } | |||
private: | private: | |||
skipping to change at line 85 | skipping to change at line 86 | |||
bool unhelpful_; | bool unhelpful_; | |||
string _special; | string _special; | |||
IndexType * _type; | IndexType * _type; | |||
}; | }; | |||
// Inherit from this interface to implement a new query operation. | // Inherit from this interface to implement a new query operation. | |||
// The query optimizer will clone the QueryOp that is provided, giving | // The query optimizer will clone the QueryOp that is provided, giving | |||
// each clone its own query plan. | // each clone its own query plan. | |||
class QueryOp { | class QueryOp { | |||
public: | public: | |||
QueryOp() : complete_(), qp_(), error_() {} | QueryOp() : _complete(), _stopRequested(), _qp(), _error() {} | |||
virtual ~QueryOp() {} | virtual ~QueryOp() {} | |||
/** this gets called after a query plan is set? ERH 2/16/10 */ | /** this gets called after a query plan is set? ERH 2/16/10 */ | |||
virtual void init() = 0; | virtual void init() = 0; | |||
virtual void next() = 0; | virtual void next() = 0; | |||
virtual bool mayRecordPlan() const = 0; | virtual bool mayRecordPlan() const = 0; | |||
/** @return a copy of the inheriting class, which will be run with its own | /** @return a copy of the inheriting class, which will be run with its own | |||
query plan. | 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. | ||||
*/ | */ | |||
virtual QueryOp *clone() const = 0; | virtual QueryOp *clone() const = 0; | |||
bool complete() const { return complete_; } | bool complete() const { return _complete; } | |||
bool error() const { return error_; } | bool error() const { return _error; } | |||
string exceptionMessage() const { return exceptionMessage_; } | bool stopRequested() const { return _stopRequested; } | |||
const QueryPlan &qp() const { return *qp_; } | string exceptionMessage() const { return _exceptionMessage; } | |||
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; } | void setQueryPlan( const QueryPlan *qp ) { _qp = qp; } | |||
void setExceptionMessage( const string &exceptionMessage ) { | void setExceptionMessage( const string &exceptionMessage ) { | |||
error_ = true; | _error = true; | |||
exceptionMessage_ = exceptionMessage; | _exceptionMessage = exceptionMessage; | |||
} | } | |||
protected: | protected: | |||
void setComplete() { complete_ = true; } | void setComplete() { _complete = true; } | |||
void setStop() { setComplete(); _stopRequested = true; } | ||||
private: | private: | |||
bool complete_; | bool _complete; | |||
string exceptionMessage_; | bool _stopRequested; | |||
const QueryPlan *qp_; | string _exceptionMessage; | |||
bool error_; | const QueryPlan *_qp; | |||
bool _error; | ||||
}; | }; | |||
// Set of candidate query plans for a particular query. Used for runni ng | // Set of candidate query plans for a particular query. Used for runni ng | |||
// a QueryOp on these plans. | // a QueryOp on these plans. | |||
class QueryPlanSet { | class QueryPlanSet { | |||
public: | public: | |||
typedef boost::shared_ptr< QueryPlan > PlanPtr; | typedef boost::shared_ptr< QueryPlan > PlanPtr; | |||
typedef vector< PlanPtr > PlanSet; | typedef vector< PlanPtr > PlanSet; | |||
skipping to change at line 137 | skipping to change at line 143 | |||
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() ); | |||
int nPlans() const { return plans_.size(); } | int nPlans() const { return plans_.size(); } | |||
shared_ptr< QueryOp > runOp( QueryOp &op ); | shared_ptr< QueryOp > runOp( QueryOp &op ); | |||
template< class T > | template< class T > | |||
shared_ptr< T > runOp( T &op ) { | shared_ptr< T > runOp( T &op ) { | |||
return dynamic_pointer_cast< T >( runOp( static_cast< QueryOp& >( op ) ) ); | return dynamic_pointer_cast< T >( runOp( static_cast< QueryOp& >( op ) ) ); | |||
} | } | |||
const FieldRangeSet &fbs() const { return fbs_; } | ||||
BSONObj explain() const; | BSONObj explain() const; | |||
bool usingPrerecordedPlan() const { return usingPrerecordedPlan_; } | bool usingPrerecordedPlan() const { return usingPrerecordedPlan_; } | |||
PlanPtr getBestGuess() const; | PlanPtr getBestGuess() const; | |||
void setBestGuessOnly() { _bestGuessOnly = true; } | ||||
//for testing | ||||
const FieldRangeSet &fbs() const { return fbs_; } | ||||
private: | private: | |||
void addOtherPlans( bool checkFirst ); | void addOtherPlans( bool checkFirst ); | |||
void addPlan( PlanPtr plan, bool checkFirst ) { | void addPlan( PlanPtr 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 { | struct Runner { | |||
skipping to change at line 171 | skipping to change at line 179 | |||
PlanSet plans_; | PlanSet plans_; | |||
bool mayRecordPlan_; | bool mayRecordPlan_; | |||
bool usingPrerecordedPlan_; | bool usingPrerecordedPlan_; | |||
BSONObj hint_; | BSONObj hint_; | |||
BSONObj order_; | BSONObj order_; | |||
long long oldNScanned_; | long long oldNScanned_; | |||
bool honorRecordedPlan_; | bool honorRecordedPlan_; | |||
BSONObj min_; | BSONObj min_; | |||
BSONObj max_; | BSONObj max_; | |||
string _special; | string _special; | |||
bool _bestGuessOnly; | ||||
}; | ||||
// Handles $or type queries by generating a QueryPlanSet for each $or c | ||||
lause | ||||
// 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 queries for | ||||
use by | ||||
// subsequent $or queries, even though there may be a significant aggre | ||||
gate | ||||
// $nor component that would not be represented in QueryPattern. | ||||
class MultiPlanScanner { | ||||
public: | ||||
MultiPlanScanner( const char *ns, | ||||
const BSONObj &query, | ||||
const BSONObj &order, | ||||
const BSONElement *hint = 0, | ||||
bool honorRecordedPlan = true, | ||||
const BSONObj &min = BSONObj(), | ||||
const BSONObj &max = BSONObj() ); | ||||
shared_ptr< QueryOp > runOp( QueryOp &op ); | ||||
template< class T > | ||||
shared_ptr< T > runOp( T &op ) { | ||||
return dynamic_pointer_cast< T >( runOp( static_cast< QueryOp& | ||||
>( op ) ) ); | ||||
} | ||||
shared_ptr< QueryOp > runOpOnce( QueryOp &op ); | ||||
template< class T > | ||||
shared_ptr< T > runOpOnce( T &op ) { | ||||
return dynamic_pointer_cast< T >( runOpOnce( static_cast< Query | ||||
Op& >( op ) ) ); | ||||
} | ||||
bool mayRunMore() const { return _i < _n; } | ||||
BSONObj oldExplain() const { assertNotOr(); return _currentQps->exp | ||||
lain(); } | ||||
// just report this when only one query op | ||||
bool usingPrerecordedPlan() const { | ||||
return !_or && _currentQps->usingPrerecordedPlan(); | ||||
} | ||||
void setBestGuessOnly() { _bestGuessOnly = true; } | ||||
private: | ||||
//temp | ||||
void assertNotOr() const { | ||||
massert( 13266, "not implemented for $or query", !_or ); | ||||
} | ||||
// temp (and yucky) | ||||
BSONObj nextSimpleQuery() { | ||||
massert( 13267, "only generate simple query if $or", _or ); | ||||
massert( 13270, "no more simple queries", mayRunMore() ); | ||||
BSONObjBuilder b; | ||||
BSONArrayBuilder norb; | ||||
BSONObjIterator i( _query ); | ||||
while( i.more() ) { | ||||
BSONElement e = i.next(); | ||||
if ( strcmp( e.fieldName(), "$nor" ) == 0 ) { | ||||
massert( 13269, "$nor must be array", e.type() == Array | ||||
); | ||||
BSONObjIterator j( e.embeddedObject() ); | ||||
while( j.more() ) { | ||||
norb << j.next(); | ||||
} | ||||
} else if ( strcmp( e.fieldName(), "$or" ) == 0 ) { | ||||
BSONObjIterator j( e.embeddedObject() ); | ||||
for( int k = 0; k < _i; ++k ) { | ||||
norb << j.next(); | ||||
} | ||||
b << "$or" << BSON_ARRAY( j.next() ); | ||||
} else { | ||||
b.append( e ); | ||||
} | ||||
} | ||||
BSONArray nor = norb.arr(); | ||||
if ( !nor.isEmpty() ) { | ||||
b << "$nor" << nor; | ||||
} | ||||
++_i; | ||||
BSONObj ret = b.obj(); | ||||
return ret; | ||||
} | ||||
const char * _ns; | ||||
bool _or; | ||||
BSONObj _query; | ||||
// FieldRangeOrSet _fros; | ||||
auto_ptr< QueryPlanSet > _currentQps; | ||||
int _i; | ||||
int _n; | ||||
bool _honorRecordedPlan; | ||||
bool _bestGuessOnly; | ||||
}; | ||||
class MultiCursor : public Cursor { | ||||
public: | ||||
class CursorOp : public QueryOp { | ||||
public: | ||||
virtual shared_ptr< Cursor > newCursor() const = 0; | ||||
virtual auto_ptr< CoveredIndexMatcher > newMatcher() const = 0; | ||||
}; | ||||
// takes ownership of 'op' | ||||
MultiCursor( const char *ns, const BSONObj &pattern, const BSONObj | ||||
&order, auto_ptr< CursorOp > op = auto_ptr< CursorOp >( 0 ) ) | ||||
: _mps( new MultiPlanScanner( ns, pattern, order ) ) { | ||||
if ( op.get() ) { | ||||
_op = op; | ||||
} else { | ||||
_op.reset( new NoOp() ); | ||||
_mps->setBestGuessOnly(); | ||||
} | ||||
if ( _mps->mayRunMore() ) { | ||||
nextClause(); | ||||
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, auto_ptr< CoveredIndexMatcher > matcher ) | ||||
: _op( new NoOp() ), _c( c ), _mps( mps ), _matcher( matcher ) { | ||||
_mps->setBestGuessOnly(); | ||||
} | ||||
virtual bool ok() { return _c->ok(); } | ||||
virtual Record* _current() { return _c->_current(); } | ||||
virtual BSONObj current() { return _c->current(); } | ||||
virtual DiskLoc currLoc() { return _c->currLoc(); } | ||||
virtual bool advance() { | ||||
_c->advance(); | ||||
while( !ok() && _mps->mayRunMore() ) { | ||||
nextClause(); | ||||
} | ||||
return ok(); | ||||
} | ||||
virtual BSONObj currKey() const { return _c->currKey(); } | ||||
virtual DiskLoc refLoc() { return _c->refLoc(); } | ||||
virtual void noteLocation() { _c->noteLocation(); } | ||||
virtual void checkLocation() { | ||||
_c->checkLocation(); | ||||
if ( !ok() ) { | ||||
advance(); | ||||
} | ||||
} | ||||
virtual bool supportGetMore() { return true; } | ||||
// with update we could potentially get the same document on multip | ||||
le | ||||
// 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) { | ||||
return _c->getsetdup( loc ); | ||||
} | ||||
virtual CoveredIndexMatcher *matcher() const { return _matcher.get( | ||||
); } | ||||
private: | ||||
class NoOp : public CursorOp { | ||||
virtual void init() { setComplete(); } | ||||
virtual void next() {} | ||||
virtual bool mayRecordPlan() const { return false; } | ||||
virtual QueryOp *clone() const { return new NoOp(); } | ||||
virtual shared_ptr< Cursor > newCursor() const { return qp().ne | ||||
wCursor(); } | ||||
virtual auto_ptr< CoveredIndexMatcher > newMatcher() const { | ||||
return auto_ptr< CoveredIndexMatcher >( new CoveredIndexMat | ||||
cher( qp().query(), qp().indexKey() ) ); | ||||
} | ||||
}; | ||||
void nextClause() { | ||||
shared_ptr< CursorOp > best = _mps->runOpOnce( *_op ); | ||||
massert( 10401 , best->exceptionMessage(), best->complete() ); | ||||
_c = best->newCursor(); | ||||
_matcher = best->newMatcher(); | ||||
} | ||||
auto_ptr< CursorOp > _op; | ||||
shared_ptr< Cursor > _c; | ||||
auto_ptr< MultiPlanScanner > _mps; | ||||
auto_ptr< CoveredIndexMatcher > _matcher; | ||||
}; | }; | |||
// 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 ){ | inline bool isSimpleIdQuery( const BSONObj& query ){ | |||
return | return | |||
strcmp( query.firstElement().fieldName() , "_id" ) == 0 && | strcmp( query.firstElement().fieldName() , "_id" ) == 0 && | |||
query.nFields() == 1 && | query.nFields() == 1 && | |||
query.firstElement().isSimpleType(); | query.firstElement().isSimpleType(); | |||
} | } | |||
// matcher() will always work on the returned cursor | ||||
inline shared_ptr< Cursor > bestGuessCursor( const char *ns, const BSON | ||||
Obj &query, const BSONObj &sort ) { | ||||
if( !query.getField( "$or" ).eoo() ) { | ||||
return shared_ptr< Cursor >( new MultiCursor( ns, query, sort ) | ||||
); | ||||
} else { | ||||
shared_ptr< Cursor > ret = QueryPlanSet( ns, query, sort ).getB | ||||
estGuess()->newCursor(); | ||||
if ( !query.isEmpty() ) { | ||||
auto_ptr< CoveredIndexMatcher > matcher( new CoveredIndexMa | ||||
tcher( query, ret->indexKeyPattern() ) ); | ||||
ret->setMatcher( matcher ); | ||||
} | ||||
return ret; | ||||
} | ||||
} | ||||
} // namespace mongo | } // namespace mongo | |||
End of changes. 13 change blocks. | ||||
18 lines changed or deleted | 254 lines changed or added | |||
queryutil.h | queryutil.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 "jsobj.h" | #include "jsobj.h" | |||
namespace mongo { | namespace mongo { | |||
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; | |||
} | } | |||
}; | }; | |||
struct FieldInterval { | struct FieldInterval { | |||
FieldInterval(){} | FieldInterval(){} | |||
FieldInterval( const BSONElement& e ){ | FieldInterval( const BSONElement& e ){ | |||
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; | |||
bool valid() const { | bool valid() 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_.i | return ( cmp < 0 || ( cmp == 0 && _lower._inclusive && _upper._ | |||
nclusive_ ) ); | inclusive ) ); | |||
} | } | |||
}; | }; | |||
// range of a field's value that may be determined from query -- used t o | // range of a field's value that may be determined from query -- used t o | |||
// determine index limits | // determine index limits | |||
class FieldRange { | class FieldRange { | |||
public: | public: | |||
FieldRange( const BSONElement &e = BSONObj().firstElement() , bool isNot=false , bool optimize=true ); | FieldRange( const BSONElement &e = BSONObj().firstElement() , bool isNot=false , bool optimize=true ); | |||
const FieldRange &operator&=( const FieldRange &other ); | const FieldRange &operator&=( const FieldRange &other ); | |||
const FieldRange &operator|=( const FieldRange &other ); | const FieldRange &operator|=( const FieldRange &other ); | |||
BSONElement min() const { assert( !empty() ); return intervals_[ 0 | // does not remove fully contained ranges (eg [1,3] - [2,2] doesn't | |||
].lower_.bound_; } | remove anything) | |||
BSONElement max() const { assert( !empty() ); return intervals_[ in | // in future we can change so that an or on $in:[3] combined with $ | |||
tervals_.size() - 1 ].upper_.bound_; } | in:{$gt:2} doesn't scan 3 a second time | |||
bool minInclusive() const { assert( !empty() ); return intervals_[ | const FieldRange &operator-=( const FieldRange &other ); | |||
0 ].lower_.inclusive_; } | BSONElement min() const { assert( !empty() ); return _intervals[ 0 | |||
bool maxInclusive() const { assert( !empty() ); return intervals_[ | ]._lower._bound; } | |||
intervals_.size() - 1 ].upper_.inclusive_; } | BSONElement max() const { assert( !empty() ); return _intervals[ _i | |||
ntervals.size() - 1 ]._upper._bound; } | ||||
bool minInclusive() const { assert( !empty() ); return _intervals[ | ||||
0 ]._lower._inclusive; } | ||||
bool maxInclusive() const { assert( !empty() ); return _intervals[ | ||||
_intervals.size() - 1 ]._upper._inclusive; } | ||||
bool equality() const { | bool equality() const { | |||
return | return | |||
!empty() && | !empty() && | |||
min().woCompare( max(), false ) == 0 && | min().woCompare( max(), false ) == 0 && | |||
maxInclusive() && | maxInclusive() && | |||
minInclusive(); | minInclusive(); | |||
} | } | |||
bool nontrivial() const { | bool nontrivial() const { | |||
return | return | |||
! empty() && | ! empty() && | |||
( minKey.firstElement().woCompare( min(), false ) != 0 || | ( minKey.firstElement().woCompare( min(), false ) != 0 || | |||
maxKey.firstElement().woCompare( max(), false ) != 0 ); | maxKey.firstElement().woCompare( max(), false ) != 0 ); | |||
} | } | |||
bool empty() const { return intervals_.empty(); } | bool empty() const { return _intervals.empty(); } | |||
const vector< FieldInterval > &intervals() const { return in | const vector< FieldInterval > &intervals() const { return _i | |||
tervals_; } | ntervals; } | |||
string getSpecial() const { return _special; } | string getSpecial() const { return _special; } | |||
private: | private: | |||
BSONObj addObj( const BSONObj &o ); | BSONObj addObj( const BSONObj &o ); | |||
vector< FieldInterval > intervals_; | void finishOperation( const vector< FieldInterval > &newIntervals, | |||
vector< BSONObj > objData_; | const FieldRange &other ); | |||
vector< FieldInterval > _intervals; | ||||
vector< BSONObj > _objData; | ||||
string _special; | string _special; | |||
}; | }; | |||
// implements query pattern matching, used to determine if a query is | // implements query pattern matching, used to determine if a query is | |||
// similar to an earlier query and should use the same plan | // similar to an earlier query and should use the same plan | |||
class QueryPattern { | class QueryPattern { | |||
public: | public: | |||
friend class FieldRangeSet; | friend class FieldRangeSet; | |||
enum Type { | enum Type { | |||
Equality, | Equality, | |||
skipping to change at line 104 | skipping to change at line 108 | |||
bool operator==( const QueryPattern &other ) const { | bool operator==( const QueryPattern &other ) const { | |||
bool less = operator<( other ); | bool less = operator<( other ); | |||
bool more = other.operator<( *this ); | bool more = other.operator<( *this ); | |||
assert( !( less && more ) ); | assert( !( less && more ) ); | |||
return !( less || more ); | return !( less || more ); | |||
} | } | |||
bool operator!=( const QueryPattern &other ) const { | bool operator!=( const QueryPattern &other ) const { | |||
return !operator==( other ); | return !operator==( other ); | |||
} | } | |||
bool operator<( const QueryPattern &other ) const { | bool operator<( const QueryPattern &other ) const { | |||
map< string, Type >::const_iterator i = fieldTypes_.begin(); | map< string, Type >::const_iterator i = _fieldTypes.begin(); | |||
map< string, Type >::const_iterator j = other.fieldTypes_.begin | map< string, Type >::const_iterator j = other._fieldTypes.begin | |||
(); | (); | |||
while( i != fieldTypes_.end() ) { | while( i != _fieldTypes.end() ) { | |||
if ( j == other.fieldTypes_.end() ) | if ( j == other._fieldTypes.end() ) | |||
return false; | return false; | |||
if ( i->first < j->first ) | if ( i->first < j->first ) | |||
return true; | return true; | |||
else if ( i->first > j->first ) | else if ( i->first > j->first ) | |||
return false; | return false; | |||
if ( i->second < j->second ) | if ( i->second < j->second ) | |||
return true; | return true; | |||
else if ( i->second > j->second ) | else if ( i->second > j->second ) | |||
return false; | return false; | |||
++i; | ++i; | |||
++j; | ++j; | |||
} | } | |||
if ( j != other.fieldTypes_.end() ) | if ( j != other._fieldTypes.end() ) | |||
return true; | return true; | |||
return sort_.woCompare( other.sort_ ) < 0; | return _sort.woCompare( other._sort ) < 0; | |||
} | } | |||
private: | private: | |||
QueryPattern() {} | QueryPattern() {} | |||
void setSort( const BSONObj sort ) { | void setSort( const BSONObj sort ) { | |||
sort_ = normalizeSort( sort ); | _sort = normalizeSort( sort ); | |||
} | } | |||
BSONObj static normalizeSort( const BSONObj &spec ) { | BSONObj static normalizeSort( const BSONObj &spec ) { | |||
if ( spec.isEmpty() ) | if ( spec.isEmpty() ) | |||
return spec; | return spec; | |||
int direction = ( spec.firstElement().number() >= 0 ) ? 1 : -1; | int direction = ( spec.firstElement().number() >= 0 ) ? 1 : -1; | |||
BSONObjIterator i( spec ); | BSONObjIterator i( spec ); | |||
BSONObjBuilder b; | BSONObjBuilder b; | |||
while( i.moreWithEOO() ) { | while( i.moreWithEOO() ) { | |||
BSONElement e = i.next(); | BSONElement e = i.next(); | |||
if ( e.eoo() ) | if ( e.eoo() ) | |||
break; | break; | |||
b.append( e.fieldName(), direction * ( ( e.number() >= 0 ) ? -1 : 1 ) ); | b.append( e.fieldName(), direction * ( ( e.number() >= 0 ) ? -1 : 1 ) ); | |||
} | } | |||
return b.obj(); | return b.obj(); | |||
} | } | |||
map< string, Type > fieldTypes_; | map< string, Type > _fieldTypes; | |||
BSONObj sort_; | BSONObj _sort; | |||
}; | }; | |||
// a BoundList contains intervals specified by inclusive start | // a BoundList contains intervals specified by inclusive start | |||
// and end bounds. The intervals should be nonoverlapping and occur in | // 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} | // 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 | // and direction +1, one valid BoundList is: (1, 2); (4, 6). The same BoundList | |||
// would be valid for index {i:-1} with direction -1. | // would be valid for index {i:-1} with direction -1. | |||
typedef vector< pair< BSONObj, BSONObj > > BoundList; | typedef vector< pair< BSONObj, BSONObj > > BoundList; | |||
// ranges of fields' value that may be determined from query -- used to | // ranges of fields' value that may be determined from query -- used to | |||
// determine index limits | // determine index limits | |||
class FieldRangeSet { | class FieldRangeSet { | |||
public: | public: | |||
friend class FieldRangeOrSet; | ||||
FieldRangeSet( const char *ns, const BSONObj &query , bool optimize =true ); | FieldRangeSet( const char *ns, const BSONObj &query , bool optimize =true ); | |||
const FieldRange &range( const char *fieldName ) const { | const FieldRange &range( const char *fieldName ) const { | |||
map< string, FieldRange >::const_iterator f = ranges_.find( fie | map< string, FieldRange >::const_iterator f = _ranges.find( fie | |||
ldName ); | ldName ); | |||
if ( f == ranges_.end() ) | if ( f == _ranges.end() ) | |||
return trivialRange(); | return trivialRange(); | |||
return f->second; | return f->second; | |||
} | } | |||
int nNontrivialRanges() const { | int nNontrivialRanges() const { | |||
int count = 0; | int count = 0; | |||
for( map< string, FieldRange >::const_iterator i = ranges_.begi n(); i != ranges_.end(); ++i ) | for( map< string, FieldRange >::const_iterator i = _ranges.begi n(); i != _ranges.end(); ++i ) | |||
if ( i->second.nontrivial() ) | if ( i->second.nontrivial() ) | |||
++count; | ++count; | |||
return count; | return count; | |||
} | } | |||
const char *ns() const { return ns_; } | const char *ns() const { return _ns; } | |||
BSONObj query() const { return query_; } | ||||
// if fields is specified, order fields of returned object to match those of 'fields' | // if fields is specified, order fields of returned object to match those of 'fields' | |||
BSONObj simplifiedQuery( const BSONObj &fields = BSONObj() ) const; | BSONObj simplifiedQuery( const BSONObj &fields = BSONObj() ) const; | |||
bool matchPossible() const { | bool matchPossible() const { | |||
for( map< string, FieldRange >::const_iterator i = ranges_.begi n(); i != ranges_.end(); ++i ) | for( map< string, FieldRange >::const_iterator i = _ranges.begi n(); i != _ranges.end(); ++i ) | |||
if ( i->second.empty() ) | if ( i->second.empty() ) | |||
return false; | return false; | |||
return true; | return true; | |||
} | } | |||
QueryPattern pattern( const BSONObj &sort = BSONObj() ) const; | QueryPattern pattern( const BSONObj &sort = BSONObj() ) const; | |||
BoundList indexBounds( const BSONObj &keyPattern, int direction ) c onst; | BoundList indexBounds( const BSONObj &keyPattern, int direction ) c onst; | |||
string getSpecial() const; | string getSpecial() const; | |||
// intended to handle sets without _orSets | ||||
const FieldRangeSet &operator-=( const FieldRangeSet &other ) { | ||||
for( map< string, FieldRange >::const_iterator i = other._range | ||||
s.begin(); | ||||
i != other._ranges.end(); ++i ) { | ||||
map< string, FieldRange >::iterator f = _ranges.find( i->fi | ||||
rst.c_str() ); | ||||
if ( f != _ranges.end() ) | ||||
f->second -= i->second; | ||||
} | ||||
return *this; | ||||
} | ||||
BSONObj query() const { return _query; } | ||||
private: | private: | |||
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 *trivialRange_; | |||
static FieldRange &trivialRange(); | static FieldRange &trivialRange(); | |||
mutable map< string, FieldRange > ranges_; | mutable map< string, FieldRange > _ranges; | |||
const char *ns_; | const char *_ns; | |||
BSONObj query_; | BSONObj _query; | |||
}; | }; | |||
// generages FieldRangeSet objects, accounting for or clauses | ||||
// class FieldRangeOrSet { | ||||
// public: | ||||
// FieldRangeOrSet( const char *ns, const BSONObj &query , bool opti | ||||
mize=true ); | ||||
// // if there's a trivial or clause, we won't use or ranges to help | ||||
with scanning | ||||
// bool trivialOr() const { | ||||
// for( list< FieldRangeSet >::const_iterator i = _orSets.begin( | ||||
); i != _orSets.end(); ++i ) { | ||||
// if ( i->nNontrivialRanges() == 0 ) { | ||||
// return true; | ||||
// } | ||||
// } | ||||
// return false; | ||||
// } | ||||
// bool orFinished() const { return _orFound && _orSets.empty(); } | ||||
// // removes first or clause, and removes the field ranges it cover | ||||
s from all subsequent or clauses | ||||
// 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; | ||||
// } | ||||
// } | ||||
// _orSets.pop_front(); | ||||
// } | ||||
// private: | ||||
// FieldRangeSet _baseSet; | ||||
// list< FieldRangeSet > _orSets; | ||||
// bool _orFound; | ||||
// }; | ||||
/** | /** | |||
used for doing field limiting | used for doing field limiting | |||
*/ | */ | |||
class FieldMatcher { | class FieldMatcher { | |||
public: | public: | |||
FieldMatcher() | FieldMatcher() | |||
: _include(true) | : _include(true) | |||
, _special(false) | , _special(false) | |||
, _includeID(true) | , _includeID(true) | |||
, _skip(0) | , _skip(0) | |||
skipping to change at line 217 | skipping to change at line 269 | |||
void add( const BSONObj& o ); | void add( const BSONObj& o ); | |||
void append( BSONObjBuilder& b , const BSONElement& e ) const; | void append( BSONObjBuilder& b , const BSONElement& e ) const; | |||
BSONObj getSpec() const; | BSONObj getSpec() const; | |||
bool includeID() { return _includeID; } | bool includeID() { return _includeID; } | |||
private: | private: | |||
void add( const string& field, bool include ); | void add( const string& field, bool include ); | |||
void add( const string& field, int skip, int limit ); | void add( const string& field, int skip, int limit ); | |||
void appendArray( BSONObjBuilder& b , const BSONObj& a ) const; | void appendArray( BSONObjBuilder& b , const BSONObj& a , bool neste d=false) const; | |||
bool _include; // true if default at this level is to include | bool _include; // true if default at this level is to include | |||
bool _special; // true if this level can't be skipped or included w ithout recursing | bool _special; // true if this level can't be skipped or included w ithout recursing | |||
//TODO: benchmark vector<pair> vs map | //TODO: benchmark vector<pair> vs map | |||
typedef map<string, boost::shared_ptr<FieldMatcher> > FieldMap; | typedef map<string, boost::shared_ptr<FieldMatcher> > FieldMap; | |||
FieldMap _fields; | FieldMap _fields; | |||
BSONObj _source; | BSONObj _source; | |||
bool _includeID; | bool _includeID; | |||
// used for $slice operator | // used for $slice operator | |||
End of changes. 23 change blocks. | ||||
46 lines changed or deleted | 107 lines changed or added | |||
queue.h | queue.h | |||
---|---|---|---|---|
skipping to change at line 32 | skipping to change at line 32 | |||
#include <queue> | #include <queue> | |||
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") { } | ||||
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(); | |||
} | } | |||
End of changes. 1 change blocks. | ||||
0 lines changed or deleted | 2 lines changed or added | |||
rec.h | rec.h | |||
---|---|---|---|---|
skipping to change at line 30 | skipping to change at line 30 | |||
_ support > 2GB data per file | _ support > 2GB data per file | |||
_ multiple files, not just indexes.dat | _ multiple files, not just indexes.dat | |||
_ lazier writes? (may be done?) | _ lazier writes? (may be done?) | |||
_ configurable cache size | _ configurable cache size | |||
_ fix on abnormal terminations to be able to restart some | _ fix on abnormal terminations to be able to restart some | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "reci.h" | #include "reci.h" | |||
#include "reccache.h" | //#include "reccache.h" | |||
namespace mongo { | namespace mongo { | |||
/* ------------------------------------------------------------------------ -- | /* ------------------------------------------------------------------------ -- | |||
A RecStoreInterface for the normal mongo mem mapped file (MongoDataFile) | A RecStoreInterface for the normal mongo mem mapped file (MongoDataFile) | |||
storage | storage | |||
*/ | */ | |||
NamespaceDetails* nsdetails_notinline(const char *ns); | NamespaceDetails* nsdetails_notinline(const char *ns); | |||
End of changes. 1 change blocks. | ||||
1 lines changed or deleted | 1 lines changed or added | |||
reccache.h | reccache.h | |||
---|---|---|---|---|
skipping to change at line 34 | skipping to change at line 34 | |||
dblock | dblock | |||
RecCache::rcmutex | RecCache::rcmutex | |||
i.e. always lock dblock first if you lock both | i.e. always lock dblock first if you lock both | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#error deprecated | ||||
#include "reci.h" | #include "reci.h" | |||
#include "recstore.h" | #include "recstore.h" | |||
namespace mongo { | namespace mongo { | |||
class RecCache { | class RecCache { | |||
struct Node { | struct Node { | |||
Node(void* _data) : data((char *) _data) { dirty = false; newer = 0 ; } | Node(void* _data) : data((char *) _data) { dirty = false; newer = 0 ; } | |||
~Node() { | ~Node() { | |||
free(data); | free(data); | |||
skipping to change at line 246 | skipping to change at line 248 | |||
VIRT void closeFiles(string dbname, string path) { | VIRT void closeFiles(string dbname, string path) { | |||
theRecCache.closeFiles(dbname, dbpath); | theRecCache.closeFiles(dbname, dbpath); | |||
} | } | |||
}; | }; | |||
/* see concurrency.h - note on a lock reset from read->write we don't | /* see concurrency.h - note on a lock reset from read->write we don't | |||
call dbunlocking_read, we just wait for the final dbunlocking_write | call dbunlocking_read, we just wait for the final dbunlocking_write | |||
call | call | |||
*/ | */ | |||
inline void dbunlocking_read() { | //inline void dbunlocking_read() { | |||
/* | /* | |||
Client *c = currentClient.get(); | Client *c = currentClient.get(); | |||
if ( c ) | if ( c ) | |||
c->top.clientStop(); | c->top.clientStop(); | |||
*/ | */ | |||
} | //} | |||
inline void dbunlocking_write() { | //inline void dbunlocking_write() { | |||
theRecCache.ejectOld(); | //theRecCache.ejectOld(); | |||
dbunlocking_read(); | // dbunlocking_read(); | |||
} | //} | |||
} /*namespace*/ | } /*namespace*/ | |||
End of changes. 4 change blocks. | ||||
6 lines changed or deleted | 8 lines changed or added | |||
recstore.h | recstore.h | |||
---|---|---|---|---|
skipping to change at line 20 | skipping to change at line 20 | |||
* 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 | |||
#error deprecated | ||||
#include "../util/file.h" | #include "../util/file.h" | |||
namespace mongo { | namespace mongo { | |||
using boost::uint32_t; | using boost::uint32_t; | |||
using boost::uint64_t; | using boost::uint64_t; | |||
/* Current version supports only consistent record sizes within a store. */ | /* Current version supports only consistent record sizes within a store. */ | |||
class BasicRecStore { | class BasicRecStore { | |||
End of changes. 1 change blocks. | ||||
0 lines changed or deleted | 2 lines changed or added | |||
redef_macros.h | redef_macros.h | |||
---|---|---|---|---|
skipping to change at line 32 | skipping to change at line 32 | |||
#if defined(MONGO_MACROS_CLEANED) | #if defined(MONGO_MACROS_CLEANED) | |||
// util/allocator.h | // util/allocator.h | |||
#define malloc MONGO_malloc | #define malloc MONGO_malloc | |||
#define realloc MONGO_realloc | #define realloc MONGO_realloc | |||
// util/assert_util.h | // util/assert_util.h | |||
#define assert MONGO_assert | #define assert MONGO_assert | |||
#define dassert MONGO_dassert | #define dassert MONGO_dassert | |||
#define wassert MONGO_wassert | #define wassert MONGO_wassert | |||
#define massert MONGO_massert | ||||
#define uassert MONGO_uassert | ||||
#define BOOST_CHECK_EXCEPTION MONGO_BOOST_CHECK_EXCEPTION | #define BOOST_CHECK_EXCEPTION MONGO_BOOST_CHECK_EXCEPTION | |||
#define DESTRUCTOR_GUARD MONGO_DESTRUCTOR_GUARD | #define DESTRUCTOR_GUARD MONGO_DESTRUCTOR_GUARD | |||
// util/goodies.h | // util/goodies.h | |||
#define PRINT MONGO_PRINT | #define PRINT MONGO_PRINT | |||
#define PRINTFL MONGO_PRINTFL | #define PRINTFL MONGO_PRINTFL | |||
#define asctime MONGO_asctime | #define asctime MONGO_asctime | |||
#define gmtime MONGO_gmtime | #define gmtime MONGO_gmtime | |||
#define localtime MONGO_localtime | #define localtime MONGO_localtime | |||
#define ctime MONGO_ctime | #define ctime MONGO_ctime | |||
End of changes. 1 change blocks. | ||||
0 lines changed or deleted | 2 lines changed or added | |||
repl.h | repl.h | |||
---|---|---|---|---|
skipping to change at line 81 | skipping to change at line 81 | |||
extern ReplSettings replSettings; | extern ReplSettings replSettings; | |||
bool cloneFrom(const char *masterHost, string& errmsg, const string& fr omdb, bool logForReplication, | bool cloneFrom(const char *masterHost, string& errmsg, const string& fr omdb, bool logForReplication, | |||
bool slaveOk, bool useReplAuth, bool snap shot); | bool slaveOk, bool useReplAuth, bool snap shot); | |||
/* A replication exception */ | /* A replication exception */ | |||
class SyncException : public DBException { | class SyncException : public DBException { | |||
public: | public: | |||
virtual const char* what() const throw() { return "sync exception"; } | virtual const char* what() const throw() { return "sync exception"; } | |||
virtual int getCode(){ return 10001; } | virtual int getCode() const { return 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: ..., localLogTs: . .., dbsNextPass: { ... }, incompleteCloneDbs: { ... } } | |||
'source' defaults to 'main'; support for multiple source names is | 'source' defaults to 'main'; support for multiple source names is | |||
End of changes. 1 change blocks. | ||||
1 lines changed or deleted | 1 lines changed or added | |||
replpair.h | replpair.h | |||
---|---|---|---|---|
skipping to change at line 121 | skipping to change at line 121 | |||
If 'client' is not specified, the current client is used. | If 'client' is not specified, the current client is used. | |||
*/ | */ | |||
inline bool _isMaster( const char *client = 0 ) { | inline bool _isMaster( const char *client = 0 ) { | |||
if( replSet ) { | if( replSet ) { | |||
if( theReplSet ) | if( theReplSet ) | |||
return theReplSet->isMaster(client); | return theReplSet->isMaster(client); | |||
return false; | return false; | |||
} | } | |||
if( ! replSettings.slave ) | if( ! replSettings.slave ) | |||
return true; | return true; | |||
if ( replAllDead ) | if ( replAllDead ) | |||
return false; | return false; | |||
if ( replPair ) { | if ( replPair ) { | |||
if( replPair->state == ReplPair::State_Master ) | if( replPair->state == ReplPair::State_Master ) | |||
return true; | return true; | |||
} | } | |||
else { | else { | |||
if( replSettings.master ) { | if( replSettings.master ) { | |||
// if running with --master --slave, allow. note that mast er is also true | // if running with --master --slave, allow. note that mast er is also true | |||
// for repl pairs so the check for replPair above is import ant. | // for repl pairs so the check for replPair above is import ant. | |||
return true; | return true; | |||
} | } | |||
} | } | |||
if ( cc().isGod() ) | if ( cc().isGod() ) | |||
return true; | return true; | |||
skipping to change at line 165 | skipping to change at line 165 | |||
inline void notMasterUnless(bool expr) { | inline void notMasterUnless(bool expr) { | |||
uassert( 10107 , "not master" , expr ); | uassert( 10107 , "not master" , expr ); | |||
} | } | |||
/* we allow queries to SimpleSlave's -- but not to the slave (nonmaster ) member of a replica pair | /* we allow queries to SimpleSlave's -- but not to the slave (nonmaster ) member of a replica pair | |||
so that queries to a pair are realtime consistent as much as possibl e. use setSlaveOk() to | so that queries to a pair are realtime consistent as much as possibl e. use setSlaveOk() to | |||
query the nonmaster member of a replica pair. | query the nonmaster member of a replica pair. | |||
*/ | */ | |||
inline void replVerifyReadsOk(ParsedQuery& pq) { | inline void replVerifyReadsOk(ParsedQuery& pq) { | |||
if( replSet ) | if( replSet ) | |||
notMasterUnless(isMaster()); | notMasterUnless(isMaster() || pq.hasOption(QueryOption_SlaveOk) ); | |||
else | else | |||
notMasterUnless(isMaster() || pq.hasOption( QueryOption_SlaveOk ) || replSettings.slave == SimpleSlave ); | notMasterUnless(isMaster() || pq.hasOption(QueryOption_SlaveOk) || replSettings.slave == SimpleSlave ); | |||
} | } | |||
inline bool isMasterNs( const char *ns ) { | inline bool isMasterNs( const char *ns ) { | |||
char cl[ 256 ]; | char cl[ 256 ]; | |||
nsToDatabase( ns, cl ); | nsToDatabase( ns, cl ); | |||
return isMaster( cl ); | return isMaster( cl ); | |||
} | } | |||
inline ReplPair::ReplPair(const char *remoteEnd, const char *arb) { | inline ReplPair::ReplPair(const char *remoteEnd, const char *arb) { | |||
state = -1; | state = -1; | |||
End of changes. 4 change blocks. | ||||
7 lines changed or deleted | 7 lines changed or added | |||
replset.h | replset.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 "../../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/hostandport.h" | #include "../../util/hostandport.h" | |||
#include "../commands.h" | ||||
#include "rstime.h" | ||||
#include "rsmember.h" | ||||
#include "rs_config.h" | ||||
namespace mongo { | namespace mongo { | |||
struct Target; | ||||
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; | ||||
/** most operations on a ReplSet object should be done while locked. */ | ||||
class RSBase : boost::noncopyable { | ||||
private: | ||||
mutex m; | ||||
int _locked; | ||||
protected: | ||||
RSBase() : m("RSBase"), _locked(0) { } | ||||
class lock : scoped_lock { | ||||
RSBase& _b; | ||||
public: | ||||
lock(RSBase* b) : scoped_lock(b->m), _b(*b) { b->_locked++; } | ||||
~lock() { _b._locked--; } | ||||
}; | ||||
bool locked() const { return _locked; } | ||||
}; | ||||
/* information about the entire repl set, such as the various servers i n the set, and their state */ | /* information about the entire repl set, such as the various servers i n the set, and their state */ | |||
/* note: We currently do not free mem when the set goes away - it is as sumed the replset is a | /* note: We currently do not free mem when the set goes away - it is as sumed the replset is a | |||
singleton and long lived. | singleton and long lived. | |||
*/ | */ | |||
class ReplSet { | class ReplSet : RSBase { | |||
public: | public: | |||
bool isMaster(const char *client) { | /** info on our state if the replset isn't yet "up". for example, | |||
//zzz | if we are pre-initiation. */ | |||
return false; | enum StartupStatus { | |||
} | PRESTART=0, LOADINGCONFIG=1, BADCONFIG=2, EMPTYCONFIG=3, | |||
void fillIsMaster(BSONObjBuilder&); | EMPTYUNREACHABLE=4, STARTED=5, SOON=6 | |||
}; | ||||
static enum StartupStatus { PRESTART=0, LOADINGCONFIG=1, BADCONFIG= | static StartupStatus startupStatus; | |||
2, EMPTYCONFIG=3, EMPTYUNREACHABLE=4, FINISHME=5 } startupStatus; | ||||
static string startupStatusMsg; | static string startupStatusMsg; | |||
bool fatal; | ||||
bool ok() const { return !fatal; } | void fatal(); | |||
bool isMaster(const char *client); | ||||
void fillIsMaster(BSONObjBuilder&); | ||||
bool ok() const { return _myState != FATAL; } | ||||
MemberState state() const { return _myState; } | ||||
string name() const { return _name; } /* @return replica set's logi | ||||
cal name */ | ||||
/* @return replica set's logical name */ | void relinquish(); | |||
string getName() const { return _name; } | void assumePrimary(); | |||
/* cfgString format is | /* cfgString format is | |||
replsetname/host1,host2:port,... | replsetname/host1,host2:port,... | |||
where :port is optional. | where :port is optional. | |||
throws exception if a problem initializing. */ | ||||
throws exception if a problem initializing. | ||||
*/ | ||||
ReplSet(string cfgString); | ReplSet(string cfgString); | |||
/* call after constructing to start - returns fairly quickly after | ||||
launching its threads */ | ||||
void go() { _myState = STARTUP2; startThreads(); } | ||||
// for replSetGetStatus command | // for replSetGetStatus command | |||
void summarizeStatus(BSONObjBuilder&) const; | void summarizeStatus(BSONObjBuilder&) const; | |||
void summarizeAsHtml(stringstream&) const; | ||||
const ReplSetConfig& config() { return *_cfg; } | ||||
private: | private: | |||
MemberState _myState; | ||||
string _name; | string _name; | |||
const vector<HostAndPort> *_seeds; | const vector<HostAndPort> *_seeds; | |||
ReplSetConfig *_cfg; | ||||
/** load our configuration from admin.replset. try seed machines t oo. | /** load our configuration from admin.replset. try seed machines t oo. | |||
throws exception if a problem. | throws exception if a problem. | |||
*/ | */ | |||
void _loadConfigFinish(vector<ReplSetConfig>& v); | ||||
void loadConfig(); | void loadConfig(); | |||
void initFromConfig(ReplSetConfig& c);//, bool save); | ||||
// void addMemberIfMissing(const HostAndPort& p); | class Consensus { | |||
ReplSet &rs; | ||||
struct LastYea { | ||||
LastYea() : when(0), who(0xffffffff) { } | ||||
time_t when; | ||||
unsigned who; | ||||
}; | ||||
Atomic<LastYea> ly; | ||||
unsigned yea(unsigned memberId); // throws VoteException | ||||
void _electSelf(); | ||||
bool weAreFreshest(); | ||||
public: | ||||
Consensus(ReplSet *t) : rs(*t) { } | ||||
int totalVotes() const; | ||||
bool aMajoritySeemsToBeUp() const; | ||||
void electSelf(); | ||||
void electCmdReceived(BSONObj, BSONObjBuilder*); | ||||
} elect; | ||||
struct MemberInfo : public List1<MemberInfo>::Base { | public: | |||
MemberInfo(string h, int p) : _port(p), _host(h) { | class Member : public List1<Member>::Base { | |||
_dead = false; | public: | |||
_lastHeartbeat = 0; | Member(HostAndPort h, unsigned ord, const ReplSetConfig::Member | |||
_upSince = 0; | Cfg *c); | |||
_health = -1.0; | ||||
} | string fullName() const { return h().toString(); } | |||
string fullName() const { | const ReplSetConfig::MemberCfg& config() const { return *_confi | |||
if( _port < 0 ) return _host; | g; } | |||
stringstream ss; | const HeartbeatInfo& hbinfo() const { return _hbinfo; } | |||
ss << _host << ':' << _port; | string lhb() { return _hbinfo.lastHeartbeatMsg; } | |||
return ss.str(); | MemberState state() const { return _hbinfo.hbstate; } | |||
} | const HostAndPort& h() const { return _h; } | |||
double health() const { return _health; } | unsigned id() const { return _hbinfo.id(); } | |||
time_t upSince() const { return _upSince; } | ||||
time_t lastHeartbeat() const { return _lastHeartbeat; } | void summarizeAsHtml(stringstream& s) const; | |||
friend class ReplSet; | ||||
private: | private: | |||
friend class FeedbackThread; // feedbackthread is the primary w | const ReplSetConfig::MemberCfg *_config; /* todo: when this cha | |||
riter to these objects | nges??? */ | |||
HostAndPort _h; | ||||
HeartbeatInfo _hbinfo; | ||||
}; | ||||
list<HostAndPort> memberHostnames() const; | ||||
const Member* currentPrimary() const { return _currentPrimary; } | ||||
bool primary() const { return _myState == PRIMARY; } | ||||
const ReplSetConfig::MemberCfg& myConfig() const { return _self->co | ||||
nfig(); } | ||||
void msgUpdateHBInfo(HeartbeatInfo); | ||||
bool _dead; | private: | |||
const int _port; | const Member *_currentPrimary; | |||
const string _host; | Member *_self; | |||
double _health; | List1<Member> _members; /* all members of the set EXCEPT self. */ | |||
time_t _lastHeartbeat; | ||||
time_t _upSince; | public: | |||
class Manager : public task::Server { | ||||
bool got(const any&); | ||||
ReplSet *rs; | ||||
int _primary; | ||||
const Member* findOtherPrimary(); | ||||
void noteARemoteIsPrimary(const Member *); | ||||
public: | public: | |||
DiagStr _lastHeartbeatErrMsg; | Manager(ReplSet *rs); | |||
void msgReceivedNewConfig(BSONObj) { assert(false); } | ||||
void msgCheckNewState(); | ||||
}; | }; | |||
/* all members of the set EXCEPT SELF. */ | shared_ptr<Manager> mgr; | |||
List1<MemberInfo> _members; | ||||
void startHealthThreads(); | private: | |||
Member* head() const { return _members.head(); } | ||||
void getTargets(list<Target>&); | ||||
static string stateAsStr(MemberState state); | ||||
static string stateAsHtml(MemberState state); | ||||
void startThreads(); | ||||
friend class FeedbackThread; | friend class FeedbackThread; | |||
friend class CmdReplSetElect; | ||||
}; | ||||
inline void ReplSet::fatal() | ||||
{ | ||||
lock l(this); | ||||
_myState = FATAL; | ||||
log() << "replSet error fatal error, stopping replication" << rsLog | ||||
; | ||||
} | ||||
inline ReplSet::Member::Member(HostAndPort h, unsigned ord, const ReplS | ||||
etConfig::MemberCfg *c) : | ||||
_config(c), _h(h), _hbinfo(ord) { } | ||||
inline bool ReplSet::isMaster(const char *client) { | ||||
/* todo replset */ | ||||
return false; | ||||
} | ||||
class ReplSetCommand : public Command { | ||||
protected: | ||||
ReplSetCommand(const char * s) : Command(s) { } | ||||
virtual bool slaveOk() const { return true; } | ||||
virtual bool adminOnly() const { return true; } | ||||
virtual bool logTheOp() { return false; } | ||||
virtual LockType locktype() const { return NONE; } | ||||
virtual void help( stringstream &help ) const { help << "internal"; | ||||
} | ||||
bool check(string& errmsg, BSONObjBuilder& result) { | ||||
if( !replSet ) { | ||||
errmsg = "not running with --replSet"; | ||||
return false; | ||||
} | ||||
if( theReplSet == 0 ) { | ||||
result.append("startupStatus", ReplSet::startupStatus); | ||||
errmsg = ReplSet::startupStatusMsg.empty() ? "replset unkno | ||||
wn error 2" : ReplSet::startupStatusMsg; | ||||
return false; | ||||
} | ||||
return true; | ||||
} | ||||
}; | }; | |||
} | } | |||
End of changes. 24 change blocks. | ||||
45 lines changed or deleted | 161 lines changed or added | |||
request.h | request.h | |||
---|---|---|---|---|
skipping to change at line 28 | skipping to change at line 28 | |||
#pragma once | #pragma once | |||
#include "../pch.h" | #include "../pch.h" | |||
#include "../util/message.h" | #include "../util/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 ClientInfo; | class ClientInfo; | |||
class Request : boost::noncopyable { | class Request : boost::noncopyable { | |||
public: | public: | |||
Request( Message& m, AbstractMessagingPort* p ); | Request( Message& m, AbstractMessagingPort* p ); | |||
// ---- message info ----- | // ---- message info ----- | |||
const char * getns() const { | const char * getns() const { | |||
return _d.getns(); | return _d.getns(); | |||
} | } | |||
int op() const { | int op() const { | |||
return _m.data->operation(); | return _m.operation(); | |||
} | } | |||
bool expectResponse() const { | bool expectResponse() const { | |||
return op() == dbQuery || op() == dbGetMore; | return op() == dbQuery || op() == dbGetMore; | |||
} | } | |||
bool isCommand() const; | bool isCommand() const; | |||
MSGID id() const { | MSGID id() const { | |||
return _id; | return _id; | |||
} | } | |||
DBConfig * getConfig() const { | DBConfig * getConfig() const { | |||
return _config; | return _config; | |||
} | } | |||
bool isShardingEnabled() const { | bool isShardingEnabled() const { | |||
return _config->isShardingEnabled(); | return _config->isShardingEnabled(); | |||
} | } | |||
ChunkManager * getChunkManager() const { | ChunkManagerPtr getChunkManager() const { | |||
return _chunkManager; | return _chunkManager; | |||
} | } | |||
int getClientId() const { | int getClientId() const { | |||
return _clientId; | return _clientId; | |||
} | } | |||
ClientInfo * getClientInfo() const { | ClientInfo * getClientInfo() const { | |||
return _clientInfo; | return _clientInfo; | |||
} | } | |||
skipping to change at line 85 | skipping to change at line 86 | |||
void reply( Message & response ){ | void reply( Message & response ){ | |||
_p->reply( _m , response , _id ); | _p->reply( _m , response , _id ); | |||
} | } | |||
Message& m() { return _m; } | Message& m() { return _m; } | |||
DbMessage& d() { return _d; } | DbMessage& d() { return _d; } | |||
AbstractMessagingPort* p() const { return _p; } | AbstractMessagingPort* p() const { return _p; } | |||
void process( int attempt = 0 ); | void process( int attempt = 0 ); | |||
void gotInsert(); | ||||
private: | private: | |||
void reset( bool reload=false ); | void reset( bool reload=false ); | |||
Message& _m; | Message& _m; | |||
DbMessage _d; | DbMessage _d; | |||
AbstractMessagingPort* _p; | AbstractMessagingPort* _p; | |||
MSGID _id; | MSGID _id; | |||
DBConfig * _config; | DBConfig * _config; | |||
ChunkManager * _chunkManager; | ChunkManagerPtr _chunkManager; | |||
int _clientId; | int _clientId; | |||
ClientInfo * _clientInfo; | ClientInfo * _clientInfo; | |||
OpCounters* _counter; | ||||
}; | }; | |||
typedef map<int,ClientInfo*> ClientCache; | typedef map<int,ClientInfo*> ClientCache; | |||
class ClientInfo { | class ClientInfo { | |||
public: | public: | |||
ClientInfo( int clientId ); | ClientInfo( int clientId ); | |||
~ClientInfo(); | ~ClientInfo(); | |||
string getRemote() const { return _remote; } | string getRemote() const { return _remote; } | |||
End of changes. 6 change blocks. | ||||
3 lines changed or deleted | 8 lines changed or added | |||
resource.h | resource.h | |||
---|---|---|---|---|
//{{NO_DEPENDENCIES}} | //{{NO_DEPENDENCIES}} | |||
// Microsoft Visual C++ generated include file. | // Microsoft Visual C++ generated include file. | |||
// Used by db.rc | // Used by db.rc | |||
// | ||||
/** | #define IDI_ICON2 102 | |||
* Copyright (C) 2008 10gen Inc. | ||||
* | ||||
* This program is free software: you can redistribute it and/or modify | ||||
* it under the terms of the GNU Affero General Public License, version 3 | ||||
, | ||||
* as published by the Free Software Foundation. | ||||
* | ||||
* This program is distributed in the hope that it will be useful, | ||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
* GNU Affero General Public License for more details. | ||||
* | ||||
* You should have received a copy of the GNU Affero General Public Licen | ||||
se | ||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
*/ | ||||
namespace mongo { | ||||
// Next default values for new objects | // Next default values for new objects | |||
// | // | |||
#ifdef APSTUDIO_INVOKED | #ifdef APSTUDIO_INVOKED | |||
#ifndef APSTUDIO_READONLY_SYMBOLS | #ifndef APSTUDIO_READONLY_SYMBOLS | |||
#define _APS_NEXT_RESOURCE_VALUE 101 | #define _APS_NEXT_RESOURCE_VALUE 103 | |||
#define _APS_NEXT_COMMAND_VALUE 40001 | #define _APS_NEXT_COMMAND_VALUE 40001 | |||
#define _APS_NEXT_CONTROL_VALUE 1001 | #define _APS_NEXT_CONTROL_VALUE 1001 | |||
#define _APS_NEXT_SYMED_VALUE 101 | #define _APS_NEXT_SYMED_VALUE 101 | |||
#endif | #endif | |||
#endif | #endif | |||
} // namespace mongo | ||||
End of changes. 3 change blocks. | ||||
21 lines changed or deleted | 3 lines changed or added | |||
rs_config.h | rs_config.h | |||
---|---|---|---|---|
skipping to change at line 28 | skipping to change at line 28 | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../../util/hostandport.h" | #include "../../util/hostandport.h" | |||
#include "health.h" | #include "health.h" | |||
namespace mongo { | namespace mongo { | |||
/** | const string rsConfigNs = "local.system.replset"; | |||
local.system.replset | ||||
This collection has one object per server in the set. | ||||
See "Replical Sets Configuration" on mongodb.org wiki for details on th | ||||
e format. | ||||
*/ | ||||
class ReplSetConfig { | class ReplSetConfig { | |||
enum { EMPTYCONFIG = -2 }; | ||||
public: | public: | |||
/* if something is misconfigured, throws an exception. | /* if something is misconfigured, throws an exception. | |||
if couldn't be queried or is just blank, ok() will be false. | if couldn't be queried or is just blank, ok() will be false. | |||
*/ | */ | |||
ReplSetConfig(const HostAndPort& h); | ReplSetConfig(const HostAndPort& h); | |||
ReplSetConfig(BSONObj cfg); | ReplSetConfig(BSONObj cfg); | |||
bool ok() const { return _ok; } | bool ok() const { return _ok; } | |||
struct Member { | struct MemberCfg { | |||
Member() : _id(-1), votes(1), priority(1.0), arbiterOnly(false) | MemberCfg() : _id(-1), votes(1), priority(1.0), arbiterOnly(fal | |||
{ } | se) { } | |||
int _id; /* ordinal */ | int _id; /* ordinal */ | |||
unsigned votes; /* how many votes this node gets. default 1. */ | unsigned votes; /* how many votes this node gets. default 1. */ | |||
HostAndPort h; | HostAndPort h; | |||
double priority; /* 0 means can never be primary */ | double priority; /* 0 means can never be primary */ | |||
bool arbiterOnly; | bool arbiterOnly; | |||
void check() const; /* check validity, assert if not. */ | void check() const; /* check validity, assert if not. */ | |||
BSONObj asBson() const; | BSONObj asBson() const; | |||
}; | }; | |||
vector<Member> members; | vector<MemberCfg> members; | |||
string _id; | string _id; | |||
int version; | int version; | |||
HealthOptions ho; | HealthOptions ho; | |||
string md5; | string md5; | |||
BSONObj getLastErrorDefaults; | BSONObj getLastErrorDefaults; | |||
// true if could connect, and there is no cfg object there at all | list<HostAndPort> otherMemberHostnames() const; // except self | |||
bool empty() const { return version == -2; } | ||||
/** @return true if could connect, and there is no cfg object there | ||||
at all */ | ||||
bool empty() const { return version == EMPTYCONFIG; } | ||||
string toString() const { return asBson().toString(); } | string toString() const { return asBson().toString(); } | |||
/** validate the settings. does not call check() on each member, yo u have to do that separately. */ | /** validate the settings. does not call check() on each member, yo u have to do that separately. */ | |||
void check() const; | void check() const; | |||
void save(); // to local db | static void receivedNewConfig(BSONObj); | |||
void saveConfigLocally(); // to local db | ||||
string saveConfigEverywhere(); // returns textual info on what happ | ||||
ened | ||||
BSONObj asBson() const; | ||||
private: | private: | |||
bool _ok; | bool _ok; | |||
void from(BSONObj); | void from(BSONObj); | |||
void clear(); | void clear(); | |||
BSONObj asBson() const; | ||||
}; | }; | |||
} | } | |||
End of changes. 7 change blocks. | ||||
16 lines changed or deleted | 17 lines changed or added | |||
scanandorder.h | scanandorder.h | |||
---|---|---|---|---|
skipping to change at line 53 | skipping to change at line 53 | |||
return o.extractFields(pattern,true); | return o.extractFields(pattern,true); | |||
} | } | |||
}; | }; | |||
/* 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) { | inline void fillQueryResultFromObj(BufBuilder& bb, FieldMatcher *filter , BSONObj& js, DiskLoc* loc=NULL) { | |||
if ( filter ) { | if ( filter ) { | |||
BSONObjBuilder b( bb ); | BSONObjBuilder b( bb ); | |||
BSONObjIterator i( js ); | BSONObjIterator i( js ); | |||
while ( i.more() ){ | while ( i.more() ){ | |||
BSONElement e = i.next(); | BSONElement e = i.next(); | |||
const char * fname = e.fieldName(); | const char * fname = e.fieldName(); | |||
if ( strcmp( fname , "_id" ) == 0 ){ | if ( strcmp( fname , "_id" ) == 0 ){ | |||
if (filter->includeID()) | if (filter->includeID()) | |||
b.append( e ); | b.append( e ); | |||
} else { | } else { | |||
filter->append( b , e ); | filter->append( b , e ); | |||
} | } | |||
} | } | |||
if (loc) | ||||
b.append("$diskLoc", loc->toBSONObj()); | ||||
b.done(); | ||||
} else if (loc) { | ||||
BSONObjBuilder b( bb ); | ||||
b.appendElements(js); | ||||
b.append("$diskLoc", loc->toBSONObj()); | ||||
b.done(); | b.done(); | |||
} else { | } else { | |||
bb.append((void*) js.objdata(), js.objsize()); | bb.append((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 | BestMap best; // key -> full object | |||
int startFrom; | int startFrom; | |||
int limit; // max to send back. | int limit; // max to send back. | |||
KeyType order; | KeyType order; | |||
unsigned approxSize; | unsigned approxSize; | |||
void _add(BSONObj& k, BSONObj o) { | void _add(BSONObj& k, BSONObj o, DiskLoc* loc) { | |||
best.insert(make_pair(k,o)); | if (!loc){ | |||
best.insert(make_pair(k,o)); | ||||
} else { | ||||
BSONObjBuilder b; | ||||
b.appendElements(o); | ||||
b.append("$diskLoc", loc->toBSONObj()); | ||||
best.insert(make_pair(k, b.obj())); | ||||
} | ||||
} | } | |||
void _addIfBetter(BSONObj& k, BSONObj o, BestMap::iterator i) { | void _addIfBetter(BSONObj& k, BSONObj o, BestMap::iterator i, DiskL oc* loc) { | |||
const BSONObj& worstBestKey = i->first; | const BSONObj& worstBestKey = i->first; | |||
int c = worstBestKey.woCompare(k, order.pattern); | int c = worstBestKey.woCompare(k, order.pattern); | |||
if ( c > 0 ) { | if ( c > 0 ) { | |||
// k is better, 'upgrade' | // k is better, 'upgrade' | |||
best.erase(i); | best.erase(i); | |||
_add(k, o); | _add(k, o, loc); | |||
} | } | |||
} | } | |||
public: | public: | |||
ScanAndOrder(int _startFrom, int _limit, BSONObj _order) : | ScanAndOrder(int _startFrom, int _limit, BSONObj _order) : | |||
best( BSONObjCmp( _order ) ), | best( BSONObjCmp( _order ) ), | |||
startFrom(_startFrom), order(_order) { | startFrom(_startFrom), order(_order) { | |||
limit = _limit > 0 ? _limit + startFrom : 0x7fffffff; | limit = _limit > 0 ? _limit + startFrom : 0x7fffffff; | |||
approxSize = 0; | approxSize = 0; | |||
} | } | |||
int size() const { | int size() const { | |||
return best.size(); | return best.size(); | |||
} | } | |||
void add(BSONObj o) { | void add(BSONObj o, DiskLoc* loc) { | |||
assert( o.isValid() ); | assert( o.isValid() ); | |||
BSONObj k = order.getKeyFromObject(o); | BSONObj k = order.getKeyFromObject(o); | |||
if ( (int) best.size() < limit ) { | if ( (int) best.size() < limit ) { | |||
approxSize += k.objsize(); | approxSize += k.objsize(); | |||
uassert( 10128 , "too much key data for sort() with no ind ex. add an index or specify a smaller limit", approxSize < 1 * 1024 * 1024 ); | uassert( 10128 , "too much key data for sort() with no ind ex. add an index or specify a smaller limit", approxSize < 1 * 1024 * 1024 ); | |||
_add(k, o); | _add(k, o, loc); | |||
return; | return; | |||
} | } | |||
BestMap::iterator i; | BestMap::iterator i; | |||
assert( best.end() != best.begin() ); | assert( best.end() != best.begin() ); | |||
i = best.end(); | i = best.end(); | |||
i--; | i--; | |||
_addIfBetter(k, o, i); | _addIfBetter(k, o, i, loc); | |||
} | } | |||
void _fill(BufBuilder& b, FieldMatcher *filter, int& nout, BestMap: :iterator begin, BestMap::iterator end) { | void _fill(BufBuilder& b, FieldMatcher *filter, int& nout, BestMap: :iterator begin, BestMap::iterator end) { | |||
int n = 0; | int n = 0; | |||
int nFilled = 0; | int nFilled = 0; | |||
for ( BestMap::iterator i = begin; i != end; i++ ) { | for ( BestMap::iterator i = begin; i != end; i++ ) { | |||
n++; | n++; | |||
if ( n <= startFrom ) | if ( n <= startFrom ) | |||
continue; | continue; | |||
BSONObj& o = i->second; | BSONObj& o = i->second; | |||
End of changes. 8 change blocks. | ||||
8 lines changed or deleted | 22 lines changed or added | |||
security.h | security.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/thread/tss.hpp> | ||||
#undef assert | ||||
#define assert MONGO_assert | ||||
#include "nonce.h" | #include "nonce.h" | |||
#include "concurrency.h" | #include "concurrency.h" | |||
namespace mongo { | namespace mongo { | |||
// --noauth cmd line option | // --noauth cmd line option | |||
extern bool noauth; | extern bool noauth; | |||
/* for a particular db */ | /* for a particular db */ | |||
struct Auth { | struct Auth { | |||
Auth() { level = 0; } | Auth() { level = 0; } | |||
int level; | int level; | |||
}; | }; | |||
class AuthenticationInfo : boost::noncopyable { | class AuthenticationInfo : boost::noncopyable { | |||
mongo::mutex _lock; | mongo::mutex _lock; | |||
map<string, Auth> m; // dbname -> auth | map<string, Auth> m; // dbname -> auth | |||
static int warned; | static int warned; | |||
public: | public: | |||
bool isLocalHost; | bool isLocalHost; | |||
AuthenticationInfo() { isLocalHost = false; } | AuthenticationInfo() : _lock("AuthenticationInfo") { isLocalHost = false; } | |||
~AuthenticationInfo() { | ~AuthenticationInfo() { | |||
} | } | |||
void logout(const string& dbname ) { | void logout(const string& dbname ) { | |||
scoped_lock lk(_lock); | scoped_lock lk(_lock); | |||
m.erase(dbname); | m.erase(dbname); | |||
} | } | |||
void authorize(const string& dbname ) { | void authorize(const string& dbname ) { | |||
scoped_lock lk(_lock); | scoped_lock lk(_lock); | |||
m[dbname].level = 2; | m[dbname].level = 2; | |||
} | } | |||
End of changes. 2 change blocks. | ||||
5 lines changed or deleted | 1 lines changed or added | |||
shard.h | shard.h | |||
---|---|---|---|---|
skipping to change at line 38 | skipping to change at line 38 | |||
class Shard { | class Shard { | |||
public: | public: | |||
Shard() | Shard() | |||
: _name(""),_addr(""){ | : _name(""),_addr(""){ | |||
} | } | |||
Shard( const string& name , const string& addr ) | Shard( const string& name , const string& addr ) | |||
: _name(name), _addr( addr ){ | : _name(name), _addr( addr ){ | |||
} | } | |||
Shard( const string& ident ){ | ||||
reset( ident ); | ||||
} | ||||
Shard( const Shard& other ) | Shard( const Shard& other ) | |||
: _name( other._name ) , _addr( other._addr ){ | : _name( other._name ) , _addr( other._addr ){ | |||
} | } | |||
Shard( const Shard* other ) | Shard( const Shard* other ) | |||
: _name( other->_name ) ,_addr( other->_addr ){ | : _name( other->_name ) ,_addr( other->_addr ){ | |||
} | } | |||
static Shard make( const string& ident ){ | static Shard make( const string& ident ){ | |||
Shard s; | Shard s; | |||
skipping to change at line 162 | skipping to change at line 166 | |||
} | } | |||
private: | private: | |||
Shard _shard; | Shard _shard; | |||
long long _mapped; | long long _mapped; | |||
double _writeLock; | double _writeLock; | |||
}; | }; | |||
class ShardConnection { | class ShardConnection { | |||
public: | public: | |||
ShardConnection( const Shard * s ) | ShardConnection( const Shard * s , const string& ns ); | |||
: _conn( s->getConnString() ){ | ShardConnection( const Shard& s , const string& ns ); | |||
} | ShardConnection( const string& addr , const string& ns ); | |||
ShardConnection( const Shard& s ) | void done(); | |||
: _conn( s.getConnString() ){ | void kill(); | |||
} | ||||
ShardConnection( const string& addr ) | DBClientBase& conn(){ | |||
: _conn( addr ){ | assert( _conn ); | |||
return *_conn; | ||||
} | } | |||
void done(){ | DBClientBase* operator->(){ | |||
_conn.done(); | assert( _conn ); | |||
return _conn; | ||||
} | } | |||
void kill(){ | DBClientBase* get(){ | |||
_conn.kill(); | assert( _conn ); | |||
return _conn; | ||||
} | } | |||
DBClientBase& conn(){ | string getHost() const { | |||
return _conn.conn(); | return _addr; | |||
} | } | |||
DBClientBase* operator->(){ | static void sync(); | |||
return _conn.get(); | ||||
} | ||||
private: | private: | |||
ScopedDbConnection _conn; | void _init(); | |||
string _addr; | ||||
string _ns; | ||||
DBClientBase* _conn; | ||||
}; | }; | |||
} | } | |||
End of changes. 9 change blocks. | ||||
18 lines changed or deleted | 26 lines changed or added | |||
shardkey.h | shardkey.h | |||
---|---|---|---|---|
skipping to change at line 45 | skipping to change at line 45 | |||
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 ){ | bool isGlobalMin( const BSONObj& k ) const{ | |||
return k.woCompare( globalMin() ) == 0; | return k.woCompare( globalMin() ) == 0; | |||
} | } | |||
bool isGlobalMax( const BSONObj& k ){ | bool isGlobalMax( const BSONObj& k ) const{ | |||
return k.woCompare( globalMax() ) == 0; | return k.woCompare( globalMax() ) == 0; | |||
} | } | |||
bool isGlobal( const BSONObj& k ){ | 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 ); | 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 ); | bool hasShardKey( const BSONObj& obj ) const; | |||
/** | /** | |||
returns a query that filters results only for the range desired, i.e. returns | returns a query that filters results only for the range desired, i.e. returns | |||
{ "field" : { $gte: keyval(min), $lt: keyval(max) } } | { "field" : { $gte: keyval(min), $lt: keyval(max) } } | |||
*/ | */ | |||
void getFilter( BSONObjBuilder& b , const BSONObj& min, const BSONO bj& max ); | void getFilter( BSONObjBuilder& b , const BSONObj& min, const BSONO bj& max ) const; | |||
/** | /** | |||
Returns if the given sort pattern can be ordered by the shard ke y pattern. | Returns if the given sort pattern can be ordered by the shard ke y pattern. | |||
Example | Example | |||
sort: { ts: -1 } | sort: { ts: -1 } | |||
*this: { ts:1 } | *this: { ts:1 } | |||
-> -1 | -> -1 | |||
@return | @return | |||
0 if sort either doesn't have all the fields or has extra fie lds | 0 if sort either doesn't have all the fields or has extra fie lds | |||
< 0 if sort is descending | < 0 if sort is descending | |||
> 1 if sort is ascending | > 1 if sort is ascending | |||
*/ | */ | |||
int canOrder( const BSONObj& sort ); | int canOrder( const BSONObj& sort ) const; | |||
BSONObj key() { 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 string& key ) const { | bool partOfShardKey(const string& key ) const { | |||
return patternfields.count( key ) > 0; | return patternfields.count( key ) > 0; | |||
} | } | |||
bool uniqueAllowd( const BSONObj& otherPattern ) const; | bool uniqueAllowd( const BSONObj& otherPattern ) const; | |||
End of changes. 8 change blocks. | ||||
8 lines changed or deleted | 8 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"; } | ||||
void run(); | void run(); | |||
}; | }; | |||
extern Snapshots statsSnapshots; | extern Snapshots statsSnapshots; | |||
extern SnapshotThread snapshotThread; | extern SnapshotThread snapshotThread; | |||
} | } | |||
End of changes. 1 change blocks. | ||||
0 lines changed or deleted | 1 lines changed or added | |||
sock.h | sock.h | |||
---|---|---|---|---|
skipping to change at line 45 | skipping to change at line 45 | |||
inline int getLastError() { | inline int getLastError() { | |||
return WSAGetLastError(); | return WSAGetLastError(); | |||
} | } | |||
inline const char* gai_strerror(int code) { | inline const char* gai_strerror(int code) { | |||
return ::gai_strerrorA(code); | return ::gai_strerrorA(code); | |||
} | } | |||
inline void disableNagle(int sock) { | inline void disableNagle(int sock) { | |||
int x = 1; | int x = 1; | |||
if ( setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *) &x, sizeof (x)) ) | if ( setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *) &x, sizeof (x)) ) | |||
out() << "ERROR: disableNagle failed" << endl; | 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 ) { | inline void prebindOptions( int sock ) { | |||
} | } | |||
// This won't actually be used on windows | // This won't actually be used on windows | |||
struct sockaddr_un { | struct sockaddr_un { | |||
short sun_family; | short sun_family; | |||
char sun_path[108]; // length from unix header | char sun_path[108]; // length from unix header | |||
}; | }; | |||
skipping to change at line 87 | skipping to change at line 89 | |||
inline void disableNagle(int sock) { | inline void disableNagle(int sock) { | |||
int x = 1; | int x = 1; | |||
#ifdef SOL_TCP | #ifdef SOL_TCP | |||
int level = SOL_TCP; | int level = SOL_TCP; | |||
#else | #else | |||
int level = SOL_SOCKET; | int level = SOL_SOCKET; | |||
#endif | #endif | |||
if ( setsockopt(sock, level, TCP_NODELAY, (char *) &x, sizeof(x)) ) | if ( setsockopt(sock, level, TCP_NODELAY, (char *) &x, sizeof(x)) ) | |||
log() << "ERROR: disableNagle failed" << endl; | log() << "ERROR: disableNagle failed: " << errnoWithDescription | |||
() << endl; | ||||
#ifdef SO_KEEPALIVE | ||||
if ( setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &x, sizeof | ||||
(x)) ) | ||||
log() << "ERROR: SO_KEEPALIVE failed: " << errnoWithDescription | ||||
() << endl; | ||||
#endif | ||||
} | } | |||
inline void prebindOptions( int sock ) { | inline void prebindOptions( int sock ) { | |||
DEV log() << "doing prebind option" << endl; | DEV log() << "doing prebind option" << endl; | |||
int x = 1; | int x = 1; | |||
if ( setsockopt( sock , SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x)) < 0 ) | if ( setsockopt( sock , SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x)) < 0 ) | |||
out() << "Failed to set socket opt, SO_REUSEADDR" << endl; | out() << "Failed to set socket opt, SO_REUSEADDR" << endl; | |||
} | } | |||
#endif | #endif | |||
skipping to change at line 241 | skipping to change at line 248 | |||
int ec = gethostname(buf, 127); | int ec = gethostname(buf, 127); | |||
if ( ec || *buf == 0 ) { | if ( ec || *buf == 0 ) { | |||
log() << "can't get this server's hostname " << errnoWithDescri ption() << endl; | log() << "can't get this server's hostname " << errnoWithDescri ption() << endl; | |||
return ""; | return ""; | |||
} | } | |||
return buf; | return buf; | |||
} | } | |||
class ListeningSockets { | class ListeningSockets { | |||
public: | public: | |||
ListeningSockets() : _sockets( new set<int>() ){ | ListeningSockets() : _mutex("ListeningSockets"), _sockets( new set< int>() ){ | |||
} | } | |||
void add( int sock ){ | void add( int sock ){ | |||
scoped_lock lk( _mutex ); | scoped_lock lk( _mutex ); | |||
_sockets->insert( sock ); | _sockets->insert( sock ); | |||
} | } | |||
void remove( int sock ){ | void remove( int sock ){ | |||
scoped_lock lk( _mutex ); | scoped_lock lk( _mutex ); | |||
_sockets->erase( sock ); | _sockets->erase( sock ); | |||
} | } | |||
End of changes. 3 change blocks. | ||||
2 lines changed or deleted | 13 lines changed or added | |||
syncclusterconnection.h | syncclusterconnection.h | |||
---|---|---|---|---|
// syncclusterconnection.h | // @file syncclusterconnection.h | |||
/* | /* | |||
* Copyright 2010 10gen Inc. | * Copyright 2010 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 25 | skipping to change at line 26 | |||
* limitations under the License. | * limitations under the License. | |||
*/ | */ | |||
#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. | |||
* | ||||
* Write operations are two-phase. First, all nodes are asked to fsync | ||||
. If successful | ||||
* everywhere, the write is sent everywhere and then followed by an fsy | ||||
nc. There is no | ||||
* rollback if a problem occurs during the second phase. Naturally, wi | ||||
th all these fsyncs, | ||||
* these operations will be quite slow -- use sparingly. | ||||
* | ||||
* 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 | ||||
* 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 commaSeperated should be 3 hosts comma seperated | * @param commaSeparated should be 3 hosts comma separated | |||
*/ | */ | |||
SyncClusterConnection( string commaSeperated ); | SyncClusterConnection( const list<HostAndPort> & ); | |||
SyncClusterConnection( string commaSeparated ); | ||||
SyncClusterConnection( string a , string b , string c ); | SyncClusterConnection( string a , string b , string c ); | |||
~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 64 | skipping to change at line 76 | |||
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 ); | |||
virtual void insert( const string &ns, const vector< BSONObj >& v ) ; | virtual void insert( const string &ns, const vector< BSONObj >& v ) ; | |||
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 string toString(){ | ||||
return _toString(); | ||||
} | ||||
virtual bool call( Message &toSend, Message &response, bool assertO k ); | virtual bool call( Message &toSend, Message &response, bool assertO k ); | |||
virtual void say( Message &toSend ); | virtual void say( Message &toSend ); | |||
virtual void sayPiggyBack( Message &toSend ); | virtual void sayPiggyBack( Message &toSend ); | |||
virtual string getServerAddress() const { return _address; } | virtual string getServerAddress() const { return _address; } | |||
virtual bool isFailed() const { return false; } | ||||
virtual bool isFailed() const { | virtual string toString() { return _toString(); } | |||
return false; | ||||
} | ||||
private: | private: | |||
SyncClusterConnection( SyncClusterConnection& prev ); | SyncClusterConnection( SyncClusterConnection& prev ); | |||
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<DBClientConnection*> _conns; | vector<DBClientConnection*> _conns; | |||
map<string,int> _lockTypes; | map<string,int> _lockTypes; | |||
mongo::mutex _mutex; | mongo::mutex _mutex; | |||
}; | }; | |||
}; | }; | |||
#include "undef_macros.h" | #include "undef_macros.h" | |||
End of changes. 14 change blocks. | ||||
21 lines changed or deleted | 24 lines changed or added | |||
thread_pool.h | thread_pool.h | |||
---|---|---|---|---|
skipping to change at line 32 | skipping to change at line 32 | |||
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 functor | |||
// 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 wait fo rever. | |||
// Also, new tasks could be scheduled after this returns. | // Also, new tasks could be scheduled after this returns. | |||
void join(); | void join(); | |||
skipping to change at line 62 | skipping to change at line 62 | |||
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, typename 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, typename 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::bind(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 removed i n 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); | |||
End of changes. 2 change blocks. | ||||
2 lines changed or deleted | 2 lines changed or added | |||
top.h | top.h | |||
---|---|---|---|---|
skipping to change at line 32 | skipping to change at line 32 | |||
#define assert MONGO_assert | #define assert MONGO_assert | |||
namespace mongo { | namespace mongo { | |||
/** | /** | |||
* tracks usage by collection | * tracks usage by collection | |||
*/ | */ | |||
class Top { | class Top { | |||
public: | public: | |||
Top() : _lock("Top") { } | ||||
class UsageData { | class UsageData { | |||
public: | 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; | |||
End of changes. 1 change blocks. | ||||
0 lines changed or deleted | 2 lines changed or added | |||
undef_macros.h | undef_macros.h | |||
---|---|---|---|---|
skipping to change at line 35 | skipping to change at line 35 | |||
#if !defined(MONGO_EXPOSE_MACROS) && !defined(MONGO_MACROS_CLEANED) | #if !defined(MONGO_EXPOSE_MACROS) && !defined(MONGO_MACROS_CLEANED) | |||
// util/allocator.h | // util/allocator.h | |||
#undef malloc | #undef malloc | |||
#undef realloc | #undef realloc | |||
// util/assert_util.h | // util/assert_util.h | |||
#undef assert | #undef assert | |||
#undef dassert | #undef dassert | |||
#undef wassert | #undef wassert | |||
#undef massert | ||||
#undef uassert | ||||
#undef BOOST_CHECK_EXCEPTION | #undef BOOST_CHECK_EXCEPTION | |||
#undef DESTRUCTOR_GUARD | #undef DESTRUCTOR_GUARD | |||
// util/goodies.h | // util/goodies.h | |||
#undef PRINT | #undef PRINT | |||
#undef PRINTFL | #undef PRINTFL | |||
#undef asctime | #undef asctime | |||
#undef gmtime | #undef gmtime | |||
#undef localtime | #undef localtime | |||
#undef ctime | #undef ctime | |||
End of changes. 1 change blocks. | ||||
0 lines changed or deleted | 2 lines changed or added | |||
update.h | update.h | |||
---|---|---|---|---|
skipping to change at line 103 | skipping to change at line 103 | |||
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; | |||
} | } | |||
} | } | |||
bool isIndexed( const set<string>& idxKeys ) const { | static bool isIndexed( const string& fullName , const set<string>& | |||
idxKeys ){ | ||||
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; | |||
string fullName = fieldName; | ||||
// check if there is an index key equal to mod | // check if there is an index key equal to mod | |||
if ( idxKeys.count(fullName) ) | if ( idxKeys.count(fullName) ) | |||
return true; | return true; | |||
// check if there is an index key that is a child of mod | // check if there is an index key that is a child of mod | |||
set< string >::const_iterator j = idxKeys.upper_bound( fullName ); | set< string >::const_iterator j = idxKeys.upper_bound( fullName ); | |||
if ( j != idxKeys.end() && j->find( fullName ) == 0 && (*j)[ful lName.size()] == '.' ) | if ( j != idxKeys.end() && j->find( fullName ) == 0 && (*j)[ful lName.size()] == '.' ) | |||
return true; | return true; | |||
return false; | ||||
} | ||||
bool isIndexed( const set<string>& idxKeys ) const { | ||||
string fullName = fieldName; | ||||
if ( isIndexed( fullName , idxKeys ) ) | ||||
return true; | ||||
if ( strstr( fieldName , "." ) ){ | ||||
// check for a.0.1 | ||||
StringBuilder buf( fullName.size() + 1 ); | ||||
for ( size_t i=0; i<fullName.size(); i++ ){ | ||||
char c = fullName[i]; | ||||
buf << c; | ||||
if ( c != '.' ) | ||||
continue; | ||||
if ( ! isdigit( fullName[i+1] ) ) | ||||
continue; | ||||
bool possible = true; | ||||
size_t j=i+2; | ||||
for ( ; j<fullName.size(); j++ ){ | ||||
char d = fullName[j]; | ||||
if ( d == '.' ) | ||||
break; | ||||
if ( isdigit( d ) ) | ||||
continue; | ||||
possible = false; | ||||
break; | ||||
} | ||||
if ( possible ) | ||||
i = j; | ||||
} | ||||
string x = buf.str(); | ||||
if ( isIndexed( x , idxKeys ) ) | ||||
return true; | ||||
} | ||||
return false; | return false; | |||
} | } | |||
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; | |||
skipping to change at line 368 | skipping to change at line 412 | |||
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; | |||
if ( incType ){ | ||||
BSONObjBuilder bb( b.subobjStart( "$set" ) ); | ||||
appendIncValue( bb ); | ||||
bb.done(); | ||||
return; | ||||
} | ||||
const char * name = fixedOpName ? fixedOpName : Mod::modNames[o | ||||
p()]; | ||||
BSONObjBuilder bb( b.subobjStart( name ) ); | ||||
if ( fixed ) | ||||
bb.appendAs( *fixed , m->fieldName ); | ||||
else | ||||
bb.appendAs( m->elt , m->fieldName ); | ||||
bb.done(); | ||||
} | ||||
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 ) const { | void appendIncValue( Builder& b , bool useFullName ) const { | |||
const char * n = useFullName ? m->fieldName : m->shortFieldName | ||||
; | ||||
switch ( incType ){ | switch ( incType ){ | |||
case NumberDouble: | case NumberDouble: | |||
b.append( m->shortFieldName , incdouble ); break; | b.append( n , incdouble ); break; | |||
case NumberLong: | case NumberLong: | |||
b.append( m->shortFieldName , inclong ); break; | b.append( n , inclong ); break; | |||
case NumberInt: | case NumberInt: | |||
b.append( m->shortFieldName , incint ); break; | b.append( n , incint ); break; | |||
default: | default: | |||
assert(0); | assert(0); | |||
} | } | |||
} | } | |||
string toString() const; | ||||
}; | }; | |||
/** | /** | |||
* 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; | return lexNumCmp( l.c_str(), r.c_str() ) < 0; | |||
skipping to change at line 534 | skipping to change at line 566 | |||
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; | ||||
friend class ModSet; | friend class ModSet; | |||
}; | }; | |||
} | } | |||
End of changes. 10 change blocks. | ||||
24 lines changed or deleted | 59 lines changed or added | |||
util.h | util.h | |||
---|---|---|---|---|
skipping to change at line 31 | skipping to change at line 31 | |||
#include "../pch.h" | #include "../pch.h" | |||
#include "../client/dbclient.h" | #include "../client/dbclient.h" | |||
/** | /** | |||
some generic sharding utils that can be used in mongod or mongos | some generic sharding utils that can be used in mongod or mongos | |||
*/ | */ | |||
namespace mongo { | namespace mongo { | |||
/** | /** | |||
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 std::exception { | */ | |||
class StaleConfigException : public AssertionException { | ||||
public: | public: | |||
StaleConfigException( const string& ns , const string& msg){ | StaleConfigException( const string& ns , const string& raw){ | |||
code = 9996; | ||||
stringstream s; | stringstream s; | |||
s << "StaleConfigException ns: " << ns << " " << msg; | s << "StaleConfigException ns: " << ns << " " << raw; | |||
_msg = s.str(); | msg = s.str(); | |||
log(1) << _msg << endl; | log(1) << msg << endl; | |||
} | } | |||
virtual ~StaleConfigException() throw(){} | virtual ~StaleConfigException() throw(){} | |||
virtual const char* what() const throw(){ | ||||
return _msg.c_str(); | ||||
} | ||||
private: | private: | |||
string _msg; | ||||
}; | }; | |||
void checkShardVersion( DBClientBase & conn , const string& ns , bool a uthoritative = false ); | void checkShardVersion( DBClientBase & conn , const string& ns , bool a uthoritative = false ); | |||
} | } | |||
End of changes. 5 change blocks. | ||||
10 lines changed or deleted | 8 lines changed or added | |||
value.h | value.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 string COULD be mangled but with the double buffering, assuming wr ites | /** 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 | are infrequent, it's unlikely. thus, this is reasonable for lockless s etting of | |||
diagnostic strings, where their content isn't critical. | diagnostic strings, where their content isn't critical. | |||
*/ | */ | |||
class DiagStr { | class DiagStr { | |||
char buf1[256]; | char buf1[256]; | |||
char buf2[256]; | char buf2[256]; | |||
char *p; | char *p; | |||
public: | public: | |||
DiagStr() { | DiagStr() { | |||
memset(buf1, 0, 256); | memset(buf1, 0, 256); | |||
memset(buf2, 0, 256); | memset(buf2, 0, 256); | |||
p = buf1; | p = buf1; | |||
} | } | |||
const char * get() const { return p; } | const char * get() const { return p; } | |||
void set(const char *s) { | void set(const char *s) { | |||
char *q = (p==buf1) ? buf2 : buf1; | char *q = (p==buf1) ? buf2 : buf1; | |||
strncpy(q, s, 255); | strncpy(q, s, 255); | |||
p = q; | p = q; | |||
} | } | |||
}; | }; | |||
extern mutex _atomicMutex; | ||||
/** atomic wrapper for a value. enters a mutex on each access. must | ||||
be copyable. | ||||
*/ | ||||
template<typename T> | ||||
class Atomic : boost::noncopyable { | ||||
T val; | ||||
public: | ||||
Atomic<T>() { } | ||||
void operator=(const T& a) { | ||||
scoped_lock lk(_atomicMutex); | ||||
val = a; } | ||||
operator T() const { | ||||
scoped_lock lk(_atomicMutex); | ||||
return val; } | ||||
class tran : private scoped_lock { | ||||
Atomic<T>& _a; | ||||
public: | ||||
tran(Atomic<T>& a) : scoped_lock(_atomicMutex), _a(a) { } | ||||
T& ref() { return _a.val; } | ||||
}; | ||||
}; | ||||
} | } | |||
End of changes. 4 change blocks. | ||||
18 lines changed or deleted | 45 lines changed or added | |||