accumulator.h | accumulator.h | |||
---|---|---|---|---|
skipping to change at line 35 | skipping to change at line 35 | |||
namespace mongo { | namespace mongo { | |||
class ExpressionContext; | class ExpressionContext; | |||
class Accumulator : | class Accumulator : | |||
public ExpressionNary { | public ExpressionNary { | |||
public: | public: | |||
// virtuals from ExpressionNary | // virtuals from ExpressionNary | |||
virtual void addOperand(const intrusive_ptr<Expression> &pExpressio n); | virtual void addOperand(const intrusive_ptr<Expression> &pExpressio n); | |||
virtual void addToBsonObj( | virtual void addToBsonObj( | |||
BSONObjBuilder *pBuilder, string fieldName, unsigned depth) con | BSONObjBuilder *pBuilder, string fieldName, | |||
st; | bool requireExpression) const; | |||
virtual void addToBsonArray( | virtual void addToBsonArray(BSONArrayBuilder *pBuilder) const; | |||
BSONArrayBuilder *pBuilder, unsigned depth) const; | ||||
/* | /* | |||
Get the accumulated value. | Get the accumulated value. | |||
@returns the accumulated value | @returns the accumulated value | |||
*/ | */ | |||
virtual intrusive_ptr<const Value> getValue() const = 0; | virtual intrusive_ptr<const Value> getValue() const = 0; | |||
protected: | protected: | |||
Accumulator(); | Accumulator(); | |||
skipping to change at line 59 | skipping to change at line 59 | |||
/* | /* | |||
Convenience method for doing this for accumulators. The pattern | Convenience method for doing this for accumulators. The pattern | |||
is always the same, so a common implementation works, but require s | is always the same, so a common implementation works, but require s | |||
knowing the operator name. | knowing the operator name. | |||
@param pBuilder the builder to add to | @param pBuilder the builder to add to | |||
@param fieldName the projected name | @param fieldName the projected name | |||
@param opName the operator name | @param opName the operator name | |||
*/ | */ | |||
void opToBson( | void opToBson( | |||
BSONObjBuilder *pBuilder, string fieldName, string opName, | BSONObjBuilder *pBuilder, string fieldName, string opName) cons | |||
unsigned depth) const; | t; | |||
}; | }; | |||
class AccumulatorAddToSet : | class AccumulatorAddToSet : | |||
public Accumulator { | public Accumulator { | |||
public: | public: | |||
// virtuals from Expression | // virtuals from Expression | |||
virtual intrusive_ptr<const Value> evaluate( | virtual intrusive_ptr<const Value> evaluate( | |||
const intrusive_ptr<Document> &pDocument) const; | const intrusive_ptr<Document> &pDocument) const; | |||
virtual intrusive_ptr<const Value> getValue() const; | virtual intrusive_ptr<const Value> getValue() const; | |||
virtual const char *getOpName() const; | virtual const char *getOpName() const; | |||
End of changes. 2 change blocks. | ||||
6 lines changed or deleted | 5 lines changed or added | |||
alignedbuilder.h | alignedbuilder.h | |||
---|---|---|---|---|
skipping to change at line 90 | skipping to change at line 90 | |||
*((unsigned long long*)grow(sizeof(unsigned long long))) = j; | *((unsigned long long*)grow(sizeof(unsigned long long))) = j; | |||
} | } | |||
void appendBuf(const void *src, size_t len) { memcpy(grow((unsigned ) len), src, len); } | void appendBuf(const void *src, size_t len) { memcpy(grow((unsigned ) len), src, len); } | |||
template<class T> | template<class T> | |||
void appendStruct(const T& s) { appendBuf(&s, sizeof(T)); } | void appendStruct(const T& s) { appendBuf(&s, sizeof(T)); } | |||
void appendStr(const StringData &str , bool includeEOO = true ) { | void appendStr(const StringData &str , bool includeEOO = true ) { | |||
const unsigned len = str.size() + ( includeEOO ? 1 : 0 ); | const unsigned len = str.size() + ( includeEOO ? 1 : 0 ); | |||
assert( len < (unsigned) BSONObjMaxUserSize ); | verify( len < (unsigned) BSONObjMaxUserSize ); | |||
memcpy(grow(len), str.data(), len); | memcpy(grow(len), str.data(), len); | |||
} | } | |||
/** @return the in-use length */ | /** @return the in-use length */ | |||
unsigned len() const { return _len; } | unsigned len() const { return _len; } | |||
private: | private: | |||
static const unsigned Alignment = 8192; | static const unsigned Alignment = 8192; | |||
/** returns the pre-grow write position */ | /** returns the pre-grow write position */ | |||
End of changes. 1 change blocks. | ||||
1 lines changed or deleted | 1 lines changed or added | |||
allocator.h | allocator.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 "mongo/util/signal_handlers.h" | ||||
// we need the "real" malloc here | ||||
#include "mongo/client/undef_macros.h" | ||||
namespace mongo { | namespace mongo { | |||
inline void * ourmalloc(size_t size) { | inline void * ourmalloc(size_t size) { | |||
void *x = malloc(size); | void *x = malloc(size); | |||
if ( x == 0 ) dbexit( EXIT_OOM_MALLOC , "malloc fails"); | if ( x == 0 ) printStackAndExit(0); | |||
return x; | return x; | |||
} | } | |||
inline void * ourrealloc(void *ptr, size_t size) { | inline void * ourrealloc(void *ptr, size_t size) { | |||
void *x = realloc(ptr, size); | void *x = realloc(ptr, size); | |||
if ( x == 0 ) dbexit( EXIT_OOM_REALLOC , "realloc fails"); | if ( x == 0 ) printStackAndExit(0); | |||
return x; | return x; | |||
} | } | |||
#define MONGO_malloc mongo::ourmalloc | #define MONGO_malloc mongo::ourmalloc | |||
#define malloc MONGO_malloc | ||||
#define MONGO_realloc mongo::ourrealloc | #define MONGO_realloc mongo::ourrealloc | |||
#define realloc MONGO_realloc | ||||
// this redefines 'malloc' to 'MONGO_malloc', etc | ||||
#include "mongo/client/redef_macros.h" | ||||
} // namespace mongo | } // namespace mongo | |||
End of changes. 5 change blocks. | ||||
4 lines changed or deleted | 10 lines changed or added | |||
array.h | array.h | |||
---|---|---|---|---|
skipping to change at line 44 | skipping to change at line 44 | |||
~FastArray() { | ~FastArray() { | |||
delete[] _data; | delete[] _data; | |||
} | } | |||
void clear() { | void clear() { | |||
_size = 0; | _size = 0; | |||
} | } | |||
T& operator[]( int x ) { | T& operator[]( int x ) { | |||
assert( x >= 0 && x < _capacity ); | verify( x >= 0 && x < _capacity ); | |||
return _data[x]; | return _data[x]; | |||
} | } | |||
T& getNext() { | T& getNext() { | |||
return _data[_size++]; | return _data[_size++]; | |||
} | } | |||
void push_back( const T& t ) { | void push_back( const T& t ) { | |||
assert( _size < _capacity ); | verify( _size < _capacity ); | |||
_data[_size++] = t; | _data[_size++] = t; | |||
} | } | |||
void sort( int (*comp)(const void *, const void *) ) { | void sort( int (*comp)(const void *, const void *) ) { | |||
qsort( _data , _size , sizeof(T) , comp ); | qsort( _data , _size , sizeof(T) , comp ); | |||
} | } | |||
int size() { | int size() { | |||
return _size; | return _size; | |||
} | } | |||
End of changes. 2 change blocks. | ||||
2 lines changed or deleted | 2 lines changed or added | |||
assert_util.h | assert_util.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 "../bson/inline_decls.h" | #include <iostream> | |||
#include <typeinfo> | ||||
// MONGO_NORETURN undefed at end of file | #include "mongo/bson/inline_decls.h" | |||
#ifdef __GNUC__ | #include "mongo/platform/compiler.h" | |||
# define MONGO_NORETURN __attribute__((__noreturn__)) | ||||
#else | ||||
# define MONGO_NORETURN | ||||
#endif | ||||
namespace mongo { | namespace mongo { | |||
enum CommonErrorCodes { | enum CommonErrorCodes { | |||
DatabaseDifferCaseCode = 13297 , | DatabaseDifferCaseCode = 13297 , | |||
SendStaleConfigCode = 13388 , | SendStaleConfigCode = 13388 , | |||
RecvStaleConfigCode = 9996 | RecvStaleConfigCode = 9996 | |||
}; | }; | |||
class AssertionCount { | class AssertionCount { | |||
skipping to change at line 153 | skipping to change at line 150 | |||
}; | }; | |||
class MsgAssertionException : public AssertionException { | class MsgAssertionException : public AssertionException { | |||
public: | public: | |||
MsgAssertionException( const ExceptionInfo& ei ) : AssertionExcepti on( ei ) {} | MsgAssertionException( const ExceptionInfo& ei ) : AssertionExcepti on( ei ) {} | |||
MsgAssertionException(int c, const std::string& m) : AssertionExcep tion( m , c ) {} | MsgAssertionException(int c, const std::string& m) : AssertionExcep tion( m , c ) {} | |||
virtual bool severe() { return false; } | virtual bool severe() { return false; } | |||
virtual void appendPrefix( std::stringstream& ss ) const; | virtual void appendPrefix( std::stringstream& ss ) const; | |||
}; | }; | |||
void asserted(const char *msg, const char *file, unsigned line) MONGO_N ORETURN; | MONGO_COMPILER_NORETURN void verifyFailed(const char *msg, const char * file, unsigned line); | |||
void wasserted(const char *msg, const char *file, unsigned line); | void wasserted(const char *msg, const char *file, unsigned line); | |||
void verifyFailed( int msgid ); | MONGO_COMPILER_NORETURN void fassertFailed( int msgid ); | |||
void fassertFailed( int msgid ); | ||||
/** a "user assertion". throws UserAssertion. logs. typically used f or errors that a user | /** a "user assertion". throws UserAssertion. logs. typically used f or errors that a user | |||
could cause, such as duplicate key, disk full, etc. | could cause, such as duplicate key, disk full, etc. | |||
*/ | */ | |||
void uasserted(int msgid, const char *msg) MONGO_NORETURN; | MONGO_COMPILER_NORETURN void uasserted(int msgid, const char *msg); | |||
void uasserted(int msgid , const std::string &msg); | MONGO_COMPILER_NORETURN void uasserted(int msgid , const std::string &m | |||
sg); | ||||
/** reported via lasterror, but don't throw exception */ | ||||
void uassert_nothrow(const char *msg); | ||||
/** msgassert and massert are for errors that are internal but have a w ell defined error text std::string. | /** msgassert and massert are for errors that are internal but have a w ell defined error text std::string. | |||
a stack trace is logged. | a stack trace is logged. | |||
*/ | */ | |||
void msgassertedNoTrace(int msgid, const char *msg) MONGO_NORETURN; | MONGO_COMPILER_NORETURN void msgassertedNoTrace(int msgid, const char * msg); | |||
inline void msgassertedNoTrace(int msgid, const std::string& msg) { msg assertedNoTrace( msgid , msg.c_str() ); } | inline void msgassertedNoTrace(int msgid, const std::string& msg) { msg assertedNoTrace( msgid , msg.c_str() ); } | |||
void msgasserted(int msgid, const char *msg) MONGO_NORETURN; | MONGO_COMPILER_NORETURN void msgasserted(int msgid, const char *msg); | |||
void msgasserted(int msgid, const std::string &msg); | MONGO_COMPILER_NORETURN void msgasserted(int msgid, const std::string & | |||
msg); | ||||
/* convert various types of exceptions to strings */ | /* convert various types of exceptions to strings */ | |||
inline std::string causedBy( const char* e ){ return (std::string)" :: caused by :: " + e; } | inline std::string causedBy( const char* e ){ return (std::string)" :: caused by :: " + e; } | |||
inline std::string causedBy( const DBException& e ){ return causedBy( e .toString().c_str() ); } | inline std::string causedBy( const DBException& e ){ return causedBy( e .toString().c_str() ); } | |||
inline std::string causedBy( const std::exception& e ){ return causedBy ( e.what() ); } | inline std::string causedBy( const std::exception& e ){ return causedBy ( e.what() ); } | |||
inline std::string causedBy( const std::string& e ){ return causedBy( e .c_str() ); } | inline std::string causedBy( const std::string& e ){ return causedBy( e .c_str() ); } | |||
/** in the mongodb source, use verify() instead of assert(). verify is | ||||
always evaluated even in release builds. */ | ||||
inline void verify( int msgid , bool testOK ) { if ( ! testOK ) verifyF | ||||
ailed( msgid ); } | ||||
/** abends on condition failure */ | /** abends on condition failure */ | |||
inline void fassert( int msgid , bool testOK ) { if ( ! testOK ) fasser tFailed( msgid ); } | inline void fassert( int msgid , bool testOK ) { if ( ! testOK ) fasser tFailed( msgid ); } | |||
#ifdef assert | ||||
#undef assert | ||||
#endif | ||||
#define MONGO_assert(_Expression) (void)( MONGO_likely(!!(_Expression)) || | ||||
(mongo::asserted(#_Expression, __FILE__, __LINE__), 0) ) | ||||
#define assert MONGO_assert | ||||
/* "user assert". if asserts, user did something wrong, not our code * / | /* "user assert". if asserts, user did something wrong, not our code * / | |||
#define MONGO_uassert(msgid, msg, expr) (void)( MONGO_likely(!!(expr)) || ( mongo::uasserted(msgid, msg), 0) ) | #define MONGO_uassert(msgid, msg, expr) (void)( MONGO_likely(!!(expr)) || ( mongo::uasserted(msgid, msg), 0) ) | |||
#define uassert MONGO_uassert | ||||
/* warning only - keeps going */ | /* warning only - keeps going */ | |||
#define MONGO_wassert(_Expression) (void)( MONGO_likely(!!(_Expression)) || (mongo::wasserted(#_Expression, __FILE__, __LINE__), 0) ) | #define MONGO_wassert(_Expression) (void)( MONGO_likely(!!(_Expression)) || (mongo::wasserted(#_Expression, __FILE__, __LINE__), 0) ) | |||
#define wassert MONGO_wassert | ||||
/* display a message, no context, and throw assertionexception | /* display a message, no context, and throw assertionexception | |||
easy way to throw an exception and log something without our stack t race | easy way to throw an exception and log something without our stack t race | |||
display happening. | display happening. | |||
*/ | */ | |||
#define MONGO_massert(msgid, msg, expr) (void)( MONGO_likely(!!(expr)) || ( mongo::msgasserted(msgid, msg), 0) ) | #define MONGO_massert(msgid, msg, expr) (void)( MONGO_likely(!!(expr)) || ( mongo::msgasserted(msgid, msg), 0) ) | |||
#define massert MONGO_massert | /* same as massert except no msgid */ | |||
#define MONGO_verify(_Expression) (void)( MONGO_likely(!!(_Expression)) || | ||||
(mongo::verifyFailed(#_Expression, __FILE__, __LINE__), 0) ) | ||||
/* dassert is 'debug assert' -- might want to turn off for production a s these | /* dassert is 'debug assert' -- might want to turn off for production a s these | |||
could be slow. | could be slow. | |||
*/ | */ | |||
#if defined(_DEBUG) | #if defined(_DEBUG) | |||
# define MONGO_dassert assert | # define MONGO_dassert verify | |||
#else | #else | |||
# define MONGO_dassert(x) | # define MONGO_dassert(x) | |||
#endif | #endif | |||
#define dassert MONGO_dassert | ||||
#ifdef MONGO_EXPOSE_MACROS | ||||
# define dassert MONGO_dassert | ||||
# define verify MONGO_verify | ||||
# define uassert MONGO_uassert | ||||
# define wassert MONGO_wassert | ||||
# define massert MONGO_massert | ||||
#endif | ||||
// some special ids that we want to duplicate | // some special ids that we want to duplicate | |||
// > 10000 asserts | // > 10000 asserts | |||
// < 10000 UserException | // < 10000 UserException | |||
enum { ASSERT_ID_DUPKEY = 11000 }; | enum { ASSERT_ID_DUPKEY = 11000 }; | |||
/* throws a uassertion with an appropriate msg */ | /* throws a uassertion with an appropriate msg */ | |||
void streamNotGood( int code , std::string msg , std::ios& myios ) MONG O_NORETURN; | MONGO_COMPILER_NORETURN void streamNotGood( int code , std::string msg , std::ios& myios ); | |||
inline void assertStreamGood(unsigned msgid, std::string msg, std::ios& myios) { | inline void assertStreamGood(unsigned msgid, std::string msg, std::ios& myios) { | |||
if( !myios.good() ) streamNotGood(msgid, msg, myios); | if( !myios.good() ) streamNotGood(msgid, msg, myios); | |||
} | } | |||
std::string demangleName( const type_info& typeinfo ); | std::string demangleName( const std::type_info& typeinfo ); | |||
} // namespace mongo | } // namespace mongo | |||
#define BOOST_CHECK_EXCEPTION MONGO_BOOST_CHECK_EXCEPTION | #define MONGO_ASSERT_ON_EXCEPTION( expression ) \ | |||
#define MONGO_BOOST_CHECK_EXCEPTION( expression ) \ | ||||
try { \ | try { \ | |||
expression; \ | expression; \ | |||
} catch ( const std::exception &e ) { \ | } catch ( const std::exception &e ) { \ | |||
stringstream ss; \ | stringstream ss; \ | |||
ss << "caught boost exception: " << e.what() << ' ' << __FILE__ << ' ' << __LINE__; \ | ss << "caught exception: " << e.what() << ' ' << __FILE__ << ' ' << __LINE__; \ | |||
msgasserted( 13294 , ss.str() ); \ | msgasserted( 13294 , ss.str() ); \ | |||
} catch ( ... ) { \ | } catch ( ... ) { \ | |||
massert( 10437 , "unknown boost failed" , false ); \ | massert( 10437 , "unknown exception" , false ); \ | |||
} | } | |||
#define MONGO_BOOST_CHECK_EXCEPTION_WITH_MSG( expression, msg ) \ | #define MONGO_ASSERT_ON_EXCEPTION_WITH_MSG( expression, msg ) \ | |||
try { \ | try { \ | |||
expression; \ | expression; \ | |||
} catch ( const std::exception &e ) { \ | } catch ( const std::exception &e ) { \ | |||
stringstream ss; \ | stringstream ss; \ | |||
ss << msg << " caught boost exception: " << e.what(); \ | ss << msg << " caught exception exception: " << e.what(); \ | |||
msgasserted( 14043 , ss.str() ); \ | msgasserted( 14043 , ss.str() ); \ | |||
} catch ( ... ) { \ | } catch ( ... ) { \ | |||
msgasserted( 14044 , std::string("unknown boost failed ") + msg ); \ | msgasserted( 14044 , std::string("unknown exception") + msg ); \ | |||
} | } | |||
#define DESTRUCTOR_GUARD MONGO_DESTRUCTOR_GUARD | #define DESTRUCTOR_GUARD MONGO_DESTRUCTOR_GUARD | |||
#define MONGO_DESTRUCTOR_GUARD( expression ) \ | #define MONGO_DESTRUCTOR_GUARD( expression ) \ | |||
try { \ | try { \ | |||
expression; \ | expression; \ | |||
} catch ( const std::exception &e ) { \ | } catch ( const std::exception &e ) { \ | |||
problem() << "caught exception (" << e.what() << ") in destructor ( " << __FUNCTION__ << ")" << endl; \ | problem() << "caught exception (" << e.what() << ") in destructor ( " << __FUNCTION__ << ")" << endl; \ | |||
} catch ( ... ) { \ | } catch ( ... ) { \ | |||
problem() << "caught unknown exception in destructor (" << __FUNCTI ON__ << ")" << endl; \ | problem() << "caught unknown exception in destructor (" << __FUNCTI ON__ << ")" << endl; \ | |||
} | } | |||
#undef MONGO_NORETURN | ||||
End of changes. 23 change blocks. | ||||
45 lines changed or deleted | 33 lines changed or added | |||
atomic_int.h | atomic_int.h | |||
---|---|---|---|---|
skipping to change at line 25 | skipping to change at line 25 | |||
* 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 | |||
#if defined(_WIN32) | #if defined(_WIN32) | |||
# include <windows.h> | # include <windows.h> | |||
#endif | #endif | |||
#include "mongo/platform/compiler.h" | ||||
namespace mongo { | namespace mongo { | |||
struct AtomicUInt { | /** | |||
* An unsigned integer supporting atomic read-modify-write operations. | ||||
* | ||||
* Many operations on these types depend on natural alignment (4 byte a | ||||
lignment for 4-byte | ||||
* words, i.e.). | ||||
*/ | ||||
struct MONGO_COMPILER_ALIGN_TYPE( 4 ) AtomicUInt { | ||||
AtomicUInt() : x(0) {} | AtomicUInt() : x(0) {} | |||
AtomicUInt(unsigned z) : x(z) { } | AtomicUInt(unsigned z) : x(z) { } | |||
operator unsigned() const { return x; } | operator unsigned() const { return x; } | |||
unsigned get() const { return x; } | unsigned get() const { return x; } | |||
inline void set(unsigned newX); | ||||
inline AtomicUInt operator++(); // ++prefix | inline AtomicUInt operator++(); // ++prefix | |||
inline AtomicUInt operator++(int);// postfix++ | inline AtomicUInt operator++(int);// postfix++ | |||
inline AtomicUInt operator--(); // --prefix | inline AtomicUInt operator--(); // --prefix | |||
inline AtomicUInt operator--(int); // postfix-- | inline AtomicUInt operator--(int); // postfix-- | |||
inline void signedAdd(int by); | ||||
inline void zero(); | inline void zero() { set(0); } | |||
volatile unsigned x; | volatile unsigned x; | |||
}; | }; | |||
#if defined(_WIN32) | #if defined(_WIN32) | |||
void AtomicUInt::zero() { | void AtomicUInt::set(unsigned newX) { | |||
InterlockedExchange((volatile long*)&x, 0); | InterlockedExchange((volatile long *)&x, newX); | |||
} | } | |||
AtomicUInt AtomicUInt::operator++() { | AtomicUInt AtomicUInt::operator++() { | |||
return InterlockedIncrement((volatile long*)&x); | return InterlockedIncrement((volatile long*)&x); | |||
} | } | |||
AtomicUInt AtomicUInt::operator++(int) { | AtomicUInt AtomicUInt::operator++(int) { | |||
return InterlockedIncrement((volatile long*)&x)-1; | return InterlockedIncrement((volatile long*)&x)-1; | |||
} | } | |||
AtomicUInt AtomicUInt::operator--() { | AtomicUInt AtomicUInt::operator--() { | |||
return InterlockedDecrement((volatile long*)&x); | return InterlockedDecrement((volatile long*)&x); | |||
} | } | |||
AtomicUInt AtomicUInt::operator--(int) { | AtomicUInt AtomicUInt::operator--(int) { | |||
return InterlockedDecrement((volatile long*)&x)+1; | return InterlockedDecrement((volatile long*)&x)+1; | |||
} | } | |||
# if defined(_WIN64) | ||||
// don't see an InterlockedAdd for _WIN32...hmmm | ||||
void AtomicUInt::signedAdd(int by) { | ||||
InterlockedAdd((volatile long *)&x,by); | ||||
} | ||||
# endif | ||||
#elif defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) | #elif defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) | |||
// this is in GCC >= 4.1 | // this is in GCC >= 4.1 | |||
inline void AtomicUInt::zero() { x = 0; } // TODO: this isn't thread sa fe - maybe | inline void AtomicUInt::set(unsigned newX) { __sync_synchronize(); x = newX; } | |||
AtomicUInt AtomicUInt::operator++() { | AtomicUInt AtomicUInt::operator++() { | |||
return __sync_add_and_fetch(&x, 1); | return __sync_add_and_fetch(&x, 1); | |||
} | } | |||
AtomicUInt AtomicUInt::operator++(int) { | AtomicUInt AtomicUInt::operator++(int) { | |||
return __sync_fetch_and_add(&x, 1); | return __sync_fetch_and_add(&x, 1); | |||
} | } | |||
AtomicUInt AtomicUInt::operator--() { | AtomicUInt AtomicUInt::operator--() { | |||
return __sync_add_and_fetch(&x, -1); | return __sync_add_and_fetch(&x, -1); | |||
} | } | |||
AtomicUInt AtomicUInt::operator--(int) { | AtomicUInt AtomicUInt::operator--(int) { | |||
return __sync_fetch_and_add(&x, -1); | return __sync_fetch_and_add(&x, -1); | |||
} | } | |||
void AtomicUInt::signedAdd(int by) { | ||||
__sync_fetch_and_add(&x, by); | ||||
} | ||||
#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) | #elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) | |||
inline void AtomicUInt::zero() { x = 0; } // TODO: this isn't thread sa | inline void AtomicUInt::set(unsigned newX) { | |||
fe | asm volatile("mfence" ::: "memory"); | |||
x = newX; | ||||
} | ||||
// from boost 1.39 interprocess/detail/atomic.hpp | // from boost 1.39 interprocess/detail/atomic.hpp | |||
inline unsigned atomic_int_helper(volatile unsigned *x, int val) { | inline unsigned atomic_int_helper(volatile unsigned *x, int val) { | |||
int r; | int r; | |||
asm volatile | asm volatile | |||
( | ( | |||
"lock\n\t" | "lock\n\t" | |||
"xadd %1, %0": | "xadd %1, %0": | |||
"+m"( *x ), "=r"( r ): // outputs (%0, %1) | "+m"( *x ), "=r"( r ): // outputs (%0, %1) | |||
"1"( val ): // inputs (%2 == %1) | "1"( val ): // inputs (%2 == %1) | |||
"memory", "cc" // clobbers | "memory", "cc" // clobbers | |||
skipping to change at line 102 | skipping to change at line 124 | |||
} | } | |||
AtomicUInt AtomicUInt::operator++(int) { | AtomicUInt AtomicUInt::operator++(int) { | |||
return atomic_int_helper(&x, 1); | return atomic_int_helper(&x, 1); | |||
} | } | |||
AtomicUInt AtomicUInt::operator--() { | AtomicUInt AtomicUInt::operator--() { | |||
return atomic_int_helper(&x, -1)-1; | return atomic_int_helper(&x, -1)-1; | |||
} | } | |||
AtomicUInt AtomicUInt::operator--(int) { | AtomicUInt AtomicUInt::operator--(int) { | |||
return atomic_int_helper(&x, -1); | return atomic_int_helper(&x, -1); | |||
} | } | |||
void AtomicUInt::signedAdd(int by) { | ||||
atomic_int_helper(&x, by); | ||||
} | ||||
#else | #else | |||
# error "unsupported compiler or platform" | # error "unsupported compiler or platform" | |||
#endif | #endif | |||
} // namespace mongo | } // namespace mongo | |||
End of changes. 11 change blocks. | ||||
9 lines changed or deleted | 34 lines changed or added | |||
background.h | background.h | |||
---|---|---|---|---|
skipping to change at line 53 | skipping to change at line 53 | |||
/* check for in progress before instantiating */ | /* check for in progress before instantiating */ | |||
BackgroundOperation(const char *ns); | BackgroundOperation(const char *ns); | |||
virtual ~BackgroundOperation(); | virtual ~BackgroundOperation(); | |||
private: | private: | |||
NamespaceString _ns; | NamespaceString _ns; | |||
static map<string, unsigned> dbsInProg; | static map<string, unsigned> dbsInProg; | |||
static set<string> nsInProg; | static set<string> nsInProg; | |||
static SimpleMutex m; | ||||
}; | }; | |||
} // namespace mongo | } // namespace mongo | |||
End of changes. 1 change blocks. | ||||
0 lines changed or deleted | 1 lines changed or added | |||
balance.h | balance.h | |||
---|---|---|---|---|
skipping to change at line 23 | skipping to change at line 23 | |||
* GNU Affero General Public License for more details. | * GNU Affero General Public License for more details. | |||
* | * | |||
* You should have received a copy of the GNU Affero General Public Licen se | * You should have received a copy of the GNU Affero General Public Licen se | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../pch.h" | #include "../pch.h" | |||
#include "../util/background.h" | #include "../util/background.h" | |||
#include "../client/dbclient.h" | ||||
#include "balancer_policy.h" | #include "balancer_policy.h" | |||
namespace mongo { | namespace mongo { | |||
/** | /** | |||
* The balancer is a background task that tries to keep the number of c hunks across all servers of the cluster even. Although | * The balancer is a background task that tries to keep the number of c hunks across all servers of the cluster even. Although | |||
* every mongos will have one balancer running, only one of them will b e active at the any given point in time. The balancer | * every mongos will have one balancer running, only one of them will b e active at the any given point in time. The balancer | |||
* uses a 'DistributedLock' for that coordination. | * uses a 'DistributedLock' for that coordination. | |||
* | * | |||
* The balancer does act continuously but in "rounds". At a given round , it would decide if there is an imbalance by | * The balancer does act continuously but in "rounds". At a given round , it would decide if there is an imbalance by | |||
End of changes. 1 change blocks. | ||||
1 lines changed or deleted | 0 lines changed or added | |||
base64.h | base64.h | |||
---|---|---|---|---|
skipping to change at line 40 | skipping to change at line 40 | |||
"+/") | "+/") | |||
, decode(new unsigned char[257]) { | , decode(new unsigned char[257]) { | |||
memset( decode.get() , 0 , 256 ); | memset( decode.get() , 0 , 256 ); | |||
for ( int i=0; i<64; i++ ) { | for ( int i=0; i<64; i++ ) { | |||
decode[ encode[i] ] = i; | decode[ encode[i] ] = i; | |||
} | } | |||
test(); | test(); | |||
} | } | |||
void test() { | void test() { | |||
assert( strlen( (char*)encode ) == 64 ); | verify( strlen( (char*)encode ) == 64 ); | |||
for ( int i=0; i<26; i++ ) | for ( int i=0; i<26; i++ ) | |||
assert( encode[i] == toupper( encode[i+26] ) ); | verify( encode[i] == toupper( encode[i+26] ) ); | |||
} | } | |||
char e( int x ) { | char e( int x ) { | |||
return encode[x&0x3f]; | return encode[x&0x3f]; | |||
} | } | |||
private: | private: | |||
const unsigned char * encode; | const unsigned char * encode; | |||
public: | public: | |||
boost::scoped_array<unsigned char> decode; | boost::scoped_array<unsigned char> decode; | |||
End of changes. 2 change blocks. | ||||
2 lines changed or deleted | 2 lines changed or added | |||
bson-inl.h | bson-inl.h | |||
---|---|---|---|---|
skipping to change at line 124 | skipping to change at line 124 | |||
case jstOID: | case jstOID: | |||
return memcmp(l.value(), r.value(), 12); | return memcmp(l.value(), r.value(), 12); | |||
case Code: | case Code: | |||
case Symbol: | case Symbol: | |||
case String: | case String: | |||
/* todo: a utf sort order version one day... */ | /* todo: a utf sort order version one day... */ | |||
{ | { | |||
// we use memcmp as we allow zeros in UTF8 strings | // we use memcmp as we allow zeros in UTF8 strings | |||
int lsz = l.valuestrsize(); | int lsz = l.valuestrsize(); | |||
int rsz = r.valuestrsize(); | int rsz = r.valuestrsize(); | |||
int common = min(lsz, rsz); | int common = std::min(lsz, rsz); | |||
int res = memcmp(l.valuestr(), r.valuestr(), common); | int res = memcmp(l.valuestr(), r.valuestr(), common); | |||
if( res ) | if( res ) | |||
return res; | return res; | |||
// longer string is the greater one | // longer string is the greater one | |||
return lsz-rsz; | return lsz-rsz; | |||
} | } | |||
case Object: | case Object: | |||
case Array: | case Array: | |||
return l.embeddedObject().woCompare( r.embeddedObject() ); | return l.embeddedObject().woCompare( r.embeddedObject() ); | |||
case DBRef: { | case DBRef: { | |||
skipping to change at line 165 | skipping to change at line 165 | |||
return f; | return f; | |||
f = strcmp( l.codeWScopeCode() , r.codeWScopeCode() ); | f = strcmp( l.codeWScopeCode() , r.codeWScopeCode() ); | |||
if ( f ) | if ( f ) | |||
return f; | return f; | |||
f = strcmp( l.codeWScopeScopeData() , r.codeWScopeScopeData() ) ; | f = strcmp( l.codeWScopeScopeData() , r.codeWScopeScopeData() ) ; | |||
if ( f ) | if ( f ) | |||
return f; | return f; | |||
return 0; | return 0; | |||
} | } | |||
default: | default: | |||
assert( false); | verify( false); | |||
} | } | |||
return -1; | return -1; | |||
} | } | |||
/* wo = "well ordered" | /* wo = "well ordered" | |||
note: (mongodb related) : this can only change in behavior when inde x version # changes | note: (mongodb related) : this can only change in behavior when inde x version # changes | |||
*/ | */ | |||
inline int BSONElement::woCompare( const BSONElement &e, | inline int BSONElement::woCompare( const BSONElement &e, | |||
bool considerFieldName ) const { | bool considerFieldName ) const { | |||
int lt = (int) canonicalType(); | int lt = (int) canonicalType(); | |||
skipping to change at line 196 | skipping to change at line 196 | |||
return x; | return x; | |||
} | } | |||
inline BSONObjIterator BSONObj::begin() const { | inline BSONObjIterator BSONObj::begin() const { | |||
return BSONObjIterator(*this); | return BSONObjIterator(*this); | |||
} | } | |||
inline BSONObj BSONElement::embeddedObjectUserCheck() const { | inline BSONObj BSONElement::embeddedObjectUserCheck() const { | |||
if ( MONGO_likely(isABSONObj()) ) | if ( MONGO_likely(isABSONObj()) ) | |||
return BSONObj(value()); | return BSONObj(value()); | |||
stringstream ss; | std::stringstream ss; | |||
ss << "invalid parameter: expected an object (" << fieldName() << " )"; | ss << "invalid parameter: expected an object (" << fieldName() << " )"; | |||
uasserted( 10065 , ss.str() ); | uasserted( 10065 , ss.str() ); | |||
return BSONObj(); // never reachable | return BSONObj(); // never reachable | |||
} | } | |||
inline BSONObj BSONElement::embeddedObject() const { | inline BSONObj BSONElement::embeddedObject() const { | |||
assert( isABSONObj() ); | verify( isABSONObj() ); | |||
return BSONObj(value()); | return BSONObj(value()); | |||
} | } | |||
inline BSONObj BSONElement::codeWScopeObject() const { | inline BSONObj BSONElement::codeWScopeObject() const { | |||
assert( type() == CodeWScope ); | verify( type() == CodeWScope ); | |||
int strSizeWNull = *(int *)( value() + 4 ); | int strSizeWNull = *(int *)( value() + 4 ); | |||
return BSONObj( value() + 4 + 4 + strSizeWNull ); | return BSONObj( value() + 4 + 4 + strSizeWNull ); | |||
} | } | |||
// deep (full) equality | // deep (full) equality | |||
inline bool BSONObj::equal(const BSONObj &rhs) const { | inline bool BSONObj::equal(const BSONObj &rhs) const { | |||
BSONObjIterator i(*this); | BSONObjIterator i(*this); | |||
BSONObjIterator j(rhs); | BSONObjIterator j(rhs); | |||
BSONElement l,r; | BSONElement l,r; | |||
do { | do { | |||
skipping to change at line 321 | skipping to change at line 321 | |||
while ( it.moreWithEOO() ) { | while ( it.moreWithEOO() ) { | |||
BSONElement e = it.next(); | BSONElement e = it.next(); | |||
if ( e.eoo() ) break; | if ( e.eoo() ) break; | |||
append(e); | append(e); | |||
} | } | |||
return *this; | return *this; | |||
} | } | |||
/* add all the fields from the object specified to this object if they don't exist */ | /* add all the fields from the object specified to this object if they don't exist */ | |||
inline BSONObjBuilder& BSONObjBuilder::appendElementsUnique(BSONObj x) { | inline BSONObjBuilder& BSONObjBuilder::appendElementsUnique(BSONObj x) { | |||
set<string> have; | std::set<std::string> have; | |||
{ | { | |||
BSONObjIterator i = iterator(); | BSONObjIterator i = iterator(); | |||
while ( i.more() ) | while ( i.more() ) | |||
have.insert( i.next().fieldName() ); | have.insert( i.next().fieldName() ); | |||
} | } | |||
BSONObjIterator it(x); | BSONObjIterator it(x); | |||
while ( it.more() ) { | while ( it.more() ) { | |||
BSONElement e = it.next(); | BSONElement e = it.next(); | |||
if ( have.count( e.fieldName() ) ) | if ( have.count( e.fieldName() ) ) | |||
skipping to change at line 401 | skipping to change at line 401 | |||
return *s_->_builder; | return *s_->_builder; | |||
} | } | |||
inline | inline | |||
BSONObjBuilder& Labeler::operator<<( const BSONElement& e ) { | BSONObjBuilder& Labeler::operator<<( const BSONElement& e ) { | |||
s_->subobj()->appendAs( e, l_.l_ ); | s_->subobj()->appendAs( e, l_.l_ ); | |||
return *s_->_builder; | return *s_->_builder; | |||
} | } | |||
// {a: {b:1}} -> {a.b:1} | // {a: {b:1}} -> {a.b:1} | |||
void nested2dotted(BSONObjBuilder& b, const BSONObj& obj, const string& base=""); | void nested2dotted(BSONObjBuilder& b, const BSONObj& obj, const std::st ring& base=""); | |||
inline BSONObj nested2dotted(const BSONObj& obj) { | inline BSONObj nested2dotted(const BSONObj& obj) { | |||
BSONObjBuilder b; | BSONObjBuilder b; | |||
nested2dotted(b, obj); | nested2dotted(b, obj); | |||
return b.obj(); | return b.obj(); | |||
} | } | |||
// {a.b:1} -> {a: {b:1}} | // {a.b:1} -> {a: {b:1}} | |||
void dotted2nested(BSONObjBuilder& b, const BSONObj& obj); | void dotted2nested(BSONObjBuilder& b, const BSONObj& obj); | |||
inline BSONObj dotted2nested(const BSONObj& obj) { | inline BSONObj dotted2nested(const BSONObj& obj) { | |||
BSONObjBuilder b; | BSONObjBuilder b; | |||
skipping to change at line 435 | skipping to change at line 435 | |||
if ( strcmp( name.data() , i.next().fieldName() ) == 0 ) | if ( strcmp( name.data() , i.next().fieldName() ) == 0 ) | |||
return true; | return true; | |||
return false; | return false; | |||
} | } | |||
/* WARNING: nested/dotted conversions are not 100% reversible | /* WARNING: nested/dotted conversions are not 100% reversible | |||
* nested2dotted(dotted2nested({a.b: {c:1}})) -> {a.b.c: 1} | * nested2dotted(dotted2nested({a.b: {c:1}})) -> {a.b.c: 1} | |||
* also, dotted2nested ignores order | * also, dotted2nested ignores order | |||
*/ | */ | |||
typedef map<string, BSONElement> BSONMap; | typedef std::map<std::string, BSONElement> BSONMap; | |||
inline BSONMap bson2map(const BSONObj& obj) { | inline BSONMap bson2map(const BSONObj& obj) { | |||
BSONMap m; | BSONMap m; | |||
BSONObjIterator it(obj); | BSONObjIterator it(obj); | |||
while (it.more()) { | while (it.more()) { | |||
BSONElement e = it.next(); | BSONElement e = it.next(); | |||
m[e.fieldName()] = e; | m[e.fieldName()] = e; | |||
} | } | |||
return m; | return m; | |||
} | } | |||
struct BSONElementFieldNameCmp { | struct BSONElementFieldNameCmp { | |||
bool operator()( const BSONElement &l, const BSONElement &r ) const { | bool operator()( const BSONElement &l, const BSONElement &r ) const { | |||
return strcmp( l.fieldName() , r.fieldName() ) <= 0; | return strcmp( l.fieldName() , r.fieldName() ) <= 0; | |||
} | } | |||
}; | }; | |||
typedef set<BSONElement, BSONElementFieldNameCmp> BSONSortedElements; | typedef std::set<BSONElement, BSONElementFieldNameCmp> BSONSortedElemen ts; | |||
inline BSONSortedElements bson2set( const BSONObj& obj ) { | inline BSONSortedElements bson2set( const BSONObj& obj ) { | |||
BSONSortedElements s; | BSONSortedElements s; | |||
BSONObjIterator it(obj); | BSONObjIterator it(obj); | |||
while ( it.more() ) | while ( it.more() ) | |||
s.insert( it.next() ); | s.insert( it.next() ); | |||
return s; | return s; | |||
} | } | |||
inline string BSONObj::toString( bool isArray, bool full ) const { | inline std::string BSONObj::toString( bool isArray, bool full ) const { | |||
if ( isEmpty() ) return "{}"; | if ( isEmpty() ) return "{}"; | |||
StringBuilder s; | StringBuilder s; | |||
toString(s, isArray, full); | toString(s, isArray, full); | |||
return s.str(); | return s.str(); | |||
} | } | |||
inline void BSONObj::toString(StringBuilder& s, bool isArray, bool ful l ) const { | inline void BSONObj::toString( StringBuilder& s, bool isArray, bool fu ll, int depth ) const { | |||
if ( isEmpty() ) { | if ( isEmpty() ) { | |||
s << "{}"; | s << "{}"; | |||
return; | return; | |||
} | } | |||
s << ( isArray ? "[ " : "{ " ); | s << ( isArray ? "[ " : "{ " ); | |||
BSONObjIterator i(*this); | BSONObjIterator i(*this); | |||
bool first = true; | bool first = true; | |||
while ( 1 ) { | while ( 1 ) { | |||
massert( 10327 , "Object does not end with EOO", i.moreWithEOO () ); | massert( 10327 , "Object does not end with EOO", i.moreWithEOO () ); | |||
skipping to change at line 494 | skipping to change at line 494 | |||
e.validate(); | e.validate(); | |||
bool end = ( e.size() + offset == this->objsize() ); | bool end = ( e.size() + offset == this->objsize() ); | |||
if ( e.eoo() ) { | if ( e.eoo() ) { | |||
massert( 10331 , "EOO Before end of object", end ); | massert( 10331 , "EOO Before end of object", end ); | |||
break; | break; | |||
} | } | |||
if ( first ) | if ( first ) | |||
first = false; | first = false; | |||
else | else | |||
s << ", "; | s << ", "; | |||
e.toString(s, !isArray, full ); | e.toString( s, !isArray, full, depth ); | |||
} | } | |||
s << ( isArray ? " ]" : " }" ); | s << ( isArray ? " ]" : " }" ); | |||
} | } | |||
inline void BSONElement::validate() const { | inline void BSONElement::validate() const { | |||
const BSONType t = type(); | const BSONType t = type(); | |||
switch( t ) { | switch( t ) { | |||
case DBRef: | case DBRef: | |||
case Code: | case Code: | |||
skipping to change at line 601 | skipping to change at line 601 | |||
case RegEx: { | case RegEx: { | |||
const char *p = value(); | const char *p = value(); | |||
size_t len1 = ( maxLen == -1 ) ? strlen( p ) : (size_t)mongo::s trnlen( p, remain ); | size_t len1 = ( maxLen == -1 ) ? strlen( p ) : (size_t)mongo::s trnlen( p, remain ); | |||
//massert( 10318 , "Invalid regex string", len1 != -1 ); // ER H - 4/28/10 - don't think this does anything | //massert( 10318 , "Invalid regex string", len1 != -1 ); // ER H - 4/28/10 - don't think this does anything | |||
p = p + len1 + 1; | p = p + len1 + 1; | |||
size_t len2; | size_t len2; | |||
if( maxLen == -1 ) | if( maxLen == -1 ) | |||
len2 = strlen( p ); | len2 = strlen( p ); | |||
else { | else { | |||
size_t x = remain - len1 - 1; | size_t x = remain - len1 - 1; | |||
assert( x <= 0x7fffffff ); | verify( x <= 0x7fffffff ); | |||
len2 = mongo::strnlen( p, (int) x ); | len2 = mongo::strnlen( p, (int) x ); | |||
} | } | |||
//massert( 10319 , "Invalid regex options string", len2 != -1 ); // ERH - 4/28/10 - don't think this does anything | //massert( 10319 , "Invalid regex options string", len2 != -1 ); // ERH - 4/28/10 - don't think this does anything | |||
x = (int) (len1 + 1 + len2 + 1); | x = (int) (len1 + 1 + len2 + 1); | |||
} | } | |||
break; | break; | |||
default: { | default: { | |||
StringBuilder ss; | StringBuilder ss; | |||
ss << "BSONElement: bad type " << (int) type(); | ss << "BSONElement: bad type " << (int) type(); | |||
string msg = ss.str(); | std::string msg = ss.str(); | |||
massert( 13655 , msg.c_str(),false); | massert( 13655 , msg.c_str(),false); | |||
} | } | |||
} | } | |||
totalSize = x + fieldNameSize() + 1; // BSONType | totalSize = x + fieldNameSize() + 1; // BSONType | |||
return totalSize; | return totalSize; | |||
} | } | |||
inline int BSONElement::size() const { | inline int BSONElement::size() const { | |||
if ( totalSize >= 0 ) | if ( totalSize >= 0 ) | |||
skipping to change at line 677 | skipping to change at line 677 | |||
p = p + len1 + 1; | p = p + len1 + 1; | |||
size_t len2; | size_t len2; | |||
len2 = strlen( p ); | len2 = strlen( p ); | |||
x = (int) (len1 + 1 + len2 + 1); | x = (int) (len1 + 1 + len2 + 1); | |||
} | } | |||
break; | break; | |||
default: | default: | |||
{ | { | |||
StringBuilder ss; | StringBuilder ss; | |||
ss << "BSONElement: bad type " << (int) type(); | ss << "BSONElement: bad type " << (int) type(); | |||
string msg = ss.str(); | std::string msg = ss.str(); | |||
massert(10320 , msg.c_str(),false); | massert(10320 , msg.c_str(),false); | |||
} | } | |||
} | } | |||
totalSize = x + fieldNameSize() + 1; // BSONType | totalSize = x + fieldNameSize() + 1; // BSONType | |||
return totalSize; | return totalSize; | |||
} | } | |||
inline string BSONElement::toString( bool includeFieldName, bool full ) const { | inline std::string BSONElement::toString( bool includeFieldName, bool f ull ) const { | |||
StringBuilder s; | StringBuilder s; | |||
toString(s, includeFieldName, full); | toString(s, includeFieldName, full); | |||
return s.str(); | return s.str(); | |||
} | } | |||
inline void BSONElement::toString(StringBuilder& s, bool includeFieldNa | inline void BSONElement::toString( StringBuilder& s, bool includeFieldN | |||
me, bool full ) const { | ame, bool full, int depth ) const { | |||
if ( depth > BSONObj::maxToStringRecursionDepth ) { | ||||
// check if we want the full/complete string | ||||
if ( full ) { | ||||
StringBuilder s; | ||||
s << "Reached maximum recursion depth of "; | ||||
s << BSONObj::maxToStringRecursionDepth; | ||||
uassert(16150, s.str(), full != true); | ||||
} | ||||
s << "..."; | ||||
return; | ||||
} | ||||
if ( includeFieldName && type() != EOO ) | if ( includeFieldName && type() != EOO ) | |||
s << fieldName() << ": "; | s << fieldName() << ": "; | |||
switch ( type() ) { | switch ( type() ) { | |||
case EOO: | case EOO: | |||
s << "EOO"; | s << "EOO"; | |||
break; | break; | |||
case mongo::Date: | case mongo::Date: | |||
s << "new Date(" << (long long) date() << ')'; | s << "new Date(" << (long long) date() << ')'; | |||
break; | break; | |||
case RegEx: { | case RegEx: { | |||
skipping to change at line 720 | skipping to change at line 733 | |||
case NumberLong: | case NumberLong: | |||
s << _numberLong(); | s << _numberLong(); | |||
break; | break; | |||
case NumberInt: | case NumberInt: | |||
s << _numberInt(); | s << _numberInt(); | |||
break; | break; | |||
case mongo::Bool: | case mongo::Bool: | |||
s << ( boolean() ? "true" : "false" ); | s << ( boolean() ? "true" : "false" ); | |||
break; | break; | |||
case Object: | case Object: | |||
embeddedObject().toString(s, false, full); | embeddedObject().toString(s, false, full, depth+1); | |||
break; | break; | |||
case mongo::Array: | case mongo::Array: | |||
embeddedObject().toString(s, true, full); | embeddedObject().toString(s, true, full, depth+1); | |||
break; | break; | |||
case Undefined: | case Undefined: | |||
s << "undefined"; | s << "undefined"; | |||
break; | break; | |||
case jstNULL: | case jstNULL: | |||
s << "null"; | s << "null"; | |||
break; | break; | |||
case MaxKey: | case MaxKey: | |||
s << "MaxKey"; | s << "MaxKey"; | |||
break; | break; | |||
skipping to change at line 798 | skipping to change at line 811 | |||
} | } | |||
/* return has eoo() true if no match | /* return has eoo() true if no match | |||
supports "." notation to reach into embedded objects | supports "." notation to reach into embedded objects | |||
*/ | */ | |||
inline BSONElement BSONObj::getFieldDotted(const char *name) const { | inline BSONElement BSONObj::getFieldDotted(const char *name) const { | |||
BSONElement e = getField( name ); | BSONElement e = getField( name ); | |||
if ( e.eoo() ) { | if ( e.eoo() ) { | |||
const char *p = strchr(name, '.'); | const char *p = strchr(name, '.'); | |||
if ( p ) { | if ( p ) { | |||
string left(name, p-name); | std::string left(name, p-name); | |||
BSONObj sub = getObjectField(left.c_str()); | BSONObj sub = getObjectField(left.c_str()); | |||
return sub.isEmpty() ? BSONElement() : sub.getFieldDotted(p +1); | return sub.isEmpty() ? BSONElement() : sub.getFieldDotted(p +1); | |||
} | } | |||
} | } | |||
return e; | return e; | |||
} | } | |||
inline BSONObj BSONObj::getObjectField(const char *name) const { | inline BSONObj BSONObj::getObjectField(const char *name) const { | |||
BSONElement e = getField(name); | BSONElement e = getField(name); | |||
skipping to change at line 836 | skipping to change at line 849 | |||
/* little endian ordering here, but perhaps that is ok regardless a s BSON is spec'd | /* little endian ordering here, but perhaps that is ok regardless a s BSON is spec'd | |||
to be little endian external to the system. (i.e. the rest of th e implementation of bson, | to be little endian external to the system. (i.e. the rest of th e implementation of bson, | |||
not this part, fails to support big endian) | not this part, fails to support big endian) | |||
*/ | */ | |||
static char p[] = { /*size*/5, 0, 0, 0, /*eoo*/0 }; | static char p[] = { /*size*/5, 0, 0, 0, /*eoo*/0 }; | |||
_objdata = p; | _objdata = p; | |||
} | } | |||
inline BSONObj BSONElement::Obj() const { return embeddedObjectUserChec k(); } | inline BSONObj BSONElement::Obj() const { return embeddedObjectUserChec k(); } | |||
inline BSONElement BSONElement::operator[] (const string& field) const { | inline BSONElement BSONElement::operator[] (const std::string& field) c onst { | |||
BSONObj o = Obj(); | BSONObj o = Obj(); | |||
return o[field]; | return o[field]; | |||
} | } | |||
inline void BSONObj::elems(vector<BSONElement> &v) const { | inline void BSONObj::elems(std::vector<BSONElement> &v) const { | |||
BSONObjIterator i(*this); | BSONObjIterator i(*this); | |||
while( i.more() ) | while( i.more() ) | |||
v.push_back(i.next()); | v.push_back(i.next()); | |||
} | } | |||
inline void BSONObj::elems(list<BSONElement> &v) const { | inline void BSONObj::elems(std::list<BSONElement> &v) const { | |||
BSONObjIterator i(*this); | BSONObjIterator i(*this); | |||
while( i.more() ) | while( i.more() ) | |||
v.push_back(i.next()); | v.push_back(i.next()); | |||
} | } | |||
template <class T> | template <class T> | |||
void BSONObj::Vals(vector<T>& v) const { | void BSONObj::Vals(std::vector<T>& v) const { | |||
BSONObjIterator i(*this); | BSONObjIterator i(*this); | |||
while( i.more() ) { | while( i.more() ) { | |||
T t; | T t; | |||
i.next().Val(t); | i.next().Val(t); | |||
v.push_back(t); | v.push_back(t); | |||
} | } | |||
} | } | |||
template <class T> | template <class T> | |||
void BSONObj::Vals(list<T>& v) const { | void BSONObj::Vals(std::list<T>& v) const { | |||
BSONObjIterator i(*this); | BSONObjIterator i(*this); | |||
while( i.more() ) { | while( i.more() ) { | |||
T t; | T t; | |||
i.next().Val(t); | i.next().Val(t); | |||
v.push_back(t); | v.push_back(t); | |||
} | } | |||
} | } | |||
template <class T> | template <class T> | |||
void BSONObj::vals(vector<T>& v) const { | void BSONObj::vals(std::vector<T>& v) const { | |||
BSONObjIterator i(*this); | BSONObjIterator i(*this); | |||
while( i.more() ) { | while( i.more() ) { | |||
try { | try { | |||
T t; | T t; | |||
i.next().Val(t); | i.next().Val(t); | |||
v.push_back(t); | v.push_back(t); | |||
} | } | |||
catch(...) { } | catch(...) { } | |||
} | } | |||
} | } | |||
template <class T> | template <class T> | |||
void BSONObj::vals(list<T>& v) const { | void BSONObj::vals(std::list<T>& v) const { | |||
BSONObjIterator i(*this); | BSONObjIterator i(*this); | |||
while( i.more() ) { | while( i.more() ) { | |||
try { | try { | |||
T t; | T t; | |||
i.next().Val(t); | i.next().Val(t); | |||
v.push_back(t); | v.push_back(t); | |||
} | } | |||
catch(...) { } | catch(...) { } | |||
} | } | |||
} | } | |||
inline ostream& operator<<( ostream &s, const BSONObj &o ) { | inline std::ostream& operator<<( std::ostream &s, const BSONObj &o ) { | |||
return s << o.toString(); | return s << o.toString(); | |||
} | } | |||
inline ostream& operator<<( ostream &s, const BSONElement &e ) { | inline std::ostream& operator<<( std::ostream &s, const BSONElement &e ) { | |||
return s << e.toString(); | return s << e.toString(); | |||
} | } | |||
inline StringBuilder& operator<<( StringBuilder &s, const BSONObj &o ) { | inline StringBuilder& operator<<( StringBuilder &s, const BSONObj &o ) { | |||
o.toString( s ); | o.toString( s ); | |||
return s; | return s; | |||
} | } | |||
inline StringBuilder& operator<<( StringBuilder &s, const BSONElement & e ) { | inline StringBuilder& operator<<( StringBuilder &s, const BSONElement & e ) { | |||
e.toString( s ); | e.toString( s ); | |||
return s; | return s; | |||
skipping to change at line 924 | skipping to change at line 937 | |||
inline void BSONElement::Val(BSONObj& v) const { v = Obj(); } | inline void BSONElement::Val(BSONObj& v) const { v = Obj(); } | |||
template<typename T> | template<typename T> | |||
inline BSONFieldValue<BSONObj> BSONField<T>::query( const char * q , co nst T& t ) const { | inline BSONFieldValue<BSONObj> BSONField<T>::query( const char * q , co nst T& t ) const { | |||
BSONObjBuilder b; | BSONObjBuilder b; | |||
b.append( q , t ); | b.append( q , t ); | |||
return BSONFieldValue<BSONObj>( _name , b.obj() ); | return BSONFieldValue<BSONObj>( _name , b.obj() ); | |||
} | } | |||
// used by jsonString() | // used by jsonString() | |||
inline string escape( string s , bool escape_slash=false) { | inline std::string escape( std::string s , bool escape_slash=false) { | |||
StringBuilder ret; | StringBuilder ret; | |||
for ( string::iterator i = s.begin(); i != s.end(); ++i ) { | for ( std::string::iterator i = s.begin(); i != s.end(); ++i ) { | |||
switch ( *i ) { | switch ( *i ) { | |||
case '"': | case '"': | |||
ret << "\\\""; | ret << "\\\""; | |||
break; | break; | |||
case '\\': | case '\\': | |||
ret << "\\\\"; | ret << "\\\\"; | |||
break; | break; | |||
case '/': | case '/': | |||
ret << (escape_slash ? "\\/" : "/"); | ret << (escape_slash ? "\\/" : "/"); | |||
break; | break; | |||
skipping to change at line 966 | skipping to change at line 979 | |||
ret << "\\u00" << toHexLower(&c, 1); | ret << "\\u00" << toHexLower(&c, 1); | |||
} | } | |||
else { | else { | |||
ret << *i; | ret << *i; | |||
} | } | |||
} | } | |||
} | } | |||
return ret.str(); | return ret.str(); | |||
} | } | |||
inline string BSONObj::hexDump() const { | inline std::string BSONObj::hexDump() const { | |||
stringstream ss; | std::stringstream ss; | |||
const char *d = objdata(); | const char *d = objdata(); | |||
int size = objsize(); | int size = objsize(); | |||
for( int i = 0; i < size; ++i ) { | for( int i = 0; i < size; ++i ) { | |||
ss.width( 2 ); | ss.width( 2 ); | |||
ss.fill( '0' ); | ss.fill( '0' ); | |||
ss << hex << (unsigned)(unsigned char)( d[ i ] ) << dec; | ss << std::hex << (unsigned)(unsigned char)( d[ i ] ) << std::d ec; | |||
if ( ( d[ i ] >= '0' && d[ i ] <= '9' ) || ( d[ i ] >= 'A' && d [ i ] <= 'z' ) ) | if ( ( d[ i ] >= '0' && d[ i ] <= '9' ) || ( d[ i ] >= 'A' && d [ i ] <= 'z' ) ) | |||
ss << '\'' << d[ i ] << '\''; | ss << '\'' << d[ i ] << '\''; | |||
if ( i != size - 1 ) | if ( i != size - 1 ) | |||
ss << ' '; | ss << ' '; | |||
} | } | |||
return ss.str(); | return ss.str(); | |||
} | } | |||
inline void BSONObjBuilder::appendKeys( const BSONObj& keyPattern , con st BSONObj& values ) { | inline void BSONObjBuilder::appendKeys( const BSONObj& keyPattern , con st BSONObj& values ) { | |||
BSONObjIterator i(keyPattern); | BSONObjIterator i(keyPattern); | |||
BSONObjIterator j(values); | BSONObjIterator j(values); | |||
while ( i.more() && j.more() ) { | while ( i.more() && j.more() ) { | |||
appendAs( j.next() , i.next().fieldName() ); | appendAs( j.next() , i.next().fieldName() ); | |||
} | } | |||
assert( ! i.more() ); | verify( ! i.more() ); | |||
assert( ! j.more() ); | verify( ! j.more() ); | |||
} | } | |||
inline BSONObj BSONObj::removeField(const StringData& name) const { | inline BSONObj BSONObj::removeField(const StringData& name) const { | |||
BSONObjBuilder b; | BSONObjBuilder b; | |||
BSONObjIterator i(*this); | BSONObjIterator i(*this); | |||
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(name.data(), fname) ) | if( strcmp(name.data(), fname) ) | |||
b.append(e); | b.append(e); | |||
End of changes. 34 change blocks. | ||||
37 lines changed or deleted | 50 lines changed or added | |||
bson.h | bson.h | |||
---|---|---|---|---|
skipping to change at line 56 | skipping to change at line 56 | |||
#include <sstream> | #include <sstream> | |||
//#include <boost/utility.hpp> | //#include <boost/utility.hpp> | |||
namespace bson { | namespace bson { | |||
using std::string; | using std::string; | |||
using std::stringstream; | using std::stringstream; | |||
class assertion : public std::exception { | class assertion : public std::exception { | |||
public: | public: | |||
assertion( unsigned u , const string& s ) | assertion( unsigned u , const std::string& s ) | |||
: id( u ) , msg( s ) { | : id( u ) , msg( s ) { | |||
stringstream ss; | std::stringstream ss; | |||
ss << "BsonAssertion id: " << u << " " << s; | ss << "BsonAssertion id: " << u << " " << s; | |||
full = ss.str(); | full = ss.str(); | |||
} | } | |||
virtual ~assertion() throw() {} | virtual ~assertion() throw() {} | |||
virtual const char* what() const throw() { return full.c_str(); } | virtual const char* what() const throw() { return full.c_str(); } | |||
unsigned id; | unsigned id; | |||
string msg; | std::string msg; | |||
string full; | std::string full; | |||
}; | }; | |||
} | } | |||
namespace mongo { | namespace mongo { | |||
#if !defined(assert) | #if !defined(verify) | |||
inline void assert(bool expr) { | inline void verify(bool expr) { | |||
if(!expr) { | if(!expr) { | |||
throw bson::assertion( 0 , "assertion failure in bson library" ); | throw bson::assertion( 0 , "assertion failure in bson library" ); | |||
} | } | |||
} | } | |||
#endif | #endif | |||
#if !defined(uassert) | #if !defined(uassert) | |||
inline void uasserted(unsigned msgid, std::string s) { | inline void uasserted(unsigned msgid, std::string s) { | |||
throw bson::assertion( msgid , s ); | throw bson::assertion( msgid , s ); | |||
} | } | |||
End of changes. 4 change blocks. | ||||
6 lines changed or deleted | 6 lines changed or added | |||
bson_db.h | bson_db.h | |||
---|---|---|---|---|
skipping to change at line 50 | skipping to change at line 50 | |||
appendTimestamp( fieldName , t.asDate() ); | appendTimestamp( fieldName , t.asDate() ); | |||
return *this; | return *this; | |||
} | } | |||
inline OpTime BSONElement::_opTime() const { | inline OpTime BSONElement::_opTime() const { | |||
if( type() == mongo::Date || type() == Timestamp ) | if( type() == mongo::Date || type() == Timestamp ) | |||
return OpTime( *reinterpret_cast< const unsigned long long* >( value() ) ); | return OpTime( *reinterpret_cast< const unsigned long long* >( value() ) ); | |||
return OpTime(); | return OpTime(); | |||
} | } | |||
inline string BSONElement::_asCode() const { | inline std::string BSONElement::_asCode() const { | |||
switch( type() ) { | switch( type() ) { | |||
case mongo::String: | case mongo::String: | |||
case Code: | case Code: | |||
return string(valuestr(), valuestrsize()-1); | return std::string(valuestr(), valuestrsize()-1); | |||
case CodeWScope: | case CodeWScope: | |||
return string(codeWScopeCode(), *(int*)(valuestr())-1); | return std::string(codeWScopeCode(), *(int*)(valuestr())-1); | |||
default: | default: | |||
log() << "can't convert type: " << (int)(type()) << " to code" << endl; | log() << "can't convert type: " << (int)(type()) << " to code" << std::endl; | |||
} | } | |||
uassert( 10062 , "not code" , 0 ); | uassert( 10062 , "not code" , 0 ); | |||
return ""; | return ""; | |||
} | } | |||
inline BSONObjBuilder& BSONObjBuilderValueStream::operator<<(DateNowLab eler& id) { | inline BSONObjBuilder& BSONObjBuilderValueStream::operator<<(DateNowLab eler& id) { | |||
_builder->appendDate(_fieldName, jsTime()); | _builder->appendDate(_fieldName, jsTime()); | |||
_fieldName = 0; | _fieldName = 0; | |||
return *_builder; | return *_builder; | |||
} | } | |||
End of changes. 4 change blocks. | ||||
4 lines changed or deleted | 4 lines changed or added | |||
bsonelement.h | bsonelement.h | |||
---|---|---|---|---|
skipping to change at line 62 | skipping to change at line 62 | |||
-------- size() ------------ | -------- size() ------------ | |||
-fieldNameSize- | -fieldNameSize- | |||
value() | value() | |||
type() | type() | |||
*/ | */ | |||
class BSONElement { | class BSONElement { | |||
public: | public: | |||
/** These functions, which start with a capital letter, throw a Use rException if the | /** These functions, which start with a capital letter, throw a Use rException if the | |||
element is not of the required type. Example: | element is not of the required type. Example: | |||
string foo = obj["foo"].String(); // exception if not a string type or DNE | std::string foo = obj["foo"].String(); // std::exception if not a std::string type or DNE | |||
*/ | */ | |||
string String() const { return chk(mongo::String).value str(); } | std::string String() const { return chk(mongo::String).value str(); } | |||
Date_t Date() const { return chk(mongo::Date).date(); } | Date_t Date() const { return chk(mongo::Date).date(); } | |||
double Number() const { return chk(isNumber()).number() ; } | double Number() const { return chk(isNumber()).number() ; } | |||
double Double() const { return chk(NumberDouble)._numbe rDouble(); } | double Double() const { return chk(NumberDouble)._numbe rDouble(); } | |||
long long Long() const { return chk(NumberLong)._numberL ong(); } | long long Long() const { return chk(NumberLong)._numberL ong(); } | |||
int Int() const { return chk(NumberInt)._numberIn t(); } | int Int() const { return chk(NumberInt)._numberIn t(); } | |||
bool Bool() const { return chk(mongo::Bool).boolean (); } | bool Bool() const { return chk(mongo::Bool).boolean (); } | |||
vector<BSONElement> Array() const; // see implementation for detail ed comments | std::vector<BSONElement> Array() const; // see implementation for d etailed comments | |||
mongo::OID OID() const { return chk(jstOID).__oid(); } | mongo::OID OID() const { return chk(jstOID).__oid(); } | |||
void Null() const { chk(isNull()); } // throw UserE xception if not null | void Null() const { chk(isNull()); } // throw UserE xception if not null | |||
void OK() const { chk(ok()); } // throw UserE xception if element DNE | void OK() const { chk(ok()); } // throw UserE xception if element DNE | |||
/** @return the embedded object associated with this field. | /** @return the embedded object associated with this field. | |||
Note the returned object is a reference to within the parent bs on object. If that | Note the returned object is a reference to within the parent bs on object. If that | |||
object is out of scope, this pointer will no longer be valid. Ca ll getOwned() on the | object is out of scope, this pointer will no longer be valid. Ca ll getOwned() on the | |||
returned BSONObj if you need your own copy. | returned BSONObj if you need your own copy. | |||
throws UserException if the element is not of type object. | throws UserException if the element is not of type object. | |||
*/ | */ | |||
skipping to change at line 94 | skipping to change at line 94 | |||
/** populate v with the value of the element. If type does not mat ch, throw exception. | /** populate v with the value of the element. If type does not mat ch, throw exception. | |||
useful in templates -- see also BSONObj::Vals(). | useful in templates -- see also BSONObj::Vals(). | |||
*/ | */ | |||
void Val(Date_t& v) const { v = Date(); } | void Val(Date_t& v) const { v = Date(); } | |||
void Val(long long& v) const { v = Long(); } | void Val(long long& v) const { v = Long(); } | |||
void Val(bool& v) const { v = Bool(); } | void Val(bool& v) const { v = Bool(); } | |||
void Val(BSONObj& v) const; | void Val(BSONObj& v) const; | |||
void Val(mongo::OID& v) const { v = OID(); } | void Val(mongo::OID& v) const { v = OID(); } | |||
void Val(int& v) const { v = Int(); } | void Val(int& v) const { v = Int(); } | |||
void Val(double& v) const { v = Double(); } | void Val(double& v) const { v = Double(); } | |||
void Val(string& v) const { v = String(); } | void Val(std::string& v) const { v = String(); } | |||
/** Use ok() to check if a value is assigned: | /** Use ok() to check if a value is assigned: | |||
if( myObj["foo"].ok() ) ... | if( myObj["foo"].ok() ) ... | |||
*/ | */ | |||
bool ok() const { return !eoo(); } | bool ok() const { return !eoo(); } | |||
string toString( bool includeFieldName = true, bool full=false) con | std::string toString( bool includeFieldName = true, bool full=false | |||
st; | ) const; | |||
void toString(StringBuilder& s, bool includeFieldName = true, bool | void toString(StringBuilder& s, bool includeFieldName = true, bool | |||
full=false) const; | full=false, int depth=0) const; | |||
string jsonString( JsonStringFormat format, bool includeFieldNames | std::string jsonString( JsonStringFormat format, bool includeFieldN | |||
= true, int pretty = 0 ) const; | ames = true, int pretty = 0 ) const; | |||
operator string() const { return toString(); } | operator std::string() const { return toString(); } | |||
/** Returns the type of the element */ | /** Returns the type of the element */ | |||
BSONType type() const { return (BSONType) *data; } | BSONType type() const { return (BSONType) *reinterpret_cast< const signed char * >(data); } | |||
/** retrieve a field within this element | /** retrieve a field within this element | |||
throws exception if *this is not an embedded object | throws exception if *this is not an embedded object | |||
*/ | */ | |||
BSONElement operator[] (const string& field) const; | BSONElement operator[] (const std::string& field) const; | |||
/** returns the tyoe of the element fixed for the main type | /** returns the tyoe of the element fixed for the main type | |||
the main purpose is numbers. any numeric type will return Numb erDouble | the main purpose is numbers. any numeric type will return Numb erDouble | |||
Note: if the order changes, indexes have to be re-built or than can be corruption | Note: if the order changes, indexes have to be re-built or than can be corruption | |||
*/ | */ | |||
int canonicalType() const; | int canonicalType() const; | |||
/** Indicates if it is the end-of-object element, which is present at the end of | /** Indicates if it is the end-of-object element, which is present at the end of | |||
every BSON object. | every BSON object. | |||
*/ | */ | |||
skipping to change at line 186 | skipping to change at line 186 | |||
*/ | */ | |||
bool trueValue() const; | bool trueValue() const; | |||
/** True if number, string, bool, date, OID */ | /** True if number, string, bool, date, OID */ | |||
bool isSimpleType() const; | bool isSimpleType() const; | |||
/** True if element is of a numeric type. */ | /** True if element is of a numeric type. */ | |||
bool isNumber() const; | bool isNumber() const; | |||
/** Return double value for this field. MUST be NumberDouble type. */ | /** Return double value for this field. MUST be NumberDouble type. */ | |||
double _numberDouble() const {return *reinterpret_cast< const doubl e* >( value() ); } | double _numberDouble() const {return (reinterpret_cast< const Packe dDouble* >( value() ))->d; } | |||
/** Return int value for this field. MUST be NumberInt type. */ | /** Return int value for this field. MUST be NumberInt type. */ | |||
int _numberInt() const {return *reinterpret_cast< const int* >( val ue() ); } | int _numberInt() const {return *reinterpret_cast< const int* >( val ue() ); } | |||
/** Return long long value for this field. MUST be NumberLong type. */ | /** Return long long value for this field. MUST be NumberLong type. */ | |||
long long _numberLong() const {return *reinterpret_cast< const long long* >( value() ); } | long long _numberLong() const {return *reinterpret_cast< const long long* >( value() ); } | |||
/** Retrieve int value for the element safely. Zero returned if no t a number. */ | /** Retrieve int value for the element safely. Zero returned if no t a number. */ | |||
int numberInt() const; | int numberInt() const; | |||
/** Retrieve long value for the element safely. Zero returned if n ot a number. */ | /** Retrieve long value for the element safely. Zero returned if n ot a number. */ | |||
long long numberLong() const; | long long numberLong() const; | |||
/** Retrieve the numeric value of the element. If not of a numeric type, returns 0. | /** Retrieve the numeric value of the element. If not of a numeric type, returns 0. | |||
skipping to change at line 239 | skipping to change at line 239 | |||
*/ | */ | |||
const char * valuestr() const { | const char * valuestr() const { | |||
return value() + 4; | return value() + 4; | |||
} | } | |||
/** Get the string value of the element. If not a string returns " ". */ | /** Get the string value of the element. If not a string returns " ". */ | |||
const char *valuestrsafe() const { | const char *valuestrsafe() const { | |||
return type() == mongo::String ? valuestr() : ""; | return type() == mongo::String ? valuestr() : ""; | |||
} | } | |||
/** Get the string value of the element. If not a string returns " ". */ | /** Get the string value of the element. If not a string returns " ". */ | |||
string str() const { | std::string str() const { | |||
return type() == mongo::String ? string(valuestr(), valuestrsiz | return type() == mongo::String ? std::string(valuestr(), values | |||
e()-1) : string(); | trsize()-1) : std::string(); | |||
} | } | |||
/** Get javascript code of a CodeWScope data element. */ | /** Get javascript code of a CodeWScope data element. */ | |||
const char * codeWScopeCode() const { | const char * codeWScopeCode() const { | |||
return value() + 8; | return value() + 8; | |||
} | } | |||
/** Get the scope SavedContext of a CodeWScope data element. */ | /** Get the scope SavedContext of a CodeWScope data element. */ | |||
const char * codeWScopeScopeData() const { | const char * codeWScopeScopeData() const { | |||
// TODO fix | // TODO fix | |||
return codeWScopeCode() + strlen( codeWScopeCode() ) + 1; | return codeWScopeCode() + strlen( codeWScopeCode() ) + 1; | |||
skipping to change at line 264 | skipping to change at line 264 | |||
BSONObj embeddedObject() const; | BSONObj embeddedObject() const; | |||
/* uasserts if not an object */ | /* uasserts if not an object */ | |||
BSONObj embeddedObjectUserCheck() const; | BSONObj embeddedObjectUserCheck() const; | |||
BSONObj codeWScopeObject() const; | BSONObj codeWScopeObject() const; | |||
/** Get raw binary data. Element must be of type BinData. Doesn't handle type 2 specially */ | /** Get raw binary data. Element must be of type BinData. Doesn't handle type 2 specially */ | |||
const char *binData(int& len) const { | const char *binData(int& len) const { | |||
// BinData: <int len> <byte subtype> <byte[len] data> | // BinData: <int len> <byte subtype> <byte[len] data> | |||
assert( type() == BinData ); | verify( type() == BinData ); | |||
len = valuestrsize(); | len = valuestrsize(); | |||
return value() + 5; | return value() + 5; | |||
} | } | |||
/** Get binary data. Element must be of type BinData. Handles type 2 */ | /** Get binary data. Element must be of type BinData. Handles type 2 */ | |||
const char *binDataClean(int& len) const { | const char *binDataClean(int& len) const { | |||
// BinData: <int len> <byte subtype> <byte[len] data> | // BinData: <int len> <byte subtype> <byte[len] data> | |||
if (binDataType() != ByteArrayDeprecated) { | if (binDataType() != ByteArrayDeprecated) { | |||
return binData(len); | return binData(len); | |||
} | } | |||
else { | else { | |||
// Skip extra size | // Skip extra size | |||
len = valuestrsize() - 4; | len = valuestrsize() - 4; | |||
return value() + 5 + 4; | return value() + 5 + 4; | |||
} | } | |||
} | } | |||
BinDataType binDataType() const { | BinDataType binDataType() const { | |||
// BinData: <int len> <byte subtype> <byte[len] data> | // BinData: <int len> <byte subtype> <byte[len] data> | |||
assert( type() == BinData ); | verify( type() == BinData ); | |||
unsigned char c = (value() + 4)[0]; | unsigned char c = (value() + 4)[0]; | |||
return (BinDataType)c; | return (BinDataType)c; | |||
} | } | |||
/** Retrieve the regex string for a Regex element */ | /** Retrieve the regex string for a Regex element */ | |||
const char *regex() const { | const char *regex() const { | |||
assert(type() == RegEx); | verify(type() == RegEx); | |||
return value(); | return value(); | |||
} | } | |||
/** Retrieve the regex flags (options) for a Regex element */ | /** Retrieve the regex flags (options) for a Regex element */ | |||
const char *regexFlags() const { | const char *regexFlags() const { | |||
const char *p = regex(); | const char *p = regex(); | |||
return p + strlen(p) + 1; | return p + strlen(p) + 1; | |||
} | } | |||
/** like operator== but doesn't check the fieldname, | /** like operator== but doesn't check the fieldname, | |||
skipping to change at line 409 | skipping to change at line 409 | |||
explicit BSONElement(const char *d) : data(d) { | explicit BSONElement(const char *d) : data(d) { | |||
fieldNameSize_ = -1; | fieldNameSize_ = -1; | |||
totalSize = -1; | totalSize = -1; | |||
if ( eoo() ) { | if ( eoo() ) { | |||
fieldNameSize_ = 0; | fieldNameSize_ = 0; | |||
totalSize = 1; | totalSize = 1; | |||
} | } | |||
} | } | |||
string _asCode() const; | std::string _asCode() const; | |||
OpTime _opTime() const; | OpTime _opTime() const; | |||
private: | private: | |||
const char *data; | const char *data; | |||
mutable int fieldNameSize_; // cached value | mutable int fieldNameSize_; // cached value | |||
int fieldNameSize() const { | int fieldNameSize() const { | |||
if ( fieldNameSize_ == -1 ) | if ( fieldNameSize_ == -1 ) | |||
fieldNameSize_ = (int)strlen( fieldName() ) + 1; | fieldNameSize_ = (int)strlen( fieldName() ) + 1; | |||
return fieldNameSize_; | return fieldNameSize_; | |||
} | } | |||
skipping to change at line 481 | skipping to change at line 481 | |||
return 45; | return 45; | |||
case RegEx: | case RegEx: | |||
return 50; | return 50; | |||
case DBRef: | case DBRef: | |||
return 55; | return 55; | |||
case Code: | case Code: | |||
return 60; | return 60; | |||
case CodeWScope: | case CodeWScope: | |||
return 65; | return 65; | |||
default: | default: | |||
assert(0); | verify(0); | |||
return -1; | return -1; | |||
} | } | |||
} | } | |||
inline bool BSONElement::trueValue() const { | inline bool BSONElement::trueValue() const { | |||
switch( type() ) { | switch( type() ) { | |||
case NumberLong: | case NumberLong: | |||
return *reinterpret_cast< const long long* >( value() ) != 0; | return *reinterpret_cast< const long long* >( value() ) != 0; | |||
case NumberDouble: | case NumberDouble: | |||
return *reinterpret_cast< const double* >( value() ) != 0; | return (reinterpret_cast < const PackedDouble* >(value ()))->d != 0; | |||
case NumberInt: | case NumberInt: | |||
return *reinterpret_cast< const int* >( value() ) != 0; | return *reinterpret_cast< const int* >( value() ) != 0; | |||
case mongo::Bool: | case mongo::Bool: | |||
return boolean(); | return boolean(); | |||
case EOO: | case EOO: | |||
case jstNULL: | case jstNULL: | |||
case Undefined: | case Undefined: | |||
return false; | return false; | |||
default: | default: | |||
End of changes. 15 change blocks. | ||||
23 lines changed or deleted | 23 lines changed or added | |||
bsonmisc.h | bsonmisc.h | |||
---|---|---|---|---|
skipping to change at line 41 | skipping to change at line 41 | |||
public: | public: | |||
BSONObjCmp( const BSONObj &order = BSONObj() ) : _order( order ) {} | BSONObjCmp( const BSONObj &order = BSONObj() ) : _order( order ) {} | |||
bool operator()( const BSONObj &l, const BSONObj &r ) const { | bool operator()( const BSONObj &l, const BSONObj &r ) const { | |||
return l.woCompare( r, _order ) < 0; | return l.woCompare( r, _order ) < 0; | |||
} | } | |||
BSONObj order() const { return _order; } | BSONObj order() const { return _order; } | |||
private: | private: | |||
BSONObj _order; | BSONObj _order; | |||
}; | }; | |||
typedef set<BSONObj,BSONObjCmp> BSONObjSet; | typedef std::set<BSONObj,BSONObjCmp> BSONObjSet; | |||
enum FieldCompareResult { | enum FieldCompareResult { | |||
LEFT_SUBFIELD = -2, | LEFT_SUBFIELD = -2, | |||
LEFT_BEFORE = -1, | LEFT_BEFORE = -1, | |||
SAME = 0, | SAME = 0, | |||
RIGHT_BEFORE = 1 , | RIGHT_BEFORE = 1 , | |||
RIGHT_SUBFIELD = 2 | RIGHT_SUBFIELD = 2 | |||
}; | }; | |||
FieldCompareResult compareDottedFieldNames( const string& l , const str | class LexNumCmp; | |||
ing& r ); | FieldCompareResult compareDottedFieldNames( const std::string& l , cons | |||
t std::string& r , | ||||
const LexNumCmp& cmp ); | ||||
/** Use BSON macro to build a BSONObj from a stream | /** Use BSON macro to build a BSONObj from a stream | |||
e.g., | e.g., | |||
BSON( "name" << "joe" << "age" << 33 ) | BSON( "name" << "joe" << "age" << 33 ) | |||
with auto-generated object id: | with auto-generated object id: | |||
BSON( GENOID << "name" << "joe" << "age" << 33 ) | BSON( GENOID << "name" << "joe" << "age" << 33 ) | |||
The labels GT, GTE, LT, LTE, NE can be helpful for stream-oriented construction | The labels GT, GTE, LT, LTE, NE can be helpful for stream-oriented construction | |||
skipping to change at line 77 | skipping to change at line 79 | |||
/** Use BSON_ARRAY macro like BSON macro, but without keys | /** Use BSON_ARRAY macro like BSON macro, but without keys | |||
BSONArray arr = BSON_ARRAY( "hello" << 1 << BSON( "foo" << BSON_ARR AY( "bar" << "baz" << "qux" ) ) ); | BSONArray arr = BSON_ARRAY( "hello" << 1 << BSON( "foo" << BSON_ARR AY( "bar" << "baz" << "qux" ) ) ); | |||
*/ | */ | |||
#define BSON_ARRAY(x) (( mongo::BSONArrayBuilder() << x ).arr()) | #define BSON_ARRAY(x) (( mongo::BSONArrayBuilder() << x ).arr()) | |||
/* Utility class to auto assign object IDs. | /* Utility class to auto assign object IDs. | |||
Example: | Example: | |||
cout << BSON( GENOID << "z" << 3 ); // { _id : ..., z : 3 } | std::cout << BSON( GENOID << "z" << 3 ); // { _id : ..., z : 3 } | |||
*/ | */ | |||
extern struct GENOIDLabeler { } GENOID; | extern struct GENOIDLabeler { } GENOID; | |||
/* Utility class to add a Date element with the current time | /* Utility class to add a Date element with the current time | |||
Example: | Example: | |||
cout << BSON( "created" << DATENOW ); // { created : "2009-10-09 1 1:41:42" } | std::cout << BSON( "created" << DATENOW ); // { created : "2009-10 -09 11:41:42" } | |||
*/ | */ | |||
extern struct DateNowLabeler { } DATENOW; | extern struct DateNowLabeler { } DATENOW; | |||
/* Utility class to assign a NULL value to a given attribute | /* Utility class to assign a NULL value to a given attribute | |||
Example: | Example: | |||
cout << BSON( "a" << BSONNULL ); // { a : null } | std::cout << BSON( "a" << BSONNULL ); // { a : null } | |||
*/ | */ | |||
extern struct NullLabeler { } BSONNULL; | extern struct NullLabeler { } BSONNULL; | |||
/* Utility class to add the minKey (minus infinity) to a given attribut e | /* Utility class to add the minKey (minus infinity) to a given attribut e | |||
Example: | Example: | |||
cout << BSON( "a" << MINKEY ); // { "a" : { "$minKey" : 1 } } | std::cout << BSON( "a" << MINKEY ); // { "a" : { "$minKey" : 1 } } | |||
*/ | */ | |||
extern struct MinKeyLabeler { } MINKEY; | extern struct MinKeyLabeler { } MINKEY; | |||
extern struct MaxKeyLabeler { } MAXKEY; | extern struct MaxKeyLabeler { } MAXKEY; | |||
// Utility class to implement GT, GTE, etc as described above. | // Utility class to implement GT, GTE, etc as described above. | |||
class Labeler { | class Labeler { | |||
public: | public: | |||
struct Label { | struct Label { | |||
Label( const char *l ) : l_( l ) {} | Label( const char *l ) : l_( l ) {} | |||
const char *l_; | const char *l_; | |||
skipping to change at line 127 | skipping to change at line 129 | |||
private: | private: | |||
const Label &l_; | const Label &l_; | |||
BSONObjBuilderValueStream *s_; | BSONObjBuilderValueStream *s_; | |||
}; | }; | |||
extern Labeler::Label GT; | extern Labeler::Label GT; | |||
extern Labeler::Label GTE; | extern Labeler::Label GTE; | |||
extern Labeler::Label LT; | extern Labeler::Label LT; | |||
extern Labeler::Label LTE; | extern Labeler::Label LTE; | |||
extern Labeler::Label NE; | extern Labeler::Label NE; | |||
extern Labeler::Label SIZE; | extern Labeler::Label NIN; | |||
extern Labeler::Label BSIZE; | ||||
// $or helper: OR(BSON("x" << GT << 7), BSON("y" << LT << 6)); | // $or helper: OR(BSON("x" << GT << 7), BSON("y" << LT << 6)); | |||
// becomes : {$or: [{x: {$gt: 7}}, {y: {$lt: 6}}]} | // becomes : {$or: [{x: {$gt: 7}}, {y: {$lt: 6}}]} | |||
inline BSONObj OR(const BSONObj& a, const BSONObj& b); | inline BSONObj OR(const BSONObj& a, const BSONObj& b); | |||
inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c) ; | inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c) ; | |||
inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c, const BSONObj& d); | inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c, const BSONObj& d); | |||
inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c, const BSONObj& d, const BSONObj& e); | inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c, const BSONObj& d, const BSONObj& e); | |||
inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c, const BSONObj& d, const BSONObj& e, const BSONObj& f); | inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c, const BSONObj& d, const BSONObj& e, const BSONObj& f); | |||
// definitions in bsonobjbuilder.h b/c of incomplete types | // definitions in bsonobjbuilder.h b/c of incomplete types | |||
skipping to change at line 167 | skipping to change at line 170 | |||
void endField( const char *nextFieldName = 0 ); | void endField( const char *nextFieldName = 0 ); | |||
bool subobjStarted() const { return _fieldName != 0; } | bool subobjStarted() const { return _fieldName != 0; } | |||
private: | private: | |||
const char * _fieldName; | const char * _fieldName; | |||
BSONObjBuilder * _builder; | BSONObjBuilder * _builder; | |||
bool haveSubobj() const { return _subobj.get() != 0; } | bool haveSubobj() const { return _subobj.get() != 0; } | |||
BSONObjBuilder *subobj(); | BSONObjBuilder *subobj(); | |||
auto_ptr< BSONObjBuilder > _subobj; | std::auto_ptr< BSONObjBuilder > _subobj; | |||
}; | }; | |||
/** | /** | |||
used in conjuction with BSONObjBuilder, allows for proper buffer siz e to prevent crazy memory usage | used in conjuction with BSONObjBuilder, allows for proper buffer siz e to prevent crazy memory usage | |||
*/ | */ | |||
class BSONSizeTracker { | class BSONSizeTracker { | |||
public: | public: | |||
BSONSizeTracker() { | BSONSizeTracker() { | |||
_pos = 0; | _pos = 0; | |||
for ( int i=0; i<SIZE; i++ ) | for ( int i=0; i<SIZE; i++ ) | |||
End of changes. 8 change blocks. | ||||
9 lines changed or deleted | 12 lines changed or added | |||
bsonobj.h | bsonobj.h | |||
---|---|---|---|---|
skipping to change at line 31 | skipping to change at line 31 | |||
#include <boost/intrusive_ptr.hpp> | #include <boost/intrusive_ptr.hpp> | |||
#include <set> | #include <set> | |||
#include <list> | #include <list> | |||
#include <vector> | #include <vector> | |||
#include "util/atomic_int.h" | #include "util/atomic_int.h" | |||
#include "util/builder.h" | #include "util/builder.h" | |||
#include "stringdata.h" | #include "stringdata.h" | |||
namespace mongo { | namespace mongo { | |||
typedef set< BSONElement, BSONElementCmpWithoutField > BSONElementSet; | typedef std::set< BSONElement, BSONElementCmpWithoutField > BSONElement | |||
typedef multiset< BSONElement, BSONElementCmpWithoutField > BSONElement | Set; | |||
MSet; | typedef std::multiset< BSONElement, BSONElementCmpWithoutField > BSONEl | |||
ementMSet; | ||||
/** | /** | |||
C++ representation of a "BSON" object -- that is, an extended JSON-s tyle | C++ representation of a "BSON" object -- that is, an extended JSON-s tyle | |||
object in a binary representation. | object in a binary representation. | |||
See bsonspec.org. | See bsonspec.org. | |||
Note that BSONObj's have a smart pointer capability built in -- so y ou can | Note that BSONObj's have a smart pointer capability built in -- so y ou can | |||
pass them around by value. The reference counts used to implement t his | pass them around by value. The reference counts used to implement t his | |||
do not use locking, so copying and destroying BSONObj's are not thre ad-safe | do not use locking, so copying and destroying BSONObj's are not thre ad-safe | |||
skipping to change at line 141 | skipping to change at line 141 | |||
@see isOwned() | @see isOwned() | |||
*/ | */ | |||
BSONObj getOwned() const; | BSONObj getOwned() const; | |||
/** @return a new full (and owned) copy of the object. */ | /** @return a new full (and owned) copy of the object. */ | |||
BSONObj copy() const; | BSONObj copy() const; | |||
/** Readable representation of a BSON object in an extended JSON-st yle notation. | /** Readable representation of a BSON object in an extended JSON-st yle notation. | |||
This is an abbreviated representation which might be used for l ogging. | This is an abbreviated representation which might be used for l ogging. | |||
*/ | */ | |||
string toString( bool isArray = false, bool full=false ) const; | enum { maxToStringRecursionDepth = 100 }; | |||
void toString(StringBuilder& s, bool isArray = false, bool full=fal | ||||
se ) const; | std::string toString( bool isArray = false, bool full=false ) const | |||
; | ||||
void toString( StringBuilder& s, bool isArray = false, bool full=fa | ||||
lse, int depth=0 ) const; | ||||
/** Properly formatted JSON string. | /** Properly formatted JSON string. | |||
@param pretty if true we try to add some lf's and indentation | @param pretty if true we try to add some lf's and indentation | |||
*/ | */ | |||
string jsonString( JsonStringFormat format = Strict, int pretty = 0 ) const; | std::string jsonString( JsonStringFormat format = Strict, int prett y = 0 ) const; | |||
/** note: addFields always adds _id even if not specified */ | /** note: addFields always adds _id even if not specified */ | |||
int addFields(BSONObj& from, set<string>& fields); /* returns n add ed */ | int addFields(BSONObj& from, std::set<std::string>& fields); /* ret urns n added */ | |||
/** remove specified field and return a new object with the remaini ng fields. | /** remove specified field and return a new object with the remaini ng fields. | |||
slowish as builds a full new object | slowish as builds a full new object | |||
*/ | */ | |||
BSONObj removeField(const StringData& name) const; | BSONObj removeField(const StringData& name) const; | |||
/** returns # of top level fields in the object | /** returns # of top level fields in the object | |||
note: iterates to count the fields | note: iterates to count the fields | |||
*/ | */ | |||
int nFields() const; | int nFields() const; | |||
/** adds the field names to the fields set. does NOT clear it (app ends). */ | /** adds the field names to the fields set. does NOT clear it (app ends). */ | |||
int getFieldNames(set<string>& fields) const; | int getFieldNames(std::set<std::string>& fields) const; | |||
/** @return the specified element. element.eoo() will be true if n ot found. | /** @return the specified element. element.eoo() will be true if n ot found. | |||
@param name field to find. supports dot (".") notation to reach into embedded objects. | @param name field to find. supports dot (".") notation to reach into embedded objects. | |||
for example "x.y" means "in the nested object in field x, retr ieve field y" | for example "x.y" means "in the nested object in field x, retr ieve field y" | |||
*/ | */ | |||
BSONElement getFieldDotted(const char *name) const; | BSONElement getFieldDotted(const char *name) const; | |||
/** @return the specified element. element.eoo() will be true if n ot found. | /** @return the specified element. element.eoo() will be true if n ot found. | |||
@param name field to find. supports dot (".") notation to reach into embedded objects. | @param name field to find. supports dot (".") notation to reach into embedded objects. | |||
for example "x.y" means "in the nested object in field x, retr ieve field y" | for example "x.y" means "in the nested object in field x, retr ieve field y" | |||
*/ | */ | |||
BSONElement getFieldDotted(const string& name) const { | BSONElement getFieldDotted(const std::string& name) const { | |||
return getFieldDotted( name.c_str() ); | return getFieldDotted( name.c_str() ); | |||
} | } | |||
/** Like getFieldDotted(), but expands arrays and returns all match ing objects. | /** Like getFieldDotted(), but expands arrays and returns all match ing objects. | |||
* Turning off expandLastArray allows you to retrieve nested array objects instead of | * Turning off expandLastArray allows you to retrieve nested array objects instead of | |||
* their contents. | * their contents. | |||
*/ | */ | |||
void getFieldsDotted(const StringData& name, BSONElementSet &ret, b ool expandLastArray = true ) const; | void getFieldsDotted(const StringData& name, BSONElementSet &ret, b ool expandLastArray = true ) const; | |||
void getFieldsDotted(const StringData& name, BSONElementMSet &ret, bool expandLastArray = true ) const; | void getFieldsDotted(const StringData& name, BSONElementMSet &ret, bool expandLastArray = true ) const; | |||
skipping to change at line 210 | skipping to change at line 212 | |||
*/ | */ | |||
void getFields(unsigned n, const char **fieldNames, BSONElement *fi elds) const; | void getFields(unsigned n, const char **fieldNames, BSONElement *fi elds) const; | |||
/** Get the field of the specified name. eoo() is true on the retur ned | /** Get the field of the specified name. eoo() is true on the retur ned | |||
element if not found. | element if not found. | |||
*/ | */ | |||
BSONElement operator[] (const char *field) const { | BSONElement operator[] (const char *field) const { | |||
return getField(field); | return getField(field); | |||
} | } | |||
BSONElement operator[] (const string& field) const { | BSONElement operator[] (const std::string& field) const { | |||
return getField(field); | return getField(field); | |||
} | } | |||
BSONElement operator[] (int field) const { | BSONElement operator[] (int field) const { | |||
StringBuilder ss; | StringBuilder ss; | |||
ss << field; | ss << field; | |||
string s = ss.str(); | std::string s = ss.str(); | |||
return getField(s.c_str()); | return getField(s.c_str()); | |||
} | } | |||
/** @return true if field exists */ | /** @return true if field exists */ | |||
bool hasField( const char * name ) const { return !getField(name).e oo(); } | bool hasField( const char * name ) const { return !getField(name).e oo(); } | |||
/** @return true if field exists */ | /** @return true if field exists */ | |||
bool hasElement(const char *name) const { return hasField(name); } | bool hasElement(const char *name) const { return hasField(name); } | |||
/** @return "" if DNE or wrong type */ | /** @return "" if DNE or wrong type */ | |||
const char * getStringField(const char *name) const; | const char * getStringField(const char *name) const; | |||
skipping to change at line 284 | skipping to change at line 286 | |||
criter: isValid() no . or $ field names | criter: isValid() no . or $ field names | |||
*/ | */ | |||
bool okForStorage() const; | bool okForStorage() const; | |||
/** @return true if object is empty -- i.e., {} */ | /** @return true if object is empty -- i.e., {} */ | |||
bool isEmpty() const { return objsize() <= 5; } | bool isEmpty() const { return objsize() <= 5; } | |||
void dump() const; | void dump() const; | |||
/** Alternative output format */ | /** Alternative output format */ | |||
string hexDump() const; | std::string hexDump() const; | |||
/**wo='well ordered'. fields must be in same order in each object. | /**wo='well ordered'. fields must be in same order in each object. | |||
Ordering is with respect to the signs of the elements | Ordering is with respect to the signs of the elements | |||
and allows ascending / descending key mixing. | and allows ascending / descending key mixing. | |||
@return <0 if l<r. 0 if l==r. >0 if l>r | @return <0 if l<r. 0 if l==r. >0 if l>r | |||
*/ | */ | |||
int woCompare(const BSONObj& r, const Ordering &o, | int woCompare(const BSONObj& r, const Ordering &o, | |||
bool considerFieldName=true) const; | bool considerFieldName=true) const; | |||
/**wo='well ordered'. fields must be in same order in each object. | /**wo='well ordered'. fields must be in same order in each object. | |||
skipping to change at line 371 | skipping to change at line 373 | |||
BSONObj clientReadable() const; | BSONObj clientReadable() const; | |||
/** 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; | std::string md5() const; | |||
bool operator==( const BSONObj& other ) const { return equal( other ); } | bool operator==( const BSONObj& other ) const { return equal( other ); } | |||
bool operator!=(const BSONObj& other) const { return !operator==( o ther); } | bool operator!=(const BSONObj& other) const { return !operator==( o ther); } | |||
enum MatchType { | enum MatchType { | |||
Equality = 0, | Equality = 0, | |||
LT = 0x1, | LT = 0x1, | |||
LTE = 0x3, | LTE = 0x3, | |||
GTE = 0x6, | GTE = 0x6, | |||
GT = 0x4, | GT = 0x4, | |||
skipping to change at line 399 | skipping to change at line 401 | |||
opTYPE = 0x0F, | opTYPE = 0x0F, | |||
opREGEX = 0x10, | opREGEX = 0x10, | |||
opOPTIONS = 0x11, | opOPTIONS = 0x11, | |||
opELEM_MATCH = 0x12, | opELEM_MATCH = 0x12, | |||
opNEAR = 0x13, | opNEAR = 0x13, | |||
opWITHIN = 0x14, | opWITHIN = 0x14, | |||
opMAX_DISTANCE=0x15 | opMAX_DISTANCE=0x15 | |||
}; | }; | |||
/** add all elements of the object to the specified vector */ | /** add all elements of the object to the specified vector */ | |||
void elems(vector<BSONElement> &) const; | void elems(std::vector<BSONElement> &) const; | |||
/** add all elements of the object to the specified list */ | /** add all elements of the object to the specified list */ | |||
void elems(list<BSONElement> &) const; | void elems(std::list<BSONElement> &) const; | |||
/** add all values of the object to the specified vector. If type mismatches, exception. | /** add all values of the object to the specified vector. If type mismatches, exception. | |||
this is most useful when the BSONObj is an array, but can be us ed with non-arrays too in theory. | this is most useful when the BSONObj is an array, but can be us ed with non-arrays too in theory. | |||
example: | example: | |||
bo sub = y["subobj"].Obj(); | bo sub = y["subobj"].Obj(); | |||
vector<int> myints; | std::vector<int> myints; | |||
sub.Vals(myints); | sub.Vals(myints); | |||
*/ | */ | |||
template <class T> | template <class T> | |||
void Vals(vector<T> &) const; | void Vals(std::vector<T> &) const; | |||
/** add all values of the object to the specified list. If type mi smatches, exception. */ | /** add all values of the object to the specified list. If type mi smatches, exception. */ | |||
template <class T> | template <class T> | |||
void Vals(list<T> &) const; | void Vals(std::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(std::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(std::list<T> &) const; | |||
friend class BSONObjIterator; | friend class BSONObjIterator; | |||
typedef BSONObjIterator iterator; | typedef BSONObjIterator iterator; | |||
/** use something like this: | /** use something like this: | |||
for( BSONObj::iterator i = myObj.begin(); i.more(); ) { | for( BSONObj::iterator i = myObj.begin(); i.more(); ) { | |||
BSONElement e = i.next(); | BSONElement e = i.next(); | |||
... | ... | |||
} | } | |||
*/ | */ | |||
BSONObjIterator begin() const; | BSONObjIterator begin() const; | |||
void appendSelfToBufBuilder(BufBuilder& b) const { | void appendSelfToBufBuilder(BufBuilder& b) const { | |||
assert( objsize() ); | verify( objsize() ); | |||
b.appendBuf(reinterpret_cast<const void *>( objdata() ), objsiz e()); | b.appendBuf(reinterpret_cast<const void *>( objdata() ), objsiz e()); | |||
} | } | |||
#pragma pack(1) | #pragma pack(1) | |||
class Holder : boost::noncopyable { | class Holder : boost::noncopyable { | |||
private: | private: | |||
Holder(); // this class should never be explicitly created | Holder(); // this class should never be explicitly created | |||
AtomicUInt refCount; | AtomicUInt refCount; | |||
public: | public: | |||
char data[4]; // start of object | char data[4]; // start of object | |||
void zero() { refCount.zero(); } | void zero() { refCount.zero(); } | |||
// these are called automatically by boost::intrusive_ptr | // these are called automatically by boost::intrusive_ptr | |||
friend void intrusive_ptr_add_ref(Holder* h) { h->refCount++; } | friend void intrusive_ptr_add_ref(Holder* h) { h->refCount++; } | |||
friend void intrusive_ptr_release(Holder* h) { | friend void intrusive_ptr_release(Holder* h) { | |||
#if defined(_DEBUG) // cant use dassert or DEV here | #if defined(_DEBUG) // cant use dassert or DEV here | |||
assert((int)h->refCount > 0); // make sure we haven't alrea dy freed the buffer | verify((int)h->refCount > 0); // make sure we haven't alrea dy freed the buffer | |||
#endif | #endif | |||
if(--(h->refCount) == 0){ | if(--(h->refCount) == 0){ | |||
#if defined(_DEBUG) | #if defined(_DEBUG) | |||
unsigned sz = (unsigned&) *h->data; | unsigned sz = (unsigned&) *h->data; | |||
assert(sz < BSONObjMaxInternalSize * 3); | verify(sz < BSONObjMaxInternalSize * 3); | |||
memset(h->data, 0xdd, sz); | memset(h->data, 0xdd, sz); | |||
#endif | #endif | |||
free(h); | free(h); | |||
} | } | |||
} | } | |||
}; | }; | |||
#pragma pack() | #pragma pack() | |||
BSONObj(const BSONObj &rO): | ||||
_objdata(rO._objdata), _holder(rO._holder) { | ||||
} | ||||
BSONObj &operator=(const BSONObj &rRHS) { | ||||
if (this != &rRHS) { | ||||
_objdata = rRHS._objdata; | ||||
_holder = rRHS._holder; | ||||
} | ||||
return *this; | ||||
} | ||||
private: | private: | |||
const char *_objdata; | const char *_objdata; | |||
boost::intrusive_ptr< Holder > _holder; | boost::intrusive_ptr< Holder > _holder; | |||
void _assertInvalid() const; | void _assertInvalid() const; | |||
void init(Holder *holder) { | void init(Holder *holder) { | |||
_holder = holder; // holder is now managed by intrusive_ptr | _holder = holder; // holder is now managed by intrusive_ptr | |||
init(holder->data); | init(holder->data); | |||
} | } | |||
void init(const char *data) { | void init(const char *data) { | |||
_objdata = data; | _objdata = data; | |||
if ( !isValid() ) | if ( !isValid() ) | |||
_assertInvalid(); | _assertInvalid(); | |||
} | } | |||
}; | }; | |||
ostream& operator<<( ostream &s, const BSONObj &o ); | std::ostream& operator<<( std::ostream &s, const BSONObj &o ); | |||
ostream& operator<<( ostream &s, const BSONElement &e ); | std::ostream& operator<<( std::ostream &s, const BSONElement &e ); | |||
StringBuilder& operator<<( StringBuilder &s, const BSONObj &o ); | StringBuilder& operator<<( StringBuilder &s, const BSONObj &o ); | |||
StringBuilder& operator<<( StringBuilder &s, const BSONElement &e ); | StringBuilder& operator<<( StringBuilder &s, const BSONElement &e ); | |||
struct BSONArray : BSONObj { | struct BSONArray : BSONObj { | |||
// Don't add anything other than forwarding constructors!!! | // Don't add anything other than forwarding constructors!!! | |||
BSONArray(): BSONObj() {} | BSONArray(): BSONObj() {} | |||
explicit BSONArray(const BSONObj& obj): BSONObj(obj) {} | explicit BSONArray(const BSONObj& obj): BSONObj(obj) {} | |||
}; | }; | |||
End of changes. 22 change blocks. | ||||
26 lines changed or deleted | 42 lines changed or added | |||
bsonobjbuilder.h | bsonobjbuilder.h | |||
---|---|---|---|---|
skipping to change at line 29 | skipping to change at line 29 | |||
* See the License for the specific language governing permissions and | * See the License for the specific language governing permissions and | |||
* limitations under the License. | * limitations under the License. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include <limits> | #include <limits> | |||
#include <cmath> | #include <cmath> | |||
#include <boost/static_assert.hpp> | #include <boost/static_assert.hpp> | |||
#if defined(MONGO_EXPOSE_MACROS) | #if defined(MONGO_EXPOSE_MACROS) | |||
// boost changed it | #define verify MONGO_verify | |||
#undef assert | ||||
#define assert MONGO_assert | ||||
#endif | #endif | |||
#include "bsonelement.h" | #include "bsonelement.h" | |||
#include "bsonobj.h" | #include "bsonobj.h" | |||
#include "bsonmisc.h" | #include "bsonmisc.h" | |||
#if defined(_DEBUG) && defined(MONGO_EXPOSE_MACROS) | #if defined(_DEBUG) && defined(MONGO_EXPOSE_MACROS) | |||
#include "../util/log.h" | #include "../util/log.h" | |||
#endif | #endif | |||
namespace mongo { | namespace mongo { | |||
using namespace std; | ||||
#if defined(_WIN32) | #if defined(_WIN32) | |||
// warning: 'this' : used in base member initializer list | // warning: 'this' : used in base member initializer list | |||
#pragma warning( disable : 4355 ) | #pragma warning( disable : 4355 ) | |||
#endif | #endif | |||
template<typename T> | template<typename T> | |||
class BSONFieldValue { | class BSONFieldValue { | |||
public: | public: | |||
BSONFieldValue( const string& name , const T& t ) { | BSONFieldValue( const std::string& name , const T& t ) { | |||
_name = name; | _name = name; | |||
_t = t; | _t = t; | |||
} | } | |||
const T& value() const { return _t; } | const T& value() const { return _t; } | |||
const string& name() const { return _name; } | const std::string& name() const { return _name; } | |||
private: | private: | |||
string _name; | std::string _name; | |||
T _t; | T _t; | |||
}; | }; | |||
template<typename T> | template<typename T> | |||
class BSONField { | class BSONField { | |||
public: | public: | |||
BSONField( const string& name , const string& longName="" ) | BSONField( const std::string& name , const std::string& longName="" ) | |||
: _name(name), _longName(longName) {} | : _name(name), _longName(longName) {} | |||
const string& name() const { return _name; } | const std::string& name() const { return _name; } | |||
operator string() const { return _name; } | operator std::string() const { return _name; } | |||
BSONFieldValue<T> make( const T& t ) const { | BSONFieldValue<T> make( const T& t ) const { | |||
return BSONFieldValue<T>( _name , t ); | return BSONFieldValue<T>( _name , t ); | |||
} | } | |||
BSONFieldValue<BSONObj> gt( const T& t ) const { return query( "$gt " , t ); } | BSONFieldValue<BSONObj> gt( const T& t ) const { return query( "$gt " , t ); } | |||
BSONFieldValue<BSONObj> lt( const T& t ) const { return query( "$lt " , t ); } | BSONFieldValue<BSONObj> lt( const T& t ) const { return query( "$lt " , t ); } | |||
BSONFieldValue<BSONObj> query( const char * q , const T& t ) const; | BSONFieldValue<BSONObj> query( const char * q , const T& t ) const; | |||
BSONFieldValue<T> operator()( const T& t ) const { | BSONFieldValue<T> operator()( const T& t ) const { | |||
return BSONFieldValue<T>( _name , t ); | return BSONFieldValue<T>( _name , t ); | |||
} | } | |||
private: | private: | |||
string _name; | std::string _name; | |||
string _longName; | std::string _longName; | |||
}; | }; | |||
/** Utility for creating a BSONObj. | /** Utility for creating a BSONObj. | |||
See also the BSON() and BSON_ARRAY() macros. | See also the BSON() and BSON_ARRAY() macros. | |||
*/ | */ | |||
class BSONObjBuilder : boost::noncopyable { | class BSONObjBuilder : boost::noncopyable { | |||
public: | public: | |||
/** @param initsize this is just a hint as to the final size of the object */ | /** @param initsize this is just a hint as to the final size of the object */ | |||
BSONObjBuilder(int initsize=512) : _b(_buf), _buf(initsize + sizeof (unsigned)), _offset( sizeof(unsigned) ), _s( this ) , _tracker(0) , _doneC alled(false) { | BSONObjBuilder(int initsize=512) : _b(_buf), _buf(initsize + sizeof (unsigned)), _offset( sizeof(unsigned) ), _s( this ) , _tracker(0) , _doneC alled(false) { | |||
_b.appendNum((unsigned)0); // ref-count | _b.appendNum((unsigned)0); // ref-count | |||
skipping to change at line 128 | skipping to change at line 124 | |||
} | } | |||
/** add all the fields from the object specified to this object */ | /** add all the fields from the object specified to this object */ | |||
BSONObjBuilder& appendElements(BSONObj x); | BSONObjBuilder& appendElements(BSONObj x); | |||
/** add all the fields from the object specified to this object if they don't exist already */ | /** add all the fields from the object specified to this object if they don't exist already */ | |||
BSONObjBuilder& appendElementsUnique( BSONObj x ); | BSONObjBuilder& appendElementsUnique( BSONObj x ); | |||
/** append element to the object we are building */ | /** append element to the object we are building */ | |||
BSONObjBuilder& append( const BSONElement& e) { | BSONObjBuilder& append( const BSONElement& e) { | |||
assert( !e.eoo() ); // do not append eoo, that would corrupt us . the builder auto appends when done() is called. | verify( !e.eoo() ); // do not append eoo, that would corrupt us . the builder auto appends when done() is called. | |||
_b.appendBuf((void*) e.rawdata(), e.size()); | _b.appendBuf((void*) e.rawdata(), e.size()); | |||
return *this; | return *this; | |||
} | } | |||
/** append an element but with a new name */ | /** append an element but with a new name */ | |||
BSONObjBuilder& appendAs(const BSONElement& e, const StringData& fi eldName) { | BSONObjBuilder& appendAs(const BSONElement& e, const StringData& fi eldName) { | |||
assert( !e.eoo() ); // do not append eoo, that would corrupt us . the builder auto appends when done() is called. | verify( !e.eoo() ); // do not append eoo, that would corrupt us . the builder auto appends when done() is called. | |||
_b.appendNum((char) e.type()); | _b.appendNum((char) e.type()); | |||
_b.appendStr(fieldName); | _b.appendStr(fieldName); | |||
_b.appendBuf((void *) e.value(), e.valuesize()); | _b.appendBuf((void *) e.value(), e.valuesize()); | |||
return *this; | return *this; | |||
} | } | |||
/** add a subobject as a member */ | /** add a subobject as a member */ | |||
BSONObjBuilder& append(const StringData& fieldName, BSONObj subObj) { | BSONObjBuilder& append(const StringData& fieldName, BSONObj subObj) { | |||
_b.appendNum((char) Object); | _b.appendNum((char) Object); | |||
_b.appendStr(fieldName); | _b.appendStr(fieldName); | |||
_b.appendBuf((void *) subObj.objdata(), subObj.objsize()); | _b.appendBuf((void *) subObj.objdata(), subObj.objsize()); | |||
return *this; | return *this; | |||
} | } | |||
/** add a subobject as a member */ | /** add a subobject as a member */ | |||
BSONObjBuilder& appendObject(const StringData& fieldName, const cha r * objdata , int size = 0 ) { | BSONObjBuilder& appendObject(const StringData& fieldName, const cha r * objdata , int size = 0 ) { | |||
assert( objdata ); | verify( objdata ); | |||
if ( size == 0 ) { | if ( size == 0 ) { | |||
size = *((int*)objdata); | size = *((int*)objdata); | |||
} | } | |||
assert( size > 4 && size < 100000000 ); | verify( size > 4 && size < 100000000 ); | |||
_b.appendNum((char) Object); | _b.appendNum((char) Object); | |||
_b.appendStr(fieldName); | _b.appendStr(fieldName); | |||
_b.appendBuf((void*)objdata, size ); | _b.appendBuf((void*)objdata, size ); | |||
return *this; | return *this; | |||
} | } | |||
/** add header for a new subobject and return bufbuilder for writin g to | /** add header for a new subobject and return bufbuilder for writin g to | |||
* the subobject's body | * the subobject's body | |||
* | * | |||
skipping to change at line 245 | skipping to change at line 241 | |||
_b.appendStr(fieldName); | _b.appendStr(fieldName); | |||
_b.appendNum(n); | _b.appendNum(n); | |||
return *this; | return *this; | |||
} | } | |||
/** appends a number. if n < max(int)/2 then uses int, otherwise l ong long */ | /** appends a number. if n < max(int)/2 then uses int, otherwise l ong long */ | |||
BSONObjBuilder& appendIntOrLL( const StringData& fieldName , long l ong n ) { | BSONObjBuilder& appendIntOrLL( const StringData& fieldName , long l ong n ) { | |||
long long x = n; | long long x = n; | |||
if ( x < 0 ) | if ( x < 0 ) | |||
x = x * -1; | x = x * -1; | |||
if ( x < ( (numeric_limits<int>::max)() / 2 ) ) // extra () to avoid max macro on windows | if ( x < ( (std::numeric_limits<int>::max)() / 2 ) ) // extra ( ) to avoid max macro on windows | |||
append( fieldName , (int)n ); | append( fieldName , (int)n ); | |||
else | else | |||
append( fieldName , n ); | append( fieldName , n ); | |||
return *this; | return *this; | |||
} | } | |||
/** | /** | |||
* appendNumber is a series of method for appending the smallest se nsible type | * appendNumber is a series of method for appending the smallest se nsible type | |||
* mostly for JS | * mostly for JS | |||
*/ | */ | |||
skipping to change at line 298 | skipping to change at line 294 | |||
BSONObjBuilder& append(const StringData& fieldName, double n) { | BSONObjBuilder& append(const StringData& fieldName, double n) { | |||
_b.appendNum((char) NumberDouble); | _b.appendNum((char) NumberDouble); | |||
_b.appendStr(fieldName); | _b.appendStr(fieldName); | |||
_b.appendNum(n); | _b.appendNum(n); | |||
return *this; | return *this; | |||
} | } | |||
/** tries to append the data as a number | /** tries to append the data as a number | |||
* @return true if the data was able to be converted to a number | * @return true if the data was able to be converted to a number | |||
*/ | */ | |||
bool appendAsNumber( const StringData& fieldName , const string& da ta ); | bool appendAsNumber( const StringData& fieldName , const std::strin g& data ); | |||
/** Append a BSON Object ID (OID type). | /** Append a BSON Object ID (OID type). | |||
@deprecated Generally, it is preferred to use the append append (name, oid) | @deprecated Generally, it is preferred to use the append append (name, oid) | |||
method for this. | method for this. | |||
*/ | */ | |||
BSONObjBuilder& appendOID(const StringData& fieldName, OID *oid = 0 , bool generateIfBlank = false ) { | BSONObjBuilder& appendOID(const StringData& fieldName, OID *oid = 0 , bool generateIfBlank = false ) { | |||
_b.appendNum((char) jstOID); | _b.appendNum((char) jstOID); | |||
_b.appendStr(fieldName); | _b.appendStr(fieldName); | |||
if ( oid ) | if ( oid ) | |||
_b.appendBuf( (void *) oid, 12 ); | _b.appendBuf( (void *) oid, 12 ); | |||
skipping to change at line 360 | skipping to change at line 356 | |||
/** Append a date. | /** Append a date. | |||
@param dt a Java-style 64 bit date value, that is | @param dt a Java-style 64 bit date value, that is | |||
the number of milliseconds since January 1, 1970, 00:00:00 GMT | the number of milliseconds since January 1, 1970, 00:00:00 GMT | |||
*/ | */ | |||
BSONObjBuilder& appendDate(const StringData& fieldName, Date_t dt) { | BSONObjBuilder& appendDate(const StringData& fieldName, Date_t dt) { | |||
/* easy to pass a time_t to this and get a bad result. thus th is warning. */ | /* easy to pass a time_t to this and get a bad result. thus th is warning. */ | |||
#if defined(_DEBUG) && defined(MONGO_EXPOSE_MACROS) | #if defined(_DEBUG) && defined(MONGO_EXPOSE_MACROS) | |||
if( dt > 0 && dt <= 0xffffffff ) { | if( dt > 0 && dt <= 0xffffffff ) { | |||
static int n; | static int n; | |||
if( n++ == 0 ) | if( n++ == 0 ) | |||
log() << "DEV WARNING appendDate() called with a tiny ( but nonzero) date" << endl; | log() << "DEV WARNING appendDate() called with a tiny ( but nonzero) date" << std::endl; | |||
} | } | |||
#endif | #endif | |||
_b.appendNum((char) Date); | _b.appendNum((char) Date); | |||
_b.appendStr(fieldName); | _b.appendStr(fieldName); | |||
_b.appendNum(dt); | _b.appendNum(dt); | |||
return *this; | return *this; | |||
} | } | |||
BSONObjBuilder& append(const StringData& fieldName, Date_t dt) { | BSONObjBuilder& append(const StringData& fieldName, Date_t dt) { | |||
return appendDate(fieldName, dt); | return appendDate(fieldName, dt); | |||
} | } | |||
skipping to change at line 406 | skipping to change at line 402 | |||
_b.appendStr(fieldName); | _b.appendStr(fieldName); | |||
_b.appendNum((int)sz); | _b.appendNum((int)sz); | |||
_b.appendBuf(str, sz); | _b.appendBuf(str, sz); | |||
return *this; | return *this; | |||
} | } | |||
/** Append a string element */ | /** Append a string element */ | |||
BSONObjBuilder& append(const StringData& fieldName, const char *str ) { | BSONObjBuilder& append(const StringData& fieldName, const char *str ) { | |||
return append(fieldName, str, (int) strlen(str)+1); | return append(fieldName, str, (int) strlen(str)+1); | |||
} | } | |||
/** Append a string element */ | /** Append a string element */ | |||
BSONObjBuilder& append(const StringData& fieldName, const string& s tr) { | BSONObjBuilder& append(const StringData& fieldName, const std::stri ng& str) { | |||
return append(fieldName, str.c_str(), (int) str.size()+1); | return append(fieldName, str.c_str(), (int) str.size()+1); | |||
} | } | |||
BSONObjBuilder& appendSymbol(const StringData& fieldName, const Str ingData& symbol) { | BSONObjBuilder& appendSymbol(const StringData& fieldName, const Str ingData& symbol) { | |||
_b.appendNum((char) Symbol); | _b.appendNum((char) Symbol); | |||
_b.appendStr(fieldName); | _b.appendStr(fieldName); | |||
_b.appendNum((int) symbol.size()+1); | _b.appendNum((int) symbol.size()+1); | |||
_b.appendStr(symbol); | _b.appendStr(symbol); | |||
return *this; | return *this; | |||
} | } | |||
skipping to change at line 536 | skipping to change at line 532 | |||
} | } | |||
/** | /** | |||
these are the min/max when comparing, not strict min/max element s for a given type | these are the min/max when comparing, not strict min/max element s for a given type | |||
*/ | */ | |||
void appendMinForType( const StringData& fieldName , int type ); | void appendMinForType( const StringData& fieldName , int type ); | |||
void appendMaxForType( const StringData& fieldName , int type ); | void appendMaxForType( const StringData& fieldName , int type ); | |||
/** Append an array of values. */ | /** Append an array of values. */ | |||
template < class T > | template < class T > | |||
BSONObjBuilder& append( const StringData& fieldName, const vector< T >& vals ); | BSONObjBuilder& append( const StringData& fieldName, const std::vec tor< T >& vals ); | |||
template < class T > | template < class T > | |||
BSONObjBuilder& append( const StringData& fieldName, const list< T >& vals ); | BSONObjBuilder& append( const StringData& fieldName, const std::lis t< T >& vals ); | |||
/** Append a set of values. */ | /** Append a set of values. */ | |||
template < class T > | template < class T > | |||
BSONObjBuilder& append( const StringData& fieldName, const set< T > & vals ); | BSONObjBuilder& append( const StringData& fieldName, const std::set < T >& vals ); | |||
/** | /** | |||
* destructive | * destructive | |||
* The returned BSONObj will free the buffer when it is finished. | * The returned BSONObj will free the buffer when it is finished. | |||
* @return owned BSONObj | * @return owned BSONObj | |||
*/ | */ | |||
BSONObj obj() { | BSONObj obj() { | |||
bool own = owned(); | bool own = owned(); | |||
massert( 10335 , "builder does not own memory", own ); | massert( 10335 , "builder does not own memory", own ); | |||
doneFast(); | doneFast(); | |||
skipping to change at line 587 | skipping to change at line 583 | |||
BSONObj asTempObj() { | BSONObj asTempObj() { | |||
BSONObj temp(_done()); | BSONObj temp(_done()); | |||
_b.setlen(_b.len()-1); //next append should overwrite the EOO | _b.setlen(_b.len()-1); //next append should overwrite the EOO | |||
_doneCalled = false; | _doneCalled = false; | |||
return temp; | return temp; | |||
} | } | |||
/* assume ownership of the buffer - you must then free it (with fre e()) */ | /* assume ownership of the buffer - you must then free it (with fre e()) */ | |||
char* decouple(int& l) { | char* decouple(int& l) { | |||
char *x = _done(); | char *x = _done(); | |||
assert( x ); | verify( x ); | |||
l = _b.len(); | l = _b.len(); | |||
_b.decouple(); | _b.decouple(); | |||
return x; | return x; | |||
} | } | |||
void decouple() { | void decouple() { | |||
_b.decouple(); // post done() call version. be sure jsobj f rees... | _b.decouple(); // post done() call version. be sure jsobj f rees... | |||
} | } | |||
void appendKeys( const BSONObj& keyPattern , const BSONObj& values ); | void appendKeys( const BSONObj& keyPattern , const BSONObj& values ); | |||
static string numStr( int i ) { | static std::string numStr( int i ) { | |||
if (i>=0 && i<100 && numStrsReady) | if (i>=0 && i<100 && numStrsReady) | |||
return numStrs[i]; | return numStrs[i]; | |||
StringBuilder o; | StringBuilder o; | |||
o << i; | o << i; | |||
return o.str(); | return o.str(); | |||
} | } | |||
/** Stream oriented way to add field names and values. */ | /** Stream oriented way to add field names and values. */ | |||
BSONObjBuilderValueStream &operator<<(const char * name ) { | BSONObjBuilderValueStream &operator<<(const char * name ) { | |||
_s.endField( name ); | _s.endField( name ); | |||
return _s; | return _s; | |||
} | } | |||
/** Stream oriented way to add field names and values. */ | /** Stream oriented way to add field names and values. */ | |||
BSONObjBuilder& operator<<( GENOIDLabeler ) { return genOID(); } | BSONObjBuilder& operator<<( GENOIDLabeler ) { return genOID(); } | |||
// prevent implicit string conversions which would allow bad things like BSON( BSON( "foo" << 1 ) << 2 ) | // prevent implicit string conversions which would allow bad things like BSON( BSON( "foo" << 1 ) << 2 ) | |||
struct ForceExplicitString { | struct ForceExplicitString { | |||
ForceExplicitString( const string &str ) : str_( str ) {} | ForceExplicitString( const std::string &str ) : str_( str ) {} | |||
string str_; | std::string str_; | |||
}; | }; | |||
/** Stream oriented way to add field names and values. */ | /** Stream oriented way to add field names and values. */ | |||
BSONObjBuilderValueStream &operator<<( const ForceExplicitString& n ame ) { | BSONObjBuilderValueStream &operator<<( const ForceExplicitString& n ame ) { | |||
return operator<<( name.str_.c_str() ); | return operator<<( name.str_.c_str() ); | |||
} | } | |||
Labeler operator<<( const Labeler::Label &l ) { | Labeler operator<<( const Labeler::Label &l ) { | |||
massert( 10336 , "No subobject started", _s.subobjStarted() ); | massert( 10336 , "No subobject started", _s.subobjStarted() ); | |||
return _s << l; | return _s << l; | |||
skipping to change at line 682 | skipping to change at line 678 | |||
return data; | return data; | |||
} | } | |||
BufBuilder &_b; | BufBuilder &_b; | |||
BufBuilder _buf; | BufBuilder _buf; | |||
int _offset; | int _offset; | |||
BSONObjBuilderValueStream _s; | BSONObjBuilderValueStream _s; | |||
BSONSizeTracker * _tracker; | BSONSizeTracker * _tracker; | |||
bool _doneCalled; | bool _doneCalled; | |||
static const string numStrs[100]; // cache of 0 to 99 inclusive | static const std::string numStrs[100]; // cache of 0 to 99 inclusiv e | |||
static bool numStrsReady; // for static init safety. see comments i n db/jsobj.cpp | static bool numStrsReady; // for static init safety. see comments i n db/jsobj.cpp | |||
}; | }; | |||
class BSONArrayBuilder : boost::noncopyable { | class BSONArrayBuilder : boost::noncopyable { | |||
public: | public: | |||
BSONArrayBuilder() : _i(0), _b() {} | BSONArrayBuilder() : _i(0), _b() {} | |||
BSONArrayBuilder( BufBuilder &_b ) : _i(0), _b(_b) {} | BSONArrayBuilder( BufBuilder &_b ) : _i(0), _b(_b) {} | |||
BSONArrayBuilder( int initialSize ) : _i(0), _b(initialSize) {} | BSONArrayBuilder( int initialSize ) : _i(0), _b(initialSize) {} | |||
template <typename T> | template <typename T> | |||
skipping to change at line 729 | skipping to change at line 725 | |||
void doneFast() { _b.doneFast(); } | void doneFast() { _b.doneFast(); } | |||
template <typename T> | template <typename T> | |||
BSONArrayBuilder& append(const StringData& name, const T& x) { | BSONArrayBuilder& append(const StringData& name, const T& x) { | |||
fill( name ); | fill( name ); | |||
append( x ); | append( x ); | |||
return *this; | return *this; | |||
} | } | |||
template < class T > | ||||
BSONArrayBuilder& append( const std::list< T >& vals ); | ||||
template < class T > | ||||
BSONArrayBuilder& append( const std::set< T >& vals ); | ||||
// These two just use next position | // These two just use next position | |||
BufBuilder &subobjStart() { return _b.subobjStart( num() ); } | BufBuilder &subobjStart() { return _b.subobjStart( num() ); } | |||
BufBuilder &subarrayStart() { return _b.subarrayStart( num() ); } | BufBuilder &subarrayStart() { return _b.subarrayStart( num() ); } | |||
// These fill missing entries up to pos. if pos is < next pos is ig nored | // These fill missing entries up to pos. if pos is < next pos is ig nored | |||
BufBuilder &subobjStart(int pos) { | BufBuilder &subobjStart(int pos) { | |||
fill(pos); | fill(pos); | |||
return _b.subobjStart( num() ); | return _b.subobjStart( num() ); | |||
} | } | |||
BufBuilder &subarrayStart(int pos) { | BufBuilder &subarrayStart(int pos) { | |||
skipping to change at line 778 | skipping to change at line 780 | |||
private: | private: | |||
// These two are undefined privates to prevent their accidental | // These two are undefined privates to prevent their accidental | |||
// use as we don't support unsigned ints in BSON | // use as we don't support unsigned ints in BSON | |||
BSONObjBuilder& append(const StringData& fieldName, unsigned int va l); | BSONObjBuilder& append(const StringData& fieldName, unsigned int va l); | |||
BSONObjBuilder& append(const StringData& fieldName, unsigned long l ong val); | BSONObjBuilder& append(const StringData& fieldName, unsigned long l ong val); | |||
void fill( const StringData& name ) { | void fill( const StringData& name ) { | |||
char *r; | char *r; | |||
long int n = strtol( name.data(), &r, 10 ); | long int n = strtol( name.data(), &r, 10 ); | |||
if ( *r ) | if ( *r ) | |||
uasserted( 13048, (string)"can't append to array using stri ng field name [" + name.data() + "]" ); | uasserted( 13048, (std::string)"can't append to array using string field name [" + name.data() + "]" ); | |||
fill(n); | fill(n); | |||
} | } | |||
void fill (int upTo){ | void fill (int upTo){ | |||
// if this is changed make sure to update error message and jst ests/set7.js | // if this is changed make sure to update error message and jst ests/set7.js | |||
const int maxElems = 1500000; | const int maxElems = 1500000; | |||
BOOST_STATIC_ASSERT(maxElems < (BSONObjMaxUserSize/10)); | BOOST_STATIC_ASSERT(maxElems < (BSONObjMaxUserSize/10)); | |||
uassert(15891, "can't backfill array to larger than 1,500,000 e lements", upTo <= maxElems); | uassert(15891, "can't backfill array to larger than 1,500,000 e lements", upTo <= maxElems); | |||
while( _i < upTo ) | while( _i < upTo ) | |||
skipping to change at line 803 | skipping to change at line 805 | |||
static BSONObj n = nullObj(); | static BSONObj n = nullObj(); | |||
return n.firstElement(); | return n.firstElement(); | |||
} | } | |||
static BSONObj nullObj() { | static BSONObj nullObj() { | |||
BSONObjBuilder _b; | BSONObjBuilder _b; | |||
_b.appendNull( "" ); | _b.appendNull( "" ); | |||
return _b.obj(); | return _b.obj(); | |||
} | } | |||
string num() { return _b.numStr(_i++); } | std::string num() { return _b.numStr(_i++); } | |||
int _i; | int _i; | |||
BSONObjBuilder _b; | BSONObjBuilder _b; | |||
}; | }; | |||
template < class T > | template < class T > | |||
inline BSONObjBuilder& BSONObjBuilder::append( const StringData& fieldN ame, const vector< T >& vals ) { | inline BSONObjBuilder& BSONObjBuilder::append( const StringData& fieldN ame, const std::vector< T >& vals ) { | |||
BSONObjBuilder arrBuilder; | BSONObjBuilder arrBuilder; | |||
for ( unsigned int i = 0; i < vals.size(); ++i ) | for ( unsigned int i = 0; i < vals.size(); ++i ) | |||
arrBuilder.append( numStr( i ), vals[ i ] ); | arrBuilder.append( numStr( i ), vals[ i ] ); | |||
appendArray( fieldName, arrBuilder.done() ); | appendArray( fieldName, arrBuilder.done() ); | |||
return *this; | return *this; | |||
} | } | |||
template < class L > | template < class L > | |||
inline BSONObjBuilder& _appendIt( BSONObjBuilder& _this, const StringDa ta& fieldName, const L& vals ) { | inline BSONObjBuilder& _appendIt( BSONObjBuilder& _this, const StringDa ta& fieldName, const L& vals ) { | |||
BSONObjBuilder arrBuilder; | BSONObjBuilder arrBuilder; | |||
int n = 0; | int n = 0; | |||
for( typename L::const_iterator i = vals.begin(); i != vals.end(); i++ ) | for( typename L::const_iterator i = vals.begin(); i != vals.end(); i++ ) | |||
arrBuilder.append( BSONObjBuilder::numStr(n++), *i ); | arrBuilder.append( BSONObjBuilder::numStr(n++), *i ); | |||
_this.appendArray( fieldName, arrBuilder.done() ); | _this.appendArray( fieldName, arrBuilder.done() ); | |||
return _this; | return _this; | |||
} | } | |||
template < class T > | template < class T > | |||
inline BSONObjBuilder& BSONObjBuilder::append( const StringData& fieldN | inline BSONObjBuilder& BSONObjBuilder::append( const StringData& fieldN | |||
ame, const list< T >& vals ) { | ame, const std::list< T >& vals ) { | |||
return _appendIt< list< T > >( *this, fieldName, vals ); | return _appendIt< std::list< T > >( *this, fieldName, vals ); | |||
} | ||||
template < class T > | ||||
inline BSONObjBuilder& BSONObjBuilder::append( const StringData& fieldN | ||||
ame, const std::set< T >& vals ) { | ||||
return _appendIt< std::set< T > >( *this, fieldName, vals ); | ||||
} | ||||
template < class L > | ||||
inline BSONArrayBuilder& _appendArrayIt( BSONArrayBuilder& _this, const | ||||
L& vals ) { | ||||
for( typename L::const_iterator i = vals.begin(); i != vals.end(); | ||||
i++ ) | ||||
_this.append( *i ); | ||||
return _this; | ||||
} | ||||
template < class T > | ||||
inline BSONArrayBuilder& BSONArrayBuilder::append( const std::list< T > | ||||
& vals ) { | ||||
return _appendArrayIt< std::list< T > >( *this, vals ); | ||||
} | } | |||
template < class T > | template < class T > | |||
inline BSONObjBuilder& BSONObjBuilder::append( const StringData& fieldN | inline BSONArrayBuilder& BSONArrayBuilder::append( const std::set< T >& | |||
ame, const set< T >& vals ) { | vals ) { | |||
return _appendIt< set< T > >( *this, fieldName, vals ); | return _appendArrayIt< std::set< T > >( *this, vals ); | |||
} | } | |||
// $or helper: OR(BSON("x" << GT << 7), BSON("y" << LT 6)); | // $or helper: OR(BSON("x" << GT << 7), BSON("y" << LT 6)); | |||
inline BSONObj OR(const BSONObj& a, const BSONObj& b) | inline BSONObj OR(const BSONObj& a, const BSONObj& b) | |||
{ return BSON( "$or" << BSON_ARRAY(a << b) ); } | { return BSON( "$or" << BSON_ARRAY(a << b) ); } | |||
inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c) | inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c) | |||
{ return BSON( "$or" << BSON_ARRAY(a << b << c) ); } | { return BSON( "$or" << BSON_ARRAY(a << b << c) ); } | |||
inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c, const BSONObj& d) | inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c, const BSONObj& d) | |||
{ return BSON( "$or" << BSON_ARRAY(a << b << c << d) ); } | { return BSON( "$or" << BSON_ARRAY(a << b << c << d) ); } | |||
inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c, const BSONObj& d, const BSONObj& e) | inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c, const BSONObj& d, const BSONObj& e) | |||
End of changes. 29 change blocks. | ||||
38 lines changed or deleted | 61 lines changed or added | |||
bsonobjiterator.h | bsonobjiterator.h | |||
---|---|---|---|---|
skipping to change at line 61 | skipping to change at line 61 | |||
} | } | |||
/** @return true if more elements exist to be enumerated. */ | /** @return true if more elements exist to be enumerated. */ | |||
bool more() { return _pos < _theend; } | bool more() { return _pos < _theend; } | |||
/** @return true if more elements exist to be enumerated INCLUDING the EOO element which is always at the end. */ | /** @return true if more elements exist to be enumerated INCLUDING the EOO element which is always at the end. */ | |||
bool moreWithEOO() { return _pos <= _theend; } | bool moreWithEOO() { return _pos <= _theend; } | |||
/** @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 ) { | BSONElement next( bool checkEnd ) { | |||
assert( _pos <= _theend ); | verify( _pos <= _theend ); | |||
BSONElement e( _pos, checkEnd ? (int)(_theend + 1 - _pos) : -1 ); | BSONElement e( _pos, checkEnd ? (int)(_theend + 1 - _pos) : -1 ); | |||
_pos += e.size( checkEnd ? (int)(_theend + 1 - _pos) : -1 ); | _pos += e.size( checkEnd ? (int)(_theend + 1 - _pos) : -1 ); | |||
return e; | return e; | |||
} | } | |||
BSONElement next() { | BSONElement next() { | |||
assert( _pos <= _theend ); | verify( _pos <= _theend ); | |||
BSONElement e(_pos); | BSONElement e(_pos); | |||
_pos += e.size(); | _pos += e.size(); | |||
return e; | return e; | |||
} | } | |||
void operator++() { next(); } | void operator++() { next(); } | |||
void operator++(int) { next(); } | void operator++(int) { next(); } | |||
BSONElement operator*() { | BSONElement operator*() { | |||
assert( _pos <= _theend ); | verify( _pos <= _theend ); | |||
return BSONElement(_pos); | return BSONElement(_pos); | |||
} | } | |||
private: | private: | |||
const char* _pos; | const char* _pos; | |||
const char* _theend; | const char* _theend; | |||
}; | }; | |||
class BSONObjIteratorSorted { | /** Base class implementing ordered iteration through BSONElements. */ | |||
class BSONIteratorSorted { | ||||
public: | public: | |||
BSONObjIteratorSorted( const BSONObj& o ); | ~BSONIteratorSorted() { | |||
verify( _fields ); | ||||
~BSONObjIteratorSorted() { | ||||
assert( _fields ); | ||||
delete[] _fields; | delete[] _fields; | |||
_fields = 0; | _fields = 0; | |||
} | } | |||
bool more() { | bool more() { | |||
return _cur < _nfields; | return _cur < _nfields; | |||
} | } | |||
BSONElement next() { | BSONElement next() { | |||
assert( _fields ); | verify( _fields ); | |||
if ( _cur < _nfields ) | if ( _cur < _nfields ) | |||
return BSONElement( _fields[_cur++] ); | return BSONElement( _fields[_cur++] ); | |||
return BSONElement(); | return BSONElement(); | |||
} | } | |||
protected: | ||||
class ElementFieldCmp; | ||||
BSONIteratorSorted( const BSONObj &o, const ElementFieldCmp &cmp ); | ||||
private: | private: | |||
const char ** _fields; | const char ** _fields; | |||
int _nfields; | int _nfields; | |||
int _cur; | int _cur; | |||
}; | }; | |||
/** Provides iteration of a BSONObj's BSONElements in lexical field ord | ||||
er. */ | ||||
class BSONObjIteratorSorted : public BSONIteratorSorted { | ||||
public: | ||||
BSONObjIteratorSorted( const BSONObj &object ); | ||||
}; | ||||
/** | ||||
* Provides iteration of a BSONArray's BSONElements in numeric field or | ||||
der. | ||||
* The elements of a bson array should always be numerically ordered by | ||||
field name, but this | ||||
* implementation re-sorts them anyway. | ||||
*/ | ||||
class BSONArrayIteratorSorted : public BSONIteratorSorted { | ||||
public: | ||||
BSONArrayIteratorSorted( const BSONArray &array ); | ||||
}; | ||||
/** transform a BSON array into a vector of BSONElements. | /** transform a BSON array into a vector of BSONElements. | |||
we match array # positions with their vector position, and ignore | we match array # positions with their vector position, and ignore | |||
any fields with non-numeric field names. | any fields with non-numeric field names. | |||
*/ | */ | |||
inline vector<BSONElement> BSONElement::Array() const { | inline std::vector<BSONElement> BSONElement::Array() const { | |||
chk(mongo::Array); | chk(mongo::Array); | |||
vector<BSONElement> v; | std::vector<BSONElement> v; | |||
BSONObjIterator i(Obj()); | BSONObjIterator i(Obj()); | |||
while( i.more() ) { | while( i.more() ) { | |||
BSONElement e = i.next(); | BSONElement e = i.next(); | |||
const char *f = e.fieldName(); | const char *f = e.fieldName(); | |||
try { | try { | |||
unsigned u = stringToNum(f); | unsigned u = stringToNum(f); | |||
assert( u < 1000000 ); | verify( u < 1000000 ); | |||
if( u >= v.size() ) | if( u >= v.size() ) | |||
v.resize(u+1); | v.resize(u+1); | |||
v[u] = e; | v[u] = e; | |||
} | } | |||
catch(unsigned) { } | catch(unsigned) { } | |||
} | } | |||
return v; | return v; | |||
} | } | |||
/** Similar to BOOST_FOREACH | /** Similar to BOOST_FOREACH | |||
End of changes. 11 change blocks. | ||||
12 lines changed or deleted | 34 lines changed or added | |||
bsontypes.h | bsontypes.h | |||
---|---|---|---|---|
skipping to change at line 26 | skipping to change at line 26 | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "util/misc.h" | #include "util/misc.h" | |||
namespace bson { } | namespace bson { } | |||
namespace mongo { | namespace mongo { | |||
using namespace std; | ||||
class BSONArrayBuilder; | class BSONArrayBuilder; | |||
class BSONElement; | class BSONElement; | |||
class BSONObj; | class BSONObj; | |||
class BSONObjBuilder; | class BSONObjBuilder; | |||
class BSONObjBuilderValueStream; | class BSONObjBuilderValueStream; | |||
class BSONObjIterator; | class BSONObjIterator; | |||
class Ordering; | class Ordering; | |||
class Record; | class Record; | |||
struct BSONArray; // empty subclass of BSONObj useful for overloading | struct BSONArray; // empty subclass of BSONObj useful for overloading | |||
struct BSONElementCmpWithoutField; | struct BSONElementCmpWithoutField; | |||
End of changes. 1 change blocks. | ||||
2 lines changed or deleted | 0 lines changed or added | |||
btree.h | btree.h | |||
---|---|---|---|---|
skipping to change at line 21 | skipping to change at line 21 | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Affero General Public License for more details. | * GNU Affero General Public License for more details. | |||
* | * | |||
* You should have received a copy of the GNU Affero General Public Licen se | * You should have received a copy of the GNU Affero General Public Licen se | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../pch.h" | #include "pch.h" | |||
#include "jsobj.h" | #include "mongo/db/jsobj.h" | |||
#include "diskloc.h" | #include "mongo/db/diskloc.h" | |||
#include "pdfile.h" | #include "mongo/db/pdfile.h" | |||
#include "key.h" | #include "mongo/db/key.h" | |||
namespace mongo { | namespace mongo { | |||
/** | /** | |||
* Our btree implementation generally follows the standard btree algori thm, | * Our btree implementation generally follows the standard btree algori thm, | |||
* which is described in many places. The nodes of our btree are refer red to | * which is described in many places. The nodes of our btree are refer red to | |||
* as buckets below. These buckets are of size BucketSize and their bo dy is | * as buckets below. These buckets are of size BucketSize and their bo dy is | |||
* an ordered array of <bson key, disk loc> pairs, where disk loc is th e disk | * an ordered array of <bson key, disk loc> pairs, where disk loc is th e disk | |||
* location of a document and bson key is a projection of this document into | * location of a document and bson key is a projection of this document into | |||
* the schema of the index for this btree. Ordering is determined on t he | * the schema of the index for this btree. Ordering is determined on t he | |||
skipping to change at line 93 | skipping to change at line 93 | |||
*/ | */ | |||
Loc prevChildBucket; | Loc prevChildBucket; | |||
/** The location of the record associated with this key. */ | /** The location of the record associated with this key. */ | |||
Loc recordLoc; | Loc recordLoc; | |||
short keyDataOfs() const { return (short) _kdo; } | short keyDataOfs() const { return (short) _kdo; } | |||
/** Offset within current bucket of the variable width bson key for this _KeyNode. */ | /** Offset within current bucket of the variable width bson key for this _KeyNode. */ | |||
unsigned short _kdo; | unsigned short _kdo; | |||
void setKeyDataOfs(short s) { | void setKeyDataOfs(short s) { | |||
_kdo = s; | _kdo = s; | |||
assert(s>=0); | verify(s>=0); | |||
} | } | |||
/** Seems to be redundant. */ | /** Seems to be redundant. */ | |||
void setKeyDataOfsSavingUse(short s) { | void setKeyDataOfsSavingUse(short s) { | |||
_kdo = s; | _kdo = s; | |||
assert(s>=0); | verify(s>=0); | |||
} | } | |||
/** | /** | |||
* Unused keys are not returned by read operations. Keys may be ma rked | * Unused keys are not returned by read operations. Keys may be ma rked | |||
* as unused in cases where it is difficult to delete them while | * as unused in cases where it is difficult to delete them while | |||
* maintaining the constraints required of a btree. | * maintaining the constraints required of a btree. | |||
* | * | |||
* Setting ofs to odd is the sentinel for unused, as real recordLoc 's | * Setting ofs to odd is the sentinel for unused, as real recordLoc 's | |||
* are always even numbers. Note we need to keep its value basical ly | * are always even numbers. Note we need to keep its value basical ly | |||
* the same as we use the recordLoc as part of the key in the index | * the same as we use the recordLoc as part of the key in the index | |||
* (to handle duplicate keys efficiently). | * (to handle duplicate keys efficiently). | |||
skipping to change at line 183 | skipping to change at line 183 | |||
public: | public: | |||
typedef __KeyNode<DiskLoc> _KeyNode; | typedef __KeyNode<DiskLoc> _KeyNode; | |||
typedef DiskLoc Loc; | typedef DiskLoc Loc; | |||
typedef KeyBson Key; | typedef KeyBson Key; | |||
typedef KeyBson KeyOwned; | typedef KeyBson KeyOwned; | |||
enum { BucketSize = 8192 }; | enum { BucketSize = 8192 }; | |||
// largest key size we allow. note we very much need to support bi gger keys (somehow) in the future. | // largest key size we allow. note we very much need to support bi gger keys (somehow) in the future. | |||
static const int KeyMax = OldBucketSize / 10; | static const int KeyMax = OldBucketSize / 10; | |||
// A sentinel value sometimes used to identify a deallocated bucket | ||||
. | ||||
static const int INVALID_N_SENTINEL = -1; | ||||
}; | }; | |||
// a a a ofs ofs ofs ofs | // a a a ofs ofs ofs ofs | |||
class DiskLoc56Bit { | class DiskLoc56Bit { | |||
int ofs; | int ofs; | |||
unsigned char _a[3]; | unsigned char _a[3]; | |||
unsigned long long Z() const { | unsigned long long Z() const { | |||
// endian | // endian | |||
return *((unsigned long long*)this) & 0x00ffffffffffffffULL; | return *((unsigned long long*)this) & 0x00ffffffffffffffULL; | |||
} | } | |||
skipping to change at line 241 | skipping to change at line 243 | |||
bool operator!=(const DiskLoc& rhs) const { return !(*this==rhs); } | bool operator!=(const DiskLoc& rhs) const { return !(*this==rhs); } | |||
bool isNull() const { return ofs < 0; } | bool isNull() const { return ofs < 0; } | |||
void Null() { | void Null() { | |||
ofs = OurNullOfs; | ofs = OurNullOfs; | |||
_a[0] = _a[1] = _a[2] = 0; | _a[0] = _a[1] = _a[2] = 0; | |||
} | } | |||
string toString() const { return DiskLoc(*this).toString(); } | string toString() const { return DiskLoc(*this).toString(); } | |||
void operator=(const DiskLoc& loc) { | void operator=(const DiskLoc& loc) { | |||
ofs = loc.getOfs(); | ofs = loc.getOfs(); | |||
int la = loc.a(); | int la = loc.a(); | |||
assert( la <= 0xffffff ); // must fit in 3 bytes | verify( la <= 0xffffff ); // must fit in 3 bytes | |||
if( la < 0 ) { | if( la < 0 ) { | |||
assert( la == -1 ); | if ( la != -1 ) { | |||
log() << "btree diskloc isn't negative 1: " << la << en | ||||
dl; | ||||
verify ( la == -1 ); | ||||
} | ||||
la = 0; | la = 0; | |||
ofs = OurNullOfs; | ofs = OurNullOfs; | |||
} | } | |||
memcpy(_a, &la, 3); // endian | memcpy(_a, &la, 3); // endian | |||
dassert( ofs != 0 ); | dassert( ofs != 0 ); | |||
} | } | |||
DiskLoc56Bit& writing() const { | DiskLoc56Bit& writing() const { | |||
return *((DiskLoc56Bit*) getDur().writingPtr((void*)this, 7)); | return *((DiskLoc56Bit*) getDur().writingPtr((void*)this, 7)); | |||
} | } | |||
}; | }; | |||
skipping to change at line 265 | skipping to change at line 270 | |||
class BtreeData_V1 { | class BtreeData_V1 { | |||
public: | public: | |||
typedef DiskLoc56Bit Loc; | typedef DiskLoc56Bit Loc; | |||
//typedef DiskLoc Loc; | //typedef DiskLoc Loc; | |||
typedef __KeyNode<Loc> _KeyNode; | typedef __KeyNode<Loc> _KeyNode; | |||
typedef KeyV1 Key; | typedef KeyV1 Key; | |||
typedef KeyV1Owned KeyOwned; | typedef KeyV1Owned KeyOwned; | |||
enum { BucketSize = 8192-16 }; // leave room for Record header | enum { BucketSize = 8192-16 }; // leave room for Record header | |||
// largest key size we allow. note we very much need to support bi gger keys (somehow) in the future. | // largest key size we allow. note we very much need to support bi gger keys (somehow) in the future. | |||
static const int KeyMax = 1024; | static const int KeyMax = 1024; | |||
// A sentinel value sometimes used to identify a deallocated bucket | ||||
. | ||||
static const unsigned short INVALID_N_SENTINEL = 0xffff; | ||||
protected: | protected: | |||
/** Parent bucket of this bucket, which isNull() for the root bucke t. */ | /** Parent bucket of this bucket, which isNull() for the root bucke t. */ | |||
Loc parent; | Loc parent; | |||
/** Given that there are n keys, this is the n index child. */ | /** Given that there are n keys, this is the n index child. */ | |||
Loc nextChild; | Loc nextChild; | |||
unsigned short flags; | unsigned short flags; | |||
/** basicInsert() assumes the next three members are consecutive an d in this order: */ | /** basicInsert() assumes the next three members are consecutive an d in this order: */ | |||
skipping to change at line 411 | skipping to change at line 418 | |||
* keys and 'key'. | * keys and 'key'. | |||
* Postconditions: | * Postconditions: | |||
* - If there is space for key without packing, it is inserted as the | * - If there is space for key without packing, it is inserted as the | |||
* last key with specified prevChild and true is returned. | * last key with specified prevChild and true is returned. | |||
* Importantly, nextChild is not updated! | * Importantly, nextChild is not updated! | |||
* - Otherwise false is returned and there is no change. | * - Otherwise false is returned and there is no change. | |||
*/ | */ | |||
bool _pushBack(const DiskLoc recordLoc, const Key& key, const Order ing &order, const DiskLoc prevChild); | bool _pushBack(const DiskLoc recordLoc, const Key& key, const Order ing &order, const DiskLoc prevChild); | |||
void pushBack(const DiskLoc recordLoc, const Key& key, const Orderi ng &order, const DiskLoc prevChild) { | void pushBack(const DiskLoc recordLoc, const Key& key, const Orderi ng &order, const DiskLoc prevChild) { | |||
bool ok = _pushBack( recordLoc , key , order , prevChild ); | bool ok = _pushBack( recordLoc , key , order , prevChild ); | |||
assert(ok); | verify(ok); | |||
} | } | |||
/** | /** | |||
* This is a special purpose function used by BtreeBuilder. The | * This is a special purpose function used by BtreeBuilder. The | |||
* interface is quite dangerous if you're not careful. The bson ke y | * interface is quite dangerous if you're not careful. The bson ke y | |||
* returned here points to bucket memory that has been invalidated but | * returned here points to bucket memory that has been invalidated but | |||
* not yet reclaimed. | * not yet reclaimed. | |||
* | * | |||
* TODO Maybe this could be replaced with two functions, one which | * TODO Maybe this could be replaced with two functions, one which | |||
* returns the last key without deleting it and another which simpl y | * returns the last key without deleting it and another which simpl y | |||
skipping to change at line 575 | skipping to change at line 582 | |||
* - The bson 'key' must fit in the bucket without packing. | * - The bson 'key' must fit in the bucket without packing. | |||
* - If 'key' and 'prevChildBucket' are set at index i, the btree | * - If 'key' and 'prevChildBucket' are set at index i, the btree | |||
* ordering properties will be maintained. | * ordering properties will be maintained. | |||
* Postconditions: | * Postconditions: | |||
* - The specified key is set at index i, replacing the existing | * - The specified key is set at index i, replacing the existing | |||
* _KeyNode data and without shifting any other _KeyNode objects . | * _KeyNode data and without shifting any other _KeyNode objects . | |||
*/ | */ | |||
void setKey( int i, const DiskLoc recordLoc, const Key& key, const DiskLoc prevChildBucket ); | void setKey( int i, const DiskLoc recordLoc, const Key& key, const DiskLoc prevChildBucket ); | |||
}; | }; | |||
class IndexInsertionContinuation; | ||||
template< class V> | template< class V> | |||
struct Continuation; | struct IndexInsertionContinuationImpl; | |||
/** | /** | |||
* This class adds functionality for manipulating buckets that are asse mbled | * This class adds functionality for manipulating buckets that are asse mbled | |||
* in a tree. The requirements for const and non const functions and | * in a tree. The requirements for const and non const functions and | |||
* arguments are generally the same as in BtreeBucket. Because this cl ass | * arguments are generally the same as in BtreeBucket. Because this cl ass | |||
* deals with tree structure, some functions that are marked const may | * deals with tree structure, some functions that are marked const may | |||
* trigger modification of another node in the btree or potentially of the | * trigger modification of another node in the btree or potentially of the | |||
* current node. In such cases, the function's implementation explicit ly | * current node. In such cases, the function's implementation explicit ly | |||
* casts away const when indicating an intent to write to the durabilit y | * casts away const when indicating an intent to write to the durabilit y | |||
* layer. The DiskLocs provided to such functions should be passed by | * layer. The DiskLocs provided to such functions should be passed by | |||
skipping to change at line 609 | skipping to change at line 618 | |||
* | * | |||
* TODO There are several cases in which the 'this' pointer is invalida ted | * TODO There are several cases in which the 'this' pointer is invalida ted | |||
* as a result of deallocation. A seperate class representing a btree would | * as a result of deallocation. A seperate class representing a btree would | |||
* alleviate some fragile cases where the implementation must currently | * alleviate some fragile cases where the implementation must currently | |||
* behave correctly if the 'this' pointer is suddenly invalidated by a | * behave correctly if the 'this' pointer is suddenly invalidated by a | |||
* callee. | * callee. | |||
*/ | */ | |||
template< class V > | template< class V > | |||
class BtreeBucket : public BucketBasics<V> { | class BtreeBucket : public BucketBasics<V> { | |||
friend class BtreeCursor; | friend class BtreeCursor; | |||
friend struct Continuation<V>; | friend struct IndexInsertionContinuationImpl<V>; | |||
public: | public: | |||
// make compiler happy: | // make compiler happy: | |||
typedef typename V::Key Key; | typedef typename V::Key Key; | |||
typedef typename V::KeyOwned KeyOwned; | typedef typename V::KeyOwned KeyOwned; | |||
typedef typename BucketBasics<V>::KeyNode KeyNode; | typedef typename BucketBasics<V>::KeyNode KeyNode; | |||
typedef typename BucketBasics<V>::_KeyNode _KeyNode; | typedef typename BucketBasics<V>::_KeyNode _KeyNode; | |||
typedef typename BucketBasics<V>::Loc Loc; | typedef typename BucketBasics<V>::Loc Loc; | |||
const _KeyNode& k(int i) const { return static_cast< const Buck etBasics<V> * >(this)->k(i); } | const _KeyNode& k(int i) const { return static_cast< const Buck etBasics<V> * >(this)->k(i); } | |||
protected: | protected: | |||
_KeyNode& k(int i) { return static_cast< BucketBasi cs<V> * >(this)->_k(i); } | _KeyNode& k(int i) { return static_cast< BucketBasi cs<V> * >(this)->_k(i); } | |||
skipping to change at line 689 | skipping to change at line 698 | |||
* and @return 0. The root of the btree may be changed, so | * and @return 0. The root of the btree may be changed, so | |||
* 'this'/thisLoc may no longer be the root upon return. | * 'this'/thisLoc may no longer be the root upon return. | |||
*/ | */ | |||
int bt_insert(const DiskLoc thisLoc, const DiskLoc recordLoc, | int bt_insert(const DiskLoc thisLoc, const DiskLoc recordLoc, | |||
const BSONObj& key, const Ordering &order, bool dupsA llowed, | const BSONObj& key, const Ordering &order, bool dupsA llowed, | |||
IndexDetails& idx, bool toplevel = true) const; | IndexDetails& idx, bool toplevel = true) const; | |||
/** does the insert in two steps - can then use an upgradable lock for step 1, which | /** does the insert in two steps - can then use an upgradable lock for step 1, which | |||
is the part which may have page faults. also that step is most of the computational work. | is the part which may have page faults. also that step is most of the computational work. | |||
*/ | */ | |||
void twoStepInsert(DiskLoc thisLoc, Continuation<V> &c, bool dupsAl lowed) const; | void twoStepInsert(DiskLoc thisLoc, IndexInsertionContinuationImpl< V> &c, bool dupsAllowed) const; | |||
/** | /** | |||
* Preconditions: | * Preconditions: | |||
* - 'key' has a valid schema for this index, and may have objsize () > KeyMax. | * - 'key' has a valid schema for this index, and may have objsize () > KeyMax. | |||
* Postconditions: | * Postconditions: | |||
* - If key / recordLoc are in the btree, they are removed (possib ly | * - If key / recordLoc are in the btree, they are removed (possib ly | |||
* by being marked as an unused key), @return true, and potentia lly | * by being marked as an unused key), @return true, and potentia lly | |||
* invalidate 'this' / thisLoc and change the head. | * invalidate 'this' / thisLoc and change the head. | |||
* - If key / recordLoc are not in the btree, @return false and do nothing. | * - If key / recordLoc are not in the btree, @return false and do nothing. | |||
*/ | */ | |||
skipping to change at line 943 | skipping to change at line 952 | |||
*/ | */ | |||
void insertHere(const DiskLoc thisLoc, int keypos, | void insertHere(const DiskLoc thisLoc, int keypos, | |||
const DiskLoc recordLoc, const Key& key, const Orde ring &order, | const DiskLoc recordLoc, const Key& key, const Orde ring &order, | |||
const DiskLoc lchild, const DiskLoc rchild, IndexDe tails &idx) const; | const DiskLoc lchild, const DiskLoc rchild, IndexDe tails &idx) const; | |||
/** bt_insert() is basically just a wrapper around this. */ | /** bt_insert() is basically just a wrapper around this. */ | |||
int _insert(const DiskLoc thisLoc, const DiskLoc recordLoc, | int _insert(const DiskLoc thisLoc, const DiskLoc recordLoc, | |||
const Key& key, const Ordering &order, bool dupsAllowed , | const Key& key, const Ordering &order, bool dupsAllowed , | |||
const DiskLoc lChild, const DiskLoc rChild, IndexDetail s &idx) const; | const DiskLoc lChild, const DiskLoc rChild, IndexDetail s &idx) const; | |||
void insertStepOne(DiskLoc thisLoc, Continuation<V>& c, bool dupsAl | void insertStepOne( | |||
lowed) const; | DiskLoc thisLoc, IndexInsertionContinuationImpl<V>& c, bool | |||
dupsAllowed) const; | ||||
bool find(const IndexDetails& idx, const Key& key, const DiskLoc &r ecordLoc, const Ordering &order, int& pos, bool assertIfDup) const; | bool find(const IndexDetails& idx, const Key& key, const DiskLoc &r ecordLoc, const Ordering &order, int& pos, bool assertIfDup) const; | |||
static bool customFind( int l, int h, const BSONObj &keyBegin, int keyBeginLen, bool afterKey, const vector< const BSONElement * > &keyEnd, co nst vector< bool > &keyEndInclusive, const Ordering &order, int direction, DiskLoc &thisLoc, int &keyOfs, pair< DiskLoc, int > &bestParent ) ; | static bool customFind( int l, int h, const BSONObj &keyBegin, int keyBeginLen, bool afterKey, const vector< const BSONElement * > &keyEnd, co nst vector< bool > &keyEndInclusive, const Ordering &order, int direction, DiskLoc &thisLoc, int &keyOfs, pair< DiskLoc, int > &bestParent ) ; | |||
static void findLargestKey(const DiskLoc& thisLoc, DiskLoc& largest Loc, int& largestKey); | static void findLargestKey(const DiskLoc& thisLoc, DiskLoc& largest Loc, int& largestKey); | |||
static int customBSONCmp( const BSONObj &l, const BSONObj &rBegin, int rBeginLen, bool rSup, const vector< const BSONElement * > &rEnd, const vector< bool > &rEndInclusive, const Ordering &o, int direction ); | static int customBSONCmp( const BSONObj &l, const BSONObj &rBegin, int rBeginLen, bool rSup, const vector< const BSONElement * > &rEnd, const vector< bool > &rEndInclusive, const Ordering &o, int direction ); | |||
/** If child is non null, set its parent to thisLoc */ | /** If child is non null, set its parent to thisLoc */ | |||
static void fix(const DiskLoc thisLoc, const DiskLoc child); | static void fix(const DiskLoc thisLoc, const DiskLoc child); | |||
/** | /** | |||
skipping to change at line 994 | skipping to change at line 1004 | |||
void deleteInternalKey( const DiskLoc thisLoc, int keypos, IndexDet ails &id, const Ordering &order ); | void deleteInternalKey( const DiskLoc thisLoc, int keypos, IndexDet ails &id, const Ordering &order ); | |||
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 Key& key ); | static string dupKeyError( const IndexDetails& idx , const Key& key ); | |||
}; | }; | |||
#pragma pack() | #pragma pack() | |||
class FieldRangeVector; | class FieldRangeVector; | |||
class FieldRangeVectorIterator; | class FieldRangeVectorIterator; | |||
/** | ||||
* A Cursor class for Btree iteration. | ||||
* | ||||
* A BtreeCursor can record its current btree position (noteLoc()) and | ||||
then relocate this | ||||
* position after a write (checkLoc()). A recorded btree position cons | ||||
ists of a btree bucket, | ||||
* bucket key offset, and unique btree key. To relocate a unique btree | ||||
key, a BtreeCursor first | ||||
* checks the btree key at its recorded btree bucket and bucket key off | ||||
set. If the key at that | ||||
* location does not match the recorded btree key, and an adjacent key | ||||
also fails to match, | ||||
* the recorded key (or the next existing key following it) is located | ||||
in the btree using binary | ||||
* search. If the recorded btree bucket is invalidated, the initial re | ||||
corded bucket check is | ||||
* not attempted (see SERVER-4575). | ||||
*/ | ||||
class BtreeCursor : public Cursor { | class BtreeCursor : public Cursor { | |||
protected: | protected: | |||
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& | BtreeCursor( NamespaceDetails *_d, int _idxNo, const IndexDetails& | |||
_id, const shared_ptr< FieldRangeVector > &_bounds, int _direction ); | _id, | |||
const shared_ptr< FieldRangeVector > &_bounds, int sing | ||||
leIntervalLimit, | ||||
int _direction ); | ||||
public: | public: | |||
virtual ~BtreeCursor(); | virtual ~BtreeCursor(); | |||
/** makes an appropriate subclass depending on the index version */ | /** makes an appropriate subclass depending on the index version */ | |||
static BtreeCursor* make( NamespaceDetails *_d, const IndexDetails& , const BSONObj &startKey, const BSONObj &endKey, bool endKeyInclusive, int direction ); | static BtreeCursor* make( NamespaceDetails *_d, const IndexDetails& , const BSONObj &startKey, const BSONObj &endKey, bool endKeyInclusive, int direction ); | |||
static BtreeCursor* make( NamespaceDetails *_d, const IndexDetails& _id, const shared_ptr< FieldRangeVector > &_bounds, int _direction ); | static BtreeCursor* make( NamespaceDetails *_d, const IndexDetails& _id, const shared_ptr< FieldRangeVector > &_bounds, int _direction ); | |||
static BtreeCursor* make( NamespaceDetails *_d, int _idxNo, const I ndexDetails&, const BSONObj &startKey, const BSONObj &endKey, bool endKeyIn clusive, int direction ); | static BtreeCursor* make( NamespaceDetails *_d, int _idxNo, const I ndexDetails&, const BSONObj &startKey, const BSONObj &endKey, bool endKeyIn clusive, int direction ); | |||
static BtreeCursor* make( NamespaceDetails *_d, int _idxNo, const I | static BtreeCursor* make( NamespaceDetails *_d, int _idxNo, const I | |||
ndexDetails& _id, const shared_ptr< FieldRangeVector > &_bounds, int _direc | ndexDetails& _id, | |||
tion ); | const shared_ptr< FieldRangeVector > &_bou | |||
nds, | ||||
int singleIntervalLimit, int _direction ); | ||||
virtual bool ok() { return !bucket.isNull(); } | virtual bool ok() { return !bucket.isNull(); } | |||
virtual bool advance(); | virtual bool advance(); | |||
virtual void noteLocation(); // updates keyAtKeyOfs... | virtual void noteLocation(); // updates keyAtKeyOfs... | |||
virtual void checkLocation() = 0; | virtual void checkLocation() = 0; | |||
virtual bool supportGetMore() { return true; } | virtual bool supportGetMore() { return true; } | |||
virtual bool supportYields() { return true; } | virtual bool supportYields() { 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(). | |||
skipping to change at line 1032 | skipping to change at line 1058 | |||
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; | |||
} | } | |||
virtual bool modifiedKeys() const { return _multikey; } | virtual bool modifiedKeys() const { return _multikey; } | |||
virtual bool isMultiKey() const { return _multikey; } | virtual bool isMultiKey() const { return _multikey; } | |||
/*const _KeyNode& _currKeyNode() const { | /*const _KeyNode& _currKeyNode() const { | |||
assert( !bucket.isNull() ); | verify( !bucket.isNull() ); | |||
const _KeyNode& kn = keyNode(keyOfs); | const _KeyNode& kn = keyNode(keyOfs); | |||
assert( kn.isUsed() ); | verify( kn.isUsed() ); | |||
return kn; | return kn; | |||
}*/ | }*/ | |||
/** returns BSONObj() if ofs is out of range */ | /** returns BSONObj() if ofs is out of range */ | |||
virtual BSONObj keyAt(int ofs) const = 0; | virtual BSONObj keyAt(int ofs) const = 0; | |||
virtual BSONObj currKey() const = 0; | virtual BSONObj currKey() const = 0; | |||
virtual BSONObj indexKeyPattern() { return indexDetails.keyPattern( ); } | virtual BSONObj indexKeyPattern() { return indexDetails.keyPattern( ); } | |||
virtual void aboutToDeleteBucket(const DiskLoc& b) { | virtual void aboutToDeleteBucket(const DiskLoc& b) { | |||
skipping to change at line 1066 | skipping to change at line 1092 | |||
return key.replaceFieldNames( indexDetails.keyPattern() ).clien tReadable(); | return key.replaceFieldNames( indexDetails.keyPattern() ).clien tReadable(); | |||
} | } | |||
virtual BSONObj prettyIndexBounds() const; | virtual BSONObj prettyIndexBounds() const; | |||
virtual CoveredIndexMatcher *matcher() const { return _matcher.get( ); } | virtual CoveredIndexMatcher *matcher() const { return _matcher.get( ); } | |||
virtual shared_ptr< CoveredIndexMatcher > matcherPtr() const { retu rn _matcher; } | virtual shared_ptr< CoveredIndexMatcher > matcherPtr() const { retu rn _matcher; } | |||
virtual void setMatcher( shared_ptr< CoveredIndexMatcher > matcher ) { _matcher = matcher; } | virtual void setMatcher( shared_ptr< CoveredIndexMatcher > matcher ) { _matcher = matcher; } | |||
virtual const Projection::KeyOnly *keyFieldsOnly() const { return _ | ||||
keyFieldsOnly.get(); } | ||||
virtual void setKeyFieldsOnly( const shared_ptr<Projection::KeyOnly | ||||
> &keyFieldsOnly ) { | ||||
_keyFieldsOnly = keyFieldsOnly; | ||||
} | ||||
virtual long long nscanned() { return _nscanned; } | virtual long long nscanned() { return _nscanned; } | |||
/** for debugging only */ | /** for debugging only */ | |||
const DiskLoc getBucket() const { return bucket; } | const DiskLoc getBucket() const { return bucket; } | |||
int getKeyOfs() const { return keyOfs; } | int getKeyOfs() const { return keyOfs; } | |||
// just for unit tests | // just for unit tests | |||
virtual bool curKeyHasChild() = 0; | virtual bool curKeyHasChild() = 0; | |||
protected: | protected: | |||
skipping to change at line 1118 | skipping to change at line 1150 | |||
const BSONObj _order; | const BSONObj _order; | |||
const Ordering _ordering; | const Ordering _ordering; | |||
DiskLoc bucket; | DiskLoc bucket; | |||
int keyOfs; | int keyOfs; | |||
const int _direction; // 1=fwd,-1=reverse | const 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; | |||
const shared_ptr< FieldRangeVector > _bounds; | const shared_ptr< FieldRangeVector > _bounds; | |||
auto_ptr< FieldRangeVectorIterator > _boundsIterator; | auto_ptr< FieldRangeVectorIterator > _boundsIterator; | |||
shared_ptr< CoveredIndexMatcher > _matcher; | shared_ptr< CoveredIndexMatcher > _matcher; | |||
shared_ptr<Projection::KeyOnly> _keyFieldsOnly; | ||||
bool _independentFieldRanges; | bool _independentFieldRanges; | |||
long long _nscanned; | long long _nscanned; | |||
}; | }; | |||
template< class V > | ||||
struct Continuation { | ||||
//Continuation(const typename V::Key & k); | ||||
Continuation(DiskLoc thisLoc, DiskLoc _recordLoc, const BSONObj &_k | ||||
ey, | ||||
Ordering _order, IndexDetails& _idx) : | ||||
bLoc(thisLoc), recordLoc(_recordLoc), key(_key), order(_order), | ||||
idx(_idx) { | ||||
op = Nothing; | ||||
} | ||||
DiskLoc bLoc; | ||||
DiskLoc recordLoc; | ||||
typename V::KeyOwned key; | ||||
const Ordering order; | ||||
IndexDetails& idx; | ||||
enum Op { Nothing, SetUsed, InsertHere } op; | ||||
int pos; | ||||
const BtreeBucket<V> *b; | ||||
void stepTwo() { | ||||
if( op == Nothing ) | ||||
return; | ||||
else if( op == SetUsed ) { | ||||
const typename V::_KeyNode& kn = b->k(pos); | ||||
kn.writing().setUsed(); | ||||
} | ||||
else { | ||||
b->insertHere(bLoc, pos, recordLoc, key, order, DiskLoc(), | ||||
DiskLoc(), idx); | ||||
} | ||||
} | ||||
}; | ||||
/** Renames the index namespace for this btree's index. */ | /** Renames the index namespace for this btree's index. */ | |||
void renameIndexNamespace(const char *oldNs, const char *newNs); | void renameIndexNamespace(const char *oldNs, const char *newNs); | |||
/** | /** | |||
* give us a writable version of the btree bucket (declares write inten t). | * give us a writable version of the btree bucket (declares write inten t). | |||
* note it is likely more efficient to declare write intent on somethin g smaller when you can. | * note it is likely more efficient to declare write intent on somethin g smaller when you can. | |||
*/ | */ | |||
template< class V > | template< class V > | |||
BtreeBucket<V> * DiskLoc::btreemod() const { | BtreeBucket<V> * DiskLoc::btreemod() const { | |||
assert( _a != -1 ); | verify( _a != -1 ); | |||
BtreeBucket<V> *b = const_cast< BtreeBucket<V> * >( btree<V>() ); | BtreeBucket<V> *b = const_cast< BtreeBucket<V> * >( btree<V>() ); | |||
return static_cast< BtreeBucket<V>* >( getDur().writingPtr( b, V::B ucketSize ) ); | return static_cast< BtreeBucket<V>* >( getDur().writingPtr( b, V::B ucketSize ) ); | |||
} | } | |||
template< class V > | template< class V > | |||
BucketBasics<V>::KeyNode::KeyNode(const BucketBasics<V>& bb, const _Key Node &k) : | BucketBasics<V>::KeyNode::KeyNode(const BucketBasics<V>& bb, const _Key Node &k) : | |||
prevChildBucket(k.prevChildBucket), | prevChildBucket(k.prevChildBucket), | |||
recordLoc(k.recordLoc), key(bb.data+k.keyDataOfs()) | recordLoc(k.recordLoc), key(bb.data+k.keyDataOfs()) | |||
{ } | { } | |||
End of changes. 22 change blocks. | ||||
58 lines changed or deleted | 69 lines changed or added | |||
bufreader.h | bufreader.h | |||
---|---|---|---|---|
skipping to change at line 67 | skipping to change at line 67 | |||
/** return current offset into buffer */ | /** return current offset into buffer */ | |||
unsigned offset() const { return (char*)_pos - (char*)_start; } | unsigned offset() const { return (char*)_pos - (char*)_start; } | |||
/** return remaining bytes */ | /** return remaining bytes */ | |||
unsigned remaining() const { return (char*)_end -(char*)_pos; } | unsigned remaining() const { return (char*)_end -(char*)_pos; } | |||
/** back up by nbytes */ | /** back up by nbytes */ | |||
void rewind(unsigned nbytes) { | void rewind(unsigned nbytes) { | |||
_pos = ((char *) _pos) - nbytes; | _pos = ((char *) _pos) - nbytes; | |||
assert( _pos >= _start ); | verify( _pos >= _start ); | |||
} | } | |||
/** return current position pointer, and advance by len */ | /** return current position pointer, and advance by len */ | |||
const void* skip(unsigned len) { | const void* skip(unsigned len) { | |||
const char *nxt = ((char *) _pos) + len; | const char *nxt = ((char *) _pos) + len; | |||
if( _end < nxt ) throw eof(); | if( _end < nxt ) throw eof(); | |||
const void *p = _pos; | const void *p = _pos; | |||
_pos = nxt; | _pos = nxt; | |||
return p; | return p; | |||
} | } | |||
End of changes. 1 change blocks. | ||||
1 lines changed or deleted | 1 lines changed or added | |||
builder.h | builder.h | |||
---|---|---|---|---|
skipping to change at line 30 | skipping to change at line 30 | |||
#include <cfloat> | #include <cfloat> | |||
#include <string> | #include <string> | |||
#include <sstream> | #include <sstream> | |||
#include <iostream> | #include <iostream> | |||
#include <string.h> | #include <string.h> | |||
#include <stdio.h> | #include <stdio.h> | |||
#include "../inline_decls.h" | #include "../inline_decls.h" | |||
#include "../stringdata.h" | #include "../stringdata.h" | |||
namespace mongo { | namespace mongo { | |||
/* Accessing unaligned doubles on ARM generates an alignment trap and a | ||||
borts with SIGBUS on Linux. | ||||
Wrapping the double in a packed struct forces gcc to generate code t | ||||
hat works with unaligned values too. | ||||
The generated code for other architectures (which already allow unal | ||||
igned accesses) is the same as if | ||||
there was a direct pointer access. | ||||
*/ | ||||
struct PackedDouble { | ||||
double d; | ||||
} PACKED_DECL; | ||||
/* 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. | |||
Also note that the server has some basic checks to enforce this limi t but those checks are not exhaustive | Also note that the server has some basic checks to enforce this limi t but those checks are not exhaustive | |||
for example need to check for size too big after | for example need to check for size too big after | |||
update $push (append) operation | update $push (append) operation | |||
various db.eval() type operations | various db.eval() type operations | |||
*/ | */ | |||
const int BSONObjMaxUserSize = 16 * 1024 * 1024; | const int BSONObjMaxUserSize = 16 * 1024 * 1024; | |||
/* | /* | |||
Sometimes we need objects slightly larger - an object in the replica tion local.oplog | Sometimes we need objects slightly larger - an object in the replica tion local.oplog | |||
is slightly larger than a user object for example. | is slightly larger than a user object for example. | |||
*/ | */ | |||
const int BSONObjMaxInternalSize = BSONObjMaxUserSize + ( 16 * 1024 ); | const int BSONObjMaxInternalSize = BSONObjMaxUserSize + ( 16 * 1024 ); | |||
const int BufferMaxSize = 64 * 1024 * 1024; | const int BufferMaxSize = 64 * 1024 * 1024; | |||
class StringBuilder; | ||||
void msgasserted(int msgid, const char *msg); | void msgasserted(int msgid, const char *msg); | |||
template <typename Allocator> | ||||
class StringBuilderImpl; | ||||
class TrivialAllocator { | class TrivialAllocator { | |||
public: | public: | |||
void* Malloc(size_t sz) { return malloc(sz); } | void* Malloc(size_t sz) { return malloc(sz); } | |||
void* Realloc(void *p, size_t sz) { return realloc(p, sz); } | void* Realloc(void *p, size_t sz) { return realloc(p, sz); } | |||
void Free(void *p) { free(p); } | void Free(void *p) { free(p); } | |||
}; | }; | |||
class StackAllocator { | class StackAllocator { | |||
public: | public: | |||
enum { SZ = 512 }; | enum { SZ = 512 }; | |||
skipping to change at line 161 | skipping to change at line 170 | |||
void appendNum(int j) { | void appendNum(int j) { | |||
*((int*)grow(sizeof(int))) = j; | *((int*)grow(sizeof(int))) = j; | |||
} | } | |||
void appendNum(unsigned j) { | void appendNum(unsigned j) { | |||
*((unsigned*)grow(sizeof(unsigned))) = j; | *((unsigned*)grow(sizeof(unsigned))) = j; | |||
} | } | |||
void appendNum(bool j) { | void appendNum(bool j) { | |||
*((bool*)grow(sizeof(bool))) = j; | *((bool*)grow(sizeof(bool))) = j; | |||
} | } | |||
void appendNum(double j) { | void appendNum(double j) { | |||
*((double*)grow(sizeof(double))) = j; | (reinterpret_cast< PackedDouble* >(grow(sizeof(double))))->d = j; | |||
} | } | |||
void appendNum(long long j) { | void appendNum(long long j) { | |||
*((long long*)grow(sizeof(long long))) = j; | *((long long*)grow(sizeof(long long))) = j; | |||
} | } | |||
void appendNum(unsigned long long j) { | void appendNum(unsigned long long j) { | |||
*((unsigned long long*)grow(sizeof(unsigned long long))) = j; | *((unsigned long long*)grow(sizeof(unsigned long long))) = j; | |||
} | } | |||
void appendBuf(const void *src, size_t len) { | void appendBuf(const void *src, size_t len) { | |||
memcpy(grow((int) len), src, len); | memcpy(grow((int) len), src, len); | |||
skipping to change at line 212 | skipping to change at line 221 | |||
void NOINLINE_DECL grow_reallocate() { | void NOINLINE_DECL grow_reallocate() { | |||
int a = 64; | int a = 64; | |||
while( a < l ) | while( a < l ) | |||
a = a * 2; | a = a * 2; | |||
if ( a > BufferMaxSize ) { | if ( a > BufferMaxSize ) { | |||
std::stringstream ss; | std::stringstream ss; | |||
ss << "BufBuilder attempted to grow() to " << a << " bytes, past the 64MB limit."; | ss << "BufBuilder attempted to grow() to " << a << " bytes, past the 64MB limit."; | |||
msgasserted(13548, ss.str().c_str()); | msgasserted(13548, ss.str().c_str()); | |||
} | } | |||
data = (char *) al.Realloc(data, a); | data = (char *) al.Realloc(data, a); | |||
size= a; | if ( data == NULL ) | |||
msgasserted( 16070 , "out of memory BufBuilder::grow_reallo | ||||
cate" ); | ||||
size = a; | ||||
} | } | |||
char *data; | char *data; | |||
int l; | int l; | |||
int size; | int size; | |||
friend class StringBuilder; | friend class StringBuilderImpl<Allocator>; | |||
}; | }; | |||
typedef _BufBuilder<TrivialAllocator> BufBuilder; | typedef _BufBuilder<TrivialAllocator> BufBuilder; | |||
/** The StackBufBuilder builds smaller datasets on the stack instead of using malloc. | /** The StackBufBuilder builds smaller datasets on the stack instead of using malloc. | |||
this can be significantly faster for small bufs. However, you ca n not decouple() the | this can be significantly faster for small bufs. However, you ca n not decouple() the | |||
buffer with StackBufBuilder. | buffer with StackBufBuilder. | |||
While designed to be a variable on the stack, if you were to dynami cally allocate one, | While designed to be a variable on the stack, if you were to dynami cally allocate one, | |||
nothing bad would happen. In fact in some circumstances this mig ht make sense, say, | nothing bad would happen. In fact in some circumstances this mig ht make sense, say, | |||
embedded in some other object. | embedded in some other object. | |||
skipping to change at line 246 | skipping to change at line 257 | |||
namespace { | namespace { | |||
#if defined(_WIN32) | #if defined(_WIN32) | |||
int (*mongo_snprintf)(char *str, size_t size, const char *format, . ..) = &sprintf_s; | int (*mongo_snprintf)(char *str, size_t size, const char *format, . ..) = &sprintf_s; | |||
#else | #else | |||
int (*mongo_snprintf)(char *str, size_t size, const char *format, . ..) = &snprintf; | int (*mongo_snprintf)(char *str, size_t size, const char *format, . ..) = &snprintf; | |||
#endif | #endif | |||
} | } | |||
/** stringstream deals with locale so this is a lot faster than std::st ringstream for UTF8 */ | /** stringstream deals with locale so this is a lot faster than std::st ringstream for UTF8 */ | |||
class StringBuilder { | template <typename Allocator> | |||
class StringBuilderImpl { | ||||
public: | public: | |||
static const size_t MONGO_DBL_SIZE = 3 + DBL_MANT_DIG - DBL_MIN_EXP ; | static const size_t MONGO_DBL_SIZE = 3 + DBL_MANT_DIG - DBL_MIN_EXP ; | |||
static const size_t MONGO_S32_SIZE = 12; | static const size_t MONGO_S32_SIZE = 12; | |||
static const size_t MONGO_U32_SIZE = 11; | static const size_t MONGO_U32_SIZE = 11; | |||
static const size_t MONGO_S64_SIZE = 23; | static const size_t MONGO_S64_SIZE = 23; | |||
static const size_t MONGO_U64_SIZE = 22; | static const size_t MONGO_U64_SIZE = 22; | |||
static const size_t MONGO_S16_SIZE = 7; | static const size_t MONGO_S16_SIZE = 7; | |||
StringBuilder() { } | StringBuilderImpl() { } | |||
StringBuilder& operator<<( double x ) { | StringBuilderImpl& operator<<( double x ) { | |||
return SBNUM( x , MONGO_DBL_SIZE , "%g" ); | return SBNUM( x , MONGO_DBL_SIZE , "%g" ); | |||
} | } | |||
StringBuilder& operator<<( int x ) { | StringBuilderImpl& operator<<( int x ) { | |||
return SBNUM( x , MONGO_S32_SIZE , "%d" ); | return SBNUM( x , MONGO_S32_SIZE , "%d" ); | |||
} | } | |||
StringBuilder& operator<<( unsigned x ) { | StringBuilderImpl& operator<<( unsigned x ) { | |||
return SBNUM( x , MONGO_U32_SIZE , "%u" ); | return SBNUM( x , MONGO_U32_SIZE , "%u" ); | |||
} | } | |||
StringBuilder& operator<<( long x ) { | StringBuilderImpl& operator<<( long x ) { | |||
return SBNUM( x , MONGO_S64_SIZE , "%ld" ); | return SBNUM( x , MONGO_S64_SIZE , "%ld" ); | |||
} | } | |||
StringBuilder& operator<<( unsigned long x ) { | StringBuilderImpl& operator<<( unsigned long x ) { | |||
return SBNUM( x , MONGO_U64_SIZE , "%lu" ); | return SBNUM( x , MONGO_U64_SIZE , "%lu" ); | |||
} | } | |||
StringBuilder& operator<<( long long x ) { | StringBuilderImpl& operator<<( long long x ) { | |||
return SBNUM( x , MONGO_S64_SIZE , "%lld" ); | return SBNUM( x , MONGO_S64_SIZE , "%lld" ); | |||
} | } | |||
StringBuilder& operator<<( unsigned long long x ) { | StringBuilderImpl& operator<<( unsigned long long x ) { | |||
return SBNUM( x , MONGO_U64_SIZE , "%llu" ); | return SBNUM( x , MONGO_U64_SIZE , "%llu" ); | |||
} | } | |||
StringBuilder& operator<<( short x ) { | StringBuilderImpl& operator<<( short x ) { | |||
return SBNUM( x , MONGO_S16_SIZE , "%hd" ); | return SBNUM( x , MONGO_S16_SIZE , "%hd" ); | |||
} | } | |||
StringBuilder& operator<<( char c ) { | StringBuilderImpl& operator<<( char c ) { | |||
_buf.grow( 1 )[0] = c; | _buf.grow( 1 )[0] = c; | |||
return *this; | return *this; | |||
} | } | |||
void appendDoubleNice( double x ) { | void appendDoubleNice( double x ) { | |||
const int prev = _buf.l; | const int prev = _buf.l; | |||
const int maxSize = 32; | const int maxSize = 32; | |||
char * start = _buf.grow( maxSize ); | char * start = _buf.grow( maxSize ); | |||
int z = mongo_snprintf( start , maxSize , "%.16g" , x ); | int z = mongo_snprintf( start , maxSize , "%.16g" , x ); | |||
assert( z >= 0 ); | verify( z >= 0 ); | |||
assert( z < maxSize ); | verify( z < maxSize ); | |||
_buf.l = prev + z; | _buf.l = prev + z; | |||
if( strchr(start, '.') == 0 && strchr(start, 'E') == 0 && strch r(start, 'N') == 0 ) { | if( strchr(start, '.') == 0 && strchr(start, 'E') == 0 && strch r(start, 'N') == 0 ) { | |||
write( ".0" , 2 ); | write( ".0" , 2 ); | |||
} | } | |||
} | } | |||
void write( const char* buf, int len) { memcpy( _buf.grow( len ) , buf , len ); } | void write( const char* buf, int len) { memcpy( _buf.grow( len ) , buf , len ); } | |||
void append( const StringData& str ) { memcpy( _buf.grow( str.size( ) ) , str.data() , str.size() ); } | void append( const StringData& str ) { memcpy( _buf.grow( str.size( ) ) , str.data() , str.size() ); } | |||
StringBuilder& operator<<( const StringData& str ) { | StringBuilderImpl& operator<<( const StringData& str ) { | |||
append( str ); | append( str ); | |||
return *this; | return *this; | |||
} | } | |||
void reset( int maxSize = 0 ) { _buf.reset( maxSize ); } | void reset( int maxSize = 0 ) { _buf.reset( maxSize ); } | |||
std::string str() const { return std::string(_buf.data, _buf.l); } | std::string str() const { return std::string(_buf.data, _buf.l); } | |||
int len() const { return _buf.l; } | int len() const { return _buf.l; } | |||
private: | private: | |||
StackBufBuilder _buf; | _BufBuilder<Allocator> _buf; | |||
// non-copyable, non-assignable | // non-copyable, non-assignable | |||
StringBuilder( const StringBuilder& ); | StringBuilderImpl( const StringBuilderImpl& ); | |||
StringBuilder& operator=( const StringBuilder& ); | StringBuilderImpl& operator=( const StringBuilderImpl& ); | |||
template <typename T> | template <typename T> | |||
StringBuilder& SBNUM(T val,int maxSize,const char *macro) { | StringBuilderImpl& SBNUM(T val,int maxSize,const char *macro) { | |||
int prev = _buf.l; | int prev = _buf.l; | |||
int z = mongo_snprintf( _buf.grow(maxSize) , maxSize , macro , (val) ); | int z = mongo_snprintf( _buf.grow(maxSize) , maxSize , macro , (val) ); | |||
assert( z >= 0 ); | verify( z >= 0 ); | |||
assert( z < maxSize ); | verify( z < maxSize ); | |||
_buf.l = prev + z; | _buf.l = prev + z; | |||
return *this; | return *this; | |||
} | } | |||
}; | }; | |||
typedef StringBuilderImpl<TrivialAllocator> StringBuilder; | ||||
typedef StringBuilderImpl<StackAllocator> StackStringBuilder; | ||||
} // namespace mongo | } // namespace mongo | |||
End of changes. 24 change blocks. | ||||
25 lines changed or deleted | 44 lines changed or added | |||
chunk.h | chunk.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 "../pch.h" | #include "../pch.h" | |||
#include "../bson/util/atomic_int.h" | #include "../bson/util/atomic_int.h" | |||
#include "../client/dbclient.h" | ||||
#include "../client/distlock.h" | #include "../client/distlock.h" | |||
#include "shardkey.h" | #include "shardkey.h" | |||
#include "shard.h" | #include "shard.h" | |||
#include "util.h" | #include "util.h" | |||
#include "mongo/util/concurrency/ticketholder.h" | ||||
namespace mongo { | namespace mongo { | |||
class DBConfig; | class DBConfig; | |||
class Chunk; | class Chunk; | |||
class ChunkRange; | class ChunkRange; | |||
class ChunkManager; | class ChunkManager; | |||
class ChunkObjUnitTest; | class ChunkObjUnitTest; | |||
typedef shared_ptr<const Chunk> ChunkPtr; | typedef shared_ptr<const Chunk> ChunkPtr; | |||
skipping to change at line 237 | skipping to change at line 237 | |||
const BSONObj& getMax() const { return _max; } | const BSONObj& getMax() const { return _max; } | |||
// clones of Chunk methods | // clones of Chunk methods | |||
bool contains(const BSONObj& obj) const; | bool contains(const BSONObj& obj) const; | |||
ChunkRange(ChunkMap::const_iterator begin, const ChunkMap::const_it erator end) | ChunkRange(ChunkMap::const_iterator begin, const ChunkMap::const_it erator end) | |||
: _manager(begin->second->getManager()) | : _manager(begin->second->getManager()) | |||
, _shard(begin->second->getShard()) | , _shard(begin->second->getShard()) | |||
, _min(begin->second->getMin()) | , _min(begin->second->getMin()) | |||
, _max(boost::prior(end)->second->getMax()) { | , _max(boost::prior(end)->second->getMax()) { | |||
assert( begin != end ); | verify( begin != end ); | |||
DEV while (begin != end) { | DEV while (begin != end) { | |||
assert(begin->second->getManager() == _manager); | verify(begin->second->getManager() == _manager); | |||
assert(begin->second->getShard() == _shard); | verify(begin->second->getShard() == _shard); | |||
++begin; | ++begin; | |||
} | } | |||
} | } | |||
// Merge min and max (must be adjacent ranges) | // Merge min and max (must be adjacent ranges) | |||
ChunkRange(const ChunkRange& min, const ChunkRange& max) | ChunkRange(const ChunkRange& min, const ChunkRange& max) | |||
: _manager(min.getManager()) | : _manager(min.getManager()) | |||
, _shard(min.getShard()) | , _shard(min.getShard()) | |||
, _min(min.getMin()) | , _min(min.getMin()) | |||
, _max(max.getMax()) { | , _max(max.getMax()) { | |||
assert(min.getShard() == max.getShard()); | verify(min.getShard() == max.getShard()); | |||
assert(min.getManager() == max.getManager()); | verify(min.getManager() == max.getManager()); | |||
assert(min.getMax() == max.getMin()); | verify(min.getMax() == max.getMin()); | |||
} | } | |||
friend ostream& operator<<(ostream& out, const ChunkRange& cr) { | friend ostream& operator<<(ostream& out, const ChunkRange& cr) { | |||
return (out << "ChunkRange(min=" << cr._min << ", max=" << cr._ max << ", shard=" << cr._shard <<")"); | return (out << "ChunkRange(min=" << cr._min << ", max=" << cr._ max << ", shard=" << cr._shard <<")"); | |||
} | } | |||
private: | private: | |||
const ChunkManager* _manager; | const ChunkManager* _manager; | |||
const Shard _shard; | const Shard _shard; | |||
const BSONObj _min; | const BSONObj _min; | |||
skipping to change at line 315 | skipping to change at line 315 | |||
void createFirstChunks( const Shard& primary , vector<BSONObj>* ini tPoints , vector<Shard>* initShards ) const; // only call from DBConfig::sh ardCollection | void createFirstChunks( const Shard& primary , vector<BSONObj>* ini tPoints , vector<Shard>* initShards ) const; // only call from DBConfig::sh ardCollection | |||
ChunkPtr findChunk( const BSONObj& obj ) const; | ChunkPtr findChunk( const BSONObj& obj ) const; | |||
ChunkPtr findChunkOnServer( const Shard& shard ) const; | ChunkPtr findChunkOnServer( const Shard& shard ) const; | |||
const ShardKeyPattern& getShardKey() const { return _key; } | const ShardKeyPattern& getShardKey() const { return _key; } | |||
bool isUnique() const { return _unique; } | bool isUnique() const { return _unique; } | |||
void getShardsForQuery( set<Shard>& shards , const BSONObj& query ) const; | void getShardsForQuery( set<Shard>& shards , const BSONObj& query ) const; | |||
void getAllShards( set<Shard>& all ) const; | void getAllShards( set<Shard>& all ) const; | |||
void getShardsForRange(set<Shard>& shards, const BSONObj& min, cons | /** @param shards set to the shards covered by the interval [min, m | |||
t BSONObj& max, bool fullKeyReq = true) const; // [min, max) | ax], see SERVER-4791 */ | |||
void getShardsForRange(set<Shard>& shards, const BSONObj& min, cons | ||||
t BSONObj& max, bool fullKeyReq = true) const; | ||||
ChunkMap getChunkMap() const { return _chunkMap; } | ChunkMap getChunkMap() const { return _chunkMap; } | |||
/** | /** | |||
* Returns true if, for this shard, the chunks are identical in bot h chunk managers | * Returns true if, for this shard, the chunks are identical in bot h chunk managers | |||
*/ | */ | |||
bool compatibleWith( const ChunkManager& other, const Shard& shard ) const; | bool compatibleWith( const ChunkManager& other, const Shard& shard ) const; | |||
bool compatibleWith( ChunkManagerPtr other, const Shard& shard ) co nst { if( ! other ) return false; return compatibleWith( *other, shard ); } | bool compatibleWith( ChunkManagerPtr other, const Shard& shard ) co nst { if( ! other ) return false; return compatibleWith( *other, shard ); } | |||
bool compatibleWith( const Chunk& other ) const; | bool compatibleWith( const Chunk& other ) const; | |||
skipping to change at line 383 | skipping to change at line 384 | |||
mutable mutex _mutex; // only used with _nsLock | mutable mutex _mutex; // only used with _nsLock | |||
mutable DistributedLock _nsLock; | mutable DistributedLock _nsLock; | |||
const unsigned long long _sequenceNumber; | const unsigned long long _sequenceNumber; | |||
mutable TicketHolder _splitTickets; // number of concurrent splitVe ctor we can do from a splitIfShould per collection | mutable TicketHolder _splitTickets; // number of concurrent splitVe ctor we can do from a splitIfShould per collection | |||
friend class Chunk; | friend class Chunk; | |||
friend class ChunkRangeManager; // only needed for CRM::assertValid () | friend class ChunkRangeManager; // only needed for CRM::assertValid () | |||
static AtomicUInt NextSequenceNumber; | static AtomicUInt NextSequenceNumber; | |||
/** Just for testing */ | ||||
friend class TestableChunkManager; | ||||
ChunkManager(); | ||||
}; | }; | |||
// 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()); | |||
} | } | |||
End of changes. 7 change blocks. | ||||
9 lines changed or deleted | 15 lines changed or added | |||
client.h | client.h | |||
---|---|---|---|---|
skipping to change at line 37 | skipping to change at line 37 | |||
#include "../pch.h" | #include "../pch.h" | |||
#include "security.h" | #include "security.h" | |||
#include "namespace-inl.h" | #include "namespace-inl.h" | |||
#include "lasterror.h" | #include "lasterror.h" | |||
#include "stats/top.h" | #include "stats/top.h" | |||
#include "../db/client_common.h" | #include "../db/client_common.h" | |||
#include "../util/concurrency/threadlocal.h" | #include "../util/concurrency/threadlocal.h" | |||
#include "../util/net/message_port.h" | #include "../util/net/message_port.h" | |||
#include "../util/concurrency/rwlock.h" | #include "../util/concurrency/rwlock.h" | |||
#include "d_concurrency.h" | #include "d_concurrency.h" | |||
#include "mongo/util/paths.h" | ||||
namespace mongo { | namespace mongo { | |||
extern class ReplSet *theReplSet; | extern class ReplSet *theReplSet; | |||
class AuthenticationInfo; | class AuthenticationInfo; | |||
class Database; | class Database; | |||
class CurOp; | class CurOp; | |||
class Command; | class Command; | |||
class Client; | class Client; | |||
class AbstractMessagingPort; | class AbstractMessagingPort; | |||
class LockCollectionForReading; | class LockCollectionForReading; | |||
class PageFaultRetryableSection; | class PageFaultRetryableSection; | |||
#if defined(CLC) | ||||
typedef LockCollectionForReading _LockCollectionForReading; | ||||
#else | ||||
typedef readlock _LockCollectionForReading; | ||||
#endif | ||||
TSP_DECLARE(Client, currentClient) | TSP_DECLARE(Client, currentClient) | |||
typedef long long ConnectionId; | typedef long long ConnectionId; | |||
/** the database's concept of an outside "client" */ | /** the database's concept of an outside "client" */ | |||
class Client : public ClientBasic { | class Client : public ClientBasic { | |||
static Client *syncThread; | static Client *syncThread; | |||
public: | public: | |||
LockState _ls; | ||||
// always be in clientsMutex when manipulating this. killop stuff u ses these. | // always be in clientsMutex when manipulating this. killop stuff u ses these. | |||
static set<Client*> clients; | static set<Client*>& clients; | |||
static mongo::mutex clientsMutex; | static mongo::mutex& clientsMutex; | |||
static int getActiveClientCount( int& writers , int& readers ); | static int getActiveClientCount( int& writers , int& readers ); | |||
class Context; | class Context; | |||
~Client(); | ~Client(); | |||
static int recommendedYieldMicros( int * writers = 0 , int * reader s = 0 ); | static int recommendedYieldMicros( int * writers = 0 , int * reader s = 0 ); | |||
/** 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. | |||
*/ | */ | |||
static Client& initThread(const char *desc, AbstractMessagingPort * mp = 0); | static Client& initThread(const char *desc, AbstractMessagingPort * mp = 0); | |||
skipping to change at line 104 | skipping to change at line 101 | |||
bool isSyncThread() const { return this == syncThread; } | bool isSyncThread() const { return this == syncThread; } | |||
string clientAddress(bool includePort=false) const; | string clientAddress(bool includePort=false) const; | |||
const AuthenticationInfo * getAuthenticationInfo() const { return & _ai; } | const AuthenticationInfo * getAuthenticationInfo() const { return & _ai; } | |||
AuthenticationInfo * getAuthenticationInfo() { return &_ai; } | AuthenticationInfo * getAuthenticationInfo() { return &_ai; } | |||
bool isAdmin() { return _ai.isAuthorized( "admin" ); } | bool isAdmin() { return _ai.isAuthorized( "admin" ); } | |||
CurOp* curop() const { return _curOp; } | CurOp* curop() const { return _curOp; } | |||
Context* getContext() const { return _context; } | Context* getContext() const { return _context; } | |||
Database* database() const { return _context ? _context->db() : 0; } | Database* database() const { return _context ? _context->db() : 0; } | |||
const char *ns() const { return _context->ns(); } | const char *ns() const { return _context->ns(); } | |||
const char *desc() const { return _desc; } | const std::string desc() const { return _desc; } | |||
void setLastOp( OpTime op ) { _lastOp = op; } | void setLastOp( OpTime op ) { _lastOp = op; } | |||
OpTime getLastOp() const { return _lastOp; } | OpTime getLastOp() const { return _lastOp; } | |||
/** caution -- use Context class instead */ | /** caution -- use Context class instead */ | |||
void setContext(Context *c) { _context = c; } | void setContext(Context *c) { _context = c; } | |||
/* report what the last operation was. used by getlasterror */ | /* report what the last operation was. used by getlasterror */ | |||
void appendLastOp( BSONObjBuilder& b ) const; | void appendLastOp( BSONObjBuilder& b ) const; | |||
bool isGod() const { return _god; } /* this is for map/reduce write s */ | bool isGod() const { return _god; } /* this is for map/reduce write s */ | |||
string toString() const; | string toString() const; | |||
void gotHandshake( const BSONObj& o ); | void gotHandshake( const BSONObj& o ); | |||
bool hasRemote() const { return _mp; } | bool hasRemote() const { return _mp; } | |||
HostAndPort getRemote() const { assert( _mp ); return _mp->remote() ; } | HostAndPort getRemote() const { verify( _mp ); return _mp->remote() ; } | |||
BSONObj getRemoteID() const { return _remoteId; } | BSONObj getRemoteID() const { return _remoteId; } | |||
BSONObj getHandshake() const { return _handshake; } | BSONObj getHandshake() const { return _handshake; } | |||
AbstractMessagingPort * port() const { return _mp; } | AbstractMessagingPort * port() const { return _mp; } | |||
ConnectionId getConnectionId() const { return _connectionId; } | ConnectionId getConnectionId() const { return _connectionId; } | |||
bool inPageFaultRetryableSection() const { return _pageFaultRetryab leSection != 0; } | bool inPageFaultRetryableSection() const { return _pageFaultRetryab leSection != 0; } | |||
PageFaultRetryableSection* getPageFaultRetryableSection() const { r eturn _pageFaultRetryableSection; } | PageFaultRetryableSection* getPageFaultRetryableSection() const { r eturn _pageFaultRetryableSection; } | |||
bool hasWrittenThisPass() const { return _hasWrittenThisPass; } | bool hasWrittenThisPass() const { return _hasWrittenThisPass; } | |||
void writeHappened() { _hasWrittenThisPass = true; } | void writeHappened() { _hasWrittenThisPass = true; } | |||
void newTopLevelRequest() { _hasWrittenThisPass = false; } | ||||
bool allowedToThrowPageFaultException() const; | bool allowedToThrowPageFaultException() const; | |||
private: | private: | |||
Client(const char *desc, AbstractMessagingPort *p = 0); | Client(const char *desc, AbstractMessagingPort *p = 0); | |||
friend class CurOp; | friend class CurOp; | |||
ConnectionId _connectionId; // > 0 for things "conn", 0 otherwise | ConnectionId _connectionId; // > 0 for things "conn", 0 otherwise | |||
string _threadId; // "" on non support systems | string _threadId; // "" on non support systems | |||
CurOp * _curOp; | CurOp * _curOp; | |||
Context * _context; | Context * _context; | |||
bool _shutdown; // to track if Client::shutdown() gets called | bool _shutdown; // to track if Client::shutdown() gets called | |||
const char * const _desc; | const std::string _desc; | |||
bool _god; | bool _god; | |||
AuthenticationInfo _ai; | AuthenticationInfo _ai; | |||
OpTime _lastOp; | OpTime _lastOp; | |||
BSONObj _handshake; | BSONObj _handshake; | |||
BSONObj _remoteId; | BSONObj _remoteId; | |||
AbstractMessagingPort * const _mp; | AbstractMessagingPort * const _mp; | |||
unsigned _sometimes; | unsigned _sometimes; | |||
bool _hasWrittenThisPass; | bool _hasWrittenThisPass; | |||
PageFaultRetryableSection *_pageFaultRetryableSection; | PageFaultRetryableSection *_pageFaultRetryableSection; | |||
skipping to change at line 180 | skipping to change at line 178 | |||
//static void assureDatabaseIsOpen(const string& ns, string path=db path); | //static void assureDatabaseIsOpen(const string& ns, string path=db path); | |||
/** "read lock, and set my context, all in one operation" | /** "read lock, and set my context, all in one operation" | |||
* This handles (if not recursively locked) opening an unopened da tabase. | * This handles (if not recursively locked) opening an unopened da tabase. | |||
*/ | */ | |||
class ReadContext : boost::noncopyable { | class ReadContext : boost::noncopyable { | |||
public: | public: | |||
ReadContext(const string& ns, string path=dbpath, bool doauth=t rue ); | ReadContext(const string& ns, string path=dbpath, bool doauth=t rue ); | |||
Context& ctx() { return *c.get(); } | Context& ctx() { return *c.get(); } | |||
private: | private: | |||
scoped_ptr<_LockCollectionForReading> lk; | scoped_ptr<Lock::DBRead> lk; | |||
scoped_ptr<Context> c; | scoped_ptr<Context> c; | |||
}; | }; | |||
/* Set database we want to use, then, restores when we finish (are out of scope) | /* Set database we want to use, then, restores when we finish (are out of scope) | |||
Note this is also helpful if an exception happens as the state i f fixed up. | Note this is also helpful if an exception happens as the state i f fixed up. | |||
*/ | */ | |||
class Context : boost::noncopyable { | class Context : boost::noncopyable { | |||
public: | public: | |||
/** this is probably what you want */ | /** this is probably what you want */ | |||
Context(const string& ns, string path=dbpath, bool doauth=true, bool doVersion=true ); | Context(const string& ns, string path=dbpath, bool doauth=true, bool doVersion=true ); | |||
skipping to change at line 214 | skipping to change at line 212 | |||
const char * ns() const { return _ns.c_str(); } | const char * ns() const { return _ns.c_str(); } | |||
bool equals( const string& ns , const string& path=dbpath ) con st { return _ns == ns && _path == path; } | bool equals( const string& ns , const string& path=dbpath ) con st { return _ns == ns && _path == path; } | |||
/** @return if the db was created by this Context */ | /** @return if the db was created by this Context */ | |||
bool justCreated() const { return _justCreated; } | bool justCreated() const { return _justCreated; } | |||
/** @return true iff the current Context is using db/path */ | /** @return true iff the current Context is using db/path */ | |||
bool inDB( const string& db , const string& path=dbpath ) const ; | bool inDB( const string& db , const string& path=dbpath ) const ; | |||
void _clear() { // this is sort of an "early destruct" indicati on, _ns can never be uncleared | void _clear() { // this is sort of an "early destruct" indicati on, _ns can never be uncleared | |||
const_cast<string&>(_ns).empty(); | const_cast<string&>(_ns).clear(); | |||
_db = 0; | _db = 0; | |||
} | } | |||
/** call before unlocking, so clear any non-thread safe state | /** call before unlocking, so clear any non-thread safe state | |||
* _db gets restored on the relock | * _db gets restored on the relock | |||
*/ | */ | |||
void unlocked() { _db = 0; } | void unlocked() { _db = 0; } | |||
/** call after going back into the lock, will re-establish non- thread safe stuff */ | /** call after going back into the lock, will re-establish non- thread safe stuff */ | |||
void relocked() { _finishInit(); } | void relocked() { _finishInit(); } | |||
private: | private: | |||
friend class CurOp; | friend class CurOp; | |||
void _finishInit( bool doauth=true); | void _finishInit( bool doauth=true); | |||
void _auth( int lockState ); | void _auth( int lockState ); | |||
void checkNotStale() const; | void checkNotStale() const; | |||
void checkNsAccess( bool doauth, int lockState = d.dbMutex.getS | void checkNsAccess( bool doauth ); | |||
tate() ); | void checkNsAccess( bool doauth, int lockState ); | |||
Client * const _client; | Client * const _client; | |||
Context * const _oldContext; | Context * const _oldContext; | |||
const string _path; | const string _path; | |||
bool _justCreated; | bool _justCreated; | |||
bool _doVersion; | bool _doVersion; | |||
const string _ns; | const string _ns; | |||
Database * _db; | Database * _db; | |||
}; // class Client::Context | }; // class Client::Context | |||
struct LockStatus { | class WriteContext : boost::noncopyable { | |||
LockStatus(); | public: | |||
string whichCollection; | WriteContext(const string& ns, string path=dbpath, bool doauth= | |||
unsigned excluder, global, collection; | true ); | |||
string toString() const; | Context& ctx() { return _c; } | |||
} lockStatus; | private: | |||
Lock::DBWrite _lk; | ||||
#if defined(CLC) | Context _c; | |||
void checkLocks() const; | }; | |||
#else | ||||
void checkLocks() const { } | ||||
#endif | ||||
}; // class Client | }; // class Client | |||
/** get the Client object for this thread. */ | /** get the Client object for this thread. */ | |||
inline Client& cc() { | inline Client& cc() { | |||
Client * c = currentClient.get(); | Client * c = currentClient.get(); | |||
assert( c ); | verify( c ); | |||
return *c; | return *c; | |||
} | } | |||
inline Client::GodScope::GodScope() { | inline Client::GodScope::GodScope() { | |||
_prev = cc()._god; | _prev = cc()._god; | |||
cc()._god = true; | cc()._god = true; | |||
} | } | |||
inline Client::GodScope::~GodScope() { cc()._god = _prev; } | inline Client::GodScope::~GodScope() { cc()._god = _prev; } | |||
/* this unreadlocks and then writelocks; i.e. it does NOT upgrade insid | ||||
e the | ||||
lock (and is thus wrong to use if you need that, which is usually). | ||||
that said we use it today for a specific case where the usage is cor | ||||
rect. | ||||
*/ | ||||
#if 0 | ||||
inline void mongolock::releaseAndWriteLock() { | ||||
if( !_writelock ) { | ||||
#if BOOST_VERSION >= 103500 | ||||
int s = d.dbMutex.getState(); | ||||
if( s != -1 ) { | ||||
log() << "error: releaseAndWriteLock() s == " << s << endl; | ||||
msgasserted( 12600, "releaseAndWriteLock: unlock_shared fai | ||||
led, probably recursive" ); | ||||
} | ||||
#endif | ||||
_writelock = true; | ||||
d.dbMutex.unlock_shared(); | ||||
d.dbMutex.lock(); | ||||
// todo: unlocked() method says to call it before unlocking, no | ||||
t after. so fix this here, | ||||
// or fix the doc there. | ||||
if ( cc().getContext() ) | ||||
cc().getContext()->unlocked(); | ||||
} | ||||
} | ||||
#endif | ||||
inline bool haveClient() { return currentClient.get() > 0; } | inline bool haveClient() { return currentClient.get() > 0; } | |||
}; | }; | |||
End of changes. 14 change blocks. | ||||
60 lines changed or deleted | 23 lines changed or added | |||
clientOnly-private.h | clientOnly-private.h | |||
---|---|---|---|---|
#pragma once | #pragma once | |||
#include "db/mongomutex.h" | #include "db/mongomutex.h" | |||
namespace mongo { | namespace mongo { | |||
namespace shellUtils { | namespace shell_utils { | |||
extern mongo::mutex &mongoProgramOutputMutex; | extern mongo::mutex &mongoProgramOutputMutex; | |||
} | } | |||
} | } | |||
End of changes. 1 change blocks. | ||||
1 lines changed or deleted | 1 lines changed or added | |||
clientcursor.h | clientcursor.h | |||
---|---|---|---|---|
skipping to change at line 27 | skipping to change at line 27 | |||
*/ | */ | |||
/* Cursor -- and its derived classes -- are our internal cursors. | /* Cursor -- and its derived classes -- are our internal cursors. | |||
ClientCursor is a wrapper that represents a cursorid from our database | ClientCursor is a wrapper that represents a cursorid from our database | |||
application's perspective. | application's perspective. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../pch.h" | #include "pch.h" | |||
#include <boost/thread/recursive_mutex.hpp> | ||||
#include "cursor.h" | #include "cursor.h" | |||
#include "jsobj.h" | #include "jsobj.h" | |||
#include "../util/net/message.h" | #include "../util/net/message.h" | |||
#include "../util/net/listen.h" | #include "../util/net/listen.h" | |||
#include "../util/background.h" | #include "../util/background.h" | |||
#include "diskloc.h" | #include "diskloc.h" | |||
#include "dbhelpers.h" | #include "dbhelpers.h" | |||
#include "matcher.h" | #include "matcher.h" | |||
#include "../client/dbclient.h" | ||||
#include "projection.h" | #include "projection.h" | |||
#include "s/d_chunk_manager.h" | #include "s/d_chunk_manager.h" | |||
namespace mongo { | namespace mongo { | |||
typedef boost::recursive_mutex::scoped_lock recursive_scoped_lock; | ||||
typedef long long CursorId; /* passed to the client so it can send back on getMore */ | typedef long long CursorId; /* passed to the client so it can send back on getMore */ | |||
class Cursor; /* internal server cursor base class */ | class Cursor; /* internal server cursor base class */ | |||
class ClientCursor; | class ClientCursor; | |||
class ParsedQuery; | class ParsedQuery; | |||
struct ByLocKey { | struct ByLocKey { | |||
ByLocKey( const DiskLoc & l , const CursorId& i ) : loc(l), id(i) { } | ByLocKey( const DiskLoc & l , const CursorId& i ) : loc(l), id(i) { } | |||
static ByLocKey min( const DiskLoc& l ) { return ByLocKey( l , nume ric_limits<long long>::min() ); } | static ByLocKey min( const DiskLoc& l ) { return ByLocKey( l , nume ric_limits<long long>::min() ); } | |||
skipping to change at line 74 | skipping to change at line 77 | |||
}; | }; | |||
/* todo: make this map be per connection. this will prevent cursor hij acking security attacks perhaps. | /* todo: make this map be per connection. this will prevent cursor hij acking security attacks perhaps. | |||
* ERH: 9/2010 this may not work since some drivers send getMore over a different connection | * ERH: 9/2010 this may not work since some drivers send getMore over a different connection | |||
*/ | */ | |||
typedef map<CursorId, ClientCursor*> CCById; | typedef map<CursorId, ClientCursor*> CCById; | |||
typedef map<ByLocKey, ClientCursor*> CCByLoc; | typedef map<ByLocKey, ClientCursor*> CCByLoc; | |||
extern BSONObj id_obj; | extern BSONObj id_obj; | |||
class ClientCursor { | class ClientCursor : private boost::noncopyable { | |||
friend class CmdCursorInfo; | friend class CmdCursorInfo; | |||
public: | public: | |||
static void assertNoCursors(); | static void assertNoCursors(); | |||
/* use this to assure we don't in the background time out cursor wh ile it is under use. | /* use this to assure we don't in the background time out cursor wh ile it is under use. | |||
if you are using noTimeout() already, there is no risk anyway. | if you are using noTimeout() already, there is no risk anyway. | |||
Further, this mechanism guards against two getMore requests on t he same cursor executing | Further, this mechanism guards against two getMore requests on t he same cursor executing | |||
at the same time - which might be bad. That should never happen , but if a client driver | at the same time - which might be bad. That should never happen , but if a client driver | |||
had a bug, it could (or perhaps some sort of attack situation). | had a bug, it could (or perhaps some sort of attack situation). | |||
*/ | */ | |||
class Pointer : boost::noncopyable { | class Pointer : boost::noncopyable { | |||
ClientCursor *_c; | ClientCursor *_c; | |||
public: | public: | |||
ClientCursor * c() { return _c; } | ClientCursor * c() { return _c; } | |||
void release() { | void release() { | |||
if( _c ) { | if( _c ) { | |||
assert( _c->_pinValue >= 100 ); | verify( _c->_pinValue >= 100 ); | |||
_c->_pinValue -= 100; | _c->_pinValue -= 100; | |||
_c = 0; | _c = 0; | |||
} | } | |||
} | } | |||
/** | /** | |||
* call this if during a yield, the cursor got deleted | * call this if during a yield, the cursor got deleted | |||
* if so, we don't want to use the point address | * if so, we don't want to use the point address | |||
*/ | */ | |||
void deleted() { | void deleted() { | |||
_c = 0; | _c = 0; | |||
skipping to change at line 142 | skipping to change at line 145 | |||
else { | else { | |||
_c = 0; | _c = 0; | |||
_id = -1; | _id = -1; | |||
} | } | |||
} | } | |||
~CleanupPointer() { | ~CleanupPointer() { | |||
DESTRUCTOR_GUARD ( reset(); ); | DESTRUCTOR_GUARD ( reset(); ); | |||
} | } | |||
operator bool() { return _c; } | operator bool() { return _c; } | |||
ClientCursor * operator-> () { return _c; } | ClientCursor * operator-> () { return _c; } | |||
/** Release ownership of the ClientCursor. */ | ||||
void release() { | ||||
_c = 0; | ||||
_id = -1; | ||||
} | ||||
private: | private: | |||
ClientCursor *_c; | ClientCursor *_c; | |||
CursorId _id; | CursorId _id; | |||
}; | }; | |||
/** | /** | |||
* Iterates through all ClientCursors, under its own ccmutex lock. | * Iterates through all ClientCursors, under its own ccmutex lock. | |||
* Also supports deletion on the fly. | * Also supports deletion on the fly. | |||
*/ | */ | |||
class LockedIterator : boost::noncopyable { | class LockedIterator : boost::noncopyable { | |||
skipping to change at line 220 | skipping to change at line 228 | |||
bool yieldSometimes( RecordNeeds need, bool *yielded = 0 ); | bool yieldSometimes( RecordNeeds need, bool *yielded = 0 ); | |||
static int suggestYieldMicros(); | static int suggestYieldMicros(); | |||
static void staticYield( int micros , const StringData& ns , Record * rec ); | static void staticYield( int micros , const StringData& ns , Record * rec ); | |||
struct YieldData { CursorId _id; bool _doingDeletes; }; | struct YieldData { CursorId _id; bool _doingDeletes; }; | |||
bool prepareToYield( YieldData &data ); | bool prepareToYield( YieldData &data ); | |||
static bool recoverFromYield( const YieldData &data ); | static bool recoverFromYield( const YieldData &data ); | |||
struct YieldLock : boost::noncopyable { | struct YieldLock : boost::noncopyable { | |||
explicit YieldLock( ptr<ClientCursor> cc ) | ||||
: _canYield(cc->_c->supportYields()) { | explicit YieldLock( ptr<ClientCursor> cc ); | |||
if ( _canYield ) { | ||||
cc->prepareToYield( _data ); | ~YieldLock(); | |||
_unlock.reset(new dbtempreleasecond()); | ||||
} | /** | |||
} | * @return if the cursor is still ok | |||
~YieldLock() { | * if it is, we also relock | |||
if ( _unlock ) { | */ | |||
log( LL_WARNING ) << "ClientCursor::YieldLock not close | bool stillOk(); | |||
d properly" << endl; | ||||
relock(); | void relock(); | |||
} | ||||
} | ||||
bool stillOk() { | ||||
if ( ! _canYield ) | ||||
return true; | ||||
relock(); | ||||
return ClientCursor::recoverFromYield( _data ); | ||||
} | ||||
void relock() { | ||||
_unlock.reset(); | ||||
} | ||||
private: | private: | |||
const bool _canYield; | const bool _canYield; | |||
YieldData _data; | YieldData _data; | |||
scoped_ptr<dbtempreleasecond> _unlock; | scoped_ptr<dbtempreleasecond> _unlock; | |||
}; | }; | |||
// --- some pass through helpers for Cursor --- | // --- some pass through helpers for Cursor --- | |||
Cursor* c() const { return _c.get(); } | Cursor* c() const { return _c.get(); } | |||
int pos() const { return _pos; } | int pos() const { return _pos; } | |||
skipping to change at line 290 | skipping to change at line 289 | |||
BSONElement getFieldDotted( const string& name , BSONObj& holder , bool * fromKey = 0 ) ; | BSONElement getFieldDotted( const string& name , BSONObj& holder , bool * fromKey = 0 ) ; | |||
/** extract items from object which match a pattern object. | /** extract items from object which match a pattern object. | |||
* e.g., if pattern is { x : 1, y : 1 }, builds an object with | * e.g., if pattern is { x : 1, y : 1 }, builds an object with | |||
* x and y elements of this object, if they are present. | * x and y elements of this object, if they are present. | |||
* returns elements with original field names | * returns elements with original field names | |||
* NOTE: copied from BSONObj::extractFields | * NOTE: copied from BSONObj::extractFields | |||
*/ | */ | |||
BSONObj extractFields(const BSONObj &pattern , bool fillWithNull = false) ; | BSONObj extractFields(const BSONObj &pattern , bool fillWithNull = false) ; | |||
void fillQueryResultFromObj( BufBuilder &b ) const; | ||||
bool currentIsDup() { return _c->getsetdup( _c->currLoc() ); } | bool currentIsDup() { return _c->getsetdup( _c->currLoc() ); } | |||
bool currentMatches() { | bool currentMatches() { | |||
if ( ! _c->matcher() ) | if ( ! _c->matcher() ) | |||
return true; | return true; | |||
return _c->matcher()->matchesCurrent( _c.get() ); | return _c->matcher()->matchesCurrent( _c.get() ); | |||
} | } | |||
void setChunkManager( ShardChunkManagerPtr manager ){ _chunkManager = manager; } | void setChunkManager( ShardChunkManagerPtr manager ){ _chunkManager = manager; } | |||
ShardChunkManagerPtr getChunkManager(){ return _chunkManager; } | ShardChunkManagerPtr getChunkManager(){ return _chunkManager; } | |||
skipping to change at line 313 | skipping to change at line 314 | |||
static ClientCursor* find_inlock(CursorId id, bool warn = true) { | static ClientCursor* find_inlock(CursorId id, bool warn = true) { | |||
CCById::iterator it = clientCursorsById.find(id); | CCById::iterator it = clientCursorsById.find(id); | |||
if ( it == clientCursorsById.end() ) { | if ( it == clientCursorsById.end() ) { | |||
if ( warn ) | if ( warn ) | |||
OCCASIONALLY out() << "ClientCursor::find(): cursor not found in map " << id << " (ok after a drop)\n"; | OCCASIONALLY out() << "ClientCursor::find(): cursor not found in map " << id << " (ok after a drop)\n"; | |||
return 0; | return 0; | |||
} | } | |||
return it->second; | return it->second; | |||
} | } | |||
/* call when cursor's location changes so that we can update the | ||||
cursorsbylocation map. if you are locked and internally iterating | ||||
, only | ||||
need to call when you are ready to "unlock". | ||||
*/ | ||||
void updateLocation(); | ||||
public: | public: | |||
static ClientCursor* find(CursorId id, bool warn = true) { | static ClientCursor* find(CursorId id, bool warn = true) { | |||
recursive_scoped_lock lock(ccmutex); | recursive_scoped_lock lock(ccmutex); | |||
ClientCursor *c = find_inlock(id, warn); | ClientCursor *c = find_inlock(id, warn); | |||
// if this asserts, your code was not thread safe - you either need to set no timeout | // if this asserts, your code was not thread safe - you either need to set no timeout | |||
// for the cursor or keep a ClientCursor::Pointer in scope for it. | // for the cursor or keep a ClientCursor::Pointer in scope for it. | |||
massert( 12521, "internal error: use of an unlocked ClientCurso r", c == 0 || c->_pinValue ); | massert( 12521, "internal error: use of an unlocked ClientCurso r", c == 0 || c->_pinValue ); | |||
return c; | return c; | |||
} | } | |||
/** | /** | |||
* Deletes the cursor with the provided @param 'id' if one exists. | * Deletes the cursor with the provided @param 'id' if one exists. | |||
* @throw if the cursor with the provided id is pinned. | * @throw if the cursor with the provided id is pinned. | |||
*/ | */ | |||
static bool erase(CursorId id) { | static bool erase(CursorId id); | |||
recursive_scoped_lock lock(ccmutex); | ||||
ClientCursor *cc = find_inlock(id); | ||||
if ( cc ) { | ||||
assert( cc->_pinValue < 100 ); // you can't still have an a | ||||
ctive ClientCursor::Pointer | ||||
delete cc; | ||||
return true; | ||||
} | ||||
return false; | ||||
} | ||||
/** | /** | |||
* @return number of cursors found | * @return number of cursors found | |||
*/ | */ | |||
static int erase( int n , long long * ids ); | static int erase( int n , long long * ids ); | |||
/* call when cursor's location changes so that we can update the | ||||
cursorsbylocation map. if you are locked and internally iterati | ||||
ng, only | ||||
need to call when you are ready to "unlock". | ||||
*/ | ||||
void updateLocation(); | ||||
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 371 | skipping to change at line 364 | |||
void storeOpForSlave( DiskLoc last ); | void storeOpForSlave( DiskLoc last ); | |||
void updateSlaveLocation( CurOp& curop ); | void updateSlaveLocation( CurOp& curop ); | |||
unsigned idleTime() const { return _idleAgeMillis; } | unsigned idleTime() const { return _idleAgeMillis; } | |||
void setDoingDeletes( bool doingDeletes ) {_doingDeletes = doingDel etes; } | void setDoingDeletes( bool doingDeletes ) {_doingDeletes = doingDel etes; } | |||
void slaveReadTill( const OpTime& t ) { _slaveReadTill = t; } | void slaveReadTill( const OpTime& t ) { _slaveReadTill = t; } | |||
/** Just for testing. */ | ||||
OpTime getSlaveReadTill() const { return _slaveReadTill; } | ||||
public: // static methods | public: // static methods | |||
static void idleTimeReport(unsigned millis); | static void idleTimeReport(unsigned millis); | |||
static void appendStats( BSONObjBuilder& result ); | static void appendStats( BSONObjBuilder& result ); | |||
static unsigned numCursors() { return clientCursorsById.size(); } | static unsigned numCursors() { return clientCursorsById.size(); } | |||
static void informAboutToDeleteBucket(const DiskLoc& b); | static void informAboutToDeleteBucket(const DiskLoc& b); | |||
static void aboutToDelete(const DiskLoc& dl); | static void aboutToDelete(const DiskLoc& dl); | |||
static void find( const string& ns , set<CursorId>& all ); | static void find( const string& ns , set<CursorId>& all ); | |||
skipping to change at line 448 | skipping to change at line 444 | |||
public: | public: | |||
string name() const { return "ClientCursorMonitor"; } | string name() const { return "ClientCursorMonitor"; } | |||
void run(); | void run(); | |||
}; | }; | |||
} // namespace mongo | } // namespace mongo | |||
// ClientCursor should only be used with auto_ptr because it needs to be | // ClientCursor should only be used with auto_ptr because it needs to be | |||
// release()ed after a yield if stillOk() returns false and these pointer t ypes | // release()ed after a yield if stillOk() returns false and these pointer t ypes | |||
// do not support releasing. This will prevent them from being used acciden tally | // do not support releasing. This will prevent them from being used acciden tally | |||
// Instead of auto_ptr<>, which still requires some degree of manual manage | ||||
ment | ||||
// of this, consider using ClientCursor::CleanupPointer which handles | ||||
// ClientCursor's unusual self-deletion mechanics | ||||
namespace boost{ | namespace boost{ | |||
template<> class scoped_ptr<mongo::ClientCursor> {}; | template<> class scoped_ptr<mongo::ClientCursor> {}; | |||
template<> class shared_ptr<mongo::ClientCursor> {}; | template<> class shared_ptr<mongo::ClientCursor> {}; | |||
} | } | |||
End of changes. 13 change blocks. | ||||
45 lines changed or deleted | 43 lines changed or added | |||
commands.h | commands.h | |||
---|---|---|---|---|
skipping to change at line 40 | skipping to change at line 40 | |||
/** mongodb "commands" (sent via db.$cmd.findOne(...)) | /** mongodb "commands" (sent via db.$cmd.findOne(...)) | |||
subclass to make a command. define a singleton object for it. | subclass to make a command. define a singleton object for it. | |||
*/ | */ | |||
class Command { | class Command { | |||
protected: | protected: | |||
string parseNsFullyQualified(const string& dbname, const BSONObj& c mdObj) const; | string parseNsFullyQualified(const string& dbname, const BSONObj& c mdObj) const; | |||
public: | public: | |||
// only makes sense for commands where 1st parm is the collection. | // only makes sense for commands where 1st parm is the collection. | |||
virtual string parseNs(const string& dbname, const BSONObj& cmdObj) const; | virtual string parseNs(const string& dbname, const BSONObj& cmdObj) const; | |||
// warning: isAuthorized uses the lockType() return values, and val | ||||
ues are being passed | ||||
// around as ints so be careful as it isn't really typesafe and wil | ||||
l need cleanup later | ||||
enum LockType { READ = -1 , NONE = 0 , WRITE = 1 }; | enum LockType { READ = -1 , NONE = 0 , WRITE = 1 }; | |||
const string name; | const string name; | |||
/* run the given command | /* run the given command | |||
implement this... | implement this... | |||
fromRepl - command is being invoked as part of replication synci ng. In this situation you | fromRepl - command is being invoked as part of replication synci ng. In this situation you | |||
normally do not want to log the command to the local oplog. | normally do not want to log the command to the local oplog. | |||
return value is true if succeeded. if false, set errmsg text. | return value is true if succeeded. if false, set errmsg text. | |||
*/ | */ | |||
virtual bool run(const string& db, BSONObj& cmdObj, int options, st ring& errmsg, BSONObjBuilder& result, bool fromRepl = false ) = 0; | virtual bool run(const string& db, BSONObj& cmdObj, int options, st ring& errmsg, BSONObjBuilder& result, bool fromRepl = false ) = 0; | |||
/* | /* | |||
note: logTheTop() MUST be false if READ | note: logTheOp() 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; | |||
/** if true, lock globally instead of just the one database. by def | ||||
ault only the one | ||||
database will be locked. | ||||
*/ | ||||
virtual bool lockGlobally() const { return false; } | ||||
/* Return true if only the admin ns has privileges to run this comm and. */ | /* Return true if only the admin ns has privileges to run this comm and. */ | |||
virtual bool adminOnly() const { | virtual bool adminOnly() const { | |||
return false; | return false; | |||
} | } | |||
void htmlHelp(stringstream&) const; | void htmlHelp(stringstream&) const; | |||
/* Like adminOnly, but even stricter: we must either be authenticat ed for admin db, | /* Like adminOnly, but even stricter: we must either be authenticat ed for admin db, | |||
or, if running without auth, on the local interface. Used for t hings which | or, if running without auth, on the local interface. Used for t hings which | |||
are so major that remote invocation may not make sense (e.g., sh utdownServer). | are so major that remote invocation may not make sense (e.g., sh utdownServer). | |||
End of changes. 3 change blocks. | ||||
1 lines changed or deleted | 11 lines changed or added | |||
config.h | config.h | |||
---|---|---|---|---|
skipping to change at line 27 | skipping to change at line 27 | |||
*/ | */ | |||
/* This file is things related to the "grid configuration": | /* This file is things related to the "grid configuration": | |||
- what machines make up the db component of our cloud | - what machines make up the db component of our cloud | |||
- where various ranges of things live | - where various ranges of things live | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../db/namespace.h" | #include "../db/namespace.h" | |||
#include "../client/dbclient.h" | ||||
#include "../client/model.h" | #include "../client/model.h" | |||
#include "chunk.h" | #include "chunk.h" | |||
#include "shard.h" | #include "shard.h" | |||
#include "shardkey.h" | #include "shardkey.h" | |||
namespace mongo { | namespace mongo { | |||
struct ShardNS { | struct ShardNS { | |||
static string shard; | static string shard; | |||
skipping to change at line 86 | skipping to change at line 85 | |||
bool isSharded() const { | bool isSharded() const { | |||
return _cm.get(); | return _cm.get(); | |||
} | } | |||
ChunkManagerPtr getCM() const { | ChunkManagerPtr getCM() const { | |||
return _cm; | return _cm; | |||
} | } | |||
void resetCM( ChunkManager * cm ) { | void resetCM( ChunkManager * cm ) { | |||
assert(cm); | verify(cm); | |||
assert(_cm); // this has to be already sharded | verify(_cm); // this has to be already sharded | |||
_cm.reset( cm ); | _cm.reset( cm ); | |||
} | } | |||
void shard( const string& ns , const ShardKeyPattern& key , boo l unique ); | void shard( const string& ns , const ShardKeyPattern& key , boo l unique ); | |||
void unshard(); | void unshard(); | |||
bool isDirty() const { return _dirty; } | bool isDirty() const { return _dirty; } | |||
bool wasDropped() const { return _dropped; } | bool wasDropped() const { return _dropped; } | |||
void save( const string& ns , DBClientBase* conn ); | void save( const string& ns , DBClientBase* conn ); | |||
skipping to change at line 120 | skipping to change at line 119 | |||
typedef map<string,CollectionInfo> Collections; | typedef map<string,CollectionInfo> Collections; | |||
public: | public: | |||
DBConfig( string name ) | DBConfig( string name ) | |||
: _name( name ) , | : _name( name ) , | |||
_primary("config","") , | _primary("config","") , | |||
_shardingEnabled(false), | _shardingEnabled(false), | |||
_lock("DBConfig") , | _lock("DBConfig") , | |||
_hitConfigServerLock( "DBConfig::_hitConfigServerLock" ) { | _hitConfigServerLock( "DBConfig::_hitConfigServerLock" ) { | |||
assert( name.size() ); | verify( name.size() ); | |||
} | } | |||
virtual ~DBConfig() {} | virtual ~DBConfig() {} | |||
string getName() { return _name; }; | string getName() { return _name; }; | |||
/** | /** | |||
* @return if anything in this db is partitioned or not | * @return if anything in this db is partitioned or not | |||
*/ | */ | |||
bool isShardingEnabled() { | bool isShardingEnabled() { | |||
return _shardingEnabled; | return _shardingEnabled; | |||
skipping to change at line 146 | skipping to change at line 145 | |||
/** | /** | |||
@return true if there was sharding info to remove | @return true if there was sharding info to remove | |||
*/ | */ | |||
bool removeSharding( const string& ns ); | bool removeSharding( const string& ns ); | |||
/** | /** | |||
* @return whether or not the 'ns' collection is partitioned | * @return whether or not the 'ns' collection is partitioned | |||
*/ | */ | |||
bool isSharded( const string& ns ); | bool isSharded( const string& ns ); | |||
// Atomically returns *either* the chunk manager *or* the primary s | ||||
hard for the collection, | ||||
// neither if the collection doesn't exist. | ||||
void getChunkManagerOrPrimary( const string& ns, ChunkManagerPtr& m | ||||
anager, ShardPtr& primary ); | ||||
ChunkManagerPtr getChunkManager( const string& ns , bool reload = f alse, bool forceReload = false ); | ChunkManagerPtr getChunkManager( const string& ns , bool reload = f alse, bool forceReload = false ); | |||
ChunkManagerPtr getChunkManagerIfExists( const string& ns , bool re load = false, bool forceReload = false ); | ChunkManagerPtr getChunkManagerIfExists( const string& ns , bool re load = false, bool forceReload = false ); | |||
const Shard& getShard( const string& ns ); | const Shard& getShard( const string& ns ); | |||
/** | /** | |||
* @return the correct for shard for the ns | * @return the correct for shard for the ns | |||
* if the namespace is sharded, will return NULL | * if the namespace is sharded, will return NULL | |||
*/ | */ | |||
ShardPtr getShardIfExists( const string& ns ); | ShardPtr getShardIfExists( const string& ns ); | |||
skipping to change at line 177 | skipping to change at line 180 | |||
// model stuff | // model stuff | |||
// lockless loading | // lockless loading | |||
void serialize(BSONObjBuilder& to); | void serialize(BSONObjBuilder& to); | |||
void unserialize(const BSONObj& from); | void unserialize(const BSONObj& from); | |||
void getAllShards(set<Shard>& shards) const; | void getAllShards(set<Shard>& shards) const; | |||
void getAllShardedCollections(set<string>& namespaces) const; | ||||
protected: | protected: | |||
/** | /** | |||
lockless | lockless | |||
*/ | */ | |||
bool _isSharded( const string& ns ); | bool _isSharded( const string& ns ); | |||
bool _dropShardedCollections( int& num, set<Shard>& allServers , st ring& errmsg ); | bool _dropShardedCollections( int& num, set<Shard>& allServers , st ring& errmsg ); | |||
bool _load(); | bool _load(); | |||
End of changes. 5 change blocks. | ||||
4 lines changed or deleted | 11 lines changed or added | |||
connections.h | connections.h | |||
---|---|---|---|---|
skipping to change at line 22 | skipping to change at line 22 | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Affero General Public License for more details. | * GNU Affero General Public License for more details. | |||
* | * | |||
* You should have received a copy of the GNU Affero General Public Lice nse | * You should have received a copy of the GNU Affero General Public Lice nse | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include <map> | #include <map> | |||
#include "../../client/dbclient.h" | ||||
#include "../security_common.h" | #include "../security_common.h" | |||
namespace mongo { | namespace mongo { | |||
/** here we keep a single connection (with reconnect) for a set of host s, | /** here we keep a single connection (with reconnect) for a set of host s, | |||
one each, and allow one user at a time per host. if in use already for that | one each, and allow one user at a time per host. if in use already for that | |||
host, we block. so this is an easy way to keep a 1-deep pool of co nnections | host, we block. so this is an easy way to keep a 1-deep pool of co nnections | |||
that many threads can share. | that many threads can share. | |||
thread-safe. | thread-safe. | |||
skipping to change at line 51 | skipping to change at line 50 | |||
scopedconn object for same host). | scopedconn object for same host). | |||
*/ | */ | |||
class ScopedConn { | class ScopedConn { | |||
public: | public: | |||
/** throws assertions if connect failure etc. */ | /** throws assertions if connect failure etc. */ | |||
ScopedConn(string hostport); | ScopedConn(string hostport); | |||
~ScopedConn() { | ~ScopedConn() { | |||
// conLock releases... | // conLock releases... | |||
} | } | |||
void reconnect() { | void reconnect() { | |||
conn().reset(new DBClientConnection(true, 0, 10)); | x->cc.reset(new DBClientConnection(true, 0, 10)); | |||
x->cc->_logLevel = 2; | ||||
x->connected = false; | x->connected = false; | |||
connect(); | connect(); | |||
} | } | |||
/* If we were to run a query and not exhaust the cursor, future use of the connection would be problematic. | /* If we were to run a query and not exhaust the cursor, future use of the connection would be problematic. | |||
So here what we do is wrapper known safe methods and not allow c ursor-style queries at all. This makes | So here what we do is wrapper known safe methods and not allow c ursor-style queries at all. This makes | |||
ScopedConn limited in functionality but very safe. More non-cur sor wrappers can be added here if needed. | ScopedConn limited in functionality but very safe. More non-cur sor wrappers can be added here if needed. | |||
*/ | */ | |||
bool runCommand(const string &dbname, const BSONObj& cmd, BSONObj & info, int options=0) { | bool runCommand(const string &dbname, const BSONObj& cmd, BSONObj & info, int options=0) { | |||
return conn()->runCommand(dbname, cmd, info, options); | return conn()->runCommand(dbname, cmd, info, options); | |||
End of changes. 2 change blocks. | ||||
2 lines changed or deleted | 2 lines changed or added | |||
connpool.h | connpool.h | |||
---|---|---|---|---|
skipping to change at line 21 | skipping to change at line 21 | |||
* Unless required by applicable law or agreed to in writing, software | * Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | * distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. | |||
* See the License for the specific language governing permissions and | * See the License for the specific language governing permissions and | |||
* limitations under the License. | * limitations under the License. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include <stack> | #include <stack> | |||
#include "dbclient.h" | ||||
#include "redef_macros.h" | ||||
#include "../util/background.h" | #include "mongo/util/background.h" | |||
#include "mongo/client/dbclientinterface.h" | ||||
namespace mongo { | namespace mongo { | |||
class Shard; | class Shard; | |||
class DBConnectionPool; | class DBConnectionPool; | |||
/** | /** | |||
* not thread safe | * not thread safe | |||
* thread safety is handled by DBConnectionPool | * thread safety is handled by DBConnectionPool | |||
*/ | */ | |||
class PoolForHost { | class PoolForHost { | |||
public: | public: | |||
PoolForHost() | PoolForHost() | |||
: _created(0) {} | : _created(0) {} | |||
PoolForHost( const PoolForHost& other ) { | PoolForHost( const PoolForHost& other ) { | |||
assert(other._pool.size() == 0); | verify(other._pool.size() == 0); | |||
_created = other._created; | _created = other._created; | |||
assert( _created == 0 ); | verify( _created == 0 ); | |||
} | } | |||
~PoolForHost(); | ~PoolForHost(); | |||
int numAvailable() const { return (int)_pool.size(); } | int numAvailable() const { return (int)_pool.size(); } | |||
void createdOne( DBClientBase * base ); | void createdOne( DBClientBase * base ); | |||
long long numCreated() const { return _created; } | long long numCreated() const { return _created; } | |||
ConnectionString::ConnectionType type() const { assert(_created); r eturn _type; } | ConnectionString::ConnectionType type() const { verify(_created); r eturn _type; } | |||
/** | /** | |||
* gets a connection or return NULL | * gets a connection or return NULL | |||
*/ | */ | |||
DBClientBase * get( DBConnectionPool * pool , double socketTimeout ); | DBClientBase * get( DBConnectionPool * pool , double socketTimeout ); | |||
void done( DBConnectionPool * pool , DBClientBase * c ); | void done( DBConnectionPool * pool , DBClientBase * c ); | |||
void flush(); | void flush(); | |||
skipping to change at line 290 | skipping to change at line 289 | |||
void _setSocketTimeout(); | void _setSocketTimeout(); | |||
const string _host; | const string _host; | |||
DBClientBase *_conn; | DBClientBase *_conn; | |||
const double _socketTimeout; | const double _socketTimeout; | |||
}; | }; | |||
} // namespace mongo | } // namespace mongo | |||
#include "undef_macros.h" | ||||
End of changes. 6 change blocks. | ||||
6 lines changed or deleted | 5 lines changed or added | |||
core.h | core.h | |||
---|---|---|---|---|
skipping to change at line 101 | skipping to change at line 101 | |||
h._copy( (char*)&h._hash , bindata ); | h._copy( (char*)&h._hash , bindata ); | |||
h._fix(); | h._fix(); | |||
return h; | return h; | |||
} | } | |||
explicit GeoHash( const BSONElement& e , unsigned bits=32 ) { | explicit GeoHash( const BSONElement& e , unsigned bits=32 ) { | |||
_bits = bits; | _bits = bits; | |||
if ( e.type() == BinData ) { | if ( e.type() == BinData ) { | |||
int len = 0; | int len = 0; | |||
_copy( (char*)&_hash , e.binData( len ) ); | _copy( (char*)&_hash , e.binData( len ) ); | |||
assert( len == 8 ); | verify( len == 8 ); | |||
_bits = bits; | _bits = bits; | |||
} | } | |||
else { | else { | |||
cout << "GeoHash bad element: " << e << endl; | cout << "GeoHash bad element: " << e << endl; | |||
uassert(13047,"wrong type for geo index. if you're using a pre-release version, need to rebuild index",0); | uassert(13047,"wrong type for geo index. if you're using a pre-release version, need to rebuild index",0); | |||
} | } | |||
_fix(); | _fix(); | |||
} | } | |||
GeoHash( unsigned x , unsigned y , unsigned bits=32) { | GeoHash( unsigned x , unsigned y , unsigned bits=32) { | |||
skipping to change at line 126 | skipping to change at line 126 | |||
_hash = old._hash; | _hash = old._hash; | |||
_bits = old._bits; | _bits = old._bits; | |||
} | } | |||
GeoHash( long long hash , unsigned bits ) | GeoHash( long long hash , unsigned bits ) | |||
: _hash( hash ) , _bits( bits ) { | : _hash( hash ) , _bits( bits ) { | |||
_fix(); | _fix(); | |||
} | } | |||
void init( unsigned x , unsigned y , unsigned bits ) { | void init( unsigned x , unsigned y , unsigned bits ) { | |||
assert( bits <= 32 ); | verify( bits <= 32 ); | |||
_hash = 0; | _hash = 0; | |||
_bits = bits; | _bits = bits; | |||
for ( unsigned i=0; i<bits; i++ ) { | for ( unsigned i=0; i<bits; i++ ) { | |||
if ( isBitSet( x , i ) ) _hash |= geoBitSets.masks64[i*2]; | if ( isBitSet( x , i ) ) _hash |= geoBitSets.masks64[i*2]; | |||
if ( isBitSet( y , i ) ) _hash |= geoBitSets.masks64[(i*2)+ 1]; | if ( isBitSet( y , i ) ) _hash |= geoBitSets.masks64[(i*2)+ 1]; | |||
} | } | |||
} | } | |||
void unhash_fast( unsigned& x , unsigned& y ) const { | void unhash_fast( unsigned& x , unsigned& y ) const { | |||
x = 0; | x = 0; | |||
skipping to change at line 175 | skipping to change at line 175 | |||
*/ | */ | |||
static bool isBitSet( unsigned val , unsigned bit ) { | static bool isBitSet( unsigned val , unsigned bit ) { | |||
return geoBitSets.masks32[bit] & val; | return geoBitSets.masks32[bit] & val; | |||
} | } | |||
GeoHash up() const { | GeoHash up() const { | |||
return GeoHash( _hash , _bits - 1 ); | return GeoHash( _hash , _bits - 1 ); | |||
} | } | |||
bool hasPrefix( const GeoHash& other ) const { | bool hasPrefix( const GeoHash& other ) const { | |||
assert( other._bits <= _bits ); | verify( other._bits <= _bits ); | |||
if ( other._bits == 0 ) | if ( other._bits == 0 ) | |||
return true; | return true; | |||
long long x = other._hash ^ _hash; | long long x = other._hash ^ _hash; | |||
x = x >> (64-(other._bits*2)); | x = x >> (64-(other._bits*2)); | |||
return x == 0; | return x == 0; | |||
} | } | |||
string toString() const { | string toString() const { | |||
StringBuilder buf; | StringBuilder buf; | |||
for ( unsigned x=0; x<_bits*2; x++ ) | for ( unsigned x=0; x<_bits*2; x++ ) | |||
skipping to change at line 205 | skipping to change at line 205 | |||
void init( const string& s ) { | void init( const string& s ) { | |||
_hash = 0; | _hash = 0; | |||
_bits = s.size() / 2; | _bits = s.size() / 2; | |||
for ( unsigned pos=0; pos<s.size(); pos++ ) | for ( unsigned pos=0; pos<s.size(); pos++ ) | |||
if ( s[pos] == '1' ) | if ( s[pos] == '1' ) | |||
setBit( pos , 1 ); | setBit( pos , 1 ); | |||
} | } | |||
void setBit( unsigned pos , bool one ) { | void setBit( unsigned pos , bool one ) { | |||
assert( pos < _bits * 2 ); | verify( pos < _bits * 2 ); | |||
if ( one ) | if ( one ) | |||
_hash |= geoBitSets.masks64[pos]; | _hash |= geoBitSets.masks64[pos]; | |||
else if ( _hash & geoBitSets.masks64[pos] ) | else if ( _hash & geoBitSets.masks64[pos] ) | |||
_hash &= ~geoBitSets.masks64[pos]; | _hash &= ~geoBitSets.masks64[pos]; | |||
} | } | |||
bool getBit( unsigned pos ) const { | bool getBit( unsigned pos ) const { | |||
return _hash & geoBitSets.masks64[pos]; | return _hash & geoBitSets.masks64[pos]; | |||
} | } | |||
bool getBitX( unsigned pos ) const { | bool getBitX( unsigned pos ) const { | |||
assert( pos < 32 ); | verify( pos < 32 ); | |||
return getBit( pos * 2 ); | return getBit( pos * 2 ); | |||
} | } | |||
bool getBitY( unsigned pos ) const { | bool getBitY( unsigned pos ) const { | |||
assert( pos < 32 ); | verify( pos < 32 ); | |||
return getBit( ( pos * 2 ) + 1 ); | return getBit( ( pos * 2 ) + 1 ); | |||
} | } | |||
BSONObj wrap( const char* name = "" ) const { | BSONObj wrap( const char* name = "" ) const { | |||
BSONObjBuilder b(20); | BSONObjBuilder b(20); | |||
append( b , name ); | append( b , name ); | |||
BSONObj o = b.obj(); | BSONObj o = b.obj(); | |||
if( ! strlen( name ) ) assert( o.objsize() == 20 ); | if( ! strlen( name ) ) verify( o.objsize() == 20 ); | |||
return o; | return o; | |||
} | } | |||
bool constrains() const { | bool constrains() const { | |||
return _bits > 0; | return _bits > 0; | |||
} | } | |||
bool canRefine() const { | bool canRefine() const { | |||
return _bits < 32; | return _bits < 32; | |||
} | } | |||
skipping to change at line 260 | skipping to change at line 260 | |||
bool atMaxX() const { | bool atMaxX() const { | |||
return ( _hash & geoBitSets.allX[ _bits ] ) == geoBitSets.allX[ _bits ]; | return ( _hash & geoBitSets.allX[ _bits ] ) == geoBitSets.allX[ _bits ]; | |||
} | } | |||
bool atMaxY() const { | bool atMaxY() const { | |||
return ( _hash & geoBitSets.allY[ _bits ] ) == geoBitSets.allY[ _bits ]; | return ( _hash & geoBitSets.allY[ _bits ] ) == geoBitSets.allY[ _bits ]; | |||
} | } | |||
void move( int x , int y ) { | void move( int x , int y ) { | |||
assert( _bits ); | verify( _bits ); | |||
_move( 0 , x ); | _move( 0 , x ); | |||
_move( 1 , y ); | _move( 1 , y ); | |||
} | } | |||
void _move( unsigned offset , int d ) { | void _move( unsigned offset , int d ) { | |||
if ( d == 0 ) | if ( d == 0 ) | |||
return; | return; | |||
assert( d <= 1 && d>= -1 ); // TEMP | verify( d <= 1 && d>= -1 ); // TEMP | |||
bool from, to; | bool from, to; | |||
if ( d > 0 ) { | if ( d > 0 ) { | |||
from = 0; | from = 0; | |||
to = 1; | to = 1; | |||
} | } | |||
else { | else { | |||
from = 1; | from = 1; | |||
to = 0; | to = 0; | |||
} | } | |||
skipping to change at line 301 | skipping to change at line 301 | |||
for ( ; pos < ( _bits * 2 ) ; pos += 2 ) { | for ( ; pos < ( _bits * 2 ) ; pos += 2 ) { | |||
setBit( pos , from ); | setBit( pos , from ); | |||
} | } | |||
return; | return; | |||
} | } | |||
setBit( pos , from ); | setBit( pos , from ); | |||
pos -= 2; | pos -= 2; | |||
} | } | |||
assert(0); | verify(0); | |||
} | } | |||
GeoHash& operator=(const GeoHash& h) { | GeoHash& operator=(const GeoHash& h) { | |||
_hash = h._hash; | _hash = h._hash; | |||
_bits = h._bits; | _bits = h._bits; | |||
return *this; | return *this; | |||
} | } | |||
bool operator==(const GeoHash& h ) const { | bool operator==(const GeoHash& h ) const { | |||
return _hash == h._hash && _bits == h._bits; | return _hash == h._hash && _bits == h._bits; | |||
skipping to change at line 326 | skipping to change at line 326 | |||
} | } | |||
bool operator<(const GeoHash& h ) const { | bool operator<(const GeoHash& h ) const { | |||
if( _hash != h._hash ) return _hash < h._hash; | if( _hash != h._hash ) return _hash < h._hash; | |||
return _bits < h._bits; | return _bits < h._bits; | |||
} | } | |||
GeoHash& operator+=( const char * s ) { | GeoHash& operator+=( const char * s ) { | |||
unsigned pos = _bits * 2; | unsigned pos = _bits * 2; | |||
_bits += strlen(s) / 2; | _bits += strlen(s) / 2; | |||
assert( _bits <= 32 ); | verify( _bits <= 32 ); | |||
while ( s[0] ) { | while ( s[0] ) { | |||
if ( s[0] == '1' ) | if ( s[0] == '1' ) | |||
setBit( pos , 1 ); | setBit( pos , 1 ); | |||
pos++; | pos++; | |||
s++; | s++; | |||
} | } | |||
return *this; | return *this; | |||
} | } | |||
skipping to change at line 533 | skipping to change at line 533 | |||
double sin_x2(sin(p2._x)), cos_x2(cos(p2._x)); | double sin_x2(sin(p2._x)), cos_x2(cos(p2._x)); | |||
double sin_y2(sin(p2._y)), cos_y2(cos(p2._y)); | double sin_y2(sin(p2._y)), cos_y2(cos(p2._y)); | |||
double cross_prod = | double cross_prod = | |||
(cos_y1*cos_x1 * cos_y2*cos_x2) + | (cos_y1*cos_x1 * cos_y2*cos_x2) + | |||
(cos_y1*sin_x1 * cos_y2*sin_x2) + | (cos_y1*sin_x1 * cos_y2*sin_x2) + | |||
(sin_y1 * sin_y2); | (sin_y1 * sin_y2); | |||
if (cross_prod >= 1 || cross_prod <= -1) { | if (cross_prod >= 1 || cross_prod <= -1) { | |||
// fun with floats | // fun with floats | |||
assert( fabs(cross_prod)-1 < 1e-6 ); | verify( fabs(cross_prod)-1 < 1e-6 ); | |||
return cross_prod > 0 ? 0 : M_PI; | return cross_prod > 0 ? 0 : M_PI; | |||
} | } | |||
return acos(cross_prod); | return acos(cross_prod); | |||
} | } | |||
// note: return is still in radians as that can be multiplied by radius to get arc length | // note: return is still in radians as that can be multiplied by radius to get arc length | |||
inline double spheredist_deg( const Point& p1, const Point& p2 ) { | inline double spheredist_deg( const Point& p1, const Point& p2 ) { | |||
return spheredist_rad( | return spheredist_rad( | |||
Point( deg2rad(p1._x), deg2rad(p1._y) ), | Point( deg2rad(p1._x), deg2rad(p1._y) ), | |||
End of changes. 12 change blocks. | ||||
12 lines changed or deleted | 12 lines changed or added | |||
counters.h | counters.h | |||
---|---|---|---|---|
skipping to change at line 36 | skipping to change at line 36 | |||
namespace mongo { | namespace mongo { | |||
/** | /** | |||
* for storing operation counters | * for storing operation counters | |||
* note: not thread safe. ok with that for speed | * note: not thread safe. ok with that for speed | |||
*/ | */ | |||
class OpCounters { | class OpCounters { | |||
public: | public: | |||
OpCounters(); | OpCounters(); | |||
void incInsertInWriteLock(int n) { _insert.x += n; } | ||||
AtomicUInt * getInsert() { return _insert; } | void gotInsert() { _insert++; } | |||
AtomicUInt * getQuery() { return _query; } | void gotQuery() { _query++; } | |||
AtomicUInt * getUpdate() { return _update; } | void gotUpdate() { _update++; } | |||
AtomicUInt * getDelete() { return _delete; } | void gotDelete() { _delete++; } | |||
AtomicUInt * getGetMore() { return _getmore; } | void gotGetMore() { _getmore++; } | |||
AtomicUInt * getCommand() { return _command; } | void gotCommand() { _command++; } | |||
void incInsertInWriteLock(int n) { _insert->x += n; } | ||||
void gotInsert() { _insert[0]++; } | ||||
void gotQuery() { _query[0]++; } | ||||
void gotUpdate() { _update[0]++; } | ||||
void gotDelete() { _delete[0]++; } | ||||
void gotGetMore() { _getmore[0]++; } | ||||
void gotCommand() { _command[0]++; } | ||||
void gotOp( int op , bool isCommand ); | void gotOp( int op , bool isCommand ); | |||
BSONObj& getObj(); | BSONObj getObj(); | |||
private: | private: | |||
BSONObj _obj; | ||||
// todo: there will be a lot of cache line contention on these. ne ed to do something | // todo: there will be a lot of cache line contention on these. ne ed to do something | |||
// else eventually. | // else eventually. | |||
AtomicUInt * _insert; | AtomicUInt _insert; | |||
AtomicUInt * _query; | AtomicUInt _query; | |||
AtomicUInt * _update; | AtomicUInt _update; | |||
AtomicUInt * _delete; | AtomicUInt _delete; | |||
AtomicUInt * _getmore; | AtomicUInt _getmore; | |||
AtomicUInt * _command; | AtomicUInt _command; | |||
}; | }; | |||
extern OpCounters globalOpCounters; | extern OpCounters globalOpCounters; | |||
extern OpCounters replOpCounters; | extern OpCounters replOpCounters; | |||
class IndexCounters { | class IndexCounters { | |||
public: | public: | |||
IndexCounters(); | IndexCounters(); | |||
// used without a mutex intentionally (can race) | // used without a mutex intentionally (can race) | |||
End of changes. 4 change blocks. | ||||
23 lines changed or deleted | 14 lines changed or added | |||
curop.h | curop.h | |||
---|---|---|---|---|
skipping to change at line 27 | skipping to change at line 27 | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "namespace-inl.h" | #include "namespace-inl.h" | |||
#include "client.h" | #include "client.h" | |||
#include "../bson/util/atomic_int.h" | #include "../bson/util/atomic_int.h" | |||
#include "../util/concurrency/spin_lock.h" | #include "../util/concurrency/spin_lock.h" | |||
#include "../util/time_support.h" | #include "../util/time_support.h" | |||
#include "../util/net/hostandport.h" | #include "../util/net/hostandport.h" | |||
#include "../util/progress_meter.h" | ||||
namespace mongo { | namespace mongo { | |||
class CurOp; | class CurOp; | |||
/* lifespan is different than CurOp because of recursives with DBDirect Client */ | /* lifespan is different than CurOp because of recursives with DBDirect Client */ | |||
class OpDebug { | class OpDebug { | |||
public: | public: | |||
OpDebug() : ns(""){ reset(); } | OpDebug() : ns(""){ reset(); } | |||
skipping to change at line 60 | skipping to change at line 61 | |||
BSONObj query; | BSONObj query; | |||
BSONObj updateobj; | BSONObj updateobj; | |||
// detailed options | // detailed options | |||
long long cursorid; | long long cursorid; | |||
int ntoreturn; | int ntoreturn; | |||
int ntoskip; | int ntoskip; | |||
bool exhaust; | bool exhaust; | |||
// debugging/profile info | // debugging/profile info | |||
int nscanned; | long long nscanned; | |||
bool idhack; // indicates short circuited code path on an u pdate to make the update faster | bool idhack; // indicates short circuited code path on an u pdate to make the update faster | |||
bool scanAndOrder; // scanandorder query plan aspect was used | bool scanAndOrder; // scanandorder query plan aspect was used | |||
bool moved; // update resulted in a move (moves are expens | long long nupdated; // number of records updated | |||
ive) | long long nmoved; // updates resulted in a move (moves are expen | |||
sive) | ||||
bool fastmod; | bool fastmod; | |||
bool fastmodinsert; // upsert of an $operation. builds a default o bject | bool fastmodinsert; // upsert of an $operation. builds a default o bject | |||
bool upsert; // true if the update actually did an insert | bool upsert; // true if the update actually did an insert | |||
int keyUpdates; | int keyUpdates; | |||
// error handling | // error handling | |||
ExceptionInfo exceptionInfo; | ExceptionInfo exceptionInfo; | |||
// response info | // response info | |||
int executionTime; | int executionTime; | |||
skipping to change at line 165 | skipping to change at line 167 | |||
void ensureStarted() { | void ensureStarted() { | |||
if ( _start == 0 ) | if ( _start == 0 ) | |||
_start = _checkpoint = curTimeMicros64(); | _start = _checkpoint = curTimeMicros64(); | |||
} | } | |||
bool isStarted() const { return _start > 0; } | bool isStarted() const { return _start > 0; } | |||
void enter( Client::Context * context ); | void enter( Client::Context * context ); | |||
void leave( Client::Context * context ); | void leave( Client::Context * context ); | |||
void reset(); | void reset(); | |||
void reset( const HostAndPort& remote, int op ); | void reset( const HostAndPort& remote, int op ); | |||
void markCommand() { _command = true; } | void markCommand() { _command = true; } | |||
void waitingForLock( char type ) { | ||||
void waitingForLock( int type ) { | ||||
_waitingForLock = true; | _waitingForLock = true; | |||
if ( type > 0 ) | _lockType = type; | |||
_lockType = 1; | ||||
else | ||||
_lockType = -1; | ||||
} | } | |||
void gotLock() { _waitingForLock = false; } | void gotLock() { _waitingForLock = false; } | |||
OpDebug& debug() { return _debug; } | OpDebug& debug() { return _debug; } | |||
int profileLevel() const { return _dbprofile; } | int profileLevel() const { return _dbprofile; } | |||
const char * getNS() const { return _ns; } | const char * getNS() const { return _ns; } | |||
bool shouldDBProfile( int ms ) const { | bool shouldDBProfile( int ms ) const { | |||
if ( _dbprofile <= 0 ) | if ( _dbprofile <= 0 ) | |||
return false; | return false; | |||
return _dbprofile >= 2 || ms >= cmdLine.slowMS; | return _dbprofile >= 2 || ms >= cmdLine.slowMS; | |||
} | } | |||
AtomicUInt opNum() const { return _opNum; } | AtomicUInt opNum() const { return _opNum; } | |||
/** if this op is running */ | /** if this op is running */ | |||
bool active() const { return _active; } | bool active() const { return _active; } | |||
int getLockType() const { return _lockType; } | char lockType() const { return _lockType; } | |||
bool displayInCurop() const { return _active && ! _suppressFromCuro | ||||
p; } | ||||
bool isWaitingForLock() const { return _waitingForLock; } | bool isWaitingForLock() const { return _waitingForLock; } | |||
int getOp() const { return _op; } | int getOp() const { return _op; } | |||
unsigned long long startTime() { // micros | unsigned long long startTime() { // micros | |||
ensureStarted(); | ensureStarted(); | |||
return _start; | return _start; | |||
} | } | |||
void done() { | void done() { | |||
_active = false; | _active = false; | |||
_end = curTimeMicros64(); | _end = curTimeMicros64(); | |||
} | } | |||
skipping to change at line 228 | skipping to change at line 227 | |||
ProgressMeter& getProgressMeter() { return _progressMeter; } | ProgressMeter& getProgressMeter() { return _progressMeter; } | |||
CurOp *parent() const { return _wrapped; } | CurOp *parent() const { return _wrapped; } | |||
void kill() { _killed = true; } | void kill() { _killed = true; } | |||
bool killed() const { return _killed; } | bool killed() const { return _killed; } | |||
void yielded() { _numYields++; } | void yielded() { _numYields++; } | |||
void setNS(const char *ns) { | void setNS(const char *ns) { | |||
strncpy(_ns, ns, Namespace::MaxNsLen); | strncpy(_ns, ns, Namespace::MaxNsLen); | |||
_ns[Namespace::MaxNsLen] = 0; | _ns[Namespace::MaxNsLen] = 0; | |||
} | } | |||
void suppressFromCurop() { _suppressFromCurop = true; } | ||||
long long getExpectedLatencyMs() const { return _expectedLatencyMs; | ||||
} | ||||
void setExpectedLatencyMs( long long latency ) { _expectedLatencyMs | ||||
= latency; } | ||||
private: | private: | |||
friend class Client; | friend class Client; | |||
void _reset(); | void _reset(); | |||
static AtomicUInt _nextOpNum; | static AtomicUInt _nextOpNum; | |||
Client * _client; | Client * _client; | |||
CurOp * _wrapped; | CurOp * _wrapped; | |||
unsigned long long _start; | unsigned long long _start; | |||
unsigned long long _checkpoint; | unsigned long long _checkpoint; | |||
unsigned long long _end; | unsigned long long _end; | |||
bool _active; | bool _active; | |||
bool _suppressFromCurop; // unless $all is set | ||||
int _op; | int _op; | |||
bool _command; | bool _command; | |||
int _lockType; // see concurrency.h for values | char _lockType; // r w R W | |||
bool _waitingForLock; | bool _waitingForLock; | |||
int _dbprofile; // 0=off, 1=slow, 2=all | int _dbprofile; // 0=off, 1=slow, 2=all | |||
AtomicUInt _opNum; // todo: simple being "unsigned" m ay make more sense here | AtomicUInt _opNum; // todo: simple being "unsigned" m ay make more sense here | |||
char _ns[Namespace::MaxNsLen+2]; | char _ns[Namespace::MaxNsLen+2]; | |||
HostAndPort _remote; // CAREFUL here with thread safety | HostAndPort _remote; // CAREFUL here with thread safety | |||
CachedBSONObj _query; // CachedBSONObj is thread safe | CachedBSONObj _query; // CachedBSONObj is thread safe | |||
OpDebug _debug; | OpDebug _debug; | |||
ThreadSafeString _message; | ThreadSafeString _message; | |||
ProgressMeter _progressMeter; | ProgressMeter _progressMeter; | |||
volatile bool _killed; | volatile bool _killed; | |||
int _numYields; | int _numYields; | |||
// this is how much "extra" time a query might take | ||||
// a writebacklisten for example will block for 30s | ||||
// so this should be 30000 in that case | ||||
long long _expectedLatencyMs; | ||||
}; | }; | |||
/* _globalKill: we are shutting down | /* _globalKill: we are shutting down | |||
otherwise kill attribute set on specified CurOp | otherwise kill attribute set on specified CurOp | |||
this class does not handle races between interruptJs and the checkFo rInterrupt functions - those must be | this class does not handle races between interruptJs and the checkFo rInterrupt functions - those must be | |||
handled by the client of this class | handled by the client of this class | |||
*/ | */ | |||
extern class KillCurrentOp { | extern class KillCurrentOp { | |||
public: | public: | |||
void killAll(); | void killAll(); | |||
void kill(AtomicUInt i); | void kill(AtomicUInt i); | |||
/** @return true if global interrupt and should terminate the opera tion */ | /** @return true if global interrupt and should terminate the opera tion */ | |||
bool globalInterruptCheck() const { return _globalKill; } | bool globalInterruptCheck() const { return _globalKill; } | |||
void checkForInterrupt( bool heedMutex = true ) { | /** | |||
Client& c = cc(); | * @param heedMutex if true and have a write lock, won't kill op si | |||
if ( heedMutex && d.dbMutex.isWriteLocked() ) | nce it might be unsafe | |||
return; | */ | |||
if( _globalKill ) | void checkForInterrupt( bool heedMutex = true ); | |||
uasserted(11600,"interrupted at shutdown"); | ||||
if( c.curop()->killed() ) | ||||
uasserted(11601,"interrupted"); | ||||
if( c.sometimes(1024) ) { | ||||
AbstractMessagingPort *p = cc().port(); | ||||
if( p ) | ||||
p->assertStillConnected(); | ||||
} | ||||
} | ||||
/** @return "" if not interrupted. otherwise, you should stop. */ | /** @return "" if not interrupted. otherwise, you should stop. */ | |||
const char *checkForInterruptNoAssert( /*bool heedMutex = true*/ ) | const char *checkForInterruptNoAssert(); | |||
{ | ||||
Client& c = cc(); | ||||
// always called withi false so commented out: | ||||
/*if ( heedMutex && d.dbMutex.isWriteLocked() ) | ||||
return "";*/ | ||||
if( _globalKill ) | ||||
return "interrupted at shutdown"; | ||||
if( c.curop()->killed() ) | ||||
return "interrupted"; | ||||
if( c.sometimes(1024) ) { | ||||
try { | ||||
AbstractMessagingPort *p = cc().port(); | ||||
if( p ) | ||||
p->assertStillConnected(); | ||||
} | ||||
catch(...) { | ||||
log() << "no longer connected to client"; | ||||
return "no longer connected to client"; | ||||
} | ||||
} | ||||
return ""; | ||||
} | ||||
private: | private: | |||
void interruptJs( AtomicUInt *op ); | void interruptJs( AtomicUInt *op ); | |||
volatile bool _globalKill; | volatile bool _globalKill; | |||
} killCurrentOp; | } killCurrentOp; | |||
} | } | |||
End of changes. 12 change blocks. | ||||
48 lines changed or deleted | 31 lines changed or added | |||
cursor.h | cursor.h | |||
---|---|---|---|---|
skipping to change at line 24 | skipping to change at line 24 | |||
* 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" | #include "matcher.h" | |||
#include "mongo/db/projection.h" | ||||
namespace mongo { | namespace mongo { | |||
class NamespaceDetails; | class NamespaceDetails; | |||
class Record; | class Record; | |||
class CoveredIndexMatcher; | class CoveredIndexMatcher; | |||
/* Query cursors, base class. This is for our internal cursors. "Clie | /** | |||
ntCursor" is a separate | * Query cursors, base class. This is for our internal cursors. "Clie | |||
concept and is for the user's cursor. | ntCursor" is a separate | |||
* 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 | |||
ClientCursor::ccmutex. Don't cause a deadlock, you've been warned. | n a | |||
*/ | * ClientCursor::ccmutex. Don't cause a deadlock, you've been warned. | |||
* | ||||
* Two general techniques may be used to ensure a Cursor is in a consis | ||||
tent state after a write. | ||||
* - The Cursor may be advanced before the document at its current | ||||
position is deleted. | ||||
* - The Cursor may record its position and then relocate this posi | ||||
tion. | ||||
* A particular Cursor may potentially utilize only one of the above te | ||||
chniques, but a client | ||||
* that is Cursor subclass agnostic must implement a pattern handling b | ||||
oth techniques. | ||||
* | ||||
* When the document at a Cursor's current position is deleted (or move | ||||
d to a new location) the | ||||
* following pattern is used: | ||||
* DiskLoc toDelete = cursor->currLoc(); | ||||
* cursor->advance(); | ||||
* cursor->prepareToTouchEarlierIterate(); | ||||
* delete( toDelete ); | ||||
* cursor->recoverFromTouchingEarlierIterate(); | ||||
* | ||||
* When a cursor yields, the following pattern is used: | ||||
* cursor->prepareToYield(); | ||||
* while( Op theOp = nextOp() ) { | ||||
* if ( theOp.type() == INSERT || theOp.type() == UPDATE_IN_PLA | ||||
CE ) { | ||||
* theOp.run(); | ||||
* } | ||||
* else if ( theOp.type() == DELETE ) { | ||||
* if ( cursor->refLoc() == theOp.toDelete() ) { | ||||
* cursor->recoverFromYield(); | ||||
* cursor->advance(); | ||||
* cursor->prepareToYield(); | ||||
* } | ||||
* theOp.run(); | ||||
* } | ||||
* } | ||||
* cursor->recoverFromYield(); | ||||
* | ||||
* The break before a getMore request is typically treated as a yield, | ||||
but if a Cursor supports | ||||
* getMore but not yield the following pattern is currently used: | ||||
* cursor->noteLocation(); | ||||
* runOtherOps(); | ||||
* cursor->checkLocation(); | ||||
* | ||||
* A Cursor may rely on additional callbacks not listed above to reloca | ||||
te its position after a | ||||
* write. | ||||
*/ | ||||
class Cursor : boost::noncopyable { | class Cursor : boost::noncopyable { | |||
public: | public: | |||
virtual ~Cursor() {} | virtual ~Cursor() {} | |||
virtual bool ok() = 0; | virtual bool ok() = 0; | |||
bool eof() { return !ok(); } | bool eof() { 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(); } | |||
skipping to change at line 95 | skipping to change at line 137 | |||
* modified. It is ok if the current iterate also points to the do cument to be modified. | * modified. It is ok if the current iterate also points to the do cument to be modified. | |||
*/ | */ | |||
virtual void prepareToTouchEarlierIterate() { noteLocation(); } | virtual void prepareToTouchEarlierIterate() { noteLocation(); } | |||
/** Recover from a previous call to prepareToTouchEarlierIterate(). */ | /** Recover from a previous call to prepareToTouchEarlierIterate(). */ | |||
virtual void recoverFromTouchingEarlierIterate() { checkLocation(); } | virtual void recoverFromTouchingEarlierIterate() { checkLocation(); } | |||
virtual bool supportYields() = 0; | virtual bool supportYields() = 0; | |||
/** Called before a ClientCursor yield. */ | /** Called before a ClientCursor yield. */ | |||
virtual bool prepareToYield() { noteLocation(); return supportYield s(); } | virtual void prepareToYield() { noteLocation(); } | |||
/** Called after a ClientCursor yield. Recovers from a previous ca ll to prepareToYield(). */ | /** Called after a ClientCursor yield. Recovers from a previous ca ll to prepareToYield(). */ | |||
virtual void recoverFromYield() { checkLocation(); } | virtual void recoverFromYield() { checkLocation(); } | |||
virtual string toString() { return "abstract?"; } | virtual string toString() { return "abstract?"; } | |||
/* used for multikey index traversal to avoid sending back dups. se e Matcher::matches(). | /* used for multikey index traversal to avoid sending back dups. se e Matcher::matches(). | |||
if a multikey index traversal: | if a multikey index traversal: | |||
if loc has already been sent, returns true. | if loc has already been sent, returns true. | |||
otherwise, marks loc as sent. | otherwise, marks loc as sent. | |||
skipping to change at line 149 | skipping to change at line 191 | |||
// Used when we want fast matcher lookup | // Used when we want fast matcher lookup | |||
virtual CoveredIndexMatcher *matcher() const { return 0; } | virtual CoveredIndexMatcher *matcher() const { return 0; } | |||
// Used when we need to share this matcher with someone else | // Used when we need to share this matcher with someone else | |||
virtual shared_ptr< CoveredIndexMatcher > matcherPtr() const { retu rn shared_ptr< CoveredIndexMatcher >(); } | virtual shared_ptr< CoveredIndexMatcher > matcherPtr() const { retu rn shared_ptr< CoveredIndexMatcher >(); } | |||
virtual bool currentMatches( MatchDetails *details = 0 ) { | virtual bool currentMatches( MatchDetails *details = 0 ) { | |||
return !matcher() || matcher()->matchesCurrent( this, details ) ; | return !matcher() || matcher()->matchesCurrent( this, details ) ; | |||
} | } | |||
// A convenience function for setting the value of matcher() manual ly | // A convenience function for setting the value of matcher() manual ly | |||
// so it may accessed later. Implementations which must generate | // so it may be accessed later. Implementations which must generat e | |||
// their own matcher() should assert here. | // their own matcher() should assert here. | |||
virtual void setMatcher( shared_ptr< CoveredIndexMatcher > matcher ) { | virtual void setMatcher( shared_ptr< CoveredIndexMatcher > matcher ) { | |||
massert( 13285, "manual matcher config not allowed", false ); | massert( 13285, "manual matcher config not allowed", false ); | |||
} | } | |||
/** @return the covered index projector for the current iterate, if | ||||
any. */ | ||||
virtual const Projection::KeyOnly *keyFieldsOnly() const { return 0 | ||||
; } | ||||
/** | ||||
* Manually set the value of keyFieldsOnly() so it may be accessed | ||||
later. Implementations | ||||
* that generate their own keyFieldsOnly() must assert. | ||||
*/ | ||||
virtual void setKeyFieldsOnly( const shared_ptr<Projection::KeyOnly | ||||
> &keyFieldsOnly ) { | ||||
massert( 16159, "manual keyFieldsOnly config not allowed", fals | ||||
e ); | ||||
} | ||||
virtual void explainDetails( BSONObjBuilder& b ) { return; } | virtual void explainDetails( BSONObjBuilder& b ) { return; } | |||
}; | }; | |||
// strategy object implementing direction of traversal. | // strategy object implementing direction of traversal. | |||
class AdvanceStrategy { | class AdvanceStrategy { | |||
public: | public: | |||
virtual ~AdvanceStrategy() { } | virtual ~AdvanceStrategy() { } | |||
virtual DiskLoc next( const DiskLoc &prev ) const = 0; | virtual DiskLoc next( const DiskLoc &prev ) const = 0; | |||
}; | }; | |||
const AdvanceStrategy *forward(); | const AdvanceStrategy *forward(); | |||
const AdvanceStrategy *reverse(); | const AdvanceStrategy *reverse(); | |||
/* table-scan style cursor */ | /** | |||
* table-scan style cursor | ||||
* | ||||
* A BasicCursor relies on advance() to ensure it is in a consistent st | ||||
ate after a write. If | ||||
* the document at a BasicCursor's current position will be deleted or | ||||
relocated, the cursor | ||||
* must first be advanced. The same is true of BasicCursor subclasses. | ||||
*/ | ||||
class BasicCursor : public Cursor { | class BasicCursor : public Cursor { | |||
public: | public: | |||
BasicCursor(DiskLoc dl, const AdvanceStrategy *_s = forward()) : cu rr(dl), s( _s ), _nscanned() { | BasicCursor(DiskLoc dl, const AdvanceStrategy *_s = forward()) : cu rr(dl), s( _s ), _nscanned() { | |||
incNscanned(); | incNscanned(); | |||
init(); | init(); | |||
} | } | |||
BasicCursor(const AdvanceStrategy *_s = forward()) : s( _s ), _nsca nned() { | BasicCursor(const AdvanceStrategy *_s = forward()) : s( _s ), _nsca nned() { | |||
init(); | init(); | |||
} | } | |||
bool ok() { return !curr.isNull(); } | bool ok() { return !curr.isNull(); } | |||
Record* _current() { | Record* _current() { | |||
assert( ok() ); | verify( ok() ); | |||
return curr.rec(); | return curr.rec(); | |||
} | } | |||
BSONObj current() { | BSONObj current() { | |||
Record *r = _current(); | Record *r = _current(); | |||
BSONObj j(r); | BSONObj j(r); | |||
return j; | return j; | |||
} | } | |||
virtual DiskLoc currLoc() { return curr; } | virtual DiskLoc currLoc() { return curr; } | |||
virtual DiskLoc refLoc() { return curr.isNull() ? last : curr; } | virtual DiskLoc refLoc() { return curr.isNull() ? last : curr; } | |||
bool advance(); | bool advance(); | |||
skipping to change at line 205 | skipping to change at line 264 | |||
} | } | |||
virtual bool tailable() { return tailable_; } | virtual bool tailable() { return tailable_; } | |||
virtual bool getsetdup(DiskLoc loc) { return false; } | virtual bool getsetdup(DiskLoc loc) { return false; } | |||
virtual bool isMultiKey() const { return false; } | virtual bool isMultiKey() const { return false; } | |||
virtual bool modifiedKeys() const { return false; } | virtual bool modifiedKeys() const { return false; } | |||
virtual bool supportGetMore() { return true; } | virtual bool supportGetMore() { return true; } | |||
virtual bool supportYields() { return true; } | virtual bool supportYields() { return true; } | |||
virtual CoveredIndexMatcher *matcher() const { return _matcher.get( ); } | virtual CoveredIndexMatcher *matcher() const { return _matcher.get( ); } | |||
virtual shared_ptr< CoveredIndexMatcher > matcherPtr() const { retu rn _matcher; } | virtual shared_ptr< CoveredIndexMatcher > matcherPtr() const { retu rn _matcher; } | |||
virtual void setMatcher( shared_ptr< CoveredIndexMatcher > matcher ) { _matcher = matcher; } | virtual void setMatcher( shared_ptr< CoveredIndexMatcher > matcher ) { _matcher = matcher; } | |||
virtual const Projection::KeyOnly *keyFieldsOnly() const { return _ | ||||
keyFieldsOnly.get(); } | ||||
virtual void setKeyFieldsOnly( const shared_ptr<Projection::KeyOnly | ||||
> &keyFieldsOnly ) { | ||||
_keyFieldsOnly = keyFieldsOnly; | ||||
} | ||||
virtual long long nscanned() { return _nscanned; } | virtual long long nscanned() { return _nscanned; } | |||
protected: | protected: | |||
DiskLoc curr, last; | DiskLoc curr, last; | |||
const AdvanceStrategy *s; | const AdvanceStrategy *s; | |||
void incNscanned() { if ( !curr.isNull() ) { ++_nscanned; } } | void incNscanned() { if ( !curr.isNull() ) { ++_nscanned; } } | |||
private: | private: | |||
bool tailable_; | bool tailable_; | |||
shared_ptr< CoveredIndexMatcher > _matcher; | shared_ptr< CoveredIndexMatcher > _matcher; | |||
shared_ptr<Projection::KeyOnly> _keyFieldsOnly; | ||||
long long _nscanned; | long long _nscanned; | |||
void init() { tailable_ = false; } | void init() { tailable_ = false; } | |||
}; | }; | |||
/* used for order { $natural: -1 } */ | /* used for order { $natural: -1 } */ | |||
class ReverseCursor : public BasicCursor { | class ReverseCursor : public BasicCursor { | |||
public: | public: | |||
ReverseCursor(DiskLoc dl) : BasicCursor( dl, reverse() ) { } | ReverseCursor(DiskLoc dl) : BasicCursor( dl, reverse() ) { } | |||
ReverseCursor() : BasicCursor( reverse() ) { } | ReverseCursor() : BasicCursor( reverse() ) { } | |||
virtual string toString() { return "ReverseCursor"; } | virtual string toString() { return "ReverseCursor"; } | |||
End of changes. 9 change blocks. | ||||
12 lines changed or deleted | 94 lines changed or added | |||
cursors.h | cursors.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 Lice nse | * You should have received a copy of the GNU Affero General Public Lice nse | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../pch.h" | #include "../pch.h" | |||
#include "../db/jsobj.h" | #include "../db/jsobj.h" | |||
#include "../db/dbmessage.h" | #include "../db/dbmessage.h" | |||
#include "../client/dbclient.h" | ||||
#include "../client/parallel.h" | #include "../client/parallel.h" | |||
#include "request.h" | #include "request.h" | |||
namespace mongo { | namespace mongo { | |||
class ShardedClientCursor : boost::noncopyable { | class ShardedClientCursor : boost::noncopyable { | |||
public: | public: | |||
ShardedClientCursor( QueryMessage& q , ClusteredCursor * cursor ); | ShardedClientCursor( QueryMessage& q , ClusteredCursor * cursor ); | |||
virtual ~ShardedClientCursor(); | virtual ~ShardedClientCursor(); | |||
long long getId(); | long long getId(); | |||
/** | /** | |||
* @return the cumulative number of documents seen by this cursor. | ||||
*/ | ||||
int getTotalSent() const; | ||||
/** | ||||
* Sends queries to the shards, gather the result for this batch an | ||||
d sends the response | ||||
* to the socket. | ||||
* | ||||
* @return whether there is more data left | * @return whether there is more data left | |||
*/ | */ | |||
bool sendNextBatch( Request& r ) { return sendNextBatch( r , _ntore | bool sendNextBatchAndReply( Request& r ); | |||
turn ); } | ||||
bool sendNextBatch( Request& r , int ntoreturn ); | /** | |||
* Sends queries to the shards and gather the result for this batch | ||||
. | ||||
* | ||||
* @param r The request object from the client | ||||
* @param ntoreturn Number of documents to return | ||||
* @param buffer The buffer to use to store the results. | ||||
* @param docCount This will contain the number of documents gather | ||||
ed for this batch after | ||||
* a successful call. | ||||
* | ||||
* @return true if this is not the final batch. | ||||
*/ | ||||
bool sendNextBatch( Request& r, int ntoreturn, BufBuilder& buffer, | ||||
int& docCount ); | ||||
void accessed(); | void accessed(); | |||
/** @return idle time in ms */ | /** @return idle time in ms */ | |||
long long idleTime( long long now ); | long long idleTime( long long now ); | |||
// The default initial buffer size for sending responses. | ||||
static const int INIT_REPLY_BUFFER_SIZE; | ||||
protected: | protected: | |||
ClusteredCursor * _cursor; | ClusteredCursor * _cursor; | |||
int _skip; | int _skip; | |||
int _ntoreturn; | int _ntoreturn; | |||
int _totalSent; | int _totalSent; | |||
bool _done; | bool _done; | |||
End of changes. 4 change blocks. | ||||
4 lines changed or deleted | 29 lines changed or added | |||
d_chunk_manager.h | d_chunk_manager.h | |||
---|---|---|---|---|
skipping to change at line 29 | skipping to change at line 29 | |||
#pragma once | #pragma once | |||
#include "../pch.h" | #include "../pch.h" | |||
#include "../db/jsobj.h" | #include "../db/jsobj.h" | |||
#include "util.h" | #include "util.h" | |||
namespace mongo { | namespace mongo { | |||
class ClientCursor; | class ClientCursor; | |||
class DBClientCursorInterface; | ||||
/** | /** | |||
* Controls the boundaries of all the chunks for a given collection tha t live in this shard. | * Controls the boundaries of all the chunks for a given collection tha t live in this shard. | |||
* | * | |||
* ShardChunkManager instances never change after construction. There a re methods provided that would generate a | * ShardChunkManager instances never change after construction. There a re methods provided that would generate a | |||
* new manager if new chunks are added, subtracted, or split. | * new manager if new chunks are added, subtracted, or split. | |||
* | * | |||
* TODO | * TODO | |||
* The responsibility of maintaining the version for a shard is still shared between this class and its caller. The | * The responsibility of maintaining the version for a shard is still shared between this class and its caller. The | |||
* manager does check corner cases (e.g. cloning out the last chunk g enerates a manager with version 0) but ultimately | * manager does check corner cases (e.g. cloning out the last chunk g enerates a manager with version 0) but ultimately | |||
* still cannot be responsible to set all versions. Currently, they a re a function of the global state as opposed to | * still cannot be responsible to set all versions. Currently, they a re a function of the global state as opposed to | |||
End of changes. 1 change blocks. | ||||
1 lines changed or deleted | 1 lines changed or added | |||
d_concurrency.h | d_concurrency.h | |||
---|---|---|---|---|
// @file d_concurrency.h | // @file d_concurrency.h | |||
// only used by mongod, thus the name ('d') | ||||
// (also used by dbtests test binary, which is running mongod test code) | ||||
#pragma once | #pragma once | |||
#include "../util/concurrency/rwlock.h" | #include "mongo/util/concurrency/qlock.h" | |||
#include "db/mongomutex.h" | #include "mongo/util/concurrency/mutex.h" | |||
#include "mongo/bson/stringdata.h" | ||||
#include "mongo/db/jsobj.h" | ||||
#include "mongo/db/lockstat.h" | ||||
namespace mongo { | namespace mongo { | |||
namespace clcimpl { | class WrapperForRWLock; | |||
enum LockStates { Unlocked, AcquireShared=1, LockedShared=2, Acquir | class LockState; | |||
eExclusive=4, LockedExclusive=8 }; | ||||
class Shared : boost::noncopyable { | class Lock : boost::noncopyable { | |||
unsigned& state; | public: | |||
RWLock *rw; | enum Nestable { notnestable=0, local, admin }; | |||
public: | static int isLocked(); // true if *anything* is locked (by us) | |||
Shared(unsigned& state, RWLock& lock); | static int isReadLocked(); // r or R | |||
~Shared(); | static int somethingWriteLocked(); // w or W | |||
bool recursed() const { return rw == 0; } | static bool isW(); // W | |||
}; | static bool isR(); | |||
class Exclusive : boost::noncopyable { | static bool isRW(); // R or W. i.e., we are write-exclusive | |||
unsigned& state; | static bool nested(); | |||
RWLock *rw; | static bool isWriteLocked(const StringData& ns); | |||
public: | static bool atLeastReadLocked(const StringData& ns); // true if thi | |||
Exclusive(unsigned& state, RWLock& lock); | s db is locked | |||
~Exclusive(); | static void assertAtLeastReadLocked(const StringData& ns); | |||
}; | static void assertWriteLocked(const StringData& ns); | |||
} | ||||
static bool dbLevelLockingEnabled(); | ||||
typedef readlock GlobalSharedLock; | ||||
class ScopedLock; | ||||
class ExcludeAllWrites : boost::noncopyable { | ||||
clcimpl::Exclusive lk; | // note: avoid TempRelease when possible. not a good thing. | |||
GlobalSharedLock gslk; | struct TempRelease { | |||
TempRelease(); | ||||
~TempRelease(); | ||||
const bool cant; // true if couldn't because of recursive locki | ||||
ng | ||||
ScopedLock *scopedLk; | ||||
}; | ||||
class ScopedLock : boost::noncopyable { | ||||
protected: | ||||
friend struct TempRelease; | ||||
ScopedLock(); | ||||
virtual void tempRelease() = 0; | ||||
virtual void relock() = 0; | ||||
public: | ||||
virtual ~ScopedLock(); | ||||
}; | ||||
// note that for these classes recursive locking is ok if the recur | ||||
sive locking "makes sense" | ||||
// i.e. you could grab globalread after globalwrite. | ||||
class GlobalWrite : public ScopedLock { | ||||
bool stoppedGreed; | ||||
bool noop; | ||||
protected: | ||||
void tempRelease(); | ||||
void relock(); | ||||
public: | ||||
/** @param stopGreed after acquisition stop greediness of other | ||||
threads for write locks. this | ||||
should generally not be used it is for exceptional circumst | ||||
ances. journaling uses it. | ||||
perhaps this should go away it makes the software more comp | ||||
licated. | ||||
*/ | ||||
// timeoutms is only for writelocktry -- deprecated -- do not u | ||||
se | ||||
GlobalWrite(bool stopGreed = false, int timeoutms = -1 ); | ||||
virtual ~GlobalWrite(); | ||||
void downgrade(); // W -> R | ||||
bool upgrade(); // caution see notes | ||||
}; | ||||
class GlobalRead : public ScopedLock { // recursive is ok | ||||
public: | ||||
bool noop; | ||||
protected: | ||||
void tempRelease(); | ||||
void relock(); | ||||
public: | ||||
// timeoutms is only for readlocktry -- deprecated -- do not us | ||||
e | ||||
GlobalRead( int timeoutms = -1 ); | ||||
virtual ~GlobalRead(); | ||||
}; | ||||
// lock this database. do not shared_lock globally first, that is h | ||||
andledin herein. | ||||
class DBWrite : public ScopedLock { | ||||
bool isW(LockState&) const; | ||||
void lockTop(LockState&); | ||||
void lockNestable(Nestable db); | ||||
void lockOther(const string& db); | ||||
bool locked_w; | ||||
bool locked_W; | ||||
WrapperForRWLock *weLocked; | ||||
const string what; | ||||
bool _nested; | ||||
void lockDB(const string& ns); | ||||
void unlockDB(); | ||||
protected: | ||||
void tempRelease(); | ||||
void relock(); | ||||
public: | ||||
DBWrite(const StringData& dbOrNs); | ||||
virtual ~DBWrite(); | ||||
}; | ||||
// lock this database for reading. do not shared_lock globally firs | ||||
t, that is handledin herein. | ||||
class DBRead : public ScopedLock { | ||||
bool isRW(LockState&) const; | ||||
void lockTop(LockState&); | ||||
void lockNestable(Nestable db); | ||||
void lockOther(const string& db); | ||||
bool locked_r; | ||||
WrapperForRWLock *weLocked; | ||||
string what; | ||||
bool _nested; | ||||
void lockDB(const string& ns); | ||||
void unlockDB(); | ||||
protected: | ||||
void tempRelease(); | ||||
void relock(); | ||||
public: | ||||
DBRead(const StringData& dbOrNs); | ||||
virtual ~DBRead(); | ||||
}; | ||||
}; | ||||
class readlocktry : boost::noncopyable { | ||||
bool _got; | ||||
scoped_ptr<Lock::GlobalRead> _dbrlock; | ||||
public: | public: | |||
ExcludeAllWrites(); | readlocktry( int tryms ); | |||
~ExcludeAllWrites(); | ~readlocktry(); | |||
bool got() const { return _got; } | ||||
}; | }; | |||
class todoGlobalWriteLock : boost::noncopyable { | class writelocktry : boost::noncopyable { | |||
bool _got; | ||||
scoped_ptr<Lock::GlobalWrite> _dbwlock; | ||||
public: | public: | |||
writelocktry( int tryms ); | ||||
~writelocktry(); | ||||
bool got() const { return _got; } | ||||
}; | }; | |||
class LockCollectionForReading : boost::noncopyable { | /** a mutex, but reported in curop() - thus a "high level" (HL) one | |||
GlobalSharedLock gslk; | some overhead so we don't use this for everything. the externalobj | |||
clcimpl::Shared clk; | sort mutex | |||
uses this, as it can be held for eons. implementation still needed. | ||||
*/ | ||||
class HLMutex : public SimpleMutex { | ||||
LockStat ls; | ||||
public: | public: | |||
LockCollectionForReading(string coll); | HLMutex(const char *name); | |||
~LockCollectionForReading(); | ||||
}; | }; | |||
#if defined(CLC) | // implementation stuff | |||
class LockCollectionForWriting : boost::noncopyable { | // per thread | |||
struct Locks { | class LockState { | |||
Locks(string ns); | ||||
SimpleRWLock::Shared excluder; | ||||
GlobalSharedLock gslk; | ||||
rwlock clk; | ||||
}; | ||||
scoped_ptr<Locks> locks; | ||||
public: | public: | |||
LockCollectionForWriting(string db); | LockState(); | |||
~LockCollectionForWriting(); | void dump(); | |||
static void Dump(); | ||||
void reportState(BSONObjBuilder& b); | ||||
unsigned recursiveCount() const { return _recursive; } | ||||
/** | ||||
* @return 0 rwRW | ||||
*/ | ||||
char threadState() const { return _threadState; } | ||||
void locked( char newState ); // RWrw | ||||
void unlocked(); // _threadState = 0 | ||||
/** | ||||
* you have to be locked already to call this | ||||
* this is mostly for W_to_R or R_to_W | ||||
*/ | ||||
void changeLockState( char newstate ); | ||||
Lock::Nestable whichNestable() const { return _whichNestable; } | ||||
int nestableCount() const { return _nestableCount; } | ||||
int otherCount() const { return _otherCount; } | ||||
string otherName() const { return _otherName; } | ||||
WrapperForRWLock* otherLock() const { return _otherLock; } | ||||
void enterScopedLock( Lock::ScopedLock* lock ); | ||||
Lock::ScopedLock* leaveScopedLock(); | ||||
void lockedNestable( Lock::Nestable what , int type ); | ||||
void unlockedNestable(); | ||||
void lockedOther( const string& db , int type , WrapperForRWLock* l | ||||
ock ); | ||||
void unlockedOther(); | ||||
private: | ||||
unsigned _recursive; // we allow recursively asking for a | ||||
lock; we track that here | ||||
// global lock related | ||||
char _threadState; // 0, 'r', 'w', 'R', 'W' | ||||
// db level locking related | ||||
Lock::Nestable _whichNestable; | ||||
int _nestableCount; // recursive lock count on local or | ||||
admin db XXX - change name | ||||
int _otherCount; // >0 means write lock, <0 read lo | ||||
ck - XXX change name | ||||
string _otherName; // which database are we locking and | ||||
working with (besides local/admin) | ||||
WrapperForRWLock* _otherLock; // so we don't have to check the map | ||||
too often (the map has a mutex) | ||||
// for temprelease | ||||
// for the nonrecursive case. otherwise there would be many | ||||
// the first lock goes here, which is ok since we can't yield recur | ||||
sive locks | ||||
Lock::ScopedLock* _scopedLk; | ||||
}; | }; | |||
#else | ||||
#endif | ||||
} | } | |||
End of changes. 11 change blocks. | ||||
48 lines changed or deleted | 214 lines changed or added | |||
d_globals.h | d_globals.h | |||
---|---|---|---|---|
// @file d_globals.h | // @file d_globals.h | |||
// | // | |||
// these are global variables used in mongod ("d"). also used in test bina ry as that is effectively a variation on mongod code. | // these are global variables used in mongod ("d"). also used in test bina ry as that is effectively a variation on mongod code. | |||
// that is, these are not in mongos. | // that is, these are not in mongos. | |||
// | // | |||
#pragma once | #pragma once | |||
namespace mongo { | namespace mongo { | |||
class RWLock; | class RWLockRecursive; | |||
class MongoMutex; | class MongoMutex; | |||
class ClientCursorMonitor; | class ClientCursorMonitor; | |||
struct DGlobals : boost::noncopyable { | struct DGlobals : boost::noncopyable { | |||
DGlobals(); | DGlobals(); | |||
// these are intentionally never deleted: | // these are intentionally never deleted: | |||
RWLock& writeExcluder; | ||||
MongoMutex &dbMutex; | MongoMutex &dbMutex; | |||
ClientCursorMonitor& clientCursorMonitor; | ClientCursorMonitor& clientCursorMonitor; | |||
}; | }; | |||
extern DGlobals d; | extern DGlobals d; | |||
}; | }; | |||
End of changes. 2 change blocks. | ||||
2 lines changed or deleted | 1 lines changed or added | |||
d_logic.h | d_logic.h | |||
---|---|---|---|---|
skipping to change at line 26 | skipping to change at line 26 | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../pch.h" | #include "../pch.h" | |||
#include "../db/jsobj.h" | #include "../db/jsobj.h" | |||
#include "d_chunk_manager.h" | #include "d_chunk_manager.h" | |||
#include "util.h" | #include "util.h" | |||
#include "mongo/util/concurrency/ticketholder.h" | ||||
namespace mongo { | namespace mongo { | |||
class Database; | class Database; | |||
class DiskLoc; | class DiskLoc; | |||
typedef ShardChunkVersion ConfigVersion; | typedef ShardChunkVersion ConfigVersion; | |||
// -------------- | // -------------- | |||
// --- global state --- | // --- global state --- | |||
skipping to change at line 151 | skipping to change at line 152 | |||
private: | private: | |||
bool _enabled; | bool _enabled; | |||
string _configServer; | string _configServer; | |||
string _shardName; | string _shardName; | |||
string _shardHost; | string _shardHost; | |||
// protects state below | // protects state below | |||
mutable mongo::mutex _mutex; | mutable mongo::mutex _mutex; | |||
// protects accessing the config server | ||||
// Using a ticket holder so we can have multiple redundant tries at | ||||
any given time | ||||
mutable TicketHolder _configServerTickets; | ||||
// map from a namespace into the ensemble of chunk ranges that are stored in this mongod | // map from a namespace into the ensemble of chunk ranges that are stored in this mongod | |||
// a ShardChunkManager carries all state we need for a collection a t this shard, including its version information | // a ShardChunkManager carries all state we need for a collection a t this shard, including its version information | |||
typedef map<string,ShardChunkManagerPtr> ChunkManagersMap; | typedef map<string,ShardChunkManagerPtr> ChunkManagersMap; | |||
ChunkManagersMap _chunks; | ChunkManagersMap _chunks; | |||
}; | }; | |||
extern ShardingState shardingState; | extern ShardingState shardingState; | |||
/** | /** | |||
skipping to change at line 224 | skipping to change at line 228 | |||
unsigned long long extractVersion( BSONElement e , string& errmsg ); | unsigned long long extractVersion( BSONElement e , string& errmsg ); | |||
/** | /** | |||
* @return true if we have any shard info for the ns | * @return true if we have any shard info for the ns | |||
*/ | */ | |||
bool haveLocalShardingInfo( const string& ns ); | bool haveLocalShardingInfo( const string& ns ); | |||
/** | /** | |||
* @return true if the current threads shard version is ok, or not in s harded version | * @return true if the current threads shard version is ok, or not in s harded version | |||
* Also returns an error message and the Config/ShardChunkVersions caus ing conflicts | ||||
*/ | */ | |||
bool shardVersionOk( const string& ns , string& errmsg ); | bool shardVersionOk( const string& ns , string& errmsg, ConfigVersion& received, ConfigVersion& wanted ); | |||
/** | /** | |||
* @return true if we took care of the message and nothing else should be done | * @return true if we took care of the message and nothing else should be done | |||
*/ | */ | |||
struct DbResponse; | struct DbResponse; | |||
bool _handlePossibleShardedMessage( Message &m, DbResponse * dbresponse ); | bool _handlePossibleShardedMessage( Message &m, DbResponse * dbresponse ); | |||
/** What does this do? document please? */ | /** What does this do? document please? */ | |||
inline bool handlePossibleShardedMessage( Message &m, DbResponse * dbre sponse ) { | inline bool handlePossibleShardedMessage( Message &m, DbResponse * dbre sponse ) { | |||
End of changes. 4 change blocks. | ||||
1 lines changed or deleted | 7 lines changed or added | |||
database.h | database.h | |||
---|---|---|---|---|
skipping to change at line 21 | skipping to change at line 21 | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Affero General Public License for more details. | * GNU Affero General Public License for more details. | |||
* | * | |||
* You should have received a copy of the GNU Affero General Public Licen se | * You should have received a copy of the GNU Affero General Public Licen se | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "cmdline.h" | #include "mongo/db/cmdline.h" | |||
#include "namespace.h" | #include "mongo/db/namespace_details.h" | |||
namespace mongo { | namespace mongo { | |||
class Extent; | class Extent; | |||
class MongoDataFile; | class MongoDataFile; | |||
class ClientCursor; | class ClientCursor; | |||
struct ByLocKey; | struct ByLocKey; | |||
typedef map<ByLocKey, ClientCursor*> CCByLoc; | typedef map<ByLocKey, ClientCursor*> CCByLoc; | |||
/** | /** | |||
End of changes. 1 change blocks. | ||||
2 lines changed or deleted | 2 lines changed or added | |||
databaseholder.h | databaseholder.h | |||
---|---|---|---|---|
skipping to change at line 13 | skipping to change at line 13 | |||
#pragma once | #pragma once | |||
namespace mongo { | namespace mongo { | |||
/** | /** | |||
* path + dbname -> Database | * path + dbname -> Database | |||
*/ | */ | |||
class DatabaseHolder { | class DatabaseHolder { | |||
typedef map<string,Database*> DBs; | typedef map<string,Database*> DBs; | |||
typedef map<string,DBs> Paths; | typedef map<string,DBs> Paths; | |||
// todo: we want something faster than this if called a lot: | ||||
mutable SimpleMutex _m; | ||||
Paths _paths; | ||||
int _size; | ||||
public: | public: | |||
DatabaseHolder() : _size(0) { } | DatabaseHolder() : _m("dbholder"),_size(0) { } | |||
bool __isLoaded( const string& ns , const string& path ) const { | bool __isLoaded( const string& ns , const string& path ) const { | |||
SimpleMutex::scoped_lock lk(_m); | ||||
Paths::const_iterator x = _paths.find( path ); | Paths::const_iterator x = _paths.find( path ); | |||
if ( x == _paths.end() ) | if ( x == _paths.end() ) | |||
return false; | return false; | |||
const DBs& m = x->second; | const DBs& m = x->second; | |||
string db = _todb( ns ); | string db = _todb( ns ); | |||
DBs::const_iterator it = m.find(db); | DBs::const_iterator it = m.find(db); | |||
return it != m.end(); | return it != m.end(); | |||
} | } | |||
// must be write locked as otherwise isLoaded could go false->true on you | // must be write locked as otherwise isLoaded could go false->true on you | |||
// in the background and you might not expect that. | // in the background and you might not expect that. | |||
bool _isLoaded( const string& ns , const string& path ) const { | bool _isLoaded( const string& ns , const string& path ) const { | |||
d.dbMutex.assertWriteLocked(); | Lock::assertWriteLocked(ns); | |||
return __isLoaded(ns,path); | return __isLoaded(ns,path); | |||
} | } | |||
Database * get( const string& ns , const string& path ) const { | Database * get( const string& ns , const string& path ) const { | |||
d.dbMutex.assertAtLeastReadLocked(); | SimpleMutex::scoped_lock lk(_m); | |||
Lock::assertAtLeastReadLocked(ns); | ||||
Paths::const_iterator x = _paths.find( path ); | Paths::const_iterator x = _paths.find( path ); | |||
if ( x == _paths.end() ) | if ( x == _paths.end() ) | |||
return 0; | return 0; | |||
const DBs& m = x->second; | const DBs& m = x->second; | |||
string db = _todb( ns ); | string db = _todb( ns ); | |||
DBs::const_iterator it = m.find(db); | DBs::const_iterator it = m.find(db); | |||
if ( it != m.end() ) | if ( it != m.end() ) | |||
return it->second; | return it->second; | |||
return 0; | return 0; | |||
} | } | |||
void _put( const string& ns , const string& path , Database * db ) | ||||
{ | ||||
d.dbMutex.assertAtLeastReadLocked(); | ||||
DBs& m = _paths[path]; | ||||
Database*& d = m[_todb(ns)]; | ||||
if( d ) { | ||||
dlog(2) << "info dbholder put db was already set " << ns << | ||||
endl; | ||||
} | ||||
else { | ||||
_size++; | ||||
} | ||||
d = db; | ||||
} | ||||
Database* getOrCreate( const string& ns , const string& path , bool & justCreated ); | Database* getOrCreate( const string& ns , const string& path , bool & justCreated ); | |||
void erase( const string& ns , const string& path ) { | void erase( const string& ns , const string& path ) { | |||
d.dbMutex.assertWriteLocked(); // write lock req'd as a Databas | SimpleMutex::scoped_lock lk(_m); | |||
e obj can be in use dbHolderMutex is mainly just to control the holder itse | verify( Lock::isW() ); | |||
lf | ||||
DBs& m = _paths[path]; | DBs& m = _paths[path]; | |||
_size -= (int)m.erase( _todb( ns ) ); | _size -= (int)m.erase( _todb( ns ) ); | |||
} | } | |||
/** @param force - force close even if something underway - use at shutdown */ | /** @param force - force close even if something underway - use at shutdown */ | |||
bool closeAll( const string& path , BSONObjBuilder& result, bool fo rce ); | bool closeAll( const string& path , BSONObjBuilder& result, bool fo rce ); | |||
// "info" as this is informational only could change on you if you are not write locked | // "info" as this is informational only could change on you if you are not write locked | |||
int sizeInfo() const { return _size; } | int sizeInfo() const { return _size; } | |||
void forEach(boost::function<void(Database *)> f) const { | ||||
d.dbMutex.assertWriteLocked(); | ||||
for ( Paths::const_iterator i=_paths.begin(); i!=_paths.end(); | ||||
i++ ) { | ||||
DBs m = i->second; | ||||
for( DBs::const_iterator j=m.begin(); j!=m.end(); j++ ) { | ||||
f(j->second); | ||||
} | ||||
} | ||||
} | ||||
/** | /** | |||
* gets all unique db names, ignoring paths | * gets all unique db names, ignoring paths | |||
*/ | */ | |||
void getAllShortNames( bool locked, set<string>& all ) const { | void getAllShortNames( bool locked, set<string>& all ) const { | |||
d.dbMutex.assertAtLeastReadLocked(); | SimpleMutex::scoped_lock lk(_m); | |||
for ( Paths::const_iterator i=_paths.begin(); i!=_paths.end(); i++ ) { | for ( Paths::const_iterator i=_paths.begin(); i!=_paths.end(); i++ ) { | |||
DBs m = i->second; | DBs m = i->second; | |||
for( DBs::const_iterator j=m.begin(); j!=m.end(); j++ ) { | for( DBs::const_iterator j=m.begin(); j!=m.end(); j++ ) { | |||
all.insert( j->first ); | all.insert( j->first ); | |||
} | } | |||
} | } | |||
} | } | |||
private: | private: | |||
static string _todb( const string& ns ) { | static string _todb( const string& ns ) { | |||
skipping to change at line 112 | skipping to change at line 96 | |||
} | } | |||
static string __todb( const string& ns ) { | static string __todb( const string& ns ) { | |||
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; | ||||
int _size; | ||||
}; | }; | |||
DatabaseHolder& dbHolderUnchecked(); | DatabaseHolder& dbHolderUnchecked(); | |||
inline const DatabaseHolder& dbHolder() { | inline const DatabaseHolder& dbHolder() { | |||
dassert( d.dbMutex.atLeastReadLocked() ); | dassert( Lock::isLocked() ); | |||
return dbHolderUnchecked(); | return dbHolderUnchecked(); | |||
} | } | |||
inline DatabaseHolder& dbHolderW() { | inline DatabaseHolder& dbHolderW() { | |||
dassert( d.dbMutex.isWriteLocked() ); | dassert( Lock::isW() ); | |||
return dbHolderUnchecked(); | return dbHolderUnchecked(); | |||
} | } | |||
} | } | |||
End of changes. 12 change blocks. | ||||
37 lines changed or deleted | 14 lines changed or added | |||
db.h | db.h | |||
---|---|---|---|---|
skipping to change at line 29 | skipping to change at line 29 | |||
#include "../pch.h" | #include "../pch.h" | |||
#include "../util/net/message.h" | #include "../util/net/message.h" | |||
#include "mongomutex.h" | #include "mongomutex.h" | |||
#include "pdfile.h" | #include "pdfile.h" | |||
#include "curop.h" | #include "curop.h" | |||
#include "client.h" | #include "client.h" | |||
#include "databaseholder.h" | #include "databaseholder.h" | |||
namespace mongo { | namespace mongo { | |||
// todo: relocked is being called when there was no unlock below. | ||||
// that is weird. | ||||
struct dbtemprelease { | struct dbtemprelease { | |||
Client::Context * _context; | Client::Context * _context; | |||
int _locktype; | scoped_ptr<Lock::TempRelease> tr; | |||
dbtemprelease() { | dbtemprelease() { | |||
const Client& c = cc(); | const Client& c = cc(); | |||
_context = c.getContext(); | _context = c.getContext(); | |||
_locktype = d.dbMutex.getState(); | verify( Lock::isLocked() ); | |||
assert( _locktype ); | if( Lock::nested() ) { | |||
Lock::nested(); | ||||
if ( _locktype > 0 ) { | massert(10298 , "can't temprelease nested lock", false); | |||
massert( 10298 , "can't temprelease nested write lock", _lo | ||||
cktype == 1); | ||||
if ( _context ) _context->unlocked(); | ||||
d.dbMutex.unlock(); | ||||
} | } | |||
else { | if ( _context ) { | |||
massert( 10299 , "can't temprelease nested read lock", _loc | _context->unlocked(); | |||
ktype == -1); | ||||
if ( _context ) _context->unlocked(); | ||||
d.dbMutex.unlock_shared(); | ||||
} | } | |||
tr.reset(new Lock::TempRelease); | ||||
verify( 14814 , c.curop() ); | verify( c.curop() ); | |||
c.curop()->yielded(); | c.curop()->yielded(); | |||
} | } | |||
~dbtemprelease() { | ~dbtemprelease() { | |||
if ( _locktype > 0 ) | tr.reset(); | |||
d.dbMutex.lock(); | if ( _context ) | |||
else | _context->relocked(); | |||
d.dbMutex.lock_shared(); | ||||
if ( _context ) _context->relocked(); | ||||
} | } | |||
}; | }; | |||
/** must be write locked | /** must be write locked | |||
no assert (and no release) if nested write lock | no verify(and no release) if nested write lock | |||
a lot like dbtempreleasecond but no malloc so should be a tiny bit | a lot like dbtempreleasecond, eliminate? | |||
faster | ||||
*/ | */ | |||
struct dbtempreleasewritelock { | struct dbtempreleasewritelock { | |||
Client::Context * _context; | Client::Context * _context; | |||
int _locktype; | int _locktype; | |||
scoped_ptr<Lock::TempRelease> tr; | ||||
dbtempreleasewritelock() { | dbtempreleasewritelock() { | |||
const Client& c = cc(); | const Client& c = cc(); | |||
_context = c.getContext(); | _context = c.getContext(); | |||
_locktype = d.dbMutex.getState(); | verify( Lock::isW() ); | |||
assert( _locktype >= 1 ); | if( Lock::nested() ) | |||
if( _locktype > 1 ) | return; | |||
return; // nested | ||||
if ( _context ) | if ( _context ) | |||
_context->unlocked(); | _context->unlocked(); | |||
d.dbMutex.unlock(); | tr.reset(new Lock::TempRelease); | |||
verify( 14845 , c.curop() ); | verify( c.curop() ); | |||
c.curop()->yielded(); | c.curop()->yielded(); | |||
} | } | |||
~dbtempreleasewritelock() { | ~dbtempreleasewritelock() { | |||
if ( _locktype == 1 ) | tr.reset(); | |||
d.dbMutex.lock(); | ||||
if ( _context ) | if ( _context ) | |||
_context->relocked(); | _context->relocked(); | |||
} | } | |||
}; | }; | |||
/** | /** | |||
only does a temp release if we're not nested and have a lock | only does a temp release if we're not nested and have a lock | |||
*/ | */ | |||
struct dbtempreleasecond { | class dbtempreleasecond : boost::noncopyable { | |||
dbtemprelease * real; | dbtemprelease * real; | |||
int locktype; | public: | |||
dbtempreleasecond() { | dbtempreleasecond() { | |||
real = 0; | real = 0; | |||
locktype = d.dbMutex.getState(); | if( Lock::isLocked() ) { | |||
if ( locktype == 1 || locktype == -1 ) | // if nested don't temprelease, and we don't complain eithe | |||
real = new dbtemprelease(); | r for this class | |||
if( !Lock::nested() ) { | ||||
real = new dbtemprelease(); | ||||
} | ||||
} | ||||
} | } | |||
~dbtempreleasecond() { | ~dbtempreleasecond() { | |||
if ( real ) { | if ( real ) { | |||
delete real; | delete real; | |||
real = 0; | real = 0; | |||
} | } | |||
} | } | |||
bool unlocked() const { return real != 0; } | ||||
bool unlocked() { | ||||
return real != 0; | ||||
} | ||||
}; | }; | |||
} // namespace mongo | } // namespace mongo | |||
End of changes. 17 change blocks. | ||||
46 lines changed or deleted | 34 lines changed or added | |||
dbclient.h | dbclient.h | |||
---|---|---|---|---|
/** @file dbclient.h | /** @file dbclient.h | |||
Core MongoDB C++ driver interfaces are defined here. | Include this file when writing client C++ applications, to get access t | |||
o the | ||||
mongod C++ driver. | ||||
*/ | */ | |||
/* Copyright 2009 10gen Inc. | /* Copyright 2009 10gen Inc. | |||
* | * | |||
* Licensed under the Apache License, Version 2.0 (the "License"); | * Licensed under the Apache License, Version 2.0 (the "License"); | |||
* you may not use this file except in compliance with the License. | * you may not use this file except in compliance with the License. | |||
* You may obtain a copy of the License at | * You may obtain a copy of the License at | |||
* | * | |||
* http://www.apache.org/licenses/LICENSE-2.0 | * http://www.apache.org/licenses/LICENSE-2.0 | |||
* | * | |||
* Unless required by applicable law or agreed to in writing, software | * Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | * distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. | |||
* See the License for the specific language governing permissions and | * See the License for the specific language governing permissions and | |||
* limitations under the License. | * limitations under the License. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../pch.h" | #ifdef MONGO_EXPOSE_MACROS | |||
#include "../util/net/message.h" | #error dbclient.h is for C++ driver consumer use only | |||
#include "../util/net/message_port.h" | ||||
#include "../db/jsobj.h" | ||||
#include "../db/json.h" | ||||
#include "../db/security.h" | ||||
#include <stack> | ||||
namespace mongo { | ||||
/** the query field 'options' can have these bits set: */ | ||||
enum QueryOptions { | ||||
/** Tailable means cursor is not closed when the last data is retri | ||||
eved. rather, the cursor marks | ||||
the final object's position. you can resume using the cursor la | ||||
ter, from where it was located, | ||||
if more data were received. Set on dbQuery and dbGetMore. | ||||
like any "latent cursor", the cursor may become invalid at some | ||||
point -- for example if that | ||||
final object it references were deleted. Thus, you should be pr | ||||
epared to requery if you get back | ||||
ResultFlag_CursorNotFound. | ||||
*/ | ||||
QueryOption_CursorTailable = 1 << 1, | ||||
/** allow query of replica slave. normally these return an error e | ||||
xcept for namespace "local". | ||||
*/ | ||||
QueryOption_SlaveOk = 1 << 2, | ||||
// findingStart mode is used to find the first operation of interes | ||||
t when | ||||
// we are scanning through a repl log. For efficiency in the commo | ||||
n case, | ||||
// where the first operation of interest is closer to the tail than | ||||
the head, | ||||
// we start from the tail of the log and work backwards until we fi | ||||
nd the | ||||
// first operation of interest. Then we scan forward from that fir | ||||
st operation, | ||||
// actually returning results to the client. During the findingSta | ||||
rt phase, | ||||
// we release the db mutex occasionally to avoid blocking the db pr | ||||
ocess for | ||||
// an extended period of time. | ||||
QueryOption_OplogReplay = 1 << 3, | ||||
/** The server normally times out idle cursors after an inactivy pe | ||||
riod to prevent excess memory uses | ||||
Set this option to prevent that. | ||||
*/ | ||||
QueryOption_NoCursorTimeout = 1 << 4, | ||||
/** Use with QueryOption_CursorTailable. If we are at the end of t | ||||
he data, block for a while rather | ||||
than returning no data. After a timeout period, we do return as | ||||
normal. | ||||
*/ | ||||
QueryOption_AwaitData = 1 << 5, | ||||
/** Stream the data down full blast in multiple "more" packages, on | ||||
the assumption that the client | ||||
will fully read all data queried. Faster when you are pulling | ||||
a lot of data and know you want to | ||||
pull it all down. Note: it is not allowed to not read all the | ||||
data unless you close the connection. | ||||
Use the query( boost::function<void(const BSONObj&)> f, ... ) v | ||||
ersion of the connection's query() | ||||
method, and it will take care of all the details for you. | ||||
*/ | ||||
QueryOption_Exhaust = 1 << 6, | ||||
/** When sharded, this means its ok to return partial results | ||||
Usually we will fail a query if all required shards aren't up | ||||
If this is set, it'll be a partial result set | ||||
*/ | ||||
QueryOption_PartialResults = 1 << 7 , | ||||
QueryOption_AllSupported = QueryOption_CursorTailable | QueryOption | ||||
_SlaveOk | QueryOption_OplogReplay | QueryOption_NoCursorTimeout | QueryOpt | ||||
ion_AwaitData | QueryOption_Exhaust | QueryOption_PartialResults | ||||
}; | ||||
enum UpdateOptions { | ||||
/** Upsert - that is, insert the item if no matching item is found. | ||||
*/ | ||||
UpdateOption_Upsert = 1 << 0, | ||||
/** Update multiple documents (if multiple documents match query ex | ||||
pression). | ||||
(Default is update a single document and stop.) */ | ||||
UpdateOption_Multi = 1 << 1, | ||||
/** flag from mongo saying this update went everywhere */ | ||||
UpdateOption_Broadcast = 1 << 2 | ||||
}; | ||||
enum RemoveOptions { | ||||
/** only delete one option */ | ||||
RemoveOption_JustOne = 1 << 0, | ||||
/** flag from mongo saying this update went everywhere */ | ||||
RemoveOption_Broadcast = 1 << 1 | ||||
}; | ||||
/** | ||||
* need to put in DbMesssage::ReservedOptions as well | ||||
*/ | ||||
enum InsertOptions { | ||||
/** With muli-insert keep processing inserts if one fails */ | ||||
InsertOption_ContinueOnError = 1 << 0 | ||||
}; | ||||
class DBClientBase; | ||||
/** | ||||
* ConnectionString handles parsing different ways to connect to mongo | ||||
and determining method | ||||
* samples: | ||||
* server | ||||
* server:port | ||||
* foo/server:port,server:port SET | ||||
* server,server,server SYNC | ||||
* | ||||
* tyipcal use | ||||
* string errmsg, | ||||
* ConnectionString cs = ConnectionString::parse( url , errmsg ); | ||||
* if ( ! cs.isValid() ) throw "bad: " + errmsg; | ||||
* DBClientBase * conn = cs.connect( errmsg ); | ||||
*/ | ||||
class ConnectionString { | ||||
public: | ||||
enum ConnectionType { INVALID , MASTER , PAIR , SET , SYNC }; | ||||
ConnectionString() { | ||||
_type = INVALID; | ||||
} | ||||
ConnectionString( const HostAndPort& server ) { | ||||
_type = MASTER; | ||||
_servers.push_back( server ); | ||||
_finishInit(); | ||||
} | ||||
ConnectionString( ConnectionType type , const string& s , const str | ||||
ing& setName = "" ) { | ||||
_type = type; | ||||
_setName = setName; | ||||
_fillServers( s ); | ||||
switch ( _type ) { | ||||
case MASTER: | ||||
assert( _servers.size() == 1 ); | ||||
break; | ||||
case SET: | ||||
assert( _setName.size() ); | ||||
assert( _servers.size() >= 1 ); // 1 is ok since we can der | ||||
ive | ||||
break; | ||||
case PAIR: | ||||
assert( _servers.size() == 2 ); | ||||
break; | ||||
default: | ||||
assert( _servers.size() > 0 ); | ||||
} | ||||
_finishInit(); | ||||
} | ||||
ConnectionString( const string& s , ConnectionType favoredMultipleT | ||||
ype ) { | ||||
_type = INVALID; | ||||
_fillServers( s ); | ||||
if ( _type != INVALID ) { | ||||
// set already | ||||
} | ||||
else if ( _servers.size() == 1 ) { | ||||
_type = MASTER; | ||||
} | ||||
else { | ||||
_type = favoredMultipleType; | ||||
assert( _type == SET || _type == SYNC ); | ||||
} | ||||
_finishInit(); | ||||
} | ||||
bool isValid() const { return _type != INVALID; } | ||||
string toString() const { return _string; } | ||||
DBClientBase* connect( string& errmsg, double socketTimeout = 0 ) c | ||||
onst; | ||||
string getSetName() const { return _setName; } | ||||
vector<HostAndPort> getServers() const { return _servers; } | ||||
ConnectionType type() const { return _type; } | ||||
static ConnectionString parse( const string& url , string& errmsg ) | ||||
; | ||||
static string typeToString( ConnectionType type ); | ||||
private: | ||||
void _fillServers( string s ); | ||||
void _finishInit(); | ||||
ConnectionType _type; | ||||
vector<HostAndPort> _servers; | ||||
string _string; | ||||
string _setName; | ||||
}; | ||||
/** | ||||
* controls how much a clients cares about writes | ||||
* default is NORMAL | ||||
*/ | ||||
enum WriteConcern { | ||||
W_NONE = 0 , // TODO: not every connection type fully supports this | ||||
W_NORMAL = 1 | ||||
// TODO SAFE = 2 | ||||
}; | ||||
class BSONObj; | ||||
class ScopedDbConnection; | ||||
class DBClientCursor; | ||||
class DBClientCursorBatchIterator; | ||||
/** Represents a Mongo query expression. Typically one uses the QUERY( | ||||
...) macro to construct a Query object. | ||||
Examples: | ||||
QUERY( "age" << 33 << "school" << "UCLA" ).sort("name") | ||||
QUERY( "age" << GT << 30 << LT << 50 ) | ||||
*/ | ||||
class Query { | ||||
public: | ||||
BSONObj obj; | ||||
Query() : obj(BSONObj()) { } | ||||
Query(const BSONObj& b) : obj(b) { } | ||||
Query(const string &json) : | ||||
obj(fromjson(json)) { } | ||||
Query(const char * json) : | ||||
obj(fromjson(json)) { } | ||||
/** Add a sort (ORDER BY) criteria to the query expression. | ||||
@param sortPattern the sort order template. For example to ord | ||||
er by name ascending, time descending: | ||||
{ name : 1, ts : -1 } | ||||
i.e. | ||||
BSON( "name" << 1 << "ts" << -1 ) | ||||
or | ||||
fromjson(" name : 1, ts : -1 ") | ||||
*/ | ||||
Query& sort(const BSONObj& sortPattern); | ||||
/** Add a sort (ORDER BY) criteria to the query expression. | ||||
This version of sort() assumes you want to sort on a single fie | ||||
ld. | ||||
@param asc = 1 for ascending order | ||||
asc = -1 for descending order | ||||
*/ | ||||
Query& sort(const string &field, int asc = 1) { sort( BSON( field < | ||||
< asc ) ); return *this; } | ||||
/** Provide a hint to the query. | ||||
@param keyPattern Key pattern for the index to use. | ||||
Example: | ||||
hint("{ts:1}") | ||||
*/ | ||||
Query& hint(BSONObj keyPattern); | ||||
Query& hint(const string &jsonKeyPatt) { return hint(fromjson(jsonK | ||||
eyPatt)); } | ||||
/** Provide min and/or max index limits for the query. | ||||
min <= x < max | ||||
*/ | ||||
Query& minKey(const BSONObj &val); | ||||
/** | ||||
max is exclusive | ||||
*/ | ||||
Query& maxKey(const BSONObj &val); | ||||
/** Return explain information about execution of this query instea | ||||
d of the actual query results. | ||||
Normally it is easier to use the mongo shell to run db.find(... | ||||
).explain(). | ||||
*/ | ||||
Query& explain(); | ||||
/** Use snapshot mode for the query. Snapshot mode assures no dupl | ||||
icates are returned, or objects missed, which were | ||||
present at both the start and end of the query's execution (if | ||||
an object is new during the query, or deleted during | ||||
the query, it may or may not be returned, even with snapshot mo | ||||
de). | ||||
Note that short query responses (less than 1MB) are always effe | ||||
ctively snapshotted. | ||||
Currently, snapshot mode may not be used with sorting or explic | ||||
it hints. | ||||
*/ | ||||
Query& snapshot(); | ||||
/** Queries to the Mongo database support a $where parameter option | ||||
which contains | ||||
a javascript function that is evaluated to see whether objects | ||||
being queried match | ||||
its criteria. Use this helper to append such a function to a q | ||||
uery object. | ||||
Your query may also contain other traditional Mongo query terms | ||||
. | ||||
@param jscode The javascript function to evaluate against each | ||||
potential object | ||||
match. The function must return true for matched object | ||||
s. Use the this | ||||
variable to inspect the current object. | ||||
@param scope SavedContext for the javascript object. List in a | ||||
BSON object any | ||||
variables you would like defined when the jscode execute | ||||
s. One can think | ||||
of these as "bind variables". | ||||
Examples: | ||||
conn.findOne("test.coll", Query("{a:3}").where("this.b == 2 | | ||||
| this.c == 3")); | ||||
Query badBalance = Query().where("this.debits - this.credits | ||||
< 0"); | ||||
*/ | ||||
Query& where(const string &jscode, BSONObj scope); | ||||
Query& where(const string &jscode) { return where(jscode, BSONObj() | ||||
); } | ||||
/** | ||||
* @return true if this query has an orderby, hint, or some other f | ||||
ield | ||||
*/ | ||||
bool isComplex( bool * hasDollar = 0 ) const; | ||||
BSONObj getFilter() const; | ||||
BSONObj getSort() const; | ||||
BSONObj getHint() const; | ||||
bool isExplain() const; | ||||
string toString() const; | ||||
operator string() const { return toString(); } | ||||
private: | ||||
void makeComplex(); | ||||
template< class T > | ||||
void appendComplex( const char *fieldName, const T& val ) { | ||||
makeComplex(); | ||||
BSONObjBuilder b; | ||||
b.appendElements(obj); | ||||
b.append(fieldName, val); | ||||
obj = b.obj(); | ||||
} | ||||
}; | ||||
/** | ||||
* Represents a full query description, including all options required | ||||
for the query to be passed on | ||||
* to other hosts | ||||
*/ | ||||
class QuerySpec { | ||||
string _ns; | ||||
int _ntoskip; | ||||
int _ntoreturn; | ||||
int _options; | ||||
BSONObj _query; | ||||
BSONObj _fields; | ||||
Query _queryObj; | ||||
public: | ||||
QuerySpec( const string& ns, | ||||
const BSONObj& query, const BSONObj& fields, | ||||
int ntoskip, int ntoreturn, int options ) | ||||
: _ns( ns ), _ntoskip( ntoskip ), _ntoreturn( ntoreturn ), _opt | ||||
ions( options ), | ||||
_query( query.getOwned() ), _fields( fields.getOwned() ) , _q | ||||
ueryObj( _query ) { | ||||
} | ||||
QuerySpec() {} | ||||
bool isEmpty() const { return _ns.size() == 0; } | ||||
bool isExplain() const { return _queryObj.isExplain(); } | ||||
BSONObj filter() const { return _queryObj.getFilter(); } | ||||
BSONObj hint() const { return _queryObj.getHint(); } | ||||
BSONObj sort() const { return _queryObj.getSort(); } | ||||
BSONObj query() const { return _query; } | ||||
BSONObj fields() const { return _fields; } | ||||
BSONObj* fieldsData() { return &_fields; } | ||||
// don't love this, but needed downstrem | ||||
const BSONObj* fieldsPtr() const { return &_fields; } | ||||
string ns() const { return _ns; } | ||||
int ntoskip() const { return _ntoskip; } | ||||
int ntoreturn() const { return _ntoreturn; } | ||||
int options() const { return _options; } | ||||
void setFields( BSONObj& o ) { _fields = o.getOwned(); } | ||||
string toString() const { | ||||
return str::stream() << "QSpec " << | ||||
BSON( "ns" << _ns << "n2skip" << _ntoskip << "n2return" << | ||||
_ntoreturn << "options" << _options | ||||
<< "query" << _query << "fields" << _fields ); | ||||
} | ||||
}; | ||||
/** Typically one uses the QUERY(...) macro to construct a Query object | ||||
. | ||||
Example: QUERY( "age" << 33 << "school" << "UCLA" ) | ||||
*/ | ||||
#define QUERY(x) mongo::Query( BSON(x) ) | ||||
// Useful utilities for namespaces | ||||
/** @return the database name portion of an ns string */ | ||||
string nsGetDB( const string &ns ); | ||||
/** @return the collection name portion of an ns string */ | ||||
string nsGetCollection( const string &ns ); | ||||
/** | ||||
interface that handles communication with the db | ||||
*/ | ||||
class DBConnector { | ||||
public: | ||||
virtual ~DBConnector() {} | ||||
/** actualServer is set to the actual server where they call went i | ||||
f there was a choice (SlaveOk) */ | ||||
virtual bool call( Message &toSend, Message &response, bool assertO | ||||
k=true , string * actualServer = 0 ) = 0; | ||||
virtual void say( Message &toSend, bool isRetry = false , string * | ||||
actualServer = 0 ) = 0; | ||||
virtual void sayPiggyBack( Message &toSend ) = 0; | ||||
/* used by QueryOption_Exhaust. To use that your subclass must imp | ||||
lement this. */ | ||||
virtual bool recv( Message& m ) { assert(false); return false; } | ||||
// In general, for lazy queries, we'll need to say, recv, then chec | ||||
kResponse | ||||
virtual void checkResponse( const char* data, int nReturned, bool* | ||||
retry = NULL, string* targetHost = NULL ) { | ||||
if( retry ) *retry = false; if( targetHost ) *targetHost = ""; | ||||
} | ||||
virtual bool lazySupported() const = 0; | ||||
}; | ||||
/** | ||||
The interface that any db connection should implement | ||||
*/ | ||||
class DBClientInterface : boost::noncopyable { | ||||
public: | ||||
virtual auto_ptr<DBClientCursor> query(const string &ns, Query quer | ||||
y, int nToReturn = 0, int nToSkip = 0, | ||||
const BSONObj *fieldsToRetur | ||||
n = 0, int queryOptions = 0 , int batchSize = 0 ) = 0; | ||||
virtual void insert( const string &ns, BSONObj obj , int flags=0) = | ||||
0; | ||||
virtual void insert( const string &ns, const vector< BSONObj >& v , | ||||
int flags=0) = 0; | ||||
virtual void remove( const string &ns , Query query, bool justOne = | ||||
0 ) = 0; | ||||
virtual void update( const string &ns , Query query , BSONObj obj , | ||||
bool upsert = 0 , bool multi = 0 ) = 0; | ||||
virtual ~DBClientInterface() { } | ||||
/** | ||||
@return a single object that matches the query. if none do, the | ||||
n the object is empty | ||||
@throws AssertionException | ||||
*/ | ||||
virtual BSONObj findOne(const string &ns, const Query& query, const | ||||
BSONObj *fieldsToReturn = 0, int queryOptions = 0); | ||||
/** query N objects from the database into an array. makes sense m | ||||
ostly when you want a small number of results. if a huge number, use | ||||
query() and iterate the cursor. | ||||
*/ | ||||
void findN(vector<BSONObj>& out, const string&ns, Query query, int | ||||
nToReturn, int nToSkip = 0, const BSONObj *fieldsToReturn = 0, int queryOpt | ||||
ions = 0); | ||||
virtual string getServerAddress() const = 0; | ||||
/** don't use this - called automatically by DBClientCursor for you | ||||
*/ | ||||
virtual auto_ptr<DBClientCursor> getMore( const string &ns, long lo | ||||
ng cursorId, int nToReturn = 0, int options = 0 ) = 0; | ||||
}; | ||||
/** | ||||
DB "commands" | ||||
Basically just invocations of connection.$cmd.findOne({...}); | ||||
*/ | ||||
class DBClientWithCommands : public DBClientInterface { | ||||
set<string> _seenIndexes; | ||||
public: | ||||
/** controls how chatty the client is about network errors & such. | ||||
See log.h */ | ||||
int _logLevel; | ||||
DBClientWithCommands() : _logLevel(0), _cachedAvailableOptions( (en | ||||
um QueryOptions)0 ), _haveCachedAvailableOptions(false) { } | ||||
/** helper function. run a simple command where the command expres | ||||
sion is simply | ||||
{ command : 1 } | ||||
@param info -- where to put result object. may be null if call | ||||
er doesn't need that info | ||||
@param command -- command name | ||||
@return true if the command returned "ok". | ||||
*/ | ||||
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 | ||||
commands have prebuilt helper functions -- see below. If a hel | ||||
per is not available you can | ||||
directly call runCommand. | ||||
@param dbname database name. Use "admin" for global administra | ||||
tive commands. | ||||
@param cmd the command object to execute. For example, { isma | ||||
ster : 1 } | ||||
@param info the result object the database returns. Typically h | ||||
as { ok : ..., errmsg : ... } fields | ||||
set. | ||||
@param options see enum QueryOptions - normally not needed to r | ||||
un a command | ||||
@return true if the command returned "ok". | ||||
*/ | ||||
virtual bool runCommand(const string &dbname, const BSONObj& cmd, B | ||||
SONObj &info, int options=0); | ||||
/** Authorize access to a particular database. | ||||
Authentication is separate for each database on the server -- y | ||||
ou may authenticate for any | ||||
number of databases on a single connection. | ||||
The "admin" database is special and once authenticated provides | ||||
access to all databases on the | ||||
server. | ||||
@param digestPassword if password is plain text, set this | ||||
to true. otherwise assumed to be pre-digested | ||||
@param[out] authLevel level of authentication for the giv | ||||
en user | ||||
@return true if successful | ||||
*/ | ||||
virtual bool auth(const string &dbname, const string &username, con | ||||
st string &pwd, string& errmsg, bool digestPassword = true, Auth::Level * l | ||||
evel = NULL); | ||||
/** count number of objects in collection ns that match the query c | ||||
riteria specified | ||||
throws UserAssertion if database returns an error | ||||
*/ | ||||
virtual unsigned long long count(const string &ns, const BSONObj& q | ||||
uery = BSONObj(), int options=0, int limit=0, int skip=0 ); | ||||
string createPasswordDigest( const string &username , const string | ||||
&clearTextPassword ); | ||||
/** returns true in isMaster parm if this db is the current master | ||||
of a replica pair. | ||||
pass in info for more details e.g.: | ||||
{ "ismaster" : 1.0 , "msg" : "not paired" , "ok" : 1.0 } | ||||
returns true if command invoked successfully. | ||||
*/ | ||||
virtual bool isMaster(bool& isMaster, BSONObj *info=0); | ||||
/** | ||||
Create a new collection in the database. Normally, collection c | ||||
reation is automatic. You would | ||||
use this function if you wish to specify special options on crea | ||||
tion. | ||||
If the collection already exists, no action occurs. | ||||
@param ns fully qualified collection name | ||||
@param size desired initial extent size for the collection. | ||||
Must be <= 1000000000 for normal collections. | ||||
For fixed size (capped) collections, this size is | ||||
the total/max size of the | ||||
collection. | ||||
@param capped if true, this is a fixed size collection (where ol | ||||
d data rolls out). | ||||
@param max maximum number of objects if capped (optional). | ||||
returns true if successful. | ||||
*/ | ||||
bool createCollection(const string &ns, long long size = 0, bool ca | ||||
pped = false, int max = 0, BSONObj *info = 0); | ||||
/** Get error result from the last write operation (insert/update/d | ||||
elete) on this connection. | ||||
@return error message text, or empty string if no error. | ||||
*/ | ||||
string getLastError(bool fsync = false, bool j = false, int w = 0, | ||||
int wtimeout = 0); | ||||
/** Get error result from the last write operation (insert/update/d | ||||
elete) on this connection. | ||||
@return full error object. | ||||
If "w" is -1, wait for propagation to majority of nodes. | ||||
If "wtimeout" is 0, the operation will block indefinitely if ne | ||||
eded. | ||||
*/ | ||||
virtual BSONObj getLastErrorDetailed(bool fsync = false, bool j = f | ||||
alse, int w = 0, int wtimeout = 0); | ||||
/** Can be called with the returned value from getLastErrorDetailed | ||||
to extract an error string. | ||||
If all you need is the string, just call getLastError() instead | ||||
. | ||||
*/ | ||||
static string getLastErrorString( const BSONObj& res ); | ||||
/** Return the last error which has occurred, even if not the very | ||||
last operation. | ||||
@return { err : <error message>, nPrev : <how_many_ops_back_occu | ||||
rred>, ok : 1 } | ||||
result.err will be null if no error has occurred. | ||||
*/ | ||||
BSONObj getPrevError(); | ||||
/** Reset the previous error state for this connection (accessed vi | ||||
a getLastError and | ||||
getPrevError). Useful when performing several operations at on | ||||
ce and then checking | ||||
for an error after attempting all operations. | ||||
*/ | ||||
bool resetError() { return simpleCommand("admin", 0, "reseterror"); | ||||
} | ||||
/** Delete the specified collection. */ | ||||
virtual bool dropCollection( const string &ns ) { | ||||
string db = nsGetDB( ns ); | ||||
string coll = nsGetCollection( ns ); | ||||
uassert( 10011 , "no collection name", coll.size() ); | ||||
BSONObj info; | ||||
bool res = runCommand( db.c_str() , BSON( "drop" << coll ) , in | ||||
fo ); | ||||
resetIndexCache(); | ||||
return res; | ||||
} | ||||
/** Perform a repair and compaction of the specified database. May | ||||
take a long time to run. Disk space | ||||
must be available equal to the size of the database while repair | ||||
ing. | ||||
*/ | ||||
bool repairDatabase(const string &dbname, BSONObj *info = 0) { | ||||
return simpleCommand(dbname, info, "repairDatabase"); | ||||
} | ||||
/** Copy database from one server or name to another server or name | ||||
. | ||||
Generally, you should dropDatabase() first as otherwise the copi | ||||
ed information will MERGE | ||||
into whatever data is already present in this database. | ||||
For security reasons this function only works when you are autho | ||||
rized to access the "admin" db. However, | ||||
if you have access to said db, you can copy any database from on | ||||
e place to another. | ||||
TODO: this needs enhancement to be more flexible in terms of sec | ||||
urity. | ||||
This method provides a way to "rename" a database by copying it | ||||
to a new db name and | ||||
location. The copy is "repaired" and compacted. | ||||
fromdb database name from which to copy. | ||||
todb database name to copy to. | ||||
fromhost hostname of the database (and optionally, ":port") from | ||||
which to | ||||
copy the data. copies from self if "". | ||||
returns true if successful | ||||
*/ | ||||
bool copyDatabase(const string &fromdb, const string &todb, const s | ||||
tring &fromhost = "", BSONObj *info = 0); | ||||
/** The Mongo database provides built-in performance profiling capa | ||||
bilities. Uset setDbProfilingLevel() | ||||
to enable. Profiling information is then written to the system. | ||||
profiling collection, which one can | ||||
then query. | ||||
*/ | ||||
enum ProfilingLevel { | ||||
ProfileOff = 0, | ||||
ProfileSlow = 1, // log very slow (>100ms) operations | ||||
ProfileAll = 2 | ||||
}; | ||||
bool setDbProfilingLevel(const string &dbname, ProfilingLevel level | ||||
, BSONObj *info = 0); | ||||
bool getDbProfilingLevel(const string &dbname, ProfilingLevel& leve | ||||
l, BSONObj *info = 0); | ||||
/** This implicitly converts from char*, string, and BSONObj to be | ||||
an argument to mapreduce | ||||
You shouldn't need to explicitly construct this | ||||
*/ | ||||
struct MROutput { | ||||
MROutput(const char* collection) : out(BSON("replace" << collec | ||||
tion)) {} | ||||
MROutput(const string& collection) : out(BSON("replace" << coll | ||||
ection)) {} | ||||
MROutput(const BSONObj& obj) : out(obj) {} | ||||
BSONObj out; | ||||
}; | ||||
static MROutput MRInline; | ||||
/** Run a map/reduce job on the server. | ||||
See http://www.mongodb.org/display/DOCS/MapReduce | ||||
ns namespace (db+collection name) of input data | ||||
jsmapf javascript map function code | ||||
jsreducef javascript reduce function code. | ||||
query optional query filter for the input | ||||
output either a string collection name or an object represen | ||||
ting output type | ||||
if not specified uses inline output type | ||||
returns a result object which contains: | ||||
{ result : <collection_name>, | ||||
numObjects : <number_of_objects_scanned>, | ||||
timeMillis : <job_time>, | ||||
ok : <1_if_ok>, | ||||
[, err : <errmsg_if_error>] | ||||
} | ||||
For example one might call: | ||||
result.getField("ok").trueValue() | ||||
on the result to check if ok. | ||||
*/ | ||||
BSONObj mapreduce(const string &ns, const string &jsmapf, const str | ||||
ing &jsreducef, BSONObj query = BSONObj(), MROutput output = MRInline); | ||||
/** Run javascript code on the database server. | ||||
dbname database SavedContext in which the code runs. The java | ||||
script variable 'db' will be assigned | ||||
to this database when the function is invoked. | ||||
jscode source code for a javascript function. | ||||
info the command object which contains any information on t | ||||
he invocation result including | ||||
the return value and other information. If an error | ||||
occurs running the jscode, error | ||||
information will be in info. (try "out() << info.toSt | ||||
ring()") | ||||
retValue return value from the jscode function. | ||||
args args to pass to the jscode function. when invoked, th | ||||
e 'args' variable will be defined | ||||
for use by the jscode. | ||||
returns true if runs ok. | ||||
See testDbEval() in dbclient.cpp for an example of usage. | ||||
*/ | ||||
bool eval(const string &dbname, const string &jscode, BSONObj& info | ||||
, BSONElement& retValue, BSONObj *args = 0); | ||||
/** validate a collection, checking for errors and reporting back s | ||||
tatistics. | ||||
this operation is slow and blocking. | ||||
*/ | ||||
bool validate( const string &ns , bool scandata=true ) { | ||||
BSONObj cmd = BSON( "validate" << nsGetCollection( ns ) << "sca | ||||
ndata" << scandata ); | ||||
BSONObj info; | ||||
return runCommand( nsGetDB( ns ).c_str() , cmd , info ); | ||||
} | ||||
/* The following helpers are simply more convenient forms of eval() | ||||
for certain common cases */ | ||||
/* invocation with no return value of interest -- with or without o | ||||
ne simple parameter */ | ||||
bool eval(const string &dbname, const string &jscode); | ||||
template< class T > | ||||
bool eval(const string &dbname, const string &jscode, T parm1) { | ||||
BSONObj info; | ||||
BSONElement retValue; | ||||
BSONObjBuilder b; | ||||
b.append("0", parm1); | ||||
BSONObj args = b.done(); | ||||
return eval(dbname, jscode, info, retValue, &args); | ||||
} | ||||
/** eval invocation with one parm to server and one numeric field ( | ||||
either int or double) returned */ | ||||
template< class T, class NumType > | ||||
bool eval(const string &dbname, const string &jscode, T parm1, NumT | ||||
ype& ret) { | ||||
BSONObj info; | ||||
BSONElement retValue; | ||||
BSONObjBuilder b; | ||||
b.append("0", parm1); | ||||
BSONObj args = b.done(); | ||||
if ( !eval(dbname, jscode, info, retValue, &args) ) | ||||
return false; | ||||
ret = (NumType) retValue.number(); | ||||
return true; | ||||
} | ||||
/** | ||||
get a list of all the current databases | ||||
uses the { listDatabases : 1 } command. | ||||
throws on error | ||||
*/ | ||||
list<string> getDatabaseNames(); | ||||
/** | ||||
get a list of all the current collections in db | ||||
*/ | ||||
list<string> getCollectionNames( const string& db ); | ||||
bool exists( const string& ns ); | ||||
/** Create an index if it does not already exist. | ||||
ensureIndex calls are remembered so it is safe/fast to call thi | ||||
s function many | ||||
times in your code. | ||||
@param ns collection to be indexed | ||||
@param keys the "key pattern" for the index. e.g., { name : 1 } | ||||
@param unique if true, indicates that key uniqueness should be e | ||||
nforced for this index | ||||
@param name if not specified, it will be created from the keys a | ||||
utomatically (which is recommended) | ||||
@param cache if set to false, the index cache for the connection | ||||
won't remember this call | ||||
@param background build index in the background (see mongodb doc | ||||
s/wiki for details) | ||||
@param v index version. leave at default value. (unit tests set | ||||
this parameter.) | ||||
@return whether or not sent message to db. | ||||
should be true on first call, false on subsequent unless reset | ||||
IndexCache was called | ||||
*/ | ||||
virtual bool ensureIndex( const string &ns , BSONObj keys , bool un | ||||
ique = false, const string &name = "", | ||||
bool cache = true, bool background = fals | ||||
e, int v = -1 ); | ||||
/** | ||||
clears the index cache, so the subsequent call to ensureIndex fo | ||||
r any index will go to the server | ||||
*/ | ||||
virtual void resetIndexCache(); | ||||
virtual auto_ptr<DBClientCursor> getIndexes( const string &ns ); | ||||
virtual void dropIndex( const string& ns , BSONObj keys ); | ||||
virtual void dropIndex( const string& ns , const string& indexName | ||||
); | ||||
/** | ||||
drops all indexes for the collection | ||||
*/ | ||||
virtual void dropIndexes( const string& ns ); | ||||
virtual void reIndex( const string& ns ); | ||||
string genIndexName( const BSONObj& keys ); | ||||
/** Erase / drop an entire database */ | ||||
virtual bool dropDatabase(const string &dbname, BSONObj *info = 0) | ||||
{ | ||||
bool ret = simpleCommand(dbname, info, "dropDatabase"); | ||||
resetIndexCache(); | ||||
return ret; | ||||
} | ||||
virtual string toString() = 0; | ||||
protected: | ||||
/** if the result of a command is ok*/ | ||||
bool isOk(const BSONObj&); | ||||
/** if the element contains a not master error */ | ||||
bool isNotMasterErrorString( const BSONElement& e ); | ||||
BSONObj _countCmd(const string &ns, const BSONObj& query, int optio | ||||
ns, int limit, int skip ); | ||||
enum QueryOptions availableOptions(); | ||||
private: | ||||
enum QueryOptions _cachedAvailableOptions; | ||||
bool _haveCachedAvailableOptions; | ||||
}; | ||||
/** | ||||
abstract class that implements the core db operations | ||||
*/ | ||||
class DBClientBase : public DBClientWithCommands, public DBConnector { | ||||
protected: | ||||
WriteConcern _writeConcern; | ||||
public: | ||||
DBClientBase() { | ||||
_writeConcern = W_NORMAL; | ||||
} | ||||
WriteConcern getWriteConcern() const { return _writeConcern; } | ||||
void setWriteConcern( WriteConcern w ) { _writeConcern = w; } | ||||
/** send a query to the database. | ||||
@param ns namespace to query, format is <dbname>.<collectname>[.<c | ||||
ollectname>]* | ||||
@param query query to perform on the collection. this is a BSONOb | ||||
j (binary JSON) | ||||
You may format as | ||||
{ query: { ... }, orderby: { ... } } | ||||
to specify a sort order. | ||||
@param nToReturn n to return (i.e., limit). 0 = unlimited | ||||
@param nToSkip start with the nth item | ||||
@param fieldsToReturn optional template of which fields to select. | ||||
if unspecified, returns all fields | ||||
@param queryOptions see options enum at top of this file | ||||
@return cursor. 0 if error (connection failure) | ||||
@throws AssertionException | ||||
*/ | ||||
virtual auto_ptr<DBClientCursor> query(const string &ns, Query quer | ||||
y, int nToReturn = 0, int nToSkip = 0, | ||||
const BSONObj *fieldsToRetur | ||||
n = 0, int queryOptions = 0 , int batchSize = 0 ); | ||||
/** don't use this - called automatically by DBClientCursor for you | ||||
@param cursorId id of cursor to retrieve | ||||
@return an handle to a previously allocated cursor | ||||
@throws AssertionException | ||||
*/ | ||||
virtual auto_ptr<DBClientCursor> getMore( const string &ns, long lo | ||||
ng cursorId, int nToReturn = 0, int options = 0 ); | ||||
/** | ||||
insert an object into the database | ||||
*/ | ||||
virtual void insert( const string &ns , BSONObj obj , int flags=0); | ||||
/** | ||||
insert a vector of objects into the database | ||||
*/ | ||||
virtual void insert( const string &ns, const vector< BSONObj >& v , | ||||
int flags=0); | ||||
/** | ||||
remove matching objects from the database | ||||
@param justOne if this true, then once a single match is found w | ||||
ill stop | ||||
*/ | ||||
virtual void remove( const string &ns , Query q , bool justOne = 0 | ||||
); | ||||
/** | ||||
updates objects matching query | ||||
*/ | ||||
virtual void update( const string &ns , Query query , BSONObj obj , | ||||
bool upsert = false , bool multi = false ); | ||||
virtual bool isFailed() const = 0; | ||||
virtual void killCursor( long long cursorID ) = 0; | ||||
virtual bool callRead( Message& toSend , Message& response ) = 0; | ||||
// virtual bool callWrite( Message& toSend , Message& response ) = | ||||
0; // TODO: add this if needed | ||||
virtual ConnectionString::ConnectionType type() const = 0; | ||||
virtual double getSoTimeout() const = 0; | ||||
}; // DBClientBase | ||||
class DBClientReplicaSet; | ||||
class ConnectException : public UserException { | ||||
public: | ||||
ConnectException(string msg) : UserException(9000,msg) { } | ||||
}; | ||||
/** | ||||
A basic connection to the database. | ||||
This is the main entry point for talking to a simple Mongo setup | ||||
*/ | ||||
class DBClientConnection : public DBClientBase { | ||||
public: | ||||
/** | ||||
@param _autoReconnect if true, automatically reconnect on a conn | ||||
ection failure | ||||
@param cp used by DBClientReplicaSet. You do not need to specif | ||||
y this parameter | ||||
@param timeout tcp timeout in seconds - this is for read/write, | ||||
not connect. | ||||
Connect timeout is fixed, but short, at 5 seconds. | ||||
*/ | ||||
DBClientConnection(bool _autoReconnect=false, DBClientReplicaSet* c | ||||
p=0, double so_timeout=0) : | ||||
clientSet(cp), _failed(false), autoReconnect(_autoReconnect), l | ||||
astReconnectTry(0), _so_timeout(so_timeout) { | ||||
_numConnections++; | ||||
} | ||||
virtual ~DBClientConnection() { | ||||
_numConnections--; | ||||
} | ||||
/** Connect to a Mongo database server. | ||||
If autoReconnect is true, you can try to use the DBClientConnect | ||||
ion even when | ||||
false was returned -- it will try to connect again. | ||||
@param serverHostname host to connect to. can include port numb | ||||
er ( 127.0.0.1 , 127.0.0.1:5555 ) | ||||
If you use IPv6 you must add a port number | ||||
( ::1:27017 ) | ||||
@param errmsg any relevant error message will appended to the st | ||||
ring | ||||
@deprecated please use HostAndPort | ||||
@return false if fails to connect. | ||||
*/ | ||||
virtual bool connect(const char * hostname, string& errmsg) { | ||||
// TODO: remove this method | ||||
HostAndPort t( hostname ); | ||||
return connect( t , errmsg ); | ||||
} | ||||
/** Connect to a Mongo database server. | ||||
If autoReconnect is true, you can try to use the DBClientConnect | ||||
ion even when | ||||
false was returned -- it will try to connect again. | ||||
@param server server to connect to. | ||||
@param errmsg any relevant error message will appended to the st | ||||
ring | ||||
@return false if fails to connect. | ||||
*/ | ||||
virtual bool connect(const HostAndPort& server, string& errmsg); | ||||
/** Connect to a Mongo database server. Exception throwing version | ||||
. | ||||
Throws a UserException if cannot connect. | ||||
If autoReconnect is true, you can try to use the DBClientConnect | ||||
ion even when | ||||
false was returned -- it will try to connect again. | ||||
@param serverHostname host to connect to. can include port numb | ||||
er ( 127.0.0.1 , 127.0.0.1:5555 ) | ||||
*/ | ||||
void connect(const string& serverHostname) { | ||||
string errmsg; | ||||
if( !connect(HostAndPort(serverHostname), errmsg) ) | ||||
throw ConnectException(string("can't connect ") + errmsg); | ||||
} | ||||
virtual bool auth(const string &dbname, const string &username, con | ||||
st string &pwd, string& errmsg, bool digestPassword = true, Auth::Level* le | ||||
vel=NULL); | ||||
virtual auto_ptr<DBClientCursor> query(const string &ns, Query quer | ||||
y=Query(), int nToReturn = 0, int nToSkip = 0, | ||||
const BSONObj *fieldsToRetur | ||||
n = 0, int queryOptions = 0 , int batchSize = 0 ) { | ||||
checkConnection(); | ||||
return DBClientBase::query( ns, query, nToReturn, nToSkip, fiel | ||||
dsToReturn, queryOptions , batchSize ); | ||||
} | ||||
/** Uses QueryOption_Exhaust | ||||
Exhaust mode sends back all data queries as fast as possible, w | ||||
ith no back-and-for for OP_GETMORE. If you are certain | ||||
you will exhaust the query, it could be useful. | ||||
Use DBClientCursorBatchIterator version if you want to do items | ||||
in large blocks, perhaps to avoid granular locking and such. | ||||
*/ | ||||
unsigned long long query( boost::function<void(const BSONObj&)> f, | ||||
const string& ns, Query query, const BSONObj *fieldsToReturn = 0, int query | ||||
Options = 0); | ||||
unsigned long long query( boost::function<void(DBClientCursorBatchI | ||||
terator&)> f, const string& ns, Query query, const BSONObj *fieldsToReturn | ||||
= 0, int queryOptions = 0); | ||||
virtual bool runCommand(const string &dbname, const BSONObj& cmd, B | ||||
SONObj &info, int options=0); | ||||
/** | ||||
@return true if this connection is currently in a failed state. | ||||
When autoreconnect is on, | ||||
a connection will transition back to an ok state after r | ||||
econnecting. | ||||
*/ | ||||
bool isFailed() const { return _failed; } | ||||
MessagingPort& port() { assert(p); return *p; } | ||||
string toStringLong() const { | ||||
stringstream ss; | ||||
ss << _serverString; | ||||
if ( _failed ) ss << " failed"; | ||||
return ss.str(); | ||||
} | ||||
/** Returns the address of the server */ | ||||
string toString() { return _serverString; } | ||||
string getServerAddress() const { return _serverString; } | ||||
virtual void killCursor( long long cursorID ); | ||||
virtual bool callRead( Message& toSend , Message& response ) { retu | ||||
rn call( toSend , response ); } | ||||
virtual void say( Message &toSend, bool isRetry = false , string * | ||||
actualServer = 0 ); | ||||
virtual bool recv( Message& m ); | ||||
virtual void checkResponse( const char *data, int nReturned, bool* | ||||
retry = NULL, string* host = NULL ); | ||||
virtual bool call( Message &toSend, Message &response, bool assertO | ||||
k = true , string * actualServer = 0 ); | ||||
virtual ConnectionString::ConnectionType type() const { return Conn | ||||
ectionString::MASTER; } | ||||
void setSoTimeout(double to) { _so_timeout = to; } | ||||
double getSoTimeout() const { return _so_timeout; } | ||||
virtual bool lazySupported() const { return true; } | ||||
static int getNumConnections() { | ||||
return _numConnections; | ||||
} | ||||
static void setLazyKillCursor( bool lazy ) { _lazyKillCursor = lazy | ||||
; } | ||||
static bool getLazyKillCursor() { return _lazyKillCursor; } | ||||
protected: | ||||
friend class SyncClusterConnection; | ||||
virtual void sayPiggyBack( Message &toSend ); | ||||
DBClientReplicaSet *clientSet; | ||||
boost::scoped_ptr<MessagingPort> p; | ||||
boost::scoped_ptr<SockAddr> server; | ||||
bool _failed; | ||||
const bool autoReconnect; | ||||
time_t lastReconnectTry; | ||||
HostAndPort _server; // remember for reconnects | ||||
string _serverString; | ||||
void _checkConnection(); | ||||
// throws SocketException if in failed state and not reconnecting o | ||||
r if waiting to reconnect | ||||
void checkConnection() { if( _failed ) _checkConnection(); } | ||||
map< string, pair<string,string> > authCache; | ||||
double _so_timeout; | ||||
bool _connect( string& errmsg ); | ||||
static AtomicUInt _numConnections; | ||||
static bool _lazyKillCursor; // lazy means we piggy back kill curso | ||||
rs on next op | ||||
#ifdef MONGO_SSL | ||||
static SSLManager* sslManager(); | ||||
static SSLManager* _sslManager; | ||||
#endif | #endif | |||
}; | ||||
/** pings server to check if it's up | ||||
*/ | ||||
bool serverAlive( const string &uri ); | ||||
DBClientBase * createDirectClient(); | ||||
BSONElement getErrField( const BSONObj& result ); | #include "mongo/client/redef_macros.h" | |||
bool hasErrField( const BSONObj& result ); | ||||
inline std::ostream& operator<<( std::ostream &s, const Query &q ) { | #include "pch.h" | |||
return s << q.toString(); | ||||
} | ||||
} // namespace mongo | #include "mongo/client/connpool.h" | |||
#include "mongo/client/dbclient_rs.h" | ||||
#include "mongo/client/dbclientcursor.h" | ||||
#include "mongo/client/dbclientinterface.h" | ||||
#include "mongo/client/gridfs.h" | ||||
#include "mongo/client/model.h" | ||||
#include "mongo/client/syncclusterconnection.h" | ||||
#include "dbclientcursor.h" | #include "mongo/client/undef_macros.h" | |||
#include "dbclient_rs.h" | ||||
#include "undef_macros.h" | ||||
End of changes. 7 change blocks. | ||||
1211 lines changed or deleted | 14 lines changed or added | |||
dbclient_rs.h | dbclient_rs.h | |||
---|---|---|---|---|
skipping to change at line 20 | skipping to change at line 20 | |||
* | * | |||
* Unless required by applicable law or agreed to in writing, software | * Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | * distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. | |||
* See the License for the specific language governing permissions and | * See the License for the specific language governing permissions and | |||
* limitations under the License. | * limitations under the License. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../pch.h" | #include "pch.h" | |||
#include "dbclient.h" | ||||
#include <boost/function.hpp> | ||||
#include <boost/shared_ptr.hpp> | ||||
#include <set> | ||||
#include <utility> | ||||
#include "mongo/client/dbclientinterface.h" | ||||
#include "mongo/util/net/hostandport.h" | ||||
namespace mongo { | namespace mongo { | |||
class ReplicaSetMonitor; | class ReplicaSetMonitor; | |||
typedef shared_ptr<ReplicaSetMonitor> ReplicaSetMonitorPtr; | typedef shared_ptr<ReplicaSetMonitor> ReplicaSetMonitorPtr; | |||
typedef pair<set<string>,set<int> > NodeDiff; | typedef pair<set<string>,set<int> > NodeDiff; | |||
/** | /** | |||
* manages state about a replica set for client | * manages state about a replica set for client | |||
* keeps tabs on whose master and what slaves are up | * keeps tabs on whose master and what slaves are up | |||
skipping to change at line 58 | skipping to change at line 65 | |||
*/ | */ | |||
static ReplicaSetMonitorPtr get( const string& name ); | static ReplicaSetMonitorPtr get( const string& name ); | |||
/** | /** | |||
* checks all sets for current master and new secondaries | * checks all sets for current master and new secondaries | |||
* usually only called from a BackgroundJob | * usually only called from a BackgroundJob | |||
*/ | */ | |||
static void checkAll( bool checkAllSecondaries ); | static void checkAll( bool checkAllSecondaries ); | |||
/** | /** | |||
* this is called whenever the config of any repclia set changes | * deletes the ReplicaSetMonitor for the given set name. | |||
*/ | ||||
static void remove( const string& name ); | ||||
/** | ||||
* this is called whenever the config of any replica set changes | ||||
* currently only 1 globally | * currently only 1 globally | |||
* asserts if one already exists | * asserts if one already exists | |||
* ownership passes to ReplicaSetMonitor and the hook will actually never be deleted | * ownership passes to ReplicaSetMonitor and the hook will actually never be deleted | |||
*/ | */ | |||
static void setConfigChangeHook( ConfigChangeHook hook ); | static void setConfigChangeHook( ConfigChangeHook hook ); | |||
~ReplicaSetMonitor(); | ~ReplicaSetMonitor(); | |||
/** @return HostAndPort or throws an exception */ | /** @return HostAndPort or throws an exception */ | |||
HostAndPort getMaster(); | HostAndPort getMaster(); | |||
skipping to change at line 108 | skipping to change at line 120 | |||
private: | private: | |||
/** | /** | |||
* This populates a list of hosts from the list of seeds (discardin g the | * This populates a list of hosts from the list of seeds (discardin g the | |||
* seed list). | * seed list). | |||
* @param name set name | * @param name set name | |||
* @param servers seeds | * @param servers seeds | |||
*/ | */ | |||
ReplicaSetMonitor( const string& name , const vector<HostAndPort>& servers ); | ReplicaSetMonitor( const string& name , const vector<HostAndPort>& servers ); | |||
/** | ||||
* Checks all connections from the host list and sets the current | ||||
* master. | ||||
* | ||||
* @param checkAllSecondaries if set to false, stop immediately whe | ||||
n | ||||
* the master is found or when _master is not -1. | ||||
*/ | ||||
void _check( bool checkAllSecondaries ); | void _check( bool checkAllSecondaries ); | |||
/** | /** | |||
* Use replSetGetStatus command to make sure hosts in host list are up | * Use replSetGetStatus command to make sure hosts in host list are up | |||
* and readable. Sets Node::ok appropriately. | * and readable. Sets Node::ok appropriately. | |||
*/ | */ | |||
void _checkStatus(DBClientConnection *conn); | void _checkStatus( const string& hostAddr ); | |||
/** | /** | |||
* Add array of hosts to host list. Doesn't do anything if hosts ar e | * Add array of hosts to host list. Doesn't do anything if hosts ar e | |||
* already in host list. | * already in host list. | |||
* @param hostList the list of hosts to add | * @param hostList the list of hosts to add | |||
* @param changed if new hosts were added | * @param changed if new hosts were added | |||
*/ | */ | |||
void _checkHosts(const BSONObj& hostList, bool& changed); | void _checkHosts(const BSONObj& hostList, bool& changed); | |||
/** | /** | |||
* Updates host list. | * Updates host list. | |||
* @param c the connection to check | * Invariant: if nodesOffset is >= 0, _nodes[nodesOffset].conn shou | |||
ld be | ||||
* equal to conn. | ||||
* | ||||
* @param conn the connection to check | ||||
* @param maybePrimary OUT | * @param maybePrimary OUT | |||
* @param verbose | * @param verbose | |||
* @param nodesOffset - offset into _nodes array, -1 for not in it | * @param nodesOffset - offset into _nodes array, -1 for not in it | |||
* @return if the connection is good | * | |||
* @return true if the connection is good or false if invariant | ||||
* is broken | ||||
*/ | */ | |||
bool _checkConnection( DBClientConnection * c , string& maybePrimar | bool _checkConnection( DBClientConnection* conn, string& maybePrima | |||
y , bool verbose , int nodesOffset ); | ry, | |||
bool verbose, int nodesOffset ); | ||||
string _getServerAddress_inlock() const; | string _getServerAddress_inlock() const; | |||
NodeDiff _getHostDiff_inlock( const BSONObj& hostList ); | NodeDiff _getHostDiff_inlock( const BSONObj& hostList ); | |||
bool _shouldChangeHosts( const BSONObj& hostList, bool inlock ); | bool _shouldChangeHosts( const BSONObj& hostList, bool inlock ); | |||
/** | ||||
* @return the index to _nodes corresponding to the server address. | ||||
*/ | ||||
int _find( const string& server ) const ; | int _find( const string& server ) const ; | |||
int _find_inlock( const string& server ) const ; | int _find_inlock( const string& server ) const ; | |||
int _find( const HostAndPort& server ) const ; | ||||
mutable mongo::mutex _lock; // protects _nodes | /** | |||
* Checks whether the given connection matches the connection store | ||||
d in _nodes. | ||||
* Mainly used for sanity checking to confirm that nodeOffset still | ||||
* refers to the right connection after releasing and reacquiring | ||||
* a mutex. | ||||
*/ | ||||
bool _checkConnMatch_inlock( DBClientConnection* conn, size_t nodeO | ||||
ffset ) const; | ||||
// protects _nodes and indices pointing to it (_master & _nextSlave | ||||
) | ||||
mutable mongo::mutex _lock; | ||||
/** | ||||
* "Synchronizes" the _checkConnection method. Should ideally be on | ||||
e mutex per | ||||
* connection object being used. The purpose of this lock is to mak | ||||
e sure that | ||||
* the reply from the connection the lock holder got is the actual | ||||
response | ||||
* to what it sent. | ||||
* | ||||
* Deadlock WARNING: never acquire this while holding _lock | ||||
*/ | ||||
mutable mongo::mutex _checkConnectionLock; | mutable mongo::mutex _checkConnectionLock; | |||
string _name; | string _name; | |||
// note these get copied around in the nodes vector so be sure to m aintain copyable semantics here | // note these get copied around in the nodes vector so be sure to m aintain copyable semantics here | |||
struct Node { | struct Node { | |||
Node( const HostAndPort& a , DBClientConnection* c ) | Node( const HostAndPort& a , DBClientConnection* c ) | |||
: addr( a ) , conn(c) , ok(true) , | : addr( a ) , conn(c) , ok(true) , | |||
ismaster(false), secondary( false ) , hidden( false ) , p ingTimeMillis(0) { | ismaster(false), secondary( false ) , hidden( false ) , p ingTimeMillis(0) { | |||
ok = conn.get() == NULL; | ok = conn.get() == NULL; | |||
skipping to change at line 214 | skipping to change at line 259 | |||
/** Use this class to connect to a replica set of servers. The class w ill manage | /** Use this class to connect to a replica set of servers. The class w ill manage | |||
checking for which server in a replica set is master, and do failove r automatically. | checking for which server in a replica set is master, and do failove r automatically. | |||
This can also be used to connect to replica pairs since pairs are a subset of sets | This can also be used to connect to replica pairs since pairs are a subset of sets | |||
On a failover situation, expect at least one operation to return an error (throw | On a failover situation, expect at least one operation to return an error (throw | |||
an exception) before the failover is complete. Operations are not r etried. | an exception) before the failover is complete. Operations are not r etried. | |||
*/ | */ | |||
class DBClientReplicaSet : public DBClientBase { | class DBClientReplicaSet : public DBClientBase { | |||
public: | public: | |||
using DBClientBase::query; | ||||
/** Call connect() after constructing. autoReconnect is always on f or DBClientReplicaSet connections. */ | /** Call connect() after constructing. autoReconnect is always on f or DBClientReplicaSet connections. */ | |||
DBClientReplicaSet( const string& name , const vector<HostAndPort>& servers, double so_timeout=0 ); | DBClientReplicaSet( const string& name , const vector<HostAndPort>& servers, double so_timeout=0 ); | |||
virtual ~DBClientReplicaSet(); | virtual ~DBClientReplicaSet(); | |||
/** Returns false if nomember of the set were reachable, or neither is | /** Returns false if nomember of the set were reachable, or neither is | |||
* master, although, | * master, although, | |||
* when false returned, you can still try to use this connection ob ject, it will | * when false returned, you can still try to use this connection ob ject, it will | |||
* try reconnects. | * try reconnects. | |||
*/ | */ | |||
bool connect(); | bool connect(); | |||
End of changes. 12 change blocks. | ||||
11 lines changed or deleted | 65 lines changed or added | |||
dbclientcursor.h | dbclientcursor.h | |||
---|---|---|---|---|
skipping to change at line 20 | skipping to change at line 20 | |||
* | * | |||
* Unless required by applicable law or agreed to in writing, software | * Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | * distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. | |||
* See the License for the specific language governing permissions and | * See the License for the specific language governing permissions and | |||
* limitations under the License. | * limitations under the License. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../pch.h" | #include "pch.h" | |||
#include "../util/net/message.h" | ||||
#include "../db/jsobj.h" | ||||
#include "../db/json.h" | ||||
#include <stack> | #include <stack> | |||
#include "mongo/client/dbclientinterface.h" | ||||
#include "mongo/db/jsobj.h" | ||||
#include "mongo/db/json.h" | ||||
#include "mongo/util/net/message.h" | ||||
namespace mongo { | namespace mongo { | |||
class AScopedConnection; | class AScopedConnection; | |||
/** for mock purposes only -- do not create variants of DBClientCursor, nor hang code here | /** for mock purposes only -- do not create variants of DBClientCursor, nor hang code here | |||
@see DBClientMockCursor | @see DBClientMockCursor | |||
*/ | */ | |||
class DBClientCursorInterface : boost::noncopyable { | class DBClientCursorInterface : boost::noncopyable { | |||
public: | public: | |||
virtual ~DBClientCursorInterface() {} | virtual ~DBClientCursorInterface() {} | |||
skipping to change at line 248 | skipping to change at line 251 | |||
++_n; | ++_n; | |||
return _c.nextSafe(); | return _c.nextSafe(); | |||
} | } | |||
int n() const { return _n; } | int n() const { return _n; } | |||
private: | private: | |||
DBClientCursor &_c; | DBClientCursor &_c; | |||
int _n; | int _n; | |||
}; | }; | |||
} // namespace mongo | } // namespace mongo | |||
#include "undef_macros.h" | ||||
End of changes. 3 change blocks. | ||||
4 lines changed or deleted | 7 lines changed or added | |||
dbhelpers.h | dbhelpers.h | |||
---|---|---|---|---|
skipping to change at line 97 | skipping to change at line 97 | |||
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); | |||
static bool getFirst(const char *ns, BSONObj& result) { return getS ingleton(ns, result); } | static bool getFirst(const char *ns, BSONObj& result) { return getS ingleton(ns, result); } | |||
static bool getLast(const char *ns, BSONObj& result); // get last o bject int he collection; e.g. {$natural : -1} | static bool getLast(const char *ns, BSONObj& result); // get last o bject int he collection; e.g. {$natural : -1} | |||
/** | /** | |||
* you have to lock | * you have to lock | |||
* you do not have to have Context set | * you do not have to have Context set | |||
* o has to have an _id field or will assert | * o has to have an _id field or will assert | |||
*/ | */ | |||
static void upsert( const string& ns , const BSONObj& o ); | static void upsert( const string& ns , const BSONObj& o, bool fromM igrate = false ); | |||
/** You do not need to set the database before calling. | /** You do not need to set the database before calling. | |||
@return true if collection is empty. | @return true if collection is empty. | |||
*/ | */ | |||
static bool isEmpty(const char *ns, bool doAuth=true); | static bool isEmpty(const char *ns, bool doAuth=true); | |||
// TODO: this should be somewhere else probably | // TODO: this should be somewhere else probably | |||
static BSONObj toKeyFormat( const BSONObj& o , BSONObj& key ); | static BSONObj toKeyFormat( const BSONObj& o , BSONObj& key ); | |||
class RemoveCallback { | class RemoveCallback { | |||
public: | public: | |||
virtual ~RemoveCallback() {} | virtual ~RemoveCallback() {} | |||
virtual void goingToDelete( const BSONObj& o ) = 0; | virtual void goingToDelete( const BSONObj& o ) = 0; | |||
}; | }; | |||
/* removeRange: operation is oplog'd */ | ||||
static long long removeRange( const string& ns , const BSONObj& min | ||||
, const BSONObj& max , bool yield = false , bool maxInclusive = false , Re | ||||
moveCallback * callback = 0 ); | ||||
/* Remove all objects from a collection. | /** | |||
You do not need to set the database before calling. | * Remove all documents in the range. | |||
*/ | * Does oplog the individual document deletions. | |||
static void emptyCollection(const char *ns); | */ | |||
static long long removeRange( const string& ns , | ||||
}; | const BSONObj& min , | |||
const BSONObj& max , | ||||
bool yield = false , | ||||
bool maxInclusive = false , | ||||
RemoveCallback * callback = 0, | ||||
bool fromMigrate = false ); | ||||
class Database; | /** | |||
* Remove all documents from a collection. | ||||
* You do not need to set the database before calling. | ||||
* Does not oplog the operation. | ||||
*/ | ||||
static void emptyCollection(const char *ns); | ||||
// manage a set using collection backed storage | ||||
class DbSet { | ||||
public: | ||||
DbSet( const string &name = "", const BSONObj &key = BSONObj() ) : | ||||
name_( name ), | ||||
key_( key.getOwned() ) { | ||||
} | ||||
~DbSet(); | ||||
void reset( const string &name = "", const BSONObj &key = BSONObj() | ||||
); | ||||
bool get( const BSONObj &obj ) const; | ||||
void set( const BSONObj &obj, bool val ); | ||||
private: | ||||
string name_; | ||||
BSONObj key_; | ||||
}; | }; | |||
/** | /** | |||
* user for saving deleted bson objects to a flat file | * user for saving deleted bson objects to a flat file | |||
*/ | */ | |||
class RemoveSaver : public Helpers::RemoveCallback , boost::noncopyable { | class RemoveSaver : public Helpers::RemoveCallback , boost::noncopyable { | |||
public: | public: | |||
RemoveSaver( const string& type , const string& ns , const string& why); | RemoveSaver( const string& type , const string& ns , const string& why); | |||
~RemoveSaver(); | ~RemoveSaver(); | |||
End of changes. 5 change blocks. | ||||
27 lines changed or deleted | 18 lines changed or added | |||
dbmessage.h | dbmessage.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 "diskloc.h" | ||||
#include "jsobj.h" | #include "jsobj.h" | |||
#include "namespace-inl.h" | #include "namespace-inl.h" | |||
#include "../util/net/message.h" | #include "../util/net/message.h" | |||
#include "../client/constants.h" | #include "../client/constants.h" | |||
#include "instance.h" | #include "instance.h" | |||
namespace mongo { | namespace mongo { | |||
/* db response format | /* db response format | |||
skipping to change at line 220 | skipping to change at line 219 | |||
return js; | return js; | |||
} | } | |||
const Message& msg() const { return m; } | const Message& msg() const { return m; } | |||
void markSet() { | void markSet() { | |||
mark = nextjsobj; | mark = nextjsobj; | |||
} | } | |||
void markReset() { | void markReset() { | |||
assert( mark ); | verify( mark ); | |||
nextjsobj = mark; | nextjsobj = mark; | |||
} | } | |||
private: | private: | |||
const Message& m; | const Message& m; | |||
int* reserved; | int* reserved; | |||
const char *data; | const char *data; | |||
const char *nextjsobj; | const char *nextjsobj; | |||
const char *theEnd; | const char *theEnd; | |||
End of changes. 2 change blocks. | ||||
2 lines changed or deleted | 1 lines changed or added | |||
dbtests.h | dbtests.h | |||
---|---|---|---|---|
skipping to change at line 20 | skipping to change at line 20 | |||
* | * | |||
* This program is distributed in the hope that it will be useful, | * This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Affero General Public License for more details. | * GNU Affero General Public License for more details. | |||
* | * | |||
* You should have received a copy of the GNU Affero General Public Licen se | * You should have received a copy of the GNU Affero General Public Licen se | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | */ | |||
#include "framework.h" | #pragma once | |||
#include "mongo/db/instance.h" | ||||
#include "mongo/unittest/unittest.h" | ||||
using namespace mongo; | using namespace mongo; | |||
using namespace mongo::regression; | using namespace mongo::unittest; | |||
using boost::shared_ptr; | using boost::shared_ptr; | |||
End of changes. 2 change blocks. | ||||
2 lines changed or deleted | 5 lines changed or added | |||
debug_util.h | debug_util.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 | |||
#ifndef _WIN32 | ||||
#include <signal.h> | ||||
#endif | ||||
namespace mongo { | namespace mongo { | |||
// for debugging | // for debugging | |||
typedef struct _Ints { | typedef struct _Ints { | |||
int i[100]; | int i[100]; | |||
} *Ints; | } *Ints; | |||
typedef struct _Chars { | typedef struct _Chars { | |||
char c[200]; | char c[200]; | |||
} *Chars; | } *Chars; | |||
typedef char CHARS[400]; | typedef char CHARS[400]; | |||
typedef struct _OWS { | typedef struct _OWS { | |||
int size; | int size; | |||
char type; | char type; | |||
char string[400]; | char string[400]; | |||
} *OWS; | } *OWS; | |||
#if defined(_DEBUG) | #if defined(_DEBUG) | |||
enum {DEBUG_BUILD = 1}; | enum {DEBUG_BUILD = 1}; | |||
const bool debug=true; | ||||
#else | #else | |||
enum {DEBUG_BUILD = 0}; | enum {DEBUG_BUILD = 0}; | |||
const bool debug=false; | ||||
#endif | #endif | |||
#define MONGO_DEV if( DEBUG_BUILD ) | #define MONGO_DEV if( DEBUG_BUILD ) | |||
#define DEV MONGO_DEV | #define DEV MONGO_DEV | |||
#define MONGO_DEBUGGING if( 0 ) | #define MONGO_DEBUGGING if( 0 ) | |||
#define DEBUGGING MONGO_DEBUGGING | #define DEBUGGING MONGO_DEBUGGING | |||
// The following declare one unique counter per enclosing function. | // The following declare one unique counter per enclosing function. | |||
// NOTE The implementation double-increments on a match, but we don't reall y care. | // NOTE The implementation double-increments on a match, but we don't reall y care. | |||
skipping to change at line 77 | skipping to change at line 75 | |||
#if defined(_WIN32) | #if defined(_WIN32) | |||
inline int strcasecmp(const char* s1, const char* s2) {return _stricmp( s1, s2);} | inline int strcasecmp(const char* s1, const char* s2) {return _stricmp( s1, s2);} | |||
#endif | #endif | |||
// Sets SIGTRAP handler to launch GDB | // Sets SIGTRAP handler to launch GDB | |||
// Noop unless on *NIX and compiled with _DEBUG | // Noop unless on *NIX and compiled with _DEBUG | |||
void setupSIGTRAPforGDB(); | void setupSIGTRAPforGDB(); | |||
extern int tlogLevel; | extern int tlogLevel; | |||
void mongo_breakpoint(); | ||||
inline void breakpoint() { | inline void breakpoint() { | |||
if ( tlogLevel < 0 ) | if ( tlogLevel < 0 ) | |||
return; | return; | |||
#ifdef _WIN32 | mongo_breakpoint(); | |||
//DEV DebugBreak(); | ||||
#endif | ||||
#ifndef _WIN32 | ||||
// code to raise a breakpoint in GDB | ||||
ONCE { | ||||
//prevent SIGTRAP from crashing the program if default action i | ||||
s specified and we are not in gdb | ||||
struct sigaction current; | ||||
sigaction(SIGTRAP, NULL, ¤t); | ||||
if (current.sa_handler == SIG_DFL) { | ||||
signal(SIGTRAP, SIG_IGN); | ||||
} | ||||
} | ||||
raise(SIGTRAP); | ||||
#endif | ||||
} | ||||
// conditional breakpoint | ||||
inline void breakif(bool test) { | ||||
if (test) | ||||
breakpoint(); | ||||
} | } | |||
} // namespace mongo | } // namespace mongo | |||
End of changes. 6 change blocks. | ||||
29 lines changed or deleted | 4 lines changed or added | |||
dependency_tracker.h | dependency_tracker.h | |||
---|---|---|---|---|
skipping to change at line 26 | skipping to change at line 26 | |||
#pragma once | #pragma once | |||
#include "pch.h" | #include "pch.h" | |||
#include <boost/unordered_map.hpp> | #include <boost/unordered_map.hpp> | |||
#include "util/intrusive_counter.h" | #include "util/intrusive_counter.h" | |||
namespace mongo { | namespace mongo { | |||
class DocumentSource; | ||||
class DependencyTracker : | class DependencyTracker : | |||
public IntrusiveCounterUnsigned { | public IntrusiveCounterUnsigned { | |||
public: | public: | |||
void include(const string &fieldName); | void addDependency(const string &fieldPath, | |||
void exclude(const string &fieldName); | const DocumentSource *pSource); | |||
void removeDependency(const string &fieldPath); | ||||
bool isRequired(const string &fieldName) const; | bool getDependency(intrusive_ptr<const DocumentSource> *ppSource, | |||
const string &fieldPath) const; | ||||
private: | private: | |||
struct Tracker { | struct Tracker { | |||
string fieldName; | Tracker(const string &fieldPath, | |||
const DocumentSource *pSource); | ||||
string fieldPath; | ||||
intrusive_ptr<const DocumentSource> pSource; | ||||
struct Hash : | struct Hash : | |||
unary_function<string, size_t> { | unary_function<string, size_t> { | |||
size_t operator()(const string &rS) const; | size_t operator()(const string &rS) const; | |||
}; | }; | |||
}; | }; | |||
boost::unordered_map<string, Tracker, Tracker::Hash> map; | typedef boost::unordered_map<string, Tracker, Tracker::Hash> MapTyp | |||
e; | ||||
MapType map; | ||||
}; | }; | |||
} | } | |||
/* ======================= INLINED IMPLEMENTATIONS ======================== == */ | /* ======================= PRIVATE IMPLEMENTATIONS ======================== == */ | |||
namespace mongo { | namespace mongo { | |||
inline size_t DependencyTracker::Tracker::Hash::operator()( | inline size_t DependencyTracker::Tracker::Hash::operator()( | |||
const string &rS) const { | const string &rS) const { | |||
size_t seed = 0xf0afbeef; | size_t seed = 0xf0afbeef; | |||
boost::hash_combine(seed, rS); | boost::hash_combine(seed, rS); | |||
return seed; | return seed; | |||
} | } | |||
End of changes. 6 change blocks. | ||||
6 lines changed or deleted | 17 lines changed or added | |||
diskloc.h | diskloc.h | |||
---|---|---|---|---|
skipping to change at line 72 | skipping to change at line 72 | |||
return ofs < -1 || | return ofs < -1 || | |||
_a < -1 || | _a < -1 || | |||
_a > 524288; | _a > 524288; | |||
} | } | |||
bool isNull() const { return _a == -1; } | bool isNull() const { return _a == -1; } | |||
void Null() { | void Null() { | |||
_a = -1; | _a = -1; | |||
ofs = 0; /* note NullOfs is different. todo clean up. see refs to NullOfs in code - use is valid but outside DiskLoc context so confusing as-is. */ | ofs = 0; /* note NullOfs is different. todo clean up. see refs to NullOfs in code - use is valid but outside DiskLoc context so confusing as-is. */ | |||
} | } | |||
void assertOk() { assert(!isNull()); } | void assertOk() { verify(!isNull()); } | |||
void setInvalid() { | void setInvalid() { | |||
_a = -2; | _a = -2; | |||
ofs = 0; | ofs = 0; | |||
} | } | |||
bool isValid() const { return _a != -2; } | bool isValid() const { return _a != -2; } | |||
string toString() const { | string toString() const { | |||
if ( isNull() ) | if ( isNull() ) | |||
return "null"; | return "null"; | |||
stringstream ss; | stringstream ss; | |||
skipping to change at line 99 | skipping to change at line 99 | |||
int a() const { return _a; } | int a() const { return _a; } | |||
int& GETOFS() { return ofs; } | int& GETOFS() { return ofs; } | |||
int getOfs() const { return ofs; } | int getOfs() const { return ofs; } | |||
void set(int a, int b) { | void set(int a, int b) { | |||
_a=a; | _a=a; | |||
ofs=b; | ofs=b; | |||
} | } | |||
void inc(int amt) { | void inc(int amt) { | |||
assert( !isNull() ); | verify( !isNull() ); | |||
ofs += amt; | ofs += amt; | |||
} | } | |||
bool sameFile(DiskLoc b) { | bool sameFile(DiskLoc b) { | |||
return _a== b._a; | return _a== b._a; | |||
} | } | |||
bool operator==(const DiskLoc& b) const { | bool operator==(const DiskLoc& b) const { | |||
return _a==b._a&& ofs == b.ofs; | return _a==b._a&& ofs == b.ofs; | |||
} | } | |||
bool operator!=(const DiskLoc& b) const { | bool operator!=(const DiskLoc& b) const { | |||
return !(*this==b); | return !(*this==b); | |||
} | } | |||
const DiskLoc& operator=(const DiskLoc& b) { | const DiskLoc& operator=(const DiskLoc& b) { | |||
_a=b._a; | _a=b._a; | |||
ofs = b.ofs; | ofs = b.ofs; | |||
//assert(ofs!=0); | //verify(ofs!=0); | |||
return *this; | return *this; | |||
} | } | |||
int compare(const DiskLoc& b) const { | int compare(const DiskLoc& b) const { | |||
int x = _a - b._a; | int x = _a - b._a; | |||
if ( x ) | if ( x ) | |||
return x; | return x; | |||
return ofs - b.ofs; | return ofs - b.ofs; | |||
} | } | |||
bool operator<(const DiskLoc& b) const { | bool operator<(const DiskLoc& b) const { | |||
return compare(b) < 0; | return compare(b) < 0; | |||
End of changes. 3 change blocks. | ||||
3 lines changed or deleted | 3 lines changed or added | |||
distlock.h | distlock.h | |||
---|---|---|---|---|
skipping to change at line 21 | skipping to change at line 21 | |||
* Unless required by applicable law or agreed to in writing, software | * Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | * distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. | |||
* See the License for the specific language governing permissions and | * See the License for the specific language governing permissions and | |||
* limitations under the License. | * limitations under the License. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../pch.h" | #include "../pch.h" | |||
#include "dbclient.h" | ||||
#include "connpool.h" | #include "connpool.h" | |||
#include "redef_macros.h" | ||||
#include "syncclusterconnection.h" | #include "syncclusterconnection.h" | |||
#define LOCK_TIMEOUT (15 * 60 * 1000) | #define LOCK_TIMEOUT (15 * 60 * 1000) | |||
#define LOCK_SKEW_FACTOR (30) | #define LOCK_SKEW_FACTOR (30) | |||
#define LOCK_PING (LOCK_TIMEOUT / LOCK_SKEW_FACTOR) | #define LOCK_PING (LOCK_TIMEOUT / LOCK_SKEW_FACTOR) | |||
#define MAX_LOCK_NET_SKEW (LOCK_TIMEOUT / LOCK_SKEW_FACTOR) | #define MAX_LOCK_NET_SKEW (LOCK_TIMEOUT / LOCK_SKEW_FACTOR) | |||
#define MAX_LOCK_CLOCK_SKEW (LOCK_TIMEOUT / LOCK_SKEW_FACTOR) | #define MAX_LOCK_CLOCK_SKEW (LOCK_TIMEOUT / LOCK_SKEW_FACTOR) | |||
#define NUM_LOCK_SKEW_CHECKS (3) | #define NUM_LOCK_SKEW_CHECKS (3) | |||
// The maximum clock skew we need to handle between config servers is | // The maximum clock skew we need to handle between config servers is | |||
skipping to change at line 230 | skipping to change at line 228 | |||
return *this; | return *this; | |||
} | } | |||
dist_lock_try( DistributedLock * lock , string why ) | dist_lock_try( DistributedLock * lock , string why ) | |||
: _lock(lock), _why(why) { | : _lock(lock), _why(why) { | |||
_got = _lock->lock_try( why , false , &_other ); | _got = _lock->lock_try( why , false , &_other ); | |||
} | } | |||
~dist_lock_try() { | ~dist_lock_try() { | |||
if ( _got ) { | if ( _got ) { | |||
assert( ! _other.isEmpty() ); | verify( ! _other.isEmpty() ); | |||
_lock->unlock( &_other ); | _lock->unlock( &_other ); | |||
} | } | |||
} | } | |||
bool reestablish(){ | bool reestablish(){ | |||
return retry(); | return retry(); | |||
} | } | |||
bool retry() { | bool retry() { | |||
assert( _lock ); | verify( _lock ); | |||
assert( _got ); | verify( _got ); | |||
assert( ! _other.isEmpty() ); | verify( ! _other.isEmpty() ); | |||
return _got = _lock->lock_try( _why , true, &_other ); | return _got = _lock->lock_try( _why , true, &_other ); | |||
} | } | |||
bool got() const { return _got; } | bool got() const { return _got; } | |||
BSONObj other() const { return _other; } | BSONObj other() const { return _other; } | |||
private: | private: | |||
DistributedLock * _lock; | DistributedLock * _lock; | |||
bool _got; | bool _got; | |||
End of changes. 4 change blocks. | ||||
6 lines changed or deleted | 4 lines changed or added | |||
document.h | document.h | |||
---|---|---|---|---|
skipping to change at line 25 | skipping to change at line 25 | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "pch.h" | #include "pch.h" | |||
#include "util/intrusive_counter.h" | #include "util/intrusive_counter.h" | |||
namespace mongo { | namespace mongo { | |||
class BSONObj; | class BSONObj; | |||
class DependencyTracker; | ||||
class FieldIterator; | class FieldIterator; | |||
class Value; | class Value; | |||
class Document : | class Document : | |||
public IntrusiveCounterUnsigned { | public IntrusiveCounterUnsigned { | |||
public: | public: | |||
~Document(); | ~Document(); | |||
/* | /* | |||
Create a new Document from the given BSONObj. | Create a new Document from the given BSONObj. | |||
Document field values may be pointed to in the BSONObj, so it | Document field values may be pointed to in the BSONObj, so it | |||
must live at least as long as the resulting Document. | must live at least as long as the resulting Document. | |||
LATER - use an abstract class for the dependencies; something lik | ||||
e | ||||
a "lookup(const string &fieldName)" so there can be other | ||||
implementations. | ||||
@returns shared pointer to the newly created Document | @returns shared pointer to the newly created Document | |||
*/ | */ | |||
static intrusive_ptr<Document> createFromBsonObj(BSONObj *pBsonObj) | static intrusive_ptr<Document> createFromBsonObj( | |||
; | BSONObj *pBsonObj, const DependencyTracker *pDependencies = NUL | |||
L); | ||||
/* | /* | |||
Create a new empty Document. | Create a new empty Document. | |||
@param sizeHint a hint at what the number of fields will be; if | @param sizeHint a hint at what the number of fields will be; if | |||
known, this can be used to increase memory allocation efficienc y | known, this can be used to increase memory allocation efficienc y | |||
@returns shared pointer to the newly created Document | @returns shared pointer to the newly created Document | |||
*/ | */ | |||
static intrusive_ptr<Document> create(size_t sizeHint = 0); | static intrusive_ptr<Document> create(size_t sizeHint = 0); | |||
skipping to change at line 187 | skipping to change at line 193 | |||
boost classes such as unordered_map<>. | boost classes such as unordered_map<>. | |||
@param seed value to augment with this' hash | @param seed value to augment with this' hash | |||
*/ | */ | |||
void hash_combine(size_t &seed) const; | void hash_combine(size_t &seed) const; | |||
private: | private: | |||
friend class FieldIterator; | friend class FieldIterator; | |||
Document(size_t sizeHint); | Document(size_t sizeHint); | |||
Document(BSONObj *pBsonObj); | Document(BSONObj *pBsonObj, const DependencyTracker *pDependencies) ; | |||
/* these two vectors parallel each other */ | /* these two vectors parallel each other */ | |||
vector<string> vFieldName; | vector<string> vFieldName; | |||
vector<intrusive_ptr<const Value> > vpValue; | vector<intrusive_ptr<const Value> > vpValue; | |||
}; | }; | |||
class FieldIterator : | class FieldIterator : | |||
boost::noncopyable { | boost::noncopyable { | |||
public: | public: | |||
/* | /* | |||
skipping to change at line 240 | skipping to change at line 246 | |||
/* ======================= INLINED IMPLEMENTATIONS ======================== == */ | /* ======================= INLINED IMPLEMENTATIONS ======================== == */ | |||
namespace mongo { | namespace mongo { | |||
inline size_t Document::getFieldCount() const { | inline size_t Document::getFieldCount() const { | |||
return vFieldName.size(); | return vFieldName.size(); | |||
} | } | |||
inline Document::FieldPair Document::getField(size_t index) const { | inline Document::FieldPair Document::getField(size_t index) const { | |||
assert( index < vFieldName.size() ); | verify( index < vFieldName.size() ); | |||
return FieldPair(vFieldName[index], vpValue[index]); | return FieldPair(vFieldName[index], vpValue[index]); | |||
} | } | |||
} | } | |||
End of changes. 5 change blocks. | ||||
4 lines changed or deleted | 11 lines changed or added | |||
document_source.h | document_source.h | |||
---|---|---|---|---|
skipping to change at line 24 | skipping to change at line 24 | |||
* 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 <boost/unordered_map.hpp> | #include <boost/unordered_map.hpp> | |||
#include "util/intrusive_counter.h" | #include "util/intrusive_counter.h" | |||
#include "client/parallel.h" | #include "client/parallel.h" | |||
#include "db/clientcursor.h" | ||||
#include "db/jsobj.h" | #include "db/jsobj.h" | |||
#include "db/pipeline/dependency_tracker.h" | ||||
#include "db/pipeline/document.h" | #include "db/pipeline/document.h" | |||
#include "db/pipeline/expression.h" | #include "db/pipeline/expression.h" | |||
#include "db/pipeline/value.h" | #include "db/pipeline/value.h" | |||
#include "util/string_writer.h" | #include "util/string_writer.h" | |||
namespace mongo { | namespace mongo { | |||
class Accumulator; | class Accumulator; | |||
class Cursor; | class Cursor; | |||
class DependencyTracker; | class DependencyTracker; | |||
class Document; | class Document; | |||
skipping to change at line 48 | skipping to change at line 50 | |||
class ExpressionObject; | class ExpressionObject; | |||
class Matcher; | class Matcher; | |||
class DocumentSource : | class DocumentSource : | |||
public IntrusiveCounterUnsigned, | public IntrusiveCounterUnsigned, | |||
public StringWriter { | public StringWriter { | |||
public: | public: | |||
virtual ~DocumentSource(); | virtual ~DocumentSource(); | |||
// virtuals from StringWriter | // virtuals from StringWriter | |||
virtual void writeString(stringstream &ss) const; | ||||
/** | /** | |||
Write out a string representation of this pipeline operator. | Set the step for a user-specified pipeline step. | |||
@param ss string stream to write the string representation to | The step is used for diagnostics. | |||
*/ | ||||
virtual void writeString(stringstream &ss) const; | @param step step number 0 to n. | |||
*/ | ||||
void setPipelineStep(int step); | ||||
/** | ||||
Get the user-specified pipeline step. | ||||
@returns the step number, or -1 if it has never been set | ||||
*/ | ||||
int getPipelineStep() const; | ||||
/** | /** | |||
Is the source at EOF? | Is the source at EOF? | |||
@returns true if the source has no more Documents to return. | @returns true if the source has no more Documents to return. | |||
*/ | */ | |||
virtual bool eof() = 0; | virtual bool eof() = 0; | |||
/** | /** | |||
Advance the state of the DocumentSource so that it will return th e | Advance the state of the DocumentSource so that it will return th e | |||
next Document. | next Document. | |||
The default implementation returns false, after checking for | ||||
interrupts. Derived classes can call the default implementation | ||||
in their own implementations in order to check for interrupts. | ||||
@returns whether there is another document to fetch, i.e., whethe r or | @returns whether there is another document to fetch, i.e., whethe r or | |||
not getCurrent() will succeed. | not getCurrent() will succeed. This default implementation alw | |||
ays | ||||
returns false. | ||||
*/ | */ | |||
virtual bool advance() = 0; | virtual bool advance(); | |||
/** | /** | |||
Advance the source, and return the next Expression. | Advance the source, and return the next Expression. | |||
@returns the current Document | @returns the current Document | |||
TODO throws an exception if there are no more expressions to retu rn. | TODO throws an exception if there are no more expressions to retu rn. | |||
*/ | */ | |||
virtual intrusive_ptr<Document> getCurrent() = 0; | virtual intrusive_ptr<Document> getCurrent() = 0; | |||
/** | /** | |||
Get the source's name. | ||||
@returns the string name of the source as a constant string; | ||||
this is static, and there's no need to worry about adopting it | ||||
*/ | ||||
virtual const char *getSourceName() const; | ||||
/** | ||||
Set the underlying source this source should use to get Documents | Set the underlying source this source should use to get Documents | |||
from. | from. | |||
It is an error to set the source more than once. This is to | It is an error to set the source more than once. This is to | |||
prevent changing sources once the original source has been starte d; | prevent changing sources once the original source has been starte d; | |||
this could break the state maintained by the DocumentSource. | this could break the state maintained by the DocumentSource. | |||
This pointer is not reference counted because that has led to | ||||
some circular references. As a result, this doesn't keep | ||||
sources alive, and is only intended to be used temporarily for | ||||
the lifetime of a Pipeline::run(). | ||||
@param pSource the underlying source to use | @param pSource the underlying source to use | |||
*/ | */ | |||
virtual void setSource(const intrusive_ptr<DocumentSource> &pSource ); | virtual void setSource(DocumentSource *pSource); | |||
/** | /** | |||
Attempt to coalesce this DocumentSource with its successor in the | Attempt to coalesce this DocumentSource with its successor in the | |||
document processing pipeline. If successful, the successor | document processing pipeline. If successful, the successor | |||
DocumentSource should be removed from the pipeline and discarded. | DocumentSource should be removed from the pipeline and discarded. | |||
If successful, this operation can be applied repeatedly, in an | If successful, this operation can be applied repeatedly, in an | |||
attempt to coalesce several sources together. | attempt to coalesce several sources together. | |||
The default implementation is to do nothing, and return false. | The default implementation is to do nothing, and return false. | |||
skipping to change at line 122 | skipping to change at line 153 | |||
This is intended for any operations that include expressions, and | This is intended for any operations that include expressions, and | |||
provides a hook for those to optimize those operations. | provides a hook for those to optimize those operations. | |||
The default implementation is to do nothing. | The default implementation is to do nothing. | |||
*/ | */ | |||
virtual void optimize(); | virtual void optimize(); | |||
/** | /** | |||
Adjust dependencies according to the needs of this source. | Adjust dependencies according to the needs of this source. | |||
$$$ | $$$ MONGO_LATER_SERVER_4644 | |||
@param pTracker the dependency tracker | @param pTracker the dependency tracker | |||
*/ | */ | |||
virtual void manageDependencies( | virtual void manageDependencies( | |||
const intrusive_ptr<DependencyTracker> &pTracker); | const intrusive_ptr<DependencyTracker> &pTracker); | |||
/** | /** | |||
Add the DocumentSource to the array builder. | Add the DocumentSource to the array builder. | |||
The default implementation calls sourceToBson() in order to | The default implementation calls sourceToBson() in order to | |||
convert the inner part of the object which will be added to the | convert the inner part of the object which will be added to the | |||
array being built here. | array being built here. | |||
@param pBuilder the array builder to add the operation to. | @param pBuilder the array builder to add the operation to. | |||
*/ | */ | |||
virtual void addToBsonArray(BSONArrayBuilder *pBuilder) const; | virtual void addToBsonArray(BSONArrayBuilder *pBuilder) const; | |||
protected: | protected: | |||
/** | /** | |||
Base constructor. | ||||
*/ | ||||
DocumentSource(const intrusive_ptr<ExpressionContext> &pExpCtx); | ||||
/** | ||||
Create an object that represents the document source. The object | Create an object that represents the document source. The object | |||
will have a single field whose name is the source's name. This | will have a single field whose name is the source's name. This | |||
will be used by the default implementation of addToBsonArray() | will be used by the default implementation of addToBsonArray() | |||
to add this object to a pipeline being represented in BSON. | to add this object to a pipeline being represented in BSON. | |||
@param pBuilder a blank object builder to write to | @param pBuilder a blank object builder to write to | |||
*/ | */ | |||
virtual void sourceToBson(BSONObjBuilder *pBuilder) const = 0; | virtual void sourceToBson(BSONObjBuilder *pBuilder) const = 0; | |||
/* | /* | |||
Most DocumentSources have an underlying source they get their dat a | Most DocumentSources have an underlying source they get their dat a | |||
from. This is a convenience for them. | from. This is a convenience for them. | |||
The default implementation of setSource() sets this; if you don't | The default implementation of setSource() sets this; if you don't | |||
need a source, override that to assert(). The default is to | need a source, override that to verify(). The default is to | |||
assert() if this has already been set. | verify() if this has already been set. | |||
*/ | */ | |||
intrusive_ptr<DocumentSource> pSource; | DocumentSource *pSource; | |||
/* | ||||
The zero-based user-specified pipeline step. Used for diagnostic | ||||
s. | ||||
Will be set to -1 for artificial pipeline steps that were not par | ||||
t | ||||
of the original user specification. | ||||
*/ | ||||
int step; | ||||
intrusive_ptr<ExpressionContext> pExpCtx; | ||||
}; | }; | |||
class DocumentSourceBsonArray : | class DocumentSourceBsonArray : | |||
public DocumentSource { | public DocumentSource { | |||
public: | public: | |||
// virtuals from DocumentSource | // virtuals from DocumentSource | |||
virtual ~DocumentSourceBsonArray(); | virtual ~DocumentSourceBsonArray(); | |||
virtual bool eof(); | virtual bool eof(); | |||
virtual bool advance(); | virtual bool advance(); | |||
virtual intrusive_ptr<Document> getCurrent(); | virtual intrusive_ptr<Document> getCurrent(); | |||
virtual void setSource(const intrusive_ptr<DocumentSource> &pSource ); | virtual void setSource(DocumentSource *pSource); | |||
/** | /** | |||
Create a document source based on a BSON array. | Create a document source based on a BSON array. | |||
This is usually put at the beginning of a chain of document sourc es | This is usually put at the beginning of a chain of document sourc es | |||
in order to fetch data from the database. | in order to fetch data from the database. | |||
CAUTION: the BSON is not read until the source is used. Any | CAUTION: the BSON is not read until the source is used. Any | |||
elements that appear after these documents must not be read until | elements that appear after these documents must not be read until | |||
this source is exhausted. | this source is exhausted. | |||
@param pBsonElement the BSON array to treat as a document source | @param pBsonElement the BSON array to treat as a document source | |||
@param pExpCtx the expression context for the pipeline | ||||
@returns the newly created document source | @returns the newly created document source | |||
*/ | */ | |||
static intrusive_ptr<DocumentSourceBsonArray> create( | static intrusive_ptr<DocumentSourceBsonArray> create( | |||
BSONElement *pBsonElement); | BSONElement *pBsonElement, | |||
const intrusive_ptr<ExpressionContext> &pExpCtx); | ||||
protected: | protected: | |||
// virtuals from DocumentSource | // virtuals from DocumentSource | |||
virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | |||
private: | private: | |||
DocumentSourceBsonArray(BSONElement *pBsonElement); | DocumentSourceBsonArray(BSONElement *pBsonElement, | |||
const intrusive_ptr<ExpressionContext> &pExpCtx); | ||||
BSONObj embeddedObject; | BSONObj embeddedObject; | |||
BSONObjIterator arrayIterator; | BSONObjIterator arrayIterator; | |||
BSONElement currentElement; | BSONElement currentElement; | |||
bool haveCurrent; | bool haveCurrent; | |||
}; | }; | |||
class DocumentSourceCommandFutures : | class DocumentSourceCommandFutures : | |||
public DocumentSource { | public DocumentSource { | |||
public: | public: | |||
// virtuals from DocumentSource | // virtuals from DocumentSource | |||
virtual ~DocumentSourceCommandFutures(); | virtual ~DocumentSourceCommandFutures(); | |||
virtual bool eof(); | virtual bool eof(); | |||
virtual bool advance(); | virtual bool advance(); | |||
virtual intrusive_ptr<Document> getCurrent(); | virtual intrusive_ptr<Document> getCurrent(); | |||
virtual void setSource(const intrusive_ptr<DocumentSource> &pSource ); | virtual void setSource(DocumentSource *pSource); | |||
/* convenient shorthand for a commonly used type */ | /* convenient shorthand for a commonly used type */ | |||
typedef list<shared_ptr<Future::CommandResult> > FuturesList; | typedef list<shared_ptr<Future::CommandResult> > FuturesList; | |||
/** | /** | |||
Create a DocumentSource that wraps a list of Command::Futures. | Create a DocumentSource that wraps a list of Command::Futures. | |||
@param errmsg place to write error messages to; must exist for th e | @param errmsg place to write error messages to; must exist for th e | |||
lifetime of the created DocumentSourceCommandFutures | lifetime of the created DocumentSourceCommandFutures | |||
@param pList the list of futures | @param pList the list of futures | |||
@param pExpCtx the expression context for the pipeline | ||||
@returns the newly created DocumentSource | ||||
*/ | */ | |||
static intrusive_ptr<DocumentSourceCommandFutures> create( | static intrusive_ptr<DocumentSourceCommandFutures> create( | |||
string &errmsg, FuturesList *pList); | string &errmsg, FuturesList *pList, | |||
const intrusive_ptr<ExpressionContext> &pExpCtx); | ||||
protected: | protected: | |||
// virtuals from DocumentSource | // virtuals from DocumentSource | |||
virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | |||
private: | private: | |||
DocumentSourceCommandFutures(string &errmsg, FuturesList *pList); | DocumentSourceCommandFutures(string &errmsg, FuturesList *pList, | |||
const intrusive_ptr<ExpressionContext> &pExpCtx); | ||||
/** | /** | |||
Advance to the next document, setting pCurrent appropriately. | Advance to the next document, setting pCurrent appropriately. | |||
Adjusts pCurrent, pBsonSource, and iterator, as needed. On exit, | Adjusts pCurrent, pBsonSource, and iterator, as needed. On exit, | |||
pCurrent is the Document to return, or NULL. If NULL, this | pCurrent is the Document to return, or NULL. If NULL, this | |||
indicates there is nothing more to return. | indicates there is nothing more to return. | |||
*/ | */ | |||
void getNextDocument(); | void getNextDocument(); | |||
skipping to change at line 255 | skipping to change at line 307 | |||
}; | }; | |||
class DocumentSourceCursor : | class DocumentSourceCursor : | |||
public DocumentSource { | public DocumentSource { | |||
public: | public: | |||
// virtuals from DocumentSource | // virtuals from DocumentSource | |||
virtual ~DocumentSourceCursor(); | virtual ~DocumentSourceCursor(); | |||
virtual bool eof(); | virtual bool eof(); | |||
virtual bool advance(); | virtual bool advance(); | |||
virtual intrusive_ptr<Document> getCurrent(); | virtual intrusive_ptr<Document> getCurrent(); | |||
virtual void setSource(const intrusive_ptr<DocumentSource> &pSource | virtual void setSource(DocumentSource *pSource); | |||
); | virtual void manageDependencies( | |||
const intrusive_ptr<DependencyTracker> &pTracker); | ||||
/** | /** | |||
Create a document source based on a cursor. | Create a document source based on a cursor. | |||
This is usually put at the beginning of a chain of document sourc es | This is usually put at the beginning of a chain of document sourc es | |||
in order to fetch data from the database. | in order to fetch data from the database. | |||
@param pCursor the cursor to use to fetch data | @param pCursor the cursor to use to fetch data | |||
@param pExpCtx the expression context for the pipeline | ||||
*/ | */ | |||
static intrusive_ptr<DocumentSourceCursor> create( | static intrusive_ptr<DocumentSourceCursor> create( | |||
const shared_ptr<Cursor> &pCursor); | const shared_ptr<Cursor> &pCursor, | |||
const string &ns, | ||||
const intrusive_ptr<ExpressionContext> &pExpCtx); | ||||
/** | /** | |||
Add a BSONObj dependency. | Add a BSONObj dependency. | |||
Some Cursor creation functions rely on BSON objects to specify | Some Cursor creation functions rely on BSON objects to specify | |||
their query predicate or sort. These often take a BSONObj | their query predicate or sort. These often take a BSONObj | |||
by reference for these, but to not copy it. As a result, the | by reference for these, but to not copy it. As a result, the | |||
BSONObjs specified must outlive the Cursor. In order to ensure | BSONObjs specified must outlive the Cursor. In order to ensure | |||
that, use this to preserve a pointer to the BSONObj here. | that, use this to preserve a pointer to the BSONObj here. | |||
skipping to change at line 297 | skipping to change at line 354 | |||
@param pBsonObj pointer to the BSON object to preserve | @param pBsonObj pointer to the BSON object to preserve | |||
*/ | */ | |||
void addBsonDependency(const shared_ptr<BSONObj> &pBsonObj); | void addBsonDependency(const shared_ptr<BSONObj> &pBsonObj); | |||
protected: | protected: | |||
// virtuals from DocumentSource | // virtuals from DocumentSource | |||
virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | |||
private: | private: | |||
DocumentSourceCursor(const shared_ptr<Cursor> &pTheCursor); | DocumentSourceCursor( | |||
const shared_ptr<Cursor> &pTheCursor, const string &ns, | ||||
const intrusive_ptr<ExpressionContext> &pExpCtx); | ||||
void findNext(); | void findNext(); | |||
intrusive_ptr<Document> pCurrent; | intrusive_ptr<Document> pCurrent; | |||
/* | /* | |||
The bsonDependencies must outlive the Cursor wrapped by this | The bsonDependencies must outlive the Cursor wrapped by this | |||
source. Therefore, bsonDependencies must appear before pCursor | source. Therefore, bsonDependencies must appear before pCursor | |||
in order its destructor to be called *after* pCursor's. | in order cause its destructor to be called *after* pCursor's. | |||
*/ | */ | |||
vector<shared_ptr<BSONObj> > bsonDependencies; | vector<shared_ptr<BSONObj> > bsonDependencies; | |||
shared_ptr<Cursor> pCursor; | shared_ptr<Cursor> pCursor; | |||
/* | ||||
In order to yield, we need a ClientCursor. | ||||
*/ | ||||
ClientCursor::CleanupPointer pClientCursor; | ||||
/* | ||||
Advance the cursor, and yield sometimes. | ||||
If the state of the world changed during the yield such that we | ||||
are unable to continue execution of the query, this will release | ||||
the | ||||
client cursor, and throw an error. | ||||
*/ | ||||
void advanceAndYield(); | ||||
/* | ||||
This document source hangs on to the dependency tracker when it | ||||
gets it so that it can be used for selective reification of | ||||
fields in order to avoid fields that are not required through the | ||||
pipeline. | ||||
*/ | ||||
intrusive_ptr<DependencyTracker> pDependencies; | ||||
}; | }; | |||
/* | /* | |||
This contains all the basic mechanics for filtering a stream of | This contains all the basic mechanics for filtering a stream of | |||
Documents, except for the actual predicate evaluation itself. This w as | Documents, except for the actual predicate evaluation itself. This w as | |||
factored out so we could create DocumentSources that use both Matcher | factored out so we could create DocumentSources that use both Matcher | |||
style predicates as well as full Expressions. | style predicates as well as full Expressions. | |||
*/ | */ | |||
class DocumentSourceFilterBase : | class DocumentSourceFilterBase : | |||
public DocumentSource { | public DocumentSource { | |||
skipping to change at line 340 | skipping to change at line 421 | |||
as early a point as possible in the document processing pipeline. | as early a point as possible in the document processing pipeline. | |||
See db/Matcher.h and the associated wiki documentation for the | See db/Matcher.h and the associated wiki documentation for the | |||
format. This conversion is used to move back to the low-level | format. This conversion is used to move back to the low-level | |||
find() Cursor mechanism. | find() Cursor mechanism. | |||
@param pBuilder the builder to write to | @param pBuilder the builder to write to | |||
*/ | */ | |||
virtual void toMatcherBson(BSONObjBuilder *pBuilder) const = 0; | virtual void toMatcherBson(BSONObjBuilder *pBuilder) const = 0; | |||
protected: | protected: | |||
DocumentSourceFilterBase(); | DocumentSourceFilterBase( | |||
const intrusive_ptr<ExpressionContext> &pExpCtx); | ||||
/** | /** | |||
Test the given document against the predicate and report if it | Test the given document against the predicate and report if it | |||
should be accepted or not. | should be accepted or not. | |||
@param pDocument the document to test | @param pDocument the document to test | |||
@returns true if the document matches the filter, false otherwise | @returns true if the document matches the filter, false otherwise | |||
*/ | */ | |||
virtual bool accept(const intrusive_ptr<Document> &pDocument) const = 0; | virtual bool accept(const intrusive_ptr<Document> &pDocument) const = 0; | |||
skipping to change at line 367 | skipping to change at line 449 | |||
intrusive_ptr<Document> pCurrent; | intrusive_ptr<Document> pCurrent; | |||
}; | }; | |||
class DocumentSourceFilter : | class DocumentSourceFilter : | |||
public DocumentSourceFilterBase { | public DocumentSourceFilterBase { | |||
public: | public: | |||
// virtuals from DocumentSource | // virtuals from DocumentSource | |||
virtual ~DocumentSourceFilter(); | virtual ~DocumentSourceFilter(); | |||
virtual bool coalesce(const intrusive_ptr<DocumentSource> &pNextSou rce); | virtual bool coalesce(const intrusive_ptr<DocumentSource> &pNextSou rce); | |||
virtual void optimize(); | virtual void optimize(); | |||
virtual const char *getSourceName() const; | ||||
/** | /** | |||
Create a filter. | Create a filter. | |||
@param pBsonElement the raw BSON specification for the filter | @param pBsonElement the raw BSON specification for the filter | |||
@param pExpCtx the expression context for the pipeline | ||||
@returns the filter | @returns the filter | |||
*/ | */ | |||
static intrusive_ptr<DocumentSource> createFromBson( | static intrusive_ptr<DocumentSource> createFromBson( | |||
BSONElement *pBsonElement, | BSONElement *pBsonElement, | |||
const intrusive_ptr<ExpressionContext> &pCtx); | const intrusive_ptr<ExpressionContext> &pExpCtx); | |||
/** | /** | |||
Create a filter. | Create a filter. | |||
@param pFilter the expression to use to filter | @param pFilter the expression to use to filter | |||
@param pExpCtx the expression context for the pipeline | ||||
@returns the filter | @returns the filter | |||
*/ | */ | |||
static intrusive_ptr<DocumentSourceFilter> create( | static intrusive_ptr<DocumentSourceFilter> create( | |||
const intrusive_ptr<Expression> &pFilter); | const intrusive_ptr<Expression> &pFilter, | |||
const intrusive_ptr<ExpressionContext> &pExpCtx); | ||||
/** | /** | |||
Create a BSONObj suitable for Matcher construction. | Create a BSONObj suitable for Matcher construction. | |||
This is used after filter analysis has moved as many filters to | This is used after filter analysis has moved as many filters to | |||
as early a point as possible in the document processing pipeline. | as early a point as possible in the document processing pipeline. | |||
See db/Matcher.h and the associated wiki documentation for the | See db/Matcher.h and the associated wiki documentation for the | |||
format. This conversion is used to move back to the low-level | format. This conversion is used to move back to the low-level | |||
find() Cursor mechanism. | find() Cursor mechanism. | |||
skipping to change at line 410 | skipping to change at line 496 | |||
static const char filterName[]; | static const char filterName[]; | |||
protected: | protected: | |||
// virtuals from DocumentSource | // virtuals from DocumentSource | |||
virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | |||
// virtuals from DocumentSourceFilterBase | // virtuals from DocumentSourceFilterBase | |||
virtual bool accept(const intrusive_ptr<Document> &pDocument) const ; | virtual bool accept(const intrusive_ptr<Document> &pDocument) const ; | |||
private: | private: | |||
DocumentSourceFilter(const intrusive_ptr<Expression> &pFilter); | DocumentSourceFilter(const intrusive_ptr<Expression> &pFilter, | |||
const intrusive_ptr<ExpressionContext> &pExpCt | ||||
x); | ||||
intrusive_ptr<Expression> pFilter; | intrusive_ptr<Expression> pFilter; | |||
}; | }; | |||
class DocumentSourceGroup : | class DocumentSourceGroup : | |||
public DocumentSource { | public DocumentSource { | |||
public: | public: | |||
// virtuals from DocumentSource | // virtuals from DocumentSource | |||
virtual ~DocumentSourceGroup(); | virtual ~DocumentSourceGroup(); | |||
virtual bool eof(); | virtual bool eof(); | |||
virtual bool advance(); | virtual bool advance(); | |||
virtual const char *getSourceName() const; | ||||
virtual intrusive_ptr<Document> getCurrent(); | virtual intrusive_ptr<Document> getCurrent(); | |||
/** | /** | |||
Create a new grouping DocumentSource. | Create a new grouping DocumentSource. | |||
@param pCtx the expression context | @param pExpCtx the expression context for the pipeline | |||
@returns the DocumentSource | @returns the DocumentSource | |||
*/ | */ | |||
static intrusive_ptr<DocumentSourceGroup> create( | static intrusive_ptr<DocumentSourceGroup> create( | |||
const intrusive_ptr<ExpressionContext> &pCtx); | const intrusive_ptr<ExpressionContext> &pExpCtx); | |||
/** | /** | |||
Set the Id Expression. | Set the Id Expression. | |||
Documents that pass through the grouping Document are grouped | Documents that pass through the grouping Document are grouped | |||
according to this key. This will generate the id_ field in the | according to this key. This will generate the id_ field in the | |||
result documents. | result documents. | |||
@param pExpression the group key | @param pExpression the group key | |||
*/ | */ | |||
skipping to change at line 469 | skipping to change at line 557 | |||
const intrusive_ptr<Expression> &pExpression); | const intrusive_ptr<Expression> &pExpression); | |||
/** | /** | |||
Create a grouping DocumentSource from BSON. | Create a grouping DocumentSource from BSON. | |||
This is a convenience method that uses the above, and operates on | This is a convenience method that uses the above, and operates on | |||
a BSONElement that has been deteremined to be an Object with an | a BSONElement that has been deteremined to be an Object with an | |||
element named $group. | element named $group. | |||
@param pBsonElement the BSONELement that defines the group | @param pBsonElement the BSONELement that defines the group | |||
@param pCtx the expression context | @param pExpCtx the expression context | |||
@returns the grouping DocumentSource | @returns the grouping DocumentSource | |||
*/ | */ | |||
static intrusive_ptr<DocumentSource> createFromBson( | static intrusive_ptr<DocumentSource> createFromBson( | |||
BSONElement *pBsonElement, | BSONElement *pBsonElement, | |||
const intrusive_ptr<ExpressionContext> &pCtx); | const intrusive_ptr<ExpressionContext> &pExpCtx); | |||
/** | /** | |||
Create a unifying group that can be used to combine group results | Create a unifying group that can be used to combine group results | |||
from shards. | from shards. | |||
@returns the grouping DocumentSource | @returns the grouping DocumentSource | |||
*/ | */ | |||
intrusive_ptr<DocumentSource> createMerger(); | intrusive_ptr<DocumentSource> createMerger(); | |||
static const char groupName[]; | static const char groupName[]; | |||
protected: | protected: | |||
// virtuals from DocumentSource | // virtuals from DocumentSource | |||
virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | |||
private: | private: | |||
DocumentSourceGroup(const intrusive_ptr<ExpressionContext> &pCtx); | DocumentSourceGroup(const intrusive_ptr<ExpressionContext> &pExpCtx ); | |||
/* | /* | |||
Before returning anything, this source must fetch everything from | Before returning anything, this source must fetch everything from | |||
the underlying source and group it. populate() is used to do tha t | the underlying source and group it. populate() is used to do tha t | |||
on the first call to any method on this source. The populated | on the first call to any method on this source. The populated | |||
boolean indicates that this has been done. | boolean indicates that this has been done. | |||
*/ | */ | |||
void populate(); | void populate(); | |||
bool populated; | bool populated; | |||
skipping to change at line 530 | skipping to change at line 618 | |||
vector<string> vFieldName; | vector<string> vFieldName; | |||
vector<intrusive_ptr<Accumulator> (*)( | vector<intrusive_ptr<Accumulator> (*)( | |||
const intrusive_ptr<ExpressionContext> &)> vpAccumulatorFactory ; | const intrusive_ptr<ExpressionContext> &)> vpAccumulatorFactory ; | |||
vector<intrusive_ptr<Expression> > vpExpression; | vector<intrusive_ptr<Expression> > vpExpression; | |||
intrusive_ptr<Document> makeDocument( | intrusive_ptr<Document> makeDocument( | |||
const GroupsType::iterator &rIter); | const GroupsType::iterator &rIter); | |||
GroupsType::iterator groupsIterator; | GroupsType::iterator groupsIterator; | |||
intrusive_ptr<Document> pCurrent; | intrusive_ptr<Document> pCurrent; | |||
intrusive_ptr<ExpressionContext> pCtx; | ||||
}; | }; | |||
class DocumentSourceMatch : | class DocumentSourceMatch : | |||
public DocumentSourceFilterBase { | public DocumentSourceFilterBase { | |||
public: | public: | |||
// virtuals from DocumentSource | // virtuals from DocumentSource | |||
virtual ~DocumentSourceMatch(); | virtual ~DocumentSourceMatch(); | |||
virtual const char *getSourceName() const; | ||||
virtual void manageDependencies( | ||||
const intrusive_ptr<DependencyTracker> &pTracker); | ||||
/** | /** | |||
Create a filter. | Create a filter. | |||
@param pBsonElement the raw BSON specification for the filter | @param pBsonElement the raw BSON specification for the filter | |||
@returns the filter | @returns the filter | |||
*/ | */ | |||
static intrusive_ptr<DocumentSource> createFromBson( | static intrusive_ptr<DocumentSource> createFromBson( | |||
BSONElement *pBsonElement, | BSONElement *pBsonElement, | |||
const intrusive_ptr<ExpressionContext> &pCtx); | const intrusive_ptr<ExpressionContext> &pCtx); | |||
skipping to change at line 573 | skipping to change at line 662 | |||
static const char matchName[]; | static const char matchName[]; | |||
protected: | protected: | |||
// virtuals from DocumentSource | // virtuals from DocumentSource | |||
virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | |||
// virtuals from DocumentSourceFilterBase | // virtuals from DocumentSourceFilterBase | |||
virtual bool accept(const intrusive_ptr<Document> &pDocument) const ; | virtual bool accept(const intrusive_ptr<Document> &pDocument) const ; | |||
private: | private: | |||
DocumentSourceMatch(const BSONObj &query); | DocumentSourceMatch(const BSONObj &query, | |||
const intrusive_ptr<ExpressionContext> &pExpCtx); | ||||
Matcher matcher; | Matcher matcher; | |||
}; | }; | |||
class DocumentSourceOut : | class DocumentSourceOut : | |||
public DocumentSource { | public DocumentSource { | |||
public: | public: | |||
// virtuals from DocumentSource | // virtuals from DocumentSource | |||
virtual ~DocumentSourceOut(); | virtual ~DocumentSourceOut(); | |||
virtual bool eof(); | virtual bool eof(); | |||
virtual bool advance(); | virtual bool advance(); | |||
virtual const char *getSourceName() const; | ||||
virtual intrusive_ptr<Document> getCurrent(); | virtual intrusive_ptr<Document> getCurrent(); | |||
/** | /** | |||
Create a document source for output and pass-through. | Create a document source for output and pass-through. | |||
This can be put anywhere in a pipeline and will store content as | This can be put anywhere in a pipeline and will store content as | |||
well as pass it on. | well as pass it on. | |||
@param pBsonElement the raw BSON specification for the source | ||||
@param pExpCtx the expression context for the pipeline | ||||
@returns the newly created document source | @returns the newly created document source | |||
*/ | */ | |||
static intrusive_ptr<DocumentSourceOut> createFromBson( | static intrusive_ptr<DocumentSourceOut> createFromBson( | |||
BSONElement *pBsonElement); | BSONElement *pBsonElement, | |||
const intrusive_ptr<ExpressionContext> &pExpCtx); | ||||
static const char outName[]; | static const char outName[]; | |||
protected: | protected: | |||
// virtuals from DocumentSource | // virtuals from DocumentSource | |||
virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | |||
private: | private: | |||
DocumentSourceOut(BSONElement *pBsonElement); | DocumentSourceOut(BSONElement *pBsonElement, | |||
const intrusive_ptr<ExpressionContext> &pExpCtx); | ||||
}; | }; | |||
class DocumentSourceProject : | class DocumentSourceProject : | |||
public DocumentSource, | public DocumentSource { | |||
public boost::enable_shared_from_this<DocumentSourceProject> { | ||||
public: | public: | |||
// virtuals from DocumentSource | // virtuals from DocumentSource | |||
virtual ~DocumentSourceProject(); | virtual ~DocumentSourceProject(); | |||
virtual bool eof(); | virtual bool eof(); | |||
virtual bool advance(); | virtual bool advance(); | |||
virtual const char *getSourceName() const; | ||||
virtual intrusive_ptr<Document> getCurrent(); | virtual intrusive_ptr<Document> getCurrent(); | |||
virtual void optimize(); | virtual void optimize(); | |||
virtual void manageDependencies( | ||||
const intrusive_ptr<DependencyTracker> &pTracker); | ||||
/** | /** | |||
Create a new DocumentSource that can implement projection. | Create a new DocumentSource that can implement projection. | |||
@param pExpCtx the expression context for the pipeline | ||||
@returns the projection DocumentSource | @returns the projection DocumentSource | |||
*/ | */ | |||
static intrusive_ptr<DocumentSourceProject> create(); | static intrusive_ptr<DocumentSourceProject> create( | |||
const intrusive_ptr<ExpressionContext> &pExpCtx); | ||||
/** | /** | |||
Include a field path in a projection. | Include a field path in a projection. | |||
@param fieldPath the path of the field to include | @param fieldPath the path of the field to include | |||
*/ | */ | |||
void includePath(const string &fieldPath); | void includePath(const string &fieldPath); | |||
/** | /** | |||
Exclude a field path from the projection. | Exclude a field path from the projection. | |||
skipping to change at line 659 | skipping to change at line 758 | |||
void addField(const string &fieldName, | void addField(const string &fieldName, | |||
const intrusive_ptr<Expression> &pExpression); | const intrusive_ptr<Expression> &pExpression); | |||
/** | /** | |||
Create a new projection DocumentSource from BSON. | Create a new projection DocumentSource from BSON. | |||
This is a convenience for directly handling BSON, and relies on t he | This is a convenience for directly handling BSON, and relies on t he | |||
above methods. | above methods. | |||
@param pBsonElement the BSONElement with an object named $project | @param pBsonElement the BSONElement with an object named $project | |||
@param pExpCtx the expression context for the pipeline | ||||
@returns the created projection | @returns the created projection | |||
*/ | */ | |||
static intrusive_ptr<DocumentSource> createFromBson( | static intrusive_ptr<DocumentSource> createFromBson( | |||
BSONElement *pBsonElement, | BSONElement *pBsonElement, | |||
const intrusive_ptr<ExpressionContext> &pCtx); | const intrusive_ptr<ExpressionContext> &pExpCtx); | |||
static const char projectName[]; | static const char projectName[]; | |||
protected: | protected: | |||
// virtuals from DocumentSource | // virtuals from DocumentSource | |||
virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | |||
private: | private: | |||
DocumentSourceProject(); | DocumentSourceProject(const intrusive_ptr<ExpressionContext> &pExpC tx); | |||
// configuration state | // configuration state | |||
bool excludeId; | bool excludeId; | |||
intrusive_ptr<ExpressionObject> pEO; | intrusive_ptr<ExpressionObject> pEO; | |||
/* | ||||
Utility object used by manageDependencies(). | ||||
Removes dependencies from a DependencyTracker. | ||||
*/ | ||||
class DependencyRemover : | ||||
public ExpressionObject::PathSink { | ||||
public: | ||||
// virtuals from PathSink | ||||
virtual void path(const string &path, bool include); | ||||
/* | ||||
Constructor. | ||||
Captures a reference to the smart pointer to the DependencyTr | ||||
acker | ||||
that this will remove dependencies from via | ||||
ExpressionObject::emitPaths(). | ||||
@param pTracker reference to the smart pointer to the | ||||
DependencyTracker | ||||
*/ | ||||
DependencyRemover(const intrusive_ptr<DependencyTracker> &pTrac | ||||
ker); | ||||
private: | ||||
const intrusive_ptr<DependencyTracker> &pTracker; | ||||
}; | ||||
/* | ||||
Utility object used by manageDependencies(). | ||||
Checks dependencies to see if they are present. If not, then | ||||
throws a user error. | ||||
*/ | ||||
class DependencyChecker : | ||||
public ExpressionObject::PathSink { | ||||
public: | ||||
// virtuals from PathSink | ||||
virtual void path(const string &path, bool include); | ||||
/* | ||||
Constructor. | ||||
Captures a reference to the smart pointer to the DependencyTr | ||||
acker | ||||
that this will check dependencies from from | ||||
ExpressionObject::emitPaths() to see if they are required. | ||||
@param pTracker reference to the smart pointer to the | ||||
DependencyTracker | ||||
@param pThis the projection that is making this request | ||||
*/ | ||||
DependencyChecker( | ||||
const intrusive_ptr<DependencyTracker> &pTracker, | ||||
const DocumentSourceProject *pThis); | ||||
private: | ||||
const intrusive_ptr<DependencyTracker> &pTracker; | ||||
const DocumentSourceProject *pThis; | ||||
}; | ||||
}; | }; | |||
class DocumentSourceSort : | class DocumentSourceSort : | |||
public DocumentSource { | public DocumentSource { | |||
public: | public: | |||
// virtuals from DocumentSource | // virtuals from DocumentSource | |||
virtual ~DocumentSourceSort(); | virtual ~DocumentSourceSort(); | |||
virtual bool eof(); | virtual bool eof(); | |||
virtual bool advance(); | virtual bool advance(); | |||
virtual const char *getSourceName() const; | ||||
virtual intrusive_ptr<Document> getCurrent(); | virtual intrusive_ptr<Document> getCurrent(); | |||
virtual void manageDependencies( | ||||
const intrusive_ptr<DependencyTracker> &pTracker); | ||||
/* | /* | |||
TODO | TODO | |||
Adjacent sorts should reduce to the last sort. | Adjacent sorts should reduce to the last sort. | |||
virtual bool coalesce(const intrusive_ptr<DocumentSource> &pNextSou rce); | virtual bool coalesce(const intrusive_ptr<DocumentSource> &pNextSou rce); | |||
*/ | */ | |||
/** | /** | |||
Create a new sorting DocumentSource. | Create a new sorting DocumentSource. | |||
@param pCtx the expression context | @param pExpCtx the expression context for the pipeline | |||
@returns the DocumentSource | @returns the DocumentSource | |||
*/ | */ | |||
static intrusive_ptr<DocumentSourceSort> create( | static intrusive_ptr<DocumentSourceSort> create( | |||
const intrusive_ptr<ExpressionContext> &pCtx); | const intrusive_ptr<ExpressionContext> &pExpCtx); | |||
/** | /** | |||
Add sort key field. | Add sort key field. | |||
Adds a sort key field to the key being built up. A concatenated | Adds a sort key field to the key being built up. A concatenated | |||
key is built up by calling this repeatedly. | key is built up by calling this repeatedly. | |||
@param fieldPath the field path to the key component | @param fieldPath the field path to the key component | |||
@param ascending if true, use the key for an ascending sort, | @param ascending if true, use the key for an ascending sort, | |||
otherwise, use it for descending | otherwise, use it for descending | |||
skipping to change at line 730 | skipping to change at line 892 | |||
void sortKeyToBson(BSONObjBuilder *pBuilder, bool usePrefix) const; | void sortKeyToBson(BSONObjBuilder *pBuilder, bool usePrefix) const; | |||
/** | /** | |||
Create a sorting DocumentSource from BSON. | Create a sorting DocumentSource from BSON. | |||
This is a convenience method that uses the above, and operates on | This is a convenience method that uses the above, and operates on | |||
a BSONElement that has been deteremined to be an Object with an | a BSONElement that has been deteremined to be an Object with an | |||
element named $group. | element named $group. | |||
@param pBsonElement the BSONELement that defines the group | @param pBsonElement the BSONELement that defines the group | |||
@param pCtx the expression context | @param pExpCtx the expression context for the pipeline | |||
@returns the grouping DocumentSource | @returns the grouping DocumentSource | |||
*/ | */ | |||
static intrusive_ptr<DocumentSource> createFromBson( | static intrusive_ptr<DocumentSource> createFromBson( | |||
BSONElement *pBsonElement, | BSONElement *pBsonElement, | |||
const intrusive_ptr<ExpressionContext> &pCtx); | const intrusive_ptr<ExpressionContext> &pExpCtx); | |||
static const char sortName[]; | static const char sortName[]; | |||
protected: | protected: | |||
// virtuals from DocumentSource | // virtuals from DocumentSource | |||
virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | |||
private: | private: | |||
DocumentSourceSort(const intrusive_ptr<ExpressionContext> &pCtx); | DocumentSourceSort(const intrusive_ptr<ExpressionContext> &pExpCtx) ; | |||
/* | /* | |||
Before returning anything, this source must fetch everything from | Before returning anything, this source must fetch everything from | |||
the underlying source and group it. populate() is used to do tha t | the underlying source and group it. populate() is used to do tha t | |||
on the first call to any method on this source. The populated | on the first call to any method on this source. The populated | |||
boolean indicates that this has been done. | boolean indicates that this has been done. | |||
*/ | */ | |||
void populate(); | void populate(); | |||
bool populated; | bool populated; | |||
long long count; | long long count; | |||
/* these two parallel each other */ | /* these two parallel each other */ | |||
vector<intrusive_ptr<ExpressionFieldPath> > vSortKey; | typedef vector<intrusive_ptr<ExpressionFieldPath> > SortPaths; | |||
SortPaths vSortKey; | ||||
vector<bool> vAscending; | vector<bool> vAscending; | |||
class Carrier { | class Carrier { | |||
public: | public: | |||
/* | /* | |||
We need access to the key for compares, so we have to carry | We need access to the key for compares, so we have to carry | |||
this around. | this around. | |||
*/ | */ | |||
DocumentSourceSort *pSort; | DocumentSourceSort *pSort; | |||
skipping to change at line 792 | skipping to change at line 955 | |||
indicating pL < pR, pL == pR, or pL > pR, respectively | indicating pL < pR, pL == pR, or pL > pR, respectively | |||
*/ | */ | |||
int compare(const intrusive_ptr<Document> &pL, | int compare(const intrusive_ptr<Document> &pL, | |||
const intrusive_ptr<Document> &pR); | const intrusive_ptr<Document> &pR); | |||
typedef list<Carrier> ListType; | typedef list<Carrier> ListType; | |||
ListType documents; | ListType documents; | |||
ListType::iterator listIterator; | ListType::iterator listIterator; | |||
intrusive_ptr<Document> pCurrent; | intrusive_ptr<Document> pCurrent; | |||
intrusive_ptr<ExpressionContext> pCtx; | ||||
}; | }; | |||
class DocumentSourceLimit : | class DocumentSourceLimit : | |||
public DocumentSource { | public DocumentSource { | |||
public: | public: | |||
// virtuals from DocumentSource | // virtuals from DocumentSource | |||
virtual ~DocumentSourceLimit(); | virtual ~DocumentSourceLimit(); | |||
virtual bool eof(); | virtual bool eof(); | |||
virtual bool advance(); | virtual bool advance(); | |||
virtual intrusive_ptr<Document> getCurrent(); | virtual intrusive_ptr<Document> getCurrent(); | |||
virtual const char *getSourceName() const; | ||||
virtual bool coalesce(const intrusive_ptr<DocumentSource> &pNextSou | ||||
rce); | ||||
/** | /** | |||
Create a new limiting DocumentSource. | Create a new limiting DocumentSource. | |||
@param pCtx the expression context | @param pExpCtx the expression context for the pipeline | |||
@returns the DocumentSource | @returns the DocumentSource | |||
*/ | */ | |||
static intrusive_ptr<DocumentSourceLimit> create( | static intrusive_ptr<DocumentSourceLimit> create( | |||
const intrusive_ptr<ExpressionContext> &pCtx); | const intrusive_ptr<ExpressionContext> &pExpCtx); | |||
/** | /** | |||
Create a limiting DocumentSource from BSON. | Create a limiting DocumentSource from BSON. | |||
This is a convenience method that uses the above, and operates on | This is a convenience method that uses the above, and operates on | |||
a BSONElement that has been deteremined to be an Object with an | a BSONElement that has been deteremined to be an Object with an | |||
element named $limit. | element named $limit. | |||
@param pBsonElement the BSONELement that defines the limit | @param pBsonElement the BSONELement that defines the limit | |||
@param pCtx the expression context | @param pExpCtx the expression context | |||
@returns the grouping DocumentSource | @returns the grouping DocumentSource | |||
*/ | */ | |||
static intrusive_ptr<DocumentSource> createFromBson( | static intrusive_ptr<DocumentSource> createFromBson( | |||
BSONElement *pBsonElement, | BSONElement *pBsonElement, | |||
const intrusive_ptr<ExpressionContext> &pCtx); | const intrusive_ptr<ExpressionContext> &pExpCtx); | |||
static const char limitName[]; | static const char limitName[]; | |||
protected: | protected: | |||
// virtuals from DocumentSource | // virtuals from DocumentSource | |||
virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | |||
private: | private: | |||
DocumentSourceLimit(const intrusive_ptr<ExpressionContext> &pCtx); | DocumentSourceLimit( | |||
const intrusive_ptr<ExpressionContext> &pExpCtx); | ||||
long long limit; | long long limit; | |||
long long count; | long long count; | |||
intrusive_ptr<Document> pCurrent; | intrusive_ptr<Document> pCurrent; | |||
intrusive_ptr<ExpressionContext> pCtx; | ||||
}; | }; | |||
class DocumentSourceSkip : | class DocumentSourceSkip : | |||
public DocumentSource { | public DocumentSource { | |||
public: | public: | |||
// virtuals from DocumentSource | // virtuals from DocumentSource | |||
virtual ~DocumentSourceSkip(); | virtual ~DocumentSourceSkip(); | |||
virtual bool eof(); | virtual bool eof(); | |||
virtual bool advance(); | virtual bool advance(); | |||
virtual intrusive_ptr<Document> getCurrent(); | virtual intrusive_ptr<Document> getCurrent(); | |||
virtual const char *getSourceName() const; | ||||
virtual bool coalesce(const intrusive_ptr<DocumentSource> &pNextSou | ||||
rce); | ||||
/** | /** | |||
Create a new skipping DocumentSource. | Create a new skipping DocumentSource. | |||
@param pCtx the expression context | @param pExpCtx the expression context | |||
@returns the DocumentSource | @returns the DocumentSource | |||
*/ | */ | |||
static intrusive_ptr<DocumentSourceSkip> create( | static intrusive_ptr<DocumentSourceSkip> create( | |||
const intrusive_ptr<ExpressionContext> &pCtx); | const intrusive_ptr<ExpressionContext> &pExpCtx); | |||
/** | /** | |||
Create a skipping DocumentSource from BSON. | Create a skipping DocumentSource from BSON. | |||
This is a convenience method that uses the above, and operates on | This is a convenience method that uses the above, and operates on | |||
a BSONElement that has been deteremined to be an Object with an | a BSONElement that has been deteremined to be an Object with an | |||
element named $skip. | element named $skip. | |||
@param pBsonElement the BSONELement that defines the skip | @param pBsonElement the BSONELement that defines the skip | |||
@param pCtx the expression context | @param pExpCtx the expression context | |||
@returns the grouping DocumentSource | @returns the grouping DocumentSource | |||
*/ | */ | |||
static intrusive_ptr<DocumentSource> createFromBson( | static intrusive_ptr<DocumentSource> createFromBson( | |||
BSONElement *pBsonElement, | BSONElement *pBsonElement, | |||
const intrusive_ptr<ExpressionContext> &pCtx); | const intrusive_ptr<ExpressionContext> &pExpCtx); | |||
static const char skipName[]; | static const char skipName[]; | |||
protected: | protected: | |||
// virtuals from DocumentSource | // virtuals from DocumentSource | |||
virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | |||
private: | private: | |||
DocumentSourceSkip(const intrusive_ptr<ExpressionContext> &pCtx); | DocumentSourceSkip(const intrusive_ptr<ExpressionContext> &pExpCtx) ; | |||
/* | /* | |||
Skips initial documents. | Skips initial documents. | |||
*/ | */ | |||
void skipper(); | void skipper(); | |||
long long skip; | long long skip; | |||
long long count; | long long count; | |||
intrusive_ptr<Document> pCurrent; | intrusive_ptr<Document> pCurrent; | |||
intrusive_ptr<ExpressionContext> pCtx; | ||||
}; | }; | |||
class DocumentSourceUnwind : | class DocumentSourceUnwind : | |||
public DocumentSource, | public DocumentSource { | |||
public boost::enable_shared_from_this<DocumentSourceUnwind> { | ||||
public: | public: | |||
// virtuals from DocumentSource | // virtuals from DocumentSource | |||
virtual ~DocumentSourceUnwind(); | virtual ~DocumentSourceUnwind(); | |||
virtual bool eof(); | virtual bool eof(); | |||
virtual bool advance(); | virtual bool advance(); | |||
virtual const char *getSourceName() const; | ||||
virtual intrusive_ptr<Document> getCurrent(); | virtual intrusive_ptr<Document> getCurrent(); | |||
virtual void manageDependencies( | ||||
const intrusive_ptr<DependencyTracker> &pTracker); | ||||
/** | /** | |||
Create a new DocumentSource that can implement unwind. | Create a new DocumentSource that can implement unwind. | |||
@param pExpCtx the expression context for the pipeline | ||||
@returns the projection DocumentSource | @returns the projection DocumentSource | |||
*/ | */ | |||
static intrusive_ptr<DocumentSourceUnwind> create(); | static intrusive_ptr<DocumentSourceUnwind> create( | |||
const intrusive_ptr<ExpressionContext> &pExpCtx); | ||||
/** | /** | |||
Specify the field to unwind. There must be exactly one before | Specify the field to unwind. There must be exactly one before | |||
the pipeline begins execution. | the pipeline begins execution. | |||
@param rFieldPath - path to the field to unwind | @param rFieldPath - path to the field to unwind | |||
*/ | */ | |||
void unwindField(const FieldPath &rFieldPath); | void unwindField(const FieldPath &rFieldPath); | |||
/** | /** | |||
Create a new projection DocumentSource from BSON. | Create a new projection DocumentSource from BSON. | |||
This is a convenience for directly handling BSON, and relies on t he | This is a convenience for directly handling BSON, and relies on t he | |||
above methods. | above methods. | |||
@param pBsonElement the BSONElement with an object named $project | @param pBsonElement the BSONElement with an object named $project | |||
@param pExpCtx the expression context for the pipeline | ||||
@returns the created projection | @returns the created projection | |||
*/ | */ | |||
static intrusive_ptr<DocumentSource> createFromBson( | static intrusive_ptr<DocumentSource> createFromBson( | |||
BSONElement *pBsonElement, | BSONElement *pBsonElement, | |||
const intrusive_ptr<ExpressionContext> &pCtx); | const intrusive_ptr<ExpressionContext> &pExpCtx); | |||
static const char unwindName[]; | static const char unwindName[]; | |||
protected: | protected: | |||
// virtuals from DocumentSource | // virtuals from DocumentSource | |||
virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | |||
private: | private: | |||
DocumentSourceUnwind(); | DocumentSourceUnwind(const intrusive_ptr<ExpressionContext> &pExpCt x); | |||
// configuration state | // configuration state | |||
FieldPath unwindPath; | FieldPath unwindPath; | |||
vector<int> fieldIndex; /* for the current document, the indices | vector<int> fieldIndex; /* for the current document, the indices | |||
leading down to the field being unwound */ | leading down to the field being unwound */ | |||
// iteration state | // iteration state | |||
intrusive_ptr<Document> pNoUnwindDocument; | intrusive_ptr<Document> pNoUnwindDocument; | |||
// document to return, pre-un wind | // document to return, pre-un wind | |||
skipping to change at line 980 | skipping to change at line 1147 | |||
clones (or the original) that we've made. | clones (or the original) that we've made. | |||
This expects pUnwindValue to have been set by a prior call to | This expects pUnwindValue to have been set by a prior call to | |||
advance(). However, pUnwindValue may also be NULL, in which case | advance(). However, pUnwindValue may also be NULL, in which case | |||
the field will be removed -- this is the action for an empty | the field will be removed -- this is the action for an empty | |||
array. | array. | |||
@returns a partial deep clone of pNoUnwindDocument | @returns a partial deep clone of pNoUnwindDocument | |||
*/ | */ | |||
intrusive_ptr<Document> clonePath() const; | intrusive_ptr<Document> clonePath() const; | |||
}; | }; | |||
} | } | |||
/* ======================= INLINED IMPLEMENTATIONS ======================== == */ | /* ======================= INLINED IMPLEMENTATIONS ======================== == */ | |||
namespace mongo { | namespace mongo { | |||
inline void DocumentSource::setPipelineStep(int s) { | ||||
step = s; | ||||
} | ||||
inline int DocumentSource::getPipelineStep() const { | ||||
return step; | ||||
} | ||||
inline void DocumentSourceGroup::setIdExpression( | inline void DocumentSourceGroup::setIdExpression( | |||
const intrusive_ptr<Expression> &pExpression) { | const intrusive_ptr<Expression> &pExpression) { | |||
pIdExpression = pExpression; | pIdExpression = pExpression; | |||
} | } | |||
inline DocumentSourceProject::DependencyRemover::DependencyRemover( | ||||
const intrusive_ptr<DependencyTracker> &pT): | ||||
pTracker(pT) { | ||||
} | ||||
inline DocumentSourceProject::DependencyChecker::DependencyChecker( | ||||
const intrusive_ptr<DependencyTracker> &pTrack, | ||||
const DocumentSourceProject *pT): | ||||
pTracker(pTrack), | ||||
pThis(pT) { | ||||
} | ||||
inline void DocumentSourceUnwind::resetArray() { | inline void DocumentSourceUnwind::resetArray() { | |||
pNoUnwindDocument.reset(); | pNoUnwindDocument.reset(); | |||
pUnwindArray.reset(); | pUnwindArray.reset(); | |||
pUnwinder.reset(); | pUnwinder.reset(); | |||
pUnwindValue.reset(); | pUnwindValue.reset(); | |||
} | } | |||
inline DocumentSourceSort::Carrier::Carrier( | inline DocumentSourceSort::Carrier::Carrier( | |||
DocumentSourceSort *pTheSort, | DocumentSourceSort *pTheSort, | |||
const intrusive_ptr<Document> &pTheDocument): | const intrusive_ptr<Document> &pTheDocument): | |||
End of changes. 92 change blocks. | ||||
69 lines changed or deleted | 264 lines changed or added | |||
dur.h | dur.h | |||
---|---|---|---|---|
skipping to change at line 17 | skipping to change at line 17 | |||
namespace mongo { | namespace mongo { | |||
class NamespaceDetails; | class NamespaceDetails; | |||
void mongoAbort(const char *msg); | void mongoAbort(const char *msg); | |||
void abort(); // not defined -- use mongoAbort() instead | void abort(); // not defined -- use mongoAbort() instead | |||
namespace dur { | namespace dur { | |||
void releasedWriteLock(); | ||||
// a smaller limit is likely better on 32 bit | // a smaller limit is likely better on 32 bit | |||
#if defined(__i386__) || defined(_M_IX86) | #if defined(__i386__) || defined(_M_IX86) | |||
const unsigned UncommittedBytesLimit = 50 * 1024 * 1024; | const unsigned UncommittedBytesLimit = 50 * 1024 * 1024; | |||
#else | #else | |||
const unsigned UncommittedBytesLimit = 100 * 1024 * 1024; | const unsigned UncommittedBytesLimit = 100 * 1024 * 1024; | |||
#endif | #endif | |||
/** Call during startup so durability module can initialize | /** Call during startup so durability module can initialize | |||
Throws if fatal error | Throws if fatal error | |||
Does nothing if cmdLine.dur is false | Does nothing if cmdLine.dur is false | |||
skipping to change at line 91 | skipping to change at line 93 | |||
/** Commit immediately. | /** Commit immediately. | |||
Generally, you do not want to do this often, as highly gran ular committing may affect | Generally, you do not want to do this often, as highly gran ular committing may affect | |||
performance. | performance. | |||
Does not return until the commit is complete. | Does not return until the commit is complete. | |||
You must be at least read locked when you call this. Ideal ly, you are not write locked | You must be at least read locked when you call this. Ideal ly, you are not write locked | |||
and then read operations can occur concurrently. | and then read operations can occur concurrently. | |||
Do not use this. Use commitIfNeeded() instead. | ||||
@return true if --dur is on. | @return true if --dur is on. | |||
@return false if --dur is off. (in which case there is acti on) | @return false if --dur is off. (in which case there is acti on) | |||
*/ | */ | |||
virtual bool commitNow() = 0; | virtual bool commitNow() = 0; | |||
/** Commit if enough bytes have been modified. Current threshol d is 50MB | /** Commit if enough bytes have been modified. Current threshol d is 50MB | |||
The idea is that long running write operations that dont yi eld | The idea is that long running write operations that dont yi eld | |||
(like creating an index or update with $atomic) can call th is | (like creating an index or update with $atomic) can call th is | |||
whenever the db is in a sane state and it will prevent comm its | whenever the db is in a sane state and it will prevent comm its | |||
from growing too large. | from growing too large. | |||
@return true if commited | @return true if commited | |||
*/ | */ | |||
virtual bool commitIfNeeded() = 0; | virtual bool commitIfNeeded(bool force=false) = 0; | |||
/** @return true if time to commit but does NOT do a commit */ | /** @return true if time to commit but does NOT do a commit */ | |||
virtual bool aCommitIsNeeded() const = 0; | virtual bool aCommitIsNeeded() const = 0; | |||
/** Declare write intent for a DiskLoc. @see DiskLoc::writing( ) */ | /** Declare write intent for a DiskLoc. @see DiskLoc::writing( ) */ | |||
inline DiskLoc& writingDiskLoc(DiskLoc& d) { return *((DiskLoc* ) writingPtr(&d, sizeof(d))); } | inline DiskLoc& writingDiskLoc(DiskLoc& d) { return *((DiskLoc* ) writingPtr(&d, sizeof(d))); } | |||
/** Declare write intent for an int */ | /** Declare write intent for an int */ | |||
inline int& writingInt(const int& d) { return *((int*) writingP tr((int*) &d, sizeof(d))); } | inline int& writingInt(int& d) { return *static_cast<int*>(writ ingPtr( &d, sizeof(d))); } | |||
/** "assume i've already indicated write intent, let me write" | /** "assume i've already indicated write intent, let me write" | |||
redeclaration is fine too, but this is faster. | redeclaration is fine too, but this is faster. | |||
*/ | */ | |||
template <typename T> | template <typename T> | |||
inline | inline | |||
T* alreadyDeclared(T *x) { | T* alreadyDeclared(T *x) { | |||
#if defined(_TESTINTENT) | #if defined(_TESTINTENT) | |||
return (T*) MongoMMF::switchToPrivateView(x); | return (T*) MongoMMF::switchToPrivateView(x); | |||
#else | #else | |||
skipping to change at line 135 | skipping to change at line 139 | |||
#endif | #endif | |||
} | } | |||
/** declare intent to write to x for sizeof(*x) */ | /** declare intent to write to x for sizeof(*x) */ | |||
template <typename T> | template <typename T> | |||
inline | inline | |||
T* writing(T *x) { | T* writing(T *x) { | |||
return (T*) writingPtr(x, sizeof(T)); | return (T*) writingPtr(x, sizeof(T)); | |||
} | } | |||
/** write something that doesn't have to be journaled, as this | ||||
write is "unimportant". | ||||
a good example is paddingFactor. | ||||
can be thought of as memcpy(dst,src,len) | ||||
the dur implementation acquires a mutex in this method, so | ||||
do not assume it is faster | ||||
without measuring! | ||||
*/ | ||||
virtual void setNoJournal(void *dst, void *src, unsigned len) = | ||||
0; | ||||
/** Commits pending changes, flushes all changes to main data | /** Commits pending changes, flushes all changes to main data | |||
files, then removes the journal. | files, then removes the journal. | |||
This is useful as a "barrier" to ensure that writes before this | This is useful as a "barrier" to ensure that writes before this | |||
call will never go through recovery and be applied to files | call will never go through recovery and be applied to files | |||
that have had changes made after this call applied. | that have had changes made after this call applied. | |||
*/ | */ | |||
virtual void syncDataAndTruncateJournal() = 0; | virtual void syncDataAndTruncateJournal() = 0; | |||
static DurableInterface& getDur() { return *_impl; } | static DurableInterface& getDur() { return *_impl; } | |||
skipping to change at line 182 | skipping to change at line 178 | |||
}; // class DurableInterface | }; // class DurableInterface | |||
class NonDurableImpl : public DurableInterface { | class NonDurableImpl : public DurableInterface { | |||
void* writingPtr(void *x, unsigned len); | void* writingPtr(void *x, unsigned len); | |||
void* writingAtOffset(void *buf, unsigned ofs, unsigned len) { return buf; } | void* writingAtOffset(void *buf, unsigned ofs, unsigned len) { return buf; } | |||
void* writingRangesAtOffsets(void *buf, const vector< pair< lon g long, unsigned > > &ranges) { return buf; } | void* writingRangesAtOffsets(void *buf, const vector< pair< lon g long, unsigned > > &ranges) { return buf; } | |||
void declareWriteIntent(void *, unsigned); | void declareWriteIntent(void *, unsigned); | |||
void createdFile(string filename, unsigned long long len) { } | void createdFile(string filename, unsigned long long len) { } | |||
bool awaitCommit() { return false; } | bool awaitCommit() { return false; } | |||
bool commitNow() { return false; } | bool commitNow() { return false; } | |||
bool commitIfNeeded() { return false; } | bool commitIfNeeded(bool) { return false; } | |||
bool aCommitIsNeeded() const { return false; } | bool aCommitIsNeeded() const { return false; } | |||
void setNoJournal(void *dst, void *src, unsigned len); | ||||
void syncDataAndTruncateJournal() {} | void syncDataAndTruncateJournal() {} | |||
}; | }; | |||
class DurableImpl : public DurableInterface { | class DurableImpl : public DurableInterface { | |||
void* writingPtr(void *x, unsigned len); | void* writingPtr(void *x, unsigned len); | |||
void* writingAtOffset(void *buf, unsigned ofs, unsigned len); | void* writingAtOffset(void *buf, unsigned ofs, unsigned len); | |||
void* writingRangesAtOffsets(void *buf, const vector< pair< lon g long, unsigned > > &ranges); | void* writingRangesAtOffsets(void *buf, const vector< pair< lon g long, unsigned > > &ranges); | |||
void declareWriteIntent(void *, unsigned); | void declareWriteIntent(void *, unsigned); | |||
void createdFile(string filename, unsigned long long len); | void createdFile(string filename, unsigned long long len); | |||
bool awaitCommit(); | bool awaitCommit(); | |||
bool commitNow(); | bool commitNow(); | |||
bool aCommitIsNeeded() const; | bool aCommitIsNeeded() const; | |||
bool commitIfNeeded(); | bool commitIfNeeded(bool); | |||
void setNoJournal(void *dst, void *src, unsigned len); | ||||
void syncDataAndTruncateJournal(); | void syncDataAndTruncateJournal(); | |||
}; | }; | |||
} // namespace dur | } // namespace dur | |||
inline dur::DurableInterface& getDur() { return dur::DurableInterface:: getDur(); } | inline dur::DurableInterface& getDur() { return dur::DurableInterface:: getDur(); } | |||
/** declare that we are modifying a diskloc and this is a datafile writ e. */ | /** declare that we are modifying a diskloc and this is a datafile writ e. */ | |||
inline DiskLoc& DiskLoc::writing() const { return getDur().writingDiskL oc(*const_cast< DiskLoc * >( this )); } | inline DiskLoc& DiskLoc::writing() const { return getDur().writingDiskL oc(*const_cast< DiskLoc * >( this )); } | |||
End of changes. 8 change blocks. | ||||
17 lines changed or deleted | 8 lines changed or added | |||
dur_commitjob.h | dur_commitjob.h | |||
---|---|---|---|---|
/* @file dur_commitjob.h used by dur.cpp | /* @file dur_commitjob.h used by dur.cpp */ | |||
*/ | ||||
/** | /** | |||
* Copyright (C) 2009 10gen Inc. | * Copyright (C) 2009 10gen Inc. | |||
* | * | |||
* This program is free software: you can redistribute it and/or modify | * This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Affero General Public License, version 3 , | * it under the terms of the GNU Affero General Public License, version 3 , | |||
* as published by the Free Software Foundation. | * as published by the Free Software Foundation. | |||
* | * | |||
* This program is distributed in the hope that it will be useful, | * This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
skipping to change at line 29 | skipping to change at line 28 | |||
#pragma once | #pragma once | |||
#include "../util/alignedbuilder.h" | #include "../util/alignedbuilder.h" | |||
#include "../util/mongoutils/hash.h" | #include "../util/mongoutils/hash.h" | |||
#include "../util/concurrency/synchronization.h" | #include "../util/concurrency/synchronization.h" | |||
#include "cmdline.h" | #include "cmdline.h" | |||
#include "durop.h" | #include "durop.h" | |||
#include "dur.h" | #include "dur.h" | |||
#include "taskqueue.h" | #include "taskqueue.h" | |||
#include "d_concurrency.h" | ||||
//#define DEBUG_WRITE_INTENT 1 | ||||
namespace mongo { | namespace mongo { | |||
namespace dur { | namespace dur { | |||
/** declaration of an intent to write to a region of a memory mappe | void assertLockedForCommitting(); | |||
d view | ||||
* | /** Declaration of an intent to write to a region of a memory mappe | |||
* We store the end rather than the start pointer to make operator< | d view | |||
faster | * We store the end rather than the start pointer to make operator | |||
* since that is heavily used in set lookup. | < faster | |||
* since that is heavily used in set lookup. | ||||
*/ | */ | |||
struct WriteIntent { /* copyable */ | struct WriteIntent { /* copyable */ | |||
WriteIntent() : /*w_ptr(0), */ p(0) { } | WriteIntent() : p(0) { } | |||
WriteIntent(void *a, unsigned b) : /*w_ptr(0), */ p((char*)a+b) | WriteIntent(void *a, unsigned b) : p((char*)a+b), len(b) { } | |||
, len(b) { } | void* start() const { return (char*) | |||
p - len; } | ||||
void* start() const { return (char*)p - len; } | void* end() const { return p; } | |||
void* end() const { return p; } | unsigned length() const { return len; } | |||
unsigned length() const { return len; } | ||||
bool operator < (const WriteIntent& rhs) const { return end() < rhs.end(); } | bool operator < (const WriteIntent& rhs) const { return end() < rhs.end(); } | |||
bool overlaps(const WriteIntent& rhs) const { return (start( | ||||
// can they be merged? | ) <= rhs.end() && end() >= rhs.start()); } | |||
bool overlaps(const WriteIntent& rhs) const { | bool contains(const WriteIntent& rhs) const { return (start( | |||
return (start() <= rhs.end() && end() >= rhs.start()); | ) <= rhs.start() && end() >= rhs.end()); } | |||
} | // merge into me: | |||
// is merging necessary? | ||||
bool contains(const WriteIntent& rhs) const { | ||||
return (start() <= rhs.start() && end() >= rhs.end()); | ||||
} | ||||
// merge into me | ||||
void absorb(const WriteIntent& other); | void absorb(const WriteIntent& other); | |||
friend ostream& operator << (ostream& out, const WriteIntent& w i) { | friend ostream& operator << (ostream& out, const WriteIntent& w i) { | |||
return (out << "p: " << wi.p << " end: " << wi.end() << " l en: " << wi.len); | return (out << "p: " << wi.p << " end: " << wi.end() << " l en: " << wi.len); | |||
} | } | |||
//mutable void *w_ptr; // writable mapping of p. | ||||
// mutable because set::iterator is const but this isn't used i | ||||
n op< | ||||
#if defined(_EXPERIMENTAL) | ||||
mutable unsigned ofsInJournalBuffer; | ||||
#endif | ||||
private: | private: | |||
void *p; // intent to write up to p | void *p; // intent to write up to p | |||
unsigned len; // up to this len | unsigned len; // up to this len | |||
}; | }; | |||
/** try to remember things we have already marked for journaling. false negatives are ok if infrequent - | /** try to remember things we have already marked for journaling. false negatives are ok if infrequent - | |||
we will just log them twice. | we will just log them twice. | |||
*/ | */ | |||
template<int Prime> | template<int Prime> | |||
class Already : boost::noncopyable { | class Already : boost::noncopyable { | |||
public: | public: | |||
Already() { clear(); } | Already() { clear(); } | |||
void clear() { memset(this, 0, sizeof(*this)); } | void clear() { memset(this, 0, sizeof(*this)); } | |||
/* see if we have Already recorded/indicated our write intent f or this region of memory. | /* see if we have Already recorded/indicated our write intent f or this region of memory. | |||
automatically upgrades the length if the length was shorter previously. | automatically upgrades the length if the length was shorter previously. | |||
@return true if already indicated. | @return true if already indicated. | |||
*/ | */ | |||
bool checkAndSet(void* p, int len) { | bool checkAndSet(void* p, int len) { | |||
unsigned x = mongoutils::hashPointer(p); | unsigned x = mongoutils::hashPointer(p); | |||
pair<void*, int>& nd = nodes[x % N]; | pair<void*, int>& nd = nodes[x % N]; | |||
if( nd.first == p ) { | if( nd.first == p ) { | |||
if( nd.second < len ) { | if( nd.second < len ) { | |||
nd.second = len; | nd.second = len; | |||
return false; // haven't indicated this len yet | return false; // haven't indicated this len yet | |||
} | } | |||
return true; // already indicated | return true; // already indicated | |||
} | } | |||
nd.first = p; | nd.first = p; | |||
nd.second = len; | nd.second = len; | |||
return false; // a new set | return false; // a new set | |||
} | } | |||
private: | private: | |||
enum { N = Prime }; // this should be small the idea is that it fits in the cpu cache easily | enum { N = Prime }; // this should be small the idea is that it fits in the cpu cache easily | |||
pair<void*,int> nodes[N]; | pair<void*,int> nodes[N]; | |||
}; | }; | |||
/** our record of pending/uncommitted write intents */ | /** our record of pending/uncommitted write intents */ | |||
class Writes : boost::noncopyable { | class IntentsAndDurOps : boost::noncopyable { | |||
struct D { | ||||
void *p; | ||||
unsigned len; | ||||
static void go(const D& d); | ||||
}; | ||||
public: | public: | |||
TaskQueue<D> _deferred; | vector<WriteIntent> _intents; | |||
Already<127> _alreadyNoted; | Already<127> _alreadyNoted; | |||
set<WriteIntent> _writes; | vector< shared_ptr<DurOp> > _durOps; // all the ops other than | |||
vector< shared_ptr<DurOp> > _ops; // all the ops other than bas | basic writes | |||
ic writes | ||||
bool _drained; // _deferred is drained? for asserting/testing | ||||
/** reset the Writes structure (empties all the above) */ | /** reset the IntentsAndDurOps structure (empties all the above ) */ | |||
void clear(); | void clear(); | |||
/** merges into set (ie non-deferred version) */ | ||||
void _insertWriteIntent(void* p, int len); | ||||
void insertWriteIntent(void* p, int len) { | void insertWriteIntent(void* p, int len) { | |||
#if defined(DEBUG_WRITE_INTENT) | _intents.push_back(WriteIntent(p,len)); | |||
if( _debug[p] < len ) | wassert( _intents.size() < 2000000 ); | |||
_debug[p] = len; | ||||
#endif | ||||
D d; | ||||
d.p = p; | ||||
d.len = len; | ||||
_deferred.defer(d); | ||||
} | } | |||
#if defined(DEBUG_WRITE_INTENT) | ||||
#ifdef _DEBUG | ||||
WriteIntent _last; | ||||
#endif | ||||
#if defined(DEBUG_WRITE_INTENT) | ||||
map<void*,int> _debug; | map<void*,int> _debug; | |||
#endif | #endif | |||
}; | }; | |||
#if defined(DEBUG_WRITE_INTENT) | /** so we don't have to lock the groupCommitMutex too often */ | |||
void assertAlreadyDeclared(void *, int len); | class ThreadLocalIntents { | |||
#else | enum { N = 21 }; | |||
inline void assertAlreadyDeclared(void *, int len) { } | dur::WriteIntent i[N]; | |||
#endif | int n; | |||
public: | ||||
ThreadLocalIntents() : n(0) { } | ||||
void _unspool(); | ||||
void unspool(); | ||||
void push(const WriteIntent& i); | ||||
int n_informational() const { return n; } | ||||
static AtomicUInt nSpooled; | ||||
}; | ||||
/** A commit job object for a group commit. Currently there is one instance of this object. | /** A commit job object for a group commit. Currently there is one instance of this object. | |||
concurrency: assumption is caller is appropriately locking. | concurrency: assumption is caller is appropriately locking. | |||
for example note() invocations are from the write lock. | for example note() invocations are from the write lock. | |||
other uses are in a read lock from a single thread (durThread) | other uses are in a read lock from a single thread (durThread) | |||
*/ | */ | |||
class CommitJob : boost::noncopyable { | class CommitJob : boost::noncopyable { | |||
public: | void _committingReset(); | |||
AlignedBuilder _ab; // for direct i/o writes to journal | ~CommitJob(){ verify(!"shouldn't destroy CommitJob!"); } | |||
CommitJob(); | ||||
~CommitJob(){ assert(!"shouldn't destroy CommitJob!"); } | ||||
/** record/note an intent to write */ | /** record/note an intent to write */ | |||
void note(void* p, int len); | void note(void* p, int len); | |||
// only called by : | ||||
friend class ThreadLocalIntents; | ||||
public: | ||||
SimpleMutex groupCommitMutex; | ||||
CommitJob(); | ||||
/** note an operation other than a "basic write" */ | /** note an operation other than a "basic write". threadsafe (l ocks in the impl) */ | |||
void noteOp(shared_ptr<DurOp> p); | void noteOp(shared_ptr<DurOp> p); | |||
set<WriteIntent>& writes() { | vector< shared_ptr<DurOp> >& ops() { | |||
if( !_wi._drained ) { | dassert( Lock::isLocked() ); // a rather weak chec | |||
// generally, you don't want to use the set until it is | k, we require more than that | |||
prepared (after deferred ops are applied) | groupCommitMutex.dassertLocked(); // this is what really ma | |||
// thus this assert here. | kes the below safe | |||
assert(false); | return _intentsAndDurOps._durOps; | |||
} | ||||
return _wi._writes; | ||||
} | } | |||
vector< shared_ptr<DurOp> >& ops() { return _wi._ops; } | ||||
/** this method is safe to call outside of locks. when haswritt en is false we don't do any group commit and avoid even | /** this method is safe to call outside of locks. when haswritt en is false we don't do any group commit and avoid even | |||
trying to acquire a lock, which might be helpful at times. | trying to acquire a lock, which might be helpful at times. | |||
*/ | */ | |||
bool hasWritten() const { return _hasWritten; } | bool hasWritten() const { return _hasWritten; } | |||
/** we use the commitjob object over and over, calling reset() | public: | |||
rather than reconstructing */ | /** these called by the groupCommit code as it goes along */ | |||
void reset(); | void commitingBegin(); | |||
void beginCommit(); | ||||
/** the commit code calls this when data reaches the journal (o n disk) */ | /** the commit code calls this when data reaches the journal (o n disk) */ | |||
void notifyCommitted() { _notify.notifyAll(_commitNumber); } | void committingNotifyCommitted() { | |||
groupCommitMutex.dassertLocked(); | ||||
_notify.notifyAll(_commitNumber); | ||||
} | ||||
/** we use the commitjob object over and over, calling reset() | ||||
rather than reconstructing */ | ||||
void committingReset() { | ||||
groupCommitMutex.dassertLocked(); | ||||
_committingReset(); | ||||
} | ||||
public: | ||||
/** we check how much written and if it is getting to be a lot, we commit sooner. */ | /** we check how much written and if it is getting to be a lot, we commit sooner. */ | |||
size_t bytes() const { return _bytes; } | size_t bytes() const { return _bytes; } | |||
#if defined(_DEBUG) | /** used in prepbasicwrites. sorted so that overlapping and dup | |||
const WriteIntent& lastWrite() const { return _wi._last; } | licate items | |||
#endif | * can be merged. we sort here so the caller receives somethin | |||
g they must | ||||
* keep const from their pov. */ | ||||
const vector<WriteIntent>& getIntentsSorted() { | ||||
groupCommitMutex.dassertLocked(); | ||||
sort(_intentsAndDurOps._intents.begin(), _intentsAndDurOps. | ||||
_intents.end()); | ||||
return _intentsAndDurOps._intents; | ||||
} | ||||
bool _hasWritten; | ||||
Writes& wi() { return _wi; } | ||||
private: | private: | |||
NotifyAll::When _commitNumber; | NotifyAll::When _commitNumber; | |||
bool _hasWritten; | IntentsAndDurOps _intentsAndDurOps; | |||
Writes _wi; // todo: fix name | ||||
size_t _bytes; | size_t _bytes; | |||
public: | public: | |||
NotifyAll _notify; // for getlasterror fsync:true acknowledgeme | NotifyAll _notify; // for getlasterror fsync:t | |||
nts | rue acknowledgements | |||
unsigned _nSinceCommitIfNeededCall; | unsigned _nSinceCommitIfNeededCall; // for asserts and debuggin | |||
g | ||||
}; | }; | |||
extern CommitJob& commitJob; | extern CommitJob& commitJob; | |||
#if defined(DEBUG_WRITE_INTENT) | ||||
void assertAlreadyDeclared(void *, int len); | ||||
#else | ||||
inline void assertAlreadyDeclared(void *, int len) { } | ||||
#endif | ||||
} | } | |||
} | } | |||
End of changes. 31 change blocks. | ||||
107 lines changed or deleted | 95 lines changed or added | |||
dur_recover.h | dur_recover.h | |||
---|---|---|---|---|
skipping to change at line 43 | skipping to change at line 43 | |||
void applyEntries(const vector<ParsedJournalEntry> &entries); | void applyEntries(const vector<ParsedJournalEntry> &entries); | |||
bool processFileBuffer(const void *, unsigned len); | bool processFileBuffer(const void *, unsigned len); | |||
bool processFile(boost::filesystem::path journalfile); | bool processFile(boost::filesystem::path journalfile); | |||
void _close(); // doesn't lock | void _close(); // doesn't lock | |||
list<boost::shared_ptr<MongoMMF> > _mmfs; | list<boost::shared_ptr<MongoMMF> > _mmfs; | |||
unsigned long long _lastDataSyncedFromLastRun; | unsigned long long _lastDataSyncedFromLastRun; | |||
unsigned long long _lastSeqMentionedInConsoleLog; | unsigned long long _lastSeqMentionedInConsoleLog; | |||
public: | public: | |||
mongo::mutex _mx; // protects _mmfs; see setNoJournal() too | mongo::mutex _mx; // protects _mmfs | |||
private: | private: | |||
bool _recovering; // are we in recovery or WRITETODATAFILES | bool _recovering; // are we in recovery or WRITETODATAFILES | |||
static RecoveryJob &_instance; | static RecoveryJob &_instance; | |||
}; | }; | |||
} | } | |||
} | } | |||
End of changes. 1 change blocks. | ||||
1 lines changed or deleted | 1 lines changed or added | |||
engine.h | engine.h | |||
---|---|---|---|---|
skipping to change at line 186 | skipping to change at line 186 | |||
static void setup(); | static void setup(); | |||
/** gets a scope from the pool or a new one if pool is empty | /** gets a scope from the pool or a new one if pool is empty | |||
* @param pool An identifier for the pool, usually the db name | * @param pool An identifier for the pool, usually the db name | |||
* @return the scope */ | * @return the scope */ | |||
auto_ptr<Scope> getPooledScope( const string& pool ); | auto_ptr<Scope> getPooledScope( const string& pool ); | |||
/** call this method to release some JS resources when a thread is done */ | /** call this method to release some JS resources when a thread is done */ | |||
void threadDone(); | void threadDone(); | |||
struct Unlocker { virtual ~Unlocker() {} }; | ||||
virtual auto_ptr<Unlocker> newThreadUnlocker() { return auto_ptr< U | ||||
nlocker >( new Unlocker ); } | ||||
void setScopeInitCallback( void ( *func )( Scope & ) ) { _scopeInit Callback = func; } | void setScopeInitCallback( void ( *func )( Scope & ) ) { _scopeInit Callback = func; } | |||
static void setConnectCallback( void ( *func )( DBClientWithCommand s& ) ) { _connectCallback = func; } | static void setConnectCallback( void ( *func )( DBClientWithCommand s& ) ) { _connectCallback = func; } | |||
static void runConnectCallback( DBClientWithCommands &c ) { | static void runConnectCallback( DBClientWithCommands &c ) { | |||
if ( _connectCallback ) | if ( _connectCallback ) | |||
_connectCallback( c ); | _connectCallback( c ); | |||
} | } | |||
// engine implementation may either respond to interrupt events or | // engine implementation may either respond to interrupt events or | |||
// poll for interrupts | // poll for interrupts | |||
End of changes. 1 change blocks. | ||||
4 lines changed or deleted | 0 lines changed or added | |||
engine_v8.h | engine_v8.h | |||
---|---|---|---|---|
skipping to change at line 88 | skipping to change at line 88 | |||
BSONHolder( BSONObj obj ) { | BSONHolder( BSONObj obj ) { | |||
_obj = obj.getOwned(); | _obj = obj.getOwned(); | |||
_modified = false; | _modified = false; | |||
} | } | |||
~BSONHolder() { | ~BSONHolder() { | |||
} | } | |||
BSONObj _obj; | BSONObj _obj; | |||
bool _modified; | bool _modified; | |||
list<string> _extra; | ||||
set<string> _removed; | ||||
}; | }; | |||
class V8Scope : public Scope { | class V8Scope : public Scope { | |||
public: | public: | |||
V8Scope( V8ScriptEngine * engine ); | V8Scope( V8ScriptEngine * engine ); | |||
~V8Scope(); | ~V8Scope(); | |||
virtual void reset(); | virtual void reset(); | |||
virtual void init( const BSONObj * data ); | virtual void init( const BSONObj * data ); | |||
skipping to change at line 239 | skipping to change at line 242 | |||
public: | public: | |||
V8ScriptEngine(); | V8ScriptEngine(); | |||
virtual ~V8ScriptEngine(); | virtual ~V8ScriptEngine(); | |||
virtual Scope * createScope() { return new V8Scope( this ); } | virtual Scope * createScope() { return new V8Scope( this ); } | |||
virtual void runTest() {} | virtual void runTest() {} | |||
bool utf8Ok() const { return true; } | bool utf8Ok() const { return true; } | |||
class V8UnlockForClient : public Unlocker { | ||||
// V8Unlock u_; | ||||
}; | ||||
virtual auto_ptr<Unlocker> newThreadUnlocker() { return auto_ptr< U | ||||
nlocker >( new V8UnlockForClient ); } | ||||
virtual void interrupt( unsigned opSpec ); | virtual void interrupt( unsigned opSpec ); | |||
virtual void interruptAll(); | virtual void interruptAll(); | |||
private: | private: | |||
friend class V8Scope; | friend class V8Scope; | |||
}; | }; | |||
class ExternalString : public v8::String::ExternalAsciiStringResource { | class ExternalString : public v8::String::ExternalAsciiStringResource { | |||
public: | public: | |||
ExternalString(std::string str) : _data(str) { | ExternalString(std::string str) : _data(str) { | |||
End of changes. 2 change blocks. | ||||
7 lines changed or deleted | 3 lines changed or added | |||
expression.h | expression.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 License | * You should have received a copy of the GNU Affero General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "pch.h" | #include "pch.h" | |||
#include "db/pipeline/field_path.h" | #include "db/pipeline/field_path.h" | |||
#include "util/intrusive_counter.h" | #include "util/intrusive_counter.h" | |||
#include "util/iterator.h" | ||||
namespace mongo { | namespace mongo { | |||
class BSONArrayBuilder; | class BSONArrayBuilder; | |||
class BSONElement; | class BSONElement; | |||
class BSONObjBuilder; | class BSONObjBuilder; | |||
class Builder; | class Builder; | |||
class DependencyTracker; | ||||
class Document; | class Document; | |||
class DocumentSource; | ||||
class ExpressionContext; | class ExpressionContext; | |||
class Value; | class Value; | |||
class Expression : | class Expression : | |||
public IntrusiveCounterUnsigned { | public IntrusiveCounterUnsigned { | |||
public: | public: | |||
virtual ~Expression() {}; | virtual ~Expression() {}; | |||
/* | /* | |||
Optimize the Expression. | Optimize the Expression. | |||
skipping to change at line 53 | skipping to change at line 57 | |||
$add, $and, or $or. | $add, $and, or $or. | |||
The Expression should be replaced with the return value, which ma y | The Expression should be replaced with the return value, which ma y | |||
or may not be the same object. In the case of constant folding, | or may not be the same object. In the case of constant folding, | |||
a computed expression may be replaced by a constant. | a computed expression may be replaced by a constant. | |||
@returns the optimized Expression | @returns the optimized Expression | |||
*/ | */ | |||
virtual intrusive_ptr<Expression> optimize() = 0; | virtual intrusive_ptr<Expression> optimize() = 0; | |||
/** | ||||
Add this expression's field dependencies to the dependency track | ||||
er. | ||||
Expressions are trees, so this is often recursive. | ||||
@params pTracker the tracker to add the dependencies to | ||||
*/ | ||||
virtual void addDependencies( | ||||
const intrusive_ptr<DependencyTracker> &pTracker, | ||||
const DocumentSource *pSource) const = 0; | ||||
/* | /* | |||
Evaluate the Expression using the given document as input. | Evaluate the Expression using the given document as input. | |||
@returns the computed value | @returns the computed value | |||
*/ | */ | |||
virtual intrusive_ptr<const Value> evaluate( | virtual intrusive_ptr<const Value> evaluate( | |||
const intrusive_ptr<Document> &pDocument) const = 0; | const intrusive_ptr<Document> &pDocument) const = 0; | |||
/* | /* | |||
Add the Expression (and any descendant Expressions) into a BSON | Add the Expression (and any descendant Expressions) into a BSON | |||
object that is under construction. | object that is under construction. | |||
Unevaluated Expressions always materialize as objects. Evaluatio n | Unevaluated Expressions always materialize as objects. Evaluatio n | |||
may produce a scalar or another object, either of which will be | may produce a scalar or another object, either of which will be | |||
substituted inline. | substituted inline. | |||
@param pBuilder the builder to add the expression to | @param pBuilder the builder to add the expression to | |||
@param fieldName the name the object should be given | @param fieldName the name the object should be given | |||
@param requireExpression specify true if the value must appear | ||||
as an expression; this is used by DocumentSources like | ||||
$project which distinguish between field inclusion and virtual | ||||
field specification; See ExpressionConstant. | ||||
*/ | */ | |||
virtual void addToBsonObj( | virtual void addToBsonObj( | |||
BSONObjBuilder *pBuilder, string fieldName, | BSONObjBuilder *pBuilder, string fieldName, | |||
unsigned depth) const = 0; | bool requireExpression) const = 0; | |||
/* | /* | |||
Add the Expression (and any descendant Expressions) into a BSON | Add the Expression (and any descendant Expressions) into a BSON | |||
array that is under construction. | array that is under construction. | |||
Unevaluated Expressions always materialize as objects. Evaluatio n | Unevaluated Expressions always materialize as objects. Evaluatio n | |||
may produce a scalar or another object, either of which will be | may produce a scalar or another object, either of which will be | |||
substituted inline. | substituted inline. | |||
@param pBuilder the builder to add the expression to | @param pBuilder the builder to add the expression to | |||
*/ | */ | |||
virtual void addToBsonArray(BSONArrayBuilder *pBuilder, | virtual void addToBsonArray(BSONArrayBuilder *pBuilder) const = 0; | |||
unsigned depth) const = 0; | ||||
/* | /* | |||
Convert the expression into a BSONObj that corresponds to the | Convert the expression into a BSONObj that corresponds to the | |||
db.collection.find() predicate language. This is intended for | db.collection.find() predicate language. This is intended for | |||
use by DocumentSourceFilter. | use by DocumentSourceFilter. | |||
This is more limited than the full expression language supported | This is more limited than the full expression language supported | |||
by all available expressions in a DocumentSource processing | by all available expressions in a DocumentSource processing | |||
pipeline, and will fail with an assertion if an attempt is made | pipeline, and will fail with an assertion if an attempt is made | |||
to go outside the bounds of the recognized patterns, which don't | to go outside the bounds of the recognized patterns, which don't | |||
include full computed expressions. There are other methods avail able | include full computed expressions. There are other methods avail able | |||
on DocumentSourceFilter which can be used to analyze a filter | on DocumentSourceFilter which can be used to analyze a filter | |||
predicate and break it up into appropriate expressions which can | predicate and break it up into appropriate expressions which can | |||
be translated within these constraints. As a result, the default | be translated within these constraints. As a result, the default | |||
implementation is to fail with an assertion; only a subset of | implementation is to fail with an assertion; only a subset of | |||
operators will be able to fulfill this request. | operators will be able to fulfill this request. | |||
@param pBuilder the builder to add the expression to. | @param pBuilder the builder to add the expression to. | |||
*/ | */ | |||
virtual void toMatcherBson( | virtual void toMatcherBson(BSONObjBuilder *pBuilder) const; | |||
BSONObjBuilder *pBuilder, unsigned depth) const; | ||||
/* | /* | |||
Utility class for parseObject() below. | Utility class for parseObject() below. | |||
Only one array can be unwound in a processing pipeline. If the | Only one array can be unwound in a processing pipeline. If the | |||
UNWIND_OK option is used, unwindOk() will return true, and a fiel d | UNWIND_OK option is used, unwindOk() will return true, and a fiel d | |||
can be declared as unwound using unwind(), after which unwindUsed () | can be declared as unwound using unwind(), after which unwindUsed () | |||
will return true. Only specify UNWIND_OK if it is OK to unwind a n | will return true. Only specify UNWIND_OK if it is OK to unwind a n | |||
array in the current context. | array in the current context. | |||
skipping to change at line 200 | skipping to change at line 217 | |||
EQ = 0, // return true for a == b, false otherwise | EQ = 0, // return true for a == b, false otherwise | |||
NE = 1, // return true for a != b, false otherwise | NE = 1, // return true for a != b, false otherwise | |||
GT = 2, // return true for a > b, false otherwise | GT = 2, // return true for a > b, false otherwise | |||
GTE = 3, // return true for a >= b, false otherwise | GTE = 3, // return true for a >= b, false otherwise | |||
LT = 4, // return true for a < b, false otherwise | LT = 4, // return true for a < b, false otherwise | |||
LTE = 5, // return true for a <= b, false otherwise | LTE = 5, // return true for a <= b, false otherwise | |||
CMP = 6, // return -1, 0, 1 for a < b, a == b, a > b | CMP = 6, // return -1, 0, 1 for a < b, a == b, a > b | |||
}; | }; | |||
static int signum(int i); | static int signum(int i); | |||
protected: | ||||
typedef vector<intrusive_ptr<Expression> > ExpressionVector; | ||||
}; | }; | |||
class ExpressionNary : | class ExpressionNary : | |||
public Expression, | public Expression { | |||
public boost::enable_shared_from_this<ExpressionNary> { | ||||
public: | public: | |||
// virtuals from Expression | // virtuals from Expression | |||
virtual intrusive_ptr<Expression> optimize(); | virtual intrusive_ptr<Expression> optimize(); | |||
virtual void addToBsonObj( | virtual void addToBsonObj( | |||
BSONObjBuilder *pBuilder, string fieldName, unsigned depth) con | BSONObjBuilder *pBuilder, string fieldName, | |||
st; | bool requireExpression) const; | |||
virtual void addToBsonArray( | virtual void addToBsonArray(BSONArrayBuilder *pBuilder) const; | |||
BSONArrayBuilder *pBuilder, unsigned depth) const; | virtual void addDependencies( | |||
const intrusive_ptr<DependencyTracker> &pTracker, | ||||
const DocumentSource *pSource) const; | ||||
/* | /* | |||
Add an operand to the n-ary expression. | Add an operand to the n-ary expression. | |||
@param pExpression the expression to add | @param pExpression the expression to add | |||
*/ | */ | |||
virtual void addOperand(const intrusive_ptr<Expression> &pExpressio n); | virtual void addOperand(const intrusive_ptr<Expression> &pExpressio n); | |||
/* | /* | |||
Return a factory function that will make Expression nodes of | Return a factory function that will make Expression nodes of | |||
skipping to change at line 250 | skipping to change at line 273 | |||
@returns the name of the operator; this string belongs to the cla ss | @returns the name of the operator; this string belongs to the cla ss | |||
implementation, and should not be deleted | implementation, and should not be deleted | |||
and should not | and should not | |||
*/ | */ | |||
virtual const char *getOpName() const = 0; | virtual const char *getOpName() const = 0; | |||
protected: | protected: | |||
ExpressionNary(); | ExpressionNary(); | |||
vector<intrusive_ptr<Expression> > vpOperand; | ExpressionVector vpOperand; | |||
/* | /* | |||
Add the expression to the builder. | Add the expression to the builder. | |||
If there is only one operand (a unary operator), then the operand | If there is only one operand (a unary operator), then the operand | |||
is added directly, without an array. For more than one operand, | is added directly, without an array. For more than one operand, | |||
a named array is created. In both cases, the result is an object . | a named array is created. In both cases, the result is an object . | |||
@param pBuilder the (blank) builder to add the expression to | @param pBuilder the (blank) builder to add the expression to | |||
@param pOpName the name of the operator | @param pOpName the name of the operator | |||
*/ | */ | |||
virtual void toBson(BSONObjBuilder *pBuilder, | virtual void toBson(BSONObjBuilder *pBuilder, | |||
const char *pOpName, unsigned depth) const; | const char *pOpName) const; | |||
/* | /* | |||
Checks the current size of vpOperand; if the size equal to or | Checks the current size of vpOperand; if the size equal to or | |||
greater than maxArgs, fires a user assertion indicating that this | greater than maxArgs, fires a user assertion indicating that this | |||
operator cannot have this many arguments. | operator cannot have this many arguments. | |||
The equal is there because this is intended to be used in | The equal is there because this is intended to be used in | |||
addOperand() to check for the limit *before* adding the requested | addOperand() to check for the limit *before* adding the requested | |||
argument. | argument. | |||
skipping to change at line 314 | skipping to change at line 337 | |||
/* | /* | |||
Create an expression that finds the sum of n operands. | Create an expression that finds the sum of n operands. | |||
@returns addition expression | @returns addition expression | |||
*/ | */ | |||
static intrusive_ptr<ExpressionNary> create(); | static intrusive_ptr<ExpressionNary> create(); | |||
protected: | protected: | |||
// virtuals from ExpressionNary | // virtuals from ExpressionNary | |||
virtual void toBson(BSONObjBuilder *pBuilder, | virtual void toBson(BSONObjBuilder *pBuilder, | |||
const char *pOpName, unsigned depth) const; | const char *pOpName) const; | |||
private: | private: | |||
ExpressionAdd(); | ExpressionAdd(); | |||
/* | /* | |||
If the operator can be optimized, we save the original here. | If the operator can be optimized, we save the original here. | |||
This is necessary because addition must follow its original opera nd | This is necessary because addition must follow its original opera nd | |||
ordering strictly if a string is detected, otherwise string | ordering strictly if a string is detected, otherwise string | |||
concatenation may appear to have re-ordered the operands. | concatenation may appear to have re-ordered the operands. | |||
skipping to change at line 339 | skipping to change at line 362 | |||
class ExpressionAnd : | class ExpressionAnd : | |||
public ExpressionNary { | public ExpressionNary { | |||
public: | public: | |||
// virtuals from Expression | // virtuals from Expression | |||
virtual ~ExpressionAnd(); | virtual ~ExpressionAnd(); | |||
virtual intrusive_ptr<Expression> optimize(); | virtual intrusive_ptr<Expression> optimize(); | |||
virtual intrusive_ptr<const Value> evaluate( | virtual intrusive_ptr<const Value> evaluate( | |||
const intrusive_ptr<Document> &pDocument) const; | const intrusive_ptr<Document> &pDocument) const; | |||
virtual const char *getOpName() const; | virtual const char *getOpName() const; | |||
virtual void toMatcherBson( | virtual void toMatcherBson(BSONObjBuilder *pBuilder) const; | |||
BSONObjBuilder *pBuilder, unsigned depth) const; | ||||
// virtuals from ExpressionNary | // virtuals from ExpressionNary | |||
virtual intrusive_ptr<ExpressionNary> (*getFactory() const)(); | virtual intrusive_ptr<ExpressionNary> (*getFactory() const)(); | |||
/* | /* | |||
Create an expression that finds the conjunction of n operands. | Create an expression that finds the conjunction of n operands. | |||
The conjunction uses short-circuit logic; the expressions are | The conjunction uses short-circuit logic; the expressions are | |||
evaluated in the order they were added to the conjunction, and | evaluated in the order they were added to the conjunction, and | |||
the evaluation stops and returns false on the first operand that | the evaluation stops and returns false on the first operand that | |||
evaluates to false. | evaluates to false. | |||
@returns conjunction expression | @returns conjunction expression | |||
*/ | */ | |||
static intrusive_ptr<ExpressionNary> create(); | static intrusive_ptr<ExpressionNary> create(); | |||
private: | private: | |||
ExpressionAnd(); | ExpressionAnd(); | |||
}; | }; | |||
class ExpressionCoerceToBool : | class ExpressionCoerceToBool : | |||
public Expression, | public Expression { | |||
public boost::enable_shared_from_this<ExpressionCoerceToBool> { | ||||
public: | public: | |||
// virtuals from ExpressionNary | // virtuals from ExpressionNary | |||
virtual ~ExpressionCoerceToBool(); | virtual ~ExpressionCoerceToBool(); | |||
virtual intrusive_ptr<Expression> optimize(); | virtual intrusive_ptr<Expression> optimize(); | |||
virtual void addDependencies( | ||||
const intrusive_ptr<DependencyTracker> &pTracker, | ||||
const DocumentSource *pSource) const; | ||||
virtual intrusive_ptr<const Value> evaluate( | virtual intrusive_ptr<const Value> evaluate( | |||
const intrusive_ptr<Document> &pDocument) const; | const intrusive_ptr<Document> &pDocument) const; | |||
virtual void addToBsonObj( | virtual void addToBsonObj( | |||
BSONObjBuilder *pBuilder, string fieldName, unsigned depth) con | BSONObjBuilder *pBuilder, string fieldName, | |||
st; | bool requireExpression) const; | |||
virtual void addToBsonArray( | virtual void addToBsonArray(BSONArrayBuilder *pBuilder) const; | |||
BSONArrayBuilder *pBuilder, unsigned depth) const; | ||||
static intrusive_ptr<ExpressionCoerceToBool> create( | static intrusive_ptr<ExpressionCoerceToBool> create( | |||
const intrusive_ptr<Expression> &pExpression); | const intrusive_ptr<Expression> &pExpression); | |||
private: | private: | |||
ExpressionCoerceToBool(const intrusive_ptr<Expression> &pExpression ); | ExpressionCoerceToBool(const intrusive_ptr<Expression> &pExpression ); | |||
intrusive_ptr<Expression> pExpression; | intrusive_ptr<Expression> pExpression; | |||
}; | }; | |||
skipping to change at line 434 | skipping to change at line 458 | |||
virtual const char *getOpName() const; | virtual const char *getOpName() const; | |||
virtual void addOperand(const intrusive_ptr<Expression> &pExpressio n); | virtual void addOperand(const intrusive_ptr<Expression> &pExpressio n); | |||
static intrusive_ptr<ExpressionNary> create(); | static intrusive_ptr<ExpressionNary> create(); | |||
private: | private: | |||
ExpressionCond(); | ExpressionCond(); | |||
}; | }; | |||
class ExpressionConstant : | class ExpressionConstant : | |||
public Expression, | public Expression { | |||
public boost::enable_shared_from_this<ExpressionConstant> { | ||||
public: | public: | |||
// virtuals from Expression | // virtuals from Expression | |||
virtual ~ExpressionConstant(); | virtual ~ExpressionConstant(); | |||
virtual intrusive_ptr<Expression> optimize(); | virtual intrusive_ptr<Expression> optimize(); | |||
virtual void addDependencies( | ||||
const intrusive_ptr<DependencyTracker> &pTracker, | ||||
const DocumentSource *pSource) const; | ||||
virtual intrusive_ptr<const Value> evaluate( | virtual intrusive_ptr<const Value> evaluate( | |||
const intrusive_ptr<Document> &pDocument) const; | const intrusive_ptr<Document> &pDocument) const; | |||
virtual const char *getOpName() const; | virtual const char *getOpName() const; | |||
virtual void addToBsonObj( | virtual void addToBsonObj( | |||
BSONObjBuilder *pBuilder, string fieldName, unsigned depth) con | BSONObjBuilder *pBuilder, string fieldName, | |||
st; | bool requireExpression) const; | |||
virtual void addToBsonArray( | virtual void addToBsonArray(BSONArrayBuilder *pBuilder) const; | |||
BSONArrayBuilder *pBuilder, unsigned depth) const; | ||||
static intrusive_ptr<ExpressionConstant> createFromBsonElement( | static intrusive_ptr<ExpressionConstant> createFromBsonElement( | |||
BSONElement *pBsonElement); | BSONElement *pBsonElement); | |||
static intrusive_ptr<ExpressionConstant> create( | static intrusive_ptr<ExpressionConstant> create( | |||
const intrusive_ptr<const Value> &pValue); | const intrusive_ptr<const Value> &pValue); | |||
/* | /* | |||
Get the constant value represented by this Expression. | Get the constant value represented by this Expression. | |||
@returns the value | @returns the value | |||
skipping to change at line 532 | skipping to change at line 558 | |||
virtual const char *getOpName() const; | virtual const char *getOpName() const; | |||
virtual void addOperand(const intrusive_ptr<Expression> &pExpressio n); | virtual void addOperand(const intrusive_ptr<Expression> &pExpressio n); | |||
static intrusive_ptr<ExpressionNary> create(); | static intrusive_ptr<ExpressionNary> create(); | |||
private: | private: | |||
ExpressionDivide(); | ExpressionDivide(); | |||
}; | }; | |||
class ExpressionFieldPath : | class ExpressionFieldPath : | |||
public Expression, | public Expression { | |||
public boost::enable_shared_from_this<ExpressionFieldPath> { | ||||
public: | public: | |||
// virtuals from Expression | // virtuals from Expression | |||
virtual ~ExpressionFieldPath(); | virtual ~ExpressionFieldPath(); | |||
virtual intrusive_ptr<Expression> optimize(); | virtual intrusive_ptr<Expression> optimize(); | |||
virtual void addDependencies( | ||||
const intrusive_ptr<DependencyTracker> &pTracker, | ||||
const DocumentSource *pSource) const; | ||||
virtual intrusive_ptr<const Value> evaluate( | virtual intrusive_ptr<const Value> evaluate( | |||
const intrusive_ptr<Document> &pDocument) const; | const intrusive_ptr<Document> &pDocument) const; | |||
virtual void addToBsonObj( | virtual void addToBsonObj( | |||
BSONObjBuilder *pBuilder, string fieldName, unsigned depth) con | BSONObjBuilder *pBuilder, string fieldName, | |||
st; | bool requireExpression) const; | |||
virtual void addToBsonArray( | virtual void addToBsonArray(BSONArrayBuilder *pBuilder) const; | |||
BSONArrayBuilder *pBuilder, unsigned depth) const; | ||||
/* | /* | |||
Create a field path expression. | Create a field path expression. | |||
Evaluation will extract the value associated with the given field | Evaluation will extract the value associated with the given field | |||
path from the source document. | path from the source document. | |||
@param fieldPath the field path string, without any leading docum ent | @param fieldPath the field path string, without any leading docum ent | |||
indicator | indicator | |||
@returns the newly created field path expression | @returns the newly created field path expression | |||
skipping to change at line 601 | skipping to change at line 629 | |||
@returns the field found; could be an array | @returns the field found; could be an array | |||
*/ | */ | |||
intrusive_ptr<const Value> evaluatePath( | intrusive_ptr<const Value> evaluatePath( | |||
size_t index, const size_t pathLength, | size_t index, const size_t pathLength, | |||
intrusive_ptr<Document> pDocument) const; | intrusive_ptr<Document> pDocument) const; | |||
FieldPath fieldPath; | FieldPath fieldPath; | |||
}; | }; | |||
class ExpressionFieldRange : | class ExpressionFieldRange : | |||
public Expression, | public Expression { | |||
public boost::enable_shared_from_this<ExpressionFieldRange> { | ||||
public: | public: | |||
// virtuals from expression | // virtuals from expression | |||
virtual ~ExpressionFieldRange(); | virtual ~ExpressionFieldRange(); | |||
virtual intrusive_ptr<Expression> optimize(); | virtual intrusive_ptr<Expression> optimize(); | |||
virtual void addDependencies( | ||||
const intrusive_ptr<DependencyTracker> &pTracker, | ||||
const DocumentSource *pSource) const; | ||||
virtual intrusive_ptr<const Value> evaluate( | virtual intrusive_ptr<const Value> evaluate( | |||
const intrusive_ptr<Document> &pDocument) const; | const intrusive_ptr<Document> &pDocument) const; | |||
virtual void addToBsonObj( | virtual void addToBsonObj( | |||
BSONObjBuilder *pBuilder, string fieldName, unsigned depth) con | BSONObjBuilder *pBuilder, string fieldName, | |||
st; | bool requireExpression) const; | |||
virtual void addToBsonArray( | virtual void addToBsonArray(BSONArrayBuilder *pBuilder) const; | |||
BSONArrayBuilder *pBuilder, unsigned depth) const; | virtual void toMatcherBson(BSONObjBuilder *pBuilder) const; | |||
virtual void toMatcherBson( | ||||
BSONObjBuilder *pBuilder, unsigned depth) const; | ||||
/* | /* | |||
Create a field range expression. | Create a field range expression. | |||
Field ranges are meant to match up with classic Matcher semantics , | Field ranges are meant to match up with classic Matcher semantics , | |||
and therefore are conjunctions. For example, these appear in | and therefore are conjunctions. For example, these appear in | |||
mongo shell predicates in one of these forms: | mongo shell predicates in one of these forms: | |||
{ a : C } -> (a == C) // degenerate "point" range | { a : C } -> (a == C) // degenerate "point" range | |||
{ a : { $lt : C } } -> (a < C) // open range | { a : { $lt : C } } -> (a < C) // open range | |||
{ a : { $gt : C1, $lte : C2 } } -> ((a > C1) && (a <= C2)) // clo sed | { a : { $gt : C1, $lte : C2 } } -> ((a > C1) && (a <= C2)) // clo sed | |||
skipping to change at line 690 | skipping to change at line 719 | |||
/* | /* | |||
Add to a generic Builder. | Add to a generic Builder. | |||
The methods to append items to an object and an array differ by | The methods to append items to an object and an array differ by | |||
their inclusion of a field name. For more complicated objects, | their inclusion of a field name. For more complicated objects, | |||
it makes sense to abstract that out and use a generic builder tha t | it makes sense to abstract that out and use a generic builder tha t | |||
always looks the same, and then implement addToBsonObj() and | always looks the same, and then implement addToBsonObj() and | |||
addToBsonArray() by using the common method. | addToBsonArray() by using the common method. | |||
*/ | */ | |||
void addToBson(Builder *pBuilder, unsigned depth) const; | void addToBson(Builder *pBuilder) const; | |||
}; | }; | |||
class ExpressionHour : | class ExpressionHour : | |||
public ExpressionNary { | public ExpressionNary { | |||
public: | public: | |||
// virtuals from ExpressionNary | // virtuals from ExpressionNary | |||
virtual ~ExpressionHour(); | virtual ~ExpressionHour(); | |||
virtual intrusive_ptr<const Value> evaluate( | virtual intrusive_ptr<const Value> evaluate( | |||
const intrusive_ptr<Document> &pDocument) const; | const intrusive_ptr<Document> &pDocument) const; | |||
virtual const char *getOpName() const; | virtual const char *getOpName() const; | |||
skipping to change at line 725 | skipping to change at line 754 | |||
const intrusive_ptr<Document> &pDocument) const; | const intrusive_ptr<Document> &pDocument) const; | |||
virtual const char *getOpName() const; | virtual const char *getOpName() const; | |||
virtual void addOperand(const intrusive_ptr<Expression> &pExpressio n); | virtual void addOperand(const intrusive_ptr<Expression> &pExpressio n); | |||
static intrusive_ptr<ExpressionNary> create(); | static intrusive_ptr<ExpressionNary> create(); | |||
private: | private: | |||
ExpressionIfNull(); | ExpressionIfNull(); | |||
}; | }; | |||
class ExpressionIsoDate : | ||||
public ExpressionNary { | ||||
public: | ||||
// virtuals from ExpressionNary | ||||
virtual ~ExpressionIsoDate(); | ||||
virtual intrusive_ptr<const Value> evaluate( | ||||
const intrusive_ptr<Document> &pDocument) const; | ||||
virtual const char *getOpName() const; | ||||
virtual void addOperand(const intrusive_ptr<Expression> &pExpressio | ||||
n); | ||||
static intrusive_ptr<ExpressionNary> create(); | ||||
private: | ||||
ExpressionIsoDate(); | ||||
static const char argYear[]; | ||||
static const char argMonth[]; | ||||
static const char argDayOfMonth[]; | ||||
static const char argHour[]; | ||||
static const char argMinute[]; | ||||
static const char argSecond[]; | ||||
static const unsigned flagYear; | ||||
static const unsigned flagMonth; | ||||
static const unsigned flagDayOfMonth; | ||||
static const unsigned flagHour; | ||||
static const unsigned flagMinute; | ||||
static const unsigned flagSecond; | ||||
unsigned flag; | ||||
/** | ||||
Get a named long argument out of the given document. | ||||
@param pArgs the evaluated document with the named arguments in | ||||
it | ||||
@param pName the name of the argument | ||||
@param defaultValue the value to return if the argument isn't fo | ||||
und | ||||
@returns the value if found, otherwise zero | ||||
@throws uassert for non-whole numbers or non-numbers | ||||
*/ | ||||
int getIntArg( | ||||
const intrusive_ptr<Document> &pArgs, | ||||
const char *pName, int defaultValue) const; | ||||
/** | ||||
Check that the named argument fits in an integer. | ||||
@params pName the name of the argument | ||||
@params value the long value of the argument | ||||
@returns the integer value | ||||
@throws uassert if the value is out of range | ||||
*/ | ||||
int checkIntRange(const char *pName, long long value) const; | ||||
}; | ||||
class ExpressionMinute : | class ExpressionMinute : | |||
public ExpressionNary { | public ExpressionNary { | |||
public: | public: | |||
// virtuals from ExpressionNary | // virtuals from ExpressionNary | |||
virtual ~ExpressionMinute(); | virtual ~ExpressionMinute(); | |||
virtual intrusive_ptr<const Value> evaluate( | virtual intrusive_ptr<const Value> evaluate( | |||
const intrusive_ptr<Document> &pDocument) const; | const intrusive_ptr<Document> &pDocument) const; | |||
virtual const char *getOpName() const; | virtual const char *getOpName() const; | |||
virtual void addOperand(const intrusive_ptr<Expression> &pExpressio n); | virtual void addOperand(const intrusive_ptr<Expression> &pExpressio n); | |||
skipping to change at line 830 | skipping to change at line 913 | |||
virtual const char *getOpName() const; | virtual const char *getOpName() const; | |||
virtual void addOperand(const intrusive_ptr<Expression> &pExpressio n); | virtual void addOperand(const intrusive_ptr<Expression> &pExpressio n); | |||
static intrusive_ptr<ExpressionNary> create(); | static intrusive_ptr<ExpressionNary> create(); | |||
private: | private: | |||
ExpressionNot(); | ExpressionNot(); | |||
}; | }; | |||
class ExpressionObject : | class ExpressionObject : | |||
public Expression, | public Expression { | |||
public boost::enable_shared_from_this<ExpressionObject> { | ||||
public: | public: | |||
// virtuals from Expression | // virtuals from Expression | |||
virtual ~ExpressionObject(); | virtual ~ExpressionObject(); | |||
virtual intrusive_ptr<Expression> optimize(); | virtual intrusive_ptr<Expression> optimize(); | |||
virtual void addDependencies( | ||||
const intrusive_ptr<DependencyTracker> &pTracker, | ||||
const DocumentSource *pSource) const; | ||||
virtual intrusive_ptr<const Value> evaluate( | virtual intrusive_ptr<const Value> evaluate( | |||
const intrusive_ptr<Document> &pDocument) const; | const intrusive_ptr<Document> &pDocument) const; | |||
virtual void addToBsonObj( | virtual void addToBsonObj( | |||
BSONObjBuilder *pBuilder, string fieldName, unsigned depth) con | BSONObjBuilder *pBuilder, string fieldName, | |||
st; | bool requireExpression) const; | |||
virtual void addToBsonArray( | virtual void addToBsonArray(BSONArrayBuilder *pBuilder) const; | |||
BSONArrayBuilder *pBuilder, unsigned depth) const; | ||||
/* | /* | |||
evaluate(), but return a Document instead of a Value-wrapped | evaluate(), but return a Document instead of a Value-wrapped | |||
Document. | Document. | |||
@param pDocument the input Document | @param pDocument the input Document | |||
@returns the result document | @returns the result document | |||
*/ | */ | |||
intrusive_ptr<Document> evaluateDocument( | intrusive_ptr<Document> evaluateDocument( | |||
const intrusive_ptr<Document> &pDocument) const; | const intrusive_ptr<Document> &pDocument) const; | |||
/* | /* | |||
evaluate(), but add the evaluated fields to a given document | evaluate(), but add the evaluated fields to a given document | |||
instead of creating a new one. | instead of creating a new one. | |||
@param pResult the Document to add the evaluated expressions to | @param pResult the Document to add the evaluated expressions to | |||
@param pDocument the input Document | @param pDocument the input Document | |||
@param excludeId for exclusions, exclude the _id, if present | ||||
*/ | */ | |||
void addToDocument(const intrusive_ptr<Document> &pResult, | void addToDocument(const intrusive_ptr<Document> &pResult, | |||
const intrusive_ptr<Document> &pDocument) const; | const intrusive_ptr<Document> &pDocument, | |||
bool excludeId = false) const; | ||||
/* | /* | |||
Estimate the number of fields that will result from evaluating | Estimate the number of fields that will result from evaluating | |||
this over pDocument. Does not include _id. This is an estimate | this over pDocument. Does not include _id. This is an estimate | |||
(really an upper bound) because we can't account for undefined | (really an upper bound) because we can't account for undefined | |||
fields without actually doing the evaluation. But this is still | fields without actually doing the evaluation. But this is still | |||
useful as an argument to Document::create(), if you plan to use | useful as an argument to Document::create(), if you plan to use | |||
addToDocument(). | addToDocument(). | |||
@param pDocument the input document | @param pDocument the input document | |||
skipping to change at line 914 | skipping to change at line 1001 | |||
Add a field path to the set of those to be excluded. | Add a field path to the set of those to be excluded. | |||
Note that excluding a nested field implies including everything o n | Note that excluding a nested field implies including everything o n | |||
the path leading down to it (because you're stating you want to s ee | the path leading down to it (because you're stating you want to s ee | |||
all the other fields that aren't being excluded). | all the other fields that aren't being excluded). | |||
@param fieldName the name of the field to be excluded | @param fieldName the name of the field to be excluded | |||
*/ | */ | |||
void excludePath(const string &fieldPath); | void excludePath(const string &fieldPath); | |||
/** | ||||
Get an iterator that can be used to iterate over all the result | ||||
field names in this ExpressionObject. | ||||
@returns the (intrusive_ptr'ed) iterator | ||||
*/ | ||||
Iterator<string> *getFieldIterator() const; | ||||
/* | /* | |||
Return the expression for a field. | Return the expression for a field. | |||
@param fieldName the field name for the expression to return | @param fieldName the field name for the expression to return | |||
@returns the expression used to compute the field, if it is prese nt, | @returns the expression used to compute the field, if it is prese nt, | |||
otherwise NULL. | otherwise NULL. | |||
*/ | */ | |||
intrusive_ptr<Expression> getField(const string &fieldName) const; | intrusive_ptr<Expression> getField(const string &fieldName) const; | |||
/* | /* | |||
skipping to change at line 943 | skipping to change at line 1038 | |||
@returns how many fields have been excluded. | @returns how many fields have been excluded. | |||
*/ | */ | |||
size_t getExclusionCount() const; | size_t getExclusionCount() const; | |||
/* | /* | |||
Specialized BSON conversion that allows for writing out a | Specialized BSON conversion that allows for writing out a | |||
$project specification. This creates a standalone object, which must | $project specification. This creates a standalone object, which must | |||
be added to a containing object with a name | be added to a containing object with a name | |||
@param pBuilder where to write the object to | @param pBuilder where to write the object to | |||
@param requireExpression see Expression::addToBsonObj | ||||
*/ | ||||
void documentToBson(BSONObjBuilder *pBuilder, | ||||
bool requireExpression) const; | ||||
/* | ||||
Visitor abstraction used by emitPaths(). Each path is recorded b | ||||
y | ||||
calling path(). | ||||
*/ | ||||
class PathSink { | ||||
public: | ||||
virtual ~PathSink() {}; | ||||
/** | ||||
Record a path. | ||||
@param path the dotted path string | ||||
@param include if true, the path is included; if false, the | ||||
path | ||||
is excluded | ||||
*/ | ||||
virtual void path(const string &path, bool include) = 0; | ||||
}; | ||||
/** | ||||
Emit the field paths that have been included or excluded. "Inclu | ||||
ded" | ||||
includes paths that are referenced in expressions for computed | ||||
fields. | ||||
@param pSink where to write the paths to | ||||
@param pvPath pointer to a vector of strings describing the path | ||||
on | ||||
descent; the top-level call should pass an empty vector | ||||
*/ | */ | |||
void documentToBson(BSONObjBuilder *pBuilder, unsigned depth) const ; | void emitPaths(PathSink *pPathSink) const; | |||
private: | private: | |||
ExpressionObject(); | ExpressionObject(); | |||
void includePath( | void includePath( | |||
const FieldPath *pPath, size_t pathi, size_t pathn, | const FieldPath *pPath, size_t pathi, size_t pathn, | |||
bool excludeLast); | bool excludeLast); | |||
bool excludePaths; | bool excludePaths; | |||
set<string> path; | set<string> path; | |||
/* these two vectors are maintained in parallel */ | /* these two vectors are maintained in parallel */ | |||
vector<string> vFieldName; | vector<string> vFieldName; | |||
vector<intrusive_ptr<Expression> > vpExpression; | vector<intrusive_ptr<Expression> > vpExpression; | |||
/* | /* | |||
Utility function used by documentToBson(). Emits inclusion | Utility function used by documentToBson(). Emits inclusion | |||
and exclusion paths by recursively walking down the nested | and exclusion paths by recursively walking down the nested | |||
ExpressionObject trees these have created. | ExpressionObject trees these have created. | |||
@param pBuilder the builder to write boolean valued path "fields" to | @param pSink where to write the paths to | |||
@param pvPath pointer to a vector of strings describing the path on | @param pvPath pointer to a vector of strings describing the path on | |||
descent; the top-level call should pass an empty vector | descent; the top-level call should pass an empty vector | |||
*/ | */ | |||
void emitPaths(BSONObjBuilder *pBuilder, vector<string> *pvPath) co | void emitPaths(PathSink *pPathSink, vector<string> *pvPath) const; | |||
nst; | ||||
/* | ||||
Utility object for collecting emitPaths() results in a BSON | ||||
object. | ||||
*/ | ||||
class BuilderPathSink : | ||||
public PathSink { | ||||
public: | ||||
// virtuals from PathSink | ||||
virtual void path(const string &path, bool include); | ||||
/* | ||||
Create a PathSink that writes paths to a BSONObjBuilder, | ||||
to create an object in the form of { path:is_included,...} | ||||
This object uses a builder pointer that won't guarantee the | ||||
lifetime of the builder, so make sure it outlasts the use of | ||||
this for an emitPaths() call. | ||||
@param pBuilder to the builder to write paths to | ||||
*/ | ||||
BuilderPathSink(BSONObjBuilder *pBuilder); | ||||
private: | ||||
BSONObjBuilder *pBuilder; | ||||
}; | ||||
/* utility class used by emitPaths() */ | /* utility class used by emitPaths() */ | |||
class PathPusher : | class PathPusher : | |||
boost::noncopyable { | boost::noncopyable { | |||
public: | public: | |||
PathPusher(vector<string> *pvPath, const string &s); | PathPusher(vector<string> *pvPath, const string &s); | |||
~PathPusher(); | ~PathPusher(); | |||
private: | private: | |||
vector<string> *pvPath; | vector<string> *pvPath; | |||
skipping to change at line 992 | skipping to change at line 1144 | |||
class ExpressionOr : | class ExpressionOr : | |||
public ExpressionNary { | public ExpressionNary { | |||
public: | public: | |||
// virtuals from Expression | // virtuals from Expression | |||
virtual ~ExpressionOr(); | virtual ~ExpressionOr(); | |||
virtual intrusive_ptr<Expression> optimize(); | virtual intrusive_ptr<Expression> optimize(); | |||
virtual intrusive_ptr<const Value> evaluate( | virtual intrusive_ptr<const Value> evaluate( | |||
const intrusive_ptr<Document> &pDocument) const; | const intrusive_ptr<Document> &pDocument) const; | |||
virtual const char *getOpName() const; | virtual const char *getOpName() const; | |||
virtual void toMatcherBson( | virtual void toMatcherBson(BSONObjBuilder *pBuilder) const; | |||
BSONObjBuilder *pBuilder, unsigned depth) const; | ||||
// virtuals from ExpressionNary | // virtuals from ExpressionNary | |||
virtual intrusive_ptr<ExpressionNary> (*getFactory() const)(); | virtual intrusive_ptr<ExpressionNary> (*getFactory() const)(); | |||
/* | /* | |||
Create an expression that finds the conjunction of n operands. | Create an expression that finds the conjunction of n operands. | |||
The conjunction uses short-circuit logic; the expressions are | The conjunction uses short-circuit logic; the expressions are | |||
evaluated in the order they were added to the conjunction, and | evaluated in the order they were added to the conjunction, and | |||
the evaluation stops and returns false on the first operand that | the evaluation stops and returns false on the first operand that | |||
evaluates to false. | evaluates to false. | |||
skipping to change at line 1179 | skipping to change at line 1330 | |||
inline void ExpressionFieldPath::writeFieldPath( | inline void ExpressionFieldPath::writeFieldPath( | |||
ostream &outStream, bool fieldPrefix) const { | ostream &outStream, bool fieldPrefix) const { | |||
return fieldPath.writePath(outStream, fieldPrefix); | return fieldPath.writePath(outStream, fieldPrefix); | |||
} | } | |||
inline size_t ExpressionObject::getFieldCount() const { | inline size_t ExpressionObject::getFieldCount() const { | |||
return vFieldName.size(); | return vFieldName.size(); | |||
} | } | |||
inline ExpressionObject::BuilderPathSink::BuilderPathSink( | ||||
BSONObjBuilder *pB): | ||||
pBuilder(pB) { | ||||
} | ||||
inline ExpressionObject::PathPusher::PathPusher( | inline ExpressionObject::PathPusher::PathPusher( | |||
vector<string> *pTheVPath, const string &s): | vector<string> *pTheVPath, const string &s): | |||
pvPath(pTheVPath) { | pvPath(pTheVPath) { | |||
pvPath->push_back(s); | pvPath->push_back(s); | |||
} | } | |||
inline ExpressionObject::PathPusher::~PathPusher() { | inline ExpressionObject::PathPusher::~PathPusher() { | |||
pvPath->pop_back(); | pvPath->pop_back(); | |||
} | } | |||
End of changes. 42 change blocks. | ||||
56 lines changed or deleted | 213 lines changed or added | |||
expression_context.h | expression_context.h | |||
---|---|---|---|---|
skipping to change at line 25 | skipping to change at line 25 | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "pch.h" | #include "pch.h" | |||
#include "util/intrusive_counter.h" | #include "util/intrusive_counter.h" | |||
namespace mongo { | namespace mongo { | |||
class InterruptStatus; | ||||
class ExpressionContext : | class ExpressionContext : | |||
public IntrusiveCounterUnsigned { | public IntrusiveCounterUnsigned { | |||
public: | public: | |||
virtual ~ExpressionContext(); | virtual ~ExpressionContext(); | |||
void setInShard(bool b); | void setInShard(bool b); | |||
void setInRouter(bool b); | void setInRouter(bool b); | |||
bool getInShard() const; | bool getInShard() const; | |||
bool getInRouter() const; | bool getInRouter() const; | |||
static ExpressionContext *create(); | /** | |||
Used by a pipeline to check for interrupts so that killOp() work | ||||
s. | ||||
@throws if the operation has been interrupted | ||||
*/ | ||||
void checkForInterrupt(); | ||||
static ExpressionContext *create(InterruptStatus *pStatus); | ||||
private: | private: | |||
ExpressionContext(); | ExpressionContext(InterruptStatus *pStatus); | |||
bool inShard; | bool inShard; | |||
bool inRouter; | bool inRouter; | |||
unsigned intCheckCounter; // interrupt check counter | ||||
InterruptStatus *const pStatus; | ||||
}; | }; | |||
} | } | |||
/* ======================= INLINED IMPLEMENTATIONS ======================== == */ | /* ======================= INLINED IMPLEMENTATIONS ======================== == */ | |||
namespace mongo { | namespace mongo { | |||
inline void ExpressionContext::setInShard(bool b) { | inline void ExpressionContext::setInShard(bool b) { | |||
inShard = b; | inShard = b; | |||
} | } | |||
End of changes. 4 change blocks. | ||||
2 lines changed or deleted | 14 lines changed or added | |||
extsort.h | extsort.h | |||
---|---|---|---|---|
skipping to change at line 21 | skipping to change at line 21 | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Affero General Public License for more details. | * GNU Affero General Public License for more details. | |||
* | * | |||
* You should have received a copy of the GNU Affero General Public Licen se | * You should have received a copy of the GNU Affero General Public Licen se | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../pch.h" | #include "pch.h" | |||
#include "jsobj.h" | ||||
#include "namespace-inl.h" | #include "mongo/db/index.h" | |||
#include "curop-inl.h" | #include "mongo/db/jsobj.h" | |||
#include "../util/array.h" | #include "mongo/db/namespace-inl.h" | |||
#include "mongo/db/curop-inl.h" | ||||
#include "mongo/util/array.h" | ||||
#include "mongo/util/mmap.h" | ||||
namespace mongo { | namespace mongo { | |||
/** | /** | |||
for external (disk) sorting by BSONObj and attaching a value | for external (disk) sorting by BSONObj and attaching a value | |||
*/ | */ | |||
class BSONObjExternalSorter : boost::noncopyable { | class BSONObjExternalSorter : boost::noncopyable { | |||
public: | public: | |||
BSONObjExternalSorter( IndexInterface &i, const BSONObj & order = B SONObj() , long maxFileSize = 1024 * 1024 * 100 ); | BSONObjExternalSorter( IndexInterface &i, const BSONObj & order = B SONObj() , long maxFileSize = 1024 * 1024 * 100 ); | |||
~BSONObjExternalSorter(); | ~BSONObjExternalSorter(); | |||
typedef pair<BSONObj,DiskLoc> Data; | typedef pair<BSONObj,DiskLoc> Data; | |||
private: | private: | |||
static HLMutex _extSortMutex; | ||||
IndexInterface& _idxi; | IndexInterface& _idxi; | |||
static int _compare(IndexInterface& i, const Data& l, const Data& r | static int _compare(IndexInterface& i, const Data& l, const Data& r | |||
, const Ordering& order) { | , const Ordering& order); | |||
RARELY killCurrentOp.checkForInterrupt(); | ||||
_compares++; | ||||
int x = i.keyCompare(l.first, r.first, order); | ||||
if ( x ) | ||||
return x; | ||||
return l.second.compare( r.second ); | ||||
} | ||||
class MyCmp { | class MyCmp { | |||
public: | public: | |||
MyCmp( IndexInterface& i, BSONObj order = BSONObj() ) : _i(i), _order( Ordering::make(order) ) {} | MyCmp( IndexInterface& i, BSONObj order = BSONObj() ) : _i(i), _order( Ordering::make(order) ) {} | |||
bool operator()( const Data &l, const Data &r ) const { | bool operator()( const Data &l, const Data &r ) const { | |||
return _compare(_i, l, r, _order) < 0; | return _compare(_i, l, r, _order) < 0; | |||
}; | }; | |||
private: | private: | |||
IndexInterface& _i; | IndexInterface& _i; | |||
const Ordering _order; | const Ordering _order; | |||
}; | }; | |||
static IndexInterface *extSortIdxInterface; | static IndexInterface *extSortIdxInterface; | |||
static Ordering extSortOrder; | static Ordering extSortOrder; | |||
static int extSortComp( const void *lv, const void *rv ) { | static int extSortComp( const void *lv, const void *rv ); | |||
DEV RARELY { | ||||
d.dbMutex.assertWriteLocked(); // must be as we use a globa | ||||
l var | ||||
} | ||||
Data * l = (Data*)lv; | ||||
Data * r = (Data*)rv; | ||||
return _compare(*extSortIdxInterface, *l, *r, extSortOrder); | ||||
}; | ||||
class FileIterator : boost::noncopyable { | class FileIterator : boost::noncopyable { | |||
public: | public: | |||
FileIterator( string file ); | FileIterator( string file ); | |||
~FileIterator(); | ~FileIterator(); | |||
bool more(); | bool more(); | |||
Data next(); | Data next(); | |||
private: | private: | |||
MemoryMappedFile _file; | MemoryMappedFile _file; | |||
char * _buf; | char * _buf; | |||
End of changes. 4 change blocks. | ||||
23 lines changed or deleted | 12 lines changed or added | |||
field_path.h | field_path.h | |||
---|---|---|---|---|
skipping to change at line 27 | skipping to change at line 27 | |||
#pragma once | #pragma once | |||
#include "pch.h" | #include "pch.h" | |||
namespace mongo { | namespace mongo { | |||
class FieldPath { | class FieldPath { | |||
public: | public: | |||
virtual ~FieldPath(); | virtual ~FieldPath(); | |||
/** | ||||
Constructor. | ||||
@param fieldPath the dotted field path string | ||||
*/ | ||||
FieldPath(const string &fieldPath); | FieldPath(const string &fieldPath); | |||
/** | ||||
Constructor. | ||||
*/ | ||||
FieldPath(); | FieldPath(); | |||
/* | /** | |||
Get the number of path elements in the field path. | Get the number of path elements in the field path. | |||
@returns the number of path elements | @returns the number of path elements | |||
*/ | */ | |||
size_t getPathLength() const; | size_t getPathLength() const; | |||
/* | /** | |||
Get a particular path element from the path. | Get a particular path element from the path. | |||
@param i the index of the path element | @param i the index of the path element | |||
@returns the path element | @returns the path element | |||
*/ | */ | |||
string getFieldName(size_t i) const; | string getFieldName(size_t i) const; | |||
/* | /** | |||
Get the full path. | Get the full path. | |||
@param fieldPrefix whether or not to include the field prefix | @param fieldPrefix whether or not to include the field prefix | |||
@returns the complete field path | @returns the complete field path | |||
*/ | */ | |||
string getPath(bool fieldPrefix) const; | string getPath(bool fieldPrefix) const; | |||
/* | /** | |||
Write the full path. | Write the full path. | |||
@param outStream where to write the path to | @param outStream where to write the path to | |||
@param fieldPrefix whether or not to include the field prefix | @param fieldPrefix whether or not to include the field prefix | |||
*/ | */ | |||
void writePath(ostream &outStream, bool fieldPrefix) const; | void writePath(ostream &outStream, bool fieldPrefix) const; | |||
/** | ||||
Assignment operator. | ||||
@param rRHS right hand side of the assignment | ||||
*/ | ||||
FieldPath &operator=(const FieldPath &rRHS); | FieldPath &operator=(const FieldPath &rRHS); | |||
/** | ||||
Get the prefix string. | ||||
@returns the prefix string | ||||
*/ | ||||
static const char *getPrefix(); | ||||
static const char prefix[]; | ||||
private: | private: | |||
vector<string> vFieldName; | vector<string> vFieldName; | |||
}; | }; | |||
} | } | |||
/* ======================= INLINED IMPLEMENTATIONS ======================== == */ | /* ======================= INLINED IMPLEMENTATIONS ======================== == */ | |||
namespace mongo { | namespace mongo { | |||
inline size_t FieldPath::getPathLength() const { | inline size_t FieldPath::getPathLength() const { | |||
return vFieldName.size(); | return vFieldName.size(); | |||
} | } | |||
inline string FieldPath::getFieldName(size_t i) const { | inline string FieldPath::getFieldName(size_t i) const { | |||
return vFieldName[i]; | return vFieldName[i]; | |||
} | } | |||
inline const char *FieldPath::getPrefix() { | ||||
return prefix; | ||||
} | ||||
} | } | |||
End of changes. 9 change blocks. | ||||
4 lines changed or deleted | 31 lines changed or added | |||
file.h | file.h | |||
---|---|---|---|---|
skipping to change at line 48 | skipping to change at line 48 | |||
/* NOTE: not thread-safe. (at least the windows implementation isn't. * / | /* NOTE: not thread-safe. (at least the windows implementation isn't. * / | |||
class FileInterface { | class FileInterface { | |||
public: | public: | |||
void open(const char *fn) {} | void open(const char *fn) {} | |||
void write(fileofs o, const char *data, unsigned len) {} | void write(fileofs o, const char *data, unsigned len) {} | |||
void read(fileofs o, char *data, unsigned len) {} | void read(fileofs o, char *data, unsigned len) {} | |||
bool bad() {return false;} | bool bad() {return false;} | |||
bool is_open() {return false;} | bool is_open() {return false;} | |||
fileofs len() { return 0; } | fileofs len() { return 0; } | |||
void fsync() { assert(false); } | void fsync() { verify(false); } | |||
// shrink file to size bytes. No-op if file already smaller. | // shrink file to size bytes. No-op if file already smaller. | |||
void truncate(fileofs size); | void truncate(fileofs size); | |||
/** @return -1 if error or unavailable */ | /** @return -1 if error or unavailable */ | |||
static boost::intmax_t freeSpace(const string &path) { assert(false ); return -1; } | static boost::intmax_t freeSpace(const string &path) { verify(false ); return -1; } | |||
}; | }; | |||
#if defined(_WIN32) | #if defined(_WIN32) | |||
#include <io.h> | #include <io.h> | |||
class File : public FileInterface { | class File : public FileInterface { | |||
HANDLE fd; | HANDLE fd; | |||
bool _bad; | bool _bad; | |||
string _name; | string _name; | |||
void err(BOOL b=false) { /* false = error happened */ | void err(BOOL b=false) { /* false = error happened */ | |||
skipping to change at line 213 | skipping to change at line 213 | |||
fileofs len() { | fileofs len() { | |||
off_t o = lseek(fd, 0, SEEK_END); | off_t o = lseek(fd, 0, SEEK_END); | |||
if( o != (off_t) -1 ) | if( o != (off_t) -1 ) | |||
return o; | return o; | |||
err(false); | err(false); | |||
return 0; | return 0; | |||
} | } | |||
void fsync() { ::fsync(fd); } | void fsync() { ::fsync(fd); } | |||
static boost::intmax_t freeSpace ( const string &path ) { | static boost::intmax_t freeSpace ( const string &path ) { | |||
struct statvfs info; | struct statvfs info; | |||
assert( !statvfs( path.c_str() , &info ) ); | verify( !statvfs( path.c_str() , &info ) ); | |||
return boost::intmax_t( info.f_bavail ) * info.f_frsize; | return boost::intmax_t( info.f_bavail ) * info.f_frsize; | |||
} | } | |||
void truncate(fileofs size) { | void truncate(fileofs size) { | |||
if (len() <= size) | if (len() <= size) | |||
return; | return; | |||
err(ftruncate(fd, size) == 0); | err(ftruncate(fd, size) == 0); | |||
} | } | |||
}; | }; | |||
End of changes. 3 change blocks. | ||||
3 lines changed or deleted | 3 lines changed or added | |||
file_allocator.h | file_allocator.h | |||
---|---|---|---|---|
skipping to change at line 18 | skipping to change at line 18 | |||
* | * | |||
* http://www.apache.org/licenses/LICENSE-2.0 | * http://www.apache.org/licenses/LICENSE-2.0 | |||
* | * | |||
* Unless required by applicable law or agreed to in writing, software | * Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | * distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. | |||
* See the License for the specific language governing permissions and | * See the License for the specific language governing permissions and | |||
* limitations under the License. | * limitations under the License. | |||
*/ | */ | |||
#include "../pch.h" | #include "pch.h" | |||
#include <list> | ||||
#include <boost/thread/condition.hpp> | ||||
namespace mongo { | namespace mongo { | |||
/* | /* | |||
* 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. | |||
* singleton | * singleton | |||
*/ | */ | |||
class FileAllocator : boost::noncopyable { | class FileAllocator : boost::noncopyable { | |||
/* | /* | |||
skipping to change at line 77 | skipping to change at line 81 | |||
// caller must hold pendingMutex_ lock. | // caller must hold pendingMutex_ lock. | |||
bool inProgress( const string &name ) const; | bool inProgress( const string &name ) const; | |||
/** called from the worked thread */ | /** called from the worked thread */ | |||
static void run( FileAllocator * fa ); | static void run( FileAllocator * fa ); | |||
mutable mongo::mutex _pendingMutex; | mutable mongo::mutex _pendingMutex; | |||
mutable boost::condition _pendingUpdated; | mutable boost::condition _pendingUpdated; | |||
list< string > _pending; | std::list< string > _pending; | |||
mutable map< string, long > _pendingSize; | mutable map< string, long > _pendingSize; | |||
bool _failed; | bool _failed; | |||
#endif | #endif | |||
static FileAllocator* _instance; | static FileAllocator* _instance; | |||
}; | }; | |||
} // namespace mongo | } // namespace mongo | |||
End of changes. 2 change blocks. | ||||
2 lines changed or deleted | 6 lines changed or added | |||
framework.h | framework.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/>. | |||
*/ | */ | |||
/* | /* | |||
simple portable regression system | simple portable regression system | |||
*/ | */ | |||
#include "../pch.h" | #include <string> | |||
#define ASSERT_THROWS(a,b) \ | ||||
try { \ | ||||
a; \ | ||||
mongo::regression::assert_fail( #a , __FILE__ , __LINE__ ); \ | ||||
} catch ( b& ){ \ | ||||
mongo::regression::assert_pass(); \ | ||||
} | ||||
#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 FAIL(x) mongo::regression::fail( #x , __FILE__ , __LINE__ ) | ||||
#include "../db/instance.h" | ||||
namespace mongo { | namespace mongo { | |||
namespace dbtests { | ||||
namespace regression { | int runDbTests( int argc, char ** argv, string default_dbpath ); | |||
} // dbtests | ||||
class Result; | } // namespace mongo | |||
class TestCase { | ||||
public: | ||||
virtual ~TestCase() {} | ||||
virtual void run() = 0; | ||||
virtual string getName() = 0; | ||||
}; | ||||
template< class T > | ||||
class TestHolderBase : public TestCase { | ||||
public: | ||||
TestHolderBase() {} | ||||
virtual ~TestHolderBase() {} | ||||
virtual void run() { | ||||
auto_ptr<T> t; | ||||
t.reset( create() ); | ||||
t->run(); | ||||
} | ||||
virtual T * create() = 0; | ||||
virtual string getName() { | ||||
return demangleName( typeid(T) ); | ||||
} | ||||
}; | ||||
template< class T > | ||||
class TestHolder0 : public TestHolderBase<T> { | ||||
public: | ||||
virtual T * create() { | ||||
return new T(); | ||||
} | ||||
}; | ||||
template< class T , typename A > | ||||
class TestHolder1 : public TestHolderBase<T> { | ||||
public: | ||||
TestHolder1( const A& a ) : _a(a) {} | ||||
virtual T * create() { | ||||
return new T( _a ); | ||||
} | ||||
const A& _a; | ||||
}; | ||||
class Suite { | ||||
public: | ||||
Suite( string name ) : _name( name ) { | ||||
registerSuite( name , this ); | ||||
_ran = 0; | ||||
} | ||||
virtual ~Suite() { | ||||
if ( _ran ) { | ||||
DBDirectClient c; | ||||
c.dropDatabase( "unittests" ); | ||||
} | ||||
} | ||||
template<class T> | ||||
void add() { | ||||
_tests.push_back( new TestHolder0<T>() ); | ||||
} | ||||
template<class T , typename A > | ||||
void add( const A& a ) { | ||||
_tests.push_back( new TestHolder1<T,A>(a) ); | ||||
} | ||||
Result * run( const string& filter ); | ||||
static int run( vector<string> suites , const string& filter ); | ||||
static int run( int argc , char ** argv , string default_dbpath | ||||
); | ||||
protected: | ||||
virtual void setupTests() = 0; | ||||
private: | ||||
string _name; | ||||
list<TestCase*> _tests; | ||||
bool _ran; | ||||
static map<string,Suite*> * _suites; | ||||
void registerSuite( string name , Suite * s ); | ||||
}; | ||||
void assert_pass(); | ||||
void assert_fail( const char * exp , const char * file , unsigned l | ||||
ine ); | ||||
void fail( const char * exp , const char * file , unsigned line ); | ||||
class MyAssertionException : boost::noncopyable { | ||||
public: | ||||
MyAssertionException() { | ||||
ss << "assertion: "; | ||||
} | ||||
stringstream ss; | ||||
}; | ||||
class MyAsserts { | ||||
public: | ||||
MyAsserts( const char * aexp , const char * bexp , const char * | ||||
file , unsigned line ) | ||||
: _aexp( aexp ) , _bexp( bexp ) , _file( file ) , _line( li | ||||
ne ) { | ||||
} | ||||
template<typename A,typename B> | ||||
void ae( 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; | ||||
} | ||||
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(); | ||||
private: | ||||
void _gotAssert(); | ||||
MyAssertionException * getBase(); | ||||
string _aexp; | ||||
string _bexp; | ||||
string _file; | ||||
unsigned _line; | ||||
}; | ||||
} | ||||
} | ||||
End of changes. 2 change blocks. | ||||
20 lines changed or deleted | 1 lines changed or added | |||
goodies.h | goodies.h | |||
---|---|---|---|---|
skipping to change at line 21 | skipping to change at line 21 | |||
* | * | |||
* Unless required by applicable law or agreed to in writing, software | * Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | * distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. | |||
* See the License for the specific language governing permissions and | * See the License for the specific language governing permissions and | |||
* limitations under the License. | * limitations under the License. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../bson/util/misc.h" | #include <boost/detail/endian.hpp> | |||
#include "concurrency/mutex.h" | #include <boost/thread/condition_variable.hpp> | |||
#include "mongo/bson/util/misc.h" | ||||
#include "mongo/util/concurrency/mutex.h" | ||||
#include "mongo/util/stacktrace.h" | ||||
namespace mongo { | namespace mongo { | |||
/* @return a dump of the buffer as hex byte ascii output */ | /* @return a dump of the buffer as hex byte ascii output */ | |||
string hexdump(const char *data, unsigned len); | string hexdump(const char *data, unsigned len); | |||
/** | /** | |||
* @return if this name has an increasing counter associated, return th e value | * @return if this name has an increasing counter associated, return th e value | |||
* otherwise 0 | * otherwise 0 | |||
*/ | */ | |||
unsigned setThreadName(const char * name); | long long setThreadName(const char * name); | |||
string getThreadName(); | string getThreadName(); | |||
template<class T> | template<class T> | |||
inline string ToString(const T& t) { | inline string ToString(const T& t) { | |||
stringstream s; | stringstream s; | |||
s << t; | s << t; | |||
return s.str(); | return s.str(); | |||
} | } | |||
#if !defined(_WIN32) && !defined(NOEXECINFO) && !defined(__freebsd__) && !d | ||||
efined(__openbsd__) && !defined(__sun__) | ||||
} // namespace mongo | ||||
#include <pthread.h> | ||||
#include <execinfo.h> | ||||
namespace mongo { | ||||
inline pthread_t GetCurrentThreadId() { | ||||
return pthread_self(); | ||||
} | ||||
/* use "addr2line -CFe <exe>" to parse. */ | ||||
inline void printStackTrace(ostream &o = cout) { | ||||
void *b[20]; | ||||
int size = backtrace(b, 20); | ||||
for (int i = 0; i < size; i++) | ||||
o << hex << b[i] << dec << ' '; | ||||
o << endl; | ||||
char **strings; | ||||
strings = backtrace_symbols(b, size); | ||||
for (int i = 0; i < size; i++) | ||||
o << ' ' << strings[i] << '\n'; | ||||
o.flush(); | ||||
free (strings); | ||||
} | ||||
#else | ||||
inline void printStackTrace(ostream &o = cout) { } | ||||
#endif | ||||
bool isPrime(int n); | bool isPrime(int n); | |||
int nextPrime(int n); | int nextPrime(int n); | |||
inline void dumpmemory(const char *data, int len) { | inline void dumpmemory(const char *data, int len) { | |||
if ( len > 1024 ) | if ( len > 1024 ) | |||
len = 1024; | len = 1024; | |||
try { | try { | |||
const char *q = data; | const char *q = data; | |||
const char *p = q; | const char *p = q; | |||
while ( len > 0 ) { | while ( len > 0 ) { | |||
skipping to change at line 115 | skipping to change at line 85 | |||
// PRINT(2+2); prints "2+2: 4" | // PRINT(2+2); prints "2+2: 4" | |||
#define MONGO_PRINT(x) cout << #x ": " << (x) << endl | #define MONGO_PRINT(x) cout << #x ": " << (x) << endl | |||
#define PRINT MONGO_PRINT | #define PRINT MONGO_PRINT | |||
// PRINTFL; prints file:line | // PRINTFL; prints file:line | |||
#define MONGO_PRINTFL cout << __FILE__ ":" << __LINE__ << endl | #define MONGO_PRINTFL cout << __FILE__ ":" << __LINE__ << endl | |||
#define PRINTFL MONGO_PRINTFL | #define PRINTFL MONGO_PRINTFL | |||
#define MONGO_FLOG log() << __FILE__ ":" << __LINE__ << endl | #define MONGO_FLOG log() << __FILE__ ":" << __LINE__ << endl | |||
#define FLOG MONGO_FLOG | #define FLOG MONGO_FLOG | |||
#undef assert | ||||
#define assert MONGO_assert | ||||
inline bool startsWith(const char *str, const char *prefix) { | inline bool startsWith(const char *str, const char *prefix) { | |||
size_t l = strlen(prefix); | size_t l = strlen(prefix); | |||
if ( strlen(str) < l ) return false; | if ( strlen(str) < l ) return false; | |||
return strncmp(str, prefix, l) == 0; | return strncmp(str, prefix, l) == 0; | |||
} | } | |||
inline bool startsWith(string s, string p) { return startsWith(s.c_str( ), p.c_str()); } | inline bool startsWith(string s, string p) { return startsWith(s.c_str( ), p.c_str()); } | |||
inline bool endsWith(const char *p, const char *suffix) { | inline bool endsWith(const char *p, const char *suffix) { | |||
size_t a = strlen(p); | size_t a = strlen(p); | |||
size_t b = strlen(suffix); | size_t b = strlen(suffix); | |||
skipping to change at line 144 | skipping to change at line 111 | |||
((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) { | |||
return x; | return x; | |||
} | } | |||
#else | #elif defined(BOOST_BIG_ENDIAN) | |||
inline unsigned long fixEndian(unsigned long x) { | inline unsigned long fixEndian(unsigned long x) { | |||
return swapEndian(x); | return swapEndian(x); | |||
} | } | |||
#else | ||||
#error no boost endian header defined | ||||
#endif | #endif | |||
#if !defined(_WIN32) | #if !defined(_WIN32) | |||
typedef int HANDLE; | typedef int HANDLE; | |||
inline void strcpy_s(char *dst, unsigned len, const char *src) { | inline void strcpy_s(char *dst, unsigned len, const char *src) { | |||
assert( strlen(src) < len ); | verify( strlen(src) < len ); | |||
strcpy(dst, src); | strcpy(dst, src); | |||
} | } | |||
#else | #else | |||
typedef void *HANDLE; | typedef void *HANDLE; | |||
#endif | #endif | |||
class ProgressMeter : boost::noncopyable { | ||||
public: | ||||
ProgressMeter( unsigned long long total , int secondsBetween = 3 , | ||||
int checkInterval = 100 , string units = "" ) : _units(units) { | ||||
reset( total , secondsBetween , checkInterval ); | ||||
} | ||||
ProgressMeter() { | ||||
_active = 0; | ||||
_units = ""; | ||||
} | ||||
// typically you do ProgressMeterHolder | ||||
void reset( unsigned long long total , int secondsBetween = 3 , int | ||||
checkInterval = 100 ) { | ||||
_total = total; | ||||
_secondsBetween = secondsBetween; | ||||
_checkInterval = checkInterval; | ||||
_done = 0; | ||||
_hits = 0; | ||||
_lastTime = (int)time(0); | ||||
_active = 1; | ||||
} | ||||
void finished() { | ||||
_active = 0; | ||||
} | ||||
bool isActive() { | ||||
return _active; | ||||
} | ||||
/** | ||||
* @param n how far along we are relative to the total # we set in | ||||
CurOp::setMessage | ||||
* @return if row was printed | ||||
*/ | ||||
bool hit( int n = 1 ) { | ||||
if ( ! _active ) { | ||||
cout << "warning: hit an inactive ProgressMeter" << endl; | ||||
return false; | ||||
} | ||||
_done += n; | ||||
_hits++; | ||||
if ( _hits % _checkInterval ) | ||||
return false; | ||||
int t = (int) time(0); | ||||
if ( t - _lastTime < _secondsBetween ) | ||||
return false; | ||||
if ( _total > 0 ) { | ||||
int per = (int)( ( (double)_done * 100.0 ) / (double)_total | ||||
); | ||||
cout << "\t\t" << _done << "/" << _total << "\t" << per << | ||||
"%"; | ||||
if ( ! _units.empty() ) { | ||||
cout << "\t(" << _units << ")" << endl; | ||||
} | ||||
else { | ||||
cout << endl; | ||||
} | ||||
} | ||||
_lastTime = t; | ||||
return true; | ||||
} | ||||
void setUnits( string units ) { | ||||
_units = units; | ||||
} | ||||
void setTotalWhileRunning( unsigned long long total ) { | ||||
_total = total; | ||||
} | ||||
unsigned long long done() const { return _done; } | ||||
unsigned long long hits() const { return _hits; } | ||||
unsigned long long total() const { return _total; } | ||||
string toString() const { | ||||
if ( ! _active ) | ||||
return ""; | ||||
stringstream buf; | ||||
buf << _done << "/" << _total << " " << (_done*100)/_total << " | ||||
%"; | ||||
if ( ! _units.empty() ) { | ||||
buf << "\t(" << _units << ")" << endl; | ||||
} | ||||
return buf.str(); | ||||
} | ||||
bool operator==( const ProgressMeter& other ) const { | ||||
return this == &other; | ||||
} | ||||
private: | ||||
bool _active; | ||||
unsigned long long _total; | ||||
int _secondsBetween; | ||||
int _checkInterval; | ||||
unsigned long long _done; | ||||
unsigned long long _hits; | ||||
int _lastTime; | ||||
string _units; | ||||
}; | ||||
// e.g.: | ||||
// CurOp * op = cc().curop(); | ||||
// ProgressMeterHolder pm( op->setMessage( "index: (1/3) external sort" | ||||
, d->stats.nrecords , 10 ) ); | ||||
// loop { pm.hit(); } | ||||
class ProgressMeterHolder : boost::noncopyable { | ||||
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 { | ||||
public: | ||||
TicketHolder( int num ) : _mutex("TicketHolder") { | ||||
_outof = num; | ||||
_num = num; | ||||
} | ||||
bool tryAcquire() { | ||||
scoped_lock lk( _mutex ); | ||||
if ( _num <= 0 ) { | ||||
if ( _num < 0 ) { | ||||
cerr << "DISASTER! in TicketHolder" << endl; | ||||
} | ||||
return false; | ||||
} | ||||
_num--; | ||||
return true; | ||||
} | ||||
void release() { | ||||
scoped_lock lk( _mutex ); | ||||
_num++; | ||||
} | ||||
void resize( int newSize ) { | ||||
scoped_lock lk( _mutex ); | ||||
int used = _outof - _num; | ||||
if ( used > newSize ) { | ||||
cout << "ERROR: can't resize since we're using (" << used < | ||||
< ") more than newSize(" << newSize << ")" << endl; | ||||
return; | ||||
} | ||||
_outof = newSize; | ||||
_num = _outof - used; | ||||
} | ||||
int available() const { | ||||
return _num; | ||||
} | ||||
int used() const { | ||||
return _outof - _num; | ||||
} | ||||
int outof() const { return _outof; } | ||||
private: | ||||
int _outof; | ||||
int _num; | ||||
mongo::mutex _mutex; | ||||
}; | ||||
class TicketHolderReleaser { | ||||
public: | ||||
TicketHolderReleaser( TicketHolder * holder ) { | ||||
_holder = holder; | ||||
} | ||||
~TicketHolderReleaser() { | ||||
_holder->release(); | ||||
} | ||||
private: | ||||
TicketHolder * _holder; | ||||
}; | ||||
/** | /** | |||
* this is a thread safe string | * this is a thread safe string | |||
* you will never get a bad pointer, though data may be mungedd | * you will never get a bad pointer, though data may be mungedd | |||
*/ | */ | |||
class ThreadSafeString : boost::noncopyable { | class ThreadSafeString : boost::noncopyable { | |||
public: | public: | |||
ThreadSafeString( size_t size=256 ) | ThreadSafeString( size_t size=256 ) | |||
: _size( size ) , _buf( new char[size] ) { | : _size( size ) , _buf( new char[size] ) { | |||
memset( _buf , 0 , _size ); | memset( _buf , 0 , _size ); | |||
} | } | |||
skipping to change at line 470 | skipping to change at line 229 | |||
operator T* () const { return _p; } | operator T* () const { return _p; } | |||
private: | private: | |||
T* _p; | T* _p; | |||
}; | }; | |||
using boost::shared_ptr; | using boost::shared_ptr; | |||
using boost::scoped_ptr; | using boost::scoped_ptr; | |||
using boost::scoped_array; | using boost::scoped_array; | |||
using boost::intrusive_ptr; | using boost::intrusive_ptr; | |||
using boost::bad_lexical_cast; | ||||
using boost::dynamic_pointer_cast; | using boost::dynamic_pointer_cast; | |||
} // namespace mongo | } // namespace mongo | |||
End of changes. 9 change blocks. | ||||
262 lines changed or deleted | 11 lines changed or added | |||
grid.h | grid.h | |||
---|---|---|---|---|
skipping to change at line 83 | skipping to change at line 83 | |||
bool addShard( string* name , const ConnectionString& servers , lon g long maxSize , string& errMsg ); | bool addShard( string* name , const ConnectionString& servers , lon g long maxSize , string& errMsg ); | |||
/** | /** | |||
* @return true if the config database knows about a host 'name' | * @return true if the config database knows about a host 'name' | |||
*/ | */ | |||
bool knowAboutShard( const string& name ) const; | bool knowAboutShard( const string& name ) const; | |||
/** | /** | |||
* @return true if the chunk balancing functionality is enabled | * @return true if the chunk balancing functionality is enabled | |||
*/ | */ | |||
bool shouldBalance() const; | bool shouldBalance( const string& ns = "" ) const; | |||
/** | /** | |||
* | * | |||
* Obtain grid configuration and settings data. | * Obtain grid configuration and settings data. | |||
* | * | |||
* @param name identifies a particular type of configuration data. | * @param name identifies a particular type of configuration data. | |||
* @return a BSON object containing the requested data. | * @return a BSON object containing the requested data. | |||
*/ | */ | |||
BSONObj getConfigSetting( string name ) const; | BSONObj getConfigSetting( string name ) const; | |||
End of changes. 1 change blocks. | ||||
1 lines changed or deleted | 1 lines changed or added | |||
gridfs.h | gridfs.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 "dbclient.h" | #include "mongo/bson/bsonelement.h" | |||
#include "redef_macros.h" | #include "mongo/bson/bsonobj.h" | |||
#include "mongo/client/dbclientinterface.h" | ||||
namespace mongo { | namespace mongo { | |||
typedef unsigned long long gridfs_offset; | typedef unsigned long long gridfs_offset; | |||
class GridFS; | class GridFS; | |||
class GridFile; | class GridFile; | |||
class GridFSChunk { | class GridFSChunk { | |||
public: | public: | |||
skipping to change at line 203 | skipping to change at line 204 | |||
GridFile(const GridFS * grid , BSONObj obj ); | GridFile(const GridFS * grid , BSONObj obj ); | |||
void _exists() const; | void _exists() const; | |||
const GridFS * _grid; | const GridFS * _grid; | |||
BSONObj _obj; | BSONObj _obj; | |||
friend class GridFS; | friend class GridFS; | |||
}; | }; | |||
} | } | |||
#include "undef_macros.h" | ||||
End of changes. 2 change blocks. | ||||
2 lines changed or deleted | 3 lines changed or added | |||
hashtab.h | hashtab.h | |||
---|---|---|---|---|
skipping to change at line 114 | skipping to change at line 114 | |||
// out() << "hashtab init, buflen:" << buflen << " m:" << m << endl; | // out() << "hashtab init, buflen:" << buflen << " m:" << m << endl; | |||
n = buflen / m; | n = buflen / m; | |||
if ( (n & 1) == 0 ) | if ( (n & 1) == 0 ) | |||
n--; | n--; | |||
maxChain = (int) (n * 0.05); | maxChain = (int) (n * 0.05); | |||
_buf = buf; | _buf = buf; | |||
//nodes = (Node *) buf; | //nodes = (Node *) buf; | |||
if ( sizeof(Node) != 628 ) { | if ( sizeof(Node) != 628 ) { | |||
out() << "HashTable() " << _name << " sizeof(node):" << siz eof(Node) << " n:" << n << " sizeof(Key): " << sizeof(Key) << " sizeof(Type ):" << sizeof(Type) << endl; | out() << "HashTable() " << _name << " sizeof(node):" << siz eof(Node) << " n:" << n << " sizeof(Key): " << sizeof(Key) << " sizeof(Type ):" << sizeof(Type) << endl; | |||
assert( sizeof(Node) == 628 ); | verify( 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; | |||
skipping to change at line 150 | skipping to change at line 150 | |||
bool found; | bool found; | |||
int i = _find(k, found); | int i = _find(k, found); | |||
if ( i < 0 ) | if ( i < 0 ) | |||
return false; | return false; | |||
Node* n = getDur().writing( &nodes(i) ); | Node* n = getDur().writing( &nodes(i) ); | |||
if ( !found ) { | if ( !found ) { | |||
n->k = k; | n->k = k; | |||
n->hash = k.hash(); | n->hash = k.hash(); | |||
} | } | |||
else { | else { | |||
assert( n->hash == k.hash() ); | verify( n->hash == k.hash() ); | |||
} | } | |||
n->value = value; | n->value = value; | |||
return true; | return true; | |||
} | } | |||
typedef void (*IteratorCallback)( const Key& k , Type& v ); | typedef void (*IteratorCallback)( const Key& k , Type& v ); | |||
void iterAll( IteratorCallback callback ) { | void iterAll( IteratorCallback callback ) { | |||
for ( int i=0; i<n; i++ ) { | for ( int i=0; i<n; i++ ) { | |||
if ( nodes(i).inUse() ) { | if ( nodes(i).inUse() ) { | |||
callback( nodes(i).k , nodes(i).value ); | callback( nodes(i).k , nodes(i).value ); | |||
End of changes. 2 change blocks. | ||||
2 lines changed or deleted | 2 lines changed or added | |||
hex.h | hex.h | |||
---|---|---|---|---|
skipping to change at line 29 | skipping to change at line 29 | |||
namespace mongo { | namespace mongo { | |||
//can't use hex namespace because it conflicts with hex iostream functi on | //can't use hex namespace because it conflicts with hex iostream functi on | |||
inline int fromHex( char c ) { | inline int fromHex( char c ) { | |||
if ( '0' <= c && c <= '9' ) | if ( '0' <= c && c <= '9' ) | |||
return c - '0'; | return c - '0'; | |||
if ( 'a' <= c && c <= 'f' ) | if ( 'a' <= c && c <= 'f' ) | |||
return c - 'a' + 10; | return c - 'a' + 10; | |||
if ( 'A' <= c && c <= 'F' ) | if ( 'A' <= c && c <= 'F' ) | |||
return c - 'A' + 10; | return c - 'A' + 10; | |||
assert( false ); | verify( false ); | |||
return 0xff; | return 0xff; | |||
} | } | |||
inline char fromHex( const char *c ) { | inline char fromHex( const char *c ) { | |||
return (char)(( fromHex( c[ 0 ] ) << 4 ) | fromHex( c[ 1 ] )); | return (char)(( fromHex( c[ 0 ] ) << 4 ) | fromHex( c[ 1 ] )); | |||
} | } | |||
inline string toHex(const void* inRaw, int len) { | inline string toHex(const void* inRaw, int len) { | |||
static const char hexchars[] = "0123456789ABCDEF"; | static const char hexchars[] = "0123456789ABCDEF"; | |||
StringBuilder out; | StringBuilder out; | |||
End of changes. 1 change blocks. | ||||
1 lines changed or deleted | 1 lines changed or added | |||
histogram.h | histogram.h | |||
---|---|---|---|---|
// histogram.h | ||||
/** | /** | |||
* Copyright (C) 2008 10gen Inc. | * Copyright (C) 2008 10gen Inc. | |||
* | * | |||
* This program is free software: you can redistribute it and/or modify | * This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Affero General Public License, version 3 , | * it under the terms of the GNU Affero General Public License, version 3 , | |||
* as published by the Free Software Foundation. | * as published by the Free Software Foundation. | |||
* | * | |||
* This program is distributed in the hope that it will be useful, | * This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Affero General Public License for more details. | * GNU Affero General Public License for more details. | |||
* | * | |||
* You should have received a copy of the GNU Affero General Public Licen se | * You should have received a copy of the GNU Affero General Public Licen se | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | */ | |||
#ifndef UTIL_HISTOGRAM_HEADER | #ifndef UTIL_HISTOGRAM_HEADER | |||
#define UTIL_HISTOGRAM_HEADER | #define UTIL_HISTOGRAM_HEADER | |||
#include "../pch.h" | ||||
#include <string> | #include <string> | |||
#include <stdint.h> | ||||
namespace mongo { | namespace mongo { | |||
using std::string; | ||||
/** | /** | |||
* A histogram for a 32-bit integer range. | * A histogram for a 32-bit integer range. | |||
*/ | */ | |||
class Histogram { | class Histogram { | |||
public: | public: | |||
/** | /** | |||
* Construct a histogram with 'numBuckets' buckets, optionally | * Construct a histogram with 'numBuckets' buckets, optionally | |||
* having the first bucket start at 'initialValue' rather than | * having the first bucket start at 'initialValue' rather than | |||
* 0. By default, the histogram buckets will be 'bucketSize' wide. | * 0. By default, the histogram buckets will be 'bucketSize' wide. | |||
* | * | |||
skipping to change at line 62 | skipping to change at line 57 | |||
* Usage example: | * Usage example: | |||
* Histogram::Options opts; | * Histogram::Options opts; | |||
* opts.numBuckets = 4; | * opts.numBuckets = 4; | |||
* opts.bucketSize = 125; | * opts.bucketSize = 125; | |||
* opts.exponential = true; | * opts.exponential = true; | |||
* Histogram h( opts ); | * Histogram h( opts ); | |||
* | * | |||
* Generates the bucket ranges [0..125],[126..250],[251..500],[50 1..max_int] | * Generates the bucket ranges [0..125],[126..250],[251..500],[50 1..max_int] | |||
*/ | */ | |||
struct Options { | struct Options { | |||
boost::uint32_t numBuckets; | uint32_t numBuckets; | |||
boost::uint32_t bucketSize; | uint32_t bucketSize; | |||
boost::uint32_t initialValue; | uint32_t initialValue; | |||
// use exponential buckets? | // use exponential buckets? | |||
bool exponential; | bool exponential; | |||
Options() | Options() | |||
: numBuckets(0) | : numBuckets(0) | |||
, bucketSize(0) | , bucketSize(0) | |||
, initialValue(0) | , initialValue(0) | |||
, exponential(false) {} | , exponential(false) {} | |||
}; | }; | |||
explicit Histogram( const Options& opts ); | explicit Histogram( const Options& opts ); | |||
~Histogram(); | ~Histogram(); | |||
/** | /** | |||
* Find the bucket that 'element' falls into and increment its coun t. | * Find the bucket that 'element' falls into and increment its coun t. | |||
*/ | */ | |||
void insert( boost::uint32_t element ); | void insert( uint32_t element ); | |||
/** | /** | |||
* Render the histogram as string that can be used inside an | * Render the histogram as string that can be used inside an | |||
* HTML doc. | * HTML doc. | |||
*/ | */ | |||
string toHTML() const; | std::string toHTML() const; | |||
// testing interface below -- consider it private | // testing interface below -- consider it private | |||
/** | /** | |||
* Return the count for the 'bucket'-th bucket. | * Return the count for the 'bucket'-th bucket. | |||
*/ | */ | |||
boost::uint64_t getCount( boost::uint32_t bucket ) const; | uint64_t getCount( uint32_t bucket ) const; | |||
/** | /** | |||
* Return the maximum element that would fall in the | * Return the maximum element that would fall in the | |||
* 'bucket'-th bucket. | * 'bucket'-th bucket. | |||
*/ | */ | |||
boost::uint32_t getBoundary( boost::uint32_t bucket ) const; | uint32_t getBoundary( uint32_t bucket ) const; | |||
/** | /** | |||
* Return the number of buckets in this histogram. | * Return the number of buckets in this histogram. | |||
*/ | */ | |||
boost::uint32_t getBucketsNum() const; | uint32_t getBucketsNum() const; | |||
private: | private: | |||
/** | /** | |||
* Returns the bucket where 'element' should fall | * Returns the bucket where 'element' should fall | |||
* into. Currently assumes that 'element' is greater than the | * into. Currently assumes that 'element' is greater than the | |||
* minimum 'inialValue'. | * minimum 'inialValue'. | |||
*/ | */ | |||
boost::uint32_t _findBucket( boost::uint32_t element ) const; | uint32_t _findBucket( uint32_t element ) const; | |||
boost::uint32_t _initialValue; // no value lower than it is recor | uint32_t _initialValue; // no value lower than it is recorded | |||
ded | uint32_t _numBuckets; // total buckets in the histogram | |||
boost::uint32_t _numBuckets; // total buckets in the histogram | ||||
// all below owned here | // all below owned here | |||
boost::uint32_t* _boundaries; // maximum element of each bucket | uint32_t* _boundaries; // maximum element of each bucket | |||
boost::uint64_t* _buckets; // current count of each bucket | uint64_t* _buckets; // current count of each bucket | |||
Histogram( const Histogram& ); | Histogram( const Histogram& ); | |||
Histogram& operator=( const Histogram& ); | Histogram& operator=( const Histogram& ); | |||
}; | }; | |||
} // namespace mongo | } // namespace mongo | |||
#endif // UTIL_HISTOGRAM_HEADER | #endif // UTIL_HISTOGRAM_HEADER | |||
End of changes. 13 change blocks. | ||||
20 lines changed or deleted | 14 lines changed or added | |||
hostandport.h | hostandport.h | |||
---|---|---|---|---|
skipping to change at line 43 | skipping to change at line 43 | |||
struct HostAndPort { | struct HostAndPort { | |||
HostAndPort() : _port(-1) { } | HostAndPort() : _port(-1) { } | |||
/** From a string hostname[:portnumber] or a #dynname | /** From a string hostname[:portnumber] or a #dynname | |||
Throws user assertion if bad config string or bad port #. | Throws user assertion if bad config string or bad port #. | |||
*/ | */ | |||
HostAndPort(string s); | HostAndPort(string s); | |||
/** @param p port number. -1 is ok to use default. */ | /** @param p port number. -1 is ok to use default. */ | |||
HostAndPort(string h, int p /*= -1*/) : _host(h), _port(p) { | HostAndPort(string h, int p /*= -1*/) : _host(h), _port(p) { | |||
assert( !str::startsWith(h, '#') ); | verify( !str::startsWith(h, '#') ); | |||
} | } | |||
HostAndPort(const SockAddr& sock ) : _host( sock.getAddr() ) , _por t( sock.getPort() ) { } | HostAndPort(const SockAddr& sock ) : _host( sock.getAddr() ) , _por t( sock.getPort() ) { } | |||
static HostAndPort me() { return HostAndPort("localhost", cmdLine.p ort); } | static HostAndPort me() { return HostAndPort("localhost", cmdLine.p ort); } | |||
/* uses real hostname instead of localhost */ | /* uses real hostname instead of localhost */ | |||
static HostAndPort Me(); | static HostAndPort Me(); | |||
bool operator<(const HostAndPort& r) const { | bool operator<(const HostAndPort& r) const { | |||
skipping to change at line 161 | skipping to change at line 161 | |||
ip = string(ips); | ip = string(ips); | |||
ips = ""; | ips = ""; | |||
} | } | |||
HostAndPort h = HostAndPort(ip, cmdLine.port); | HostAndPort h = HostAndPort(ip, cmdLine.port); | |||
if (!h.isLocalHost()) { | if (!h.isLocalHost()) { | |||
return h; | return h; | |||
} | } | |||
} | } | |||
string h = getHostName(); | string h = getHostName(); | |||
assert( !h.empty() ); | verify( !h.empty() ); | |||
assert( h != "localhost" ); | verify( h != "localhost" ); | |||
return HostAndPort(h, cmdLine.port); | return HostAndPort(h, cmdLine.port); | |||
} | } | |||
inline string HostAndPort::dynString() const { | inline string HostAndPort::dynString() const { | |||
return dyn() ? _dynName : toString(); | return dyn() ? _dynName : toString(); | |||
} | } | |||
inline string HostAndPort::toStringLong() const { | inline string HostAndPort::toStringLong() const { | |||
return _dynName + ':' + toString(); | return _dynName + ':' + toString(); | |||
} | } | |||
skipping to change at line 210 | skipping to change at line 210 | |||
string _host = host(); | string _host = host(); | |||
return ( _host == "localhost" | return ( _host == "localhost" | |||
|| startsWith(_host.c_str(), "127.") | || startsWith(_host.c_str(), "127.") | |||
|| _host == "::1" | || _host == "::1" | |||
|| _host == "anonymous unix socket" | || _host == "anonymous unix socket" | |||
|| _host.c_str()[0] == '/' // unix socket | || _host.c_str()[0] == '/' // unix socket | |||
); | ); | |||
} | } | |||
inline void HostAndPort::init(const char *p) { | inline void HostAndPort::init(const char *p) { | |||
uassert(13110, "HostAndPort: bad host:port config string", *p); | massert(13110, "HostAndPort: host is empty", *p); | |||
assert( *p != '#' ); | verify( *p != '#' ); | |||
assert( _dynName.empty() ); | verify( _dynName.empty() ); | |||
const char *colon = strrchr(p, ':'); | const char *colon = strrchr(p, ':'); | |||
if( colon ) { | if( colon ) { | |||
int port = atoi(colon+1); | int port = atoi(colon+1); | |||
uassert(13095, "HostAndPort: bad port #", port > 0); | uassert(13095, "HostAndPort: bad port #", port > 0); | |||
_host = string(p,colon-p); | _host = string(p,colon-p); | |||
_port = port; | _port = port; | |||
} | } | |||
else { | else { | |||
// no port specified. | // no port specified. | |||
_host = p; | _host = p; | |||
End of changes. 3 change blocks. | ||||
6 lines changed or deleted | 6 lines changed or added | |||
index.h | index.h | |||
---|---|---|---|---|
skipping to change at line 21 | skipping to change at line 21 | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Affero General Public License for more details. | * GNU Affero General Public License for more details. | |||
* | * | |||
* You should have received a copy of the GNU Affero General Public Licen se | * You should have received a copy of the GNU Affero General Public Licen se | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../pch.h" | #include "pch.h" | |||
#include "diskloc.h" | ||||
#include "jsobj.h" | #include <vector> | |||
#include "indexkey.h" | ||||
#include "key.h" | #include "mongo/db/diskloc.h" | |||
#include "mongo/db/index_insertion_continuation.h" | ||||
#include "mongo/db/indexkey.h" | ||||
#include "mongo/db/jsobj.h" | ||||
#include "mongo/db/key.h" | ||||
#include "mongo/db/namespace.h" | ||||
namespace mongo { | namespace mongo { | |||
class IndexInterface { | class IndexInterface { | |||
protected: | protected: | |||
virtual ~IndexInterface() { } | virtual ~IndexInterface() { } | |||
public: | public: | |||
static void phasedBegin(); | class IndexInserter : private boost::noncopyable { | |||
virtual void phasedQueueItemToInsert( | public: | |||
IndexInserter(); | ||||
~IndexInserter(); | ||||
void addInsertionContinuation(IndexInsertionContinuation *c); | ||||
void finishAllInsertions(); | ||||
private: | ||||
std::vector<IndexInsertionContinuation *> _continuations; | ||||
}; | ||||
virtual IndexInsertionContinuation *beginInsertIntoIndex( | ||||
int idxNo, | int idxNo, | |||
DiskLoc thisLoc, DiskLoc _recordLoc, const BSONObj &_key, | IndexDetails &_idx, DiskLoc _recordLoc, const BSONObj &_key, | |||
const Ordering& _order, IndexDetails& _idx, bool dupsAllowed) = | const Ordering& _order, bool dupsAllowed) = 0; | |||
0; | ||||
static void phasedFinish(); | ||||
virtual int keyCompare(const BSONObj& l,const BSONObj& r, const Ord ering &ordering) = 0; | virtual int keyCompare(const BSONObj& l,const BSONObj& r, const Ord ering &ordering) = 0; | |||
virtual long long fullValidate(const DiskLoc& thisLoc, const BSONOb j &order) = 0; | virtual long long fullValidate(const DiskLoc& thisLoc, const BSONOb j &order) = 0; | |||
virtual DiskLoc findSingle(const IndexDetails &indexdetails , const DiskLoc& thisLoc, const BSONObj& key) const = 0; | virtual DiskLoc findSingle(const IndexDetails &indexdetails , const DiskLoc& thisLoc, const BSONObj& key) const = 0; | |||
virtual bool unindex(const DiskLoc thisLoc, IndexDetails& id, const BSONObj& key, const DiskLoc recordLoc) const = 0; | virtual bool unindex(const DiskLoc thisLoc, IndexDetails& id, const BSONObj& key, const DiskLoc recordLoc) const = 0; | |||
virtual int bt_insert(const DiskLoc thisLoc, const DiskLoc recordLo c, | virtual int bt_insert(const DiskLoc thisLoc, const DiskLoc recordLo c, | |||
const BSONObj& key, const Ordering &order, bool dupsAllowed, | const BSONObj& key, const Ordering &order, bool dupsAllowed, | |||
IndexDetails& idx, bool toplevel = true) const = 0; | IndexDetails& idx, bool toplevel = true) const = 0; | |||
virtual DiskLoc addBucket(const IndexDetails&) = 0; | virtual DiskLoc addBucket(const IndexDetails&) = 0; | |||
virtual void uassertIfDups(IndexDetails& idx, vector<BSONObj*>& add edKeys, DiskLoc head, | virtual void uassertIfDups(IndexDetails& idx, vector<BSONObj*>& add edKeys, DiskLoc head, | |||
skipping to change at line 129 | skipping to change at line 144 | |||
/* true if the specified key is in the index */ | /* true if the specified key is in the index */ | |||
bool hasKey(const BSONObj& key); | bool hasKey(const BSONObj& key); | |||
// returns name of this index's storage area | // returns name of this index's storage area | |||
// database.table.$index | // database.table.$index | |||
string indexNamespace() const { | string indexNamespace() const { | |||
BSONObj io = info.obj(); | BSONObj io = info.obj(); | |||
string s; | string s; | |||
s.reserve(Namespace::MaxNsLen); | s.reserve(Namespace::MaxNsLen); | |||
s = io.getStringField("ns"); | s = io.getStringField("ns"); | |||
assert( !s.empty() ); | verify( !s.empty() ); | |||
s += ".$"; | s += ".$"; | |||
s += io.getStringField("name"); | s += io.getStringField("name"); | |||
return s; | return s; | |||
} | } | |||
string indexName() const { // e.g. "ts_1" | string indexName() const { // e.g. "ts_1" | |||
BSONObj io = info.obj(); | BSONObj io = info.obj(); | |||
return io.getStringField("name"); | return io.getStringField("name"); | |||
} | } | |||
skipping to change at line 235 | skipping to change at line 250 | |||
void dupCheck(IndexDetails& idx, DiskLoc curObjLoc) { | void dupCheck(IndexDetails& idx, DiskLoc curObjLoc) { | |||
if( added.empty() || !idx.unique() ) | if( added.empty() || !idx.unique() ) | |||
return; | return; | |||
const Ordering ordering = Ordering::make(idx.keyPattern()); | const Ordering ordering = Ordering::make(idx.keyPattern()); | |||
idx.idxInterface().uassertIfDups(idx, added, idx.head, curObjLo c, ordering); // "E11001 duplicate key on update" | idx.idxInterface().uassertIfDups(idx, added, idx.head, curObjLo c, ordering); // "E11001 duplicate key on update" | |||
} | } | |||
}; | }; | |||
class NamespaceDetails; | class NamespaceDetails; | |||
// changedId should be initialized to false | // changedId should be initialized to false | |||
void getIndexChanges(vector<IndexChanges>& v, NamespaceDetails& d, BSON | void getIndexChanges(vector<IndexChanges>& v, const char *ns, Namespace | |||
Obj newObj, BSONObj oldObj, bool &cangedId); | Details& d, | |||
BSONObj newObj, BSONObj oldObj, bool &cangedId); | ||||
void dupCheck(vector<IndexChanges>& v, NamespaceDetails& d, DiskLoc cur ObjLoc); | void dupCheck(vector<IndexChanges>& v, NamespaceDetails& d, DiskLoc cur ObjLoc); | |||
} // namespace mongo | } // namespace mongo | |||
End of changes. 5 change blocks. | ||||
14 lines changed or deleted | 29 lines changed or added | |||
inline_decls.h | inline_decls.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 | |||
#if defined(__GNUC__) | #if defined(__GNUC__) | |||
#define NOINLINE_DECL __attribute__((noinline)) | #define NOINLINE_DECL __attribute__((noinline)) | |||
#define PACKED_DECL __attribute__((packed)) | ||||
#elif defined(_MSC_VER) | #elif defined(_MSC_VER) | |||
#define NOINLINE_DECL __declspec(noinline) | #define NOINLINE_DECL __declspec(noinline) | |||
#define PACKED_DECL | ||||
#else | #else | |||
#define NOINLINE_DECL | #define NOINLINE_DECL | |||
#define PACKED_DECL | ||||
#endif | #endif | |||
namespace mongo { | namespace mongo { | |||
/* Note: do not clutter code with these -- ONLY use in hot spots / signific ant loops. */ | /* Note: do not clutter code with these -- ONLY use in hot spots / signific ant loops. */ | |||
#if !defined(__GNUC__) | #if !defined(__GNUC__) | |||
// branch prediction. indicate we expect to be true | // branch prediction. indicate we expect to be true | |||
End of changes. 3 change blocks. | ||||
0 lines changed or deleted | 3 lines changed or added | |||
instance.h | instance.h | |||
---|---|---|---|---|
skipping to change at line 22 | skipping to change at line 22 | |||
* 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 "../client/dbclient.h" | ||||
#include "curop-inl.h" | #include "curop-inl.h" | |||
#include "security.h" | #include "security.h" | |||
#include "cmdline.h" | #include "cmdline.h" | |||
#include "client.h" | #include "client.h" | |||
#include "mongo/client/dbclientinterface.h" | ||||
namespace mongo { | namespace mongo { | |||
extern string dbExecCommand; | extern string dbExecCommand; | |||
/** a high level recording of operations to the database - sometimes us ed for diagnostics | /** a high level recording of operations to the database - sometimes us ed for diagnostics | |||
and debugging. | and debugging. | |||
*/ | */ | |||
class DiagLog { | class DiagLog { | |||
ofstream *f; // note this is never freed | ofstream *f; // note this is never freed | |||
skipping to change at line 87 | skipping to change at line 87 | |||
/* returns true if there is no data on this server. useful when starti ng replication. | /* returns true if there is no data on this server. useful when starti ng replication. | |||
local database does NOT count. | local database does NOT count. | |||
*/ | */ | |||
bool replHasDatabases(); | bool replHasDatabases(); | |||
/** "embedded" calls to the local server directly. | /** "embedded" calls to the local server directly. | |||
Caller does not need to lock, that is handled within. | Caller does not need to lock, that is handled within. | |||
*/ | */ | |||
class DBDirectClient : public DBClientBase { | class DBDirectClient : public DBClientBase { | |||
public: | public: | |||
using DBClientBase::query; | ||||
virtual auto_ptr<DBClientCursor> query(const string &ns, Query quer y, int nToReturn = 0, int nToSkip = 0, | virtual auto_ptr<DBClientCursor> query(const string &ns, Query quer y, int nToReturn = 0, int nToSkip = 0, | |||
const BSONObj *fieldsToRetur n = 0, int queryOptions = 0, int batchSize = 0); | const BSONObj *fieldsToRetur n = 0, int queryOptions = 0, int batchSize = 0); | |||
virtual bool isFailed() const { | virtual bool isFailed() const { | |||
return false; | return false; | |||
} | } | |||
virtual string toString() { | virtual string toString() { | |||
return "DBDirectClient"; | return "DBDirectClient"; | |||
} | } | |||
virtual string getServerAddress() const { | virtual string getServerAddress() const { | |||
skipping to change at line 119 | skipping to change at line 121 | |||
return call( toSend , response ); | return call( toSend , response ); | |||
} | } | |||
virtual unsigned long long count(const string &ns, const BSONObj& q uery = BSONObj(), int options=0, int limit=0, int skip=0 ); | virtual unsigned long long count(const string &ns, const BSONObj& q uery = BSONObj(), int options=0, int limit=0, int skip=0 ); | |||
virtual ConnectionString::ConnectionType type() const { return Conn ectionString::MASTER; } | virtual ConnectionString::ConnectionType type() const { return Conn ectionString::MASTER; } | |||
double getSoTimeout() const { return 0; } | double getSoTimeout() const { return 0; } | |||
virtual bool lazySupported() const { return true; } | virtual bool lazySupported() const { return true; } | |||
virtual QueryOptions _lookupAvailableOptions(); | ||||
private: | private: | |||
static HostAndPort _clientHost; | static HostAndPort _clientHost; | |||
}; | }; | |||
extern int lockFile; | extern int lockFile; | |||
#ifdef _WIN32 | #ifdef _WIN32 | |||
extern HANDLE lockFileHandle; | extern HANDLE lockFileHandle; | |||
#endif | #endif | |||
void acquirePathLock(bool doingRepair=false); // if doingRepair=true do n't consider unclean shutdown an error | void acquirePathLock(bool doingRepair=false); // if doingRepair=true do n't consider unclean shutdown an error | |||
void maybeCreatePidFile(); | void maybeCreatePidFile(); | |||
void exitCleanly( ExitCode code ); | ||||
} // namespace mongo | } // namespace mongo | |||
End of changes. 5 change blocks. | ||||
1 lines changed or deleted | 8 lines changed or added | |||
jsobj.h | jsobj.h | |||
---|---|---|---|---|
skipping to change at line 34 | skipping to change at line 34 | |||
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 "../pch.h" | #include "../pch.h" | |||
#include "../bson/util/builder.h" | #include "../bson/util/builder.h" | |||
#include "../util/optime.h" | #include "../util/optime.h" | |||
//#include "boost/utility.hpp" | ||||
//#include <set> | ||||
#include "../bson/bsontypes.h" | #include "../bson/bsontypes.h" | |||
#include "../bson/oid.h" | #include "../bson/oid.h" | |||
#include "../bson/bsonelement.h" | #include "../bson/bsonelement.h" | |||
#include "../bson/bsonobj.h" | #include "../bson/bsonobj.h" | |||
#include "../bson/bsonmisc.h" | #include "../bson/bsonmisc.h" | |||
#include "../bson/bsonobjbuilder.h" | #include "../bson/bsonobjbuilder.h" | |||
#include "../bson/bsonobjiterator.h" | #include "../bson/bsonobjiterator.h" | |||
#include "../bson/bson-inl.h" | #include "../bson/bson-inl.h" | |||
#include "../bson/ordering.h" | #include "../bson/ordering.h" | |||
#include "../bson/stringdata.h" | #include "../bson/stringdata.h" | |||
End of changes. 1 change blocks. | ||||
2 lines changed or deleted | 0 lines changed or added | |||
jsobjmanipulator.h | jsobjmanipulator.h | |||
---|---|---|---|---|
skipping to change at line 33 | skipping to change at line 33 | |||
namespace mongo { | namespace mongo { | |||
/** Manipulate the binary representation of a BSONElement in-place. | /** Manipulate the binary representation of a BSONElement in-place. | |||
Careful, this casts away const. | Careful, this casts away const. | |||
*/ | */ | |||
class BSONElementManipulator { | class BSONElementManipulator { | |||
public: | public: | |||
BSONElementManipulator( const BSONElement &element ) : | BSONElementManipulator( const BSONElement &element ) : | |||
_element( element ) { | _element( element ) { | |||
assert( !_element.eoo() ); | verify( !_element.eoo() ); | |||
} | } | |||
/** Replace a Timestamp type with a Date type initialized to | /** Replace a Timestamp type with a Date type initialized to | |||
OpTime::now().asDate() | OpTime::now().asDate() | |||
*/ | */ | |||
void initTimestamp(); | void initTimestamp(); | |||
// Note the ones with a capital letter call getDur().writing and jo urnal | // Note the ones with a capital letter call getDur().writing and jo urnal | |||
/** Change the value, in place, of the number. */ | /** Change the value, in place, of the number. */ | |||
void setNumber(double d) { | void setNumber(double d) { | |||
if ( _element.type() == NumberDouble ) *reinterpret_cast< doubl e * >( value() ) = d; | if ( _element.type() == NumberDouble ) *reinterpret_cast< doubl e * >( value() ) = d; | |||
else if ( _element.type() == NumberInt ) *reinterpret_cast< int * >( value() ) = (int) d; | else if ( _element.type() == NumberInt ) *reinterpret_cast< int * >( value() ) = (int) d; | |||
else assert(0); | else verify(0); | |||
} | } | |||
void SetNumber(double d); | void SetNumber(double d); | |||
void setLong(long long n) { | void setLong(long long n) { | |||
assert( _element.type() == NumberLong ); | verify( _element.type() == NumberLong ); | |||
*reinterpret_cast< long long * >( value() ) = n; | *reinterpret_cast< long long * >( value() ) = n; | |||
} | } | |||
void SetLong(long long n); | void SetLong(long long n); | |||
void setInt(int n) { | void setInt(int n) { | |||
assert( _element.type() == NumberInt ); | verify( _element.type() == NumberInt ); | |||
*reinterpret_cast< int * >( value() ) = n; | *reinterpret_cast< int * >( value() ) = n; | |||
} | } | |||
void SetInt(int n); | void SetInt(int n); | |||
/** Replace the type and value of the element with the type and val ue of e, | /** Replace the type and value of the element with the type and val ue of e, | |||
preserving the original fieldName */ | preserving the original fieldName */ | |||
void replaceTypeAndValue( const BSONElement &e ) { | void replaceTypeAndValue( const BSONElement &e ) { | |||
*data() = e.type(); | *data() = e.type(); | |||
memcpy( value(), e.value(), e.valuesize() ); | memcpy( value(), e.value(), e.valuesize() ); | |||
} | } | |||
skipping to change at line 81 | skipping to change at line 81 | |||
// If have a Timestamp field as the first or second element, | // If have a Timestamp field as the first or second element, | |||
// update it to a Date field set to OpTime::now().asDate(). Th e | // update it to a Date field set to OpTime::now().asDate(). Th e | |||
// replacement policy is a work in progress. | // replacement policy is a work in progress. | |||
BSONObjIterator i( obj ); | BSONObjIterator i( obj ); | |||
for( int j = 0; i.moreWithEOO() && j < 2; ++j ) { | for( int j = 0; i.moreWithEOO() && j < 2; ++j ) { | |||
BSONElement e = i.next(); | BSONElement e = i.next(); | |||
if ( e.eoo() ) | if ( e.eoo() ) | |||
break; | break; | |||
if ( e.type() == Timestamp ) { | if ( e.type() == Timestamp ) { | |||
// performance note, this locks a mutex: | ||||
BSONElementManipulator( e ).initTimestamp(); | BSONElementManipulator( e ).initTimestamp(); | |||
break; | break; | |||
} | } | |||
} | } | |||
} | } | |||
private: | private: | |||
char *data() { return nonConst( _element.rawdata() ); } | char *data() { return nonConst( _element.rawdata() ); } | |||
char *value() { return nonConst( _element.value() ); } | char *value() { return nonConst( _element.value() ); } | |||
static char *nonConst( const char *s ) { return const_cast< char * >( s ); } | static char *nonConst( const char *s ) { return const_cast< char * >( s ); } | |||
End of changes. 5 change blocks. | ||||
4 lines changed or deleted | 5 lines changed or added | |||
key.h | key.h | |||
---|---|---|---|---|
skipping to change at line 45 | skipping to change at line 45 | |||
explicit KeyBson(const BSONObj& obj) : _o(obj) { } | explicit KeyBson(const BSONObj& obj) : _o(obj) { } | |||
int woCompare(const KeyBson& r, const Ordering &o) const; | int woCompare(const KeyBson& r, const Ordering &o) const; | |||
BSONObj toBson() const { return _o; } | BSONObj toBson() const { return _o; } | |||
string toString() const { return _o.toString(); } | string toString() const { return _o.toString(); } | |||
int dataSize() const { return _o.objsize(); } | int dataSize() const { return _o.objsize(); } | |||
const char * data() const { return _o.objdata(); } | const char * data() const { return _o.objdata(); } | |||
BSONElement _firstElement() const { return _o.firstElement(); } | BSONElement _firstElement() const { return _o.firstElement(); } | |||
bool isCompactFormat() const { return false; } | bool isCompactFormat() const { return false; } | |||
bool woEqual(const KeyBson& r) const; | bool woEqual(const KeyBson& r) const; | |||
void assign(const KeyBson& rhs) { *this = rhs; } | void assign(const KeyBson& rhs) { *this = rhs; } | |||
bool isValid() const { return true; } | ||||
private: | private: | |||
BSONObj _o; | BSONObj _o; | |||
}; | }; | |||
class KeyV1Owned; | class KeyV1Owned; | |||
// corresponding to BtreeData_V1 | // corresponding to BtreeData_V1 | |||
class KeyV1 { | class KeyV1 { | |||
void operator=(const KeyV1&); // disallowed just to make people be careful as we don't own the buffer | void operator=(const KeyV1&); // disallowed just to make people be careful as we don't own the buffer | |||
KeyV1(const KeyV1Owned&); // disallowed as this is not a great idea as KeyV1Owned likely will go out of scope | KeyV1(const KeyV1Owned&); // disallowed as this is not a great idea as KeyV1Owned likely will go out of scope | |||
skipping to change at line 87 | skipping to change at line 88 | |||
/** get the key data we want to store in the btree bucket */ | /** get the key data we want to store in the btree bucket */ | |||
const char * data() const { return (const char *) _keyData; } | const char * data() const { return (const char *) _keyData; } | |||
/** @return size of data() */ | /** @return size of data() */ | |||
int dataSize() const; | int dataSize() const; | |||
/** only used by geo, which always has bson keys */ | /** only used by geo, which always has bson keys */ | |||
BSONElement _firstElement() const { return bson().firstElement(); } | BSONElement _firstElement() const { return bson().firstElement(); } | |||
bool isCompactFormat() const { return *_keyData != IsBSON; } | bool isCompactFormat() const { return *_keyData != IsBSON; } | |||
bool isValid() const { return _keyData > (const unsigned char*)1; } | ||||
protected: | protected: | |||
enum { IsBSON = 0xff }; | enum { IsBSON = 0xff }; | |||
const unsigned char *_keyData; | const unsigned char *_keyData; | |||
BSONObj bson() const { | BSONObj bson() const { | |||
dassert( !isCompactFormat() ); | dassert( !isCompactFormat() ); | |||
return BSONObj((const char *) _keyData+1); | return BSONObj((const char *) _keyData+1); | |||
} | } | |||
private: | private: | |||
int compareHybrid(const KeyV1& right, const Ordering& order) const; | int compareHybrid(const KeyV1& right, const Ordering& order) const; | |||
}; | }; | |||
End of changes. 2 change blocks. | ||||
0 lines changed or deleted | 3 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 "../bson/oid.h" | #include "mongo/bson/oid.h" | |||
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 ; | |||
OID upsertedId; | OID upsertedId; | |||
skipping to change at line 117 | skipping to change at line 117 | |||
extern class LastErrorHolder { | extern class LastErrorHolder { | |||
public: | public: | |||
LastErrorHolder(){} | LastErrorHolder(){} | |||
~LastErrorHolder(); | ~LastErrorHolder(); | |||
LastError * get( bool create = false ); | LastError * get( bool create = false ); | |||
LastError * getSafe() { | LastError * getSafe() { | |||
LastError * le = get(false); | LastError * le = get(false); | |||
if ( ! le ) { | if ( ! le ) { | |||
error() << " no LastError!" << endl; | error() << " no LastError!" << endl; | |||
assert( le ); | verify( le ); | |||
} | } | |||
return le; | return le; | |||
} | } | |||
LastError * _get( bool create = false ); // may return a disabled L astError | LastError * _get( bool create = false ); // may return a disabled L astError | |||
void reset( LastError * le ); | void reset( LastError * le ); | |||
/** ok to call more than once. */ | /** ok to call more than once. */ | |||
void initThread(); | void initThread(); | |||
skipping to change at line 150 | skipping to change at line 150 | |||
LastError *disableForCommand(); // only call once per command invoc ation! | LastError *disableForCommand(); // only call once per command invoc ation! | |||
private: | private: | |||
boost::thread_specific_ptr<LastError> _tl; | boost::thread_specific_ptr<LastError> _tl; | |||
struct Status { | struct Status { | |||
time_t time; | time_t time; | |||
LastError *lerr; | LastError *lerr; | |||
}; | }; | |||
} lastError; | } lastError; | |||
void raiseError(int code , const char *msg); | void setLastError(int code , const char *msg); | |||
} // namespace mongo | } // namespace mongo | |||
End of changes. 3 change blocks. | ||||
3 lines changed or deleted | 3 lines changed or added | |||
linenoise.h | linenoise.h | |||
---|---|---|---|---|
skipping to change at line 39 | skipping to change at line 39 | |||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF T HE | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF T HE | |||
* POSSIBILITY OF SUCH DAMAGE. | * POSSIBILITY OF SUCH DAMAGE. | |||
*/ | */ | |||
#ifndef __LINENOISE_H | #ifndef __LINENOISE_H | |||
#define __LINENOISE_H | #define __LINENOISE_H | |||
struct linenoiseCompletions { | struct linenoiseCompletions; | |||
int completionCount; | ||||
char * * completionStrings; | ||||
}; | ||||
typedef void( linenoiseCompletionCallback )( const char *, linenoiseComplet ions * ); | typedef void( linenoiseCompletionCallback )( const char *, linenoiseComplet ions * ); | |||
void linenoiseSetCompletionCallback( linenoiseCompletionCallback * fn ); | void linenoiseSetCompletionCallback( linenoiseCompletionCallback * fn ); | |||
void linenoiseAddCompletion( linenoiseCompletions * lc, const char * str ); | void linenoiseAddCompletion( linenoiseCompletions * lc, const char * str ); | |||
char *linenoise( const char* prompt ); | char *linenoise( const char* prompt ); | |||
void linenoisePreloadBuffer( const char* preloadText ); | ||||
int linenoiseHistoryAdd( const char* line ); | int linenoiseHistoryAdd( const char* line ); | |||
int linenoiseHistorySetMaxLen( int len ); | int linenoiseHistorySetMaxLen( int len ); | |||
int linenoiseHistorySave( const char* filename ); | int linenoiseHistorySave( const char* filename ); | |||
int linenoiseHistoryLoad( const char* filename ); | int linenoiseHistoryLoad( const char* filename ); | |||
void linenoiseHistoryFree( void ); | void linenoiseHistoryFree( void ); | |||
void linenoiseClearScreen( void ); | void linenoiseClearScreen( void ); | |||
#endif /* __LINENOISE_H */ | #endif /* __LINENOISE_H */ | |||
End of changes. 2 change blocks. | ||||
4 lines changed or deleted | 2 lines changed or added | |||
list.h | list.h | |||
---|---|---|---|---|
skipping to change at line 66 | skipping to change at line 66 | |||
use(p); | use(p); | |||
and this is not: | and this is not: | |||
if( mylist.head() ) | if( mylist.head() ) | |||
use( mylist.head() ); // could become 0 | use( mylist.head() ); // could become 0 | |||
*/ | */ | |||
T* head() const { return (T*) _head; } | T* head() const { return (T*) _head; } | |||
void push(T* t) { | void push(T* t) { | |||
assert( t->_next == 0 ); | verify( t->_next == 0 ); | |||
scoped_lock lk(_m); | scoped_lock lk(_m); | |||
t->_next = (T*) _head; | t->_next = (T*) _head; | |||
_head = t; | _head = t; | |||
} | } | |||
// intentionally leaks. | // intentionally leaks. | |||
void orphanAll() { | void orphanAll() { | |||
scoped_lock lk(_m); | scoped_lock lk(_m); | |||
_head = 0; | _head = 0; | |||
} | } | |||
End of changes. 1 change blocks. | ||||
1 lines changed or deleted | 1 lines changed or added | |||
listen.h | listen.h | |||
---|---|---|---|---|
skipping to change at line 21 | skipping to change at line 21 | |||
* Unless required by applicable law or agreed to in writing, software | * Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | * distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. | |||
* See the License for the specific language governing permissions and | * See the License for the specific language governing permissions and | |||
* limitations under the License. | * limitations under the License. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "sock.h" | #include "sock.h" | |||
#include "mongo/util/concurrency/ticketholder.h" | ||||
namespace mongo { | namespace mongo { | |||
class MessagingPort; | class MessagingPort; | |||
class Listener : boost::noncopyable { | class Listener : boost::noncopyable { | |||
public: | public: | |||
Listener(const string& name, const string &ip, int port, bool logCo nnect=true ); | Listener(const string& name, const string &ip, int port, bool logCo nnect=true ); | |||
skipping to change at line 46 | skipping to change at line 47 | |||
* ownership of SSLManager remains with the caller | * ownership of SSLManager remains with the caller | |||
*/ | */ | |||
void secure( SSLManager* manager ); | void secure( SSLManager* manager ); | |||
void addSecurePort( SSLManager* manager , int additionalPort ); | void addSecurePort( SSLManager* manager , int additionalPort ); | |||
#endif | #endif | |||
void initAndListen(); // never returns unless error (start a thread ) | void initAndListen(); // never returns unless error (start a thread ) | |||
/* spawn a thread, etc., then return */ | /* spawn a thread, etc., then return */ | |||
virtual void accepted(Socket socket); | virtual void accepted(boost::shared_ptr<Socket> psocket); | |||
virtual void accepted(MessagingPort *mp); | virtual void acceptedMP(MessagingPort *mp); | |||
const int _port; | const int _port; | |||
/** | /** | |||
* @return a rough estimate of elapsed time since the server starte d | * @return a rough estimate of elapsed time since the server starte d | |||
*/ | */ | |||
long long getMyElapsedTimeMillis() const { return _elapsedTime; } | long long getMyElapsedTimeMillis() const { return _elapsedTime; } | |||
void setAsTimeTracker() { | void setAsTimeTracker() { | |||
_timeTracker = this; | _timeTracker = this; | |||
End of changes. 2 change blocks. | ||||
2 lines changed or deleted | 3 lines changed or added | |||
log.h | log.h | |||
---|---|---|---|---|
skipping to change at line 27 | skipping to change at line 27 | |||
#pragma once | #pragma once | |||
#include <string.h> | #include <string.h> | |||
#include <sstream> | #include <sstream> | |||
#include <errno.h> | #include <errno.h> | |||
#include <vector> | #include <vector> | |||
#include <boost/shared_ptr.hpp> | #include <boost/shared_ptr.hpp> | |||
#include <boost/scoped_ptr.hpp> | #include <boost/scoped_ptr.hpp> | |||
#include <boost/thread/tss.hpp> | #include <boost/thread/tss.hpp> | |||
#include "../bson/util/builder.h" | ||||
#include "debug_util.h" | #include "mongo/bson/util/builder.h" | |||
#include "mongo/util/debug_util.h" | ||||
#include "mongo/util/exit_code.h" | ||||
#ifndef _WIN32 | #ifndef _WIN32 | |||
#include <syslog.h> | #include <syslog.h> | |||
#endif | #endif | |||
namespace mongo { | namespace mongo { | |||
enum ExitCode; | enum ExitCode; | |||
enum LogLevel { LL_DEBUG , LL_INFO , LL_NOTICE , LL_WARNING , LL_ERROR , LL_SEVERE }; | enum LogLevel { LL_DEBUG , LL_INFO , LL_NOTICE , LL_WARNING , LL_ERROR , LL_SEVERE }; | |||
skipping to change at line 202 | skipping to change at line 204 | |||
} | } | |||
template< class T > | template< class T > | |||
Nullstream& operator<<(T *t) { | Nullstream& operator<<(T *t) { | |||
return operator<<( static_cast<void*>( t ) ); | return operator<<( static_cast<void*>( t ) ); | |||
} | } | |||
template< class T > | template< class T > | |||
Nullstream& operator<<(const T *t) { | Nullstream& operator<<(const T *t) { | |||
return operator<<( static_cast<const void*>( t ) ); | return operator<<( static_cast<const void*>( t ) ); | |||
} | } | |||
template< class T > | template< class T > | |||
Nullstream& operator<<(const shared_ptr<T> p ) { | Nullstream& operator<<(const boost::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; | |||
} | } | |||
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<< (std::ostream& ( *endl )(std::ostrea m&)) { | |||
return *this; | return *this; | |||
} | } | |||
virtual Nullstream& operator<< (ios_base& (*hex)(ios_base&)) { | virtual Nullstream& operator<< (std::ios_base& (*hex)(std::ios_base &)) { | |||
return *this; | return *this; | |||
} | } | |||
virtual void flush(Tee *t = 0) {} | virtual void flush(Tee *t = 0) {} | |||
}; | }; | |||
extern Nullstream nullstream; | extern Nullstream nullstream; | |||
class mutex; | ||||
class Logstream : public Nullstream { | class Logstream : public Nullstream { | |||
static mongo::mutex mutex; | static mongo::mutex mutex; | |||
static int doneSetup; | static int doneSetup; | |||
stringstream ss; | std::stringstream ss; | |||
int indent; | int indent; | |||
LogLevel logLevel; | LogLevel logLevel; | |||
static FILE* logfile; | static FILE* logfile; | |||
static boost::scoped_ptr<ostream> stream; | static boost::scoped_ptr<std::ostream> stream; | |||
static vector<Tee*> * globalTees; | static std::vector<Tee*> * globalTees; | |||
static bool isSyslog; | static bool isSyslog; | |||
public: | public: | |||
static void logLockless( const StringData& s ); | static void logLockless( const StringData& s ); | |||
static void setLogFile(FILE* f); | static void setLogFile(FILE* f); | |||
#ifndef _WIN32 | #ifndef _WIN32 | |||
static void useSyslog(const char * name) { | static void useSyslog(const char * name) { | |||
cout << "using syslog ident: " << name << endl; | std::cout << "using syslog ident: " << name << std::endl; | |||
// openlog requires heap allocated non changing pointer | // openlog requires heap allocated non changing pointer | |||
// this should only be called once per pragram execution | // this should only be called once per pragram execution | |||
char * newName = (char *) malloc( strlen(name) + 1 ); | char * newName = (char *) malloc( strlen(name) + 1 ); | |||
strcpy( newName , name); | strcpy( newName , name); | |||
openlog( newName , LOG_ODELAY , LOG_USER ); | openlog( newName , LOG_ODELAY , LOG_USER ); | |||
isSyslog = true; | isSyslog = true; | |||
} | } | |||
#endif | #endif | |||
skipping to change at line 305 | skipping to change at line 305 | |||
Logstream& operator<<(const LazyString& x) { | Logstream& operator<<(const LazyString& x) { | |||
ss << x.val(); | ss << x.val(); | |||
return *this; | return *this; | |||
} | } | |||
Nullstream& operator<< (Tee* tee) { | Nullstream& operator<< (Tee* tee) { | |||
ss << '\n'; | ss << '\n'; | |||
flush(tee); | flush(tee); | |||
return *this; | return *this; | |||
} | } | |||
Logstream& operator<< (ostream& ( *_endl )(ostream&)) { | Logstream& operator<< (std::ostream& ( *_endl )(std::ostream&)) { | |||
ss << '\n'; | ss << '\n'; | |||
flush(0); | flush(0); | |||
return *this; | return *this; | |||
} | } | |||
Logstream& operator<< (ios_base& (*_hex)(ios_base&)) { | Logstream& operator<< (std::ios_base& (*_hex)(std::ios_base&)) { | |||
ss << _hex; | ss << _hex; | |||
return *this; | return *this; | |||
} | } | |||
Logstream& prolog() { | Logstream& prolog() { | |||
return *this; | return *this; | |||
} | } | |||
void addGlobalTee( Tee * t ) { | void addGlobalTee( Tee * t ) { | |||
if ( ! globalTees ) | if ( ! globalTees ) | |||
globalTees = new vector<Tee*>(); | globalTees = new std::vector<Tee*>(); | |||
globalTees->push_back( t ); | globalTees->push_back( t ); | |||
} | } | |||
void removeGlobalTee( Tee * tee ); | ||||
void indentInc(){ indent++; } | void indentInc(){ indent++; } | |||
void indentDec(){ indent--; } | void indentDec(){ indent--; } | |||
int getIndent() const { return indent; } | int getIndent() const { return indent; } | |||
private: | private: | |||
static boost::thread_specific_ptr<Logstream> tsp; | static boost::thread_specific_ptr<Logstream> tsp; | |||
Logstream() { | Logstream() { | |||
indent = 0; | indent = 0; | |||
_init(); | _init(); | |||
} | } | |||
skipping to change at line 368 | skipping to change at line 370 | |||
/* without prolog */ | /* without prolog */ | |||
inline Nullstream& _log( int level = 0 ) { | inline Nullstream& _log( int level = 0 ) { | |||
if ( level > logLevel ) | if ( level > logLevel ) | |||
return nullstream; | return nullstream; | |||
return Logstream::get(); | return Logstream::get(); | |||
} | } | |||
/** logging which we may not want during unit tests (dbtests) runs. | /** logging which we may not want during unit tests (dbtests) runs. | |||
set tlogLevel to -1 to suppress tlog() output in a test program. */ | set tlogLevel to -1 to suppress tlog() output in a test program. */ | |||
inline Nullstream& tlog( int level = 0 ) { | Nullstream& tlog( int level = 0 ); | |||
if ( level > tlogLevel || level > logLevel ) | ||||
return nullstream; | ||||
return Logstream::get().prolog(); | ||||
} | ||||
// log if debug build or if at a certain level | // log if debug build or if at a certain level | |||
inline Nullstream& dlog( int level ) { | inline Nullstream& dlog( int level ) { | |||
if ( level <= logLevel || DEBUG_BUILD ) | if ( level <= logLevel || DEBUG_BUILD ) | |||
return Logstream::get().prolog(); | return Logstream::get().prolog(); | |||
return nullstream; | return nullstream; | |||
} | } | |||
inline Nullstream& log( int level ) { | inline Nullstream& log( int level ) { | |||
if ( level > logLevel ) | if ( level > logLevel ) | |||
skipping to change at line 428 | skipping to change at line 426 | |||
return nullstream; | return nullstream; | |||
Logstream& l = Logstream::get().prolog(); | Logstream& l = Logstream::get().prolog(); | |||
l << ' ' << getcurns() << ' '; | l << ' ' << getcurns() << ' '; | |||
return l; | return l; | |||
} | } | |||
/** | /** | |||
log to a file rather than stdout | log to a file rather than stdout | |||
defined in assert_util.cpp | defined in assert_util.cpp | |||
*/ | */ | |||
void initLogging( const string& logpath , bool append ); | bool initLogging( const string& logpath , bool append ); | |||
void rotateLogs( int signal = 0 ); | bool rotateLogs(); | |||
std::string toUtf8String(const std::wstring& wide); | std::string toUtf8String(const std::wstring& wide); | |||
/** output the error # and error message with prefix. | /** output the error # and error message with prefix. | |||
handy for use as parm in uassert/massert. | handy for use as parm in uassert/massert. | |||
*/ | */ | |||
string errnoWithPrefix( const char * prefix ); | string errnoWithPrefix( const char * prefix ); | |||
struct LogIndentLevel { | struct LogIndentLevel { | |||
LogIndentLevel(){ | LogIndentLevel(){ | |||
Logstream::get().indentInc(); | Logstream::get().indentInc(); | |||
} | } | |||
~LogIndentLevel(){ | ~LogIndentLevel(){ | |||
Logstream::get().indentDec(); | Logstream::get().indentDec(); | |||
} | } | |||
}; | }; | |||
extern Tee* const warnings; // Things put here go in serverStatus | extern Tee* const warnings; // Things put here go in serverStatus | |||
string errnoWithDescription(int errorcode = -1); | string errnoWithDescription(int errorcode = -1); | |||
void rawOut( const string &s ); | ||||
} // namespace mongo | } // namespace mongo | |||
End of changes. 15 change blocks. | ||||
21 lines changed or deleted | 20 lines changed or added | |||
lruishmap.h | lruishmap.h | |||
---|---|---|---|---|
skipping to change at line 46 | skipping to change at line 46 | |||
hashes = new int[n]; | hashes = new int[n]; | |||
for ( int i = 0; i < n; i++ ) hashes[i] = 0; | for ( int i = 0; i < n; i++ ) hashes[i] = 0; | |||
} | } | |||
~LRUishMap() { | ~LRUishMap() { | |||
delete[] keys; | delete[] keys; | |||
delete[] hashes; | delete[] hashes; | |||
} | } | |||
int _find(const K& k, bool& found) { | int _find(const K& k, bool& found) { | |||
int h = k.hash(); | int h = k.hash(); | |||
assert( h > 0 ); | verify( h > 0 ); | |||
int j = h % n; | int j = h % n; | |||
int first = j; | int first = j; | |||
for ( int i = 0; i < MaxChain; i++ ) { | for ( int i = 0; i < MaxChain; i++ ) { | |||
if ( hashes[j] == h ) { | if ( hashes[j] == h ) { | |||
if ( keys[j] == k ) { | if ( keys[j] == k ) { | |||
found = true; | found = true; | |||
return j; | return j; | |||
} | } | |||
} | } | |||
else if ( hashes[j] == 0 ) { | else if ( hashes[j] == 0 ) { | |||
End of changes. 1 change blocks. | ||||
1 lines changed or deleted | 1 lines changed or added | |||
matcher.h | matcher.h | |||
---|---|---|---|---|
skipping to change at line 89 | skipping to change at line 89 | |||
shared_ptr<Matcher> _subMatcher; | shared_ptr<Matcher> _subMatcher; | |||
bool _subMatcherOnPrimitives ; | bool _subMatcherOnPrimitives ; | |||
vector< shared_ptr<Matcher> > _allMatchers; | vector< shared_ptr<Matcher> > _allMatchers; | |||
}; | }; | |||
class Where; // used for $where javascript eval | class Where; // used for $where javascript eval | |||
class DiskLoc; | class DiskLoc; | |||
struct MatchDetails { | /** Reports information about a match request. */ | |||
MatchDetails() { | class MatchDetails { | |||
reset(); | public: | |||
} | MatchDetails(); | |||
void resetOutput(); | ||||
string toString() const; | ||||
void reset() { | /** Request that an elemMatchKey be recorded. */ | |||
_loadedObject = false; | void requestElemMatchKey() { _elemMatchKeyRequested = true; } | |||
_elemMatchKey = 0; | ||||
} | ||||
string toString() const { | bool needRecord() const { return _elemMatchKeyRequested; } | |||
stringstream ss; | ||||
ss << "loadedObject: " << _loadedObject << " "; | bool hasLoadedRecord() const { return _loadedRecord; } | |||
ss << "elemMatchKey: " << ( _elemMatchKey ? _elemMatchKey : "NU | bool hasElemMatchKey() const { return _elemMatchKeyFound; } | |||
LL" ) << " "; | string elemMatchKey() const { | |||
return ss.str(); | verify( hasElemMatchKey() ); | |||
return _elemMatchKey; | ||||
} | ||||
void setLoadedRecord( bool loadedRecord ) { _loadedRecord = loadedR | ||||
ecord; } | ||||
void setElemMatchKey( const string &elemMatchKey ) { | ||||
if ( _elemMatchKeyRequested ) { | ||||
_elemMatchKeyFound = true; | ||||
_elemMatchKey = elemMatchKey; | ||||
} | ||||
} | } | |||
bool _loadedObject; | private: | |||
const char * _elemMatchKey; // warning, this may go out of scope if | bool _loadedRecord; | |||
matched object does | bool _elemMatchKeyRequested; | |||
bool _elemMatchKeyFound; | ||||
string _elemMatchKey; | ||||
}; | }; | |||
/* Match BSON objects against a query pattern. | /* Match BSON objects against a query pattern. | |||
e.g. | e.g. | |||
db.foo.find( { a : 3 } ); | db.foo.find( { a : 3 } ); | |||
{ a : 3 } is the pattern object. See wiki documentation for full in fo. | { a : 3 } is the pattern object. See wiki documentation for full in fo. | |||
GT/LT: | GT/LT: | |||
skipping to change at line 150 | skipping to change at line 163 | |||
static int opDirection(int op) { | static int opDirection(int op) { | |||
return op <= BSONObj::LTE ? -1 : 1; | return op <= BSONObj::LTE ? -1 : 1; | |||
} | } | |||
Matcher(const BSONObj &pattern, bool nested=false); | Matcher(const BSONObj &pattern, bool nested=false); | |||
~Matcher(); | ~Matcher(); | |||
bool matches(const BSONObj& j, MatchDetails * details = 0 ) const; | bool matches(const BSONObj& j, MatchDetails * details = 0 ) const; | |||
#ifdef MONGO_LATER_SERVER_4644 | ||||
class FieldSink { | ||||
public: | ||||
virtual ~FieldSink() {}; | ||||
virtual void referenceField(const string &fieldPath) = 0; | ||||
}; | ||||
/** | ||||
Visit all of the fields that are referenced by this Matcher | ||||
(and any descendants). | ||||
This can be used to gather a list of all the references made by | ||||
this matcher. The implementation of this parallels that of | ||||
matches() above. | ||||
@param pSink a FieldSink that the caller will use to gather or | ||||
process the references | ||||
*/ | ||||
void visitReferences(FieldSink *pSink) const; | ||||
#endif /* MONGO_LATER_SERVER_4644 */ | ||||
bool atomic() const { return _atomic; } | bool atomic() const { return _atomic; } | |||
string toString() const { | string toString() const { | |||
return _jsobj.toString(); | return _jsobj.toString(); | |||
} | } | |||
void addOrDedupConstraint( const shared_ptr< FieldRangeVector > &fr v ) { | void addOrDedupConstraint( const shared_ptr< FieldRangeVector > &fr v ) { | |||
_orDedupConstraints.push_back( frv ); | _orDedupConstraints.push_back( frv ); | |||
} | } | |||
skipping to change at line 178 | skipping to change at line 212 | |||
bool keyMatch( const Matcher &docMatcher ) const; | bool keyMatch( const Matcher &docMatcher ) const; | |||
bool singleSimpleCriterion() const { | bool singleSimpleCriterion() const { | |||
return false; // TODO SERVER-958 | return false; // TODO SERVER-958 | |||
// // TODO Really check, especially if all basics are ok. | // // TODO Really check, especially if all basics are ok. | |||
// // $all, etc | // // $all, etc | |||
// // _orConstraints? | // // _orConstraints? | |||
// return ( ( basics.size() + nRegex ) < 2 ) && !where && !_orMa tchers.size() && !_norMatchers.size(); | // return ( ( basics.size() + nRegex ) < 2 ) && !where && !_orMa tchers.size() && !_norMatchers.size(); | |||
} | } | |||
const BSONObj *getQuery() const { return &_jsobj; }; | const BSONObj *getQuery() const { return &_jsobj; }; | |||
private: | private: | |||
/** | /** | |||
* Generate a matcher for the provided index key format using the | * Generate a matcher for the provided index key format using the | |||
* provided full doc matcher. | * provided full doc matcher. | |||
*/ | */ | |||
Matcher( const Matcher &docMatcher, const BSONObj &constrainIndexKe y ); | Matcher( const Matcher &docMatcher, const BSONObj &constrainIndexKe y ); | |||
void addBasic(const BSONElement &e, int c, bool isNot) { | void addBasic(const BSONElement &e, int c, bool isNot) { | |||
// TODO May want to selectively ignore these element types base d on op type. | // TODO May want to selectively ignore these element types base d on op type. | |||
skipping to change at line 236 | skipping to change at line 270 | |||
list< shared_ptr< Matcher > > _orMatchers; | list< shared_ptr< Matcher > > _orMatchers; | |||
list< shared_ptr< Matcher > > _norMatchers; | list< shared_ptr< Matcher > > _norMatchers; | |||
vector< shared_ptr< FieldRangeVector > > _orDedupConstraints; | vector< shared_ptr< FieldRangeVector > > _orDedupConstraints; | |||
friend class CoveredIndexMatcher; | friend class CoveredIndexMatcher; | |||
}; | }; | |||
// If match succeeds on index key, then attempt to match full document. | // If match succeeds on index key, then attempt to match full document. | |||
class CoveredIndexMatcher : boost::noncopyable { | class CoveredIndexMatcher : boost::noncopyable { | |||
public: | public: | |||
CoveredIndexMatcher(const BSONObj &pattern, const BSONObj &indexKey | CoveredIndexMatcher(const BSONObj &pattern, const BSONObj &indexKey | |||
Pattern , bool alwaysUseRecord=false ); | Pattern); | |||
bool matches(const BSONObj &o) { return _docMatcher->matches( o ); | ||||
} | ||||
bool matchesWithSingleKeyIndex(const BSONObj &key, const DiskLoc &r ecLoc , MatchDetails * details = 0 ) { | bool matchesWithSingleKeyIndex(const BSONObj &key, const DiskLoc &r ecLoc , MatchDetails * details = 0 ) { | |||
return matches( key, recLoc, details, true ); | return matches( key, recLoc, details, true ); | |||
} | } | |||
/** | /** | |||
* This is the preferred method for matching against a cursor, as i t | * This is the preferred method for matching against a cursor, as i t | |||
* can handle both multi and single key cursors. | * can handle both multi and single key cursors. | |||
*/ | */ | |||
bool matchesCurrent( Cursor * cursor , MatchDetails * details = 0 ) ; | bool matchesCurrent( Cursor * cursor , MatchDetails * details = 0 ) ; | |||
bool needRecord() { return _needRecord; } | bool needRecord() { return _needRecord; } | |||
skipping to change at line 259 | skipping to change at line 292 | |||
// once this is called, shouldn't use this matcher for matching any more | // once this is called, shouldn't use this matcher for matching any more | |||
void advanceOrClause( const shared_ptr< FieldRangeVector > &frv ) { | void advanceOrClause( const shared_ptr< FieldRangeVector > &frv ) { | |||
_docMatcher->addOrDedupConstraint( frv ); | _docMatcher->addOrDedupConstraint( frv ); | |||
// TODO this is not yet optimal. Since we could skip an entire | // TODO this is not yet optimal. Since we could skip an entire | |||
// or clause (if a match is impossible) between calls to advanc eOrClause() | // or clause (if a match is impossible) between calls to advanc eOrClause() | |||
// we may not pop all the clauses we can. | // we may not pop all the clauses we can. | |||
_docMatcher->popOrClause(); | _docMatcher->popOrClause(); | |||
} | } | |||
CoveredIndexMatcher *nextClauseMatcher( const BSONObj &indexKeyPatt | CoveredIndexMatcher *nextClauseMatcher( const BSONObj &indexKeyPatt | |||
ern, bool alwaysUseRecord=false ) { | ern ) { | |||
return new CoveredIndexMatcher( _docMatcher, indexKeyPattern, a | return new CoveredIndexMatcher( _docMatcher, indexKeyPattern ); | |||
lwaysUseRecord ); | ||||
} | } | |||
string toString() const; | string toString() const; | |||
private: | private: | |||
bool matches(const BSONObj &key, const DiskLoc &recLoc , MatchDetai ls * details = 0 , bool keyUsable = true ); | bool matches(const BSONObj &key, const DiskLoc &recLoc , MatchDetai ls * details = 0 , bool keyUsable = true ); | |||
CoveredIndexMatcher(const shared_ptr< Matcher > &docMatcher, const | CoveredIndexMatcher(const shared_ptr< Matcher > &docMatcher, const | |||
BSONObj &indexKeyPattern , bool alwaysUseRecord=false ); | BSONObj &indexKeyPattern); | |||
void init( bool alwaysUseRecord ); | void init(); | |||
shared_ptr< Matcher > _docMatcher; | shared_ptr< Matcher > _docMatcher; | |||
Matcher _keyMatcher; | Matcher _keyMatcher; | |||
bool _needRecord; // if the key itself isn't good enough to determi ne a positive match | bool _needRecord; // if the key itself isn't good enough to determi ne a positive match | |||
}; | }; | |||
} // namespace mongo | } // namespace mongo | |||
End of changes. 9 change blocks. | ||||
29 lines changed or deleted | 59 lines changed or added | |||
memconcept.h | memconcept.h | |||
---|---|---|---|---|
#pragma once | #pragma once | |||
/* The idea here is to 'name' memory pointers so that we can do diagnostics | ||||
. | ||||
these diagnostics might involve concurrency or other things. mainly wou | ||||
ld | ||||
be for _DEBUG builds. Experimental we'll see how useful. | ||||
*/ | ||||
namespace mongo { | namespace mongo { | |||
namespace memconcept { | namespace memconcept { | |||
enum concept { | /** these are like fancy enums - you can use them as "types" of thi | |||
err, | ngs | |||
something, | and see if foo.concept == bar.concept. | |||
database, | copyable. | |||
other, | */ | |||
memorymappedfile, | class concept { | |||
nsdetails, | public: | |||
datafileheader, | concept() { *this = err; } | |||
extent, | const char * toString() const { return c; } | |||
record, | static concept err; | |||
deletedrecord | static concept something; | |||
static concept database; | ||||
static concept other; | ||||
static concept memorymappedfile; | ||||
static concept nsdetails; | ||||
static concept datafileheader; | ||||
static concept extent; | ||||
static concept record; | ||||
static concept deletedrecord; | ||||
static concept btreebucket; | ||||
private: | ||||
const char * c; | ||||
concept(const char *); | ||||
}; | }; | |||
/** file was unmapped or something */ | /** file was unmapped or something */ | |||
void invalidate(void *p, unsigned len); | void invalidate(void *p, unsigned len=0); | |||
void invalidate(void *p); | ||||
/** note you can be more than one thing; a datafile header is also the starting pointer | /** note you can be more than one thing; a datafile header is also the starting pointer | |||
for a file */ | for a file */ | |||
void is(void *p, concept c, std::string desc = "", unsigned len=0); | void is(void *p, concept c, std::string desc = "", unsigned len=0); | |||
#if 1 | #if 1 | |||
//#if !defined(_DEBUG) | //#if !defined(_DEBUG) | |||
inline void invalidate(void *p, unsigned len) { } | inline void invalidate(void *p, unsigned len) { } | |||
inline void invalidate(void *p) { } | ||||
inline void is(void *p, concept c, std::string, unsigned) { } | inline void is(void *p, concept c, std::string, unsigned) { } | |||
#endif | #endif | |||
} | } | |||
} | } | |||
End of changes. 4 change blocks. | ||||
14 lines changed or deleted | 31 lines changed or added | |||
message.h | message.h | |||
---|---|---|---|---|
skipping to change at line 58 | skipping to change at line 58 | |||
case 0: return "none"; | case 0: return "none"; | |||
case opReply: return "reply"; | case opReply: return "reply"; | |||
case dbMsg: return "msg"; | case dbMsg: return "msg"; | |||
case dbUpdate: return "update"; | case dbUpdate: return "update"; | |||
case dbInsert: return "insert"; | case dbInsert: return "insert"; | |||
case dbQuery: return "query"; | case dbQuery: return "query"; | |||
case dbGetMore: return "getmore"; | case dbGetMore: return "getmore"; | |||
case dbDelete: return "remove"; | case dbDelete: return "remove"; | |||
case dbKillCursors: return "killcursors"; | case dbKillCursors: return "killcursors"; | |||
default: | default: | |||
PRINT(op); | massert( 16141, str::stream() << "cannot translate opcode " << | |||
assert(0); | op, !op ); | |||
return ""; | return ""; | |||
} | } | |||
} | } | |||
inline bool opIsWrite( int op ) { | inline bool opIsWrite( int op ) { | |||
switch ( op ) { | switch ( op ) { | |||
case 0: | case 0: | |||
case opReply: | case opReply: | |||
case dbMsg: | case dbMsg: | |||
skipping to change at line 82 | skipping to change at line 81 | |||
case dbKillCursors: | case dbKillCursors: | |||
return false; | return false; | |||
case dbUpdate: | case dbUpdate: | |||
case dbInsert: | case dbInsert: | |||
case dbDelete: | case dbDelete: | |||
return true; | return true; | |||
default: | default: | |||
PRINT(op); | PRINT(op); | |||
assert(0); | verify(0); | |||
return ""; | return ""; | |||
} | } | |||
} | } | |||
#pragma pack(1) | #pragma pack(1) | |||
/* see http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol | /* see http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol | |||
*/ | */ | |||
struct MSGHEADER { | struct MSGHEADER { | |||
int messageLength; // total message size, including this | int messageLength; // total message size, including this | |||
int requestID; // identifier for this message | int requestID; // identifier for this message | |||
int responseTo; // requestID from the original request | int responseTo; // requestID from the original request | |||
// (used in reponses from db) | // (used in reponses from db) | |||
int opCode; | int opCode; | |||
}; | }; | |||
struct OP_GETMORE : public MSGHEADER { | ||||
MSGHEADER header; // standard message header | ||||
int ZERO_or_flags; // 0 - reserved for future use | ||||
//cstring fullCollectionName; // "dbname.collectionname" | ||||
//int32 numberToReturn; // number of documents to return | ||||
//int64 cursorID; // cursorID from the OP_REPLY | ||||
}; | ||||
#pragma pack() | #pragma pack() | |||
#pragma pack(1) | #pragma pack(1) | |||
/* todo merge this with MSGHEADER (or inherit from it). */ | /* todo merge this with MSGHEADER (or inherit from it). */ | |||
struct MsgData { | struct MsgData { | |||
int len; /* len of the msg, including this field */ | int len; /* len of the msg, including this field */ | |||
MSGID id; /* request/reply id's match... */ | MSGID id; /* request/reply id's match... */ | |||
MSGID responseTo; /* id of the message we are responding to */ | MSGID responseTo; /* id of the message we are responding to */ | |||
short _operation; | short _operation; | |||
char _flags; | char _flags; | |||
skipping to change at line 139 | skipping to change at line 131 | |||
bool valid() { | bool valid() { | |||
if ( len <= 0 || len > ( 4 * BSONObjMaxInternalSize ) ) | if ( len <= 0 || len > ( 4 * BSONObjMaxInternalSize ) ) | |||
return false; | return false; | |||
if ( _operation < 0 || _operation > 30000 ) | if ( _operation < 0 || _operation > 30000 ) | |||
return false; | return false; | |||
return true; | return true; | |||
} | } | |||
long long getCursor() { | long long getCursor() { | |||
assert( responseTo > 0 ); | verify( responseTo > 0 ); | |||
assert( _operation == opReply ); | verify( _operation == opReply ); | |||
long long * l = (long long *)(_data + 4); | long long * l = (long long *)(_data + 4); | |||
return l[0]; | return l[0]; | |||
} | } | |||
int dataLen(); // len without header | int dataLen(); // len without header | |||
}; | }; | |||
const int MsgDataHeaderSize = sizeof(MsgData) - 4; | const int MsgDataHeaderSize = sizeof(MsgData) - 4; | |||
inline int MsgData::dataLen() { | inline int MsgData::dataLen() { | |||
return len - MsgDataHeaderSize; | return len - MsgDataHeaderSize; | |||
} | } | |||
skipping to change at line 171 | skipping to change at line 163 | |||
Message(Message& r) : _buf( 0 ), _data( 0 ), _freeIt( false ) { | Message(Message& r) : _buf( 0 ), _data( 0 ), _freeIt( false ) { | |||
*this = r; | *this = r; | |||
} | } | |||
~Message() { | ~Message() { | |||
reset(); | reset(); | |||
} | } | |||
SockAddr _from; | SockAddr _from; | |||
MsgData *header() const { | MsgData *header() const { | |||
assert( !empty() ); | verify( !empty() ); | |||
return _buf ? _buf : reinterpret_cast< MsgData* > ( _data[ 0 ]. first ); | return _buf ? _buf : reinterpret_cast< MsgData* > ( _data[ 0 ]. first ); | |||
} | } | |||
int operation() const { return header()->operation(); } | int operation() const { return header()->operation(); } | |||
MsgData *singleData() const { | MsgData *singleData() const { | |||
massert( 13273, "single data buffer expected", _buf ); | massert( 13273, "single data buffer expected", _buf ); | |||
return header(); | return header(); | |||
} | } | |||
bool empty() const { return !_buf && _data.empty(); } | bool empty() const { return !_buf && _data.empty(); } | |||
skipping to change at line 205 | skipping to change at line 197 | |||
int dataSize() const { return size() - sizeof(MSGHEADER); } | int dataSize() const { return size() - sizeof(MSGHEADER); } | |||
// concat multiple buffers - noop if <2 buffers already, otherwise can be expensive copy | // concat multiple buffers - noop if <2 buffers already, otherwise can be expensive copy | |||
// can get rid of this if we make response handling smarter | // can get rid of this if we make response handling smarter | |||
void concat() { | void concat() { | |||
if ( _buf || empty() ) { | if ( _buf || empty() ) { | |||
return; | return; | |||
} | } | |||
assert( _freeIt ); | verify( _freeIt ); | |||
int totalSize = 0; | int totalSize = 0; | |||
for( vector< pair< char *, int > >::const_iterator i = _data.be gin(); i != _data.end(); ++i ) { | for( vector< pair< char *, int > >::const_iterator i = _data.be gin(); i != _data.end(); ++i ) { | |||
totalSize += i->second; | totalSize += i->second; | |||
} | } | |||
char *buf = (char*)malloc( totalSize ); | char *buf = (char*)malloc( totalSize ); | |||
char *p = buf; | char *p = buf; | |||
for( vector< pair< char *, int > >::const_iterator i = _data.be gin(); i != _data.end(); ++i ) { | for( vector< pair< char *, int > >::const_iterator i = _data.be gin(); i != _data.end(); ++i ) { | |||
memcpy( p, i->first, i->second ); | memcpy( p, i->first, i->second ); | |||
p += i->second; | p += i->second; | |||
} | } | |||
reset(); | reset(); | |||
_setData( (MsgData*)buf, true ); | _setData( (MsgData*)buf, true ); | |||
} | } | |||
// vector swap() so this is fast | // vector swap() so this is fast | |||
Message& operator=(Message& r) { | Message& operator=(Message& r) { | |||
assert( empty() ); | verify( empty() ); | |||
assert( r._freeIt ); | verify( r._freeIt ); | |||
_buf = r._buf; | _buf = r._buf; | |||
r._buf = 0; | r._buf = 0; | |||
if ( r._data.size() > 0 ) { | if ( r._data.size() > 0 ) { | |||
_data.swap( r._data ); | _data.swap( r._data ); | |||
} | } | |||
r._freeIt = false; | r._freeIt = false; | |||
_freeIt = true; | _freeIt = true; | |||
return *this; | return *this; | |||
} | } | |||
skipping to change at line 260 | skipping to change at line 252 | |||
void appendData(char *d, int size) { | void appendData(char *d, int size) { | |||
if ( size <= 0 ) { | if ( size <= 0 ) { | |||
return; | return; | |||
} | } | |||
if ( empty() ) { | if ( empty() ) { | |||
MsgData *md = (MsgData*)d; | MsgData *md = (MsgData*)d; | |||
md->len = size; // can be updated later if more buffers add ed | md->len = size; // can be updated later if more buffers add ed | |||
_setData( md, true ); | _setData( md, true ); | |||
return; | return; | |||
} | } | |||
assert( _freeIt ); | verify( _freeIt ); | |||
if ( _buf ) { | if ( _buf ) { | |||
_data.push_back( make_pair( (char*)_buf, _buf->len ) ); | _data.push_back( make_pair( (char*)_buf, _buf->len ) ); | |||
_buf = 0; | _buf = 0; | |||
} | } | |||
_data.push_back( make_pair( d, size ) ); | _data.push_back( make_pair( d, size ) ); | |||
header()->len += size; | header()->len += size; | |||
} | } | |||
// use to set first buffer if empty | // use to set first buffer if empty | |||
void setData(MsgData *d, bool freeIt) { | void setData(MsgData *d, bool freeIt) { | |||
assert( empty() ); | verify( empty() ); | |||
_setData( d, freeIt ); | _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( empty() ); | verify( 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); | |||
_setData( d, true ); | _setData( d, true ); | |||
} | } | |||
bool doIFreeIt() { | bool doIFreeIt() { | |||
return _freeIt; | return _freeIt; | |||
End of changes. 10 change blocks. | ||||
19 lines changed or deleted | 12 lines changed or added | |||
message_port.h | message_port.h | |||
---|---|---|---|---|
skipping to change at line 50 | skipping to change at line 50 | |||
virtual void assertStillConnected() = 0; | virtual void assertStillConnected() = 0; | |||
public: | public: | |||
// TODO make this private with some helpers | // TODO make this private with some helpers | |||
/* ports can be tagged with various classes. see closeAllSockets(t ag). defaults to 0. */ | /* ports can be tagged with various classes. see closeAllSockets(t ag). defaults to 0. */ | |||
unsigned tag; | unsigned tag; | |||
}; | }; | |||
class MessagingPort : public AbstractMessagingPort , public Socket { | class MessagingPort : public AbstractMessagingPort { | |||
public: | public: | |||
MessagingPort(int fd, const SockAddr& remote); | MessagingPort(int fd, const SockAddr& remote); | |||
// in some cases the timeout will actually be 2x this value - eg we do a partial send, | // in some cases the timeout will actually be 2x this value - eg we do a partial send, | |||
// then the timeout fires, then we try to send again, then the time out fires again with | // then the timeout fires, then we try to send again, then the time out fires again with | |||
// no data sent, then we detect that the other side is down | // no data sent, then we detect that the other side is down | |||
MessagingPort(double so_timeout = 0, int logLevel = 0 ); | MessagingPort(double so_timeout = 0, int logLevel = 0 ); | |||
MessagingPort(Socket& socket); | MessagingPort(boost::shared_ptr<Socket> socket); | |||
virtual ~MessagingPort(); | virtual ~MessagingPort(); | |||
void shutdown(); | void shutdown(); | |||
/* it's assumed if you reuse a message object, that it doesn't cros s MessagingPort's. | /* it's assumed if you reuse a message object, that it doesn't cros s MessagingPort's. | |||
also, the Message data will go out of scope on the subsequent re cv call. | also, the Message data will go out of scope on the subsequent re cv call. | |||
*/ | */ | |||
bool recv(Message& m); | bool recv(Message& m); | |||
void reply(Message& received, Message& response, MSGID responseTo); | void reply(Message& received, Message& response, MSGID responseTo); | |||
skipping to change at line 88 | skipping to change at line 88 | |||
* you would do | * you would do | |||
* say( to ) | * say( to ) | |||
* recv( from ) | * recv( from ) | |||
* Note: if you fail to call recv and someone else uses this port, | * Note: if you fail to call recv and someone else uses this port, | |||
* horrible things will happend | * horrible things will happend | |||
*/ | */ | |||
bool recv( const Message& sent , Message& response ); | bool recv( const Message& sent , Message& response ); | |||
void piggyBack( Message& toSend , int responseTo = -1 ); | void piggyBack( Message& toSend , int responseTo = -1 ); | |||
unsigned remotePort() const { return Socket::remotePort(); } | unsigned remotePort() const { return psock->remotePort(); } | |||
virtual HostAndPort remote() const; | virtual HostAndPort remote() const; | |||
void assertStillConnected(); | void assertStillConnected(); | |||
boost::shared_ptr<Socket> psock; | ||||
void send( const char * data , int len, const char *context ) { | ||||
psock->send( data, len, context ); | ||||
} | ||||
void send( const vector< pair< char *, int > > &data, const char *c | ||||
ontext ) { | ||||
psock->send( data, context ); | ||||
} | ||||
bool connect(SockAddr& farEnd) { | ||||
return psock->connect( farEnd ); | ||||
} | ||||
#ifdef MONGO_SSL | ||||
/** secures inline */ | ||||
void secure( SSLManager * ssl ) { | ||||
psock->secure( ssl ); | ||||
} | ||||
#endif | ||||
private: | private: | |||
PiggyBackData * piggyBackData; | PiggyBackData * piggyBackData; | |||
// this is the parsed version of remote | // this is the parsed version of remote | |||
// mutable because its initialized only on call to remote() | // mutable because its initialized only on call to remote() | |||
mutable HostAndPort _remoteParsed; | mutable HostAndPort _remoteParsed; | |||
public: | public: | |||
static void closeAllSockets(unsigned tagMask = 0xffffffff); | static void closeAllSockets(unsigned tagMask = 0xffffffff); | |||
End of changes. 4 change blocks. | ||||
3 lines changed or deleted | 22 lines changed or added | |||
minilex.h | minilex.h | |||
---|---|---|---|---|
skipping to change at line 117 | skipping to change at line 117 | |||
p++; | p++; | |||
while ( *p && *p != '"' ) p++; | while ( *p && *p != '"' ) p++; | |||
} | } | |||
p++; | p++; | |||
} | } | |||
} | } | |||
MiniLex() { | MiniLex() { | |||
strhashmap atest; | strhashmap atest; | |||
atest["foo"] = 3; | atest["foo"] = 3; | |||
assert( atest.count("bar") == 0 ); | verify( atest.count("bar") == 0 ); | |||
assert( atest.count("foo") == 1 ); | verify( atest.count("foo") == 1 ); | |||
assert( atest["foo"] == 3 ); | verify( atest["foo"] == 3 ); | |||
for ( int i = 0; i < 256; i++ ) { | for ( int i = 0; i < 256; i++ ) { | |||
ic[i] = starter[i] = false; | ic[i] = starter[i] = false; | |||
} | } | |||
for ( int i = 'a'; i <= 'z'; i++ ) | for ( int i = 'a'; i <= 'z'; i++ ) | |||
ic[i] = starter[i] = true; | ic[i] = starter[i] = true; | |||
for ( int i = 'A'; i <= 'Z'; i++ ) | for ( int i = 'A'; i <= 'Z'; i++ ) | |||
ic[i] = starter[i] = true; | ic[i] = starter[i] = true; | |||
for ( int i = '0'; i <= '9'; i++ ) | for ( int i = '0'; i <= '9'; i++ ) | |||
ic[i] = true; | ic[i] = true; | |||
End of changes. 1 change blocks. | ||||
3 lines changed or deleted | 3 lines changed or added | |||
miniwebserver.h | miniwebserver.h | |||
---|---|---|---|---|
skipping to change at line 56 | skipping to change at line 56 | |||
static string parseURL( const char * buf ); | static string parseURL( const char * buf ); | |||
static string parseMethod( const char * headers ); | static string parseMethod( const char * headers ); | |||
static string getHeader( const char * headers , string name ); | static string getHeader( const char * headers , string name ); | |||
static const char *body( const char *buf ); | static const char *body( const char *buf ); | |||
static string urlDecode(const char* s); | static string urlDecode(const char* s); | |||
static string urlDecode(string s) {return urlDecode(s.c_str());} | static string urlDecode(string s) {return urlDecode(s.c_str());} | |||
private: | private: | |||
void accepted(Socket socket); | void accepted(boost::shared_ptr<Socket> psocket); | |||
static bool fullReceive( const char *buf ); | static bool fullReceive( const char *buf ); | |||
}; | }; | |||
} // namespace mongo | } // namespace mongo | |||
End of changes. 1 change blocks. | ||||
1 lines changed or deleted | 1 lines changed or added | |||
misc.h | misc.h | |||
---|---|---|---|---|
skipping to change at line 23 | skipping to change at line 23 | |||
* 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 <ctime> | #include <ctime> | |||
#include <limits> | ||||
namespace mongo { | namespace mongo { | |||
using namespace std; | ||||
inline void time_t_to_String(time_t t, char *buf) { | inline void time_t_to_String(time_t t, char *buf) { | |||
#if defined(_WIN32) | #if defined(_WIN32) | |||
ctime_s(buf, 32, &t); | ctime_s(buf, 32, &t); | |||
#else | #else | |||
ctime_r(&t, buf); | ctime_r(&t, buf); | |||
#endif | #endif | |||
buf[24] = 0; // don't want the \n | buf[24] = 0; // don't want the \n | |||
} | } | |||
inline string time_t_to_String(time_t t = time(0) ) { | inline std::string time_t_to_String(time_t t = time(0) ) { | |||
char buf[64]; | char buf[64]; | |||
#if defined(_WIN32) | #if defined(_WIN32) | |||
ctime_s(buf, sizeof(buf), &t); | ctime_s(buf, sizeof(buf), &t); | |||
#else | #else | |||
ctime_r(&t, buf); | ctime_r(&t, buf); | |||
#endif | #endif | |||
buf[24] = 0; // don't want the \n | buf[24] = 0; // don't want the \n | |||
return buf; | return buf; | |||
} | } | |||
inline string time_t_to_String_no_year(time_t t) { | inline std::string time_t_to_String_no_year(time_t t) { | |||
char buf[64]; | char buf[64]; | |||
#if defined(_WIN32) | #if defined(_WIN32) | |||
ctime_s(buf, sizeof(buf), &t); | ctime_s(buf, sizeof(buf), &t); | |||
#else | #else | |||
ctime_r(&t, buf); | ctime_r(&t, buf); | |||
#endif | #endif | |||
buf[19] = 0; | buf[19] = 0; | |||
return buf; | return buf; | |||
} | } | |||
inline string time_t_to_String_short(time_t t) { | inline std::string time_t_to_String_short(time_t t) { | |||
char buf[64]; | char buf[64]; | |||
#if defined(_WIN32) | #if defined(_WIN32) | |||
ctime_s(buf, sizeof(buf), &t); | ctime_s(buf, sizeof(buf), &t); | |||
#else | #else | |||
ctime_r(&t, buf); | ctime_r(&t, buf); | |||
#endif | #endif | |||
buf[19] = 0; | buf[19] = 0; | |||
if( buf[0] && buf[1] && buf[2] && buf[3] ) | if( buf[0] && buf[1] && buf[2] && buf[3] ) | |||
return buf + 4; // skip day of week | return buf + 4; // skip day of week | |||
return buf; | 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; } | |||
void toTm (tm *buf) { | void toTm (tm *buf) { | |||
time_t dtime = (time_t)(millis/1000); | time_t dtime = toTimeT(); | |||
#if defined(_WIN32) | #if defined(_WIN32) | |||
gmtime_s(buf, &dtime); | gmtime_s(buf, &dtime); | |||
#else | #else | |||
gmtime_r(&dtime, buf); | gmtime_r(&dtime, buf); | |||
#endif | #endif | |||
} | } | |||
string toString() const { | std::string toString() const { | |||
char buf[64]; | char buf[64]; | |||
time_t_to_String(millis/1000, buf); | time_t_to_String(toTimeT(), buf); | |||
return buf; | return buf; | |||
} | } | |||
time_t toTimeT() const { | ||||
// cant use uassert from bson/util | ||||
verify((long long)millis >= 0); // TODO when millis is signed, | ||||
delete | ||||
verify(((long long)millis/1000) < (std::numeric_limits<time_t>: | ||||
:max)()); | ||||
return millis / 1000; | ||||
} | ||||
}; | }; | |||
// Like strlen, but only scans up to n bytes. | // Like strlen, but only scans up to n bytes. | |||
// Returns -1 if no '0' found. | // Returns -1 if no '0' found. | |||
inline int strnlen( const char *s, int n ) { | inline int strnlen( const char *s, int n ) { | |||
for( int i = 0; i < n; ++i ) | for( int i = 0; i < n; ++i ) | |||
if ( !s[ i ] ) | if ( !s[ i ] ) | |||
return i; | return i; | |||
return -1; | return -1; | |||
} | } | |||
End of changes. 9 change blocks. | ||||
8 lines changed or deleted | 15 lines changed or added | |||
mmap.h | mmap.h | |||
---|---|---|---|---|
skipping to change at line 28 | skipping to change at line 28 | |||
#pragma once | #pragma once | |||
#include <boost/thread/xtime.hpp> | #include <boost/thread/xtime.hpp> | |||
#include "concurrency/rwlock.h" | #include "concurrency/rwlock.h" | |||
namespace mongo { | namespace mongo { | |||
class MAdvise { | class MAdvise { | |||
void *_p; | void *_p; | |||
unsigned _len; | unsigned _len; | |||
public: | public: | |||
enum Advice { Sequential=1 }; | enum Advice { Sequential=1 , Random=2 }; | |||
MAdvise(void *p, unsigned len, Advice a); | MAdvise(void *p, unsigned len, Advice a); | |||
~MAdvise(); // destructor resets the range to MADV_NORMAL | ~MAdvise(); // destructor resets the range to MADV_NORMAL | |||
}; | }; | |||
// lock order: lock dbMutex before this if you lock both | // lock order: lock dbMutex before this if you lock both | |||
class LockMongoFilesShared { | class LockMongoFilesShared { | |||
friend class LockMongoFilesExclusive; | friend class LockMongoFilesExclusive; | |||
static RWLockRecursiveNongreedy mmmutex; | static RWLockRecursiveNongreedy mmmutex; | |||
static unsigned era; | static unsigned era; | |||
RWLockRecursive::Shared lk; | RWLockRecursive::Shared lk; | |||
skipping to change at line 95 | skipping to change at line 95 | |||
static set<MongoFile*>& getAllFiles() { return mmfiles; } | static set<MongoFile*>& getAllFiles() { return mmfiles; } | |||
// callbacks if you need them | // callbacks if you need them | |||
static void (*notifyPreFlush)(); | static void (*notifyPreFlush)(); | |||
static void (*notifyPostFlush)(); | static void (*notifyPostFlush)(); | |||
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 ); | |||
#if defined(_DEBUG) | ||||
static void markAllWritable(); | ||||
static void unmarkAllWritable(); | ||||
#else | ||||
static void markAllWritable() { } | ||||
static void unmarkAllWritable() { } | ||||
#endif | ||||
virtual bool isMongoMMF() { return false; } | virtual bool isMongoMMF() { return false; } | |||
string filename() const { return _filename; } | string filename() const { return _filename; } | |||
void setFilename(string fn); | void setFilename(string fn); | |||
private: | private: | |||
string _filename; | string _filename; | |||
static int _flushAll( bool sync ); // returns n flushed | static int _flushAll( bool sync ); // returns n flushed | |||
protected: | protected: | |||
virtual void close() = 0; | virtual void close() = 0; | |||
skipping to change at line 131 | skipping to change at line 123 | |||
/* subclass must call in destructor (or at close). | /* subclass must call in destructor (or at close). | |||
removes this from pathToFile and other maps | removes this from pathToFile and other maps | |||
safe to call more than once, albeit might be wasted work | safe to call more than once, albeit might be wasted work | |||
ideal to call close to the close, if the close is well before ob ject destruction | ideal to call close to the close, if the close is well before ob ject destruction | |||
*/ | */ | |||
void destroyed(); | void destroyed(); | |||
virtual unsigned long long length() const = 0; | virtual unsigned long long length() const = 0; | |||
// only supporting on posix mmap | ||||
virtual void _lock() {} | ||||
virtual void _unlock() {} | ||||
static set<MongoFile*> mmfiles; | static set<MongoFile*> mmfiles; | |||
public: | public: | |||
static map<string,MongoFile*> pathToFile; | static map<string,MongoFile*> pathToFile; | |||
}; | }; | |||
/** look up a MMF by filename. scoped mutex locking convention. | /** look up a MMF by filename. scoped mutex locking convention. | |||
example: | example: | |||
MMFFinderByName finder; | MMFFinderByName finder; | |||
MongoMMF *a = finder.find("file_name_a"); | MongoMMF *a = finder.find("file_name_a"); | |||
MongoMMF *b = finder.find("file_name_b"); | MongoMMF *b = finder.find("file_name_b"); | |||
skipping to change at line 162 | skipping to change at line 150 | |||
*/ | */ | |||
MongoFile* findByPath(string path) { | MongoFile* findByPath(string path) { | |||
map<string,MongoFile*>::iterator i = MongoFile::pathToFile.find (path); | map<string,MongoFile*>::iterator i = MongoFile::pathToFile.find (path); | |||
return i == MongoFile::pathToFile.end() ? NULL : i->second; | return i == MongoFile::pathToFile.end() ? NULL : i->second; | |||
} | } | |||
private: | private: | |||
LockMongoFilesShared _lk; | LockMongoFilesShared _lk; | |||
}; | }; | |||
struct MongoFileAllowWrites { | ||||
MongoFileAllowWrites() { | ||||
MongoFile::markAllWritable(); | ||||
} | ||||
~MongoFileAllowWrites() { | ||||
MongoFile::unmarkAllWritable(); | ||||
} | ||||
}; | ||||
class MemoryMappedFile : public MongoFile { | class MemoryMappedFile : public MongoFile { | |||
protected: | protected: | |||
virtual void* viewForFlushing() { | virtual void* viewForFlushing() { | |||
if( views.size() == 0 ) | if( views.size() == 0 ) | |||
return 0; | return 0; | |||
assert( views.size() == 1 ); | verify( views.size() == 1 ); | |||
return views[0]; | return views[0]; | |||
} | } | |||
public: | public: | |||
MemoryMappedFile(); | MemoryMappedFile(); | |||
virtual ~MemoryMappedFile() { | virtual ~MemoryMappedFile() { | |||
LockMongoFilesExclusive lk; | LockMongoFilesExclusive lk; | |||
close(); | close(); | |||
} | } | |||
skipping to change at line 246 | skipping to change at line 225 | |||
boost::shared_ptr<mutex> _flushMutex; | boost::shared_ptr<mutex> _flushMutex; | |||
void clearWritableBits(void *privateView); | void clearWritableBits(void *privateView); | |||
public: | public: | |||
static const unsigned ChunkSize = 64 * 1024 * 1024; | static const unsigned ChunkSize = 64 * 1024 * 1024; | |||
static const unsigned NChunks = 1024 * 1024; | static const unsigned NChunks = 1024 * 1024; | |||
#else | #else | |||
void clearWritableBits(void *privateView) { } | void clearWritableBits(void *privateView) { } | |||
#endif | #endif | |||
protected: | protected: | |||
// only posix mmap implementations will support this | ||||
virtual void _lock(); | ||||
virtual void _unlock(); | ||||
/** close the current private view and open a new replacement */ | /** close the current private view and open a new replacement */ | |||
void* remapPrivateView(void *oldPrivateAddr); | void* remapPrivateView(void *oldPrivateAddr); | |||
}; | }; | |||
typedef MemoryMappedFile MMF; | typedef MemoryMappedFile MMF; | |||
/** p is called from within a mutex that MongoFile uses. so be careful not to deadlock. */ | /** p is called from within a mutex that MongoFile uses. so be careful not to deadlock. */ | |||
template < class F > | template < class F > | |||
inline void MongoFile::forEach( F p ) { | inline void MongoFile::forEach( F p ) { | |||
skipping to change at line 273 | skipping to change at line 249 | |||
#if defined(_WIN32) | #if defined(_WIN32) | |||
class ourbitset { | class ourbitset { | |||
volatile unsigned bits[MemoryMappedFile::NChunks]; // volatile as w e are doing double check locking | volatile unsigned bits[MemoryMappedFile::NChunks]; // volatile as w e are doing double check locking | |||
public: | public: | |||
ourbitset() { | ourbitset() { | |||
memset((void*) bits, 0, sizeof(bits)); | memset((void*) bits, 0, sizeof(bits)); | |||
} | } | |||
bool get(unsigned i) const { | bool get(unsigned i) const { | |||
unsigned x = i / 32; | unsigned x = i / 32; | |||
assert( x < MemoryMappedFile::NChunks ); | verify( x < MemoryMappedFile::NChunks ); | |||
return (bits[x] & (1 << (i%32))) != 0; | return (bits[x] & (1 << (i%32))) != 0; | |||
} | } | |||
void set(unsigned i) { | void set(unsigned i) { | |||
unsigned x = i / 32; | unsigned x = i / 32; | |||
wassert( x < (MemoryMappedFile::NChunks*2/3) ); // warn if gett ing close to limit | wassert( x < (MemoryMappedFile::NChunks*2/3) ); // warn if gett ing close to limit | |||
assert( x < MemoryMappedFile::NChunks ); | verify( x < MemoryMappedFile::NChunks ); | |||
bits[x] |= (1 << (i%32)); | bits[x] |= (1 << (i%32)); | |||
} | } | |||
void clear(unsigned i) { | void clear(unsigned i) { | |||
unsigned x = i / 32; | unsigned x = i / 32; | |||
assert( x < MemoryMappedFile::NChunks ); | verify( x < MemoryMappedFile::NChunks ); | |||
bits[x] &= ~(1 << (i%32)); | bits[x] &= ~(1 << (i%32)); | |||
} | } | |||
}; | }; | |||
extern ourbitset writable; | extern ourbitset writable; | |||
void makeChunkWritable(size_t chunkno); | void makeChunkWritable(size_t chunkno); | |||
inline void MemoryMappedFile::makeWritable(void *_p, unsigned len) { | inline void MemoryMappedFile::makeWritable(void *_p, unsigned len) { | |||
size_t p = (size_t) _p; | size_t p = (size_t) _p; | |||
unsigned a = p/ChunkSize; | unsigned a = p/ChunkSize; | |||
unsigned b = (p+len)/ChunkSize; | unsigned b = (p+len)/ChunkSize; | |||
for( unsigned i = a; i <= b; i++ ) { | for( unsigned i = a; i <= b; i++ ) { | |||
End of changes. 9 change blocks. | ||||
29 lines changed or deleted | 5 lines changed or added | |||
model.h | model.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 "dbclient.h" | #include "mongo/bson/bsonelement.h" | |||
#include "redef_macros.h" | #include "mongo/bson/bsonobj.h" | |||
namespace mongo { | namespace mongo { | |||
/** Model is a base class for defining objects which are serializable t o the Mongo | /** Model is a base class for defining objects which are serializable t o the Mongo | |||
database via the database driver. | database via the database driver. | |||
Definition | Definition | |||
Your serializable class should inherit from Model and implement the abstract methods | Your serializable class should inherit from Model and implement the abstract methods | |||
below. | below. | |||
skipping to change at line 61 | skipping to change at line 61 | |||
*/ | */ | |||
virtual bool load(BSONObj& query); | virtual bool load(BSONObj& query); | |||
virtual void save( bool safe=false ); | virtual void save( bool safe=false ); | |||
virtual void remove( bool safe=false ); | virtual void remove( bool safe=false ); | |||
protected: | protected: | |||
BSONObj _id; | BSONObj _id; | |||
}; | }; | |||
} // namespace mongo | } // namespace mongo | |||
#include "undef_macros.h" | ||||
End of changes. 2 change blocks. | ||||
2 lines changed or deleted | 2 lines changed or added | |||
mongommf.h | mongommf.h | |||
---|---|---|---|---|
skipping to change at line 78 | skipping to change at line 78 | |||
initialization. | initialization. | |||
*/ | */ | |||
static void* _switchToWritableView(void *private_ptr); | static void* _switchToWritableView(void *private_ptr); | |||
/** for a filename a/b/c.3 | /** for a filename a/b/c.3 | |||
filePath() is "a/b/c" | filePath() is "a/b/c" | |||
fileSuffixNo() is 3 | fileSuffixNo() is 3 | |||
if the suffix is "ns", fileSuffixNo -1 | if the suffix is "ns", fileSuffixNo -1 | |||
*/ | */ | |||
const RelativePath& relativePath() const { | const RelativePath& relativePath() const { | |||
DEV assert( !_p._p.empty() ); | DEV verify( !_p._p.empty() ); | |||
return _p; | return _p; | |||
} | } | |||
int fileSuffixNo() const { return _fileSuffixNo; } | int fileSuffixNo() const { return _fileSuffixNo; } | |||
/** true if we have written. | /** true if we have written. | |||
set in PREPLOGBUFFER, it is NOT set immediately on write intent declaration. | set in PREPLOGBUFFER, it is NOT set immediately on write intent declaration. | |||
reset to false in REMAPPRIVATEVIEW | reset to false in REMAPPRIVATEVIEW | |||
*/ | */ | |||
bool& willNeedRemap() { return _willNeedRemap; } | bool& willNeedRemap() { return _willNeedRemap; } | |||
End of changes. 1 change blocks. | ||||
1 lines changed or deleted | 1 lines changed or added | |||
mongomutex.h | mongomutex.h | |||
---|---|---|---|---|
skipping to change at line 19 | skipping to change at line 19 | |||
* | * | |||
* This program is distributed in the hope that it will be useful, | * This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Affero General Public License for more details. | * GNU Affero General Public License for more details. | |||
* | * | |||
* You should have received a copy of the GNU Affero General Public Lice nse | * You should have received a copy of the GNU Affero General Public Lice nse | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | */ | |||
/* Mutex heirarchy (1 = "leaf") | ||||
name level | ||||
Logstream::mutex 1 | ||||
ClientCursor::ccmutex 2 | ||||
dblock 3 | ||||
End func name with _inlock to indicate "caller must lock before callin | ||||
g". | ||||
*/ | ||||
#pragma once | #pragma once | |||
#include "../util/concurrency/rwlock.h" | ||||
#include "../util/mmap.h" | ||||
#include "../util/time_support.h" | #include "../util/time_support.h" | |||
#include "d_globals.h" | #include "d_globals.h" | |||
namespace mongo { | namespace mongo { | |||
class Client; | class Client; | |||
Client* curopWaitingForLock( int type ); | ||||
void curopGotLock(Client*); | ||||
/* mongomutex time stats */ | /* mongomutex time stats */ | |||
class MutexInfo { | class MutexInfo { | |||
unsigned long long enter, timeLocked; // microseconds | unsigned long long enter, timeLocked; // microseconds | |||
int locked; | int locked; | |||
unsigned long long start; // last as we touch this least often | unsigned long long start; // last as we touch this least often | |||
public: | public: | |||
MutexInfo() : timeLocked(0) , locked(0) { | MutexInfo() : timeLocked(0) , locked(0) { | |||
start = curTimeMicros64(); | start = curTimeMicros64(); | |||
} | } | |||
void entered() { | void entered() { | |||
if ( locked == 0 ) | if ( locked == 0 ) | |||
enter = curTimeMicros64(); | enter = curTimeMicros64(); | |||
locked++; | locked++; | |||
assert( locked >= 1 ); | verify( locked >= 1 ); | |||
} | } | |||
void leaving() { | void leaving() { | |||
locked--; | locked--; | |||
assert( locked >= 0 ); | verify( locked >= 0 ); | |||
if ( locked == 0 ) | if ( locked == 0 ) | |||
timeLocked += curTimeMicros64() - enter; | timeLocked += curTimeMicros64() - enter; | |||
} | } | |||
int isLocked() const { return locked; } | int isLocked() const { return locked; } | |||
void getTimingInfo(unsigned long long &s, unsigned long long &tl) c onst { | void getTimingInfo(unsigned long long &s, unsigned long long &tl) c onst { | |||
s = start; | s = start; | |||
tl = timeLocked; | tl = timeLocked; | |||
} | } | |||
unsigned long long getTimeLocked() const { return timeLocked; } | unsigned long long getTimeLocked() const { return timeLocked; } | |||
}; | }; | |||
/** the 'big lock'. a read/write lock. | /** old. see Lock class in d_concurrency.h instead. */ | |||
there is one of these, d.dbMutex. | class MongoMutex : boost::noncopyable { | |||
generally if you need to declare a mutex use the right primitive cl | ||||
ass, not this. | ||||
use readlock and writelock classes for scoped locks on this rather | ||||
than direct | ||||
manipulation. | ||||
*/ | ||||
class MongoMutex { | ||||
public: | public: | |||
MongoMutex(const char * name); | MongoMutex(); | |||
/** @return | ||||
* > 0 write lock | ||||
* = 0 no lock | ||||
* < 0 read lock | ||||
*/ | ||||
int getState() const { return _state.get(); } | ||||
bool atLeastReadLocked() const { return _state.get() != 0; } | ||||
void assertAtLeastReadLocked() const { assert(atLeastReadLocked()); | ||||
} | ||||
bool isWriteLocked/*by our thread*/() const { return getState() > 0 | ||||
; } | ||||
void assertWriteLocked() const { | ||||
assert( getState() > 0 ); | ||||
DEV assert( !_releasedEarly.get() ); | ||||
} | ||||
// write lock. use the writelock scoped lock class, not this direc | ||||
tly. | ||||
void lock() { | ||||
if ( _writeLockedAlready() ) | ||||
return; | ||||
_state.set(1); | ||||
curopWaitingForLock( 1 ); // stats | ||||
_m.lock(); | ||||
MongoFile::markAllWritable(); // for _DEBUG validation -- a no | ||||
op for release build | ||||
_acquiredWriteLock(); | ||||
} | ||||
// try write lock | ||||
bool lock_try( int millis ) { | ||||
if ( _writeLockedAlready() ) // adjusts _state | ||||
return true; | ||||
curopWaitingForLock( 1 ); | ||||
bool got = _m.lock_try( millis ); | ||||
if ( got ) { | ||||
_state.set(1); | ||||
MongoFile::markAllWritable(); // for _DEBUG validation -- a | ||||
no op for release build | ||||
_acquiredWriteLock(); | ||||
} | ||||
return got; | ||||
} | ||||
// un write lock | ||||
void unlock() { | ||||
int s = _state.get(); | ||||
if( s > 1 ) { | ||||
_state.set(s-1); // recursive lock case | ||||
return; | ||||
} | ||||
if( s != 1 ) { | ||||
if( _releasedEarly.get() ) { | ||||
_releasedEarly.set(false); | ||||
return; | ||||
} | ||||
massert( 12599, "internal error: attempt to unlock when was | ||||
n't in a write lock", false); | ||||
} | ||||
_releasingWriteLock(); | ||||
MongoFile::unmarkAllWritable(); // _DEBUG validation | ||||
_state.set(0); | ||||
_m.unlock(); | ||||
} | ||||
/* unlock (write lock), and when unlock() is called later, | ||||
be smart then and don't unlock it again. | ||||
*/ | ||||
void releaseEarly() { | ||||
assert( getState() == 1 ); // must not be recursive | ||||
assert( !_releasedEarly.get() ); | ||||
_releasedEarly.set(true); | ||||
unlock(); | ||||
} | ||||
// read lock. don't call directly, use readlock. | ||||
void lock_shared() { | ||||
int s = _state.get(); | ||||
if( s ) { | ||||
if( s > 0 ) { | ||||
// already in write lock - just be recursive and stay w | ||||
rite locked | ||||
_state.set(s+1); | ||||
} | ||||
else { | ||||
// already in read lock - recurse | ||||
_state.set(s-1); | ||||
} | ||||
} | ||||
else { | ||||
_state.set(-1); | ||||
Client *c = curopWaitingForLock( -1 ); | ||||
_m.lock_shared(); | ||||
curopGotLock(c); | ||||
} | ||||
} | ||||
// try read lock | ||||
bool lock_shared_try( int millis ) { | ||||
int s = _state.get(); | ||||
if ( s ) { | ||||
// we already have a lock, so no need to try | ||||
lock_shared(); | ||||
return true; | ||||
} | ||||
/* [dm] should there be | ||||
Client *c = curopWaitingForLock( 1 ); | ||||
here? i think so. seems to be missing. | ||||
*/ | ||||
bool got = _m.lock_shared_try( millis ); | ||||
if ( got ) | ||||
_state.set(-1); | ||||
return got; | ||||
} | ||||
void unlock_shared() { | ||||
int s = _state.get(); | ||||
if( s > 0 ) { | ||||
wassert( s > 1 ); /* we must have done a lock write first t | ||||
o have s > 1 */ | ||||
_state.set(s-1); | ||||
return; | ||||
} | ||||
if( s < -1 ) { | ||||
_state.set(s+1); | ||||
return; | ||||
} | ||||
wassert( s == -1 ); | ||||
_state.set(0); | ||||
_m.unlock_shared(); | ||||
} | ||||
MutexInfo& info() { return _minfo; } | MutexInfo& info() { return _minfo; } | |||
private: | ||||
void lockedExclusively(); | ||||
void unlockingExclusively(); | ||||
void _acquiredWriteLock(); | ||||
void _releasingWriteLock(); | ||||
/* @return true if was already write locked. increments recursive | ||||
lock count. */ | ||||
bool _writeLockedAlready(); | ||||
RWLock _m; | ||||
/* > 0 write lock with recurse count | ||||
< 0 read lock | ||||
*/ | ||||
ThreadLocalValue<int> _state; | ||||
MutexInfo _minfo; | MutexInfo _minfo; | |||
public: | ||||
// indicates we need to call dur::REMAPPRIVATEVIEW on the next writ | ||||
e lock | ||||
bool _remapPrivateViewRequested; | ||||
private: | ||||
/* See the releaseEarly() method. | ||||
we use a separate TLS value for releasedEarly - that is ok as | ||||
our normal/common code path, we never even touch it */ | ||||
ThreadLocalValue<bool> _releasedEarly; | ||||
/* this is for fsyncAndLock command. otherwise write lock's greedi | ||||
ness will | ||||
make us block on any attempted write lock the the fsync's lock. | ||||
*/ | ||||
//volatile bool _blockWrites; | ||||
}; | }; | |||
namespace dur { | ||||
void REMAPPRIVATEVIEW(); | ||||
void releasingWriteLock(); // because it's hard to include dur.h he | ||||
re | ||||
} | ||||
inline void MongoMutex::_releasingWriteLock() { | ||||
dur::releasingWriteLock(); | ||||
unlockingExclusively(); | ||||
} | ||||
inline void MongoMutex::_acquiredWriteLock() { | ||||
lockedExclusively(); | ||||
if( _remapPrivateViewRequested ) { | ||||
dur::REMAPPRIVATEVIEW(); | ||||
dassert( !_remapPrivateViewRequested ); | ||||
} | ||||
} | ||||
string sayClientState(); | ||||
/* @return true if was already write locked. increments recursive lock | ||||
count. */ | ||||
inline bool MongoMutex::_writeLockedAlready() { | ||||
int s = _state.get(); | ||||
if( s > 0 ) { | ||||
_state.set(s+1); | ||||
return true; | ||||
} | ||||
massert( 10293 , string("internal error: locks are not upgradeable: | ||||
") + sayClientState() , s == 0 ); | ||||
return false; | ||||
} | ||||
struct writelock { | ||||
writelock() { d.dbMutex.lock(); } | ||||
writelock(const string& ns) { d.dbMutex.lock(); } | ||||
~writelock() { | ||||
DESTRUCTOR_GUARD( | ||||
d.dbMutex.unlock(); | ||||
); | ||||
} | ||||
}; | ||||
struct readlock { | ||||
readlock(const string& ns) { | ||||
d.dbMutex.lock_shared(); | ||||
} | ||||
readlock() { d.dbMutex.lock_shared(); } | ||||
~readlock() { | ||||
DESTRUCTOR_GUARD( | ||||
d.dbMutex.unlock_shared(); | ||||
); | ||||
} | ||||
}; | ||||
struct readlocktry { | ||||
readlocktry( const string&ns , int tryms ) { | ||||
_got = d.dbMutex.lock_shared_try( tryms ); | ||||
} | ||||
~readlocktry() { | ||||
if ( _got ) { | ||||
d.dbMutex.unlock_shared(); | ||||
} | ||||
} | ||||
bool got() const { return _got; } | ||||
private: | ||||
bool _got; | ||||
}; | ||||
struct writelocktry { | ||||
writelocktry( const string&ns , int tryms ) { | ||||
_got = d.dbMutex.lock_try( tryms ); | ||||
} | ||||
~writelocktry() { | ||||
if ( _got ) { | ||||
d.dbMutex.unlock(); | ||||
} | ||||
} | ||||
bool got() const { return _got; } | ||||
private: | ||||
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 { | ||||
atleastreadlock( const string& ns = "" ) { | ||||
_prev = d.dbMutex.getState(); | ||||
if ( _prev == 0 ) | ||||
d.dbMutex.lock_shared(); | ||||
} | ||||
~atleastreadlock() { | ||||
if ( _prev == 0 ) | ||||
d.dbMutex.unlock_shared(); | ||||
} | ||||
private: | ||||
int _prev; | ||||
}; | ||||
/* parameterized choice of read or write locking | ||||
use readlock and writelock instead of this when statically known whi | ||||
ch you want | ||||
*/ | ||||
class mongolock { | ||||
bool _writelock; | ||||
public: | ||||
mongolock(bool write) : _writelock(write) { | ||||
if( _writelock ) { | ||||
d.dbMutex.lock(); | ||||
} | ||||
else | ||||
d.dbMutex.lock_shared(); | ||||
} | ||||
~mongolock() { | ||||
DESTRUCTOR_GUARD( | ||||
if( _writelock ) { | ||||
d.dbMutex.unlock(); | ||||
} | ||||
else { | ||||
d.dbMutex.unlock_shared(); | ||||
} | ||||
); | ||||
} | ||||
/* this unlocks, does NOT upgrade. that works for our current usage | ||||
*/ | ||||
//void releaseAndWriteLock(); | ||||
}; | ||||
/* deprecated - use writelock and readlock instead */ | ||||
struct dblock : public writelock { | ||||
dblock() : writelock("") { } | ||||
}; | ||||
// eliminate this - we should just type "d.dbMutex.assertWriteLocked(); | ||||
" instead | ||||
inline void assertInWriteLock() { d.dbMutex.assertWriteLocked(); } | ||||
} | } | |||
End of changes. 10 change blocks. | ||||
348 lines changed or deleted | 5 lines changed or added | |||
mr.h | mr.h | |||
---|---|---|---|---|
skipping to change at line 287 | skipping to change at line 287 | |||
/** State maintains ownership, do no use past State lifetime */ | /** State maintains ownership, do no use past State lifetime */ | |||
Scope* scope() { return _scope.get(); } | Scope* scope() { return _scope.get(); } | |||
const Config& config() { return _config; } | const Config& config() { return _config; } | |||
const bool isOnDisk() { return _onDisk; } | const bool isOnDisk() { return _onDisk; } | |||
long long numEmits() const { if (_jsMode) return _scope->getNum berLongLong("_emitCt"); return _numEmits; } | long long numEmits() const { if (_jsMode) return _scope->getNum berLongLong("_emitCt"); return _numEmits; } | |||
long long numReduces() const { if (_jsMode) return _scope->getN umberLongLong("_redCt"); return _config.reducer->numReduces; } | long long numReduces() const { if (_jsMode) return _scope->getN umberLongLong("_redCt"); return _config.reducer->numReduces; } | |||
long long numInMemKeys() const { if (_jsMode) return _scope->ge tNumberLongLong("_keyCt"); return _temp->size(); } | ||||
bool jsMode() {return _jsMode;} | bool jsMode() {return _jsMode;} | |||
void switchMode(bool jsMode); | void switchMode(bool jsMode); | |||
void bailFromJS(); | void bailFromJS(); | |||
const Config& _config; | const Config& _config; | |||
DBDirectClient _db; | DBDirectClient _db; | |||
protected: | protected: | |||
End of changes. 1 change blocks. | ||||
0 lines changed or deleted | 1 lines changed or added | |||
msg.h | msg.h | |||
---|---|---|---|---|
skipping to change at line 22 | skipping to change at line 22 | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Affero General Public License for more details. | * GNU Affero General Public License for more details. | |||
* | * | |||
* You should have received a copy of the GNU Affero General Public 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 <deque> | #include <deque> | |||
#include <boost/thread/condition.hpp> | ||||
#include "task.h" | #include "task.h" | |||
namespace mongo { | namespace mongo { | |||
namespace task { | namespace task { | |||
typedef boost::function<void()> lam; | typedef boost::function<void()> lam; | |||
/** typical usage is: task::fork( new Server("threadname") ); */ | /** typical usage is: task::fork( new Server("threadname") ); */ | |||
class Server : public Task { | class Server : public Task { | |||
End of changes. 1 change blocks. | ||||
0 lines changed or deleted | 2 lines changed or added | |||
mutex.h | mutex.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 "../heapcheck.h" | #ifdef _WIN32 | |||
#include "threadlocal.h" | # include <concrt.h> | |||
#endif | ||||
#include <boost/noncopyable.hpp> | ||||
#include <boost/thread/mutex.hpp> | ||||
#include <boost/thread/xtime.hpp> | ||||
#include "mongo/util/assert_util.h" | ||||
#include "mongo/util/heapcheck.h" | ||||
#include "mongo/util/concurrency/threadlocal.h" | ||||
#if defined(_DEBUG) | #if defined(_DEBUG) | |||
#include "mutexdebugger.h" | #include "mongo/util/concurrency/mutexdebugger.h" | |||
#endif | #endif | |||
namespace mongo { | namespace mongo { | |||
inline boost::xtime incxtimemillis( long long s ) { | inline boost::xtime incxtimemillis( 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 += (int)( s / 1000 ); | xt.sec += (int)( s / 1000 ); | |||
xt.nsec += (int)(( s % 1000 ) * 1000000); | xt.nsec += (int)(( s % 1000 ) * 1000000); | |||
if ( xt.nsec >= 1000000000 ) { | if ( xt.nsec >= 1000000000 ) { | |||
skipping to change at line 73 | skipping to change at line 83 | |||
if( !StaticObserver::_destroyingStatics ) { | if( !StaticObserver::_destroyingStatics ) { | |||
UNIGNORE_OBJECT( _m ); | UNIGNORE_OBJECT( _m ); | |||
delete _m; | delete _m; | |||
} | } | |||
} | } | |||
class try_lock : boost::noncopyable { | class try_lock : boost::noncopyable { | |||
public: | public: | |||
try_lock( mongo::mutex &m , int millis = 0 ) | try_lock( mongo::mutex &m , int millis = 0 ) | |||
: _l( m.boost() , incxtimemillis( millis ) ) , | : _l( m.boost() , incxtimemillis( millis ) ) , | |||
#if BOOST_VERSION >= 103500 | ||||
ok( _l.owns_lock() ) | ok( _l.owns_lock() ) | |||
#else | ||||
ok( _l.locked() ) | ||||
#endif | ||||
{ } | { } | |||
private: | private: | |||
boost::timed_mutex::scoped_timed_lock _l; | boost::timed_mutex::scoped_timed_lock _l; | |||
public: | public: | |||
const bool ok; | const bool ok; | |||
}; | }; | |||
class scoped_lock : boost::noncopyable { | class scoped_lock : boost::noncopyable { | |||
public: | public: | |||
#if defined(_DEBUG) | #if defined(_DEBUG) | |||
skipping to change at line 116 | skipping to change at line 122 | |||
} | } | |||
boost::timed_mutex::scoped_lock &boost() { return _l; } | boost::timed_mutex::scoped_lock &boost() { return _l; } | |||
private: | private: | |||
boost::timed_mutex::scoped_lock _l; | boost::timed_mutex::scoped_lock _l; | |||
}; | }; | |||
private: | private: | |||
boost::timed_mutex &boost() { return *_m; } | boost::timed_mutex &boost() { return *_m; } | |||
boost::timed_mutex *_m; | boost::timed_mutex *_m; | |||
}; | }; | |||
typedef mutex::scoped_lock scoped_lock; | typedef mongo::mutex::scoped_lock scoped_lock; | |||
typedef boost::recursive_mutex::scoped_lock recursive_scoped_lock; | ||||
/** The concept with SimpleMutex is that it is a basic lock/unlock with no | /** The concept with SimpleMutex is that it is a basic lock/unlock with no | |||
special functionality (such as try and try timeout). Thus it can be | special functionality (such as try and try timeout). Thus it can be | |||
implemented using OS-specific facilities in all environments (if desired). | implemented using OS-specific facilities in all environments (if desired). | |||
On Windows, the implementation below is faster than boost mutex. | On Windows, the implementation below is faster than boost mutex. | |||
*/ | */ | |||
#if defined(_WIN32) | #if defined(_WIN32) | |||
class SimpleMutex : boost::noncopyable { | class SimpleMutex { | |||
CRITICAL_SECTION _cs; | ||||
public: | public: | |||
SimpleMutex(const char *name) { InitializeCriticalSection(&_cs); } | SimpleMutex( const char * ) {} | |||
~SimpleMutex() { DeleteCriticalSection(&_cs); } | ||||
#if defined(_DEBUG) | ||||
ThreadLocalValue<int> _nlocksByMe; | ||||
void lock() { | ||||
assert( _nlocksByMe.get() == 0 ); // indicates you rae trying t | ||||
o lock recursively | ||||
_nlocksByMe.set(1); | ||||
EnterCriticalSection(&_cs); | ||||
} | ||||
void dassertLocked() const { | ||||
assert( _nlocksByMe.get() == 1 ); | ||||
} | ||||
void unlock() { | ||||
dassertLocked(); | ||||
_nlocksByMe.set(0); | ||||
LeaveCriticalSection(&_cs); | ||||
} | ||||
#else | ||||
void dassertLocked() const { } | void dassertLocked() const { } | |||
void lock() { | void lock() { _cs.lock(); } | |||
EnterCriticalSection(&_cs); | void unlock() { _cs.unlock(); } | |||
} | class scoped_lock { | |||
void unlock() { | ||||
LeaveCriticalSection(&_cs); | ||||
} | ||||
#endif | ||||
class scoped_lock : boost::noncopyable { | ||||
SimpleMutex& _m; | SimpleMutex& _m; | |||
public: | public: | |||
scoped_lock( SimpleMutex &m ) : _m(m) { _m.lock(); } | scoped_lock( SimpleMutex &m ) : _m(m) { _m.lock(); } | |||
~scoped_lock() { _m.unlock(); } | ~scoped_lock() { _m.unlock(); } | |||
# if defined(_DEBUG) | ||||
const SimpleMutex& m() const { return _m; } | const SimpleMutex& m() const { return _m; } | |||
# endif | ||||
}; | }; | |||
private: | ||||
Concurrency::critical_section _cs; | ||||
}; | }; | |||
#else | #else | |||
class SimpleMutex : boost::noncopyable { | class SimpleMutex : boost::noncopyable { | |||
public: | public: | |||
void dassertLocked() const { } | void dassertLocked() const { } | |||
SimpleMutex(const char* name) { assert( pthread_mutex_init(&_lock,0 ) == 0 ); } | SimpleMutex(const char* name) { verify( pthread_mutex_init(&_lock,0 ) == 0 ); } | |||
~SimpleMutex(){ | ~SimpleMutex(){ | |||
if ( ! StaticObserver::_destroyingStatics ) { | if ( ! StaticObserver::_destroyingStatics ) { | |||
assert( pthread_mutex_destroy(&_lock) == 0 ); | verify( pthread_mutex_destroy(&_lock) == 0 ); | |||
} | } | |||
} | } | |||
void lock() { assert( pthread_mutex_lock(&_lock) == 0 ); } | void lock() { verify( pthread_mutex_lock(&_lock) == 0 ); } | |||
void unlock() { assert( pthread_mutex_unlock(&_lock) == 0 ); } | void unlock() { verify( pthread_mutex_unlock(&_lock) == 0 ); } | |||
public: | public: | |||
class scoped_lock : boost::noncopyable { | class scoped_lock : boost::noncopyable { | |||
SimpleMutex& _m; | SimpleMutex& _m; | |||
public: | public: | |||
scoped_lock( SimpleMutex &m ) : _m(m) { _m.lock(); } | scoped_lock( SimpleMutex &m ) : _m(m) { _m.lock(); } | |||
~scoped_lock() { _m.unlock(); } | ~scoped_lock() { _m.unlock(); } | |||
const SimpleMutex& m() const { return _m; } | const SimpleMutex& m() const { return _m; } | |||
}; | }; | |||
private: | private: | |||
skipping to change at line 210 | skipping to change at line 191 | |||
bool isLocked() const { return n.get() > 0; } | bool isLocked() const { return n.get() > 0; } | |||
class scoped_lock : boost::noncopyable { | class scoped_lock : boost::noncopyable { | |||
RecursiveMutex& rm; | RecursiveMutex& rm; | |||
int& nLocksByMe; | int& nLocksByMe; | |||
public: | public: | |||
scoped_lock( RecursiveMutex &m ) : rm(m), nLocksByMe(rm.n.getRe f()) { | scoped_lock( RecursiveMutex &m ) : rm(m), nLocksByMe(rm.n.getRe f()) { | |||
if( nLocksByMe++ == 0 ) | if( nLocksByMe++ == 0 ) | |||
rm.m.lock(); | rm.m.lock(); | |||
} | } | |||
~scoped_lock() { | ~scoped_lock() { | |||
assert( nLocksByMe > 0 ); | verify( nLocksByMe > 0 ); | |||
if( --nLocksByMe == 0 ) { | if( --nLocksByMe == 0 ) { | |||
rm.m.unlock(); | rm.m.unlock(); | |||
} | } | |||
} | } | |||
}; | }; | |||
private: | private: | |||
SimpleMutex m; | SimpleMutex m; | |||
ThreadLocalValue<int> n; | ThreadLocalValue<int> n; | |||
}; | }; | |||
End of changes. 15 change blocks. | ||||
47 lines changed or deleted | 27 lines changed or added | |||
mutexdebugger.h | mutexdebugger.h | |||
---|---|---|---|---|
// @file mutexdebugger.h | // @file mutexdebugger.h | |||
#pragma once | #pragma once | |||
#include "mongo/client/undef_macros.h" | ||||
#include <iostream> | ||||
#include <map> | ||||
#include <set> | ||||
#include <sstream> | ||||
#include <string> | ||||
#include "boost/thread/mutex.hpp" | ||||
#include "mongo/client/redef_macros.h" | ||||
#include "mongo/util/assert_util.h" | ||||
namespace mongo { | namespace mongo { | |||
/** only used on _DEBUG builds. | /** only used on _DEBUG builds. | |||
MutexDebugger checks that we always acquire locks for multiple mute xes in a consistant (acyclic) order. | MutexDebugger checks that we always acquire locks for multiple mute xes in a consistant (acyclic) order. | |||
If we were inconsistent we could deadlock. | If we were inconsistent we could deadlock. | |||
*/ | */ | |||
class MutexDebugger { | class MutexDebugger { | |||
typedef const char * mid; // mid = mutex ID | typedef const char * mid; // mid = mutex ID | |||
typedef map<mid,int> Preceeding; | typedef std::map<mid,int> Preceeding; | |||
map< mid, int > maxNest; | std::map< mid, int > maxNest; | |||
boost::thread_specific_ptr< Preceeding > us; | boost::thread_specific_ptr< Preceeding > us; | |||
map< mid, set<mid> > followers; | std::map< mid, std::set<mid> > followers; | |||
boost::mutex &x; | boost::mutex &x; | |||
unsigned magic; | unsigned magic; | |||
void aBreakPoint() { } // for debugging | void aBreakPoint() { } // for debugging | |||
public: | public: | |||
// set these to create an assert that | // set these to create an assert that | |||
// b must never be locked before a | // b must never be locked before a | |||
// so | // so | |||
// a.lock(); b.lock(); is fine | // a.lock(); b.lock(); is fine | |||
// b.lock(); alone is fine too | // b.lock(); alone is fine too | |||
// only checked on _DEBUG builds. | // only checked on _DEBUG builds. | |||
string a,b; | std::string a,b; | |||
/** outputs some diagnostic info on mutexes (on _DEBUG builds) */ | /** outputs some diagnostic info on mutexes (on _DEBUG builds) */ | |||
void programEnding(); | void programEnding(); | |||
MutexDebugger(); | MutexDebugger(); | |||
string currentlyLocked() const { | std::string currentlyLocked() const { | |||
Preceeding *_preceeding = us.get(); | Preceeding *_preceeding = us.get(); | |||
if( _preceeding == 0 ) | if( _preceeding == 0 ) | |||
return ""; | return ""; | |||
Preceeding &preceeding = *_preceeding; | Preceeding &preceeding = *_preceeding; | |||
stringstream q; | std::stringstream q; | |||
for( Preceeding::const_iterator i = preceeding.begin(); i != pr eceeding.end(); i++ ) { | for( Preceeding::const_iterator i = preceeding.begin(); i != pr eceeding.end(); i++ ) { | |||
if( i->second > 0 ) | if( i->second > 0 ) | |||
q << " " << i->first << ' ' << i->second << '\n'; | q << " " << i->first << ' ' << i->second << '\n'; | |||
} | } | |||
return q.str(); | return q.str(); | |||
} | } | |||
void entering(mid m) { | void entering(mid m) { | |||
if( this == 0 || m == 0 ) return; | if( this == 0 || m == 0 ) return; | |||
assert( magic == 0x12345678 ); | verify( magic == 0x12345678 ); | |||
Preceeding *_preceeding = us.get(); | Preceeding *_preceeding = us.get(); | |||
if( _preceeding == 0 ) | if( _preceeding == 0 ) | |||
us.reset( _preceeding = new Preceeding() ); | us.reset( _preceeding = new Preceeding() ); | |||
Preceeding &preceeding = *_preceeding; | Preceeding &preceeding = *_preceeding; | |||
if( a == m ) { | if( a == m ) { | |||
aBreakPoint(); | aBreakPoint(); | |||
if( preceeding[b.c_str()] ) { | if( preceeding[b.c_str()] ) { | |||
cout << "****** MutexDebugger error! warning " << b << | std::cout << "****** MutexDebugger error! warning " << | |||
" was locked before " << a << endl; | b << " was locked before " << a << std::endl; | |||
assert(false); | verify(false); | |||
} | } | |||
} | } | |||
preceeding[m]++; | preceeding[m]++; | |||
if( preceeding[m] > 1 ) { | if( preceeding[m] > 1 ) { | |||
// recursive re-locking. | // recursive re-locking. | |||
if( preceeding[m] > maxNest[m] ) | if( preceeding[m] > maxNest[m] ) | |||
maxNest[m] = preceeding[m]; | maxNest[m] = preceeding[m]; | |||
return; | return; | |||
} | } | |||
bool failed = false; | bool failed = false; | |||
string err; | std::string err; | |||
{ | { | |||
boost::mutex::scoped_lock lk(x); | boost::mutex::scoped_lock lk(x); | |||
followers[m]; | followers[m]; | |||
for( Preceeding::iterator i = preceeding.begin(); i != prec eeding.end(); i++ ) { | for( Preceeding::iterator i = preceeding.begin(); i != prec eeding.end(); i++ ) { | |||
if( m != i->first && i->second > 0 ) { | if( m != i->first && i->second > 0 ) { | |||
followers[i->first].insert(m); | followers[i->first].insert(m); | |||
if( followers[m].count(i->first) != 0 ) { | if( followers[m].count(i->first) != 0 ) { | |||
failed = true; | failed = true; | |||
stringstream ss; | std::stringstream ss; | |||
mid bad = i->first; | mid bad = i->first; | |||
ss << "mutex problem" << | ss << "mutex problem" << | |||
"\n when locking " << m << | "\n when locking " << m << | |||
"\n " << bad << " was already locked and sh ould not be." | "\n " << bad << " was already locked and sh ould not be." | |||
"\n set a and b above to debug.\n"; | "\n set a and b above to debug.\n"; | |||
stringstream q; | std::stringstream q; | |||
for( Preceeding::iterator i = preceeding.begin( ); i != preceeding.end(); i++ ) { | for( Preceeding::iterator i = preceeding.begin( ); i != preceeding.end(); i++ ) { | |||
if( i->first != m && i->first != bad && i-> second > 0 ) | if( i->first != m && i->first != bad && i-> second > 0 ) | |||
q << " " << i->first << '\n'; | q << " " << i->first << '\n'; | |||
} | } | |||
string also = q.str(); | std::string also = q.str(); | |||
if( !also.empty() ) | if( !also.empty() ) | |||
ss << "also locked before " << m << " in th is thread (no particular order):\n" << also; | ss << "also locked before " << m << " in th is thread (no particular order):\n" << also; | |||
err = ss.str(); | err = ss.str(); | |||
break; | break; | |||
} | } | |||
} | } | |||
} | } | |||
} | } | |||
if( failed ) { | if( failed ) { | |||
cout << err << endl; | std::cout << err << std::endl; | |||
assert( 0 ); | verify( 0 ); | |||
} | } | |||
} | } | |||
void leaving(mid m) { | void leaving(mid m) { | |||
if( this == 0 || m == 0 ) return; // still in startup pre-main( ) | if( this == 0 || m == 0 ) return; // still in startup pre-main( ) | |||
Preceeding& preceeding = *us.get(); | Preceeding& preceeding = *us.get(); | |||
preceeding[m]--; | preceeding[m]--; | |||
if( preceeding[m] < 0 ) { | if( preceeding[m] < 0 ) { | |||
cout << "ERROR: lock count for " << m << " is " << preceedi | std::cout << "ERROR: lock count for " << m << " is " << pre | |||
ng[m] << endl; | ceeding[m] << std::endl; | |||
assert( preceeding[m] >= 0 ); | verify( preceeding[m] >= 0 ); | |||
} | } | |||
} | } | |||
}; | }; | |||
extern MutexDebugger &mutexDebugger; | extern MutexDebugger &mutexDebugger; | |||
} | } | |||
End of changes. 14 change blocks. | ||||
19 lines changed or deleted | 33 lines changed or added | |||
mvar.h | mvar.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/recursive_mutex.hpp> | ||||
#include <boost/thread/condition.hpp> | ||||
namespace mongo { | namespace mongo { | |||
/* This is based on haskell's MVar synchronization primitive: | /* This is based on haskell's MVar synchronization primitive: | |||
* http://www.haskell.org/ghc/docs/latest/html/libraries/base/Control-C oncurrent-MVar.html | * http://www.haskell.org/ghc/docs/latest/html/libraries/base/Control-C oncurrent-MVar.html | |||
* | * | |||
* It is a thread-safe queue that can hold at most one object. | * It is a thread-safe queue that can hold at most one object. | |||
* You can also think of it as a box that can be either full or empty. | * You can also think of it as a box that can be either full or empty. | |||
*/ | */ | |||
template <typename T> | template <typename T> | |||
End of changes. 1 change blocks. | ||||
0 lines changed or deleted | 3 lines changed or added | |||
namespace-inl.h | namespace-inl.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 "namespace.h" | #include "mongo/db/namespace.h" | |||
namespace mongo { | namespace mongo { | |||
inline Namespace& Namespace::operator=(const char *ns) { | inline Namespace& Namespace::operator=(const char *ns) { | |||
// we fill the remaining space with all zeroes here. as the full N amespace struct is in | // we fill the remaining space with all zeroes here. as the full N amespace struct is in | |||
// the datafiles (the .ns files specifically), that is helpful as t hen they are deterministic | // the datafiles (the .ns files specifically), that is helpful as t hen they are deterministic | |||
// in the bytes they have for a given sequence of operations. that makes testing and debugging | // in the bytes they have for a given sequence of operations. that makes testing and debugging | |||
// the data files easier. | // the data files easier. | |||
// | // | |||
// if profiling indicates this method is a significant bottleneck, we could have a version we | // if profiling indicates this method is a significant bottleneck, we could have a version we | |||
skipping to change at line 66 | skipping to change at line 66 | |||
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 | |||
} | } | |||
/* future : this doesn't need to be an inline. */ | /* future : this doesn't need to be an inline. */ | |||
inline string Namespace::getSisterNS( const char * local ) const { | inline string Namespace::getSisterNS( const char * local ) const { | |||
assert( local && local[0] != '.' ); | verify( local && local[0] != '.' ); | |||
string old(buf); | string old(buf); | |||
if ( old.find( "." ) != string::npos ) | if ( old.find( "." ) != string::npos ) | |||
old = old.substr( 0 , old.find( "." ) ); | old = old.substr( 0 , old.find( "." ) ); | |||
return old + "." + local; | return old + "." + local; | |||
} | } | |||
inline IndexDetails& NamespaceDetails::idx(int idxNo, bool missingExpec | } // namespace mongo | |||
ted ) { | ||||
if( idxNo < NIndexesBase ) { | ||||
IndexDetails& id = _indexes[idxNo]; | ||||
return id; | ||||
} | ||||
Extra *e = extra(); | ||||
if ( ! e ) { | ||||
if ( missingExpected ) | ||||
throw MsgAssertionException( 13283 , "Missing Extra" ); | ||||
massert(14045, "missing Extra", e); | ||||
} | ||||
int i = idxNo - NIndexesBase; | ||||
if( i >= NIndexesExtra ) { | ||||
e = e->next(this); | ||||
if ( ! e ) { | ||||
if ( missingExpected ) | ||||
throw MsgAssertionException( 14823 , "missing extra" ); | ||||
massert(14824, "missing Extra", e); | ||||
} | ||||
i -= NIndexesExtra; | ||||
} | ||||
return e->details[i]; | ||||
} | ||||
inline int NamespaceDetails::idxNo(IndexDetails& idx) { | ||||
IndexIterator i = ii(); | ||||
while( i.more() ) { | ||||
if( &i.next() == &idx ) | ||||
return i.pos()-1; | ||||
} | ||||
massert( 10349 , "E12000 idxNo fails", false); | ||||
return -1; | ||||
} | ||||
inline int NamespaceDetails::findIndexByKeyPattern(const BSONObj& keyPa | ||||
ttern) { | ||||
IndexIterator i = ii(); | ||||
while( i.more() ) { | ||||
if( i.next().keyPattern() == keyPattern ) | ||||
return i.pos()-1; | ||||
} | ||||
return -1; | ||||
} | ||||
// @return offset in indexes[] | ||||
inline int NamespaceDetails::findIndexByName(const char *name) { | ||||
IndexIterator i = ii(); | ||||
while( i.more() ) { | ||||
if ( strcmp(i.next().info.obj().getStringField("name"),name) == | ||||
0 ) | ||||
return i.pos()-1; | ||||
} | ||||
return -1; | ||||
} | ||||
inline NamespaceDetails::IndexIterator::IndexIterator(NamespaceDetails | ||||
*_d) { | ||||
d = _d; | ||||
i = 0; | ||||
n = d->nIndexes; | ||||
} | ||||
} | ||||
End of changes. 3 change blocks. | ||||
2 lines changed or deleted | 2 lines changed or added | |||
namespace.h | namespace.h | |||
---|---|---|---|---|
skipping to change at line 21 | skipping to change at line 21 | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Affero General Public License for more details. | * GNU Affero General Public License for more details. | |||
* | * | |||
* You should have received a copy of the GNU Affero General Public Licen se | * You should have received a copy of the GNU Affero General Public Licen se | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../pch.h" | #include "pch.h" | |||
#include "namespacestring.h" | ||||
#include "jsobj.h" | ||||
#include "querypattern.h" | ||||
#include "diskloc.h" | ||||
#include "../util/hashtab.h" | ||||
#include "mongommf.h" | ||||
#include "d_concurrency.h" | ||||
namespace mongo { | #include <cstring> | |||
#include <string> | ||||
class Database; | namespace mongo { | |||
#pragma pack(1) | #pragma pack(1) | |||
/* This helper class is used to make the HashMap below in NamespaceInde | ||||
x e.g. see line: | ||||
HashTable<Namespace,NamespaceDetails> *ht; | ||||
*/ | ||||
class Namespace { | class Namespace { | |||
public: | public: | |||
explicit Namespace(const char *ns) { *this = ns; } | explicit Namespace(const char *ns) { *this = ns; } | |||
Namespace& operator=(const char *ns); | Namespace& operator=(const char *ns); | |||
bool hasDollarSign() const { return strchr( buf , '$' ) > 0; } | bool hasDollarSign() const { return strchr( buf , '$' ) > 0; } | |||
void kill() { buf[0] = 0x7f; } | void kill() { buf[0] = 0x7f; } | |||
bool operator==(const char *r) const { return strcmp(buf, r) == 0; } | bool operator==(const char *r) const { return strcmp(buf, r) == 0; } | |||
bool operator==(const Namespace& r) const { return strcmp(buf, r.bu f) == 0; } | bool operator==(const Namespace& r) const { return strcmp(buf, r.bu f) == 0; } | |||
int hash() const; // value returned is always > 0 | int hash() const; // value returned is always > 0 | |||
size_t size() const { return strlen( buf ); } | size_t size() const { return strlen( buf ); } | |||
string toString() const { return (string) buf; } | string toString() const { return buf; } | |||
operator string() const { return (string) buf; } | operator string() const { return buf; } | |||
/* NamespaceDetails::Extra was added after fact to allow chaining o f data blocks to support more than 10 indexes | /* NamespaceDetails::Extra was added after fact to allow chaining o f data blocks to support more than 10 indexes | |||
(more than 10 IndexDetails). It's a bit hacky because of this l ate addition with backward | (more than 10 IndexDetails). It's a bit hacky because of this l ate addition with backward | |||
file support. */ | file support. */ | |||
string extraName(int i) const; | string extraName(int i) const; | |||
bool isExtra() const; /* ends with $extr... -- when true an extra b lock not a normal NamespaceDetails block */ | bool isExtra() const; /* ends with $extr... -- when true an extra b lock not a normal NamespaceDetails block */ | |||
/** ( foo.bar ).getSisterNS( "blah" ) == foo.blah | /** ( foo.bar ).getSisterNS( "blah" ) == foo.blah | |||
perhaps this should move to the NamespaceString helper? | perhaps this should move to the NamespaceString helper? | |||
*/ | */ | |||
string getSisterNS( const char * local ) const; | string getSisterNS( const char * local ) const; | |||
enum MaxNsLenValue { MaxNsLen = 128 }; | enum MaxNsLenValue { MaxNsLen = 128 }; | |||
private: | private: | |||
char buf[MaxNsLen]; | char buf[MaxNsLen]; | |||
}; | }; | |||
#pragma pack() | #pragma pack() | |||
} // namespace mongo | } // namespace mongo | |||
#include "index.h" | ||||
namespace mongo { | ||||
/** @return true if a client can modify this namespace even though it i | ||||
s under ".system." | ||||
For example <dbname>.system.users is ok for regular clients to upda | ||||
te. | ||||
@param write used when .system.js | ||||
*/ | ||||
bool legalClientSystemNS( const string& ns , bool write ); | ||||
/* 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. | ||||
*/ | ||||
const int Buckets = 19; | ||||
const int MaxBucket = 18; | ||||
extern int bucketSizes[]; | ||||
#pragma pack(1) | ||||
/* NamespaceDetails : this is the "header" for a collection that has al | ||||
l its details. | ||||
It's in the .ns file and this is a memory mapped region (thus the pa | ||||
ck pragma above). | ||||
*/ | ||||
class NamespaceDetails { | ||||
public: | ||||
enum { NIndexesMax = 64, NIndexesExtra = 30, NIndexesBase = 10 }; | ||||
/*-------- data fields, as present on disk : */ | ||||
DiskLoc firstExtent; | ||||
DiskLoc lastExtent; | ||||
/* NOTE: capped collections v1 override the meaning of deletedList. | ||||
deletedList[0] points to a list of free records (DeletedRe | ||||
cord's) for all extents in | ||||
the capped namespace. | ||||
deletedList[1] points to the last record in the prev exten | ||||
t. When the "current extent" | ||||
changes, this value is updated. !deletedList[1].isValid() | ||||
when this value is not | ||||
yet computed. | ||||
*/ | ||||
DiskLoc deletedList[Buckets]; | ||||
// ofs 168 (8 byte aligned) | ||||
struct Stats { | ||||
// datasize and nrecords MUST Be adjacent code assumes! | ||||
long long datasize; // this includes padding, but not record he | ||||
aders | ||||
long long nrecords; | ||||
} stats; | ||||
int lastExtentSize; | ||||
int nIndexes; | ||||
private: | ||||
// ofs 192 | ||||
IndexDetails _indexes[NIndexesBase]; | ||||
public: | ||||
// ofs 352 (16 byte aligned) | ||||
int capped; | ||||
int max; // max # of objects for a cap | ||||
ped table. TODO: should this be 64 bit? | ||||
double paddingFactor; // 1.0 = no padding. | ||||
// ofs 386 (16) | ||||
int flags; | ||||
DiskLoc capExtent; | ||||
DiskLoc capFirstNewRecord; | ||||
unsigned short dataFileVersion; // NamespaceDetails version. | ||||
So we can do backward compatibility in the future. See filever.h | ||||
unsigned short indexFileVersion; | ||||
unsigned long long multiKeyIndexBits; | ||||
private: | ||||
// ofs 400 (16) | ||||
unsigned long long reservedA; | ||||
long long extraOffset; // where the $extra info is l | ||||
ocated (bytes relative to this) | ||||
public: | ||||
int indexBuildInProgress; // 1 if in prog | ||||
unsigned reservedB; | ||||
// ofs 424 (8) | ||||
struct Capped2 { | ||||
unsigned long long cc2_ptr; // see capped.cpp | ||||
unsigned fileNumber; | ||||
} capped2; | ||||
char reserved[60]; | ||||
/*-------- end data 496 bytes */ | ||||
explicit NamespaceDetails( const DiskLoc &loc, bool _capped ); | ||||
class Extra { | ||||
long long _next; | ||||
public: | ||||
IndexDetails details[NIndexesExtra]; | ||||
private: | ||||
unsigned reserved2; | ||||
unsigned reserved3; | ||||
Extra(const Extra&) { assert(false); } | ||||
Extra& operator=(const Extra& r) { assert(false); return *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) { *getDur().writing(&_next) = ofs; } | ||||
void copy(NamespaceDetails *d, const Extra& e) { | ||||
memcpy(this, &e, sizeof(Extra)); | ||||
_next = 0; | ||||
} | ||||
}; | ||||
Extra* extra() { | ||||
if( extraOffset == 0 ) return 0; | ||||
return (Extra *) (((char *) this) + extraOffset); | ||||
} | ||||
/* 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 | ||||
/* called when loaded from disk */ | ||||
void onLoad(const Namespace& k); | ||||
/* dump info on this namespace. for debugging. */ | ||||
void dump(const Namespace& k); | ||||
/* dump info on all extents for this namespace. for debugging. */ | ||||
void dumpExtents(); | ||||
private: | ||||
Extent *theCapExtent() const { return capExtent.ext(); } | ||||
void advanceCapExtent( const char *ns ); | ||||
DiskLoc __capAlloc(int len); | ||||
DiskLoc cappedAlloc(const char *ns, int len); | ||||
DiskLoc &cappedFirstDeletedInCurExtent(); | ||||
bool nextIsInCapExtent( const DiskLoc &dl ) const; | ||||
public: | ||||
DiskLoc& cappedListOfAllDeletedRecords() { return deletedList[0]; } | ||||
DiskLoc& cappedLastDelRecLastExtent() { return deletedList[1]; } | ||||
void cappedDumpDelInfo(); | ||||
bool capLooped() const { return capped && capFirstNewRecord.isValid | ||||
(); } | ||||
bool inCapExtent( const DiskLoc &dl ) const; | ||||
void cappedCheckMigrate(); | ||||
/** | ||||
* Truncate documents newer than the document at 'end' from the cap | ||||
ped | ||||
* collection. The collection cannot be completely emptied using t | ||||
his | ||||
* function. An assertion will be thrown if that is attempted. | ||||
* @param inclusive - Truncate 'end' as well iff true | ||||
*/ | ||||
void cappedTruncateAfter(const char *ns, DiskLoc end, bool inclusiv | ||||
e); | ||||
/** Remove all documents from the capped collection */ | ||||
void emptyCappedCollection(const char *ns); | ||||
/* when a background index build is in progress, we don't count the | ||||
index in nIndexes until | ||||
complete, yet need to still use it in _indexRecord() - thus we u | ||||
se this function for that. | ||||
*/ | ||||
int nIndexesBeingBuilt() const { return nIndexes + indexBuildInProg | ||||
ress; } | ||||
/* NOTE: be careful with flags. are we manipulating them in read l | ||||
ocks? if so, | ||||
this isn't thread safe. TODO | ||||
*/ | ||||
enum NamespaceFlags { | ||||
Flag_HaveIdIndex = 1 << 0 // set when we have _id index (ONLY i | ||||
f ensureIdIndex was called -- 0 if that has never been called) | ||||
}; | ||||
IndexDetails& idx(int idxNo, bool missingExpected = false ); | ||||
/** get the IndexDetails for the index currently being built in the | ||||
background. (there is at most one) */ | ||||
IndexDetails& inProgIdx() { | ||||
DEV assert(indexBuildInProgress); | ||||
return idx(nIndexes); | ||||
} | ||||
class IndexIterator { | ||||
public: | ||||
int pos() { return i; } // note this is the next one to come | ||||
bool more() { return i < n; } | ||||
IndexDetails& next() { return d->idx(i++); } | ||||
private: | ||||
friend class NamespaceDetails; | ||||
int i, n; | ||||
NamespaceDetails *d; | ||||
IndexIterator(NamespaceDetails *_d); | ||||
}; | ||||
IndexIterator ii() { return IndexIterator(this); } | ||||
/* hackish - find our index # in the indexes array */ | ||||
int idxNo(IndexDetails& idx); | ||||
/* multikey indexes are indexes where there are more than one key i | ||||
n the index | ||||
for a single document. see multikey in wiki. | ||||
for these, we have to do some dedup work on queries. | ||||
*/ | ||||
bool isMultikey(int i) const { return (multiKeyIndexBits & (((unsig | ||||
ned long long) 1) << i)) != 0; } | ||||
void setIndexIsMultikey(int i) { | ||||
dassert( i < NIndexesMax ); | ||||
unsigned long long x = ((unsigned long long) 1) << i; | ||||
if( multiKeyIndexBits & x ) return; | ||||
*getDur().writing(&multiKeyIndexBits) |= x; | ||||
} | ||||
void clearIndexIsMultikey(int i) { | ||||
dassert( i < NIndexesMax ); | ||||
unsigned long long x = ((unsigned long long) 1) << i; | ||||
if( (multiKeyIndexBits & x) == 0 ) return; | ||||
*getDur().writing(&multiKeyIndexBits) &= ~x; | ||||
} | ||||
/* add a new index. does not add to system.indexes etc. - just to | ||||
NamespaceDetails. | ||||
caller must populate returned object. | ||||
*/ | ||||
IndexDetails& addIndex(const char *thisns, bool resetTransient=true | ||||
); | ||||
void aboutToDeleteAnIndex() { | ||||
*getDur().writing(&flags) = flags & ~Flag_HaveIdIndex; | ||||
} | ||||
/* returns index of the first index in which the field is present. | ||||
-1 if not present. */ | ||||
int fieldIsIndexed(const char *fieldName); | ||||
/* called to indicate that an update fit in place. | ||||
fits also called on an insert -- idea there is that if you had s | ||||
ome mix and then went to | ||||
pure inserts it would adapt and PF would trend to 1.0. note upd | ||||
ate calls insert on a move | ||||
so there is a double count there that must be adjusted for below | ||||
. | ||||
todo: greater sophistication could be helpful and added later. | ||||
for example the absolute | ||||
size of documents might be considered -- in some cases sma | ||||
ller ones are more likely | ||||
to grow than larger ones in the same collection? (not alwa | ||||
ys) | ||||
*/ | ||||
void paddingFits() { | ||||
MONGO_SOMETIMES(sometimes, 4) { // do this on a sampled basis t | ||||
o journal less | ||||
double x = paddingFactor - 0.001; | ||||
if ( x >= 1.0 ) { | ||||
*getDur().writing(&paddingFactor) = x; | ||||
//getDur().setNoJournal(&paddingFactor, &x, sizeof(x)); | ||||
} | ||||
} | ||||
} | ||||
void paddingTooSmall() { | ||||
MONGO_SOMETIMES(sometimes, 4) { // do this on a sampled basis t | ||||
o journal less | ||||
/* the more indexes we have, the higher the cost of a move. | ||||
so we take that into | ||||
account herein. note on a move that insert() calls padd | ||||
ingFits(), thus | ||||
here for example with no inserts and nIndexes = 1 we hav | ||||
e | ||||
.001*4-.001 or a 3:1 ratio to non moves -> 75% nonmoves. | ||||
insert heavy | ||||
can pushes this down considerably. further tweaking will | ||||
be a good idea but | ||||
this should be an adequate starting point. | ||||
*/ | ||||
double N = min(nIndexes,7) + 3; | ||||
double x = paddingFactor + (0.001 * N); | ||||
if ( x <= 2.0 ) { | ||||
*getDur().writing(&paddingFactor) = x; | ||||
//getDur().setNoJournal(&paddingFactor, &x, sizeof(x)); | ||||
} | ||||
} | ||||
} | ||||
// @return offset in indexes[] | ||||
int findIndexByName(const char *name); | ||||
// @return offset in indexes[] | ||||
int findIndexByKeyPattern(const BSONObj& keyPattern); | ||||
void findIndexByType( const string& name , vector<int>& matches ) { | ||||
IndexIterator i = ii(); | ||||
while ( i.more() ) { | ||||
if ( i.next().getSpec().getTypeName() == name ) | ||||
matches.push_back( i.pos() - 1 ); | ||||
} | ||||
} | ||||
/* @return -1 = not found | ||||
generally id is first index, so not that expensive an operation | ||||
(assuming present). | ||||
*/ | ||||
int findIdIndex() { | ||||
IndexIterator i = ii(); | ||||
while( i.more() ) { | ||||
if( i.next().isIdIndex() ) | ||||
return i.pos()-1; | ||||
} | ||||
return -1; | ||||
} | ||||
bool haveIdIndex() { | ||||
return (flags & NamespaceDetails::Flag_HaveIdIndex) || findIdIn | ||||
dex() >= 0; | ||||
} | ||||
/* return which "deleted bucket" for this size object */ | ||||
static int bucket(int n) { | ||||
for ( int i = 0; i < Buckets; i++ ) | ||||
if ( bucketSizes[i] > n ) | ||||
return i; | ||||
return Buckets-1; | ||||
} | ||||
/* predetermine location of the next alloc without actually doing i | ||||
t. | ||||
if cannot predetermine returns null (so still call alloc() then) | ||||
*/ | ||||
DiskLoc allocWillBeAt(const char *ns, int lenToAlloc); | ||||
/* allocate a new record. lenToAlloc includes headers. */ | ||||
DiskLoc alloc(const char *ns, int lenToAlloc, DiskLoc& extentLoc); | ||||
/* add a given record to the deleted chains for this NS */ | ||||
void addDeletedRec(DeletedRecord *d, DiskLoc dloc); | ||||
void dumpDeleted(set<DiskLoc> *extents = 0); | ||||
// Start from firstExtent by default. | ||||
DiskLoc firstRecord( const DiskLoc &startExtent = DiskLoc() ) const | ||||
; | ||||
// Start from lastExtent by default. | ||||
DiskLoc lastRecord( const DiskLoc &startExtent = DiskLoc() ) const; | ||||
long long storageSize( int * numExtents = 0 , BSONArrayBuilder * ex | ||||
tentInfo = 0 ) const; | ||||
int averageObjectSize() { | ||||
if ( stats.nrecords == 0 ) | ||||
return 5; | ||||
return (int) (stats.datasize / stats.nrecords); | ||||
} | ||||
NamespaceDetails *writingWithoutExtra() { | ||||
return ( NamespaceDetails* ) getDur().writingPtr( this, sizeof( | ||||
NamespaceDetails ) ); | ||||
} | ||||
/** Make all linked Extra objects writeable as well */ | ||||
NamespaceDetails *writingWithExtra(); | ||||
private: | ||||
DiskLoc _alloc(const char *ns, int len); | ||||
void maybeComplain( const char *ns, int len ) const; | ||||
DiskLoc __stdAlloc(int len, bool willBeAt); | ||||
void compact(); // combine adjacent deleted records | ||||
friend class NamespaceIndex; | ||||
struct ExtraOld { | ||||
// note we could use this field for more chaining later, so don | ||||
't waste it: | ||||
unsigned long long reserved1; | ||||
IndexDetails details[NIndexesExtra]; | ||||
unsigned reserved2; | ||||
unsigned reserved3; | ||||
}; | ||||
/** Update cappedLastDelRecLastExtent() after capExtent changed in | ||||
cappedTruncateAfter() */ | ||||
void cappedTruncateLastDelUpdate(); | ||||
BOOST_STATIC_ASSERT( NIndexesMax <= NIndexesBase + NIndexesExtra*2 | ||||
); | ||||
BOOST_STATIC_ASSERT( NIndexesMax <= 64 ); // multiKey bits | ||||
BOOST_STATIC_ASSERT( sizeof(NamespaceDetails::ExtraOld) == 496 ); | ||||
BOOST_STATIC_ASSERT( sizeof(NamespaceDetails::Extra) == 496 ); | ||||
}; // NamespaceDetails | ||||
#pragma pack() | ||||
/* NamespaceDetailsTransient | ||||
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 | ||||
information. | ||||
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; | ||||
as currently used that does not cause anything terrible to | ||||
happen. | ||||
todo: cleanup code, need abstractions and separation | ||||
*/ | ||||
// todo: multiple db's with the same name (repairDatbase) is not handle | ||||
d herein. that may be | ||||
// the way to go, if not used by repair, but need some sort of en | ||||
forcement / asserts. | ||||
class NamespaceDetailsTransient : boost::noncopyable { | ||||
BOOST_STATIC_ASSERT( sizeof(NamespaceDetails) == 496 ); | ||||
//Database *database; | ||||
const string _ns; | ||||
void reset(); | ||||
static std::map< string, shared_ptr< NamespaceDetailsTransient > > | ||||
_nsdMap; | ||||
NamespaceDetailsTransient(Database*,const char *ns); | ||||
public: | ||||
~NamespaceDetailsTransient(); | ||||
void addedIndex() { assertInWriteLock(); reset(); } | ||||
void deletedIndex() { assertInWriteLock(); reset(); } | ||||
/* Drop cached information on all namespaces beginning with the spe | ||||
cified prefix. | ||||
Can be useful as index namespaces share the same start as the re | ||||
gular collection. | ||||
SLOW - sequential scan of all NamespaceDetailsTransient objects | ||||
*/ | ||||
static void clearForPrefix(const char *prefix); | ||||
static void eraseForPrefix(const char *prefix); | ||||
/** | ||||
* @return a cursor interface to the query optimizer. The implemen | ||||
tation may | ||||
* utilize a single query plan or interleave results from multiple | ||||
query | ||||
* plans before settling on a single query plan. Note that the sch | ||||
ema of | ||||
* currKey() documents, the matcher(), and the isMultiKey() nature | ||||
of the | ||||
* cursor may change over the course of iteration. | ||||
* | ||||
* @param query - Query used to select indexes and populate matcher | ||||
s; this is not copied, and must outlive the Cursor | ||||
* | ||||
* @param order - Required ordering spec for documents produced by | ||||
this cursor, | ||||
* empty object default indicates no order requirement. If no inde | ||||
x exists that | ||||
* satisfies the required sort order, an empty shared_ptr is return | ||||
ed. This is nit copied, and must outlive the Cursor | ||||
* | ||||
* @param requireIndex - If true, no unindexed (ie collection scan) | ||||
cursors are | ||||
* used to generate the returned cursor. If an unindexed cursor is | ||||
required, an | ||||
* assertion is raised by the cursor during iteration. | ||||
* | ||||
* @param simpleEqualityMatch - Set to true for certain simple quer | ||||
ies - | ||||
* see queryoptimizer.cpp. | ||||
* | ||||
* The returned cursor may @throw inside of advance() or recoverFro | ||||
mYield() in | ||||
* certain error cases, for example if a capped overrun occurred du | ||||
ring a yield. | ||||
* This indicates that the cursor was unable to perform a complete | ||||
scan. | ||||
* | ||||
* This is a work in progress. Partial list of features not yet im | ||||
plemented: | ||||
* - covered indexes | ||||
* - in memory sorting | ||||
*/ | ||||
static shared_ptr<Cursor> getCursor( const char *ns, const BSONObj | ||||
&query, | ||||
const BSONObj &order = BSONObj( | ||||
), bool requireIndex = false, | ||||
bool *simpleEqualityMatch = 0 ) | ||||
; | ||||
/** | ||||
* @return a single cursor that may work well for the given query. | ||||
* It is possible no cursor is returned if the sort is not supporte | ||||
d by an index. Clients are responsible | ||||
* for checking this if they are not sure an index for a sort exist | ||||
s, and defaulting to a non-sort if | ||||
* no suitable indices exist. | ||||
*/ | ||||
static shared_ptr<Cursor> bestGuessCursor( const char *ns, const BS | ||||
ONObj &query, const BSONObj &sort ); | ||||
/* indexKeys() cache ---------------------------------------------- | ||||
------ */ | ||||
/* assumed to be in write lock for this */ | ||||
private: | ||||
bool _keysComputed; | ||||
set<string> _indexKeys; | ||||
void computeIndexKeys(); | ||||
public: | ||||
/* get set of index keys for this namespace. handy to quickly chec | ||||
k if a given | ||||
field is indexed (Note it might be a secondary component of a co | ||||
mpound index.) | ||||
*/ | ||||
set<string>& indexKeys() { | ||||
DEV assertInWriteLock(); | ||||
if ( !_keysComputed ) | ||||
computeIndexKeys(); | ||||
return _indexKeys; | ||||
} | ||||
/* IndexSpec caching */ | ||||
private: | ||||
map<const IndexDetails*,IndexSpec> _indexSpecs; | ||||
static SimpleMutex _isMutex; | ||||
public: | ||||
const IndexSpec& getIndexSpec( const IndexDetails * details ) { | ||||
IndexSpec& spec = _indexSpecs[details]; | ||||
if ( ! spec._finishedInit ) { | ||||
SimpleMutex::scoped_lock lk(_isMutex); | ||||
if ( ! spec._finishedInit ) { | ||||
spec.reset( details ); | ||||
assert( spec._finishedInit ); | ||||
} | ||||
} | ||||
return spec; | ||||
} | ||||
/* query cache (for query optimizer) ------------------------------ | ||||
------- */ | ||||
private: | ||||
int _qcWriteCount; | ||||
map< QueryPattern, pair< BSONObj, long long > > _qcCache; | ||||
static NamespaceDetailsTransient& make_inlock(const char *ns); | ||||
public: | ||||
static SimpleMutex _qcMutex; | ||||
/* you must be in the qcMutex when calling this. | ||||
A NamespaceDetailsTransient object will not go out of scope on y | ||||
ou if you are | ||||
d.dbMutex.atLeastReadLocked(), so you do't have to stay locked. | ||||
Creates a NamespaceDetailsTransient before returning if one DNE. | ||||
todo: avoid creating too many on erroneous ns queries. | ||||
*/ | ||||
static NamespaceDetailsTransient& get_inlock(const char *ns); | ||||
static NamespaceDetailsTransient& get(const char *ns) { | ||||
SimpleMutex::scoped_lock lk(_qcMutex); | ||||
return get_inlock(ns); | ||||
} | ||||
void clearQueryCache() { // public for unit tests | ||||
_qcCache.clear(); | ||||
_qcWriteCount = 0; | ||||
} | ||||
/* you must notify the cache if you are doing writes, as query plan | ||||
optimality will change */ | ||||
void notifyOfWriteOp() { | ||||
if ( _qcCache.empty() ) | ||||
return; | ||||
if ( ++_qcWriteCount >= 100 ) | ||||
clearQueryCache(); | ||||
} | ||||
BSONObj indexForPattern( const QueryPattern &pattern ) { | ||||
return _qcCache[ pattern ].first; | ||||
} | ||||
long long nScannedForPattern( const QueryPattern &pattern ) { | ||||
return _qcCache[ pattern ].second; | ||||
} | ||||
void registerIndexForPattern( const QueryPattern &pattern, const BS | ||||
ONObj &indexKey, long long nScanned ) { | ||||
_qcCache[ pattern ] = make_pair( indexKey, nScanned ); | ||||
} | ||||
}; /* NamespaceDetailsTransient */ | ||||
inline NamespaceDetailsTransient& NamespaceDetailsTransient::get_inlock | ||||
(const char *ns) { | ||||
std::map< string, shared_ptr< NamespaceDetailsTransient > >::iterat | ||||
or i = _nsdMap.find(ns); | ||||
if( i != _nsdMap.end() && | ||||
i->second.get() ) { // could be null ptr from clearForPrefix | ||||
return *i->second; | ||||
} | ||||
return make_inlock(ns); | ||||
} | ||||
/* 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.) | ||||
*/ | ||||
class NamespaceIndex { | ||||
public: | ||||
NamespaceIndex(const string &dir, const string &database) : | ||||
ht( 0 ), dir_( dir ), database_( database ) {} | ||||
/* returns true if new db will be created if we init lazily */ | ||||
bool exists() const; | ||||
void init() { | ||||
if( !ht ) | ||||
_init(); | ||||
} | ||||
void add_ns(const char *ns, DiskLoc& loc, bool capped); | ||||
void add_ns( const char *ns, const NamespaceDetails &details ); | ||||
NamespaceDetails* details(const char *ns) { | ||||
if ( !ht ) | ||||
return 0; | ||||
Namespace n(ns); | ||||
NamespaceDetails *d = ht->get(n); | ||||
if ( d && d->capped ) | ||||
d->cappedCheckMigrate(); | ||||
return d; | ||||
} | ||||
void kill_ns(const char *ns); | ||||
bool find(const char *ns, DiskLoc& loc) { | ||||
NamespaceDetails *l = details(ns); | ||||
if ( l ) { | ||||
loc = l->firstExtent; | ||||
return true; | ||||
} | ||||
return false; | ||||
} | ||||
bool allocated() const { return ht != 0; } | ||||
void getNamespaces( list<string>& tofill , bool onlyCollections = t | ||||
rue ) const; | ||||
NamespaceDetails::Extra* newExtra(const char *ns, int n, NamespaceD | ||||
etails *d); | ||||
boost::filesystem::path path() const; | ||||
unsigned long long fileLength() const { return f.length(); } | ||||
private: | ||||
void _init(); | ||||
void maybeMkdir() const; | ||||
MongoMMF f; | ||||
HashTable<Namespace,NamespaceDetails> *ht; | ||||
string dir_; | ||||
string database_; | ||||
}; | ||||
extern string dbpath; // --dbpath parm | ||||
extern bool directoryperdb; | ||||
// Rename a namespace within current 'client' db. | ||||
// (Arguments should include db name) | ||||
void renameNamespace( const char *from, const char *to, bool stayTemp); | ||||
} // namespace mongo | ||||
End of changes. 6 change blocks. | ||||
16 lines changed or deleted | 6 lines changed or added | |||
namespacestring.h | namespacestring.h | |||
---|---|---|---|---|
skipping to change at line 29 | skipping to change at line 29 | |||
#pragma once | #pragma once | |||
#include <string> | #include <string> | |||
namespace mongo { | namespace mongo { | |||
using std::string; | using std::string; | |||
/* in the mongo source code, "client" means "database". */ | /* in the mongo source code, "client" means "database". */ | |||
const int MaxDatabaseNameLen = 256; // max str len for the db name, inc luding null char | const int MaxDatabaseNameLen = 128; // max str len for the db name, inc luding null char | |||
/* 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") | |||
skipping to change at line 122 | skipping to change at line 122 | |||
void init(const char *ns) { | void init(const char *ns) { | |||
const char *p = strchr(ns, '.'); | const char *p = strchr(ns, '.'); | |||
if( p == 0 ) return; | if( p == 0 ) return; | |||
db = string(ns, p - ns); | db = string(ns, p - ns); | |||
coll = p + 1; | coll = p + 1; | |||
} | } | |||
}; | }; | |||
// "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; | for( int i = 0; i < MaxDatabaseNameLen; i++ ) { | |||
char *q = database; | database[i] = ns[i]; | |||
while ( *p != '.' ) { | if( database[i] == '.' ) { | |||
if ( *p == 0 ) | database[i] = 0; | |||
break; | return; | |||
*q++ = *p++; | } | |||
} | if( database[i] == 0 ) { | |||
*q = 0; | return; | |||
if (q-database>=MaxDatabaseNameLen) { | } | |||
log() << "nsToDatabase: ns too long. terminating, buf overrun c | ||||
ondition" << endl; | ||||
dbexit( EXIT_POSSIBLE_CORRUPTION ); | ||||
} | } | |||
// other checks should have happened already, this is defensive. th | ||||
us massert not uassert | ||||
massert(10078, "nsToDatabase: ns too long", false); | ||||
} | } | |||
inline string nsToDatabase(const char *ns) { | inline string nsToDatabase(const char *ns) { | |||
char buf[MaxDatabaseNameLen]; | char buf[MaxDatabaseNameLen]; | |||
nsToDatabase(ns, buf); | nsToDatabase(ns, buf); | |||
return buf; | return buf; | |||
} | } | |||
inline string nsToDatabase(const string& ns) { | inline string nsToDatabase(const string& ns) { | |||
size_t i = ns.find( '.' ); | size_t i = ns.find( '.' ); | |||
if ( i == string::npos ) | if ( i == string::npos ) | |||
return ns; | return ns; | |||
massert(10088, "nsToDatabase: ns too long", i < (size_t)MaxDatabase NameLen); | ||||
return ns.substr( 0 , i ); | return ns.substr( 0 , i ); | |||
} | } | |||
} | } | |||
End of changes. 4 change blocks. | ||||
13 lines changed or deleted | 14 lines changed or added | |||
ntservice.h | ntservice.h | |||
---|---|---|---|---|
skipping to change at line 26 | skipping to change at line 26 | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#if defined(_WIN32) | #if defined(_WIN32) | |||
#include <windows.h> | #include <windows.h> | |||
#include "boost/program_options.hpp" | #include "boost/program_options.hpp" | |||
namespace mongo { | namespace mongo { | |||
struct ntServiceDefaultStrings { | ||||
const wchar_t* serviceName; | ||||
const wchar_t* displayName; | ||||
const wchar_t* serviceDescription; | ||||
}; | ||||
typedef bool ( *ServiceCallback )( void ); | typedef bool ( *ServiceCallback )( void ); | |||
bool serviceParamsCheck( boost::program_options::variables_map& params, | bool serviceParamsCheck( | |||
const std::string dbpath, int argc, char* argv[] ); | boost::program_options::variables_map& params, | |||
const std::string dbpath, | ||||
const ntServiceDefaultStrings& defaultStrings, | ||||
const vector<string>& disallowedOptions, | ||||
int argc, | ||||
char* argv[] | ||||
); | ||||
class ServiceController { | class ServiceController { | |||
public: | public: | |||
ServiceController(); | ServiceController(); | |||
virtual ~ServiceController() {} | virtual ~ServiceController() {} | |||
static bool installService( const std::wstring& serviceName, const | static bool installService( | |||
std::wstring& displayName, const std::wstring& serviceDesc, const std::wstr | const std::wstring& serviceName, | |||
ing& serviceUser, const std::wstring& servicePassword, const std::string db | const std::wstring& displayName, | |||
path, int argc, char* argv[] ); | const std::wstring& serviceDesc, | |||
const std::wstring& serviceUser, | ||||
const std::wstring& servicePassword, | ||||
const std::string dbpath, | ||||
int argc, | ||||
char* argv[] | ||||
); | ||||
static bool removeService( const std::wstring& serviceName ); | static bool removeService( const std::wstring& serviceName ); | |||
static bool startService( const std::wstring& serviceName, ServiceC allback startService ); | static bool startService( const std::wstring& serviceName, ServiceC allback startService ); | |||
static bool reportStatus( DWORD reportState, DWORD waitHint = 0 ); | static bool reportStatus( DWORD reportState, DWORD waitHint = 0 ); | |||
static void WINAPI initService( DWORD argc, LPTSTR *argv ); | static void WINAPI initService( DWORD argc, LPTSTR *argv ); | |||
static void WINAPI serviceCtrl( DWORD ctrlCode ); | static void WINAPI serviceCtrl( DWORD ctrlCode ); | |||
protected: | protected: | |||
static std::wstring _serviceName; | static std::wstring _serviceName; | |||
static SERVICE_STATUS_HANDLE _statusHandle; | static SERVICE_STATUS_HANDLE _statusHandle; | |||
End of changes. 3 change blocks. | ||||
6 lines changed or deleted | 24 lines changed or added | |||
oid.h | oid.h | |||
---|---|---|---|---|
skipping to change at line 44 | skipping to change at line 44 | |||
Typical contents of the BSON ObjectID is a 12-byte value consisting of a 4-byte timestamp (seconds since epoch), | Typical contents of the BSON ObjectID is a 12-byte value consisting of a 4-byte timestamp (seconds since epoch), | |||
a 3-byte machine id, a 2-byte process id, and a 3-byte counter. Not e that the timestamp and counter fields must | a 3-byte machine id, a 2-byte process id, and a 3-byte counter. Not e that the timestamp and counter fields must | |||
be stored big endian unlike the rest of BSON. This is because they are compared byte-by-byte and we want to ensure | be stored big endian unlike the rest of BSON. This is because they are compared byte-by-byte and we want to ensure | |||
a mostly increasing order. | a mostly increasing order. | |||
*/ | */ | |||
class OID { | class OID { | |||
public: | public: | |||
OID() : a(0), b(0) { } | OID() : a(0), b(0) { } | |||
/** init from a 24 char hex string */ | /** init from a 24 char hex string */ | |||
explicit OID(const string &s) { init(s); } | explicit OID(const std::string &s) { init(s); } | |||
/** initialize to 'null' */ | /** initialize to 'null' */ | |||
void clear() { a = 0; b = 0; } | void clear() { a = 0; b = 0; } | |||
const unsigned char *getData() const { return data; } | const unsigned char *getData() const { return data; } | |||
bool operator==(const OID& r) const { return a==r.a && b==r.b; } | bool operator==(const OID& r) const { return a==r.a && b==r.b; } | |||
bool operator!=(const OID& r) const { return a!=r.a || b!=r.b; } | bool operator!=(const OID& r) const { return a!=r.a || b!=r.b; } | |||
int compare( const OID& other ) const { return memcmp( data , other .data , 12 ); } | int compare( const OID& other ) const { return memcmp( data , other .data , 12 ); } | |||
bool operator<( const OID& other ) const { return compare( other ) < 0; } | bool operator<( const OID& other ) const { return compare( other ) < 0; } | |||
bool operator<=( const OID& other ) const { return compare( other ) <= 0; } | bool operator<=( const OID& other ) const { return compare( other ) <= 0; } | |||
/** @return the object ID output as 24 hex digits */ | /** @return the object ID output as 24 hex digits */ | |||
string str() const { return toHexLower(data, 12); } | std::string str() const { return toHexLower(data, 12); } | |||
string toString() const { return str(); } | std::string toString() const { return str(); } | |||
static OID gen() { OID o; o.init(); return o; } | static OID gen() { OID o; o.init(); return o; } | |||
/** sets the contents to a new oid / randomized value */ | /** sets the contents to a new oid / randomized value */ | |||
void init(); | void init(); | |||
/** init from a 24 char hex string */ | /** init from a 24 char hex string */ | |||
void init( string s ); | void init( std::string s ); | |||
/** Set to the min/max OID that could be generated at given timesta mp. */ | /** Set to the min/max OID that could be generated at given timesta mp. */ | |||
void init( Date_t date, bool max=false ); | void init( Date_t date, bool max=false ); | |||
time_t asTimeT(); | time_t asTimeT(); | |||
Date_t asDateT() { return asTimeT() * (long long)1000; } | Date_t asDateT() { return asTimeT() * (long long)1000; } | |||
bool isSet() const { return a || b; } | bool isSet() const { return a || b; } | |||
/** | /** | |||
skipping to change at line 124 | skipping to change at line 124 | |||
}; | }; | |||
unsigned char data[12]; | unsigned char data[12]; | |||
}; | }; | |||
static unsigned ourPid(); | static unsigned ourPid(); | |||
static void foldInPid(MachineAndPid& x); | static void foldInPid(MachineAndPid& x); | |||
static MachineAndPid genMachineAndPid(); | static MachineAndPid genMachineAndPid(); | |||
}; | }; | |||
#pragma pack() | #pragma pack() | |||
ostream& operator<<( ostream &s, const OID &o ); | std::ostream& operator<<( std::ostream &s, const OID &o ); | |||
inline StringBuilder& operator<< (StringBuilder& s, const OID& o) { ret urn (s << o.str()); } | inline StringBuilder& operator<< (StringBuilder& s, const OID& o) { ret urn (s << o.str()); } | |||
/** Formatting mode for generating JSON from BSON. | /** Formatting mode for generating JSON from BSON. | |||
See <http://mongodb.onconfluence.com/display/DOCS/Mongo+Extended+JS ON> | See <http://mongodb.onconfluence.com/display/DOCS/Mongo+Extended+JS ON> | |||
for details. | for details. | |||
*/ | */ | |||
enum JsonStringFormat { | enum JsonStringFormat { | |||
/** strict RFC format */ | /** strict RFC format */ | |||
Strict, | Strict, | |||
/** 10gen format, which is close to JS format. This form is unders tandable by | /** 10gen format, which is close to JS format. This form is unders tandable by | |||
javascript running inside the Mongo server via eval() */ | javascript running inside the Mongo server via eval() */ | |||
TenGen, | TenGen, | |||
/** Javascript JSON compatible */ | /** Javascript JSON compatible */ | |||
JS | JS | |||
}; | }; | |||
ostream& operator<<( ostream &s, const OID &o ); | std::ostream& operator<<( std::ostream &s, const OID &o ); | |||
} | } | |||
End of changes. 5 change blocks. | ||||
6 lines changed or deleted | 6 lines changed or added | |||
oplog.h | oplog.h | |||
---|---|---|---|---|
skipping to change at line 30 | skipping to change at line 30 | |||
local.oplog.$main is the default | local.oplog.$main is the default | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "pdfile.h" | #include "pdfile.h" | |||
#include "db.h" | #include "db.h" | |||
#include "dbhelpers.h" | #include "dbhelpers.h" | |||
#include "clientcursor.h" | #include "clientcursor.h" | |||
#include "../client/dbclient.h" | ||||
#include "../util/optime.h" | #include "../util/optime.h" | |||
#include "../util/timer.h" | #include "../util/timer.h" | |||
namespace mongo { | namespace mongo { | |||
void createOplog(); | void createOplog(); | |||
void _logOpObjRS(const BSONObj& op); | void _logOpObjRS(const BSONObj& op); | |||
/** Write operation to the log (local.oplog.$main) | /** Write operation to the log (local.oplog.$main) | |||
skipping to change at line 52 | skipping to change at line 51 | |||
@param opstr | @param opstr | |||
"i" insert | "i" insert | |||
"u" update | "u" update | |||
"d" delete | "d" delete | |||
"c" db cmd | "c" db cmd | |||
"n" no-op | "n" no-op | |||
"db" declares presence of a database (ns is set to the db name + '. ') | "db" declares presence of a database (ns is set to the db name + '. ') | |||
See _logOp() in oplog.cpp for more details. | See _logOp() in oplog.cpp for more details. | |||
*/ | */ | |||
void logOp(const char *opstr, const char *ns, const BSONObj& obj, BSONO bj *patt = 0, bool *b = 0); | void logOp( const char *opstr, const char *ns, const BSONObj& obj, BSON Obj *patt = 0, bool *b = 0, bool fromMigrate = false ); | |||
void logKeepalive(); | void logKeepalive(); | |||
/** puts obj in the oplog as a comment (a no-op). Just for diags. | /** puts obj in the oplog as a comment (a no-op). Just for diags. | |||
convention is | convention is | |||
{ msg : "text", ... } | { msg : "text", ... } | |||
*/ | */ | |||
void logOpComment(const BSONObj& obj); | void logOpComment(const BSONObj& obj); | |||
void oplogCheckCloseDatabase( Database * db ); | void oplogCheckCloseDatabase( Database * db ); | |||
skipping to change at line 82 | skipping to change at line 81 | |||
/** | /** | |||
* The cursor will attempt to find the first op in the oplog matchi ng the | * The cursor will attempt to find the first op in the oplog matchi ng the | |||
* 'ts' field of the qp's query. | * 'ts' field of the qp's query. | |||
*/ | */ | |||
FindingStartCursor( const QueryPlan & qp ); | FindingStartCursor( const QueryPlan & qp ); | |||
/** @return true if the first matching op in the oplog has been fou nd. */ | /** @return true if the first matching op in the oplog has been fou nd. */ | |||
bool done() const { return !_findingStart; } | bool done() const { return !_findingStart; } | |||
/** @return cursor pointing to the first matching op, if done(). */ | /** @return cursor pointing to the first matching op, if done(). */ | |||
shared_ptr<Cursor> cursor() { verify( 14835, done() ); return _c; } | shared_ptr<Cursor> cursor() { verify( done() ); return _c; } | |||
/** Iterate the cursor, to continue trying to find matching op. */ | /** Iterate the cursor, to continue trying to find matching op. */ | |||
void next(); | void next(); | |||
/** Yield cursor, if not done(). */ | /** Yield cursor, if not done(). */ | |||
bool prepareToYield() { | bool prepareToYield() { | |||
if ( _findingStartCursor ) { | if ( _findingStartCursor ) { | |||
return _findingStartCursor->prepareToYield( _yieldData ); | return _findingStartCursor->prepareToYield( _yieldData ); | |||
} | } | |||
return false; | return false; | |||
skipping to change at line 104 | skipping to change at line 103 | |||
/** Recover from cursor yield. */ | /** Recover from cursor yield. */ | |||
void recoverFromYield() { | void recoverFromYield() { | |||
if ( _findingStartCursor ) { | if ( _findingStartCursor ) { | |||
if ( !ClientCursor::recoverFromYield( _yieldData ) ) { | if ( !ClientCursor::recoverFromYield( _yieldData ) ) { | |||
_findingStartCursor.reset( 0 ); | _findingStartCursor.reset( 0 ); | |||
msgassertedNoTrace( 15889, "FindingStartCursor::recover FromYield() failed to recover" ); | msgassertedNoTrace( 15889, "FindingStartCursor::recover FromYield() failed to recover" ); | |||
} | } | |||
} | } | |||
} | } | |||
/** | ||||
* @return a BasicCursor constructed using a FindingStartCursor wit | ||||
h the provided query and | ||||
* order parameters. | ||||
* @yields the db lock. | ||||
* @asserts on yield recovery failure. | ||||
*/ | ||||
static shared_ptr<Cursor> getCursor( const char *ns, const BSONObj | ||||
&query, const BSONObj &order ); | ||||
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::CleanupPointer _findingStartCursor; | ClientCursor::CleanupPointer _findingStartCursor; | |||
shared_ptr<Cursor> _c; | shared_ptr<Cursor> _c; | |||
ClientCursor::YieldData _yieldData; | ClientCursor::YieldData _yieldData; | |||
End of changes. 4 change blocks. | ||||
3 lines changed or deleted | 13 lines changed or added | |||
oplogreader.h | oplogreader.h | |||
---|---|---|---|---|
/** @file oplogreader.h */ | /** @file oplogreader.h */ | |||
#pragma once | #pragma once | |||
#include "../client/dbclient.h" | ||||
#include "../client/constants.h" | #include "../client/constants.h" | |||
#include "dbhelpers.h" | #include "dbhelpers.h" | |||
#include "mongo/client/dbclientcursor.h" | ||||
namespace mongo { | namespace mongo { | |||
/* started abstracting out the querying of the primary/master's oplog | /* started abstracting out the querying of the primary/master's oplog | |||
still fairly awkward but a start. | still fairly awkward but a start. | |||
*/ | */ | |||
class OplogReader { | class OplogReader { | |||
shared_ptr<DBClientConnection> _conn; | shared_ptr<DBClientConnection> _conn; | |||
shared_ptr<DBClientCursor> cursor; | shared_ptr<DBClientCursor> cursor; | |||
bool _doHandshake; | ||||
int _tailingQueryOptions; | ||||
public: | public: | |||
OplogReader() { } | OplogReader( bool doHandshake = true ); | |||
~OplogReader() { } | ~OplogReader() { } | |||
void resetCursor() { cursor.reset(); } | void resetCursor() { cursor.reset(); } | |||
void resetConnection() { | void resetConnection() { | |||
cursor.reset(); | cursor.reset(); | |||
_conn.reset(); | _conn.reset(); | |||
} | } | |||
DBClientConnection* conn() { return _conn.get(); } | DBClientConnection* conn() { return _conn.get(); } | |||
BSONObj findOne(const char *ns, const Query& q) { | BSONObj findOne(const char *ns, const Query& q) { | |||
return conn()->findOne(ns, q, 0, QueryOption_SlaveOk); | return conn()->findOne(ns, q, 0, QueryOption_SlaveOk); | |||
} | } | |||
skipping to change at line 51 | skipping to change at line 54 | |||
resetCursor(); | resetCursor(); | |||
} | } | |||
} | } | |||
bool haveCursor() { return cursor.get() != 0; } | bool haveCursor() { return cursor.get() != 0; } | |||
/** this is ok but commented out as when used one should consider i f QueryOption_OplogReplay | /** this is ok but commented out as when used one should consider i f QueryOption_OplogReplay | |||
is needed; if not fine, but if so, need to change. | is needed; if not fine, but if so, need to change. | |||
*//* | *//* | |||
void query(const char *ns, const BSONObj& query) { | void query(const char *ns, const BSONObj& query) { | |||
assert( !haveCursor() ); | verify( !haveCursor() ); | |||
cursor.reset( _conn->query(ns, query, 0, 0, 0, QueryOption_Slav eOk).release() ); | cursor.reset( _conn->query(ns, query, 0, 0, 0, QueryOption_Slav eOk).release() ); | |||
}*/ | }*/ | |||
/** this can be used; it is commented out as it does not indicate | /** this can be used; it is commented out as it does not indicate | |||
QueryOption_OplogReplay and that is likely important. could be uncommented | QueryOption_OplogReplay and that is likely important. could be uncommented | |||
just need to add that. | just need to add that. | |||
*/ | */ | |||
/* | /* | |||
void queryGTE(const char *ns, OpTime t) { | void queryGTE(const char *ns, OpTime t) { | |||
BSONObjBuilder q; | BSONObjBuilder q; | |||
q.appendDate("$gte", t.asDate()); | q.appendDate("$gte", t.asDate()); | |||
BSONObjBuilder q2; | BSONObjBuilder q2; | |||
q2.append("ts", q.done()); | q2.append("ts", q.done()); | |||
query(ns, q2.done()); | query(ns, q2.done()); | |||
} | } | |||
*/ | */ | |||
void tailingQuery(const char *ns, const BSONObj& query, const BSONO | void tailingQuery(const char *ns, const BSONObj& query, const BSONO | |||
bj* fields=0) { | bj* fields=0); | |||
assert( !haveCursor() ); | ||||
log(2) << "repl: " << ns << ".find(" << query.toString() << ')' | ||||
<< endl; | ||||
cursor.reset( _conn->query( ns, query, 0, 0, fields, | ||||
QueryOption_CursorTailable | QueryO | ||||
ption_SlaveOk | QueryOption_OplogReplay | | ||||
/* TODO: slaveOk maybe shouldn't us | ||||
e? */ | ||||
QueryOption_AwaitData | ||||
).release() ); | ||||
} | ||||
void tailingQueryGTE(const char *ns, OpTime t, const BSONObj* field | void tailingQueryGTE(const char *ns, OpTime t, const BSONObj* field | |||
s=0) { | s=0); | |||
BSONObjBuilder q; | ||||
q.appendDate("$gte", t.asDate()); | ||||
BSONObjBuilder query; | ||||
query.append("ts", q.done()); | ||||
tailingQuery(ns, query.done(), fields); | ||||
} | ||||
/* Do a tailing query, but only send the ts field back. */ | /* Do a tailing query, but only send the ts field back. */ | |||
void ghostQueryGTE(const char *ns, OpTime t) { | void ghostQueryGTE(const char *ns, OpTime t) { | |||
const BSONObj fields = BSON("ts" << 1 << "_id" << 0); | const BSONObj fields = BSON("ts" << 1 << "_id" << 0); | |||
return tailingQueryGTE(ns, t, &fields); | return tailingQueryGTE(ns, t, &fields); | |||
} | } | |||
bool more() { | bool more() { | |||
uassert( 15910, "Doesn't have cursor for reading oplog", cursor .get() ); | uassert( 15910, "Doesn't have cursor for reading oplog", cursor .get() ); | |||
return cursor->more(); | return cursor->more(); | |||
skipping to change at line 108 | skipping to change at line 97 | |||
bool moreInCurrentBatch() { | bool moreInCurrentBatch() { | |||
uassert( 15911, "Doesn't have cursor for reading oplog", cursor .get() ); | uassert( 15911, "Doesn't have cursor for reading oplog", cursor .get() ); | |||
return cursor->moreInCurrentBatch(); | return cursor->moreInCurrentBatch(); | |||
} | } | |||
/* old mongod's can't do the await flag... */ | /* old mongod's can't do the await flag... */ | |||
bool awaitCapable() { | bool awaitCapable() { | |||
return cursor->hasResultFlag(ResultFlag_AwaitCapable); | return cursor->hasResultFlag(ResultFlag_AwaitCapable); | |||
} | } | |||
int getTailingQueryOptions() const { return _tailingQueryOptions; } | ||||
void setTailingQueryOptions( int tailingQueryOptions ) { _tailingQu | ||||
eryOptions = tailingQueryOptions; } | ||||
void peek(vector<BSONObj>& v, int n) { | void peek(vector<BSONObj>& v, int n) { | |||
if( cursor.get() ) | if( cursor.get() ) | |||
cursor->peek(v,n); | cursor->peek(v,n); | |||
} | } | |||
BSONObj nextSafe() { return cursor->nextSafe(); } | BSONObj nextSafe() { return cursor->nextSafe(); } | |||
BSONObj next() { return cursor->next(); } | BSONObj next() { return cursor->next(); } | |||
void putBack(BSONObj op) { cursor->putBack(op); } | void putBack(BSONObj op) { cursor->putBack(op); } | |||
private: | private: | |||
/** @return true iff connection was successful */ | ||||
bool commonConnect(const string& hostName); | bool commonConnect(const string& hostName); | |||
bool passthroughHandshake(const BSONObj& rid, const int f); | bool passthroughHandshake(const BSONObj& rid, const int f); | |||
}; | }; | |||
} | } | |||
End of changes. 10 change blocks. | ||||
24 lines changed or deleted | 15 lines changed or added | |||
optime.h | optime.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 "../db/concurrency.h" | #include <boost/thread/condition.hpp> | |||
namespace mongo { | namespace mongo { | |||
void exitCleanly( ExitCode code ); | ||||
struct ClockSkewException : public DBException { | struct ClockSkewException : public DBException { | |||
ClockSkewException() : DBException( "clock skew exception" , 20001 ) {} | ClockSkewException() : DBException( "clock skew exception" , 20001 ) {} | |||
}; | }; | |||
/* replsets used to use RSOpTime. | /* replsets used to use RSOpTime. | |||
M/S uses OpTime. | M/S uses OpTime. | |||
But this is useable from both. | But this is useable from both. | |||
*/ | */ | |||
typedef unsigned long long ReplTime; | typedef unsigned long long ReplTime; | |||
skipping to change at line 45 | skipping to change at line 44 | |||
/* Operation sequence #. A combination of current second plus an ordin al value. | /* Operation sequence #. A combination of current second plus an ordin al value. | |||
*/ | */ | |||
#pragma pack(4) | #pragma pack(4) | |||
class OpTime { | class OpTime { | |||
unsigned i; // ordinal comes first so we can do a single 64 bit com pare on little endian | unsigned i; // ordinal comes first so we can do a single 64 bit com pare on little endian | |||
unsigned secs; | unsigned secs; | |||
static OpTime last; | static OpTime last; | |||
static OpTime skewed(); | static OpTime skewed(); | |||
public: | public: | |||
static void setLast(const Date_t &date) { | static void setLast(const Date_t &date) { | |||
notifier().notify_all(); // won't really do anything until writ | mutex::scoped_lock lk(m); | |||
e-lock released | notifier.notify_all(); // won't really do anything until write- | |||
lock released | ||||
last = OpTime(date); | last = OpTime(date); | |||
} | } | |||
unsigned getSecs() const { | unsigned getSecs() const { | |||
return secs; | return secs; | |||
} | } | |||
unsigned getInc() const { | unsigned getInc() const { | |||
return i; | return i; | |||
} | } | |||
OpTime(Date_t date) { | OpTime(Date_t date) { | |||
reinterpret_cast<unsigned long long&>(*this) = date.millis; | reinterpret_cast<unsigned long long&>(*this) = date.millis; | |||
skipping to change at line 78 | skipping to change at line 77 | |||
OpTime( const OpTime& other ) { | OpTime( const OpTime& other ) { | |||
secs = other.secs; | secs = other.secs; | |||
i = other.i; | i = other.i; | |||
dassert( (int)secs >= 0 ); | dassert( (int)secs >= 0 ); | |||
} | } | |||
OpTime() { | OpTime() { | |||
secs = 0; | secs = 0; | |||
i = 0; | i = 0; | |||
} | } | |||
// it isn't generally safe to not be locked for this. so use now(). some tests use this. | // it isn't generally safe to not be locked for this. so use now(). some tests use this. | |||
static OpTime now_inlock() { | static OpTime _now(); | |||
notifier().notify_all(); // won't really do anything until writ | ||||
e-lock released | ||||
unsigned t = (unsigned) time(0); | static mongo::mutex m; | |||
if ( last.secs == t ) { | ||||
last.i++; | static OpTime now(const mongo::mutex::scoped_lock&); | |||
return last; | ||||
} | static OpTime getLast(const mongo::mutex::scoped_lock&); | |||
if ( t < last.secs ) { | ||||
return skewed(); // separate function to keep out of the ho | ||||
t code path | ||||
} | ||||
last = OpTime(t, 1); | ||||
return last; | ||||
} | ||||
static OpTime now(); | ||||
static OpTime last_inlock(); | ||||
// Waits for global OpTime to be different from *this | // Waits for global OpTime to be different from *this | |||
// Must be atLeastReadLocked | ||||
// Defined in instance.cpp (only current user) as it needs dbtempre | ||||
lease | ||||
void waitForDifferent(unsigned millis); | void waitForDifferent(unsigned millis); | |||
/* We store OpTime's in the database as BSON Date datatype -- we ne eded some sort of | /* We store OpTime's in the database as BSON Date datatype -- we ne eded some sort of | |||
64 bit "container" for these values. While these are not really " Dates", that seems a | 64 bit "container" for these values. While these are not really " Dates", that seems a | |||
better choice for now than say, Number, which is floating point. Note the BinData type | better choice for now than say, Number, which is floating point. Note the BinData type | |||
is perhaps the cleanest choice, lacking a true unsigned64 datatype , but BinData has 5 | is perhaps the cleanest choice, lacking a true unsigned64 datatype , but BinData has 5 | |||
bytes of overhead. | bytes of overhead. | |||
*/ | */ | |||
unsigned long long asDate() const { | unsigned long long asDate() const { | |||
return reinterpret_cast<const unsigned long long*>(&i)[0]; | return reinterpret_cast<const unsigned long long*>(&i)[0]; | |||
skipping to change at line 157 | skipping to change at line 145 | |||
bool operator<=(const OpTime& r) const { | bool operator<=(const OpTime& r) const { | |||
return *this < r || *this == r; | return *this < r || *this == r; | |||
} | } | |||
bool operator>(const OpTime& r) const { | bool operator>(const OpTime& r) const { | |||
return !(*this <= r); | return !(*this <= r); | |||
} | } | |||
bool operator>=(const OpTime& r) const { | bool operator>=(const OpTime& r) const { | |||
return !(*this < r); | return !(*this < r); | |||
} | } | |||
private: | private: | |||
static boost::condition notifier; | ||||
// The following functions are to get around the need to define cla | ||||
ss-level statics in a cpp | ||||
static boost::condition& notifier() { | ||||
static boost::condition* holder = new boost::condition(); | ||||
return *holder; | ||||
}; | ||||
static boost::mutex& notifyMutex() { | ||||
static boost::mutex* holder = new boost::mutex(); | ||||
return *holder; | ||||
}; | ||||
}; | }; | |||
#pragma pack() | #pragma pack() | |||
} // namespace mongo | } // namespace mongo | |||
End of changes. 7 change blocks. | ||||
36 lines changed or deleted | 11 lines changed or added | |||
ordering.h | ordering.h | |||
---|---|---|---|---|
skipping to change at line 49 | skipping to change at line 49 | |||
get(0) == 1 | get(0) == 1 | |||
get(1) == -1 | get(1) == -1 | |||
*/ | */ | |||
int get(int i) const { | int get(int i) const { | |||
return ((1 << i) & bits) ? -1 : 1; | return ((1 << i) & bits) ? -1 : 1; | |||
} | } | |||
// for woCompare... | // for woCompare... | |||
unsigned descending(unsigned mask) const { return bits & mask; } | unsigned descending(unsigned mask) const { return bits & mask; } | |||
/*operator string() const { | /*operator std::string() const { | |||
StringBuilder buf; | StringBuilder buf; | |||
for ( unsigned i=0; i<nkeys; i++) | for ( unsigned i=0; i<nkeys; i++) | |||
buf.append( get(i) > 0 ? "+" : "-" ); | buf.append( get(i) > 0 ? "+" : "-" ); | |||
return buf.str(); | return buf.str(); | |||
}*/ | }*/ | |||
static Ordering make(const BSONObj& obj) { | static Ordering make(const BSONObj& obj) { | |||
unsigned b = 0; | unsigned b = 0; | |||
BSONObjIterator k(obj); | BSONObjIterator k(obj); | |||
unsigned n = 0; | unsigned n = 0; | |||
End of changes. 1 change blocks. | ||||
1 lines changed or deleted | 1 lines changed or added | |||
pagefault.h | pagefault.h | |||
---|---|---|---|---|
// @file pagefault.h | // @file pagefault.h | |||
// define this : _PAGEFAULTEXCEPTION | ||||
#pragma once | #pragma once | |||
namespace mongo { | namespace mongo { | |||
class Record; | class Record; | |||
class PageFaultException /*: public DBException*/ { | class PageFaultException /*: public DBException*/ { | |||
unsigned era; | unsigned era; | |||
Record *r; | const Record *r; | |||
public: | public: | |||
PageFaultException(const PageFaultException& rhs) : era(rhs.era), r (rhs.r) { } | PageFaultException(const PageFaultException& rhs) : era(rhs.era), r (rhs.r) { } | |||
explicit PageFaultException(Record*); | explicit PageFaultException(const Record*); | |||
void touch(); | void touch(); | |||
}; | }; | |||
class PageFaultRetryableSection : boost::noncopyable { | class PageFaultRetryableSection : boost::noncopyable { | |||
unsigned _laps; | unsigned _laps; | |||
public: | public: | |||
unsigned laps() const { return _laps; } | unsigned laps() const { return _laps; } | |||
void didLap() { _laps++; } | void didLap() { _laps++; } | |||
PageFaultRetryableSection(); | PageFaultRetryableSection(); | |||
~PageFaultRetryableSection(); | ~PageFaultRetryableSection(); | |||
End of changes. 3 change blocks. | ||||
4 lines changed or deleted | 2 lines changed or added | |||
parallel.h | parallel.h | |||
---|---|---|---|---|
skipping to change at line 24 | skipping to change at line 24 | |||
* See the License for the specific language governing permissions and | * See the License for the specific language governing permissions and | |||
* limitations under the License. | * limitations under the License. | |||
*/ | */ | |||
/** | /** | |||
tools for working in parallel/sharded/clustered environment | tools for working in parallel/sharded/clustered environment | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../pch.h" | #include "mongo/db/dbmessage.h" | |||
#include "dbclient.h" | #include "mongo/db/matcher.h" | |||
#include "redef_macros.h" | #include "mongo/db/namespacestring.h" | |||
#include "../db/dbmessage.h" | #include "mongo/s/util.h" | |||
#include "../db/matcher.h" | #include "mongo/util/concurrency/mvar.h" | |||
#include "../util/concurrency/mvar.h" | ||||
namespace mongo { | namespace mongo { | |||
/** | /** | |||
* holder for a server address and a query to run | * holder for a server address and a query to run | |||
*/ | */ | |||
class ServerAndQuery { | class ServerAndQuery { | |||
public: | public: | |||
ServerAndQuery( const string& server , BSONObj extra = BSONObj() , BSONObj orderObject = BSONObj() ) : | ServerAndQuery( const string& server , BSONObj extra = BSONObj() , BSONObj orderObject = BSONObj() ) : | |||
_server( server ) , _extra( extra.getOwned() ) , _orderObject( orderObject.getOwned() ) { | _server( server ) , _extra( extra.getOwned() ) , _orderObject( orderObject.getOwned() ) { | |||
skipping to change at line 373 | skipping to change at line 372 | |||
virtual void explain(BSONObjBuilder& b); | virtual void explain(BSONObjBuilder& b); | |||
protected: | protected: | |||
void _finishCons(); | void _finishCons(); | |||
void _init(); | void _init(); | |||
void _oldInit(); | void _oldInit(); | |||
virtual void _explain( map< string,list<BSONObj> >& out ); | virtual void _explain( map< string,list<BSONObj> >& out ); | |||
void _markStaleNS( const NamespaceString& staleNS, bool& forceReloa d, bool& fullReload ); | void _markStaleNS( const NamespaceString& staleNS, const StaleConfi gException& e, bool& forceReload, bool& fullReload ); | |||
void _handleStaleNS( const NamespaceString& staleNS, bool forceRelo ad, bool fullReload ); | void _handleStaleNS( const NamespaceString& staleNS, bool forceRelo ad, bool fullReload ); | |||
set<Shard> _qShards; | set<Shard> _qShards; | |||
QuerySpec _qSpec; | QuerySpec _qSpec; | |||
CommandInfo _cInfo; | CommandInfo _cInfo; | |||
// Count round-trips req'd for namespaces and total | // Count round-trips req'd for namespaces and total | |||
map<string,int> _staleNSMap; | map<string,int> _staleNSMap; | |||
int _totalTries; | int _totalTries; | |||
skipping to change at line 410 | skipping to change at line 409 | |||
class Future { | class Future { | |||
public: | public: | |||
class CommandResult { | class CommandResult { | |||
public: | public: | |||
string getServer() const { return _server; } | string getServer() const { return _server; } | |||
bool isDone() const { return _done; } | bool isDone() const { return _done; } | |||
bool ok() const { | bool ok() const { | |||
assert( _done ); | verify( _done ); | |||
return _ok; | return _ok; | |||
} | } | |||
BSONObj result() const { | BSONObj result() const { | |||
assert( _done ); | verify( _done ); | |||
return _res; | return _res; | |||
} | } | |||
/** | /** | |||
blocks until command is done | blocks until command is done | |||
returns ok() | returns ok() | |||
*/ | */ | |||
bool join( int maxRetries = 1 ); | bool join( int maxRetries = 1 ); | |||
private: | private: | |||
skipping to change at line 456 | skipping to change at line 455 | |||
/** | /** | |||
* @param server server name | * @param server server name | |||
* @param db db name | * @param db db name | |||
* @param cmd cmd to exec | * @param cmd cmd to exec | |||
* @param conn optional connection to use. will use standard poole d if non-specified | * @param conn optional connection to use. will use standard poole d if non-specified | |||
*/ | */ | |||
static shared_ptr<CommandResult> spawnCommand( const string& server , const string& db , const BSONObj& cmd , int options , DBClientBase * con n = 0 ); | static shared_ptr<CommandResult> spawnCommand( const string& server , const string& db , const BSONObj& cmd , int options , DBClientBase * con n = 0 ); | |||
}; | }; | |||
} | } | |||
#include "undef_macros.h" | ||||
End of changes. 5 change blocks. | ||||
9 lines changed or deleted | 8 lines changed or added | |||
pch.h | pch.h | |||
---|---|---|---|---|
skipping to change at line 24 | skipping to change at line 24 | |||
* Unless required by applicable law or agreed to in writing, software | * Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | * distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. | |||
* See the License for the specific language governing permissions and | * See the License for the specific language governing permissions and | |||
* limitations under the License. | * limitations under the License. | |||
*/ | */ | |||
#ifndef MONGO_PCH_H | #ifndef MONGO_PCH_H | |||
#define MONGO_PCH_H | #define MONGO_PCH_H | |||
#if defined(MONGO_EXPOSE_MACROS) | // our #define macros must not be active when we include | |||
// this is defined in server.h for non-MONGO_EXPOSE_MACROS | // system headers and boost headers | |||
# define BOOST_ENABLE_ASSERT_HANDLER 1 | #include "mongo/client/undef_macros.h" | |||
# define JS_C_STRINGS_ARE_UTF8 | #include "mongo/platform/basic.h" | |||
# undef SUPPORT_UCP | ||||
# define SUPPORT_UCP | ||||
# undef SUPPORT_UTF8 | ||||
# define SUPPORT_UTF8 | ||||
# undef _CRT_SECURE_NO_WARNINGS | ||||
# define _CRT_SECURE_NO_WARNINGS | ||||
#endif | ||||
#if defined(_WIN32) | ||||
// for rand_s() usage: | ||||
# define _CRT_RAND_S | ||||
# ifndef NOMINMAX | ||||
# define NOMINMAX | ||||
# endif | ||||
#define WIN32_LEAN_AND_MEAN | ||||
# include <winsock2.h> //this must be included before the first windows.h i | ||||
nclude | ||||
# include <ws2tcpip.h> | ||||
# include <wspiapi.h> | ||||
# include <windows.h> | ||||
#endif | ||||
#if defined(__linux__) && defined(MONGO_EXPOSE_MACROS) | ||||
// glibc's optimized versions are better than g++ builtins | ||||
# define __builtin_strcmp strcmp | ||||
# define __builtin_strlen strlen | ||||
# define __builtin_memchr memchr | ||||
# define __builtin_memcmp memcmp | ||||
# define __builtin_memcpy memcpy | ||||
# define __builtin_memset memset | ||||
# define __builtin_memmove memmove | ||||
#endif | ||||
#include <ctime> | #include <ctime> | |||
#include <cstring> | #include <cstring> | |||
#include <string> | #include <string> | |||
#include <memory> | #include <memory> | |||
#include <string> | #include <string> | |||
#include <iostream> | #include <iostream> | |||
#include <map> | #include <map> | |||
#include <vector> | #include <vector> | |||
#include <set> | #include <set> | |||
#include <stdio.h> | #include <stdio.h> | |||
#include <stdlib.h> | #include <stdlib.h> | |||
#include <signal.h> | #include <signal.h> | |||
#include "targetver.h" | ||||
#include "time.h" | #include "time.h" | |||
#include "string.h" | #include "string.h" | |||
#include "limits.h" | #include "limits.h" | |||
#define BOOST_FILESYSTEM_VERSION 2 | #define BOOST_FILESYSTEM_VERSION 2 | |||
#include <boost/lexical_cast.hpp> | ||||
#include <boost/shared_ptr.hpp> | #include <boost/shared_ptr.hpp> | |||
#include <boost/smart_ptr.hpp> | #include <boost/smart_ptr.hpp> | |||
#include <boost/function.hpp> | #include <boost/function.hpp> | |||
#include <boost/bind.hpp> | #include <boost/bind.hpp> | |||
#include <boost/thread/tss.hpp> | ||||
#include <boost/detail/endian.hpp> | ||||
#include <boost/version.hpp> | #include <boost/version.hpp> | |||
#include <boost/thread/thread.hpp> | ||||
#include <boost/thread/condition.hpp> | #include "mongo/client/redef_macros.h" | |||
#include <boost/thread/recursive_mutex.hpp> | ||||
#include <boost/thread/xtime.hpp> | #include "mongo/util/exit_code.h" | |||
#undef assert | ||||
#define assert MONGO_assert | ||||
namespace mongo { | namespace mongo { | |||
using namespace std; | using namespace std; | |||
using boost::shared_ptr; | using boost::shared_ptr; | |||
#if defined(_DEBUG) | void dbexit( ExitCode returnCode, const char *whyMsg = "" ); | |||
const bool debug=true; | ||||
#else | ||||
const bool debug=false; | ||||
#endif | ||||
// pdfile versions | ||||
const int PDFILE_VERSION = 4; | ||||
const int PDFILE_VERSION_MINOR = 5; | ||||
enum ExitCode { | ||||
EXIT_CLEAN = 0 , | ||||
EXIT_BADOPTIONS = 2 , | ||||
EXIT_REPLICATION_ERROR = 3 , | ||||
EXIT_NEED_UPGRADE = 4 , | ||||
EXIT_SHARDING_ERROR = 5 , | ||||
EXIT_KILL = 12 , | ||||
EXIT_ABRUPT = 14 , | ||||
EXIT_NTSERVICE_ERROR = 20 , | ||||
EXIT_JAVA = 21 , | ||||
EXIT_OOM_MALLOC = 42 , | ||||
EXIT_OOM_REALLOC = 43 , | ||||
EXIT_FS = 45 , | ||||
EXIT_CLOCK_SKEW = 47 , | ||||
EXIT_NET_ERROR = 48 , | ||||
EXIT_WINDOWS_SERVICE_STOP = 49 , | ||||
EXIT_POSSIBLE_CORRUPTION = 60 , // this means we detected a possibl | ||||
e corruption situation, like a buf overflow | ||||
EXIT_UNCAUGHT = 100 , // top level exception that wasn't caught | ||||
EXIT_TEST = 101 | ||||
}; | ||||
void dbexit( ExitCode returnCode, const char *whyMsg = "", bool tryToGe | ||||
tLock = false); | ||||
/** | /** | |||
this is here so you can't just type exit() to quit the program | this is here so you can't just type exit() to quit the program | |||
you should either use dbexit to shutdown cleanly, or ::exit to tell the system to quit | you should either use dbexit to shutdown cleanly, or ::exit to tell the system to quit | |||
if you use this, you'll get a link error since mongo::exit isn't def ined | if you use this, you'll get a link error since mongo::exit isn't def ined | |||
*/ | */ | |||
void exit( ExitCode returnCode ); | void exit( ExitCode returnCode ); | |||
bool inShutdown(); | bool inShutdown(); | |||
void asserted(const char *msg, const char *file, unsigned line); | ||||
} | } | |||
// TODO: Rework the headers so we don't need this craziness | #include "mongo/util/assert_util.h" | |||
#include "bson/inline_decls.h" | #include "mongo/util/debug_util.h" | |||
#define MONGO_assert(_Expression) (void)( MONGO_likely(!!(_Expression)) || | #include "mongo/util/goodies.h" | |||
(mongo::asserted(#_Expression, __FILE__, __LINE__), 0) ) | #include "mongo/util/allocator.h" | |||
#include "mongo/util/log.h" | ||||
#include "util/debug_util.h" | ||||
#include "util/goodies.h" | ||||
#include "util/log.h" | ||||
#include "util/allocator.h" | ||||
#include "util/assert_util.h" | ||||
namespace mongo { | ||||
void sayDbContext(const char *msg = 0); | ||||
void rawOut( const string &s ); | ||||
typedef char _TCHAR; | ||||
using boost::uint32_t; | ||||
using boost::uint64_t; | ||||
/** called by mongos, mongod, test. do not call from clients and such. | ||||
invoked before about everything except global var construction. | ||||
*/ | ||||
void doPreServerStartupInits(); | ||||
} // namespace mongo | ||||
#endif // MONGO_PCH_H | #endif // MONGO_PCH_H | |||
End of changes. 8 change blocks. | ||||
109 lines changed or deleted | 16 lines changed or added | |||
pcre.h | pcre.h | |||
---|---|---|---|---|
/************************************************* | /************************************************* | |||
* Perl-Compatible Regular Expressions * | * Perl-Compatible Regular Expressions * | |||
*************************************************/ | *************************************************/ | |||
/* This is the public header file for the PCRE library, to be #included by | /* This is the public header file for the PCRE library, to be #included by | |||
applications that call the PCRE functions. | applications that call the PCRE functions. | |||
Copyright (c) 1997-2007 University of Cambridge | Copyright (c) 1997-2012 University of Cambridge | |||
--------------------------------------------------------------------------- -- | --------------------------------------------------------------------------- -- | |||
Redistribution and use in source and binary forms, with or without | Redistribution and use in source and binary forms, with or without | |||
modification, are permitted provided that the following conditions are met: | modification, are permitted provided that the following conditions are met: | |||
* Redistributions of source code must retain the above copyright notice , | * Redistributions of source code must retain the above copyright notice , | |||
this list of conditions and the following disclaimer. | this list of conditions and the following disclaimer. | |||
* Redistributions in binary form must reproduce the above copyright | * Redistributions in binary form must reproduce the above copyright | |||
notice, this list of conditions and the following disclaimer in the | notice, this list of conditions and the following disclaimer in the | |||
skipping to change at line 44 | skipping to change at line 44 | |||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |||
POSSIBILITY OF SUCH DAMAGE. | POSSIBILITY OF SUCH DAMAGE. | |||
--------------------------------------------------------------------------- -- | --------------------------------------------------------------------------- -- | |||
*/ | */ | |||
#ifndef _PCRE_H | #ifndef _PCRE_H | |||
#define _PCRE_H | #define _PCRE_H | |||
/* The current PCRE version information. */ | /* The current PCRE version information. */ | |||
#define PCRE_MAJOR 7 | #define PCRE_MAJOR 8 | |||
#define PCRE_MINOR 4 | #define PCRE_MINOR 30 | |||
#define PCRE_PRERELEASE | #define PCRE_PRERELEASE | |||
#define PCRE_DATE 2007-09-21 | #define PCRE_DATE 2012-02-04 | |||
/* When an application links to a PCRE DLL in Windows, the symbols that are | /* When an application links to a PCRE DLL in Windows, the symbols that are | |||
imported have to be identified as such. When building PCRE, the appropriate | imported have to be identified as such. When building PCRE, the appropriate | |||
export setting is defined in pcre_internal.h, which includes this file. So we | export setting is defined in pcre_internal.h, which includes this file. So we | |||
don't change existing definitions of PCRE_EXP_DECL and PCRECPP_EXP_DECL. */ | don't change existing definitions of PCRE_EXP_DECL and PCRECPP_EXP_DECL. */ | |||
/*#if defined(_WIN32) && !defined(PCRE_STATIC) | #if defined(_WIN32) && !defined(PCRE_STATIC) | |||
#error why are we here? | ||||
# ifndef PCRE_EXP_DECL | # ifndef PCRE_EXP_DECL | |||
# define PCRE_EXP_DECL extern __declspec(dllimport) | # define PCRE_EXP_DECL extern | |||
# endif | # endif | |||
# ifdef __cplusplus | # ifdef __cplusplus | |||
# ifndef PCRECPP_EXP_DECL | # ifndef PCRECPP_EXP_DECL | |||
# define PCRECPP_EXP_DECL extern __declspec(dllimport) | # define PCRECPP_EXP_DECL extern | |||
# endif | # endif | |||
# ifndef PCRECPP_EXP_DEFN | # ifndef PCRECPP_EXP_DEFN | |||
# define PCRECPP_EXP_DEFN __declspec(dllimport) | # define PCRECPP_EXP_DEFN | |||
# endif | # endif | |||
# endif | # endif | |||
#endif*/ | #endif | |||
/* By default, we use the standard "extern" declarations. */ | /* By default, we use the standard "extern" declarations. */ | |||
#ifndef PCRE_EXP_DECL | #ifndef PCRE_EXP_DECL | |||
# ifdef __cplusplus | # ifdef __cplusplus | |||
# define PCRE_EXP_DECL extern "C" | # define PCRE_EXP_DECL extern "C" | |||
# else | # else | |||
# define PCRE_EXP_DECL extern | # define PCRE_EXP_DECL extern | |||
# endif | # endif | |||
#endif | #endif | |||
skipping to change at line 99 | skipping to change at line 98 | |||
it is needed here for malloc. */ | it is needed here for malloc. */ | |||
#include <stdlib.h> | #include <stdlib.h> | |||
/* Allow for C++ users */ | /* Allow for C++ users */ | |||
#ifdef __cplusplus | #ifdef __cplusplus | |||
extern "C" { | extern "C" { | |||
#endif | #endif | |||
/* Options */ | /* Options. Some are compile-time only, some are run-time only, and some ar | |||
e | ||||
#define PCRE_CASELESS 0x00000001 | both, so we keep them all distinct. However, almost all the bits in the opt | |||
#define PCRE_MULTILINE 0x00000002 | ions | |||
#define PCRE_DOTALL 0x00000004 | word are now used. In the long run, we may have to re-use some of the | |||
#define PCRE_EXTENDED 0x00000008 | compile-time only bits for runtime options, or vice versa. In the comments | |||
#define PCRE_ANCHORED 0x00000010 | below, "compile", "exec", and "DFA exec" mean that the option is permitted | |||
#define PCRE_DOLLAR_ENDONLY 0x00000020 | to | |||
#define PCRE_EXTRA 0x00000040 | be set for those functions; "used in" means that an option may be set only | |||
#define PCRE_NOTBOL 0x00000080 | for | |||
#define PCRE_NOTEOL 0x00000100 | compile, but is subsequently referenced in exec and/or DFA exec. Any of the | |||
#define PCRE_UNGREEDY 0x00000200 | compile-time options may be inspected during studying (and therefore JIT | |||
#define PCRE_NOTEMPTY 0x00000400 | compiling). */ | |||
#define PCRE_UTF8 0x00000800 | ||||
#define PCRE_NO_AUTO_CAPTURE 0x00001000 | #define PCRE_CASELESS 0x00000001 /* Compile */ | |||
#define PCRE_NO_UTF8_CHECK 0x00002000 | #define PCRE_MULTILINE 0x00000002 /* Compile */ | |||
#define PCRE_AUTO_CALLOUT 0x00004000 | #define PCRE_DOTALL 0x00000004 /* Compile */ | |||
#define PCRE_PARTIAL 0x00008000 | #define PCRE_EXTENDED 0x00000008 /* Compile */ | |||
#define PCRE_DFA_SHORTEST 0x00010000 | #define PCRE_ANCHORED 0x00000010 /* Compile, exec, DFA exec */ | |||
#define PCRE_DFA_RESTART 0x00020000 | #define PCRE_DOLLAR_ENDONLY 0x00000020 /* Compile, used in exec, DFA e | |||
#define PCRE_FIRSTLINE 0x00040000 | xec */ | |||
#define PCRE_DUPNAMES 0x00080000 | #define PCRE_EXTRA 0x00000040 /* Compile */ | |||
#define PCRE_NEWLINE_CR 0x00100000 | #define PCRE_NOTBOL 0x00000080 /* Exec, DFA exec */ | |||
#define PCRE_NEWLINE_LF 0x00200000 | #define PCRE_NOTEOL 0x00000100 /* Exec, DFA exec */ | |||
#define PCRE_NEWLINE_CRLF 0x00300000 | #define PCRE_UNGREEDY 0x00000200 /* Compile */ | |||
#define PCRE_NEWLINE_ANY 0x00400000 | #define PCRE_NOTEMPTY 0x00000400 /* Exec, DFA exec */ | |||
#define PCRE_NEWLINE_ANYCRLF 0x00500000 | /* The next two are also used in exec and DFA exec */ | |||
#define PCRE_BSR_ANYCRLF 0x00800000 | #define PCRE_UTF8 0x00000800 /* Compile (same as PCRE_UTF16) | |||
#define PCRE_BSR_UNICODE 0x01000000 | */ | |||
#define PCRE_UTF16 0x00000800 /* Compile (same as PCRE_UTF8) | ||||
*/ | ||||
#define PCRE_NO_AUTO_CAPTURE 0x00001000 /* Compile */ | ||||
/* The next two are also used in exec and DFA exec */ | ||||
#define PCRE_NO_UTF8_CHECK 0x00002000 /* Compile (same as PCRE_NO_UTF | ||||
16_CHECK) */ | ||||
#define PCRE_NO_UTF16_CHECK 0x00002000 /* Compile (same as PCRE_NO_UTF | ||||
8_CHECK) */ | ||||
#define PCRE_AUTO_CALLOUT 0x00004000 /* Compile */ | ||||
#define PCRE_PARTIAL_SOFT 0x00008000 /* Exec, DFA exec */ | ||||
#define PCRE_PARTIAL 0x00008000 /* Backwards compatible synonym | ||||
*/ | ||||
#define PCRE_DFA_SHORTEST 0x00010000 /* DFA exec */ | ||||
#define PCRE_DFA_RESTART 0x00020000 /* DFA exec */ | ||||
#define PCRE_FIRSTLINE 0x00040000 /* Compile, used in exec, DFA e | ||||
xec */ | ||||
#define PCRE_DUPNAMES 0x00080000 /* Compile */ | ||||
#define PCRE_NEWLINE_CR 0x00100000 /* Compile, exec, DFA exec */ | ||||
#define PCRE_NEWLINE_LF 0x00200000 /* Compile, exec, DFA exec */ | ||||
#define PCRE_NEWLINE_CRLF 0x00300000 /* Compile, exec, DFA exec */ | ||||
#define PCRE_NEWLINE_ANY 0x00400000 /* Compile, exec, DFA exec */ | ||||
#define PCRE_NEWLINE_ANYCRLF 0x00500000 /* Compile, exec, DFA exec */ | ||||
#define PCRE_BSR_ANYCRLF 0x00800000 /* Compile, exec, DFA exec */ | ||||
#define PCRE_BSR_UNICODE 0x01000000 /* Compile, exec, DFA exec */ | ||||
#define PCRE_JAVASCRIPT_COMPAT 0x02000000 /* Compile, used in exec */ | ||||
#define PCRE_NO_START_OPTIMIZE 0x04000000 /* Compile, exec, DFA exec */ | ||||
#define PCRE_NO_START_OPTIMISE 0x04000000 /* Synonym */ | ||||
#define PCRE_PARTIAL_HARD 0x08000000 /* Exec, DFA exec */ | ||||
#define PCRE_NOTEMPTY_ATSTART 0x10000000 /* Exec, DFA exec */ | ||||
#define PCRE_UCP 0x20000000 /* Compile, used in exec, DFA e | ||||
xec */ | ||||
/* Exec-time and get/set-time error codes */ | /* Exec-time and get/set-time error codes */ | |||
#define PCRE_ERROR_NOMATCH (-1) | #define PCRE_ERROR_NOMATCH (-1) | |||
#define PCRE_ERROR_NULL (-2) | #define PCRE_ERROR_NULL (-2) | |||
#define PCRE_ERROR_BADOPTION (-3) | #define PCRE_ERROR_BADOPTION (-3) | |||
#define PCRE_ERROR_BADMAGIC (-4) | #define PCRE_ERROR_BADMAGIC (-4) | |||
#define PCRE_ERROR_UNKNOWN_OPCODE (-5) | #define PCRE_ERROR_UNKNOWN_OPCODE (-5) | |||
#define PCRE_ERROR_UNKNOWN_NODE (-5) /* For backward compatibility */ | #define PCRE_ERROR_UNKNOWN_NODE (-5) /* For backward compatibility */ | |||
#define PCRE_ERROR_NOMEMORY (-6) | #define PCRE_ERROR_NOMEMORY (-6) | |||
#define PCRE_ERROR_NOSUBSTRING (-7) | #define PCRE_ERROR_NOSUBSTRING (-7) | |||
#define PCRE_ERROR_MATCHLIMIT (-8) | #define PCRE_ERROR_MATCHLIMIT (-8) | |||
#define PCRE_ERROR_CALLOUT (-9) /* Never used by PCRE itself */ | #define PCRE_ERROR_CALLOUT (-9) /* Never used by PCRE itself */ | |||
#define PCRE_ERROR_BADUTF8 (-10) | #define PCRE_ERROR_BADUTF8 (-10) /* Same for 8/16 */ | |||
#define PCRE_ERROR_BADUTF8_OFFSET (-11) | #define PCRE_ERROR_BADUTF16 (-10) /* Same for 8/16 */ | |||
#define PCRE_ERROR_PARTIAL (-12) | #define PCRE_ERROR_BADUTF8_OFFSET (-11) /* Same for 8/16 */ | |||
#define PCRE_ERROR_BADPARTIAL (-13) | #define PCRE_ERROR_BADUTF16_OFFSET (-11) /* Same for 8/16 */ | |||
#define PCRE_ERROR_INTERNAL (-14) | #define PCRE_ERROR_PARTIAL (-12) | |||
#define PCRE_ERROR_BADCOUNT (-15) | #define PCRE_ERROR_BADPARTIAL (-13) | |||
#define PCRE_ERROR_DFA_UITEM (-16) | #define PCRE_ERROR_INTERNAL (-14) | |||
#define PCRE_ERROR_DFA_UCOND (-17) | #define PCRE_ERROR_BADCOUNT (-15) | |||
#define PCRE_ERROR_DFA_UMLIMIT (-18) | #define PCRE_ERROR_DFA_UITEM (-16) | |||
#define PCRE_ERROR_DFA_WSSIZE (-19) | #define PCRE_ERROR_DFA_UCOND (-17) | |||
#define PCRE_ERROR_DFA_RECURSE (-20) | #define PCRE_ERROR_DFA_UMLIMIT (-18) | |||
#define PCRE_ERROR_RECURSIONLIMIT (-21) | #define PCRE_ERROR_DFA_WSSIZE (-19) | |||
#define PCRE_ERROR_NULLWSLIMIT (-22) /* No longer actually used */ | #define PCRE_ERROR_DFA_RECURSE (-20) | |||
#define PCRE_ERROR_BADNEWLINE (-23) | #define PCRE_ERROR_RECURSIONLIMIT (-21) | |||
#define PCRE_ERROR_NULLWSLIMIT (-22) /* No longer actually used */ | ||||
#define PCRE_ERROR_BADNEWLINE (-23) | ||||
#define PCRE_ERROR_BADOFFSET (-24) | ||||
#define PCRE_ERROR_SHORTUTF8 (-25) | ||||
#define PCRE_ERROR_SHORTUTF16 (-25) /* Same for 8/16 */ | ||||
#define PCRE_ERROR_RECURSELOOP (-26) | ||||
#define PCRE_ERROR_JIT_STACKLIMIT (-27) | ||||
#define PCRE_ERROR_BADMODE (-28) | ||||
#define PCRE_ERROR_BADENDIANNESS (-29) | ||||
/* Specific error codes for UTF-8 validity checks */ | ||||
#define PCRE_UTF8_ERR0 0 | ||||
#define PCRE_UTF8_ERR1 1 | ||||
#define PCRE_UTF8_ERR2 2 | ||||
#define PCRE_UTF8_ERR3 3 | ||||
#define PCRE_UTF8_ERR4 4 | ||||
#define PCRE_UTF8_ERR5 5 | ||||
#define PCRE_UTF8_ERR6 6 | ||||
#define PCRE_UTF8_ERR7 7 | ||||
#define PCRE_UTF8_ERR8 8 | ||||
#define PCRE_UTF8_ERR9 9 | ||||
#define PCRE_UTF8_ERR10 10 | ||||
#define PCRE_UTF8_ERR11 11 | ||||
#define PCRE_UTF8_ERR12 12 | ||||
#define PCRE_UTF8_ERR13 13 | ||||
#define PCRE_UTF8_ERR14 14 | ||||
#define PCRE_UTF8_ERR15 15 | ||||
#define PCRE_UTF8_ERR16 16 | ||||
#define PCRE_UTF8_ERR17 17 | ||||
#define PCRE_UTF8_ERR18 18 | ||||
#define PCRE_UTF8_ERR19 19 | ||||
#define PCRE_UTF8_ERR20 20 | ||||
#define PCRE_UTF8_ERR21 21 | ||||
/* Specific error codes for UTF-16 validity checks */ | ||||
#define PCRE_UTF16_ERR0 0 | ||||
#define PCRE_UTF16_ERR1 1 | ||||
#define PCRE_UTF16_ERR2 2 | ||||
#define PCRE_UTF16_ERR3 3 | ||||
#define PCRE_UTF16_ERR4 4 | ||||
/* Request types for pcre_fullinfo() */ | /* Request types for pcre_fullinfo() */ | |||
#define PCRE_INFO_OPTIONS 0 | #define PCRE_INFO_OPTIONS 0 | |||
#define PCRE_INFO_SIZE 1 | #define PCRE_INFO_SIZE 1 | |||
#define PCRE_INFO_CAPTURECOUNT 2 | #define PCRE_INFO_CAPTURECOUNT 2 | |||
#define PCRE_INFO_BACKREFMAX 3 | #define PCRE_INFO_BACKREFMAX 3 | |||
#define PCRE_INFO_FIRSTBYTE 4 | #define PCRE_INFO_FIRSTBYTE 4 | |||
#define PCRE_INFO_FIRSTCHAR 4 /* For backwards compatibility */ | #define PCRE_INFO_FIRSTCHAR 4 /* For backwards compatibility */ | |||
#define PCRE_INFO_FIRSTTABLE 5 | #define PCRE_INFO_FIRSTTABLE 5 | |||
#define PCRE_INFO_LASTLITERAL 6 | #define PCRE_INFO_LASTLITERAL 6 | |||
#define PCRE_INFO_NAMEENTRYSIZE 7 | #define PCRE_INFO_NAMEENTRYSIZE 7 | |||
#define PCRE_INFO_NAMECOUNT 8 | #define PCRE_INFO_NAMECOUNT 8 | |||
#define PCRE_INFO_NAMETABLE 9 | #define PCRE_INFO_NAMETABLE 9 | |||
#define PCRE_INFO_STUDYSIZE 10 | #define PCRE_INFO_STUDYSIZE 10 | |||
#define PCRE_INFO_DEFAULT_TABLES 11 | #define PCRE_INFO_DEFAULT_TABLES 11 | |||
#define PCRE_INFO_OKPARTIAL 12 | #define PCRE_INFO_OKPARTIAL 12 | |||
#define PCRE_INFO_JCHANGED 13 | #define PCRE_INFO_JCHANGED 13 | |||
#define PCRE_INFO_HASCRORLF 14 | #define PCRE_INFO_HASCRORLF 14 | |||
#define PCRE_INFO_MINLENGTH 15 | ||||
#define PCRE_INFO_JIT 16 | ||||
#define PCRE_INFO_JITSIZE 17 | ||||
/* Request types for pcre_config(). Do not re-arrange, in order to remain | /* Request types for pcre_config(). Do not re-arrange, in order to remain | |||
compatible. */ | compatible. */ | |||
#define PCRE_CONFIG_UTF8 0 | #define PCRE_CONFIG_UTF8 0 | |||
#define PCRE_CONFIG_NEWLINE 1 | #define PCRE_CONFIG_NEWLINE 1 | |||
#define PCRE_CONFIG_LINK_SIZE 2 | #define PCRE_CONFIG_LINK_SIZE 2 | |||
#define PCRE_CONFIG_POSIX_MALLOC_THRESHOLD 3 | #define PCRE_CONFIG_POSIX_MALLOC_THRESHOLD 3 | |||
#define PCRE_CONFIG_MATCH_LIMIT 4 | #define PCRE_CONFIG_MATCH_LIMIT 4 | |||
#define PCRE_CONFIG_STACKRECURSE 5 | #define PCRE_CONFIG_STACKRECURSE 5 | |||
#define PCRE_CONFIG_UNICODE_PROPERTIES 6 | #define PCRE_CONFIG_UNICODE_PROPERTIES 6 | |||
#define PCRE_CONFIG_MATCH_LIMIT_RECURSION 7 | #define PCRE_CONFIG_MATCH_LIMIT_RECURSION 7 | |||
#define PCRE_CONFIG_BSR 8 | #define PCRE_CONFIG_BSR 8 | |||
#define PCRE_CONFIG_JIT 9 | ||||
#define PCRE_CONFIG_UTF16 10 | ||||
#define PCRE_CONFIG_JITTARGET 11 | ||||
/* Bit flags for the pcre_extra structure. Do not re-arrange or redefine | /* Request types for pcre_study(). Do not re-arrange, in order to remain | |||
compatible. */ | ||||
#define PCRE_STUDY_JIT_COMPILE 0x0001 | ||||
/* Bit flags for the pcre[16]_extra structure. Do not re-arrange or redefin | ||||
e | ||||
these bits, just add new ones on the end, in order to remain compatible. */ | these bits, just add new ones on the end, in order to remain compatible. */ | |||
#define PCRE_EXTRA_STUDY_DATA 0x0001 | #define PCRE_EXTRA_STUDY_DATA 0x0001 | |||
#define PCRE_EXTRA_MATCH_LIMIT 0x0002 | #define PCRE_EXTRA_MATCH_LIMIT 0x0002 | |||
#define PCRE_EXTRA_CALLOUT_DATA 0x0004 | #define PCRE_EXTRA_CALLOUT_DATA 0x0004 | |||
#define PCRE_EXTRA_TABLES 0x0008 | #define PCRE_EXTRA_TABLES 0x0008 | |||
#define PCRE_EXTRA_MATCH_LIMIT_RECURSION 0x0010 | #define PCRE_EXTRA_MATCH_LIMIT_RECURSION 0x0010 | |||
#define PCRE_EXTRA_MARK 0x0020 | ||||
#define PCRE_EXTRA_EXECUTABLE_JIT 0x0040 | ||||
/* Types */ | /* Types */ | |||
struct real_pcre; /* declaration; the definition is private */ | struct real_pcre; /* declaration; the definition is private */ | |||
typedef struct real_pcre pcre; | typedef struct real_pcre pcre; | |||
struct real_pcre16; /* declaration; the definition is private | ||||
*/ | ||||
typedef struct real_pcre16 pcre16; | ||||
struct real_pcre_jit_stack; /* declaration; the definition is private | ||||
*/ | ||||
typedef struct real_pcre_jit_stack pcre_jit_stack; | ||||
struct real_pcre16_jit_stack; /* declaration; the definition is private | ||||
*/ | ||||
typedef struct real_pcre16_jit_stack pcre16_jit_stack; | ||||
/* If PCRE is compiled with 16 bit character support, PCRE_UCHAR16 must con | ||||
tain | ||||
a 16 bit wide signed data type. Otherwise it can be a dummy data type since | ||||
pcre16 functions are not implemented. There is a check for this in pcre_int | ||||
ernal.h. */ | ||||
#ifndef PCRE_UCHAR16 | ||||
#define PCRE_UCHAR16 unsigned short | ||||
#endif | ||||
#ifndef PCRE_SPTR16 | ||||
#define PCRE_SPTR16 const PCRE_UCHAR16 * | ||||
#endif | ||||
/* When PCRE is compiled as a C++ library, the subject pointer type can be | /* When PCRE is compiled as a C++ library, the subject pointer type can be | |||
replaced with a custom type. For conventional use, the public interface is a | replaced with a custom type. For conventional use, the public interface is a | |||
const char *. */ | const char *. */ | |||
#ifndef PCRE_SPTR | #ifndef PCRE_SPTR | |||
#define PCRE_SPTR const char * | #define PCRE_SPTR const char * | |||
#endif | #endif | |||
/* The structure for passing additional data to pcre_exec(). This is define d in | /* The structure for passing additional data to pcre_exec(). This is define d in | |||
such as way as to be extensible. Always add new fields at the end, in order to | such as way as to be extensible. Always add new fields at the end, in order to | |||
remain compatible. */ | remain compatible. */ | |||
typedef struct pcre_extra { | typedef struct pcre_extra { | |||
unsigned long int flags; /* Bits for which fields are set */ | unsigned long int flags; /* Bits for which fields are set */ | |||
void *study_data; /* Opaque data from pcre_study() */ | void *study_data; /* Opaque data from pcre_study() */ | |||
unsigned long int match_limit; /* Maximum number of calls to match() */ | unsigned long int match_limit; /* Maximum number of calls to match() */ | |||
void *callout_data; /* Data passed back in callouts */ | void *callout_data; /* Data passed back in callouts */ | |||
const unsigned char *tables; /* Pointer to character tables */ | const unsigned char *tables; /* Pointer to character tables */ | |||
unsigned long int match_limit_recursion; /* Max recursive calls to match( ) */ | unsigned long int match_limit_recursion; /* Max recursive calls to match( ) */ | |||
unsigned char **mark; /* For passing back a mark pointer */ | ||||
void *executable_jit; /* Contains a pointer to a compiled jit c | ||||
ode */ | ||||
} pcre_extra; | } pcre_extra; | |||
/* Same structure as above, but with 16 bit char pointers. */ | ||||
typedef struct pcre16_extra { | ||||
unsigned long int flags; /* Bits for which fields are set */ | ||||
void *study_data; /* Opaque data from pcre_study() */ | ||||
unsigned long int match_limit; /* Maximum number of calls to match() */ | ||||
void *callout_data; /* Data passed back in callouts */ | ||||
const unsigned char *tables; /* Pointer to character tables */ | ||||
unsigned long int match_limit_recursion; /* Max recursive calls to match( | ||||
) */ | ||||
PCRE_UCHAR16 **mark; /* For passing back a mark pointer */ | ||||
void *executable_jit; /* Contains a pointer to a compiled jit c | ||||
ode */ | ||||
} pcre16_extra; | ||||
/* The structure for passing out data via the pcre_callout_function. We use a | /* The structure for passing out data via the pcre_callout_function. We use a | |||
structure so that new fields can be added on the end in future versions, | structure so that new fields can be added on the end in future versions, | |||
without changing the API of the function, thereby allowing old databases to work | without changing the API of the function, thereby allowing old clients to w ork | |||
without modification. */ | without modification. */ | |||
typedef struct pcre_callout_block { | typedef struct pcre_callout_block { | |||
int version; /* Identifies version of block */ | int version; /* Identifies version of block */ | |||
/* ------------------------ Version 0 ------------------------------- */ | /* ------------------------ Version 0 ------------------------------- */ | |||
int callout_number; /* Number compiled into pattern */ | int callout_number; /* Number compiled into pattern */ | |||
int *offset_vector; /* The offset vector */ | int *offset_vector; /* The offset vector */ | |||
PCRE_SPTR subject; /* The subject being matched */ | PCRE_SPTR subject; /* The subject being matched */ | |||
int subject_length; /* The length of the subject */ | int subject_length; /* The length of the subject */ | |||
int start_match; /* Offset to start of this match attempt */ | int start_match; /* Offset to start of this match attempt */ | |||
int current_position; /* Where we currently are in the subject */ | int current_position; /* Where we currently are in the subject */ | |||
int capture_top; /* Max current capture */ | int capture_top; /* Max current capture */ | |||
int capture_last; /* Most recently closed capture */ | int capture_last; /* Most recently closed capture */ | |||
void *callout_data; /* Data passed in with the call */ | void *callout_data; /* Data passed in with the call */ | |||
/* ------------------- Added for Version 1 -------------------------- */ | /* ------------------- Added for Version 1 -------------------------- */ | |||
int pattern_position; /* Offset to next item in the pattern */ | int pattern_position; /* Offset to next item in the pattern */ | |||
int next_item_length; /* Length of next item in the pattern */ | int next_item_length; /* Length of next item in the pattern */ | |||
/* ------------------- Added for Version 2 -------------------------- */ | ||||
const unsigned char *mark; /* Pointer to current mark or NULL */ | ||||
/* ------------------------------------------------------------------ */ | /* ------------------------------------------------------------------ */ | |||
} pcre_callout_block; | } pcre_callout_block; | |||
/* Same structure as above, but with 16 bit char pointers. */ | ||||
typedef struct pcre16_callout_block { | ||||
int version; /* Identifies version of block */ | ||||
/* ------------------------ Version 0 ------------------------------- */ | ||||
int callout_number; /* Number compiled into pattern */ | ||||
int *offset_vector; /* The offset vector */ | ||||
PCRE_SPTR16 subject; /* The subject being matched */ | ||||
int subject_length; /* The length of the subject */ | ||||
int start_match; /* Offset to start of this match attempt | ||||
*/ | ||||
int current_position; /* Where we currently are in the subject | ||||
*/ | ||||
int capture_top; /* Max current capture */ | ||||
int capture_last; /* Most recently closed capture */ | ||||
void *callout_data; /* Data passed in with the call */ | ||||
/* ------------------- Added for Version 1 -------------------------- */ | ||||
int pattern_position; /* Offset to next item in the pattern */ | ||||
int next_item_length; /* Length of next item in the pattern */ | ||||
/* ------------------- Added for Version 2 -------------------------- */ | ||||
const PCRE_UCHAR16 *mark; /* Pointer to current mark or NULL */ | ||||
/* ------------------------------------------------------------------ */ | ||||
} pcre16_callout_block; | ||||
/* Indirection for store get and free functions. These can be set to | /* Indirection for store get and free functions. These can be set to | |||
alternative malloc/free functions if required. Special ones are used in the | alternative malloc/free functions if required. Special ones are used in the | |||
non-recursive case for "frames". There is also an optional callout function | non-recursive case for "frames". There is also an optional callout function | |||
that is triggered by the (?) regex item. For Virtual Pascal, these definiti ons | that is triggered by the (?) regex item. For Virtual Pascal, these definiti ons | |||
have to take another form. */ | have to take another form. */ | |||
#ifndef VPCOMPAT | #ifndef VPCOMPAT | |||
PCRE_EXP_DECL void *(*pcre_malloc)(size_t); | PCRE_EXP_DECL void *(*pcre_malloc)(size_t); | |||
PCRE_EXP_DECL void (*pcre_free)(void *); | PCRE_EXP_DECL void (*pcre_free)(void *); | |||
PCRE_EXP_DECL void *(*pcre_stack_malloc)(size_t); | PCRE_EXP_DECL void *(*pcre_stack_malloc)(size_t); | |||
PCRE_EXP_DECL void (*pcre_stack_free)(void *); | PCRE_EXP_DECL void (*pcre_stack_free)(void *); | |||
PCRE_EXP_DECL int (*pcre_callout)(pcre_callout_block *); | PCRE_EXP_DECL int (*pcre_callout)(pcre_callout_block *); | |||
PCRE_EXP_DECL void *(*pcre16_malloc)(size_t); | ||||
PCRE_EXP_DECL void (*pcre16_free)(void *); | ||||
PCRE_EXP_DECL void *(*pcre16_stack_malloc)(size_t); | ||||
PCRE_EXP_DECL void (*pcre16_stack_free)(void *); | ||||
PCRE_EXP_DECL int (*pcre16_callout)(pcre16_callout_block *); | ||||
#else /* VPCOMPAT */ | #else /* VPCOMPAT */ | |||
PCRE_EXP_DECL void *pcre_malloc(size_t); | PCRE_EXP_DECL void *pcre_malloc(size_t); | |||
PCRE_EXP_DECL void pcre_free(void *); | PCRE_EXP_DECL void pcre_free(void *); | |||
PCRE_EXP_DECL void *pcre_stack_malloc(size_t); | PCRE_EXP_DECL void *pcre_stack_malloc(size_t); | |||
PCRE_EXP_DECL void pcre_stack_free(void *); | PCRE_EXP_DECL void pcre_stack_free(void *); | |||
PCRE_EXP_DECL int pcre_callout(pcre_callout_block *); | PCRE_EXP_DECL int pcre_callout(pcre_callout_block *); | |||
PCRE_EXP_DECL void *pcre16_malloc(size_t); | ||||
PCRE_EXP_DECL void pcre16_free(void *); | ||||
PCRE_EXP_DECL void *pcre16_stack_malloc(size_t); | ||||
PCRE_EXP_DECL void pcre16_stack_free(void *); | ||||
PCRE_EXP_DECL int pcre16_callout(pcre16_callout_block *); | ||||
#endif /* VPCOMPAT */ | #endif /* VPCOMPAT */ | |||
/* User defined callback which provides a stack just before the match start | ||||
s. */ | ||||
typedef pcre_jit_stack *(*pcre_jit_callback)(void *); | ||||
typedef pcre16_jit_stack *(*pcre16_jit_callback)(void *); | ||||
/* Exported PCRE functions */ | /* Exported PCRE functions */ | |||
PCRE_EXP_DECL pcre *pcre_compile(const char *, int, const char **, int *, | PCRE_EXP_DECL pcre *pcre_compile(const char *, int, const char **, int *, | |||
const unsigned char *); | const unsigned char *); | |||
PCRE_EXP_DECL pcre16 *pcre16_compile(PCRE_SPTR16, int, const char **, int * | ||||
, | ||||
const unsigned char *); | ||||
PCRE_EXP_DECL pcre *pcre_compile2(const char *, int, int *, const char **, | PCRE_EXP_DECL pcre *pcre_compile2(const char *, int, int *, const char **, | |||
int *, const unsigned char *); | int *, const unsigned char *); | |||
PCRE_EXP_DECL pcre16 *pcre16_compile2(PCRE_SPTR16, int, int *, const char * | ||||
*, | ||||
int *, const unsigned char *); | ||||
PCRE_EXP_DECL int pcre_config(int, void *); | PCRE_EXP_DECL int pcre_config(int, void *); | |||
PCRE_EXP_DECL int pcre16_config(int, void *); | ||||
PCRE_EXP_DECL int pcre_copy_named_substring(const pcre *, const char *, | PCRE_EXP_DECL int pcre_copy_named_substring(const pcre *, const char *, | |||
int *, int, const char *, char *, int); | int *, int, const char *, char *, int); | |||
PCRE_EXP_DECL int pcre_copy_substring(const char *, int *, int, int, char | PCRE_EXP_DECL int pcre16_copy_named_substring(const pcre16 *, PCRE_SPTR16, | |||
*, | int *, int, PCRE_SPTR16, PCRE_UCHAR16 *, int); | |||
int); | PCRE_EXP_DECL int pcre_copy_substring(const char *, int *, int, int, | |||
char *, int); | ||||
PCRE_EXP_DECL int pcre16_copy_substring(PCRE_SPTR16, int *, int, int, | ||||
PCRE_UCHAR16 *, int); | ||||
PCRE_EXP_DECL int pcre_dfa_exec(const pcre *, const pcre_extra *, | PCRE_EXP_DECL int pcre_dfa_exec(const pcre *, const pcre_extra *, | |||
const char *, int, int, int, int *, int , int *, int); | const char *, int, int, int, int *, int , int *, int); | |||
PCRE_EXP_DECL int pcre16_dfa_exec(const pcre16 *, const pcre16_extra *, | ||||
PCRE_SPTR16, int, int, int, int *, int , int *, int); | ||||
PCRE_EXP_DECL int pcre_exec(const pcre *, const pcre_extra *, PCRE_SPTR, | PCRE_EXP_DECL int pcre_exec(const pcre *, const pcre_extra *, PCRE_SPTR, | |||
int, int, int, int *, int); | int, int, int, int *, int); | |||
PCRE_EXP_DECL int pcre16_exec(const pcre16 *, const pcre16_extra *, | ||||
PCRE_SPTR16, int, int, int, int *, int); | ||||
PCRE_EXP_DECL void pcre_free_substring(const char *); | PCRE_EXP_DECL void pcre_free_substring(const char *); | |||
PCRE_EXP_DECL void pcre16_free_substring(PCRE_SPTR16); | ||||
PCRE_EXP_DECL void pcre_free_substring_list(const char **); | PCRE_EXP_DECL void pcre_free_substring_list(const char **); | |||
PCRE_EXP_DECL void pcre16_free_substring_list(PCRE_SPTR16 *); | ||||
PCRE_EXP_DECL int pcre_fullinfo(const pcre *, const pcre_extra *, int, | PCRE_EXP_DECL int pcre_fullinfo(const pcre *, const pcre_extra *, int, | |||
void *); | void *); | |||
PCRE_EXP_DECL int pcre16_fullinfo(const pcre16 *, const pcre16_extra *, in | ||||
t, | ||||
void *); | ||||
PCRE_EXP_DECL int pcre_get_named_substring(const pcre *, const char *, | PCRE_EXP_DECL int pcre_get_named_substring(const pcre *, const char *, | |||
int *, int, const char *, const char **); | int *, int, const char *, const char **); | |||
PCRE_EXP_DECL int pcre16_get_named_substring(const pcre16 *, PCRE_SPTR16, | ||||
int *, int, PCRE_SPTR16, PCRE_SPTR16 *); | ||||
PCRE_EXP_DECL int pcre_get_stringnumber(const pcre *, const char *); | PCRE_EXP_DECL int pcre_get_stringnumber(const pcre *, const char *); | |||
PCRE_EXP_DECL int pcre16_get_stringnumber(const pcre16 *, PCRE_SPTR16); | ||||
PCRE_EXP_DECL int pcre_get_stringtable_entries(const pcre *, const char *, | PCRE_EXP_DECL int pcre_get_stringtable_entries(const pcre *, const char *, | |||
char **, char **); | char **, char **); | |||
PCRE_EXP_DECL int pcre16_get_stringtable_entries(const pcre16 *, PCRE_SPTR | ||||
16, | ||||
PCRE_UCHAR16 **, PCRE_UCHAR16 **); | ||||
PCRE_EXP_DECL int pcre_get_substring(const char *, int *, int, int, | PCRE_EXP_DECL int pcre_get_substring(const char *, int *, int, int, | |||
const char **); | const char **); | |||
PCRE_EXP_DECL int pcre16_get_substring(PCRE_SPTR16, int *, int, int, | ||||
PCRE_SPTR16 *); | ||||
PCRE_EXP_DECL int pcre_get_substring_list(const char *, int *, int, | PCRE_EXP_DECL int pcre_get_substring_list(const char *, int *, int, | |||
const char ***); | const char ***); | |||
PCRE_EXP_DECL int pcre_info(const pcre *, int *, int *); | PCRE_EXP_DECL int pcre16_get_substring_list(PCRE_SPTR16, int *, int, | |||
PCRE_SPTR16 **); | ||||
PCRE_EXP_DECL const unsigned char *pcre_maketables(void); | PCRE_EXP_DECL const unsigned char *pcre_maketables(void); | |||
PCRE_EXP_DECL const unsigned char *pcre16_maketables(void); | ||||
PCRE_EXP_DECL int pcre_refcount(pcre *, int); | PCRE_EXP_DECL int pcre_refcount(pcre *, int); | |||
PCRE_EXP_DECL int pcre16_refcount(pcre16 *, int); | ||||
PCRE_EXP_DECL pcre_extra *pcre_study(const pcre *, int, const char **); | PCRE_EXP_DECL pcre_extra *pcre_study(const pcre *, int, const char **); | |||
PCRE_EXP_DECL pcre16_extra *pcre16_study(const pcre16 *, int, const char ** | ||||
); | ||||
PCRE_EXP_DECL void pcre_free_study(pcre_extra *); | ||||
PCRE_EXP_DECL void pcre16_free_study(pcre16_extra *); | ||||
PCRE_EXP_DECL const char *pcre_version(void); | PCRE_EXP_DECL const char *pcre_version(void); | |||
PCRE_EXP_DECL const char *pcre16_version(void); | ||||
/* Utility functions for byte order swaps. */ | ||||
PCRE_EXP_DECL int pcre_pattern_to_host_byte_order(pcre *, pcre_extra *, | ||||
const unsigned char *); | ||||
PCRE_EXP_DECL int pcre16_pattern_to_host_byte_order(pcre16 *, pcre16_extra | ||||
*, | ||||
const unsigned char *); | ||||
PCRE_EXP_DECL int pcre16_utf16_to_host_byte_order(PCRE_UCHAR16 *, | ||||
PCRE_SPTR16, int, int *, int); | ||||
/* JIT compiler related functions. */ | ||||
PCRE_EXP_DECL pcre_jit_stack *pcre_jit_stack_alloc(int, int); | ||||
PCRE_EXP_DECL pcre16_jit_stack *pcre16_jit_stack_alloc(int, int); | ||||
PCRE_EXP_DECL void pcre_jit_stack_free(pcre_jit_stack *); | ||||
PCRE_EXP_DECL void pcre16_jit_stack_free(pcre16_jit_stack *); | ||||
PCRE_EXP_DECL void pcre_assign_jit_stack(pcre_extra *, | ||||
pcre_jit_callback, void *); | ||||
PCRE_EXP_DECL void pcre16_assign_jit_stack(pcre16_extra *, | ||||
pcre16_jit_callback, void *); | ||||
#ifdef __cplusplus | #ifdef __cplusplus | |||
} /* extern "C" */ | } /* extern "C" */ | |||
#endif | #endif | |||
#endif /* End of pcre.h */ | #endif /* End of pcre.h */ | |||
End of changes. 41 change blocks. | ||||
69 lines changed or deleted | 297 lines changed or added | |||
pcre_internal.h | pcre_internal.h | |||
---|---|---|---|---|
/************************************************* | /************************************************* | |||
* Perl-Compatible Regular Expressions * | * Perl-Compatible Regular Expressions * | |||
*************************************************/ | *************************************************/ | |||
/* PCRE is a library of functions to support regular expressions whose synt ax | /* PCRE is a library of functions to support regular expressions whose synt ax | |||
and semantics are as close as possible to those of the Perl 5 language. | and semantics are as close as possible to those of the Perl 5 language. | |||
Written by Philip Hazel | Written by Philip Hazel | |||
Copyright (c) 1997-2007 University of Cambridge | Copyright (c) 1997-2012 University of Cambridge | |||
--------------------------------------------------------------------------- -- | --------------------------------------------------------------------------- -- | |||
Redistribution and use in source and binary forms, with or without | Redistribution and use in source and binary forms, with or without | |||
modification, are permitted provided that the following conditions are met: | modification, are permitted provided that the following conditions are met: | |||
* Redistributions of source code must retain the above copyright notice , | * Redistributions of source code must retain the above copyright notice , | |||
this list of conditions and the following disclaimer. | this list of conditions and the following disclaimer. | |||
* Redistributions in binary form must reproduce the above copyright | * Redistributions in binary form must reproduce the above copyright | |||
notice, this list of conditions and the following disclaimer in the | notice, this list of conditions and the following disclaimer in the | |||
skipping to change at line 42 | skipping to change at line 42 | |||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |||
POSSIBILITY OF SUCH DAMAGE. | POSSIBILITY OF SUCH DAMAGE. | |||
--------------------------------------------------------------------------- -- | --------------------------------------------------------------------------- -- | |||
*/ | */ | |||
/* This header contains definitions that are shared between the different | /* This header contains definitions that are shared between the different | |||
modules, but which are not relevant to the exported API. This includes some | modules, but which are not relevant to the exported API. This includes some | |||
functions whose names all begin with "_pcre_". */ | functions whose names all begin with "_pcre_" or "_pcre16_" depending on | |||
the PRIV macro. */ | ||||
#ifndef PCRE_INTERNAL_H | #ifndef PCRE_INTERNAL_H | |||
#define PCRE_INTERNAL_H | #define PCRE_INTERNAL_H | |||
/* Define DEBUG to get debugging output on stdout. */ | /* Define PCRE_DEBUG to get debugging output on stdout. */ | |||
#if 0 | #if 0 | |||
#define DEBUG | #define PCRE_DEBUG | |||
#endif | ||||
/* PCRE is compiled as an 8 bit library if it is not requested otherwise. * | ||||
/ | ||||
#ifndef COMPILE_PCRE16 | ||||
#define COMPILE_PCRE8 | ||||
#endif | ||||
/* If SUPPORT_UCP is defined, SUPPORT_UTF must also be defined. The | ||||
"configure" script ensures this, but not everybody uses "configure". */ | ||||
#if defined SUPPORT_UCP && !(defined SUPPORT_UTF) | ||||
#define SUPPORT_UTF 1 | ||||
#endif | ||||
/* We define SUPPORT_UTF if SUPPORT_UTF8 is enabled for compatibility | ||||
reasons with existing code. */ | ||||
#if defined SUPPORT_UTF8 && !(defined SUPPORT_UTF) | ||||
#define SUPPORT_UTF 1 | ||||
#endif | ||||
/* Fixme: SUPPORT_UTF8 should be eventually disappear from the code. | ||||
Until then we define it if SUPPORT_UTF is defined. */ | ||||
#if defined SUPPORT_UTF && !(defined SUPPORT_UTF8) | ||||
#define SUPPORT_UTF8 1 | ||||
#endif | ||||
/* We do not support both EBCDIC and UTF-8/16 at the same time. The "config | ||||
ure" | ||||
script prevents both being selected, but not everybody uses "configure". */ | ||||
#if defined EBCDIC && defined SUPPORT_UTF | ||||
#error The use of both EBCDIC and SUPPORT_UTF8/16 is not supported. | ||||
#endif | #endif | |||
/* Use a macro for debugging printing, 'cause that eliminates the use of #i fdef | /* Use a macro for debugging printing, 'cause that eliminates the use of #i fdef | |||
inline, and there are *still* stupid compilers about that don't like indent ed | inline, and there are *still* stupid compilers about that don't like indent ed | |||
pre-processor statements, or at least there were when I first wrote this. A fter | pre-processor statements, or at least there were when I first wrote this. A fter | |||
all, it had only been about 10 years then... | all, it had only been about 10 years then... | |||
It turns out that the Mac Debugging.h header also defines the macro DPRINTF , so | It turns out that the Mac Debugging.h header also defines the macro DPRINTF , so | |||
be absolutely sure we get our version. */ | be absolutely sure we get our version. */ | |||
#undef DPRINTF | #undef DPRINTF | |||
#ifdef DEBUG | #ifdef PCRE_DEBUG | |||
#define DPRINTF(p) printf p | #define DPRINTF(p) printf p | |||
#else | #else | |||
#define DPRINTF(p) /* Nothing */ | #define DPRINTF(p) /* Nothing */ | |||
#endif | #endif | |||
/* Standard C headers plus the external interface definition. The only time | /* Standard C headers plus the external interface definition. The only time | |||
setjmp and stdarg are used is when NO_RECURSE is set. */ | setjmp and stdarg are used is when NO_RECURSE is set. */ | |||
#include <ctype.h> | #include <ctype.h> | |||
#include <limits.h> | #include <limits.h> | |||
#include <setjmp.h> | ||||
#include <stdarg.h> | ||||
#include <stddef.h> | #include <stddef.h> | |||
#include <stdio.h> | #include <stdio.h> | |||
#include <stdlib.h> | #include <stdlib.h> | |||
#include <string.h> | #include <string.h> | |||
/* When compiling a DLL for Windows, the exported symbols have to be declar ed | /* When compiling a DLL for Windows, the exported symbols have to be declar ed | |||
using some MS magic. I found some useful information on this web page: | using some MS magic. I found some useful information on this web page: | |||
http://msdn2.microsoft.com/en-us/library/y4h7bcy6(VS.80).aspx. According to the | http://msdn2.microsoft.com/en-us/library/y4h7bcy6(VS.80).aspx. According to the | |||
information there, using __declspec(dllexport) without "extern" we have a | information there, using __declspec(dllexport) without "extern" we have a | |||
definition; with "extern" we have a declaration. The settings here override the | definition; with "extern" we have a declaration. The settings here override the | |||
skipping to change at line 108 | skipping to change at line 140 | |||
The reason for wrapping this in #ifndef PCRE_EXP_DECL is so that pcretest, | The reason for wrapping this in #ifndef PCRE_EXP_DECL is so that pcretest, | |||
which is an application, but needs to import this file in order to "peek" a t | which is an application, but needs to import this file in order to "peek" a t | |||
internals, can #include pcre.h first to get an application's-eye view. | internals, can #include pcre.h first to get an application's-eye view. | |||
In principle, people compiling for non-Windows, non-Unix-like (i.e. uncommo n, | In principle, people compiling for non-Windows, non-Unix-like (i.e. uncommo n, | |||
special-purpose environments) might want to stick other stuff in front of | special-purpose environments) might want to stick other stuff in front of | |||
exported symbols. That's why, in the non-Windows case, we set PCRE_EXP_DEFN and | exported symbols. That's why, in the non-Windows case, we set PCRE_EXP_DEFN and | |||
PCRE_EXP_DATA_DEFN only if they are not already set. */ | PCRE_EXP_DATA_DEFN only if they are not already set. */ | |||
#ifndef PCRE_EXP_DECL | #ifndef PCRE_EXP_DECL | |||
/*# ifdef _WIN32 | # ifdef _WIN32 | |||
# ifndef PCRE_STATIC | # ifndef PCRE_STATIC | |||
# define PCRE_EXP_DECL extern __declspec(dllexport) | # define PCRE_EXP_DECL extern __declspec(dllexport) | |||
# define PCRE_EXP_DEFN __declspec(dllexport) | # define PCRE_EXP_DEFN __declspec(dllexport) | |||
# define PCRE_EXP_DATA_DEFN __declspec(dllexport) | # define PCRE_EXP_DATA_DEFN __declspec(dllexport) | |||
# else | # else | |||
# define PCRE_EXP_DECL extern | # define PCRE_EXP_DECL extern | |||
# define PCRE_EXP_DEFN | # define PCRE_EXP_DEFN | |||
# define PCRE_EXP_DATA_DEFN | # define PCRE_EXP_DATA_DEFN | |||
# endif | # endif | |||
# else*/ | # else | |||
# ifdef __cplusplus | # ifdef __cplusplus | |||
# define PCRE_EXP_DECL extern "C" | # define PCRE_EXP_DECL extern "C" | |||
# else | # else | |||
# define PCRE_EXP_DECL extern | # define PCRE_EXP_DECL extern | |||
# endif | # endif | |||
# ifndef PCRE_EXP_DEFN | # ifndef PCRE_EXP_DEFN | |||
# define PCRE_EXP_DEFN PCRE_EXP_DECL | # define PCRE_EXP_DEFN PCRE_EXP_DECL | |||
# endif | # endif | |||
# ifndef PCRE_EXP_DATA_DEFN | # ifndef PCRE_EXP_DATA_DEFN | |||
# define PCRE_EXP_DATA_DEFN | # define PCRE_EXP_DATA_DEFN | |||
# endif | # endif | |||
# endif | # endif | |||
/*#endif*/ | #endif | |||
/* We need to have types that specify unsigned 16-bit and 32-bit integers. | /* When compiling with the MSVC compiler, it is sometimes necessary to incl | |||
We | ude | |||
a "calling convention" before exported function names. (This is secondhand | ||||
information; I know nothing about MSVC myself). For example, something like | ||||
void __cdecl function(....) | ||||
might be needed. In order so make this easy, all the exported functions hav | ||||
e | ||||
PCRE_CALL_CONVENTION just before their names. It is rarely needed; if not | ||||
set, we ensure here that it has no effect. */ | ||||
#ifndef PCRE_CALL_CONVENTION | ||||
#define PCRE_CALL_CONVENTION | ||||
#endif | ||||
/* We need to have types that specify unsigned 8, 16 and 32-bit integers. W | ||||
e | ||||
cannot determine these outside the compilation (e.g. by running a program a s | cannot determine these outside the compilation (e.g. by running a program a s | |||
part of "configure") because PCRE is often cross-compiled for use on other | part of "configure") because PCRE is often cross-compiled for use on other | |||
systems. Instead we make use of the maximum sizes that are available at | systems. Instead we make use of the maximum sizes that are available at | |||
preprocessor time in standard C environments. */ | preprocessor time in standard C environments. */ | |||
typedef unsigned char pcre_uint8; | ||||
#if USHRT_MAX == 65535 | #if USHRT_MAX == 65535 | |||
typedef unsigned short pcre_uint16; | typedef unsigned short pcre_uint16; | |||
typedef short pcre_int16; | ||||
#elif UINT_MAX == 65535 | #elif UINT_MAX == 65535 | |||
typedef unsigned int pcre_uint16; | typedef unsigned int pcre_uint16; | |||
typedef int pcre_int16; | ||||
#else | #else | |||
#error Cannot determine a type for 16-bit unsigned integers | #error Cannot determine a type for 16-bit unsigned integers | |||
#endif | #endif | |||
#if UINT_MAX == 4294967295 | #if UINT_MAX == 4294967295 | |||
typedef unsigned int pcre_uint32; | typedef unsigned int pcre_uint32; | |||
typedef int pcre_int32; | ||||
#elif ULONG_MAX == 4294967295 | #elif ULONG_MAX == 4294967295 | |||
typedef unsigned long int pcre_uint32; | typedef unsigned long int pcre_uint32; | |||
typedef long int pcre_int32; | ||||
#else | #else | |||
#error Cannot determine a type for 32-bit unsigned integers | #error Cannot determine a type for 32-bit unsigned integers | |||
#endif | #endif | |||
/* When checking for integer overflow in pcre_compile(), we need to handle | ||||
large integers. If a 64-bit integer type is available, we can use that. | ||||
Otherwise we have to cast to double, which of course requires floating poin | ||||
t | ||||
arithmetic. Handle this by defining a macro for the appropriate type. If | ||||
stdint.h is available, include it; it may define INT64_MAX. Systems that do | ||||
not | ||||
have stdint.h (e.g. Solaris) may have inttypes.h. The macro int64_t may be | ||||
set | ||||
by "configure". */ | ||||
#if HAVE_STDINT_H | ||||
#include <stdint.h> | ||||
#elif HAVE_INTTYPES_H | ||||
#include <inttypes.h> | ||||
#endif | ||||
#if defined INT64_MAX || defined int64_t | ||||
#define INT64_OR_DOUBLE int64_t | ||||
#else | ||||
#define INT64_OR_DOUBLE double | ||||
#endif | ||||
/* All character handling must be done as unsigned characters. Otherwise th ere | /* All character handling must be done as unsigned characters. Otherwise th ere | |||
are problems with top-bit-set characters and functions such as isspace(). | are problems with top-bit-set characters and functions such as isspace(). | |||
However, we leave the interface to the outside world as char *, because tha | However, we leave the interface to the outside world as char * or short *, | |||
t | because that should make things easier for callers. This character type is | |||
should make things easier for callers. We define a short type for unsigned | called pcre_uchar. | |||
char | ||||
to save lots of typing. I tried "uchar", but it causes problems on Digital | The IN_UCHARS macro multiply its argument with the byte size of the current | |||
Unix, where it is defined in sys/types, so use "uschar" instead. */ | pcre_uchar type. Useful for memcpy and such operations, whose require the | |||
byte size of their input/output buffers. | ||||
The MAX_255 macro checks whether its pcre_uchar input is less than 256. | ||||
The TABLE_GET macro is designed for accessing elements of tables whose cont | ||||
ain | ||||
exactly 256 items. When the character is able to contain more than 256 | ||||
items, some check is needed before accessing these tables. | ||||
*/ | ||||
#ifdef COMPILE_PCRE8 | ||||
typedef unsigned char uschar; | typedef unsigned char pcre_uchar; | |||
#define IN_UCHARS(x) (x) | ||||
#define MAX_255(c) 1 | ||||
#define TABLE_GET(c, table, default) ((table)[c]) | ||||
#else | ||||
#ifdef COMPILE_PCRE16 | ||||
#if USHRT_MAX != 65535 | ||||
/* This is a warning message. Change PCRE_UCHAR16 to a 16 bit data type in | ||||
pcre.h(.in) and disable (comment out) this message. */ | ||||
#error Warning: PCRE_UCHAR16 is not a 16 bit data type. | ||||
#endif | ||||
typedef pcre_uint16 pcre_uchar; | ||||
#define IN_UCHARS(x) ((x) << 1) | ||||
#define MAX_255(c) ((c) <= 255u) | ||||
#define TABLE_GET(c, table, default) (MAX_255(c)? ((table)[c]):(default)) | ||||
#else | ||||
#error Unsupported compiling mode | ||||
#endif /* COMPILE_PCRE16 */ | ||||
#endif /* COMPILE_PCRE8 */ | ||||
/* This is an unsigned int value that no character can ever have. UTF-8 | /* This is an unsigned int value that no character can ever have. UTF-8 | |||
characters only go up to 0x7fffffff (though Unicode doesn't go beyond | characters only go up to 0x7fffffff (though Unicode doesn't go beyond | |||
0x0010ffff). */ | 0x0010ffff). */ | |||
#define NOTACHAR 0xffffffff | #define NOTACHAR 0xffffffff | |||
/* PCRE is able to support several different kinds of newline (CR, LF, CRLF , | /* PCRE is able to support several different kinds of newline (CR, LF, CRLF , | |||
"any" and "anycrlf" at present). The following macros are used to package u p | "any" and "anycrlf" at present). The following macros are used to package u p | |||
testing for newlines. NLBLOCK, PSSTART, and PSEND are defined in the variou s | testing for newlines. NLBLOCK, PSSTART, and PSEND are defined in the variou s | |||
skipping to change at line 185 | skipping to change at line 292 | |||
#define NLTYPE_FIXED 0 /* Newline is a fixed length string */ | #define NLTYPE_FIXED 0 /* Newline is a fixed length string */ | |||
#define NLTYPE_ANY 1 /* Newline is any Unicode line ending */ | #define NLTYPE_ANY 1 /* Newline is any Unicode line ending */ | |||
#define NLTYPE_ANYCRLF 2 /* Newline is CR, LF, or CRLF */ | #define NLTYPE_ANYCRLF 2 /* Newline is CR, LF, or CRLF */ | |||
/* This macro checks for a newline at the given position */ | /* This macro checks for a newline at the given position */ | |||
#define IS_NEWLINE(p) \ | #define IS_NEWLINE(p) \ | |||
((NLBLOCK->nltype != NLTYPE_FIXED)? \ | ((NLBLOCK->nltype != NLTYPE_FIXED)? \ | |||
((p) < NLBLOCK->PSEND && \ | ((p) < NLBLOCK->PSEND && \ | |||
_pcre_is_newline((p), NLBLOCK->nltype, NLBLOCK->PSEND, &(NLBLOCK->nlle | PRIV(is_newline)((p), NLBLOCK->nltype, NLBLOCK->PSEND, \ | |||
n),\ | &(NLBLOCK->nllen), utf)) \ | |||
utf8)) \ | ||||
: \ | : \ | |||
((p) <= NLBLOCK->PSEND - NLBLOCK->nllen && \ | ((p) <= NLBLOCK->PSEND - NLBLOCK->nllen && \ | |||
(p)[0] == NLBLOCK->nl[0] && \ | (p)[0] == NLBLOCK->nl[0] && \ | |||
(NLBLOCK->nllen == 1 || (p)[1] == NLBLOCK->nl[1]) \ | (NLBLOCK->nllen == 1 || (p)[1] == NLBLOCK->nl[1]) \ | |||
) \ | ) \ | |||
) | ) | |||
/* This macro checks for a newline immediately preceding the given position */ | /* This macro checks for a newline immediately preceding the given position */ | |||
#define WAS_NEWLINE(p) \ | #define WAS_NEWLINE(p) \ | |||
((NLBLOCK->nltype != NLTYPE_FIXED)? \ | ((NLBLOCK->nltype != NLTYPE_FIXED)? \ | |||
((p) > NLBLOCK->PSSTART && \ | ((p) > NLBLOCK->PSSTART && \ | |||
_pcre_was_newline((p), NLBLOCK->nltype, NLBLOCK->PSSTART, \ | PRIV(was_newline)((p), NLBLOCK->nltype, NLBLOCK->PSSTART, \ | |||
&(NLBLOCK->nllen), utf8)) \ | &(NLBLOCK->nllen), utf)) \ | |||
: \ | : \ | |||
((p) >= NLBLOCK->PSSTART + NLBLOCK->nllen && \ | ((p) >= NLBLOCK->PSSTART + NLBLOCK->nllen && \ | |||
(p)[-NLBLOCK->nllen] == NLBLOCK->nl[0] && \ | (p)[-NLBLOCK->nllen] == NLBLOCK->nl[0] && \ | |||
(NLBLOCK->nllen == 1 || (p)[-NLBLOCK->nllen+1] == NLBLOCK->nl[1]) \ | (NLBLOCK->nllen == 1 || (p)[-NLBLOCK->nllen+1] == NLBLOCK->nl[1]) \ | |||
) \ | ) \ | |||
) | ) | |||
/* When PCRE is compiled as a C++ library, the subject pointer can be repla ced | /* When PCRE is compiled as a C++ library, the subject pointer can be repla ced | |||
with a custom type. This makes it possible, for example, to allow pcre_exec () | with a custom type. This makes it possible, for example, to allow pcre_exec () | |||
to process subject strings that are discontinuous by using a smart pointer | to process subject strings that are discontinuous by using a smart pointer | |||
class. It must always be possible to inspect all of the subject string in | class. It must always be possible to inspect all of the subject string in | |||
pcre_exec() because of the way it backtracks. Two macros are required in th e | pcre_exec() because of the way it backtracks. Two macros are required in th e | |||
normal case, for sign-unspecified and unsigned char pointers. The former is | normal case, for sign-unspecified and unsigned char pointers. The former is | |||
used for the external interface and appears in pcre.h, which is why its nam e | used for the external interface and appears in pcre.h, which is why its nam e | |||
must begin with PCRE_. */ | must begin with PCRE_. */ | |||
#ifdef CUSTOM_SUBJECT_PTR | #ifdef CUSTOM_SUBJECT_PTR | |||
#define PCRE_SPTR CUSTOM_SUBJECT_PTR | #define PCRE_PUCHAR CUSTOM_SUBJECT_PTR | |||
#define USPTR CUSTOM_SUBJECT_PTR | ||||
#else | #else | |||
#define PCRE_SPTR const char * | #define PCRE_PUCHAR const pcre_uchar * | |||
#define USPTR const unsigned char * | ||||
#endif | #endif | |||
/* Include the public PCRE header and the definitions of UCP character prop erty | /* Include the public PCRE header and the definitions of UCP character prop erty | |||
values. */ | values. */ | |||
#include "pcre.h" | #include "pcre.h" | |||
#include "ucp.h" | #include "ucp.h" | |||
/* When compiling for use with the Virtual Pascal compiler, these functions | /* When compiling for use with the Virtual Pascal compiler, these functions | |||
need to have their names changed. PCRE must be compiled with the -DVPCOMPAT | need to have their names changed. PCRE must be compiled with the -DVPCOMPAT | |||
skipping to change at line 291 | skipping to change at line 396 | |||
offset limits the size of the compiled regex to around 64K, which is big en ough | offset limits the size of the compiled regex to around 64K, which is big en ough | |||
for almost everybody. However, I received a request for an even bigger limi t. | for almost everybody. However, I received a request for an even bigger limi t. | |||
For this reason, and also to make the code easier to maintain, the storing and | For this reason, and also to make the code easier to maintain, the storing and | |||
loading of offsets from the byte string is now handled by the macros that a re | loading of offsets from the byte string is now handled by the macros that a re | |||
defined here. | defined here. | |||
The macros are controlled by the value of LINK_SIZE. This defaults to 2 in | The macros are controlled by the value of LINK_SIZE. This defaults to 2 in | |||
the config.h file, but can be overridden by using -D on the command line. T his | the config.h file, but can be overridden by using -D on the command line. T his | |||
is automated on Unix systems via the "configure" command. */ | is automated on Unix systems via the "configure" command. */ | |||
#ifdef COMPILE_PCRE8 | ||||
#if LINK_SIZE == 2 | #if LINK_SIZE == 2 | |||
#define PUT(a,n,d) \ | #define PUT(a,n,d) \ | |||
(a[n] = (d) >> 8), \ | (a[n] = (d) >> 8), \ | |||
(a[(n)+1] = (d) & 255) | (a[(n)+1] = (d) & 255) | |||
#define GET(a,n) \ | #define GET(a,n) \ | |||
(((a)[n] << 8) | (a)[(n)+1]) | (((a)[n] << 8) | (a)[(n)+1]) | |||
#define MAX_PATTERN_SIZE (1 << 16) | #define MAX_PATTERN_SIZE (1 << 16) | |||
skipping to change at line 325 | skipping to change at line 432 | |||
#define PUT(a,n,d) \ | #define PUT(a,n,d) \ | |||
(a[n] = (d) >> 24), \ | (a[n] = (d) >> 24), \ | |||
(a[(n)+1] = (d) >> 16), \ | (a[(n)+1] = (d) >> 16), \ | |||
(a[(n)+2] = (d) >> 8), \ | (a[(n)+2] = (d) >> 8), \ | |||
(a[(n)+3] = (d) & 255) | (a[(n)+3] = (d) & 255) | |||
#define GET(a,n) \ | #define GET(a,n) \ | |||
(((a)[n] << 24) | ((a)[(n)+1] << 16) | ((a)[(n)+2] << 8) | (a)[(n)+3]) | (((a)[n] << 24) | ((a)[(n)+1] << 16) | ((a)[(n)+2] << 8) | (a)[(n)+3]) | |||
#define MAX_PATTERN_SIZE (1 << 30) /* Keep it positive */ | /* Keep it positive */ | |||
#define MAX_PATTERN_SIZE (1 << 30) | ||||
#else | ||||
#error LINK_SIZE must be either 2, 3, or 4 | ||||
#endif | ||||
#else /* COMPILE_PCRE8 */ | ||||
#ifdef COMPILE_PCRE16 | ||||
#if LINK_SIZE == 2 | ||||
#undef LINK_SIZE | ||||
#define LINK_SIZE 1 | ||||
#define PUT(a,n,d) \ | ||||
(a[n] = (d)) | ||||
#define GET(a,n) \ | ||||
(a[n]) | ||||
#define MAX_PATTERN_SIZE (1 << 16) | ||||
#elif LINK_SIZE == 3 || LINK_SIZE == 4 | ||||
#undef LINK_SIZE | ||||
#define LINK_SIZE 2 | ||||
#define PUT(a,n,d) \ | ||||
(a[n] = (d) >> 16), \ | ||||
(a[(n)+1] = (d) & 65535) | ||||
#define GET(a,n) \ | ||||
(((a)[n] << 16) | (a)[(n)+1]) | ||||
/* Keep it positive */ | ||||
#define MAX_PATTERN_SIZE (1 << 30) | ||||
#else | #else | |||
#error LINK_SIZE must be either 2, 3, or 4 | #error LINK_SIZE must be either 2, 3, or 4 | |||
#endif | #endif | |||
#else | ||||
#error Unsupported compiling mode | ||||
#endif /* COMPILE_PCRE16 */ | ||||
#endif /* COMPILE_PCRE8 */ | ||||
/* Convenience macro defined in terms of the others */ | /* Convenience macro defined in terms of the others */ | |||
#define PUTINC(a,n,d) PUT(a,n,d), a += LINK_SIZE | #define PUTINC(a,n,d) PUT(a,n,d), a += LINK_SIZE | |||
/* PCRE uses some other 2-byte quantities that do not change when the size of | /* PCRE uses some other 2-byte quantities that do not change when the size of | |||
offsets changes. There are used for repeat counts and for other things such as | offsets changes. There are used for repeat counts and for other things such as | |||
capturing parenthesis numbers in back references. */ | capturing parenthesis numbers in back references. */ | |||
#ifdef COMPILE_PCRE8 | ||||
#define IMM2_SIZE 2 | ||||
#define PUT2(a,n,d) \ | #define PUT2(a,n,d) \ | |||
a[n] = (d) >> 8; \ | a[n] = (d) >> 8; \ | |||
a[(n)+1] = (d) & 255 | a[(n)+1] = (d) & 255 | |||
#define GET2(a,n) \ | #define GET2(a,n) \ | |||
(((a)[n] << 8) | (a)[(n)+1]) | (((a)[n] << 8) | (a)[(n)+1]) | |||
#define PUT2INC(a,n,d) PUT2(a,n,d), a += 2 | #else /* COMPILE_PCRE8 */ | |||
#ifdef COMPILE_PCRE16 | ||||
#define IMM2_SIZE 1 | ||||
#define PUT2(a,n,d) \ | ||||
a[n] = d | ||||
#define GET2(a,n) \ | ||||
a[n] | ||||
#else | ||||
#error Unsupported compiling mode | ||||
#endif /* COMPILE_PCRE16 */ | ||||
#endif /* COMPILE_PCRE8 */ | ||||
/* When UTF-8 encoding is being used, a character is no longer just a singl | #define PUT2INC(a,n,d) PUT2(a,n,d), a += IMM2_SIZE | |||
e | ||||
byte. The macros for character handling generate simple sequences when used | ||||
in | ||||
byte-mode, and more complicated ones for UTF-8 characters. BACKCHAR should | ||||
never be called in byte mode. To make sure it can never even appear when UT | ||||
F-8 | ||||
support is omitted, we don't even define it. */ | ||||
#ifndef SUPPORT_UTF8 | /* When UTF encoding is being used, a character is no longer just a single | |||
character. The macros for character handling generate simple sequences when | ||||
used in character-mode, and more complicated ones for UTF characters. | ||||
GETCHARLENTEST and other macros are not used when UTF is not supported, | ||||
so they are not defined. To make sure they can never even appear when | ||||
UTF support is omitted, we don't even define them. */ | ||||
#ifndef SUPPORT_UTF | ||||
/* #define MAX_VALUE_FOR_SINGLE_CHAR */ | ||||
/* #define HAS_EXTRALEN(c) */ | ||||
/* #define GET_EXTRALEN(c) */ | ||||
/* #define NOT_FIRSTCHAR(c) */ | ||||
#define GETCHAR(c, eptr) c = *eptr; | #define GETCHAR(c, eptr) c = *eptr; | |||
#define GETCHARTEST(c, eptr) c = *eptr; | #define GETCHARTEST(c, eptr) c = *eptr; | |||
#define GETCHARINC(c, eptr) c = *eptr++; | #define GETCHARINC(c, eptr) c = *eptr++; | |||
#define GETCHARINCTEST(c, eptr) c = *eptr++; | #define GETCHARINCTEST(c, eptr) c = *eptr++; | |||
#define GETCHARLEN(c, eptr, len) c = *eptr; | #define GETCHARLEN(c, eptr, len) c = *eptr; | |||
/* #define GETCHARLENTEST(c, eptr, len) */ | ||||
/* #define BACKCHAR(eptr) */ | /* #define BACKCHAR(eptr) */ | |||
/* #define FORWARDCHAR(eptr) */ | ||||
/* #define ACROSSCHAR(condition, eptr, action) */ | ||||
#else /* SUPPORT_UTF8 */ | #else /* SUPPORT_UTF */ | |||
#ifdef COMPILE_PCRE8 | ||||
/* These macros were originally written in the form of loops that used data | ||||
from the tables whose names start with PRIV(utf8_table). They were rewritte | ||||
n by | ||||
a user so as not to use loops, because in some environments this gives a | ||||
significant performance advantage, and it seems never to do any harm. */ | ||||
/* Tells the biggest code point which can be encoded as a single character. | ||||
*/ | ||||
#define MAX_VALUE_FOR_SINGLE_CHAR 127 | ||||
/* Tests whether the code point needs extra characters to decode. */ | ||||
#define HAS_EXTRALEN(c) ((c) >= 0xc0) | ||||
/* Returns with the additional number of characters if IS_MULTICHAR(c) is T | ||||
RUE. | ||||
Otherwise it has an undefined behaviour. */ | ||||
#define GET_EXTRALEN(c) (PRIV(utf8_table4)[(c) & 0x3f]) | ||||
/* Returns TRUE, if the given character is not the first character | ||||
of a UTF sequence. */ | ||||
#define NOT_FIRSTCHAR(c) (((c) & 0xc0) == 0x80) | ||||
/* Base macro to pick up the remaining bytes of a UTF-8 character, not | ||||
advancing the pointer. */ | ||||
#define GETUTF8(c, eptr) \ | ||||
{ \ | ||||
if ((c & 0x20) == 0) \ | ||||
c = ((c & 0x1f) << 6) | (eptr[1] & 0x3f); \ | ||||
else if ((c & 0x10) == 0) \ | ||||
c = ((c & 0x0f) << 12) | ((eptr[1] & 0x3f) << 6) | (eptr[2] & 0x3f); | ||||
\ | ||||
else if ((c & 0x08) == 0) \ | ||||
c = ((c & 0x07) << 18) | ((eptr[1] & 0x3f) << 12) | \ | ||||
((eptr[2] & 0x3f) << 6) | (eptr[3] & 0x3f); \ | ||||
else if ((c & 0x04) == 0) \ | ||||
c = ((c & 0x03) << 24) | ((eptr[1] & 0x3f) << 18) | \ | ||||
((eptr[2] & 0x3f) << 12) | ((eptr[3] & 0x3f) << 6) | \ | ||||
(eptr[4] & 0x3f); \ | ||||
else \ | ||||
c = ((c & 0x01) << 30) | ((eptr[1] & 0x3f) << 24) | \ | ||||
((eptr[2] & 0x3f) << 18) | ((eptr[3] & 0x3f) << 12) | \ | ||||
((eptr[4] & 0x3f) << 6) | (eptr[5] & 0x3f); \ | ||||
} | ||||
/* Get the next UTF-8 character, not advancing the pointer. This is called when | /* Get the next UTF-8 character, not advancing the pointer. This is called when | |||
we know we are in UTF-8 mode. */ | we know we are in UTF-8 mode. */ | |||
#define GETCHAR(c, eptr) \ | #define GETCHAR(c, eptr) \ | |||
c = *eptr; \ | c = *eptr; \ | |||
if (c >= 0xc0) \ | if (c >= 0xc0) GETUTF8(c, eptr); | |||
{ \ | ||||
int gcii; \ | ||||
int gcaa = _pcre_utf8_table4[c & 0x3f]; /* Number of additional bytes | ||||
*/ \ | ||||
int gcss = 6*gcaa; \ | ||||
c = (c & _pcre_utf8_table3[gcaa]) << gcss; \ | ||||
for (gcii = 1; gcii <= gcaa; gcii++) \ | ||||
{ \ | ||||
gcss -= 6; \ | ||||
c |= (eptr[gcii] & 0x3f) << gcss; \ | ||||
} \ | ||||
} | ||||
/* Get the next UTF-8 character, testing for UTF-8 mode, and not advancing the | /* Get the next UTF-8 character, testing for UTF-8 mode, and not advancing the | |||
pointer. */ | pointer. */ | |||
#define GETCHARTEST(c, eptr) \ | #define GETCHARTEST(c, eptr) \ | |||
c = *eptr; \ | c = *eptr; \ | |||
if (utf8 && c >= 0xc0) \ | if (utf && c >= 0xc0) GETUTF8(c, eptr); | |||
/* Base macro to pick up the remaining bytes of a UTF-8 character, advancin | ||||
g | ||||
the pointer. */ | ||||
#define GETUTF8INC(c, eptr) \ | ||||
{ \ | { \ | |||
int gcii; \ | if ((c & 0x20) == 0) \ | |||
int gcaa = _pcre_utf8_table4[c & 0x3f]; /* Number of additional bytes | c = ((c & 0x1f) << 6) | (*eptr++ & 0x3f); \ | |||
*/ \ | else if ((c & 0x10) == 0) \ | |||
int gcss = 6*gcaa; \ | { \ | |||
c = (c & _pcre_utf8_table3[gcaa]) << gcss; \ | c = ((c & 0x0f) << 12) | ((*eptr & 0x3f) << 6) | (eptr[1] & 0x3f); \ | |||
for (gcii = 1; gcii <= gcaa; gcii++) \ | eptr += 2; \ | |||
} \ | ||||
else if ((c & 0x08) == 0) \ | ||||
{ \ | ||||
c = ((c & 0x07) << 18) | ((*eptr & 0x3f) << 12) | \ | ||||
((eptr[1] & 0x3f) << 6) | (eptr[2] & 0x3f); \ | ||||
eptr += 3; \ | ||||
} \ | ||||
else if ((c & 0x04) == 0) \ | ||||
{ \ | { \ | |||
gcss -= 6; \ | c = ((c & 0x03) << 24) | ((*eptr & 0x3f) << 18) | \ | |||
c |= (eptr[gcii] & 0x3f) << gcss; \ | ((eptr[1] & 0x3f) << 12) | ((eptr[2] & 0x3f) << 6) | \ | |||
(eptr[3] & 0x3f); \ | ||||
eptr += 4; \ | ||||
} \ | ||||
else \ | ||||
{ \ | ||||
c = ((c & 0x01) << 30) | ((*eptr & 0x3f) << 24) | \ | ||||
((eptr[1] & 0x3f) << 18) | ((eptr[2] & 0x3f) << 12) | \ | ||||
((eptr[3] & 0x3f) << 6) | (eptr[4] & 0x3f); \ | ||||
eptr += 5; \ | ||||
} \ | } \ | |||
} | } | |||
/* Get the next UTF-8 character, advancing the pointer. This is called when we | /* Get the next UTF-8 character, advancing the pointer. This is called when we | |||
know we are in UTF-8 mode. */ | know we are in UTF-8 mode. */ | |||
#define GETCHARINC(c, eptr) \ | #define GETCHARINC(c, eptr) \ | |||
c = *eptr++; \ | c = *eptr++; \ | |||
if (c >= 0xc0) \ | if (c >= 0xc0) GETUTF8INC(c, eptr); | |||
{ \ | ||||
int gcaa = _pcre_utf8_table4[c & 0x3f]; /* Number of additional bytes | ||||
*/ \ | ||||
int gcss = 6*gcaa; \ | ||||
c = (c & _pcre_utf8_table3[gcaa]) << gcss; \ | ||||
while (gcaa-- > 0) \ | ||||
{ \ | ||||
gcss -= 6; \ | ||||
c |= (*eptr++ & 0x3f) << gcss; \ | ||||
} \ | ||||
} | ||||
/* Get the next character, testing for UTF-8 mode, and advancing the pointe | /* Get the next character, testing for UTF-8 mode, and advancing the pointe | |||
r */ | r. | |||
This is called when we don't know if we are in UTF-8 mode. */ | ||||
#define GETCHARINCTEST(c, eptr) \ | #define GETCHARINCTEST(c, eptr) \ | |||
c = *eptr++; \ | c = *eptr++; \ | |||
if (utf8 && c >= 0xc0) \ | if (utf && c >= 0xc0) GETUTF8INC(c, eptr); | |||
/* Base macro to pick up the remaining bytes of a UTF-8 character, not | ||||
advancing the pointer, incrementing the length. */ | ||||
#define GETUTF8LEN(c, eptr, len) \ | ||||
{ \ | { \ | |||
int gcaa = _pcre_utf8_table4[c & 0x3f]; /* Number of additional bytes | if ((c & 0x20) == 0) \ | |||
*/ \ | { \ | |||
int gcss = 6*gcaa; \ | c = ((c & 0x1f) << 6) | (eptr[1] & 0x3f); \ | |||
c = (c & _pcre_utf8_table3[gcaa]) << gcss; \ | len++; \ | |||
while (gcaa-- > 0) \ | } \ | |||
else if ((c & 0x10) == 0) \ | ||||
{ \ | ||||
c = ((c & 0x0f) << 12) | ((eptr[1] & 0x3f) << 6) | (eptr[2] & 0x3f); | ||||
\ | ||||
len += 2; \ | ||||
} \ | ||||
else if ((c & 0x08) == 0) \ | ||||
{\ | ||||
c = ((c & 0x07) << 18) | ((eptr[1] & 0x3f) << 12) | \ | ||||
((eptr[2] & 0x3f) << 6) | (eptr[3] & 0x3f); \ | ||||
len += 3; \ | ||||
} \ | ||||
else if ((c & 0x04) == 0) \ | ||||
{ \ | { \ | |||
gcss -= 6; \ | c = ((c & 0x03) << 24) | ((eptr[1] & 0x3f) << 18) | \ | |||
c |= (*eptr++ & 0x3f) << gcss; \ | ((eptr[2] & 0x3f) << 12) | ((eptr[3] & 0x3f) << 6) | \ | |||
(eptr[4] & 0x3f); \ | ||||
len += 4; \ | ||||
} \ | ||||
else \ | ||||
{\ | ||||
c = ((c & 0x01) << 30) | ((eptr[1] & 0x3f) << 24) | \ | ||||
((eptr[2] & 0x3f) << 18) | ((eptr[3] & 0x3f) << 12) | \ | ||||
((eptr[4] & 0x3f) << 6) | (eptr[5] & 0x3f); \ | ||||
len += 5; \ | ||||
} \ | } \ | |||
} | } | |||
/* Get the next UTF-8 character, not advancing the pointer, incrementing le ngth | /* Get the next UTF-8 character, not advancing the pointer, incrementing le ngth | |||
if there are extra bytes. This is called when we know we are in UTF-8 mode. */ | if there are extra bytes. This is called when we know we are in UTF-8 mode. */ | |||
#define GETCHARLEN(c, eptr, len) \ | #define GETCHARLEN(c, eptr, len) \ | |||
c = *eptr; \ | c = *eptr; \ | |||
if (c >= 0xc0) \ | if (c >= 0xc0) GETUTF8LEN(c, eptr, len); | |||
{ \ | ||||
int gcii; \ | /* Get the next UTF-8 character, testing for UTF-8 mode, not advancing the | |||
int gcaa = _pcre_utf8_table4[c & 0x3f]; /* Number of additional bytes | pointer, incrementing length if there are extra bytes. This is called when | |||
*/ \ | we | |||
int gcss = 6*gcaa; \ | do not know if we are in UTF-8 mode. */ | |||
c = (c & _pcre_utf8_table3[gcaa]) << gcss; \ | ||||
for (gcii = 1; gcii <= gcaa; gcii++) \ | #define GETCHARLENTEST(c, eptr, len) \ | |||
{ \ | c = *eptr; \ | |||
gcss -= 6; \ | if (utf && c >= 0xc0) GETUTF8LEN(c, eptr, len); | |||
c |= (eptr[gcii] & 0x3f) << gcss; \ | ||||
} \ | ||||
len += gcaa; \ | ||||
} | ||||
/* If the pointer is not at the start of a character, move it back until | /* If the pointer is not at the start of a character, move it back until | |||
it is. This is called only in UTF-8 mode - we don't put a test within the m acro | it is. This is called only in UTF-8 mode - we don't put a test within the m acro | |||
because almost all calls are already within a block of UTF-8 only code. */ | because almost all calls are already within a block of UTF-8 only code. */ | |||
#define BACKCHAR(eptr) while((*eptr & 0xc0) == 0x80) eptr-- | #define BACKCHAR(eptr) while((*eptr & 0xc0) == 0x80) eptr-- | |||
/* Same as above, just in the other direction. */ | ||||
#define FORWARDCHAR(eptr) while((*eptr & 0xc0) == 0x80) eptr++ | ||||
/* Same as above, but it allows a fully customizable form. */ | ||||
#define ACROSSCHAR(condition, eptr, action) \ | ||||
while((condition) && ((eptr) & 0xc0) == 0x80) action | ||||
#else /* COMPILE_PCRE8 */ | ||||
#ifdef COMPILE_PCRE16 | ||||
/* Tells the biggest code point which can be encoded as a single character. | ||||
*/ | ||||
#define MAX_VALUE_FOR_SINGLE_CHAR 65535 | ||||
/* Tests whether the code point needs extra characters to decode. */ | ||||
#define HAS_EXTRALEN(c) (((c) & 0xfc00) == 0xd800) | ||||
/* Returns with the additional number of characters if IS_MULTICHAR(c) is T | ||||
RUE. | ||||
Otherwise it has an undefined behaviour. */ | ||||
#define GET_EXTRALEN(c) 1 | ||||
/* Returns TRUE, if the given character is not the first character | ||||
of a UTF sequence. */ | ||||
#define NOT_FIRSTCHAR(c) (((c) & 0xfc00) == 0xdc00) | ||||
/* Base macro to pick up the low surrogate of a UTF-16 character, not | ||||
advancing the pointer. */ | ||||
#define GETUTF16(c, eptr) \ | ||||
{ c = (((c & 0x3ff) << 10) | (eptr[1] & 0x3ff)) + 0x10000; } | ||||
/* Get the next UTF-16 character, not advancing the pointer. This is called | ||||
when | ||||
we know we are in UTF-16 mode. */ | ||||
#define GETCHAR(c, eptr) \ | ||||
c = *eptr; \ | ||||
if ((c & 0xfc00) == 0xd800) GETUTF16(c, eptr); | ||||
/* Get the next UTF-16 character, testing for UTF-16 mode, and not advancin | ||||
g the | ||||
pointer. */ | ||||
#define GETCHARTEST(c, eptr) \ | ||||
c = *eptr; \ | ||||
if (utf && (c & 0xfc00) == 0xd800) GETUTF16(c, eptr); | ||||
/* Base macro to pick up the low surrogate of a UTF-16 character, advancing | ||||
the pointer. */ | ||||
#define GETUTF16INC(c, eptr) \ | ||||
{ c = (((c & 0x3ff) << 10) | (*eptr++ & 0x3ff)) + 0x10000; } | ||||
/* Get the next UTF-16 character, advancing the pointer. This is called whe | ||||
n we | ||||
know we are in UTF-16 mode. */ | ||||
#define GETCHARINC(c, eptr) \ | ||||
c = *eptr++; \ | ||||
if ((c & 0xfc00) == 0xd800) GETUTF16INC(c, eptr); | ||||
/* Get the next character, testing for UTF-16 mode, and advancing the point | ||||
er. | ||||
This is called when we don't know if we are in UTF-16 mode. */ | ||||
#define GETCHARINCTEST(c, eptr) \ | ||||
c = *eptr++; \ | ||||
if (utf && (c & 0xfc00) == 0xd800) GETUTF16INC(c, eptr); | ||||
/* Base macro to pick up the low surrogate of a UTF-16 character, not | ||||
advancing the pointer, incrementing the length. */ | ||||
#define GETUTF16LEN(c, eptr, len) \ | ||||
{ c = (((c & 0x3ff) << 10) | (eptr[1] & 0x3ff)) + 0x10000; len++; } | ||||
/* Get the next UTF-16 character, not advancing the pointer, incrementing | ||||
length if there is a low surrogate. This is called when we know we are in | ||||
UTF-16 mode. */ | ||||
#define GETCHARLEN(c, eptr, len) \ | ||||
c = *eptr; \ | ||||
if ((c & 0xfc00) == 0xd800) GETUTF16LEN(c, eptr, len); | ||||
/* Get the next UTF-816character, testing for UTF-16 mode, not advancing th | ||||
e | ||||
pointer, incrementing length if there is a low surrogate. This is called wh | ||||
en | ||||
we do not know if we are in UTF-16 mode. */ | ||||
#define GETCHARLENTEST(c, eptr, len) \ | ||||
c = *eptr; \ | ||||
if (utf && (c & 0xfc00) == 0xd800) GETUTF16LEN(c, eptr, len); | ||||
/* If the pointer is not at the start of a character, move it back until | ||||
it is. This is called only in UTF-16 mode - we don't put a test within the | ||||
macro because almost all calls are already within a block of UTF-16 only | ||||
code. */ | ||||
#define BACKCHAR(eptr) if ((*eptr & 0xfc00) == 0xdc00) eptr-- | ||||
/* Same as above, just in the other direction. */ | ||||
#define FORWARDCHAR(eptr) if ((*eptr & 0xfc00) == 0xdc00) eptr++ | ||||
/* Same as above, but it allows a fully customizable form. */ | ||||
#define ACROSSCHAR(condition, eptr, action) \ | ||||
if ((condition) && ((eptr) & 0xfc00) == 0xdc00) action | ||||
#endif | #endif | |||
#endif /* COMPILE_PCRE8 */ | ||||
#endif /* SUPPORT_UTF */ | ||||
/* In case there is no definition of offsetof() provided - though any prope r | /* In case there is no definition of offsetof() provided - though any prope r | |||
Standard C system should have one. */ | Standard C system should have one. */ | |||
#ifndef offsetof | #ifndef offsetof | |||
#define offsetof(p_type,field) ((size_t)&(((p_type *)0)->field)) | #define offsetof(p_type,field) ((size_t)&(((p_type *)0)->field)) | |||
#endif | #endif | |||
/* These are the public options that can change during matching. */ | ||||
#define PCRE_IMS (PCRE_CASELESS|PCRE_MULTILINE|PCRE_DOTALL) | ||||
/* Private flags containing information about the compiled regex. They used to | /* Private flags containing information about the compiled regex. They used to | |||
live at the top end of the options word, but that got almost full, so now t hey | live at the top end of the options word, but that got almost full, so now t hey | |||
are in a 16-bit flags word. */ | are in a 16-bit flags word. From release 8.00, PCRE_NOPARTIAL is unused, as | |||
the restrictions on partial matching have been lifted. It remains for backw | ||||
ards | ||||
compatibility. */ | ||||
#define PCRE_NOPARTIAL 0x0001 /* can't use partial with this regex */ | #ifdef COMPILE_PCRE8 | |||
#define PCRE_FIRSTSET 0x0002 /* first_byte is set */ | #define PCRE_MODE 0x0001 /* compiled in 8 bit mode */ | |||
#define PCRE_REQCHSET 0x0004 /* req_byte is set */ | #endif | |||
#define PCRE_STARTLINE 0x0008 /* start after \n for multiline */ | #ifdef COMPILE_PCRE16 | |||
#define PCRE_JCHANGED 0x0010 /* j option used in regex */ | #define PCRE_MODE 0x0002 /* compiled in 16 bit mode */ | |||
#define PCRE_HASCRORLF 0x0020 /* explicit \r or \n in pattern */ | #endif | |||
#define PCRE_FIRSTSET 0x0010 /* first_char is set */ | ||||
#define PCRE_FCH_CASELESS 0x0020 /* caseless first char */ | ||||
#define PCRE_REQCHSET 0x0040 /* req_byte is set */ | ||||
#define PCRE_RCH_CASELESS 0x0080 /* caseless requested char */ | ||||
#define PCRE_STARTLINE 0x0100 /* start after \n for multiline */ | ||||
#define PCRE_NOPARTIAL 0x0200 /* can't use partial with this regex */ | ||||
#define PCRE_JCHANGED 0x0400 /* j option used in regex */ | ||||
#define PCRE_HASCRORLF 0x0800 /* explicit \r or \n in pattern */ | ||||
#define PCRE_HASTHEN 0x1000 /* pattern contains (*THEN) */ | ||||
/* Options for the "extra" block produced by pcre_study(). */ | /* Flags for the "extra" block produced by pcre_study(). */ | |||
#define PCRE_STUDY_MAPPED 0x01 /* a map of starting chars exists */ | #define PCRE_STUDY_MAPPED 0x0001 /* a map of starting chars exists */ | |||
#define PCRE_STUDY_MINLEN 0x0002 /* a minimum length field exists */ | ||||
/* Masks for identifying the public options that are permitted at compile | /* Masks for identifying the public options that are permitted at compile | |||
time, run time, or study time, respectively. */ | time, run time, or study time, respectively. */ | |||
#define PCRE_NEWLINE_BITS (PCRE_NEWLINE_CR|PCRE_NEWLINE_LF|PCRE_NEWLINE_ANY | \ | #define PCRE_NEWLINE_BITS (PCRE_NEWLINE_CR|PCRE_NEWLINE_LF|PCRE_NEWLINE_ANY | \ | |||
PCRE_NEWLINE_ANYCRLF) | PCRE_NEWLINE_ANYCRLF) | |||
#define PUBLIC_OPTIONS \ | #define PUBLIC_COMPILE_OPTIONS \ | |||
(PCRE_CASELESS|PCRE_EXTENDED|PCRE_ANCHORED|PCRE_MULTILINE| \ | (PCRE_CASELESS|PCRE_EXTENDED|PCRE_ANCHORED|PCRE_MULTILINE| \ | |||
PCRE_DOTALL|PCRE_DOLLAR_ENDONLY|PCRE_EXTRA|PCRE_UNGREEDY|PCRE_UTF8| \ | PCRE_DOTALL|PCRE_DOLLAR_ENDONLY|PCRE_EXTRA|PCRE_UNGREEDY|PCRE_UTF8| \ | |||
PCRE_NO_AUTO_CAPTURE|PCRE_NO_UTF8_CHECK|PCRE_AUTO_CALLOUT|PCRE_FIRSTLINE | \ | PCRE_NO_AUTO_CAPTURE|PCRE_NO_UTF8_CHECK|PCRE_AUTO_CALLOUT|PCRE_FIRSTLINE | \ | |||
PCRE_DUPNAMES|PCRE_NEWLINE_BITS|PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE) | PCRE_DUPNAMES|PCRE_NEWLINE_BITS|PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE| \ | |||
PCRE_JAVASCRIPT_COMPAT|PCRE_UCP|PCRE_NO_START_OPTIMIZE) | ||||
#define PUBLIC_EXEC_OPTIONS \ | #define PUBLIC_EXEC_OPTIONS \ | |||
(PCRE_ANCHORED|PCRE_NOTBOL|PCRE_NOTEOL|PCRE_NOTEMPTY|PCRE_NO_UTF8_CHECK| | (PCRE_ANCHORED|PCRE_NOTBOL|PCRE_NOTEOL|PCRE_NOTEMPTY|PCRE_NOTEMPTY_ATSTAR | |||
\ | T| \ | |||
PCRE_PARTIAL|PCRE_NEWLINE_BITS|PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE) | PCRE_NO_UTF8_CHECK|PCRE_PARTIAL_HARD|PCRE_PARTIAL_SOFT|PCRE_NEWLINE_BITS | |||
| \ | ||||
PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE|PCRE_NO_START_OPTIMIZE) | ||||
#define PUBLIC_DFA_EXEC_OPTIONS \ | #define PUBLIC_DFA_EXEC_OPTIONS \ | |||
(PCRE_ANCHORED|PCRE_NOTBOL|PCRE_NOTEOL|PCRE_NOTEMPTY|PCRE_NO_UTF8_CHECK| | (PCRE_ANCHORED|PCRE_NOTBOL|PCRE_NOTEOL|PCRE_NOTEMPTY|PCRE_NOTEMPTY_ATSTAR | |||
\ | T| \ | |||
PCRE_PARTIAL|PCRE_DFA_SHORTEST|PCRE_DFA_RESTART|PCRE_NEWLINE_BITS| \ | PCRE_NO_UTF8_CHECK|PCRE_PARTIAL_HARD|PCRE_PARTIAL_SOFT|PCRE_DFA_SHORTEST | |||
PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE) | | \ | |||
PCRE_DFA_RESTART|PCRE_NEWLINE_BITS|PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE| \ | ||||
PCRE_NO_START_OPTIMIZE) | ||||
#define PUBLIC_STUDY_OPTIONS 0 /* None defined */ | #define PUBLIC_STUDY_OPTIONS \ | |||
PCRE_STUDY_JIT_COMPILE | ||||
/* Magic number to provide a small check against being handed junk. Also us | /* Magic number to provide a small check against being handed junk. */ | |||
ed | ||||
to detect whether a pattern was compiled on a host of different endianness. | ||||
*/ | ||||
#define MAGIC_NUMBER 0x50435245UL /* 'PCRE' */ | #define MAGIC_NUMBER 0x50435245UL /* 'PCRE' */ | |||
/* This variable is used to detect a loaded regular expression | ||||
in different endianness. */ | ||||
#define REVERSED_MAGIC_NUMBER 0x45524350UL /* 'ERCP' */ | ||||
/* Negative values for the firstchar and reqchar variables */ | /* Negative values for the firstchar and reqchar variables */ | |||
#define REQ_UNSET (-2) | #define REQ_UNSET (-2) | |||
#define REQ_NONE (-1) | #define REQ_NONE (-1) | |||
/* The maximum remaining length of subject we are prepared to search for a | /* The maximum remaining length of subject we are prepared to search for a | |||
req_byte match. */ | req_byte match. */ | |||
#define REQ_BYTE_MAX 1000 | #define REQ_BYTE_MAX 1000 | |||
/* Flags added to firstbyte or reqbyte; a "non-literal" item is either a | /* Miscellaneous definitions. The #ifndef is to pacify compiler warnings in | |||
variable-length repeat, or a anything other than literal characters. */ | environments where these macros are defined elsewhere. Unfortunately, there | |||
is no way to do the same for the typedef. */ | ||||
#define REQ_CASELESS 0x0100 /* indicates caselessness */ | ||||
#define REQ_VARY 0x0200 /* reqbyte followed non-literal item */ | ||||
/* Miscellaneous definitions */ | ||||
typedef int BOOL; | typedef int BOOL; | |||
#ifndef FALSE | ||||
#define FALSE 0 | #define FALSE 0 | |||
#define TRUE 1 | #define TRUE 1 | |||
#endif | ||||
/* If PCRE is to support UTF-8 on EBCDIC platforms, we cannot use normal | ||||
character constants like '*' because the compiler would emit their EBCDIC c | ||||
ode, | ||||
which is different from their ASCII/UTF-8 code. Instead we define macros fo | ||||
r | ||||
the characters so that they always use the ASCII/UTF-8 code when UTF-8 supp | ||||
ort | ||||
is enabled. When UTF-8 support is not enabled, the definitions use characte | ||||
r | ||||
literals. Both character and string versions of each character are needed, | ||||
and | ||||
there are some longer strings as well. | ||||
This means that, on EBCDIC platforms, the PCRE library can handle either | ||||
EBCDIC, or UTF-8, but not both. To support both in the same compiled librar | ||||
y | ||||
would need different lookups depending on whether PCRE_UTF8 was set or not. | ||||
This would make it impossible to use characters in switch/case statements, | ||||
which would reduce performance. For a theoretical use (which nobody has ask | ||||
ed | ||||
for) in a minority area (EBCDIC platforms), this is not sensible. Any | ||||
application that did need both could compile two versions of the library, u | ||||
sing | ||||
macros to give the functions distinct names. */ | ||||
#ifndef SUPPORT_UTF | ||||
/* UTF-8 support is not enabled; use the platform-dependent character liter | ||||
als | ||||
so that PCRE works on both ASCII and EBCDIC platforms, in non-UTF-mode only | ||||
. */ | ||||
#define CHAR_HT '\t' | ||||
#define CHAR_VT '\v' | ||||
#define CHAR_FF '\f' | ||||
#define CHAR_CR '\r' | ||||
#define CHAR_NL '\n' | ||||
#define CHAR_BS '\b' | ||||
#define CHAR_BEL '\a' | ||||
#ifdef EBCDIC | ||||
#define CHAR_ESC '\047' | ||||
#define CHAR_DEL '\007' | ||||
#else | ||||
#define CHAR_ESC '\033' | ||||
#define CHAR_DEL '\177' | ||||
#endif | ||||
#define CHAR_SPACE ' ' | ||||
#define CHAR_EXCLAMATION_MARK '!' | ||||
#define CHAR_QUOTATION_MARK '"' | ||||
#define CHAR_NUMBER_SIGN '#' | ||||
#define CHAR_DOLLAR_SIGN '$' | ||||
#define CHAR_PERCENT_SIGN '%' | ||||
#define CHAR_AMPERSAND '&' | ||||
#define CHAR_APOSTROPHE '\'' | ||||
#define CHAR_LEFT_PARENTHESIS '(' | ||||
#define CHAR_RIGHT_PARENTHESIS ')' | ||||
#define CHAR_ASTERISK '*' | ||||
#define CHAR_PLUS '+' | ||||
#define CHAR_COMMA ',' | ||||
#define CHAR_MINUS '-' | ||||
#define CHAR_DOT '.' | ||||
#define CHAR_SLASH '/' | ||||
#define CHAR_0 '0' | ||||
#define CHAR_1 '1' | ||||
#define CHAR_2 '2' | ||||
#define CHAR_3 '3' | ||||
#define CHAR_4 '4' | ||||
#define CHAR_5 '5' | ||||
#define CHAR_6 '6' | ||||
#define CHAR_7 '7' | ||||
#define CHAR_8 '8' | ||||
#define CHAR_9 '9' | ||||
#define CHAR_COLON ':' | ||||
#define CHAR_SEMICOLON ';' | ||||
#define CHAR_LESS_THAN_SIGN '<' | ||||
#define CHAR_EQUALS_SIGN '=' | ||||
#define CHAR_GREATER_THAN_SIGN '>' | ||||
#define CHAR_QUESTION_MARK '?' | ||||
#define CHAR_COMMERCIAL_AT '@' | ||||
#define CHAR_A 'A' | ||||
#define CHAR_B 'B' | ||||
#define CHAR_C 'C' | ||||
#define CHAR_D 'D' | ||||
#define CHAR_E 'E' | ||||
#define CHAR_F 'F' | ||||
#define CHAR_G 'G' | ||||
#define CHAR_H 'H' | ||||
#define CHAR_I 'I' | ||||
#define CHAR_J 'J' | ||||
#define CHAR_K 'K' | ||||
#define CHAR_L 'L' | ||||
#define CHAR_M 'M' | ||||
#define CHAR_N 'N' | ||||
#define CHAR_O 'O' | ||||
#define CHAR_P 'P' | ||||
#define CHAR_Q 'Q' | ||||
#define CHAR_R 'R' | ||||
#define CHAR_S 'S' | ||||
#define CHAR_T 'T' | ||||
#define CHAR_U 'U' | ||||
#define CHAR_V 'V' | ||||
#define CHAR_W 'W' | ||||
#define CHAR_X 'X' | ||||
#define CHAR_Y 'Y' | ||||
#define CHAR_Z 'Z' | ||||
#define CHAR_LEFT_SQUARE_BRACKET '[' | ||||
#define CHAR_BACKSLASH '\\' | ||||
#define CHAR_RIGHT_SQUARE_BRACKET ']' | ||||
#define CHAR_CIRCUMFLEX_ACCENT '^' | ||||
#define CHAR_UNDERSCORE '_' | ||||
#define CHAR_GRAVE_ACCENT '`' | ||||
#define CHAR_a 'a' | ||||
#define CHAR_b 'b' | ||||
#define CHAR_c 'c' | ||||
#define CHAR_d 'd' | ||||
#define CHAR_e 'e' | ||||
#define CHAR_f 'f' | ||||
#define CHAR_g 'g' | ||||
#define CHAR_h 'h' | ||||
#define CHAR_i 'i' | ||||
#define CHAR_j 'j' | ||||
#define CHAR_k 'k' | ||||
#define CHAR_l 'l' | ||||
#define CHAR_m 'm' | ||||
#define CHAR_n 'n' | ||||
#define CHAR_o 'o' | ||||
#define CHAR_p 'p' | ||||
#define CHAR_q 'q' | ||||
#define CHAR_r 'r' | ||||
#define CHAR_s 's' | ||||
#define CHAR_t 't' | ||||
#define CHAR_u 'u' | ||||
#define CHAR_v 'v' | ||||
#define CHAR_w 'w' | ||||
#define CHAR_x 'x' | ||||
#define CHAR_y 'y' | ||||
#define CHAR_z 'z' | ||||
#define CHAR_LEFT_CURLY_BRACKET '{' | ||||
#define CHAR_VERTICAL_LINE '|' | ||||
#define CHAR_RIGHT_CURLY_BRACKET '}' | ||||
#define CHAR_TILDE '~' | ||||
#define STR_HT "\t" | ||||
#define STR_VT "\v" | ||||
#define STR_FF "\f" | ||||
#define STR_CR "\r" | ||||
#define STR_NL "\n" | ||||
#define STR_BS "\b" | ||||
#define STR_BEL "\a" | ||||
#ifdef EBCDIC | ||||
#define STR_ESC "\047" | ||||
#define STR_DEL "\007" | ||||
#else | ||||
#define STR_ESC "\033" | ||||
#define STR_DEL "\177" | ||||
#endif | ||||
#define STR_SPACE " " | ||||
#define STR_EXCLAMATION_MARK "!" | ||||
#define STR_QUOTATION_MARK "\"" | ||||
#define STR_NUMBER_SIGN "#" | ||||
#define STR_DOLLAR_SIGN "$" | ||||
#define STR_PERCENT_SIGN "%" | ||||
#define STR_AMPERSAND "&" | ||||
#define STR_APOSTROPHE "'" | ||||
#define STR_LEFT_PARENTHESIS "(" | ||||
#define STR_RIGHT_PARENTHESIS ")" | ||||
#define STR_ASTERISK "*" | ||||
#define STR_PLUS "+" | ||||
#define STR_COMMA "," | ||||
#define STR_MINUS "-" | ||||
#define STR_DOT "." | ||||
#define STR_SLASH "/" | ||||
#define STR_0 "0" | ||||
#define STR_1 "1" | ||||
#define STR_2 "2" | ||||
#define STR_3 "3" | ||||
#define STR_4 "4" | ||||
#define STR_5 "5" | ||||
#define STR_6 "6" | ||||
#define STR_7 "7" | ||||
#define STR_8 "8" | ||||
#define STR_9 "9" | ||||
#define STR_COLON ":" | ||||
#define STR_SEMICOLON ";" | ||||
#define STR_LESS_THAN_SIGN "<" | ||||
#define STR_EQUALS_SIGN "=" | ||||
#define STR_GREATER_THAN_SIGN ">" | ||||
#define STR_QUESTION_MARK "?" | ||||
#define STR_COMMERCIAL_AT "@" | ||||
#define STR_A "A" | ||||
#define STR_B "B" | ||||
#define STR_C "C" | ||||
#define STR_D "D" | ||||
#define STR_E "E" | ||||
#define STR_F "F" | ||||
#define STR_G "G" | ||||
#define STR_H "H" | ||||
#define STR_I "I" | ||||
#define STR_J "J" | ||||
#define STR_K "K" | ||||
#define STR_L "L" | ||||
#define STR_M "M" | ||||
#define STR_N "N" | ||||
#define STR_O "O" | ||||
#define STR_P "P" | ||||
#define STR_Q "Q" | ||||
#define STR_R "R" | ||||
#define STR_S "S" | ||||
#define STR_T "T" | ||||
#define STR_U "U" | ||||
#define STR_V "V" | ||||
#define STR_W "W" | ||||
#define STR_X "X" | ||||
#define STR_Y "Y" | ||||
#define STR_Z "Z" | ||||
#define STR_LEFT_SQUARE_BRACKET "[" | ||||
#define STR_BACKSLASH "\\" | ||||
#define STR_RIGHT_SQUARE_BRACKET "]" | ||||
#define STR_CIRCUMFLEX_ACCENT "^" | ||||
#define STR_UNDERSCORE "_" | ||||
#define STR_GRAVE_ACCENT "`" | ||||
#define STR_a "a" | ||||
#define STR_b "b" | ||||
#define STR_c "c" | ||||
#define STR_d "d" | ||||
#define STR_e "e" | ||||
#define STR_f "f" | ||||
#define STR_g "g" | ||||
#define STR_h "h" | ||||
#define STR_i "i" | ||||
#define STR_j "j" | ||||
#define STR_k "k" | ||||
#define STR_l "l" | ||||
#define STR_m "m" | ||||
#define STR_n "n" | ||||
#define STR_o "o" | ||||
#define STR_p "p" | ||||
#define STR_q "q" | ||||
#define STR_r "r" | ||||
#define STR_s "s" | ||||
#define STR_t "t" | ||||
#define STR_u "u" | ||||
#define STR_v "v" | ||||
#define STR_w "w" | ||||
#define STR_x "x" | ||||
#define STR_y "y" | ||||
#define STR_z "z" | ||||
#define STR_LEFT_CURLY_BRACKET "{" | ||||
#define STR_VERTICAL_LINE "|" | ||||
#define STR_RIGHT_CURLY_BRACKET "}" | ||||
#define STR_TILDE "~" | ||||
#define STRING_ACCEPT0 "ACCEPT\0" | ||||
#define STRING_COMMIT0 "COMMIT\0" | ||||
#define STRING_F0 "F\0" | ||||
#define STRING_FAIL0 "FAIL\0" | ||||
#define STRING_MARK0 "MARK\0" | ||||
#define STRING_PRUNE0 "PRUNE\0" | ||||
#define STRING_SKIP0 "SKIP\0" | ||||
#define STRING_THEN "THEN" | ||||
#define STRING_alpha0 "alpha\0" | ||||
#define STRING_lower0 "lower\0" | ||||
#define STRING_upper0 "upper\0" | ||||
#define STRING_alnum0 "alnum\0" | ||||
#define STRING_ascii0 "ascii\0" | ||||
#define STRING_blank0 "blank\0" | ||||
#define STRING_cntrl0 "cntrl\0" | ||||
#define STRING_digit0 "digit\0" | ||||
#define STRING_graph0 "graph\0" | ||||
#define STRING_print0 "print\0" | ||||
#define STRING_punct0 "punct\0" | ||||
#define STRING_space0 "space\0" | ||||
#define STRING_word0 "word\0" | ||||
#define STRING_xdigit "xdigit" | ||||
#define STRING_DEFINE "DEFINE" | ||||
#define STRING_CR_RIGHTPAR "CR)" | ||||
#define STRING_LF_RIGHTPAR "LF)" | ||||
#define STRING_CRLF_RIGHTPAR "CRLF)" | ||||
#define STRING_ANY_RIGHTPAR "ANY)" | ||||
#define STRING_ANYCRLF_RIGHTPAR "ANYCRLF)" | ||||
#define STRING_BSR_ANYCRLF_RIGHTPAR "BSR_ANYCRLF)" | ||||
#define STRING_BSR_UNICODE_RIGHTPAR "BSR_UNICODE)" | ||||
#ifdef COMPILE_PCRE8 | ||||
#define STRING_UTF_RIGHTPAR "UTF8)" | ||||
#endif | ||||
#ifdef COMPILE_PCRE16 | ||||
#define STRING_UTF_RIGHTPAR "UTF16)" | ||||
#endif | ||||
#define STRING_UCP_RIGHTPAR "UCP)" | ||||
#define STRING_NO_START_OPT_RIGHTPAR "NO_START_OPT)" | ||||
#else /* SUPPORT_UTF */ | ||||
/* UTF-8 support is enabled; always use UTF-8 (=ASCII) character codes. Thi | ||||
s | ||||
works in both modes non-EBCDIC platforms, and on EBCDIC platforms in UTF-8 | ||||
mode | ||||
only. */ | ||||
#define CHAR_HT '\011' | ||||
#define CHAR_VT '\013' | ||||
#define CHAR_FF '\014' | ||||
#define CHAR_CR '\015' | ||||
#define CHAR_NL '\012' | ||||
#define CHAR_BS '\010' | ||||
#define CHAR_BEL '\007' | ||||
#define CHAR_ESC '\033' | ||||
#define CHAR_DEL '\177' | ||||
#define CHAR_SPACE '\040' | ||||
#define CHAR_EXCLAMATION_MARK '\041' | ||||
#define CHAR_QUOTATION_MARK '\042' | ||||
#define CHAR_NUMBER_SIGN '\043' | ||||
#define CHAR_DOLLAR_SIGN '\044' | ||||
#define CHAR_PERCENT_SIGN '\045' | ||||
#define CHAR_AMPERSAND '\046' | ||||
#define CHAR_APOSTROPHE '\047' | ||||
#define CHAR_LEFT_PARENTHESIS '\050' | ||||
#define CHAR_RIGHT_PARENTHESIS '\051' | ||||
#define CHAR_ASTERISK '\052' | ||||
#define CHAR_PLUS '\053' | ||||
#define CHAR_COMMA '\054' | ||||
#define CHAR_MINUS '\055' | ||||
#define CHAR_DOT '\056' | ||||
#define CHAR_SLASH '\057' | ||||
#define CHAR_0 '\060' | ||||
#define CHAR_1 '\061' | ||||
#define CHAR_2 '\062' | ||||
#define CHAR_3 '\063' | ||||
#define CHAR_4 '\064' | ||||
#define CHAR_5 '\065' | ||||
#define CHAR_6 '\066' | ||||
#define CHAR_7 '\067' | ||||
#define CHAR_8 '\070' | ||||
#define CHAR_9 '\071' | ||||
#define CHAR_COLON '\072' | ||||
#define CHAR_SEMICOLON '\073' | ||||
#define CHAR_LESS_THAN_SIGN '\074' | ||||
#define CHAR_EQUALS_SIGN '\075' | ||||
#define CHAR_GREATER_THAN_SIGN '\076' | ||||
#define CHAR_QUESTION_MARK '\077' | ||||
#define CHAR_COMMERCIAL_AT '\100' | ||||
#define CHAR_A '\101' | ||||
#define CHAR_B '\102' | ||||
#define CHAR_C '\103' | ||||
#define CHAR_D '\104' | ||||
#define CHAR_E '\105' | ||||
#define CHAR_F '\106' | ||||
#define CHAR_G '\107' | ||||
#define CHAR_H '\110' | ||||
#define CHAR_I '\111' | ||||
#define CHAR_J '\112' | ||||
#define CHAR_K '\113' | ||||
#define CHAR_L '\114' | ||||
#define CHAR_M '\115' | ||||
#define CHAR_N '\116' | ||||
#define CHAR_O '\117' | ||||
#define CHAR_P '\120' | ||||
#define CHAR_Q '\121' | ||||
#define CHAR_R '\122' | ||||
#define CHAR_S '\123' | ||||
#define CHAR_T '\124' | ||||
#define CHAR_U '\125' | ||||
#define CHAR_V '\126' | ||||
#define CHAR_W '\127' | ||||
#define CHAR_X '\130' | ||||
#define CHAR_Y '\131' | ||||
#define CHAR_Z '\132' | ||||
#define CHAR_LEFT_SQUARE_BRACKET '\133' | ||||
#define CHAR_BACKSLASH '\134' | ||||
#define CHAR_RIGHT_SQUARE_BRACKET '\135' | ||||
#define CHAR_CIRCUMFLEX_ACCENT '\136' | ||||
#define CHAR_UNDERSCORE '\137' | ||||
#define CHAR_GRAVE_ACCENT '\140' | ||||
#define CHAR_a '\141' | ||||
#define CHAR_b '\142' | ||||
#define CHAR_c '\143' | ||||
#define CHAR_d '\144' | ||||
#define CHAR_e '\145' | ||||
#define CHAR_f '\146' | ||||
#define CHAR_g '\147' | ||||
#define CHAR_h '\150' | ||||
#define CHAR_i '\151' | ||||
#define CHAR_j '\152' | ||||
#define CHAR_k '\153' | ||||
#define CHAR_l '\154' | ||||
#define CHAR_m '\155' | ||||
#define CHAR_n '\156' | ||||
#define CHAR_o '\157' | ||||
#define CHAR_p '\160' | ||||
#define CHAR_q '\161' | ||||
#define CHAR_r '\162' | ||||
#define CHAR_s '\163' | ||||
#define CHAR_t '\164' | ||||
#define CHAR_u '\165' | ||||
#define CHAR_v '\166' | ||||
#define CHAR_w '\167' | ||||
#define CHAR_x '\170' | ||||
#define CHAR_y '\171' | ||||
#define CHAR_z '\172' | ||||
#define CHAR_LEFT_CURLY_BRACKET '\173' | ||||
#define CHAR_VERTICAL_LINE '\174' | ||||
#define CHAR_RIGHT_CURLY_BRACKET '\175' | ||||
#define CHAR_TILDE '\176' | ||||
#define STR_HT "\011" | ||||
#define STR_VT "\013" | ||||
#define STR_FF "\014" | ||||
#define STR_CR "\015" | ||||
#define STR_NL "\012" | ||||
#define STR_BS "\010" | ||||
#define STR_BEL "\007" | ||||
#define STR_ESC "\033" | ||||
#define STR_DEL "\177" | ||||
#define STR_SPACE "\040" | ||||
#define STR_EXCLAMATION_MARK "\041" | ||||
#define STR_QUOTATION_MARK "\042" | ||||
#define STR_NUMBER_SIGN "\043" | ||||
#define STR_DOLLAR_SIGN "\044" | ||||
#define STR_PERCENT_SIGN "\045" | ||||
#define STR_AMPERSAND "\046" | ||||
#define STR_APOSTROPHE "\047" | ||||
#define STR_LEFT_PARENTHESIS "\050" | ||||
#define STR_RIGHT_PARENTHESIS "\051" | ||||
#define STR_ASTERISK "\052" | ||||
#define STR_PLUS "\053" | ||||
#define STR_COMMA "\054" | ||||
#define STR_MINUS "\055" | ||||
#define STR_DOT "\056" | ||||
#define STR_SLASH "\057" | ||||
#define STR_0 "\060" | ||||
#define STR_1 "\061" | ||||
#define STR_2 "\062" | ||||
#define STR_3 "\063" | ||||
#define STR_4 "\064" | ||||
#define STR_5 "\065" | ||||
#define STR_6 "\066" | ||||
#define STR_7 "\067" | ||||
#define STR_8 "\070" | ||||
#define STR_9 "\071" | ||||
#define STR_COLON "\072" | ||||
#define STR_SEMICOLON "\073" | ||||
#define STR_LESS_THAN_SIGN "\074" | ||||
#define STR_EQUALS_SIGN "\075" | ||||
#define STR_GREATER_THAN_SIGN "\076" | ||||
#define STR_QUESTION_MARK "\077" | ||||
#define STR_COMMERCIAL_AT "\100" | ||||
#define STR_A "\101" | ||||
#define STR_B "\102" | ||||
#define STR_C "\103" | ||||
#define STR_D "\104" | ||||
#define STR_E "\105" | ||||
#define STR_F "\106" | ||||
#define STR_G "\107" | ||||
#define STR_H "\110" | ||||
#define STR_I "\111" | ||||
#define STR_J "\112" | ||||
#define STR_K "\113" | ||||
#define STR_L "\114" | ||||
#define STR_M "\115" | ||||
#define STR_N "\116" | ||||
#define STR_O "\117" | ||||
#define STR_P "\120" | ||||
#define STR_Q "\121" | ||||
#define STR_R "\122" | ||||
#define STR_S "\123" | ||||
#define STR_T "\124" | ||||
#define STR_U "\125" | ||||
#define STR_V "\126" | ||||
#define STR_W "\127" | ||||
#define STR_X "\130" | ||||
#define STR_Y "\131" | ||||
#define STR_Z "\132" | ||||
#define STR_LEFT_SQUARE_BRACKET "\133" | ||||
#define STR_BACKSLASH "\134" | ||||
#define STR_RIGHT_SQUARE_BRACKET "\135" | ||||
#define STR_CIRCUMFLEX_ACCENT "\136" | ||||
#define STR_UNDERSCORE "\137" | ||||
#define STR_GRAVE_ACCENT "\140" | ||||
#define STR_a "\141" | ||||
#define STR_b "\142" | ||||
#define STR_c "\143" | ||||
#define STR_d "\144" | ||||
#define STR_e "\145" | ||||
#define STR_f "\146" | ||||
#define STR_g "\147" | ||||
#define STR_h "\150" | ||||
#define STR_i "\151" | ||||
#define STR_j "\152" | ||||
#define STR_k "\153" | ||||
#define STR_l "\154" | ||||
#define STR_m "\155" | ||||
#define STR_n "\156" | ||||
#define STR_o "\157" | ||||
#define STR_p "\160" | ||||
#define STR_q "\161" | ||||
#define STR_r "\162" | ||||
#define STR_s "\163" | ||||
#define STR_t "\164" | ||||
#define STR_u "\165" | ||||
#define STR_v "\166" | ||||
#define STR_w "\167" | ||||
#define STR_x "\170" | ||||
#define STR_y "\171" | ||||
#define STR_z "\172" | ||||
#define STR_LEFT_CURLY_BRACKET "\173" | ||||
#define STR_VERTICAL_LINE "\174" | ||||
#define STR_RIGHT_CURLY_BRACKET "\175" | ||||
#define STR_TILDE "\176" | ||||
#define STRING_ACCEPT0 STR_A STR_C STR_C STR_E STR_P STR_T "\0 | ||||
" | ||||
#define STRING_COMMIT0 STR_C STR_O STR_M STR_M STR_I STR_T "\0 | ||||
" | ||||
#define STRING_F0 STR_F "\0" | ||||
#define STRING_FAIL0 STR_F STR_A STR_I STR_L "\0" | ||||
#define STRING_MARK0 STR_M STR_A STR_R STR_K "\0" | ||||
#define STRING_PRUNE0 STR_P STR_R STR_U STR_N STR_E "\0" | ||||
#define STRING_SKIP0 STR_S STR_K STR_I STR_P "\0" | ||||
#define STRING_THEN STR_T STR_H STR_E STR_N | ||||
#define STRING_alpha0 STR_a STR_l STR_p STR_h STR_a "\0" | ||||
#define STRING_lower0 STR_l STR_o STR_w STR_e STR_r "\0" | ||||
#define STRING_upper0 STR_u STR_p STR_p STR_e STR_r "\0" | ||||
#define STRING_alnum0 STR_a STR_l STR_n STR_u STR_m "\0" | ||||
#define STRING_ascii0 STR_a STR_s STR_c STR_i STR_i "\0" | ||||
#define STRING_blank0 STR_b STR_l STR_a STR_n STR_k "\0" | ||||
#define STRING_cntrl0 STR_c STR_n STR_t STR_r STR_l "\0" | ||||
#define STRING_digit0 STR_d STR_i STR_g STR_i STR_t "\0" | ||||
#define STRING_graph0 STR_g STR_r STR_a STR_p STR_h "\0" | ||||
#define STRING_print0 STR_p STR_r STR_i STR_n STR_t "\0" | ||||
#define STRING_punct0 STR_p STR_u STR_n STR_c STR_t "\0" | ||||
#define STRING_space0 STR_s STR_p STR_a STR_c STR_e "\0" | ||||
#define STRING_word0 STR_w STR_o STR_r STR_d "\0" | ||||
#define STRING_xdigit STR_x STR_d STR_i STR_g STR_i STR_t | ||||
#define STRING_DEFINE STR_D STR_E STR_F STR_I STR_N STR_E | ||||
#define STRING_CR_RIGHTPAR STR_C STR_R STR_RIGHT_PARENTHESIS | ||||
#define STRING_LF_RIGHTPAR STR_L STR_F STR_RIGHT_PARENTHESIS | ||||
#define STRING_CRLF_RIGHTPAR STR_C STR_R STR_L STR_F STR_RIGHT_PA | ||||
RENTHESIS | ||||
#define STRING_ANY_RIGHTPAR STR_A STR_N STR_Y STR_RIGHT_PARENTHE | ||||
SIS | ||||
#define STRING_ANYCRLF_RIGHTPAR STR_A STR_N STR_Y STR_C STR_R STR_L | ||||
STR_F STR_RIGHT_PARENTHESIS | ||||
#define STRING_BSR_ANYCRLF_RIGHTPAR STR_B STR_S STR_R STR_UNDERSCORE STR | ||||
_A STR_N STR_Y STR_C STR_R STR_L STR_F STR_RIGHT_PARENTHESIS | ||||
#define STRING_BSR_UNICODE_RIGHTPAR STR_B STR_S STR_R STR_UNDERSCORE STR | ||||
_U STR_N STR_I STR_C STR_O STR_D STR_E STR_RIGHT_PARENTHESIS | ||||
#ifdef COMPILE_PCRE8 | ||||
#define STRING_UTF_RIGHTPAR STR_U STR_T STR_F STR_8 STR_RIGHT_PA | ||||
RENTHESIS | ||||
#endif | ||||
#ifdef COMPILE_PCRE16 | ||||
#define STRING_UTF_RIGHTPAR STR_U STR_T STR_F STR_1 STR_6 STR_RI | ||||
GHT_PARENTHESIS | ||||
#endif | ||||
#define STRING_UCP_RIGHTPAR STR_U STR_C STR_P STR_RIGHT_PARENTHE | ||||
SIS | ||||
#define STRING_NO_START_OPT_RIGHTPAR STR_N STR_O STR_UNDERSCORE STR_S STR | ||||
_T STR_A STR_R STR_T STR_UNDERSCORE STR_O STR_P STR_T STR_RIGHT_PARENTHESIS | ||||
#endif /* SUPPORT_UTF */ | ||||
/* Escape items that are just an encoding of a particular data value. */ | /* Escape items that are just an encoding of a particular data value. */ | |||
#ifndef ESC_e | #ifndef ESC_e | |||
#define ESC_e 27 | #define ESC_e CHAR_ESC | |||
#endif | #endif | |||
#ifndef ESC_f | #ifndef ESC_f | |||
#define ESC_f '\f' | #define ESC_f CHAR_FF | |||
#endif | #endif | |||
#ifndef ESC_n | #ifndef ESC_n | |||
#define ESC_n '\n' | #define ESC_n CHAR_NL | |||
#endif | #endif | |||
#ifndef ESC_r | #ifndef ESC_r | |||
#define ESC_r '\r' | #define ESC_r CHAR_CR | |||
#endif | #endif | |||
/* We can't officially use ESC_t because it is a POSIX reserved identifier | /* We can't officially use ESC_t because it is a POSIX reserved identifier | |||
(presumably because of all the others like size_t). */ | (presumably because of all the others like size_t). */ | |||
#ifndef ESC_tee | #ifndef ESC_tee | |||
#define ESC_tee '\t' | #define ESC_tee CHAR_HT | |||
#endif | #endif | |||
/* Codes for different types of Unicode property */ | /* Codes for different types of Unicode property */ | |||
#define PT_ANY 0 /* Any property - matches all chars */ | #define PT_ANY 0 /* Any property - matches all chars */ | |||
#define PT_LAMP 1 /* L& - the union of Lu, Ll, Lt */ | #define PT_LAMP 1 /* L& - the union of Lu, Ll, Lt */ | |||
#define PT_GC 2 /* General characteristic (e.g. L) */ | #define PT_GC 2 /* Specified general characteristic (e.g. L) */ | |||
#define PT_PC 3 /* Particular characteristic (e.g. Lu) */ | #define PT_PC 3 /* Specified particular characteristic (e.g. Lu) | |||
*/ | ||||
#define PT_SC 4 /* Script (e.g. Han) */ | #define PT_SC 4 /* Script (e.g. Han) */ | |||
#define PT_ALNUM 5 /* Alphanumeric - the union of L and N */ | ||||
#define PT_SPACE 6 /* Perl space - Z plus 9,10,12,13 */ | ||||
#define PT_PXSPACE 7 /* POSIX space - Z plus 9,10,11,12,13 */ | ||||
#define PT_WORD 8 /* Word - L plus N plus underscore */ | ||||
/* Flag bits and data types for the extended class (OP_XCLASS) for classes that | /* Flag bits and data types for the extended class (OP_XCLASS) for classes that | |||
contain UTF-8 characters with values greater than 255. */ | contain characters with values greater than 255. */ | |||
#define XCL_NOT 0x01 /* Flag: this is a negative class */ | #define XCL_NOT 0x01 /* Flag: this is a negative class */ | |||
#define XCL_MAP 0x02 /* Flag: a 32-byte map is present */ | #define XCL_MAP 0x02 /* Flag: a 32-byte map is present */ | |||
#define XCL_END 0 /* Marks end of individual items */ | #define XCL_END 0 /* Marks end of individual items */ | |||
#define XCL_SINGLE 1 /* Single item (one multibyte char) follows */ | #define XCL_SINGLE 1 /* Single item (one multibyte char) follows */ | |||
#define XCL_RANGE 2 /* A range (two multibyte chars) follows */ | #define XCL_RANGE 2 /* A range (two multibyte chars) follows */ | |||
#define XCL_PROP 3 /* Unicode property (2-byte property code follow s) */ | #define XCL_PROP 3 /* Unicode property (2-byte property code follow s) */ | |||
#define XCL_NOTPROP 4 /* Unicode inverted property (ditto) */ | #define XCL_NOTPROP 4 /* Unicode inverted property (ditto) */ | |||
/* These are escaped items that aren't just an encoding of a particular dat a | /* These are escaped items that aren't just an encoding of a particular dat a | |||
value such as \n. They must have non-zero values, as check_escape() returns | value such as \n. They must have non-zero values, as check_escape() returns | |||
their negation. Also, they must appear in the same order as in the opcode | their negation. Also, they must appear in the same order as in the opcode | |||
definitions below, up to ESC_z. There's a dummy for OP_ANY because it | definitions below, up to ESC_z. There's a dummy for OP_ALLANY because it | |||
corresponds to "." rather than an escape sequence. The final one must be | corresponds to "." in DOTALL mode rather than an escape sequence. It is als | |||
ESC_REF as subsequent values are used for backreferences (\1, \2, \3, etc). | o | |||
There are two tests in the code for an escape greater than ESC_b and less t | used for [^] in JavaScript compatibility mode, and for \C in non-utf mode. | |||
han | In | |||
ESC_Z to detect the types that may be repeated. These are the types that | non-DOTALL mode, "." behaves like \N. | |||
consume characters. If any new escapes are put in between that don't consum | ||||
e a | The special values ESC_DU, ESC_du, etc. are used instead of ESC_D, ESC_d, e | |||
character, that code will have to change. */ | tc. | |||
when PCRE_UCP is set, when replacement of \d etc by \p sequences is require | ||||
d. | ||||
They must be contiguous, and remain in order so that the replacements can b | ||||
e | ||||
looked up from a table. | ||||
The final escape must be ESC_REF as subsequent values are used for | ||||
backreferences (\1, \2, \3, etc). There are two tests in the code for an es | ||||
cape | ||||
greater than ESC_b and less than ESC_Z to detect the types that may be | ||||
repeated. These are the types that consume characters. If any new escapes a | ||||
re | ||||
put in between that don't consume a character, that code will have to chang | ||||
e. | ||||
*/ | ||||
enum { ESC_A = 1, ESC_G, ESC_K, ESC_B, ESC_b, ESC_D, ESC_d, ESC_S, ESC_s, | enum { ESC_A = 1, ESC_G, ESC_K, ESC_B, ESC_b, ESC_D, ESC_d, ESC_S, ESC_s, | |||
ESC_W, ESC_w, ESC_dum1, ESC_C, ESC_P, ESC_p, ESC_R, ESC_H, ESC_h, | ESC_W, ESC_w, ESC_N, ESC_dum, ESC_C, ESC_P, ESC_p, ESC_R, ESC_H, | |||
ESC_V, ESC_v, ESC_X, ESC_Z, ESC_z, ESC_E, ESC_Q, ESC_k, ESC_REF }; | ESC_h, ESC_V, ESC_v, ESC_X, ESC_Z, ESC_z, | |||
ESC_E, ESC_Q, ESC_g, ESC_k, | ||||
ESC_DU, ESC_du, ESC_SU, ESC_su, ESC_WU, ESC_wu, | ||||
ESC_REF }; | ||||
/* Opcode table: Starting from 1 (i.e. after OP_END), the values up to | /* Opcode table: Starting from 1 (i.e. after OP_END), the values up to | |||
OP_EOD must correspond in order to the list of escapes immediately above. | OP_EOD must correspond in order to the list of escapes immediately above. | |||
*** NOTE NOTE NOTE *** Whenever this list is updated, the two macro definit ions | *** NOTE NOTE NOTE *** Whenever this list is updated, the two macro definit ions | |||
that follow must also be updated to match. There is also a table called | that follow must also be updated to match. There are also tables called | |||
"coptable" in pcre_dfa_exec.c that must be updated. */ | "coptable" and "poptable" in pcre_dfa_exec.c that must be updated. */ | |||
enum { | enum { | |||
OP_END, /* 0 End of pattern */ | OP_END, /* 0 End of pattern */ | |||
/* Values corresponding to backslashed metacharacters */ | /* Values corresponding to backslashed metacharacters */ | |||
OP_SOD, /* 1 Start of data: \A */ | OP_SOD, /* 1 Start of data: \A */ | |||
OP_SOM, /* 2 Start of match (subject + offset): \G */ | OP_SOM, /* 2 Start of match (subject + offset): \G */ | |||
OP_SET_SOM, /* 3 Set start of match (\K) */ | OP_SET_SOM, /* 3 Set start of match (\K) */ | |||
OP_NOT_WORD_BOUNDARY, /* 4 \B */ | OP_NOT_WORD_BOUNDARY, /* 4 \B */ | |||
OP_WORD_BOUNDARY, /* 5 \b */ | OP_WORD_BOUNDARY, /* 5 \b */ | |||
OP_NOT_DIGIT, /* 6 \D */ | OP_NOT_DIGIT, /* 6 \D */ | |||
OP_DIGIT, /* 7 \d */ | OP_DIGIT, /* 7 \d */ | |||
OP_NOT_WHITESPACE, /* 8 \S */ | OP_NOT_WHITESPACE, /* 8 \S */ | |||
OP_WHITESPACE, /* 9 \s */ | OP_WHITESPACE, /* 9 \s */ | |||
OP_NOT_WORDCHAR, /* 10 \W */ | OP_NOT_WORDCHAR, /* 10 \W */ | |||
OP_WORDCHAR, /* 11 \w */ | OP_WORDCHAR, /* 11 \w */ | |||
OP_ANY, /* 12 Match any character */ | ||||
OP_ANYBYTE, /* 13 Match any byte (\C); different to OP_ANY for UTF | ||||
-8 */ | ||||
OP_NOTPROP, /* 14 \P (not Unicode property) */ | ||||
OP_PROP, /* 15 \p (Unicode property) */ | ||||
OP_ANYNL, /* 16 \R (any newline sequence) */ | ||||
OP_NOT_HSPACE, /* 17 \H (not horizontal whitespace) */ | ||||
OP_HSPACE, /* 18 \h (horizontal whitespace) */ | ||||
OP_NOT_VSPACE, /* 19 \V (not vertical whitespace) */ | ||||
OP_VSPACE, /* 20 \v (vertical whitespace) */ | ||||
OP_EXTUNI, /* 21 \X (extended Unicode sequence */ | ||||
OP_EODN, /* 22 End of data or \n at end of data: \Z. */ | ||||
OP_EOD, /* 23 End of data: \z */ | ||||
OP_OPT, /* 24 Set runtime options */ | ||||
OP_CIRC, /* 25 Start of line - varies with multiline switch */ | ||||
OP_DOLL, /* 26 End of line - varies with multiline switch */ | ||||
OP_CHAR, /* 27 Match one character, casefully */ | ||||
OP_CHARNC, /* 28 Match one character, caselessly */ | ||||
OP_NOT, /* 29 Match one character, not the following one */ | ||||
OP_STAR, /* 30 The maximizing and minimizing versions of */ | ||||
OP_MINSTAR, /* 31 these six opcodes must come in pairs, with */ | ||||
OP_PLUS, /* 32 the minimizing one second. */ | ||||
OP_MINPLUS, /* 33 This first set applies to single characters.*/ | ||||
OP_QUERY, /* 34 */ | ||||
OP_MINQUERY, /* 35 */ | ||||
OP_UPTO, /* 36 From 0 to n matches */ | ||||
OP_MINUPTO, /* 37 */ | ||||
OP_EXACT, /* 38 Exactly n matches */ | ||||
OP_POSSTAR, /* 39 Possessified star */ | ||||
OP_POSPLUS, /* 40 Possessified plus */ | ||||
OP_POSQUERY, /* 41 Posesssified query */ | ||||
OP_POSUPTO, /* 42 Possessified upto */ | ||||
OP_NOTSTAR, /* 43 The maximizing and minimizing versions of */ | ||||
OP_NOTMINSTAR, /* 44 these six opcodes must come in pairs, with */ | ||||
OP_NOTPLUS, /* 45 the minimizing one second. They must be in */ | ||||
OP_NOTMINPLUS, /* 46 exactly the same order as those above. */ | ||||
OP_NOTQUERY, /* 47 This set applies to "not" single characters. */ | ||||
OP_NOTMINQUERY, /* 48 */ | ||||
OP_NOTUPTO, /* 49 From 0 to n matches */ | ||||
OP_NOTMINUPTO, /* 50 */ | ||||
OP_NOTEXACT, /* 51 Exactly n matches */ | ||||
OP_NOTPOSSTAR, /* 52 Possessified versions */ | ||||
OP_NOTPOSPLUS, /* 53 */ | ||||
OP_NOTPOSQUERY, /* 54 */ | ||||
OP_NOTPOSUPTO, /* 55 */ | ||||
OP_TYPESTAR, /* 56 The maximizing and minimizing versions of */ | ||||
OP_TYPEMINSTAR, /* 57 these six opcodes must come in pairs, with */ | ||||
OP_TYPEPLUS, /* 58 the minimizing one second. These codes must */ | ||||
OP_TYPEMINPLUS, /* 59 be in exactly the same order as those above. */ | ||||
OP_TYPEQUERY, /* 60 This set applies to character types such as \d * | ||||
/ | ||||
OP_TYPEMINQUERY, /* 61 */ | ||||
OP_TYPEUPTO, /* 62 From 0 to n matches */ | ||||
OP_TYPEMINUPTO, /* 63 */ | ||||
OP_TYPEEXACT, /* 64 Exactly n matches */ | ||||
OP_TYPEPOSSTAR, /* 65 Possessified versions */ | ||||
OP_TYPEPOSPLUS, /* 66 */ | ||||
OP_TYPEPOSQUERY, /* 67 */ | ||||
OP_TYPEPOSUPTO, /* 68 */ | ||||
OP_CRSTAR, /* 69 The maximizing and minimizing versions of */ | ||||
OP_CRMINSTAR, /* 70 all these opcodes must come in pairs, with */ | ||||
OP_CRPLUS, /* 71 the minimizing one second. These codes must */ | ||||
OP_CRMINPLUS, /* 72 be in exactly the same order as those above. */ | ||||
OP_CRQUERY, /* 73 These are for character classes and back refs */ | ||||
OP_CRMINQUERY, /* 74 */ | ||||
OP_CRRANGE, /* 75 These are different to the three sets above. */ | ||||
OP_CRMINRANGE, /* 76 */ | ||||
OP_CLASS, /* 77 Match a character class, chars < 256 only */ | ||||
OP_NCLASS, /* 78 Same, but the bitmap was created from a negative | ||||
class - the difference is relevant only when a U | ||||
TF-8 | ||||
character > 255 is encountered. */ | ||||
OP_XCLASS, /* 79 Extended class for handling UTF-8 chars within t | ||||
he | ||||
class. This does both positive and negative. */ | ||||
OP_REF, /* 80 Match a back reference */ | ||||
OP_RECURSE, /* 81 Match a numbered subpattern (possibly recursive) | ||||
*/ | ||||
OP_CALLOUT, /* 82 Call out to external function if provided */ | ||||
OP_ALT, /* 83 Start of alternation */ | ||||
OP_KET, /* 84 End of group that doesn't have an unbounded repe | ||||
at */ | ||||
OP_KETRMAX, /* 85 These two must remain together and in this */ | ||||
OP_KETRMIN, /* 86 order. They are for groups the repeat for ever. | ||||
*/ | ||||
/* The assertions must come before BRA, CBRA, ONCE, and COND.*/ | ||||
OP_ASSERT, /* 87 Positive lookahead */ | ||||
OP_ASSERT_NOT, /* 88 Negative lookahead */ | ||||
OP_ASSERTBACK, /* 89 Positive lookbehind */ | ||||
OP_ASSERTBACK_NOT, /* 90 Negative lookbehind */ | ||||
OP_REVERSE, /* 91 Move pointer back - used in lookbehind assertion | ||||
s */ | ||||
/* ONCE, BRA, CBRA, and COND must come after the assertions, with ONCE fi | ||||
rst, | ||||
as there's a test for >= ONCE for a subpattern that isn't an assertion. * | ||||
/ | ||||
OP_ONCE, /* 92 Atomic group */ | ||||
OP_BRA, /* 93 Start of non-capturing bracket */ | ||||
OP_CBRA, /* 94 Start of capturing bracket */ | ||||
OP_COND, /* 95 Conditional group */ | ||||
/* These three must follow the previous three, in the same order. There's | OP_ANY, /* 12 Match any character except newline */ | |||
a | OP_ALLANY, /* 13 Match any character */ | |||
check for >= SBRA to distinguish the two sets. */ | OP_ANYBYTE, /* 14 Match any byte (\C); different to OP_ANY for UTF | |||
-8 */ | ||||
OP_NOTPROP, /* 15 \P (not Unicode property) */ | ||||
OP_PROP, /* 16 \p (Unicode property) */ | ||||
OP_ANYNL, /* 17 \R (any newline sequence) */ | ||||
OP_NOT_HSPACE, /* 18 \H (not horizontal whitespace) */ | ||||
OP_HSPACE, /* 19 \h (horizontal whitespace) */ | ||||
OP_NOT_VSPACE, /* 20 \V (not vertical whitespace) */ | ||||
OP_VSPACE, /* 21 \v (vertical whitespace) */ | ||||
OP_EXTUNI, /* 22 \X (extended Unicode sequence */ | ||||
OP_EODN, /* 23 End of data or \n at end of data: \Z. */ | ||||
OP_EOD, /* 24 End of data: \z */ | ||||
OP_CIRC, /* 25 Start of line - not multiline */ | ||||
OP_CIRCM, /* 26 Start of line - multiline */ | ||||
OP_DOLL, /* 27 End of line - not multiline */ | ||||
OP_DOLLM, /* 28 End of line - multiline */ | ||||
OP_CHAR, /* 29 Match one character, casefully */ | ||||
OP_CHARI, /* 30 Match one character, caselessly */ | ||||
OP_NOT, /* 31 Match one character, not the given one, casefull | ||||
y */ | ||||
OP_NOTI, /* 32 Match one character, not the given one, caseless | ||||
ly */ | ||||
/* The following sets of 13 opcodes must always be kept in step because | ||||
the offset from the first one is used to generate the others. */ | ||||
/**** Single characters, caseful, must precede the caseless ones ****/ | ||||
OP_STAR, /* 33 The maximizing and minimizing versions of */ | ||||
OP_MINSTAR, /* 34 these six opcodes must come in pairs, with */ | ||||
OP_PLUS, /* 35 the minimizing one second. */ | ||||
OP_MINPLUS, /* 36 */ | ||||
OP_QUERY, /* 37 */ | ||||
OP_MINQUERY, /* 38 */ | ||||
OP_UPTO, /* 39 From 0 to n matches of one character, caseful*/ | ||||
OP_MINUPTO, /* 40 */ | ||||
OP_EXACT, /* 41 Exactly n matches */ | ||||
OP_POSSTAR, /* 42 Possessified star, caseful */ | ||||
OP_POSPLUS, /* 43 Possessified plus, caseful */ | ||||
OP_POSQUERY, /* 44 Posesssified query, caseful */ | ||||
OP_POSUPTO, /* 45 Possessified upto, caseful */ | ||||
/**** Single characters, caseless, must follow the caseful ones */ | ||||
OP_STARI, /* 46 */ | ||||
OP_MINSTARI, /* 47 */ | ||||
OP_PLUSI, /* 48 */ | ||||
OP_MINPLUSI, /* 49 */ | ||||
OP_QUERYI, /* 50 */ | ||||
OP_MINQUERYI, /* 51 */ | ||||
OP_UPTOI, /* 52 From 0 to n matches of one character, caseless * | ||||
/ | ||||
OP_MINUPTOI, /* 53 */ | ||||
OP_EXACTI, /* 54 */ | ||||
OP_POSSTARI, /* 55 Possessified star, caseless */ | ||||
OP_POSPLUSI, /* 56 Possessified plus, caseless */ | ||||
OP_POSQUERYI, /* 57 Posesssified query, caseless */ | ||||
OP_POSUPTOI, /* 58 Possessified upto, caseless */ | ||||
/**** The negated ones must follow the non-negated ones, and match them * | ||||
***/ | ||||
/**** Negated single character, caseful; must precede the caseless ones * | ||||
***/ | ||||
OP_NOTSTAR, /* 59 The maximizing and minimizing versions of */ | ||||
OP_NOTMINSTAR, /* 60 these six opcodes must come in pairs, with */ | ||||
OP_NOTPLUS, /* 61 the minimizing one second. They must be in */ | ||||
OP_NOTMINPLUS, /* 62 exactly the same order as those above. */ | ||||
OP_NOTQUERY, /* 63 */ | ||||
OP_NOTMINQUERY, /* 64 */ | ||||
OP_NOTUPTO, /* 65 From 0 to n matches, caseful */ | ||||
OP_NOTMINUPTO, /* 66 */ | ||||
OP_NOTEXACT, /* 67 Exactly n matches */ | ||||
OP_NOTPOSSTAR, /* 68 Possessified versions, caseful */ | ||||
OP_NOTPOSPLUS, /* 69 */ | ||||
OP_NOTPOSQUERY, /* 70 */ | ||||
OP_NOTPOSUPTO, /* 71 */ | ||||
/**** Negated single character, caseless; must follow the caseful ones ** | ||||
**/ | ||||
OP_NOTSTARI, /* 72 */ | ||||
OP_NOTMINSTARI, /* 73 */ | ||||
OP_NOTPLUSI, /* 74 */ | ||||
OP_NOTMINPLUSI, /* 75 */ | ||||
OP_NOTQUERYI, /* 76 */ | ||||
OP_NOTMINQUERYI, /* 77 */ | ||||
OP_NOTUPTOI, /* 78 From 0 to n matches, caseless */ | ||||
OP_NOTMINUPTOI, /* 79 */ | ||||
OP_NOTEXACTI, /* 80 Exactly n matches */ | ||||
OP_NOTPOSSTARI, /* 81 Possessified versions, caseless */ | ||||
OP_NOTPOSPLUSI, /* 82 */ | ||||
OP_NOTPOSQUERYI, /* 83 */ | ||||
OP_NOTPOSUPTOI, /* 84 */ | ||||
/**** Character types ****/ | ||||
OP_TYPESTAR, /* 85 The maximizing and minimizing versions of */ | ||||
OP_TYPEMINSTAR, /* 86 these six opcodes must come in pairs, with */ | ||||
OP_TYPEPLUS, /* 87 the minimizing one second. These codes must */ | ||||
OP_TYPEMINPLUS, /* 88 be in exactly the same order as those above. */ | ||||
OP_TYPEQUERY, /* 89 */ | ||||
OP_TYPEMINQUERY, /* 90 */ | ||||
OP_TYPEUPTO, /* 91 From 0 to n matches */ | ||||
OP_TYPEMINUPTO, /* 92 */ | ||||
OP_TYPEEXACT, /* 93 Exactly n matches */ | ||||
OP_TYPEPOSSTAR, /* 94 Possessified versions */ | ||||
OP_TYPEPOSPLUS, /* 95 */ | ||||
OP_TYPEPOSQUERY, /* 96 */ | ||||
OP_TYPEPOSUPTO, /* 97 */ | ||||
/* These are used for character classes and back references; only the | ||||
first six are the same as the sets above. */ | ||||
OP_CRSTAR, /* 98 The maximizing and minimizing versions of */ | ||||
OP_CRMINSTAR, /* 99 all these opcodes must come in pairs, with */ | ||||
OP_CRPLUS, /* 100 the minimizing one second. These codes must */ | ||||
OP_CRMINPLUS, /* 101 be in exactly the same order as those above. */ | ||||
OP_CRQUERY, /* 102 */ | ||||
OP_CRMINQUERY, /* 103 */ | ||||
OP_CRRANGE, /* 104 These are different to the three sets above. */ | ||||
OP_CRMINRANGE, /* 105 */ | ||||
/* End of quantifier opcodes */ | ||||
OP_CLASS, /* 106 Match a character class, chars < 256 only */ | ||||
OP_NCLASS, /* 107 Same, but the bitmap was created from a negativ | ||||
e | ||||
class - the difference is relevant only when | ||||
a | ||||
character > 255 is encountered. */ | ||||
OP_XCLASS, /* 108 Extended class for handling > 255 chars within | ||||
the | ||||
class. This does both positive and negative. | ||||
*/ | ||||
OP_REF, /* 109 Match a back reference, casefully */ | ||||
OP_REFI, /* 110 Match a back reference, caselessly */ | ||||
OP_RECURSE, /* 111 Match a numbered subpattern (possibly recursive | ||||
) */ | ||||
OP_CALLOUT, /* 112 Call out to external function if provided */ | ||||
OP_ALT, /* 113 Start of alternation */ | ||||
OP_KET, /* 114 End of group that doesn't have an unbounded rep | ||||
eat */ | ||||
OP_KETRMAX, /* 115 These two must remain together and in this */ | ||||
OP_KETRMIN, /* 116 order. They are for groups the repeat for ever. | ||||
*/ | ||||
OP_KETRPOS, /* 117 Possessive unlimited repeat. */ | ||||
/* The assertions must come before BRA, CBRA, ONCE, and COND, and the fou | ||||
r | ||||
asserts must remain in order. */ | ||||
OP_REVERSE, /* 118 Move pointer back - used in lookbehind assertio | ||||
ns */ | ||||
OP_ASSERT, /* 119 Positive lookahead */ | ||||
OP_ASSERT_NOT, /* 120 Negative lookahead */ | ||||
OP_ASSERTBACK, /* 121 Positive lookbehind */ | ||||
OP_ASSERTBACK_NOT, /* 122 Negative lookbehind */ | ||||
/* ONCE, ONCE_NC, BRA, BRAPOS, CBRA, CBRAPOS, and COND must come immediat | ||||
ely | ||||
after the assertions, with ONCE first, as there's a test for >= ONCE for | ||||
a | ||||
subpattern that isn't an assertion. The POS versions must immediately fol | ||||
low | ||||
the non-POS versions in each case. */ | ||||
OP_ONCE, /* 123 Atomic group, contains captures */ | ||||
OP_ONCE_NC, /* 124 Atomic group containing no captures */ | ||||
OP_BRA, /* 125 Start of non-capturing bracket */ | ||||
OP_BRAPOS, /* 126 Ditto, with unlimited, possessive repeat */ | ||||
OP_CBRA, /* 127 Start of capturing bracket */ | ||||
OP_CBRAPOS, /* 128 Ditto, with unlimited, possessive repeat */ | ||||
OP_COND, /* 129 Conditional group */ | ||||
OP_SBRA, /* 96 Start of non-capturing bracket, check empty */ | /* These five must follow the previous five, in the same order. There's a | |||
OP_SCBRA, /* 97 Start of capturing bracket, check empty */ | check for >= SBRA to distinguish the two sets. */ | |||
OP_SCOND, /* 98 Conditional group, check empty */ | ||||
OP_CREF, /* 99 Used to hold a capture number as condition */ | ||||
OP_RREF, /* 100 Used to hold a recursion number as condition */ | ||||
OP_DEF, /* 101 The DEFINE condition */ | ||||
OP_BRAZERO, /* 102 These two must remain together and in this */ | OP_SBRA, /* 130 Start of non-capturing bracket, check empty */ | |||
OP_BRAMINZERO, /* 103 order. */ | OP_SBRAPOS, /* 131 Ditto, with unlimited, possessive repeat */ | |||
OP_SCBRA, /* 132 Start of capturing bracket, check empty */ | ||||
OP_SCBRAPOS, /* 133 Ditto, with unlimited, possessive repeat */ | ||||
OP_SCOND, /* 134 Conditional group, check empty */ | ||||
/* The next two pairs must (respectively) be kept together. */ | ||||
OP_CREF, /* 135 Used to hold a capture number as condition */ | ||||
OP_NCREF, /* 136 Same, but generated by a name reference*/ | ||||
OP_RREF, /* 137 Used to hold a recursion number as condition */ | ||||
OP_NRREF, /* 138 Same, but generated by a name reference*/ | ||||
OP_DEF, /* 139 The DEFINE condition */ | ||||
OP_BRAZERO, /* 140 These two must remain together and in this */ | ||||
OP_BRAMINZERO, /* 141 order. */ | ||||
OP_BRAPOSZERO, /* 142 */ | ||||
/* These are backtracking control verbs */ | /* These are backtracking control verbs */ | |||
OP_PRUNE, /* 104 */ | OP_MARK, /* 143 always has an argument */ | |||
OP_SKIP, /* 105 */ | OP_PRUNE, /* 144 */ | |||
OP_THEN, /* 106 */ | OP_PRUNE_ARG, /* 145 same, but with argument */ | |||
OP_COMMIT, /* 107 */ | OP_SKIP, /* 146 */ | |||
OP_SKIP_ARG, /* 147 same, but with argument */ | ||||
OP_THEN, /* 148 */ | ||||
OP_THEN_ARG, /* 149 same, but with argument */ | ||||
OP_COMMIT, /* 150 */ | ||||
/* These are forced failure and success verbs */ | /* These are forced failure and success verbs */ | |||
OP_FAIL, /* 108 */ | OP_FAIL, /* 151 */ | |||
OP_ACCEPT /* 109 */ | OP_ACCEPT, /* 152 */ | |||
OP_ASSERT_ACCEPT, /* 153 Used inside assertions */ | ||||
OP_CLOSE, /* 154 Used before OP_ACCEPT to close open captures */ | ||||
/* This is used to skip a subpattern with a {0} quantifier */ | ||||
OP_SKIPZERO, /* 155 */ | ||||
/* This is not an opcode, but is used to check that tables indexed by opc | ||||
ode | ||||
are the correct length, in order to catch updating errors - there have be | ||||
en | ||||
some in the past. */ | ||||
OP_TABLE_LENGTH | ||||
}; | }; | |||
/* *** NOTE NOTE NOTE *** Whenever the list above is updated, the two macro | ||||
definitions that follow must also be updated to match. There are also table | ||||
s | ||||
called "coptable" and "poptable" in pcre_dfa_exec.c that must be updated. * | ||||
/ | ||||
/* This macro defines textual names for all the opcodes. These are used onl y | /* This macro defines textual names for all the opcodes. These are used onl y | |||
for debugging. The macro is referenced only in pcre_printint.c. */ | for debugging, and some of them are only partial names. The macro is refere | |||
nced | ||||
only in pcre_printint.c, which fills out the full names in many cases (and | ||||
in | ||||
some cases doesn't actually use these names at all). */ | ||||
#define OP_NAME_LIST \ | #define OP_NAME_LIST \ | |||
"End", "\\A", "\\G", "\\K", "\\B", "\\b", "\\D", "\\d", \ | "End", "\\A", "\\G", "\\K", "\\B", "\\b", "\\D", "\\d", \ | |||
"\\S", "\\s", "\\W", "\\w", "Any", "Anybyte", \ | "\\S", "\\s", "\\W", "\\w", "Any", "AllAny", "Anybyte", \ | |||
"notprop", "prop", "\\R", "\\H", "\\h", "\\V", "\\v", \ | "notprop", "prop", "\\R", "\\H", "\\h", "\\V", "\\v", \ | |||
"extuni", "\\Z", "\\z", \ | "extuni", "\\Z", "\\z", \ | |||
"Opt", "^", "$", "char", "charnc", "not", \ | "^", "^", "$", "$", "char", "chari", "not", "noti", \ | |||
"*", "*?", "+", "+?", "?", "??", "{", "{", "{", \ | "*", "*?", "+", "+?", "?", "??", \ | |||
"{", "{", "{", \ | ||||
"*+","++", "?+", "{", \ | "*+","++", "?+", "{", \ | |||
"*", "*?", "+", "+?", "?", "??", "{", "{", "{", \ | "*", "*?", "+", "+?", "?", "??", \ | |||
"{", "{", "{", \ | ||||
"*+","++", "?+", "{", \ | ||||
"*", "*?", "+", "+?", "?", "??", \ | ||||
"{", "{", "{", \ | ||||
"*+","++", "?+", "{", \ | ||||
"*", "*?", "+", "+?", "?", "??", \ | ||||
"{", "{", "{", \ | ||||
"*+","++", "?+", "{", \ | "*+","++", "?+", "{", \ | |||
"*", "*?", "+", "+?", "?", "??", "{", "{", "{", \ | "*", "*?", "+", "+?", "?", "??", "{", "{", "{", \ | |||
"*+","++", "?+", "{", \ | "*+","++", "?+", "{", \ | |||
"*", "*?", "+", "+?", "?", "??", "{", "{", \ | "*", "*?", "+", "+?", "?", "??", "{", "{", \ | |||
"class", "nclass", "xclass", "Ref", "Recurse", "Callout", \ | "class", "nclass", "xclass", "Ref", "Refi", \ | |||
"Alt", "Ket", "KetRmax", "KetRmin", "Assert", "Assert not", \ | "Recurse", "Callout", \ | |||
"AssertB", "AssertB not", "Reverse", \ | "Alt", "Ket", "KetRmax", "KetRmin", "KetRpos", \ | |||
"Once", "Bra", "CBra", "Cond", "SBra", "SCBra", "SCond", \ | "Reverse", "Assert", "Assert not", "AssertB", "AssertB not", \ | |||
"Cond ref", "Cond rec", "Cond def", "Brazero", "Braminzero", \ | "Once", "Once_NC", \ | |||
"*PRUNE", "*SKIP", "*THEN", "*COMMIT", "*FAIL", "*ACCEPT" | "Bra", "BraPos", "CBra", "CBraPos", \ | |||
"Cond", \ | ||||
"SBra", "SBraPos", "SCBra", "SCBraPos", \ | ||||
"SCond", \ | ||||
"Cond ref", "Cond nref", "Cond rec", "Cond nrec", "Cond def", \ | ||||
"Brazero", "Braminzero", "Braposzero", \ | ||||
"*MARK", "*PRUNE", "*PRUNE", "*SKIP", "*SKIP", \ | ||||
"*THEN", "*THEN", "*COMMIT", "*FAIL", \ | ||||
"*ACCEPT", "*ASSERT_ACCEPT", \ | ||||
"Close", "Skip zero" | ||||
/* This macro defines the length of fixed length operations in the compiled | /* This macro defines the length of fixed length operations in the compiled | |||
regex. The lengths are used when searching for specific things, and also in the | regex. The lengths are used when searching for specific things, and also in the | |||
debugging printing of a compiled regex. We use a macro so that it can be | debugging printing of a compiled regex. We use a macro so that it can be | |||
defined close to the definitions of the opcodes themselves. | defined close to the definitions of the opcodes themselves. | |||
As things have been extended, some of these are no longer fixed lenths, but are | As things have been extended, some of these are no longer fixed lenths, but are | |||
minima instead. For example, the length of a single-character repeat may va ry | minima instead. For example, the length of a single-character repeat may va ry | |||
in UTF-8 mode. The code that uses this table must know about such things. * / | in UTF-8 mode. The code that uses this table must know about such things. * / | |||
#define OP_LENGTHS \ | #define OP_LENGTHS \ | |||
1, /* End */ \ | 1, /* End */ \ | |||
1, 1, 1, 1, 1, /* \A, \G, \K, \B, \b */ \ | 1, 1, 1, 1, 1, /* \A, \G, \K, \B, \b */ \ | |||
1, 1, 1, 1, 1, 1, /* \D, \d, \S, \s, \W, \w */ \ | 1, 1, 1, 1, 1, 1, /* \D, \d, \S, \s, \W, \w */ \ | |||
1, 1, /* Any, Anybyte | 1, 1, 1, /* Any, AllAny, Anybyte | |||
*/ \ | */ \ | |||
3, 3, 1, /* NOTPROP, PROP, EXTUNI | 3, 3, /* \P, \p | |||
*/ \ | */ \ | |||
1, 1, 1, 1, 1, /* \R, \H, \h, \V, \v */ \ | 1, 1, 1, 1, 1, /* \R, \H, \h, \V, \v */ \ | |||
1, 1, 2, 1, 1, /* \Z, \z, Opt, ^, $ | 1, /* \X | |||
*/ \ | */ \ | |||
1, 1, 1, 1, 1, 1, /* \Z, \z, ^, ^M, $, $M | ||||
*/ \ | ||||
2, /* Char - the minimum length */ \ | 2, /* Char - the minimum length */ \ | |||
2, /* Charnc - the minimum length */ \ | 2, /* Chari - the minimum length */ \ | |||
2, /* not */ \ | 2, /* not */ \ | |||
/* Positive single-char repeats ** These are | 2, /* noti | |||
*/ \ | */ \ | |||
2, 2, 2, 2, 2, 2, /* *, *?, +, +?, ?, ?? ** minima in | /* Positive single-char repeats ** These are | |||
*/ \ | */ \ | |||
4, 4, 4, /* upto, minupto, exact ** UTF-8 mode | 2, 2, 2, 2, 2, 2, /* *, *?, +, +?, ?, ?? ** minima in | |||
*/ \ | */ \ | |||
2, 2, 2, 4, /* *+, ++, ?+, upto+ | 2+IMM2_SIZE, 2+IMM2_SIZE, /* upto, minupto ** mode | |||
*/ \ | */ \ | |||
2+IMM2_SIZE, /* exact | ||||
*/ \ | ||||
2, 2, 2, 2+IMM2_SIZE, /* *+, ++, ?+, upto+ | ||||
*/ \ | ||||
2, 2, 2, 2, 2, 2, /* *I, *?I, +I, +?I, ?I, ??I ** UTF-8 | ||||
*/ \ | ||||
2+IMM2_SIZE, 2+IMM2_SIZE, /* upto I, minupto I | ||||
*/ \ | ||||
2+IMM2_SIZE, /* exact I | ||||
*/ \ | ||||
2, 2, 2, 2+IMM2_SIZE, /* *+I, ++I, ?+I, upto+I | ||||
*/ \ | ||||
/* Negative single-char repeats - only for chars < 256 */ \ | /* Negative single-char repeats - only for chars < 256 */ \ | |||
2, 2, 2, 2, 2, 2, /* NOT *, *?, +, +?, ?, ?? */ \ | 2, 2, 2, 2, 2, 2, /* NOT *, *?, +, +?, ?, ?? */ \ | |||
4, 4, 4, /* NOT upto, minupto, exact | 2+IMM2_SIZE, 2+IMM2_SIZE, /* NOT upto, minupto | |||
*/ \ | */ \ | |||
2, 2, 2, 4, /* Possessive *, +, ?, upto | 2+IMM2_SIZE, /* NOT exact | |||
*/ \ | */ \ | |||
2, 2, 2, 2+IMM2_SIZE, /* Possessive NOT *, +, ?, upto | ||||
*/ \ | ||||
2, 2, 2, 2, 2, 2, /* NOT *I, *?I, +I, +?I, ?I, ??I | ||||
*/ \ | ||||
2+IMM2_SIZE, 2+IMM2_SIZE, /* NOT upto I, minupto I | ||||
*/ \ | ||||
2+IMM2_SIZE, /* NOT exact I | ||||
*/ \ | ||||
2, 2, 2, 2+IMM2_SIZE, /* Possessive NOT *I, +I, ?I, upto I | ||||
*/ \ | ||||
/* Positive type repeats */ \ | /* Positive type repeats */ \ | |||
2, 2, 2, 2, 2, 2, /* Type *, *?, +, +?, ?, ?? */ \ | 2, 2, 2, 2, 2, 2, /* Type *, *?, +, +?, ?, ?? */ \ | |||
4, 4, 4, /* Type upto, minupto, exact | 2+IMM2_SIZE, 2+IMM2_SIZE, /* Type upto, minupto | |||
*/ \ | */ \ | |||
2, 2, 2, 4, /* Possessive *+, ++, ?+, upto+ | 2+IMM2_SIZE, /* Type exact | |||
*/ \ | */ \ | |||
2, 2, 2, 2+IMM2_SIZE, /* Possessive *+, ++, ?+, upto+ | ||||
*/ \ | ||||
/* Character class & ref repeats */ \ | /* Character class & ref repeats */ \ | |||
1, 1, 1, 1, 1, 1, /* *, *?, +, +?, ?, ?? */ \ | 1, 1, 1, 1, 1, 1, /* *, *?, +, +?, ?, ?? */ \ | |||
5, 5, /* CRRANGE, CRMINRANGE | 1+2*IMM2_SIZE, 1+2*IMM2_SIZE, /* CRRANGE, CRMINRANGE | |||
*/ \ | */ \ | |||
33, /* CLASS | 1+(32/sizeof(pcre_uchar)), /* CLASS | |||
*/ \ | */ \ | |||
33, /* NCLASS | 1+(32/sizeof(pcre_uchar)), /* NCLASS | |||
*/ \ | */ \ | |||
0, /* XCLASS - variable length */ \ | 0, /* XCLASS - variable length */ \ | |||
3, /* REF | 1+IMM2_SIZE, /* REF | |||
*/ \ | */ \ | |||
1+IMM2_SIZE, /* REFI | ||||
*/ \ | ||||
1+LINK_SIZE, /* RECURSE */ \ | 1+LINK_SIZE, /* RECURSE */ \ | |||
2+2*LINK_SIZE, /* CALLOUT */ \ | 2+2*LINK_SIZE, /* CALLOUT */ \ | |||
1+LINK_SIZE, /* Alt */ \ | 1+LINK_SIZE, /* Alt */ \ | |||
1+LINK_SIZE, /* Ket */ \ | 1+LINK_SIZE, /* Ket */ \ | |||
1+LINK_SIZE, /* KetRmax */ \ | 1+LINK_SIZE, /* KetRmax */ \ | |||
1+LINK_SIZE, /* KetRmin */ \ | 1+LINK_SIZE, /* KetRmin */ \ | |||
1+LINK_SIZE, /* KetRpos | ||||
*/ \ | ||||
1+LINK_SIZE, /* Reverse | ||||
*/ \ | ||||
1+LINK_SIZE, /* Assert */ \ | 1+LINK_SIZE, /* Assert */ \ | |||
1+LINK_SIZE, /* Assert not */ \ | 1+LINK_SIZE, /* Assert not */ \ | |||
1+LINK_SIZE, /* Assert behind */ \ | 1+LINK_SIZE, /* Assert behind */ \ | |||
1+LINK_SIZE, /* Assert behind not */ \ | 1+LINK_SIZE, /* Assert behind not */ \ | |||
1+LINK_SIZE, /* Reverse */ \ | ||||
1+LINK_SIZE, /* ONCE */ \ | 1+LINK_SIZE, /* ONCE */ \ | |||
1+LINK_SIZE, /* ONCE_NC */ \ | ||||
1+LINK_SIZE, /* BRA */ \ | 1+LINK_SIZE, /* BRA */ \ | |||
3+LINK_SIZE, /* CBRA | 1+LINK_SIZE, /* BRAPOS | |||
*/ \ | */ \ | |||
1+LINK_SIZE+IMM2_SIZE, /* CBRA | ||||
*/ \ | ||||
1+LINK_SIZE+IMM2_SIZE, /* CBRAPOS | ||||
*/ \ | ||||
1+LINK_SIZE, /* COND */ \ | 1+LINK_SIZE, /* COND */ \ | |||
1+LINK_SIZE, /* SBRA */ \ | 1+LINK_SIZE, /* SBRA */ \ | |||
3+LINK_SIZE, /* SCBRA | 1+LINK_SIZE, /* SBRAPOS | |||
*/ \ | */ \ | |||
1+LINK_SIZE+IMM2_SIZE, /* SCBRA | ||||
*/ \ | ||||
1+LINK_SIZE+IMM2_SIZE, /* SCBRAPOS | ||||
*/ \ | ||||
1+LINK_SIZE, /* SCOND */ \ | 1+LINK_SIZE, /* SCOND */ \ | |||
3, /* CREF | 1+IMM2_SIZE, 1+IMM2_SIZE, /* CREF, NCREF | |||
*/ \ | */ \ | |||
3, /* RREF | 1+IMM2_SIZE, 1+IMM2_SIZE, /* RREF, NRREF | |||
*/ \ | */ \ | |||
1, /* DEF */ \ | 1, /* DEF */ \ | |||
1, 1, /* BRAZERO, BRAMINZERO | 1, 1, 1, /* BRAZERO, BRAMINZERO, BRAPOSZERO | |||
*/ \ | */ \ | |||
1, 1, 1, 1, /* PRUNE, SKIP, THEN, COMMIT, | 3, 1, 3, /* MARK, PRUNE, PRUNE_ARG | |||
*/ \ | */ \ | |||
1, 1 /* FAIL, ACCEPT | 1, 3, /* SKIP, SKIP_ARG | |||
*/ | */ \ | |||
1, 3, /* THEN, THEN_ARG | ||||
*/ \ | ||||
1, 1, 1, 1, /* COMMIT, FAIL, ACCEPT, ASSERT_ACCEPT | ||||
*/ \ | ||||
1+IMM2_SIZE, 1 /* CLOSE, SKIPZERO | ||||
*/ | ||||
/* A magic value for OP_RREF to indicate the "any recursion" condition. */ | /* A magic value for OP_RREF and OP_NRREF to indicate the "any recursion" | |||
condition. */ | ||||
#define RREF_ANY 0xffff | #define RREF_ANY 0xffff | |||
/* Error code numbers. They are given names so that they can more easily be | /* Compile time error code numbers. They are given names so that they can m | |||
tracked. */ | ore | |||
easily be tracked. When a new number is added, the table called eint in | ||||
pcreposix.c must be updated. */ | ||||
enum { ERR0, ERR1, ERR2, ERR3, ERR4, ERR5, ERR6, ERR7, ERR8, ERR9, | enum { ERR0, ERR1, ERR2, ERR3, ERR4, ERR5, ERR6, ERR7, ERR8, ERR9, | |||
ERR10, ERR11, ERR12, ERR13, ERR14, ERR15, ERR16, ERR17, ERR18, ERR19 , | ERR10, ERR11, ERR12, ERR13, ERR14, ERR15, ERR16, ERR17, ERR18, ERR19 , | |||
ERR20, ERR21, ERR22, ERR23, ERR24, ERR25, ERR26, ERR27, ERR28, ERR29 , | ERR20, ERR21, ERR22, ERR23, ERR24, ERR25, ERR26, ERR27, ERR28, ERR29 , | |||
ERR30, ERR31, ERR32, ERR33, ERR34, ERR35, ERR36, ERR37, ERR38, ERR39 , | ERR30, ERR31, ERR32, ERR33, ERR34, ERR35, ERR36, ERR37, ERR38, ERR39 , | |||
ERR40, ERR41, ERR42, ERR43, ERR44, ERR45, ERR46, ERR47, ERR48, ERR49 , | ERR40, ERR41, ERR42, ERR43, ERR44, ERR45, ERR46, ERR47, ERR48, ERR49 , | |||
ERR50, ERR51, ERR52, ERR53, ERR54, ERR55, ERR56, ERR57, ERR58, ERR59 , | ERR50, ERR51, ERR52, ERR53, ERR54, ERR55, ERR56, ERR57, ERR58, ERR59 , | |||
ERR60, ERR61 }; | ERR60, ERR61, ERR62, ERR63, ERR64, ERR65, ERR66, ERR67, ERR68, ERR69 | |||
, | ||||
ERR70, ERR71, ERR72, ERR73, ERR74, ERRCOUNT }; | ||||
/* The real format of the start of the pcre block; the index of names and t he | /* The real format of the start of the pcre block; the index of names and t he | |||
code vector run on as long as necessary after the end. We store an explicit | code vector run on as long as necessary after the end. We store an explicit | |||
offset to the name table so that if a regex is compiled on one host, saved, and | offset to the name table so that if a regex is compiled on one host, saved, and | |||
then run on another where the size of pointers is different, all might stil l | then run on another where the size of pointers is different, all might stil l | |||
be well. For the case of compiled-on-4 and run-on-8, we include an extra | be well. For the case of compiled-on-4 and run-on-8, we include an extra | |||
pointer that is always NULL. For future-proofing, a few dummy fields were | pointer that is always NULL. For future-proofing, a few dummy fields were | |||
originally included - even though you can never get this planning right - b ut | originally included - even though you can never get this planning right - b ut | |||
there is only one left now. | there is only one left now. | |||
NOTE NOTE NOTE: | NOTE NOTE NOTE: | |||
Because people can now save and re-use compiled patterns, any additions to this | Because people can now save and re-use compiled patterns, any additions to this | |||
structure should be made at the end, and something earlier (e.g. a new | structure should be made at the end, and something earlier (e.g. a new | |||
flag in the options or one of the dummy fields) should indicate that the ne w | flag in the options or one of the dummy fields) should indicate that the ne w | |||
fields are present. Currently PCRE always sets the dummy fields to zero. | fields are present. Currently PCRE always sets the dummy fields to zero. | |||
NOTE NOTE NOTE: | NOTE NOTE NOTE | |||
*/ | */ | |||
typedef struct real_pcre { | #ifdef COMPILE_PCRE8 | |||
#define REAL_PCRE real_pcre | ||||
#else | ||||
#define REAL_PCRE real_pcre16 | ||||
#endif | ||||
typedef struct REAL_PCRE { | ||||
pcre_uint32 magic_number; | pcre_uint32 magic_number; | |||
pcre_uint32 size; /* Total that was malloced */ | pcre_uint32 size; /* Total that was malloced */ | |||
pcre_uint32 options; /* Public options */ | pcre_uint32 options; /* Public options */ | |||
pcre_uint16 flags; /* Private flags */ | pcre_uint16 flags; /* Private flags */ | |||
pcre_uint16 dummy1; /* For future use */ | pcre_uint16 dummy1; /* For future use */ | |||
pcre_uint16 top_bracket; | pcre_uint16 top_bracket; | |||
pcre_uint16 top_backref; | pcre_uint16 top_backref; | |||
pcre_uint16 first_byte; | pcre_uint16 first_char; /* Starting character */ | |||
pcre_uint16 req_byte; | pcre_uint16 req_char; /* This character must be seen */ | |||
pcre_uint16 name_table_offset; /* Offset to name table that follows */ | pcre_uint16 name_table_offset; /* Offset to name table that follows */ | |||
pcre_uint16 name_entry_size; /* Size of any name items */ | pcre_uint16 name_entry_size; /* Size of any name items */ | |||
pcre_uint16 name_count; /* Number of name items */ | pcre_uint16 name_count; /* Number of name items */ | |||
pcre_uint16 ref_count; /* Reference count */ | pcre_uint16 ref_count; /* Reference count */ | |||
const unsigned char *tables; /* Pointer to tables or NULL for std */ | const pcre_uint8 *tables; /* Pointer to tables or NULL for std */ | |||
const unsigned char *nullpad; /* NULL padding */ | const pcre_uint8 *nullpad; /* NULL padding */ | |||
} real_pcre; | } REAL_PCRE; | |||
/* The format of the block used to store data from pcre_study(). The same | /* The format of the block used to store data from pcre_study(). The same | |||
remark (see NOTE above) about extending this structure applies. */ | remark (see NOTE above) about extending this structure applies. */ | |||
typedef struct pcre_study_data { | typedef struct pcre_study_data { | |||
pcre_uint32 size; /* Total that was malloced */ | pcre_uint32 size; /* Total that was malloced */ | |||
pcre_uint32 options; | pcre_uint32 flags; /* Private flags */ | |||
uschar start_bits[32]; | pcre_uint8 start_bits[32]; /* Starting char bits */ | |||
pcre_uint32 minlength; /* Minimum subject length */ | ||||
} pcre_study_data; | } pcre_study_data; | |||
/* Structure for building a chain of open capturing subpatterns during | ||||
compiling, so that instructions to close them can be compiled when (*ACCEPT | ||||
) is | ||||
encountered. This is also used to identify subpatterns that contain recursi | ||||
ve | ||||
back references to themselves, so that they can be made atomic. */ | ||||
typedef struct open_capitem { | ||||
struct open_capitem *next; /* Chain link */ | ||||
pcre_uint16 number; /* Capture number */ | ||||
pcre_uint16 flag; /* Set TRUE if recursive back ref */ | ||||
} open_capitem; | ||||
/* Structure for passing "static" information around between the functions | /* Structure for passing "static" information around between the functions | |||
doing the compiling, so that they are thread-safe. */ | doing the compiling, so that they are thread-safe. */ | |||
typedef struct compile_data { | typedef struct compile_data { | |||
const uschar *lcc; /* Points to lower casing table */ | const pcre_uint8 *lcc; /* Points to lower casing table */ | |||
const uschar *fcc; /* Points to case-flipping table */ | const pcre_uint8 *fcc; /* Points to case-flipping table */ | |||
const uschar *cbits; /* Points to character type table */ | const pcre_uint8 *cbits; /* Points to character type table */ | |||
const uschar *ctypes; /* Points to table of type maps */ | const pcre_uint8 *ctypes; /* Points to table of type maps */ | |||
const uschar *start_workspace;/* The start of working space */ | const pcre_uchar *start_workspace;/* The start of working space */ | |||
const uschar *start_code; /* The start of the compiled code */ | const pcre_uchar *start_code; /* The start of the compiled code */ | |||
const uschar *start_pattern; /* The start of the pattern */ | const pcre_uchar *start_pattern; /* The start of the pattern */ | |||
const uschar *end_pattern; /* The end of the pattern */ | const pcre_uchar *end_pattern; /* The end of the pattern */ | |||
uschar *hwm; /* High watermark of workspace */ | open_capitem *open_caps; /* Chain of open capture items */ | |||
uschar *name_table; /* The name/number table */ | pcre_uchar *hwm; /* High watermark of workspace */ | |||
int names_found; /* Number of entries so far */ | pcre_uchar *name_table; /* The name/number table */ | |||
int name_entry_size; /* Size of each entry */ | int names_found; /* Number of entries so far */ | |||
int bracount; /* Count of capturing parens */ | int name_entry_size; /* Size of each entry */ | |||
int top_backref; /* Maximum back reference */ | int workspace_size; /* Size of workspace */ | |||
unsigned int backref_map; /* Bitmap of low back refs */ | int bracount; /* Count of capturing parens as we comp | |||
int external_options; /* External (initial) options */ | ile */ | |||
int external_flags; /* External flag bits to be set */ | int final_bracount; /* Saved value after first pass */ | |||
int req_varyopt; /* "After variable item" flag for reqbyte * | int top_backref; /* Maximum back reference */ | |||
/ | unsigned int backref_map; /* Bitmap of low back refs */ | |||
BOOL had_accept; /* (*ACCEPT) encountered */ | int assert_depth; /* Depth of nested assertions */ | |||
int nltype; /* Newline type */ | int external_options; /* External (initial) options */ | |||
int nllen; /* Newline string length */ | int external_flags; /* External flag bits to be set */ | |||
uschar nl[4]; /* Newline string when fixed length */ | int req_varyopt; /* "After variable item" flag for reqby | |||
te */ | ||||
BOOL had_accept; /* (*ACCEPT) encountered */ | ||||
BOOL check_lookbehind; /* Lookbehinds need later checking */ | ||||
int nltype; /* Newline type */ | ||||
int nllen; /* Newline string length */ | ||||
pcre_uchar nl[4]; /* Newline string when fixed length */ | ||||
} compile_data; | } compile_data; | |||
/* Structure for maintaining a chain of pointers to the currently incomplet e | /* Structure for maintaining a chain of pointers to the currently incomplet e | |||
branches, for testing for left recursion. */ | branches, for testing for left recursion while compiling. */ | |||
typedef struct branch_chain { | typedef struct branch_chain { | |||
struct branch_chain *outer; | struct branch_chain *outer; | |||
uschar *current; | pcre_uchar *current_branch; | |||
} branch_chain; | } branch_chain; | |||
/* Structure for items in a linked list that represents an explicit recursi ve | /* Structure for items in a linked list that represents an explicit recursi ve | |||
call within the pattern. */ | call within the pattern; used by pcre_exec(). */ | |||
typedef struct recursion_info { | typedef struct recursion_info { | |||
struct recursion_info *prevrec; /* Previous recursion record (or NULL) */ | struct recursion_info *prevrec; /* Previous recursion record (or NULL) */ | |||
int group_num; /* Number of group that was called */ | int group_num; /* Number of group that was called */ | |||
const uschar *after_call; /* "Return value": points after the call in | int *offset_save; /* Pointer to start of saved offsets */ | |||
the expr */ | int saved_max; /* Number of saved offsets */ | |||
USPTR save_start; /* Old value of mstart */ | PCRE_PUCHAR subject_position; /* Position at start of recursion */ | |||
int *offset_save; /* Pointer to start of saved offsets */ | ||||
int saved_max; /* Number of saved offsets */ | ||||
} recursion_info; | } recursion_info; | |||
/* A similar structure for pcre_dfa_exec(). */ | ||||
typedef struct dfa_recursion_info { | ||||
struct dfa_recursion_info *prevrec; | ||||
int group_num; | ||||
PCRE_PUCHAR subject_position; | ||||
} dfa_recursion_info; | ||||
/* Structure for building a chain of data for holding the values of the sub ject | /* Structure for building a chain of data for holding the values of the sub ject | |||
pointer at the start of each subpattern, so as to detect when an empty stri ng | pointer at the start of each subpattern, so as to detect when an empty stri ng | |||
has been matched by a subpattern - to break infinite loops. */ | has been matched by a subpattern - to break infinite loops; used by | |||
pcre_exec(). */ | ||||
typedef struct eptrblock { | typedef struct eptrblock { | |||
struct eptrblock *epb_prev; | struct eptrblock *epb_prev; | |||
USPTR epb_saved_eptr; | PCRE_PUCHAR epb_saved_eptr; | |||
} eptrblock; | } eptrblock; | |||
/* Structure for passing "static" information around between the functions | /* Structure for passing "static" information around between the functions | |||
doing traditional NFA matching, so that they are thread-safe. */ | doing traditional NFA matching, so that they are thread-safe. */ | |||
typedef struct match_data { | typedef struct match_data { | |||
unsigned long int match_call_count; /* As it says */ | unsigned long int match_call_count; /* As it says */ | |||
unsigned long int match_limit; /* As it says */ | unsigned long int match_limit; /* As it says */ | |||
unsigned long int match_limit_recursion; /* As it says */ | unsigned long int match_limit_recursion; /* As it says */ | |||
int *offset_vector; /* Offset vector */ | int *offset_vector; /* Offset vector */ | |||
int offset_end; /* One past the end */ | int offset_end; /* One past the end */ | |||
int offset_max; /* The maximum usable for return data */ | int offset_max; /* The maximum usable for return data */ | |||
int nltype; /* Newline type */ | int nltype; /* Newline type */ | |||
int nllen; /* Newline string length */ | int nllen; /* Newline string length */ | |||
uschar nl[4]; /* Newline string when fixed */ | int name_count; /* Number of names in name table */ | |||
const uschar *lcc; /* Points to lower casing table */ | int name_entry_size; /* Size of entry in names table */ | |||
const uschar *ctypes; /* Points to table of type maps */ | pcre_uchar *name_table; /* Table of names */ | |||
BOOL offset_overflow; /* Set if too many extractions */ | pcre_uchar nl[4]; /* Newline string when fixed */ | |||
BOOL notbol; /* NOTBOL flag */ | const pcre_uint8 *lcc; /* Points to lower casing table */ | |||
BOOL noteol; /* NOTEOL flag */ | const pcre_uint8 *fcc; /* Points to case-flipping table */ | |||
BOOL utf8; /* UTF8 flag */ | const pcre_uint8 *ctypes; /* Points to table of type maps */ | |||
BOOL endonly; /* Dollar not before final \n */ | BOOL offset_overflow; /* Set if too many extractions */ | |||
BOOL notempty; /* Empty string match not wanted */ | BOOL notbol; /* NOTBOL flag */ | |||
BOOL partial; /* PARTIAL flag */ | BOOL noteol; /* NOTEOL flag */ | |||
BOOL hitend; /* Hit the end of the subject at some point | BOOL utf; /* UTF-8 / UTF-16 flag */ | |||
*/ | BOOL jscript_compat; /* JAVASCRIPT_COMPAT flag */ | |||
BOOL bsr_anycrlf; /* \R is just any CRLF, not full Unicode */ | BOOL use_ucp; /* PCRE_UCP flag */ | |||
const uschar *start_code; /* For use when recursing */ | BOOL endonly; /* Dollar not before final \n */ | |||
USPTR start_subject; /* Start of the subject string */ | BOOL notempty; /* Empty string match not wanted */ | |||
USPTR end_subject; /* End of the subject string */ | BOOL notempty_atstart; /* Empty string match at start not wanted | |||
USPTR start_match_ptr; /* Start of matched string */ | */ | |||
USPTR end_match_ptr; /* Subject position at end match */ | BOOL hitend; /* Hit the end of the subject at some poi | |||
int end_offset_top; /* Highwater mark at end of match */ | nt */ | |||
int capture_last; /* Most recent capture number */ | BOOL bsr_anycrlf; /* \R is just any CRLF, not full Unicode | |||
int start_offset; /* The start offset value */ | */ | |||
eptrblock *eptrchain; /* Chain of eptrblocks for tail recursions | BOOL hasthen; /* Pattern contains (*THEN) */ | |||
*/ | BOOL ignore_skip_arg; /* For re-run when SKIP name not found */ | |||
int eptrn; /* Next free eptrblock */ | const pcre_uchar *start_code; /* For use when recursing */ | |||
recursion_info *recursive; /* Linked list of recursion data */ | PCRE_PUCHAR start_subject; /* Start of the subject string */ | |||
void *callout_data; /* To pass back to callouts */ | PCRE_PUCHAR end_subject; /* End of the subject string */ | |||
PCRE_PUCHAR start_match_ptr; /* Start of matched string */ | ||||
PCRE_PUCHAR end_match_ptr; /* Subject position at end match */ | ||||
PCRE_PUCHAR start_used_ptr; /* Earliest consulted character */ | ||||
int partial; /* PARTIAL options */ | ||||
int end_offset_top; /* Highwater mark at end of match */ | ||||
int capture_last; /* Most recent capture number */ | ||||
int start_offset; /* The start offset value */ | ||||
int match_function_type; /* Set for certain special calls of MATCH | ||||
() */ | ||||
eptrblock *eptrchain; /* Chain of eptrblocks for tail recursion | ||||
s */ | ||||
int eptrn; /* Next free eptrblock */ | ||||
recursion_info *recursive; /* Linked list of recursion data */ | ||||
void *callout_data; /* To pass back to callouts */ | ||||
const pcre_uchar *mark; /* Mark pointer to pass back on success * | ||||
/ | ||||
const pcre_uchar *nomatch_mark;/* Mark pointer to pass back on failure * | ||||
/ | ||||
const pcre_uchar *once_target; /* Where to back up to for atomic groups | ||||
*/ | ||||
} match_data; | } match_data; | |||
/* A similar structure is used for the same purpose by the DFA matching | /* A similar structure is used for the same purpose by the DFA matching | |||
functions. */ | functions. */ | |||
typedef struct dfa_match_data { | typedef struct dfa_match_data { | |||
const uschar *start_code; /* Start of the compiled pattern */ | const pcre_uchar *start_code; /* Start of the compiled pattern */ | |||
const uschar *start_subject; /* Start of the subject string */ | const pcre_uchar *start_subject ; /* Start of the subject string */ | |||
const uschar *end_subject; /* End of subject string */ | const pcre_uchar *end_subject; /* End of subject string */ | |||
const uschar *tables; /* Character tables */ | const pcre_uchar *start_used_ptr; /* Earliest consulted character */ | |||
int moptions; /* Match options */ | const pcre_uint8 *tables; /* Character tables */ | |||
int poptions; /* Pattern options */ | int start_offset; /* The start offset value */ | |||
int nltype; /* Newline type */ | int moptions; /* Match options */ | |||
int nllen; /* Newline string length */ | int poptions; /* Pattern options */ | |||
uschar nl[4]; /* Newline string when fixed */ | int nltype; /* Newline type */ | |||
void *callout_data; /* To pass back to callouts */ | int nllen; /* Newline string length */ | |||
pcre_uchar nl[4]; /* Newline string when fixed */ | ||||
void *callout_data; /* To pass back to callouts */ | ||||
dfa_recursion_info *recursive; /* Linked list of recursion data */ | ||||
} dfa_match_data; | } dfa_match_data; | |||
/* Bit definitions for entries in the pcre_ctypes table. */ | /* Bit definitions for entries in the pcre_ctypes table. */ | |||
#define ctype_space 0x01 | #define ctype_space 0x01 | |||
#define ctype_letter 0x02 | #define ctype_letter 0x02 | |||
#define ctype_digit 0x04 | #define ctype_digit 0x04 | |||
#define ctype_xdigit 0x08 | #define ctype_xdigit 0x08 | |||
#define ctype_word 0x10 /* alphameric or '_' */ | #define ctype_word 0x10 /* alphanumeric or '_' */ | |||
#define ctype_meta 0x80 /* regexp meta char or zero (end pattern) */ | #define ctype_meta 0x80 /* regexp meta char or zero (end pattern) */ | |||
/* Offsets for the bitmap tables in pcre_cbits. Each table contains a set | /* Offsets for the bitmap tables in pcre_cbits. Each table contains a set | |||
of bits for a class map. Some classes are built by combining these tables. */ | of bits for a class map. Some classes are built by combining these tables. */ | |||
#define cbit_space 0 /* [:space:] or \s */ | #define cbit_space 0 /* [:space:] or \s */ | |||
#define cbit_xdigit 32 /* [:xdigit:] */ | #define cbit_xdigit 32 /* [:xdigit:] */ | |||
#define cbit_digit 64 /* [:digit:] or \d */ | #define cbit_digit 64 /* [:digit:] or \d */ | |||
#define cbit_upper 96 /* [:upper:] */ | #define cbit_upper 96 /* [:upper:] */ | |||
#define cbit_lower 128 /* [:lower:] */ | #define cbit_lower 128 /* [:lower:] */ | |||
skipping to change at line 1048 | skipping to change at line 2171 | |||
/* Offsets of the various tables from the base tables pointer, and | /* Offsets of the various tables from the base tables pointer, and | |||
total length. */ | total length. */ | |||
#define lcc_offset 0 | #define lcc_offset 0 | |||
#define fcc_offset 256 | #define fcc_offset 256 | |||
#define cbits_offset 512 | #define cbits_offset 512 | |||
#define ctypes_offset (cbits_offset + cbit_length) | #define ctypes_offset (cbits_offset + cbit_length) | |||
#define tables_length (ctypes_offset + 256) | #define tables_length (ctypes_offset + 256) | |||
/* Internal function prefix */ | ||||
#ifdef COMPILE_PCRE8 | ||||
#ifndef PUBL | ||||
#define PUBL(name) pcre_##name | ||||
#endif | ||||
#ifndef PRIV | ||||
#define PRIV(name) _pcre_##name | ||||
#endif | ||||
#else /* COMPILE_PCRE8 */ | ||||
#ifdef COMPILE_PCRE16 | ||||
#ifndef PUBL | ||||
#define PUBL(name) pcre16_##name | ||||
#endif | ||||
#ifndef PRIV | ||||
#define PRIV(name) _pcre16_##name | ||||
#endif | ||||
#else | ||||
#error Unsupported compiling mode | ||||
#endif /* COMPILE_PCRE16 */ | ||||
#endif /* COMPILE_PCRE8 */ | ||||
/* Layout of the UCP type table that translates property names into types a nd | /* Layout of the UCP type table that translates property names into types a nd | |||
codes. Each entry used to point directly to a name, but to reduce the numbe r of | codes. Each entry used to point directly to a name, but to reduce the numbe r of | |||
relocations in shared libraries, it now has an offset into a single string | relocations in shared libraries, it now has an offset into a single string | |||
instead. */ | instead. */ | |||
typedef struct { | typedef struct { | |||
pcre_uint16 name_offset; | pcre_uint16 name_offset; | |||
pcre_uint16 type; | pcre_uint16 type; | |||
pcre_uint16 value; | pcre_uint16 value; | |||
} ucp_type_table; | } ucp_type_table; | |||
/* Internal shared data tables. These are tables that are used by more than one | /* Internal shared data tables. These are tables that are used by more than one | |||
of the exported public functions. They have to be "external" in the C sense , | of the exported public functions. They have to be "external" in the C sense , | |||
but are not part of the PCRE public API. The data for these tables is in th e | but are not part of the PCRE public API. The data for these tables is in th e | |||
pcre_tables.c module. */ | pcre_tables.c module. */ | |||
extern const int _pcre_utf8_table1[]; | #ifdef COMPILE_PCRE8 | |||
extern const int _pcre_utf8_table2[]; | ||||
extern const int _pcre_utf8_table3[]; | extern const int PRIV(utf8_table1)[]; | |||
extern const uschar _pcre_utf8_table4[]; | extern const int PRIV(utf8_table1_size); | |||
extern const int PRIV(utf8_table2)[]; | ||||
extern const int PRIV(utf8_table3)[]; | ||||
extern const pcre_uint8 PRIV(utf8_table4)[]; | ||||
extern const int _pcre_utf8_table1_size; | #endif /* COMPILE_PCRE8 */ | |||
extern const char _pcre_utt_names[]; | extern const char PRIV(utt_names)[]; | |||
extern const ucp_type_table _pcre_utt[]; | extern const ucp_type_table PRIV(utt)[]; | |||
extern const int _pcre_utt_size; | extern const int PRIV(utt_size); | |||
extern const uschar _pcre_default_tables[]; | extern const pcre_uint8 PRIV(default_tables)[]; | |||
extern const uschar _pcre_OP_lengths[]; | extern const pcre_uint8 PRIV(OP_lengths)[]; | |||
/* Internal shared functions. These are functions that are used by more tha n | /* Internal shared functions. These are functions that are used by more tha n | |||
one of the exported public functions. They have to be "external" in the C | one of the exported public functions. They have to be "external" in the C | |||
sense, but are not part of the PCRE public API. */ | sense, but are not part of the PCRE public API. */ | |||
extern BOOL _pcre_is_newline(const uschar *, int, const uschar *, | /* String comparison functions. */ | |||
int *, BOOL); | #ifdef COMPILE_PCRE8 | |||
extern int _pcre_ord2utf8(int, uschar *); | ||||
extern real_pcre *_pcre_try_flipped(const real_pcre *, real_pcre *, | #define STRCMP_UC_UC(str1, str2) \ | |||
const pcre_study_data *, pcre_study_data *); | strcmp((char *)(str1), (char *)(str2)) | |||
extern int _pcre_ucp_findprop(const unsigned int, int *, int *); | #define STRCMP_UC_C8(str1, str2) \ | |||
extern unsigned int _pcre_ucp_othercase(const unsigned int); | strcmp((char *)(str1), (str2)) | |||
extern int _pcre_valid_utf8(const uschar *, int); | #define STRNCMP_UC_UC(str1, str2, num) \ | |||
extern BOOL _pcre_was_newline(const uschar *, int, const uschar *, | strncmp((char *)(str1), (char *)(str2), (num)) | |||
int *, BOOL); | #define STRNCMP_UC_C8(str1, str2, num) \ | |||
extern BOOL _pcre_xclass(int, const uschar *); | strncmp((char *)(str1), (str2), (num)) | |||
#define STRLEN_UC(str) strlen((const char *)str) | ||||
#else | ||||
extern int PRIV(strcmp_uc_uc)(const pcre_uchar *, | ||||
const pcre_uchar *); | ||||
extern int PRIV(strcmp_uc_c8)(const pcre_uchar *, | ||||
const char *); | ||||
extern int PRIV(strncmp_uc_uc)(const pcre_uchar *, | ||||
const pcre_uchar *, unsigned int num); | ||||
extern int PRIV(strncmp_uc_c8)(const pcre_uchar *, | ||||
const char *, unsigned int num); | ||||
extern unsigned int PRIV(strlen_uc)(const pcre_uchar *str); | ||||
#define STRCMP_UC_UC(str1, str2) \ | ||||
PRIV(strcmp_uc_uc)((str1), (str2)) | ||||
#define STRCMP_UC_C8(str1, str2) \ | ||||
PRIV(strcmp_uc_c8)((str1), (str2)) | ||||
#define STRNCMP_UC_UC(str1, str2, num) \ | ||||
PRIV(strncmp_uc_uc)((str1), (str2), (num)) | ||||
#define STRNCMP_UC_C8(str1, str2, num) \ | ||||
PRIV(strncmp_uc_c8)((str1), (str2), (num)) | ||||
#define STRLEN_UC(str) PRIV(strlen_uc)(str) | ||||
#endif /* COMPILE_PCRE8 */ | ||||
extern const pcre_uchar *PRIV(find_bracket)(const pcre_uchar *, BOOL, int); | ||||
extern BOOL PRIV(is_newline)(PCRE_PUCHAR, int, PCRE_PUCHAR, | ||||
int *, BOOL); | ||||
extern int PRIV(ord2utf)(pcre_uint32, pcre_uchar *); | ||||
extern int PRIV(valid_utf)(PCRE_PUCHAR, int, int *); | ||||
extern BOOL PRIV(was_newline)(PCRE_PUCHAR, int, PCRE_PUCHAR, | ||||
int *, BOOL); | ||||
extern BOOL PRIV(xclass)(int, const pcre_uchar *, BOOL); | ||||
#ifdef SUPPORT_JIT | ||||
extern void PRIV(jit_compile)(const REAL_PCRE *, PUBL(extra) * | ||||
); | ||||
extern int PRIV(jit_exec)(const REAL_PCRE *, void *, | ||||
const pcre_uchar *, int, int, int, int, int *, i | ||||
nt); | ||||
extern void PRIV(jit_free)(void *); | ||||
extern int PRIV(jit_get_size)(void *); | ||||
extern const char* PRIV(jit_get_target)(void); | ||||
#endif | ||||
/* Unicode character database (UCD) */ | ||||
typedef struct { | ||||
pcre_uint8 script; | ||||
pcre_uint8 chartype; | ||||
pcre_int32 other_case; | ||||
} ucd_record; | ||||
extern const ucd_record PRIV(ucd_records)[]; | ||||
extern const pcre_uint8 PRIV(ucd_stage1)[]; | ||||
extern const pcre_uint16 PRIV(ucd_stage2)[]; | ||||
extern const int PRIV(ucp_gentype)[]; | ||||
#ifdef SUPPORT_JIT | ||||
extern const int PRIV(ucp_typerange)[]; | ||||
#endif | ||||
#ifdef SUPPORT_UCP | ||||
/* UCD access macros */ | ||||
#define UCD_BLOCK_SIZE 128 | ||||
#define GET_UCD(ch) (PRIV(ucd_records) + \ | ||||
PRIV(ucd_stage2)[PRIV(ucd_stage1)[(ch) / UCD_BLOCK_SIZE] * \ | ||||
UCD_BLOCK_SIZE + (ch) % UCD_BLOCK_SIZE]) | ||||
#define UCD_CHARTYPE(ch) GET_UCD(ch)->chartype | ||||
#define UCD_SCRIPT(ch) GET_UCD(ch)->script | ||||
#define UCD_CATEGORY(ch) PRIV(ucp_gentype)[UCD_CHARTYPE(ch)] | ||||
#define UCD_OTHERCASE(ch) (ch + GET_UCD(ch)->other_case) | ||||
#endif /* SUPPORT_UCP */ | ||||
#endif | #endif | |||
/* End of pcre_internal.h */ | /* End of pcre_internal.h */ | |||
End of changes. 124 change blocks. | ||||
457 lines changed or deleted | 1771 lines changed or added | |||
pcre_scanner.h | pcre_scanner.h | |||
---|---|---|---|---|
skipping to change at line 51 | skipping to change at line 51 | |||
// ...; | // ...; | |||
// } | // } | |||
#ifndef _PCRE_SCANNER_H | #ifndef _PCRE_SCANNER_H | |||
#define _PCRE_SCANNER_H | #define _PCRE_SCANNER_H | |||
#include <assert.h> | #include <assert.h> | |||
#include <string> | #include <string> | |||
#include <vector> | #include <vector> | |||
#include "pcrecpp.h" | #include <pcrecpp.h> | |||
#include "pcre_stringpiece.h" | #include <pcre_stringpiece.h> | |||
namespace pcrecpp { | namespace pcrecpp { | |||
class Scanner { | class PCRECPP_EXP_DEFN Scanner { | |||
public: | public: | |||
Scanner(); | Scanner(); | |||
explicit Scanner(const std::string& input); | explicit Scanner(const std::string& input); | |||
~Scanner(); | ~Scanner(); | |||
// Return current line number. The returned line-number is | // Return current line number. The returned line-number is | |||
// one-based. I.e. it returns 1 + the number of consumed newlines. | // one-based. I.e. it returns 1 + the number of consumed newlines. | |||
// | // | |||
// Note: this method may be slow. It may take time proportional to | // Note: this method may be slow. It may take time proportional to | |||
// the size of the input. | // the size of the input. | |||
skipping to change at line 83 | skipping to change at line 83 | |||
// Return true iff the start of the remaining input matches "re" | // Return true iff the start of the remaining input matches "re" | |||
bool LookingAt(const RE& re) const; | bool LookingAt(const RE& re) const; | |||
// Return true iff all of the following are true | // Return true iff all of the following are true | |||
// a. the start of the remaining input matches "re", | // a. the start of the remaining input matches "re", | |||
// b. if any arguments are supplied, matched sub-patterns can be | // b. if any arguments are supplied, matched sub-patterns can be | |||
// parsed and stored into the arguments. | // parsed and stored into the arguments. | |||
// If it returns true, it skips over the matched input and any | // If it returns true, it skips over the matched input and any | |||
// following input that matches the "skip" regular expression. | // following input that matches the "skip" regular expression. | |||
bool Consume(const RE& re, | bool Consume(const RE& re, | |||
const Arg& arg0 = no_arg, | const Arg& arg0 = RE::no_arg, | |||
const Arg& arg1 = no_arg, | const Arg& arg1 = RE::no_arg, | |||
const Arg& arg2 = no_arg | const Arg& arg2 = RE::no_arg | |||
// TODO: Allow more arguments? | // TODO: Allow more arguments? | |||
); | ); | |||
// Set the "skip" regular expression. If after consuming some data, | // Set the "skip" regular expression. If after consuming some data, | |||
// a prefix of the input matches this RE, it is automatically | // a prefix of the input matches this RE, it is automatically | |||
// skipped. For example, a programming language scanner would use | // skipped. For example, a programming language scanner would use | |||
// a skip RE that matches white space and comments. | // a skip RE that matches white space and comments. | |||
// | // | |||
// scanner.SetSkipExpression("\\s+|//.*|/[*](.|\n)*?[*]/"); | // scanner.SetSkipExpression("\\s+|//.*|/[*](.|\n)*?[*]/"); | |||
// | // | |||
End of changes. 3 change blocks. | ||||
6 lines changed or deleted | 6 lines changed or added | |||
pcre_stringpiece.h | pcre_stringpiece.h | |||
---|---|---|---|---|
skipping to change at line 41 | skipping to change at line 41 | |||
// | // | |||
// A string like object that points into another piece of memory. | // A string like object that points into another piece of memory. | |||
// Useful for providing an interface that allows clients to easily | // Useful for providing an interface that allows clients to easily | |||
// pass in either a "const char*" or a "string". | // pass in either a "const char*" or a "string". | |||
// | // | |||
// Arghh! I wish C++ literals were automatically of type "string". | // Arghh! I wish C++ literals were automatically of type "string". | |||
#ifndef _PCRE_STRINGPIECE_H | #ifndef _PCRE_STRINGPIECE_H | |||
#define _PCRE_STRINGPIECE_H | #define _PCRE_STRINGPIECE_H | |||
#include <string.h> | #include <cstring> | |||
#include <string> | #include <string> | |||
#include <iosfwd> // for ostream forward-declaration | #include <iosfwd> // for ostream forward-declaration | |||
#if 0 | #if 0 | |||
#define HAVE_TYPE_TRAITS | #define HAVE_TYPE_TRAITS | |||
#include <type_traits.h> | #include <type_traits.h> | |||
#elif 0 | #elif 0 | |||
#define HAVE_TYPE_TRAITS | #define HAVE_TYPE_TRAITS | |||
#include <bits/type_traits.h> | #include <bits/type_traits.h> | |||
#endif | #endif | |||
#include <pcre.h> | #include <pcre.h> | |||
using std::memcmp; | ||||
using std::strlen; | ||||
using std::string; | using std::string; | |||
namespace pcrecpp { | namespace pcrecpp { | |||
class PCRECPP_EXP_DEFN StringPiece { | class PCRECPP_EXP_DEFN StringPiece { | |||
private: | private: | |||
const char* ptr_; | const char* ptr_; | |||
int length_; | int length_; | |||
public: | public: | |||
End of changes. 2 change blocks. | ||||
1 lines changed or deleted | 3 lines changed or added | |||
pcrecpp.h | pcrecpp.h | |||
---|---|---|---|---|
skipping to change at line 348 | skipping to change at line 348 | |||
namespace pcrecpp { | namespace pcrecpp { | |||
#define PCRE_SET_OR_CLEAR(b, o) \ | #define PCRE_SET_OR_CLEAR(b, o) \ | |||
if (b) all_options_ |= (o); else all_options_ &= ~(o); \ | if (b) all_options_ |= (o); else all_options_ &= ~(o); \ | |||
return *this | return *this | |||
#define PCRE_IS_SET(o) \ | #define PCRE_IS_SET(o) \ | |||
(all_options_ & o) == o | (all_options_ & o) == o | |||
// We convert user-passed pointers into special Arg objects | ||||
PCRECPP_EXP_DECL Arg no_arg; | ||||
/***** Compiling regular expressions: the RE class *****/ | /***** Compiling regular expressions: the RE class *****/ | |||
// RE_Options allow you to set options to be passed along to pcre, | // RE_Options allow you to set options to be passed along to pcre, | |||
// along with other options we put on top of pcre. | // along with other options we put on top of pcre. | |||
// Only 9 modifiers, plus match_limit and match_limit_recursion, | // Only 9 modifiers, plus match_limit and match_limit_recursion, | |||
// are supported now. | // are supported now. | |||
class RE_Options { | class PCRECPP_EXP_DEFN RE_Options { | |||
public: | public: | |||
// constructor | // constructor | |||
RE_Options() : match_limit_(0), match_limit_recursion_(0), all_options_(0 ) {} | RE_Options() : match_limit_(0), match_limit_recursion_(0), all_options_(0 ) {} | |||
// alternative constructor. | // alternative constructor. | |||
// To facilitate transfer of legacy code from C programs | // To facilitate transfer of legacy code from C programs | |||
// | // | |||
// This lets you do | // This lets you do | |||
// RE(pattern, RE_Options(PCRE_CASELESS|PCRE_MULTILINE)).PartialMatch( str); | // RE(pattern, RE_Options(PCRE_CASELESS|PCRE_MULTILINE)).PartialMatch( str); | |||
// But new code is better off doing | // But new code is better off doing | |||
skipping to change at line 405 | skipping to change at line 402 | |||
return PCRE_IS_SET(PCRE_MULTILINE); | return PCRE_IS_SET(PCRE_MULTILINE); | |||
} | } | |||
RE_Options &set_multiline(bool x) { | RE_Options &set_multiline(bool x) { | |||
PCRE_SET_OR_CLEAR(x, PCRE_MULTILINE); | PCRE_SET_OR_CLEAR(x, PCRE_MULTILINE); | |||
} | } | |||
bool dotall() const { | bool dotall() const { | |||
return PCRE_IS_SET(PCRE_DOTALL); | return PCRE_IS_SET(PCRE_DOTALL); | |||
} | } | |||
RE_Options &set_dotall(bool x) { | RE_Options &set_dotall(bool x) { | |||
PCRE_SET_OR_CLEAR(x,PCRE_DOTALL); | PCRE_SET_OR_CLEAR(x, PCRE_DOTALL); | |||
} | } | |||
bool extended() const { | bool extended() const { | |||
return PCRE_IS_SET(PCRE_EXTENDED); | return PCRE_IS_SET(PCRE_EXTENDED); | |||
} | } | |||
RE_Options &set_extended(bool x) { | RE_Options &set_extended(bool x) { | |||
PCRE_SET_OR_CLEAR(x,PCRE_EXTENDED); | PCRE_SET_OR_CLEAR(x, PCRE_EXTENDED); | |||
} | } | |||
bool dollar_endonly() const { | bool dollar_endonly() const { | |||
return PCRE_IS_SET(PCRE_DOLLAR_ENDONLY); | return PCRE_IS_SET(PCRE_DOLLAR_ENDONLY); | |||
} | } | |||
RE_Options &set_dollar_endonly(bool x) { | RE_Options &set_dollar_endonly(bool x) { | |||
PCRE_SET_OR_CLEAR(x,PCRE_DOLLAR_ENDONLY); | PCRE_SET_OR_CLEAR(x, PCRE_DOLLAR_ENDONLY); | |||
} | } | |||
bool extra() const { | bool extra() const { | |||
return PCRE_IS_SET( PCRE_EXTRA); | return PCRE_IS_SET(PCRE_EXTRA); | |||
} | } | |||
RE_Options &set_extra(bool x) { | RE_Options &set_extra(bool x) { | |||
PCRE_SET_OR_CLEAR(x, PCRE_EXTRA); | PCRE_SET_OR_CLEAR(x, PCRE_EXTRA); | |||
} | } | |||
bool ungreedy() const { | bool ungreedy() const { | |||
return PCRE_IS_SET(PCRE_UNGREEDY); | return PCRE_IS_SET(PCRE_UNGREEDY); | |||
} | } | |||
RE_Options &set_ungreedy(bool x) { | RE_Options &set_ungreedy(bool x) { | |||
PCRE_SET_OR_CLEAR(x, PCRE_UNGREEDY); | PCRE_SET_OR_CLEAR(x, PCRE_UNGREEDY); | |||
skipping to change at line 489 | skipping to change at line 486 | |||
return RE_Options().set_dotall(true); | return RE_Options().set_dotall(true); | |||
} | } | |||
static inline RE_Options EXTENDED() { | static inline RE_Options EXTENDED() { | |||
return RE_Options().set_extended(true); | return RE_Options().set_extended(true); | |||
} | } | |||
// Interface for regular expression matching. Also corresponds to a | // Interface for regular expression matching. Also corresponds to a | |||
// pre-compiled regular expression. An "RE" object is safe for | // pre-compiled regular expression. An "RE" object is safe for | |||
// concurrent use by multiple threads. | // concurrent use by multiple threads. | |||
class RE { | class PCRECPP_EXP_DEFN RE { | |||
public: | public: | |||
// We provide implicit conversions from strings so that users can | // We provide implicit conversions from strings so that users can | |||
// pass in a string or a "const char*" wherever an "RE" is expected. | // pass in a string or a "const char*" wherever an "RE" is expected. | |||
RE(const string& pat) { Init(pat, NULL); } | RE(const string& pat) { Init(pat, NULL); } | |||
RE(const string& pat, const RE_Options& option) { Init(pat, &option); } | RE(const string& pat, const RE_Options& option) { Init(pat, &option); } | |||
RE(const char* pat) { Init(pat, NULL); } | RE(const char* pat) { Init(pat, NULL); } | |||
RE(const char* pat, const RE_Options& option) { Init(pat, &option); } | RE(const char* pat, const RE_Options& option) { Init(pat, &option); } | |||
RE(const unsigned char* pat) { | RE(const unsigned char* pat) { | |||
Init(reinterpret_cast<const char*>(pat), NULL); | Init(reinterpret_cast<const char*>(pat), NULL); | |||
} | } | |||
skipping to change at line 624 | skipping to change at line 621 | |||
bool Extract(const StringPiece &rewrite, | bool Extract(const StringPiece &rewrite, | |||
const StringPiece &text, | const StringPiece &text, | |||
string *out) const; | string *out) const; | |||
// Escapes all potentially meaningful regexp characters in | // Escapes all potentially meaningful regexp characters in | |||
// 'unquoted'. The returned string, used as a regular expression, | // 'unquoted'. The returned string, used as a regular expression, | |||
// will exactly match the original string. For example, | // will exactly match the original string. For example, | |||
// 1.5-2.0? | // 1.5-2.0? | |||
// may become: | // may become: | |||
// 1\.5\-2\.0\? | // 1\.5\-2\.0\? | |||
// Note QuoteMeta behaves the same as perl's QuoteMeta function, | ||||
// *except* that it escapes the NUL character (\0) as backslash + 0, | ||||
// rather than backslash + NUL. | ||||
static string QuoteMeta(const StringPiece& unquoted); | static string QuoteMeta(const StringPiece& unquoted); | |||
/***** Generic matching interface *****/ | /***** Generic matching interface *****/ | |||
// Type of match (TODO: Should be restructured as part of RE_Options) | // Type of match (TODO: Should be restructured as part of RE_Options) | |||
enum Anchor { | enum Anchor { | |||
UNANCHORED, // No anchoring | UNANCHORED, // No anchoring | |||
ANCHOR_START, // Anchor at start only | ANCHOR_START, // Anchor at start only | |||
ANCHOR_BOTH // Anchor at start and end | ANCHOR_BOTH // Anchor at start and end | |||
}; | }; | |||
skipping to change at line 646 | skipping to change at line 646 | |||
// "*consumed" if successful. | // "*consumed" if successful. | |||
bool DoMatch(const StringPiece& text, | bool DoMatch(const StringPiece& text, | |||
Anchor anchor, | Anchor anchor, | |||
int* consumed, | int* consumed, | |||
const Arg* const* args, int n) const; | const Arg* const* args, int n) const; | |||
// Return the number of capturing subpatterns, or -1 if the | // Return the number of capturing subpatterns, or -1 if the | |||
// regexp wasn't valid on construction. | // regexp wasn't valid on construction. | |||
int NumberOfCapturingGroups() const; | int NumberOfCapturingGroups() const; | |||
// The default value for an argument, to indicate the end of the argument | ||||
// list. This must be used only in optional argument defaults. It should | ||||
NOT | ||||
// be passed explicitly. Some people have tried to use it like this: | ||||
// | ||||
// FullMatch(x, y, &z, no_arg, &w); | ||||
// | ||||
// This is a mistake, and will not work. | ||||
static Arg no_arg; | ||||
private: | private: | |||
void Init(const string& pattern, const RE_Options* options); | void Init(const string& pattern, const RE_Options* options); | |||
void Cleanup(); | void Cleanup(); | |||
// Match against "text", filling in "vec" (up to "vecsize" * 2/3) with | // Match against "text", filling in "vec" (up to "vecsize" * 2/3) with | |||
// pairs of integers for the beginning and end positions of matched | // pairs of integers for the beginning and end positions of matched | |||
// text. The first pair corresponds to the entire matched text; | // text. The first pair corresponds to the entire matched text; | |||
// subsequent pairs correspond, in order, to parentheses-captured | // subsequent pairs correspond, in order, to parentheses-captured | |||
// matches. Returns the number of pairs (one more than the number of | // matches. Returns the number of pairs (one more than the number of | |||
// the last subpattern with a match) if matching was successful | // the last subpattern with a match) if matching was successful | |||
// and zero if the match failed. | // and zero if the match failed. | |||
// I.e. for RE("(foo)|(bar)|(baz)") it will return 2, 3, and 4 when match ing | // I.e. for RE("(foo)|(bar)|(baz)") it will return 2, 3, and 4 when match ing | |||
// against "foo", "bar", and "baz" respectively. | // against "foo", "bar", and "baz" respectively. | |||
// When matching RE("(foo)|hello") against "hello", it will return 1. | // When matching RE("(foo)|hello") against "hello", it will return 1. | |||
// But the values for all subpattern are filled in into "vec". | // But the values for all subpattern are filled in into "vec". | |||
int TryMatch(const StringPiece& text, | int TryMatch(const StringPiece& text, | |||
int startpos, | int startpos, | |||
Anchor anchor, | Anchor anchor, | |||
bool empty_ok, | ||||
int *vec, | int *vec, | |||
int vecsize) const; | int vecsize) const; | |||
// Append the "rewrite" string, with backslash subsitutions from "text" | // Append the "rewrite" string, with backslash subsitutions from "text" | |||
// and "vec", to string "out". | // and "vec", to string "out". | |||
bool Rewrite(string *out, | bool Rewrite(string *out, | |||
const StringPiece& rewrite, | const StringPiece& rewrite, | |||
const StringPiece& text, | const StringPiece& text, | |||
int *vec, | int *vec, | |||
int veclen) const; | int veclen) const; | |||
End of changes. 10 change blocks. | ||||
9 lines changed or deleted | 20 lines changed or added | |||
pcrecpp_internal.h | pcrecpp_internal.h | |||
---|---|---|---|---|
/************************************************* | /************************************************* | |||
* Perl-Compatible Regular Expressions * | * Perl-Compatible Regular Expressions * | |||
*************************************************/ | *************************************************/ | |||
/* | /* | |||
Copyright (c) 2005, Google Inc. | ||||
All rights reserved. | ||||
--------------------------------------------------------------------------- -- | --------------------------------------------------------------------------- -- | |||
Redistribution and use in source and binary forms, with or without | Redistribution and use in source and binary forms, with or without | |||
modification, are permitted provided that the following conditions are met: | modification, are permitted provided that the following conditions are met: | |||
* Redistributions of source code must retain the above copyright notice , | * Redistributions of source code must retain the above copyright notice , | |||
this list of conditions and the following disclaimer. | this list of conditions and the following disclaimer. | |||
* Redistributions in binary form must reproduce the above copyright | * Redistributions in binary form must reproduce the above copyright | |||
notice, this list of conditions and the following disclaimer in the | notice, this list of conditions and the following disclaimer in the | |||
documentation and/or other materials provided with the distribution. | documentation and/or other materials provided with the distribution. | |||
skipping to change at line 52 | skipping to change at line 55 | |||
definition; with "extern" we have a declaration. The settings here override the | definition; with "extern" we have a declaration. The settings here override the | |||
setting in pcre.h. We use: | setting in pcre.h. We use: | |||
PCRECPP_EXP_DECL for declarations | PCRECPP_EXP_DECL for declarations | |||
PCRECPP_EXP_DEFN for definitions of exported functions | PCRECPP_EXP_DEFN for definitions of exported functions | |||
*/ | */ | |||
#ifndef PCRECPP_EXP_DECL | #ifndef PCRECPP_EXP_DECL | |||
# ifdef _WIN32 | # ifdef _WIN32 | |||
# ifndef PCRECPP_STATIC | # ifndef PCRE_STATIC | |||
# define PCRECPP_EXP_DECL extern __declspec(dllexport) | # define PCRECPP_EXP_DECL extern __declspec(dllexport) | |||
# define PCRECPP_EXP_DEFN __declspec(dllexport) | # define PCRECPP_EXP_DEFN __declspec(dllexport) | |||
# else | # else | |||
# define PCRECPP_EXP_DECL extern | # define PCRECPP_EXP_DECL extern | |||
# define PCRECPP_EXP_DEFN | # define PCRECPP_EXP_DEFN | |||
# endif | # endif | |||
# else | # else | |||
# define PCRECPP_EXP_DECL extern | # define PCRECPP_EXP_DECL extern | |||
# define PCRECPP_EXP_DEFN | # define PCRECPP_EXP_DEFN | |||
# endif | # endif | |||
End of changes. 2 change blocks. | ||||
1 lines changed or deleted | 4 lines changed or added | |||
pcrecpparg.h | pcrecpparg.h | |||
---|---|---|---|---|
skipping to change at line 51 | skipping to change at line 51 | |||
class StringPiece; | class StringPiece; | |||
// Hex/Octal/Binary? | // Hex/Octal/Binary? | |||
// Special class for parsing into objects that define a ParseFrom() method | // Special class for parsing into objects that define a ParseFrom() method | |||
template <class T> | template <class T> | |||
class _RE_MatchObject { | class _RE_MatchObject { | |||
public: | public: | |||
static inline bool Parse(const char* str, int n, void* dest) { | static inline bool Parse(const char* str, int n, void* dest) { | |||
if (dest == NULL) return true; | ||||
T* object = reinterpret_cast<T*>(dest); | T* object = reinterpret_cast<T*>(dest); | |||
return object->ParseFrom(str, n); | return object->ParseFrom(str, n); | |||
} | } | |||
}; | }; | |||
class PCRECPP_EXP_DEFN Arg { | class PCRECPP_EXP_DEFN Arg { | |||
public: | public: | |||
// Empty constructor so we can declare arrays of Arg | // Empty constructor so we can declare arrays of Arg | |||
Arg(); | Arg(); | |||
End of changes. 1 change blocks. | ||||
0 lines changed or deleted | 1 lines changed or added | |||
pcreposix.h | pcreposix.h | |||
---|---|---|---|---|
skipping to change at line 12 | skipping to change at line 12 | |||
* Perl-Compatible Regular Expressions * | * Perl-Compatible Regular Expressions * | |||
*************************************************/ | *************************************************/ | |||
#ifndef _PCREPOSIX_H | #ifndef _PCREPOSIX_H | |||
#define _PCREPOSIX_H | #define _PCREPOSIX_H | |||
/* This is the header for the POSIX wrapper interface to the PCRE Perl- | /* This is the header for the POSIX wrapper interface to the PCRE Perl- | |||
Compatible Regular Expression library. It defines the things POSIX says sho uld | Compatible Regular Expression library. It defines the things POSIX says sho uld | |||
be there. I hope. | be there. I hope. | |||
Copyright (c) 1997-2007 University of Cambridge | Copyright (c) 1997-2012 University of Cambridge | |||
--------------------------------------------------------------------------- -- | --------------------------------------------------------------------------- -- | |||
Redistribution and use in source and binary forms, with or without | Redistribution and use in source and binary forms, with or without | |||
modification, are permitted provided that the following conditions are met: | modification, are permitted provided that the following conditions are met: | |||
* Redistributions of source code must retain the above copyright notice , | * Redistributions of source code must retain the above copyright notice , | |||
this list of conditions and the following disclaimer. | this list of conditions and the following disclaimer. | |||
* Redistributions in binary form must reproduce the above copyright | * Redistributions in binary form must reproduce the above copyright | |||
notice, this list of conditions and the following disclaimer in the | notice, this list of conditions and the following disclaimer in the | |||
skipping to change at line 53 | skipping to change at line 53 | |||
/* Have to include stdlib.h in order to ensure that size_t is defined. */ | /* Have to include stdlib.h in order to ensure that size_t is defined. */ | |||
#include <stdlib.h> | #include <stdlib.h> | |||
/* Allow for C++ users */ | /* Allow for C++ users */ | |||
#ifdef __cplusplus | #ifdef __cplusplus | |||
extern "C" { | extern "C" { | |||
#endif | #endif | |||
/* Options, mostly defined by POSIX, but with a couple of extras. */ | /* Options, mostly defined by POSIX, but with some extras. */ | |||
#define REG_ICASE 0x0001 | #define REG_ICASE 0x0001 /* Maps to PCRE_CASELESS */ | |||
#define REG_NEWLINE 0x0002 | #define REG_NEWLINE 0x0002 /* Maps to PCRE_MULTILINE */ | |||
#define REG_NOTBOL 0x0004 | #define REG_NOTBOL 0x0004 /* Maps to PCRE_NOTBOL */ | |||
#define REG_NOTEOL 0x0008 | #define REG_NOTEOL 0x0008 /* Maps to PCRE_NOTEOL */ | |||
#define REG_DOTALL 0x0010 /* NOT defined by POSIX. */ | #define REG_DOTALL 0x0010 /* NOT defined by POSIX; maps to PCRE_DOTALL | |||
#define REG_NOSUB 0x0020 | */ | |||
#define REG_UTF8 0x0040 /* NOT defined by POSIX. */ | #define REG_NOSUB 0x0020 /* Maps to PCRE_NO_AUTO_CAPTURE */ | |||
#define REG_UTF8 0x0040 /* NOT defined by POSIX; maps to PCRE_UTF8 * | ||||
/ | ||||
#define REG_STARTEND 0x0080 /* BSD feature: pass subject string by so,eo | ||||
*/ | ||||
#define REG_NOTEMPTY 0x0100 /* NOT defined by POSIX; maps to PCRE_NOTEMP | ||||
TY */ | ||||
#define REG_UNGREEDY 0x0200 /* NOT defined by POSIX; maps to PCRE_UNGREE | ||||
DY */ | ||||
#define REG_UCP 0x0400 /* NOT defined by POSIX; maps to PCRE_UCP */ | ||||
/* This is not used by PCRE, but by defining it we make it easier | /* This is not used by PCRE, but by defining it we make it easier | |||
to slot PCRE into existing programs that make POSIX calls. */ | to slot PCRE into existing programs that make POSIX calls. */ | |||
#define REG_EXTENDED 0 | #define REG_EXTENDED 0 | |||
/* Error values. Not all these are relevant or used by the wrapper. */ | /* Error values. Not all these are relevant or used by the wrapper. */ | |||
enum { | enum { | |||
REG_ASSERT = 1, /* internal error ? */ | REG_ASSERT = 1, /* internal error ? */ | |||
End of changes. 3 change blocks. | ||||
9 lines changed or deleted | 18 lines changed or added | |||
pdfile.h | pdfile.h | |||
---|---|---|---|---|
skipping to change at line 28 | skipping to change at line 28 | |||
Files: | Files: | |||
database.ns - namespace index | database.ns - namespace index | |||
database.1 - data files | database.1 - data files | |||
database.2 | database.2 | |||
... | ... | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../pch.h" | #include "mongo/db/client.h" | |||
#include "../util/mmap.h" | #include "mongo/db/diskloc.h" | |||
#include "diskloc.h" | #include "mongo/db/jsobjmanipulator.h" | |||
#include "jsobjmanipulator.h" | #include "mongo/db/memconcept.h" | |||
#include "namespace-inl.h" | #include "mongo/db/mongommf.h" | |||
#include "client.h" | #include "mongo/db/namespace-inl.h" | |||
#include "mongommf.h" | #include "mongo/db/namespace_details-inl.h" | |||
#include "memconcept.h" | #include "mongo/db/namespacestring.h" | |||
#include "mongo/util/mmap.h" | ||||
#include "mongo/util/util.h" | ||||
namespace mongo { | namespace mongo { | |||
// pdfile versions | ||||
const int PDFILE_VERSION = 4; | ||||
const int PDFILE_VERSION_MINOR = 5; | ||||
class DataFileHeader; | class DataFileHeader; | |||
class Extent; | class Extent; | |||
class Record; | class Record; | |||
class Cursor; | class Cursor; | |||
class OpDebug; | class OpDebug; | |||
void dropDatabase(string db); | void dropDatabase(string db); | |||
bool repairDatabase(string db, string &errmsg, bool preserveClonedFiles OnFailure = false, bool backupOriginalFiles = false); | bool repairDatabase(string db, string &errmsg, bool preserveClonedFiles OnFailure = false, bool backupOriginalFiles = false); | |||
/* low level - only drops this ns */ | /* low level - only drops this ns */ | |||
skipping to change at line 127 | skipping to change at line 133 | |||
/** @return DiskLoc where item ends up */ | /** @return DiskLoc where item ends up */ | |||
// changedId should be initialized to false | // changedId should be initialized to false | |||
const DiskLoc updateRecord( | const DiskLoc updateRecord( | |||
const char *ns, | const char *ns, | |||
NamespaceDetails *d, | NamespaceDetails *d, | |||
NamespaceDetailsTransient *nsdt, | NamespaceDetailsTransient *nsdt, | |||
Record *toupdate, const DiskLoc& dl, | Record *toupdate, const DiskLoc& dl, | |||
const char *buf, int len, OpDebug& debug, bool god=false); | const char *buf, int len, OpDebug& debug, bool god=false); | |||
// The object o may be updated if modified on insert. | // The object o may be updated if modified on insert. | |||
void insertAndLog( const char *ns, const BSONObj &o, bool god = fal se ); | void insertAndLog( const char *ns, const BSONObj &o, bool god = fal se, bool fromMigrate = false ); | |||
/** insert will add an _id to the object if not present. if you wo uld like to see the final object | /** insert will add an _id to the object if not present. if you wo uld like to see the final object | |||
after such an addition, use this method. | after such an addition, use this method. | |||
@param o both and in and out param | @param o both and in and out param | |||
*/ | */ | |||
DiskLoc insertWithObjMod(const char *ns, BSONObj & /*out*/o, bool g od = false); | DiskLoc insertWithObjMod(const char *ns, BSONObj & /*out*/o, bool g od = false); | |||
/** @param obj in value only for this version. */ | /** @param obj in value only for this version. */ | |||
void insertNoReturnVal(const char *ns, BSONObj o, bool god = false) ; | void insertNoReturnVal(const char *ns, BSONObj o, bool god = false) ; | |||
skipping to change at line 166 | skipping to change at line 172 | |||
private: | private: | |||
vector<MongoDataFile *> files; | vector<MongoDataFile *> files; | |||
}; | }; | |||
extern DataFileMgr theDataFileMgr; | extern DataFileMgr theDataFileMgr; | |||
#pragma pack(1) | #pragma pack(1) | |||
class DeletedRecord { | class DeletedRecord { | |||
public: | public: | |||
int lengthWithHeaders; | ||||
int extentOfs; | int lengthWithHeaders() const { _accessing(); return _lengthWithHea | |||
DiskLoc nextDeleted; | ders; } | |||
int& lengthWithHeaders() { _accessing(); return _lengthWithHeaders; | ||||
} | ||||
int extentOfs() const { _accessing(); return _extentOfs; } | ||||
int& extentOfs() { _accessing(); return _extentOfs; } | ||||
// TODO: we need to not const_cast here but problem is DiskLoc::wri | ||||
ting | ||||
DiskLoc& nextDeleted() const { _accessing(); return const_cast<Disk | ||||
Loc&>(_nextDeleted); } | ||||
DiskLoc myExtentLoc(const DiskLoc& myLoc) const { | DiskLoc myExtentLoc(const DiskLoc& myLoc) const { | |||
return DiskLoc(myLoc.a(), extentOfs); | _accessing(); | |||
return DiskLoc(myLoc.a(), _extentOfs); | ||||
} | } | |||
Extent* myExtent(const DiskLoc& myLoc) { | Extent* myExtent(const DiskLoc& myLoc) { | |||
return DataFileMgr::getExtent(DiskLoc(myLoc.a(), extentOfs)); | _accessing(); | |||
return DataFileMgr::getExtent(DiskLoc(myLoc.a(), _extentOfs)); | ||||
} | } | |||
private: | ||||
void _accessing() const; | ||||
int _lengthWithHeaders; | ||||
int _extentOfs; | ||||
DiskLoc _nextDeleted; | ||||
}; | }; | |||
/* Record is a record in a datafile. DeletedRecord is similar but for deleted space. | /* Record is a record in a datafile. DeletedRecord is similar but for deleted space. | |||
*11:03:20 AM) dm10gen: regarding extentOfs... | *11:03:20 AM) dm10gen: regarding extentOfs... | |||
(11:03:42 AM) dm10gen: an extent is a continugous disk area, which cont ains many Records and DeleteRecords | (11:03:42 AM) dm10gen: an extent is a continugous disk area, which cont ains many Records and DeleteRecords | |||
(11:03:56 AM) dm10gen: a DiskLoc has two pieces, the fileno and ofs. ( 64 bit total) | (11:03:56 AM) dm10gen: a DiskLoc has two pieces, the fileno and ofs. ( 64 bit total) | |||
(11:04:16 AM) dm10gen: to keep the headesr small, instead of storing a 64 bit ptr to the full extent address, we keep just the offset | (11:04:16 AM) dm10gen: to keep the headesr small, instead of storing a 64 bit ptr to the full extent address, we keep just the offset | |||
(11:04:29 AM) dm10gen: we can do this as we know the record's address, and it has the same fileNo | (11:04:29 AM) dm10gen: we can do this as we know the record's address, and it has the same fileNo | |||
(11:04:33 AM) dm10gen: see class DiskLoc for more info | (11:04:33 AM) dm10gen: see class DiskLoc for more info | |||
(11:04:43 AM) dm10gen: so that is how Record::myExtent() works | (11:04:43 AM) dm10gen: so that is how Record::myExtent() works | |||
(11:04:53 AM) dm10gen: on an alloc(), when we build a new Record, we mu st populate its extentOfs then | (11:04:53 AM) dm10gen: on an alloc(), when we build a new Record, we mu st populate its extentOfs then | |||
*/ | */ | |||
class Record { | class Record { | |||
public: | public: | |||
enum HeaderSizeValue { HeaderSize = 16 }; | enum HeaderSizeValue { HeaderSize = 16 }; | |||
int lengthWithHeaders; | ||||
int extentOfs; | ||||
int nextOfs; | ||||
int prevOfs; | ||||
/** be careful when referencing this that your write intent was cor | int lengthWithHeaders() const { _accessing(); return _lengthWithHe | |||
rect */ | aders; } | |||
char data[4]; | int& lengthWithHeaders() { _accessing(); return _lengthWithHeaders | |||
; } | ||||
int netLength() { | int extentOfs() const { _accessing(); return _extentOfs; } | |||
return lengthWithHeaders - HeaderSize; | int& extentOfs() { _accessing(); return _extentOfs; } | |||
} | ||||
//void setNewLength(int netlen) { lengthWithHeaders = netlen + Head | int nextOfs() const { _accessing(); return _nextOfs; } | |||
erSize; } | int& nextOfs() { _accessing(); return _nextOfs; } | |||
int prevOfs() const { _accessing(); return _prevOfs; } | ||||
int& prevOfs() { _accessing(); return _prevOfs; } | ||||
const char * data() const { _accessing(); return _data; } | ||||
char * data() { _accessing(); return _data; } | ||||
int netLength() const { _accessing(); return _netLength(); } | ||||
/* use this when a record is deleted. basically a union with next/p rev fields */ | /* use this when a record is deleted. basically a union with next/p rev fields */ | |||
DeletedRecord& asDeleted() { return *((DeletedRecord*) this); } | DeletedRecord& asDeleted() { return *((DeletedRecord*) this); } | |||
Extent* myExtent(const DiskLoc& myLoc) { return DataFileMgr::getExt ent(DiskLoc(myLoc.a(), extentOfs)); } | Extent* myExtent(const DiskLoc& myLoc) { return DataFileMgr::getExt ent(DiskLoc(myLoc.a(), extentOfs() ) ); } | |||
/* get the next record in the namespace, traversing extents as nece ssary */ | /* get the next record in the namespace, traversing extents as nece ssary */ | |||
DiskLoc getNext(const DiskLoc& myLoc); | DiskLoc getNext(const DiskLoc& myLoc); | |||
DiskLoc getPrev(const DiskLoc& myLoc); | DiskLoc getPrev(const DiskLoc& myLoc); | |||
DiskLoc nextInExtent(const DiskLoc& myLoc) { | DiskLoc nextInExtent(const DiskLoc& myLoc) { | |||
if ( nextOfs == DiskLoc::NullOfs ) | _accessing(); | |||
if ( _nextOfs == DiskLoc::NullOfs ) | ||||
return DiskLoc(); | return DiskLoc(); | |||
assert( nextOfs ); | verify( _nextOfs ); | |||
return DiskLoc(myLoc.a(), nextOfs); | return DiskLoc(myLoc.a(), _nextOfs); | |||
} | } | |||
struct NP { | struct NP { | |||
int nextOfs; | int nextOfs; | |||
int prevOfs; | int prevOfs; | |||
}; | }; | |||
NP* np() { return (NP*) &nextOfs; } | NP* np() { return (NP*) &_nextOfs; } | |||
// --------------------- | // --------------------- | |||
// memory cache | // memory cache | |||
// --------------------- | // --------------------- | |||
/** | /** | |||
* touches the data so that is in physical memory | * touches the data so that is in physical memory | |||
* @param entireRecrd if false, only the header and first byte is t ouched | * @param entireRecrd if false, only the header and first byte is t ouched | |||
* if true, the entire record is touched | * if true, the entire record is touched | |||
* */ | * */ | |||
void touch( bool entireRecrd = false ); | void touch( bool entireRecrd = false ) const; | |||
/** | /** | |||
* @return if this record is likely in physical memory | * @return if this record is likely in physical memory | |||
* its not guaranteed because its possible it gets swapped out in a very unlucky windows | * its not guaranteed because its possible it gets swapped out in a very unlucky windows | |||
*/ | */ | |||
bool likelyInPhysicalMemory(); | bool likelyInPhysicalMemory() const ; | |||
/** | /** | |||
* tell the cache this Record was accessed | * tell the cache this Record was accessed | |||
* @return this, for simple chaining | * @return this, for simple chaining | |||
*/ | */ | |||
Record* accessed(); | Record* accessed(); | |||
private: | ||||
int _netLength() const { return _lengthWithHeaders - HeaderSize; } | ||||
/** | ||||
* call this when accessing a field which could hit disk | ||||
*/ | ||||
void _accessing() const; | ||||
int _lengthWithHeaders; | ||||
int _extentOfs; | ||||
int _nextOfs; | ||||
int _prevOfs; | ||||
/** be careful when referencing this that your write intent was cor | ||||
rect */ | ||||
char _data[4]; | ||||
public: | ||||
static bool MemoryTrackingEnabled; | static bool MemoryTrackingEnabled; | |||
}; | }; | |||
/* extents are datafile regions where all the records within the region | /* extents are datafile regions where all the records within the region | |||
belong to the same namespace. | belong to the same namespace. | |||
(11:12:35 AM) dm10gen: when the extent is allocated, all its empty spac e is stuck into one big DeletedRecord | (11:12:35 AM) dm10gen: when the extent is allocated, all its empty spac e is stuck into one big DeletedRecord | |||
(11:12:55 AM) dm10gen: and that is placed on the free list | (11:12:55 AM) dm10gen: and that is placed on the free list | |||
*/ | */ | |||
class Extent { | class Extent { | |||
skipping to change at line 303 | skipping to change at line 350 | |||
/* assumes already zeroed -- insufficient for block 'reuse' perhaps | /* assumes already zeroed -- insufficient for block 'reuse' perhaps | |||
Returns a DeletedRecord location which is the data in the extent re ady for us. | Returns a DeletedRecord location which is the data in the extent re ady for us. | |||
Caller will need to add that to the freelist structure in namespace detail. | Caller will need to add that to the freelist structure in namespace detail. | |||
*/ | */ | |||
DiskLoc init(const char *nsname, int _length, int _fileNo, int _off set, bool capped); | DiskLoc init(const char *nsname, int _length, int _fileNo, int _off set, bool capped); | |||
/* like init(), but for a reuse case */ | /* like init(), but for a reuse case */ | |||
DiskLoc reuse(const char *nsname, bool newUseIsAsCapped); | DiskLoc reuse(const char *nsname, bool newUseIsAsCapped); | |||
bool isOk() const { return magic == 0x41424344; } | bool isOk() const { return magic == 0x41424344; } | |||
void assertOk() const { assert(isOk()); } | void assertOk() const { verify(isOk()); } | |||
Record* newRecord(int len); | Record* newRecord(int len); | |||
Record* getRecord(DiskLoc dl) { | Record* getRecord(DiskLoc dl) { | |||
assert( !dl.isNull() ); | verify( !dl.isNull() ); | |||
assert( dl.sameFile(myLoc) ); | verify( dl.sameFile(myLoc) ); | |||
int x = dl.getOfs() - myLoc.getOfs(); | int x = dl.getOfs() - myLoc.getOfs(); | |||
assert( x > 0 ); | verify( x > 0 ); | |||
return (Record *) (((char *) this) + x); | return (Record *) (((char *) this) + x); | |||
} | } | |||
Extent* getNextExtent() { return xnext.isNull() ? 0 : DataFileMgr:: getExtent(xnext); } | Extent* getNextExtent() { return xnext.isNull() ? 0 : DataFileMgr:: getExtent(xnext); } | |||
Extent* getPrevExtent() { return xprev.isNull() ? 0 : DataFileMgr:: getExtent(xprev); } | Extent* getPrevExtent() { return xprev.isNull() ? 0 : DataFileMgr:: getExtent(xprev); } | |||
static int maxSize(); | static int maxSize(); | |||
static int minSize() { return 0x100; } | static int minSize() { return 0x1000; } | |||
/** | /** | |||
* @param len lengt of record we need | * @param len lengt of record we need | |||
* @param lastRecord size of last extent which is a factor in next extent size | * @param lastRecord size of last extent which is a factor in next extent size | |||
*/ | */ | |||
static int followupSize(int len, int lastExtentLen); | static int followupSize(int len, int lastExtentLen); | |||
/** get a suggested size for the first extent in a namespace | /** get a suggested size for the first extent in a namespace | |||
* @param len length of record we need to insert | * @param len length of record we need to insert | |||
*/ | */ | |||
static int initialSize(int len); | static int initialSize(int len); | |||
skipping to change at line 384 | skipping to change at line 431 | |||
bool uninitialized() const { return version == 0; } | bool uninitialized() const { return version == 0; } | |||
void init(int fileno, int filelength, const char* filename) { | void init(int fileno, int filelength, const char* filename) { | |||
if ( uninitialized() ) { | if ( uninitialized() ) { | |||
DEV log() << "datafileheader::init initializing " << filena me << " n:" << fileno << endl; | DEV log() << "datafileheader::init initializing " << filena me << " n:" << fileno << endl; | |||
if( !(filelength > 32768 ) ) { | if( !(filelength > 32768 ) ) { | |||
massert(13640, str::stream() << "DataFileHeader looks c orrupt at file open filelength:" << filelength << " fileno:" << fileno, fal se); | massert(13640, str::stream() << "DataFileHeader looks c orrupt at file open filelength:" << filelength << " fileno:" << fileno, fal se); | |||
} | } | |||
{ | { | |||
if( !d.dbMutex.isWriteLocked() ) { | // "something" is too vague, but we checked for the rig | |||
ht db to be locked higher up the call stack | ||||
if( !Lock::somethingWriteLocked() ) { | ||||
LockState::Dump(); | ||||
log() << "*** TEMP NOT INITIALIZING FILE " << filen ame << ", not in a write lock." << endl; | log() << "*** TEMP NOT INITIALIZING FILE " << filen ame << ", not in a write lock." << endl; | |||
log() << "temp bypass until more elaborate change - case that is manifesting is benign anyway" << endl; | log() << "temp bypass until more elaborate change - case that is manifesting is benign anyway" << endl; | |||
return; | return; | |||
/** | /** | |||
log() << "ERROR can't create outside a write lock" << endl; | log() << "ERROR can't create outside a write lock" << endl; | |||
printStackTrace(); | printStackTrace(); | |||
::abort(); | ::abort(); | |||
**/ | **/ | |||
} | } | |||
} | } | |||
getDur().createdFile(filename, filelength); | getDur().createdFile(filename, filelength); | |||
assert( HeaderSize == 8192 ); | verify( HeaderSize == 8192 ); | |||
DataFileHeader *h = getDur().writing(this); | DataFileHeader *h = getDur().writing(this); | |||
h->fileLength = filelength; | h->fileLength = filelength; | |||
h->version = PDFILE_VERSION; | h->version = PDFILE_VERSION; | |||
h->versionMinor = PDFILE_VERSION_MINOR; | h->versionMinor = PDFILE_VERSION_MINOR; | |||
h->unused.set( fileno, HeaderSize ); | h->unused.set( fileno, HeaderSize ); | |||
assert( (data-(char*)this) == HeaderSize ); | verify( (data-(char*)this) == HeaderSize ); | |||
h->unusedLength = fileLength - HeaderSize - 16; | h->unusedLength = fileLength - HeaderSize - 16; | |||
} | } | |||
} | } | |||
bool isEmpty() const { | bool isEmpty() const { | |||
return uninitialized() || ( unusedLength == fileLength - Header Size - 16 ); | return uninitialized() || ( unusedLength == fileLength - Header Size - 16 ); | |||
} | } | |||
}; | }; | |||
#pragma pack() | #pragma pack() | |||
inline Extent* MongoDataFile::_getExtent(DiskLoc loc) const { | inline Extent* MongoDataFile::_getExtent(DiskLoc loc) const { | |||
loc.assertOk(); | loc.assertOk(); | |||
Extent *e = (Extent *) (p()+loc.getOfs()); | Extent *e = (Extent *) (p()+loc.getOfs()); | |||
return e; | return e; | |||
} | } | |||
inline Extent* MongoDataFile::getExtent(DiskLoc loc) const { | inline Extent* MongoDataFile::getExtent(DiskLoc loc) const { | |||
Extent *e = _getExtent(loc); | Extent *e = _getExtent(loc); | |||
e->assertOk(); | e->assertOk(); | |||
memconcept::is(e, memconcept::extent); | memconcept::is(e, memconcept::concept::extent); | |||
return e; | return e; | |||
} | } | |||
} // namespace mongo | } // namespace mongo | |||
#include "cursor.h" | #include "cursor.h" | |||
namespace mongo { | namespace mongo { | |||
inline Record* MongoDataFile::recordAt(DiskLoc dl) { | inline Record* MongoDataFile::recordAt(DiskLoc dl) { | |||
skipping to change at line 447 | skipping to change at line 496 | |||
return (Record*) (p()+ofs); | return (Record*) (p()+ofs); | |||
} | } | |||
inline Record* MongoDataFile::makeRecord(DiskLoc dl, int size) { | inline Record* MongoDataFile::makeRecord(DiskLoc dl, int size) { | |||
int ofs = dl.getOfs(); | int ofs = dl.getOfs(); | |||
if( ofs < DataFileHeader::HeaderSize ) badOfs(ofs); // will uassert - external call to keep out of the normal code path | if( ofs < DataFileHeader::HeaderSize ) badOfs(ofs); // will uassert - external call to keep out of the normal code path | |||
return (Record*) (p()+ofs); | return (Record*) (p()+ofs); | |||
} | } | |||
inline DiskLoc Record::getNext(const DiskLoc& myLoc) { | inline DiskLoc Record::getNext(const DiskLoc& myLoc) { | |||
if ( nextOfs != DiskLoc::NullOfs ) { | _accessing(); | |||
if ( _nextOfs != DiskLoc::NullOfs ) { | ||||
/* defensive */ | /* defensive */ | |||
if ( nextOfs >= 0 && nextOfs < 10 ) { | if ( _nextOfs >= 0 && _nextOfs < 10 ) { | |||
sayDbContext("Assertion failure - Record::getNext() referen cing a deleted record?"); | sayDbContext("Assertion failure - Record::getNext() referen cing a deleted record?"); | |||
return DiskLoc(); | return DiskLoc(); | |||
} | } | |||
return DiskLoc(myLoc.a(), nextOfs); | return DiskLoc(myLoc.a(), _nextOfs); | |||
} | } | |||
Extent *e = myExtent(myLoc); | Extent *e = myExtent(myLoc); | |||
while ( 1 ) { | while ( 1 ) { | |||
if ( e->xnext.isNull() ) | if ( e->xnext.isNull() ) | |||
return DiskLoc(); // end of table. | return DiskLoc(); // end of table. | |||
e = e->xnext.ext(); | e = e->xnext.ext(); | |||
if ( !e->firstRecord.isNull() ) | if ( !e->firstRecord.isNull() ) | |||
break; | break; | |||
// entire extent could be empty, keep looking | // entire extent could be empty, keep looking | |||
} | } | |||
return e->firstRecord; | return e->firstRecord; | |||
} | } | |||
inline DiskLoc Record::getPrev(const DiskLoc& myLoc) { | inline DiskLoc Record::getPrev(const DiskLoc& myLoc) { | |||
if ( prevOfs != DiskLoc::NullOfs ) | _accessing(); | |||
return DiskLoc(myLoc.a(), prevOfs); | if ( _prevOfs != DiskLoc::NullOfs ) | |||
return DiskLoc(myLoc.a(), _prevOfs); | ||||
Extent *e = myExtent(myLoc); | Extent *e = myExtent(myLoc); | |||
if ( e->xprev.isNull() ) | if ( e->xprev.isNull() ) | |||
return DiskLoc(); | return DiskLoc(); | |||
return e->xprev.ext()->lastRecord; | return e->xprev.ext()->lastRecord; | |||
} | } | |||
inline BSONObj DiskLoc::obj() const { | inline BSONObj DiskLoc::obj() const { | |||
return BSONObj(rec()->accessed()); | return BSONObj(rec()->accessed()); | |||
} | } | |||
inline DeletedRecord* DiskLoc::drec() const { | inline DeletedRecord* DiskLoc::drec() const { | |||
assert( _a != -1 ); | verify( _a != -1 ); | |||
DeletedRecord* dr = (DeletedRecord*) rec(); | DeletedRecord* dr = (DeletedRecord*) rec(); | |||
memconcept::is(dr, memconcept::deletedrecord); | memconcept::is(dr, memconcept::concept::deletedrecord); | |||
return dr; | return dr; | |||
} | } | |||
inline Extent* DiskLoc::ext() const { | inline Extent* DiskLoc::ext() const { | |||
return DataFileMgr::getExtent(*this); | return DataFileMgr::getExtent(*this); | |||
} | } | |||
template< class V > | template< class V > | |||
inline | inline | |||
const BtreeBucket<V> * DiskLoc::btree() const { | const BtreeBucket<V> * DiskLoc::btree() const { | |||
assert( _a != -1 ); | verify( _a != -1 ); | |||
return (const BtreeBucket<V> *) rec()->data; | Record *r = rec(); | |||
memconcept::is(r, memconcept::concept::btreebucket, "", 8192); | ||||
return (const BtreeBucket<V> *) r->data(); | ||||
} | } | |||
} // namespace mongo | } // namespace mongo | |||
#include "database.h" | #include "database.h" | |||
#include "memconcept.h" | #include "memconcept.h" | |||
namespace mongo { | namespace mongo { | |||
boost::intmax_t dbSize( const char *database ); | boost::intmax_t dbSize( const char *database ); | |||
inline NamespaceIndex* nsindex(const char *ns) { | inline NamespaceIndex* nsindex(const char *ns) { | |||
Database *database = cc().database(); | Database *database = cc().database(); | |||
assert( database ); | verify( database ); | |||
memconcept::is(database, memconcept::database, ns, sizeof(Database) | memconcept::is(database, memconcept::concept::database, ns, sizeof( | |||
); | Database)); | |||
DEV { | DEV { | |||
char buf[256]; | char buf[256]; | |||
nsToDatabase(ns, buf); | nsToDatabase(ns, buf); | |||
if ( database->name != buf ) { | if ( database->name != buf ) { | |||
out() << "ERROR: attempt to write to wrong database\n"; | out() << "ERROR: attempt to write to wrong database\n"; | |||
out() << " ns:" << ns << '\n'; | out() << " ns:" << ns << '\n'; | |||
out() << " database->name:" << database->name << endl; | out() << " database->name:" << database->name << endl; | |||
assert( database->name == buf ); | verify( database->name == buf ); | |||
} | } | |||
} | } | |||
return &database->namespaceIndex; | return &database->namespaceIndex; | |||
} | } | |||
inline NamespaceDetails* nsdetails(const char *ns) { | inline NamespaceDetails* nsdetails(const char *ns) { | |||
// if this faults, did you set the current db first? (Client::Cont ext + dblock) | // if this faults, did you set the current db first? (Client::Cont ext + dblock) | |||
NamespaceDetails *d = nsindex(ns)->details(ns); | NamespaceDetails *d = nsindex(ns)->details(ns); | |||
if( d ) { | if( d ) { | |||
memconcept::is(d, memconcept::nsdetails, ns, sizeof(NamespaceDe tails)); | memconcept::is(d, memconcept::concept::nsdetails, ns, sizeof(Na mespaceDetails)); | |||
} | } | |||
return d; | return d; | |||
} | } | |||
inline Extent* DataFileMgr::getExtent(const DiskLoc& dl) { | inline Extent* DataFileMgr::getExtent(const DiskLoc& dl) { | |||
assert( dl.a() != -1 ); | verify( dl.a() != -1 ); | |||
return cc().database()->getFile(dl.a())->getExtent(dl); | return cc().database()->getFile(dl.a())->getExtent(dl); | |||
} | } | |||
inline Record* DataFileMgr::getRecord(const DiskLoc& dl) { | inline Record* DataFileMgr::getRecord(const DiskLoc& dl) { | |||
assert( dl.a() != -1 ); | verify( dl.a() != -1 ); | |||
Record* r = cc().database()->getFile(dl.a())->recordAt(dl); | Record* r = cc().database()->getFile(dl.a())->recordAt(dl); | |||
return r; | return r; | |||
} | } | |||
BOOST_STATIC_ASSERT( 16 == sizeof(DeletedRecord) ); | BOOST_STATIC_ASSERT( 16 == sizeof(DeletedRecord) ); | |||
inline DeletedRecord* DataFileMgr::makeDeletedRecord(const DiskLoc& dl, int len) { | inline DeletedRecord* DataFileMgr::makeDeletedRecord(const DiskLoc& dl, int len) { | |||
assert( dl.a() != -1 ); | verify( dl.a() != -1 ); | |||
return (DeletedRecord*) cc().database()->getFile(dl.a())->makeRecor d(dl, sizeof(DeletedRecord)); | return (DeletedRecord*) cc().database()->getFile(dl.a())->makeRecor d(dl, sizeof(DeletedRecord)); | |||
} | } | |||
void ensureHaveIdIndex(const char *ns); | void ensureHaveIdIndex(const char *ns); | |||
bool dropIndexes( NamespaceDetails *d, const char *ns, const char *name , string &errmsg, BSONObjBuilder &anObjBuilder, bool maydeleteIdIndex ); | bool dropIndexes( NamespaceDetails *d, const char *ns, const char *name , string &errmsg, BSONObjBuilder &anObjBuilder, bool maydeleteIdIndex ); | |||
inline BSONObj::BSONObj(const Record *r) { | inline BSONObj::BSONObj(const Record *r) { | |||
init(r->data); | init(r->data()); | |||
} | } | |||
} // namespace mongo | } // namespace mongo | |||
End of changes. 39 change blocks. | ||||
60 lines changed or deleted | 119 lines changed or added | |||
pipeline_d.h | pipeline_d.h | |||
---|---|---|---|---|
skipping to change at line 50 | skipping to change at line 50 | |||
will feed the execution of the pipeline. | will feed the execution of the pipeline. | |||
This method looks for early pipeline stages that can be folded i nto | This method looks for early pipeline stages that can be folded i nto | |||
the underlying cursor, and when a cursor can absorb those, they | the underlying cursor, and when a cursor can absorb those, they | |||
are removed from the head of the pipeline. For example, an | are removed from the head of the pipeline. For example, an | |||
early match can be removed and replaced with a Cursor that will | early match can be removed and replaced with a Cursor that will | |||
do an index scan. | do an index scan. | |||
@param pPipeline the logical "this" for this operation | @param pPipeline the logical "this" for this operation | |||
@param dbName the name of the database | @param dbName the name of the database | |||
@param pExpCtx the expression context for this pipeline | ||||
@returns a document source that wraps an appropriate cursor to | @returns a document source that wraps an appropriate cursor to | |||
be at the beginning of this pipeline | be at the beginning of this pipeline | |||
*/ | */ | |||
static intrusive_ptr<DocumentSource> prepareCursorSource( | static intrusive_ptr<DocumentSource> prepareCursorSource( | |||
const intrusive_ptr<Pipeline> &pPipeline, | const intrusive_ptr<Pipeline> &pPipeline, | |||
const string &dbName); | const string &dbName, | |||
const intrusive_ptr<ExpressionContext> &pExpCtx); | ||||
private: | private: | |||
PipelineD(); // does not exist: prevent instantiation | PipelineD(); // does not exist: prevent instantiation | |||
}; | }; | |||
} // namespace mongo | } // namespace mongo | |||
End of changes. 2 change blocks. | ||||
1 lines changed or deleted | 3 lines changed or added | |||
processinfo.h | processinfo.h | |||
---|---|---|---|---|
skipping to change at line 30 | skipping to change at line 30 | |||
#include <sys/types.h> | #include <sys/types.h> | |||
#include <string> | #include <string> | |||
#ifndef _WIN32 | #ifndef _WIN32 | |||
#include <unistd.h> | #include <unistd.h> | |||
#else | #else | |||
typedef int pid_t; | typedef int pid_t; | |||
int getpid(); | int getpid(); | |||
#endif | #endif | |||
namespace mongo { | #include <db/jsobj.h> | |||
class BSONObjBuilder; | namespace mongo { | |||
class ProcessInfo { | class ProcessInfo { | |||
public: | public: | |||
ProcessInfo( pid_t pid = getpid() ); | ProcessInfo( pid_t pid = getpid() ); | |||
~ProcessInfo(); | ~ProcessInfo(); | |||
/** | /** | |||
* @return mbytes | * @return mbytes | |||
*/ | */ | |||
int getVirtualMemorySize(); | int getVirtualMemorySize(); | |||
/** | /** | |||
* @return mbytes | * @return mbytes | |||
*/ | */ | |||
int getResidentSize(); | int getResidentSize(); | |||
/** | /** | |||
* Get the type of os (e.g. Windows, Linux, Mac OS) | ||||
*/ | ||||
const string& getOsType() const { return sysInfo().osType; } | ||||
/** | ||||
* Get the os Name (e.g. Ubuntu, Gentoo, Windows Server 2008) | ||||
*/ | ||||
const string& getOsName() const { return sysInfo().osName; } | ||||
/** | ||||
* Get the os version (e.g. 10.04, 11.3.0, 6.1 (build 7600)) | ||||
*/ | ||||
const string& getOsVersion() const { return sysInfo().osVersion; } | ||||
/** | ||||
* Get the cpu address size (e.g. 32, 36, 64) | ||||
*/ | ||||
const unsigned getAddrSize() const { return sysInfo().addrSize; } | ||||
/** | ||||
* Get the total amount of system memory in MB | ||||
*/ | ||||
const unsigned long long getMemSizeMB() const { return sysInfo().me | ||||
mSize / (1024 * 1024); } | ||||
/** | ||||
* Get the number of CPUs | ||||
*/ | ||||
const unsigned getNumCores() const { return sysInfo().numCores; } | ||||
/** | ||||
* Get the CPU architecture (e.g. x86, x86_64) | ||||
*/ | ||||
const string& getArch() const { return sysInfo().cpuArch; } | ||||
/** | ||||
* Determine if NUMA is enabled (interleaved) for this process | ||||
*/ | ||||
bool hasNumaEnabled() const { return sysInfo().hasNuma; } | ||||
/** | ||||
* Get extra system stats | ||||
*/ | ||||
void appendSystemDetails( BSONObjBuilder& details ) const { | ||||
details.append( StringData("extra"), sysInfo()._extraStats.copy | ||||
() ); | ||||
} | ||||
/** | ||||
* Append platform-specific data to obj | * Append platform-specific data to obj | |||
*/ | */ | |||
void getExtraInfo(BSONObjBuilder& info); | void getExtraInfo( BSONObjBuilder& info ); | |||
bool supported(); | bool supported(); | |||
static bool blockCheckSupported(); | static bool blockCheckSupported(); | |||
static bool blockInMemory( char * start ); | static bool blockInMemory( char * start ); | |||
private: | private: | |||
/** | ||||
* Host and operating system info. Does not change over time. | ||||
*/ | ||||
class SystemInfo { | ||||
public: | ||||
string osType; | ||||
string osName; | ||||
string osVersion; | ||||
unsigned addrSize; | ||||
unsigned long long memSize; | ||||
unsigned numCores; | ||||
string cpuArch; | ||||
bool hasNuma; | ||||
BSONObj _extraStats; | ||||
SystemInfo() : | ||||
addrSize( 0 ), | ||||
memSize( 0 ), | ||||
numCores( 0 ), | ||||
hasNuma( false ) { | ||||
// populate SystemInfo during construction | ||||
collectSystemInfo(); | ||||
} | ||||
private: | ||||
/** Collect host system info */ | ||||
void collectSystemInfo(); | ||||
}; | ||||
pid_t _pid; | pid_t _pid; | |||
static mongo::mutex _sysInfoLock; | ||||
static bool checkNumaEnabled(); | ||||
const SystemInfo& sysInfo() const { | ||||
// initialize and collect sysInfo on first call | ||||
// TODO: SERVER-5112 | ||||
static ProcessInfo::SystemInfo *initSysInfo = NULL; | ||||
if ( ! initSysInfo ) { | ||||
scoped_lock lk( _sysInfoLock ); | ||||
if ( ! initSysInfo ) { | ||||
initSysInfo = new SystemInfo(); | ||||
} | ||||
} | ||||
return *initSysInfo; | ||||
} | ||||
}; | }; | |||
void writePidFile( const std::string& path ); | void writePidFile( const std::string& path ); | |||
void printMemInfo( const char * whereContextStr = 0 ); | void printMemInfo( const char * whereContextStr = 0 ); | |||
} | } | |||
End of changes. 7 change blocks. | ||||
3 lines changed or deleted | 97 lines changed or added | |||
query.h | query.h | |||
---|---|---|---|---|
skipping to change at line 26 | skipping to change at line 26 | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../../pch.h" | #include "../../pch.h" | |||
#include "../../util/net/message.h" | #include "../../util/net/message.h" | |||
#include "../dbmessage.h" | #include "../dbmessage.h" | |||
#include "../jsobj.h" | #include "../jsobj.h" | |||
#include "../diskloc.h" | #include "../diskloc.h" | |||
#include "../projection.h" | #include "../explain.h" | |||
#include "../../s/d_chunk_manager.h" | ||||
// struct QueryOptions, QueryResult, QueryResultFlags in: | // struct QueryOptions, QueryResult, QueryResultFlags in: | |||
#include "../../client/dbclient.h" | ||||
namespace mongo { | namespace mongo { | |||
extern const int MaxBytesToReturnToClientAtOnce; | class ParsedQuery; | |||
class QueryOptimizerCursor; | ||||
class QueryPlanSummary; | ||||
QueryResult* processGetMore(const char *ns, int ntoreturn, long long cu rsorid , CurOp& op, int pass, bool& exhaust); | QueryResult* processGetMore(const char *ns, int ntoreturn, long long cu rsorid , CurOp& op, int pass, bool& exhaust); | |||
const char * runQuery(Message& m, QueryMessage& q, CurOp& curop, Messag e &result); | const char * runQuery(Message& m, QueryMessage& q, CurOp& curop, Messag e &result); | |||
/* This is for languages whose "objects" are not well ordered (JSON is | /** Exception indicating that a query should be retried from the beginn | |||
well ordered). | ing. */ | |||
[ { a : ... } , { b : ... } ] -> { a : ..., b : ... } | class QueryRetryException : public DBException { | |||
*/ | public: | |||
inline BSONObj transformOrderFromArrayFormat(BSONObj order) { | QueryRetryException() : DBException( "query retry exception" , 1608 | |||
/* note: this is slow, but that is ok as order will have very few p | 3 ) { | |||
ieces */ | return; | |||
BSONObjBuilder b; | massert( 16083, "reserve 16083", true ); // Reserve 16083. | |||
char p[2] = "0"; | ||||
while ( 1 ) { | ||||
BSONObj j = order.getObjectField(p); | ||||
if ( j.isEmpty() ) | ||||
break; | ||||
BSONElement e = j.firstElement(); | ||||
uassert( 10102 , "bad order array", !e.eoo()); | ||||
uassert( 10103 , "bad order array [2]", e.isNumber()); | ||||
b.append(e); | ||||
(*p)++; | ||||
uassert( 10104 , "too many ordering elements", *p <= '9'); | ||||
} | } | |||
}; | ||||
return b.obj(); | /** Interface for recording events that contribute to explain results. | |||
} | */ | |||
class ExplainRecordingStrategy { | ||||
/** | ||||
* this represents a total user query | ||||
* includes fields from the query message, both possible query levels | ||||
* parses everything up front | ||||
*/ | ||||
class ParsedQuery : boost::noncopyable { | ||||
public: | public: | |||
ParsedQuery( QueryMessage& qm ) | ExplainRecordingStrategy( const ExplainQueryInfo::AncillaryInfo &an | |||
: _ns( qm.ns ) , _ntoskip( qm.ntoskip ) , _ntoreturn( qm.ntoret | cillaryInfo ); | |||
urn ) , _options( qm.queryOptions ) { | virtual ~ExplainRecordingStrategy() {} | |||
init( qm.query ); | /** Note information about a single query plan. */ | |||
initFields( qm.fields ); | virtual void notePlan( bool scanAndOrder, bool indexOnly ) {} | |||
} | /** Note an iteration of the query. */ | |||
ParsedQuery( const char* ns , int ntoskip , int ntoreturn , int que | virtual void noteIterate( bool match, bool orderedMatch, bool loade | |||
ryoptions , const BSONObj& query , const BSONObj& fields ) | dRecord, | |||
: _ns( ns ) , _ntoskip( ntoskip ) , _ntoreturn( ntoreturn ) , _ | bool chunkSkip ) {} | |||
options( queryoptions ) { | /** Note that the query yielded. */ | |||
init( query ); | virtual void noteYield() {} | |||
initFields( fields ); | /** @return number of ordered matches noted. */ | |||
} | virtual long long orderedMatches() const { return 0; } | |||
/** @return ExplainQueryInfo for a complete query. */ | ||||
shared_ptr<ExplainQueryInfo> doneQueryInfo(); | ||||
protected: | ||||
/** @return ExplainQueryInfo for a complete query, to be implemente | ||||
d by subclass. */ | ||||
virtual shared_ptr<ExplainQueryInfo> _doneQueryInfo() = 0; | ||||
private: | ||||
ExplainQueryInfo::AncillaryInfo _ancillaryInfo; | ||||
}; | ||||
const char * ns() const { return _ns; } | /** No explain events are recorded. */ | |||
bool isLocalDB() const { return strncmp(_ns, "local.", 6) == 0; } | class NoExplainStrategy : public ExplainRecordingStrategy { | |||
public: | ||||
NoExplainStrategy(); | ||||
private: | ||||
/** @asserts always. */ | ||||
virtual shared_ptr<ExplainQueryInfo> _doneQueryInfo(); | ||||
}; | ||||
const BSONObj& getFilter() const { return _filter; } | class MatchCountingExplainStrategy : public ExplainRecordingStrategy { | |||
Projection* getFields() const { return _fields.get(); } | public: | |||
shared_ptr<Projection> getFieldPtr() const { return _fields; } | MatchCountingExplainStrategy( const ExplainQueryInfo::AncillaryInfo | |||
&ancillaryInfo ); | ||||
int getSkip() const { return _ntoskip; } | protected: | |||
int getNumToReturn() const { return _ntoreturn; } | virtual void _noteIterate( bool match, bool orderedMatch, bool load | |||
bool wantMore() const { return _wantMore; } | edRecord, | |||
int getOptions() const { return _options; } | bool chunkSkip ) = 0; | |||
bool hasOption( int x ) const { return x & _options; } | private: | |||
virtual void noteIterate( bool match, bool orderedMatch, bool loade | ||||
bool isExplain() const { return _explain; } | dRecord, | |||
bool isSnapshot() const { return _snapshot; } | bool chunkSkip ); | |||
bool returnKey() const { return _returnKey; } | virtual long long orderedMatches() const { return _orderedMatches; | |||
bool showDiskLoc() const { return _showDiskLoc; } | } | |||
long long _orderedMatches; | ||||
const BSONObj& getMin() const { return _min; } | }; | |||
const BSONObj& getMax() const { return _max; } | ||||
const BSONObj& getOrder() const { return _order; } | ||||
const BSONElement& getHint() const { return _hint; } | ||||
int getMaxScan() const { return _maxScan; } | ||||
bool couldBeCommand() const { | ||||
/* we assume you are using findOne() for running a cmd... */ | ||||
return _ntoreturn == 1 && strstr( _ns , ".$cmd" ); | ||||
} | ||||
bool hasIndexSpecifier() const { | /** Record explain events for a simple cursor representing a single cla | |||
return ! _hint.eoo() || ! _min.isEmpty() || ! _max.isEmpty(); | use and plan. */ | |||
} | class SimpleCursorExplainStrategy : public MatchCountingExplainStrategy | |||
{ | ||||
public: | ||||
SimpleCursorExplainStrategy( const ExplainQueryInfo::AncillaryInfo | ||||
&ancillaryInfo, | ||||
const shared_ptr<Cursor> &cursor ); | ||||
private: | ||||
virtual void notePlan( bool scanAndOrder, bool indexOnly ); | ||||
virtual void _noteIterate( bool match, bool orderedMatch, bool load | ||||
edRecord, | ||||
bool chunkSkip ); | ||||
virtual void noteYield(); | ||||
virtual shared_ptr<ExplainQueryInfo> _doneQueryInfo(); | ||||
shared_ptr<Cursor> _cursor; | ||||
shared_ptr<ExplainSinglePlanQueryInfo> _explainInfo; | ||||
}; | ||||
/* if ntoreturn is zero, we return up to 101 objects. on the subse | /** | |||
quent getmore, there | * Record explain events for a QueryOptimizerCursor, which may record s | |||
is only a size limit. The idea is that on a find() where one do | ome explain information | |||
esn't use much results, | * for multiple clauses and plans through an internal implementation. | |||
we don't return much, but once getmore kicks in, we start pushin | */ | |||
g significant quantities. | class QueryOptimizerCursorExplainStrategy : public MatchCountingExplain | |||
Strategy { | ||||
The n limit (vs. size) is important when someone fetches only on | public: | |||
e small field from big | QueryOptimizerCursorExplainStrategy( const ExplainQueryInfo::Ancill | |||
objects, which causes massive scanning server-side. | aryInfo &ancillaryInfo, | |||
*/ | const shared_ptr<QueryOptimizer | |||
bool enoughForFirstBatch( int n , int len ) const { | Cursor> &cursor ); | |||
if ( _ntoreturn == 0 ) | private: | |||
return ( len > 1024 * 1024 ) || n >= 101; | virtual void _noteIterate( bool match, bool orderedMatch, bool load | |||
return n >= _ntoreturn || len > MaxBytesToReturnToClientAtOnce; | edRecord, | |||
} | bool chunkSkip ); | |||
virtual shared_ptr<ExplainQueryInfo> _doneQueryInfo(); | ||||
shared_ptr<QueryOptimizerCursor> _cursor; | ||||
}; | ||||
bool enough( int n ) const { | /** Interface for building a query response in a supplied BufBuilder. * | |||
if ( _ntoreturn == 0 ) | / | |||
return false; | class ResponseBuildStrategy { | |||
return n >= _ntoreturn; | public: | |||
} | /** | |||
* @param queryPlan must be supplied if @param cursor is not a Quer | ||||
yOptimizerCursor and | ||||
* results must be sorted or read with a covered index. | ||||
*/ | ||||
ResponseBuildStrategy( const ParsedQuery &parsedQuery, const shared | ||||
_ptr<Cursor> &cursor, | ||||
BufBuilder &buf, const QueryPlanSummary &quer | ||||
yPlan ); | ||||
virtual ~ResponseBuildStrategy() {} | ||||
/** | ||||
* Handle the current iterate of the supplied cursor as a (possibly | ||||
duplicate) match. | ||||
* @return true if a match is found. | ||||
* @param orderedMatch set if it is an ordered match. | ||||
*/ | ||||
virtual bool handleMatch( bool &orderedMatch ) = 0; | ||||
/** | ||||
* Write all matches into the buffer, overwriting existing data. | ||||
* @return number of matches written, or -1 if no op. | ||||
*/ | ||||
virtual int rewriteMatches() { return -1; } | ||||
/** @return the number of matches that have been written to the buf | ||||
fer. */ | ||||
virtual int bufferedMatches() const = 0; | ||||
/** | ||||
* Callback when enough results have been read for the first batch, | ||||
with potential handoff | ||||
* to getMore. | ||||
*/ | ||||
virtual void finishedFirstBatch() {} | ||||
/** Reset the buffer. */ | ||||
void resetBuf(); | ||||
protected: | ||||
/** | ||||
* Return the document for the current iterate. Implements the $re | ||||
turnKey option. | ||||
* @param allowCovered - enable covered index support. | ||||
*/ | ||||
BSONObj current( bool allowCovered ) const; | ||||
const ParsedQuery &_parsedQuery; | ||||
shared_ptr<Cursor> _cursor; | ||||
shared_ptr<QueryOptimizerCursor> _queryOptimizerCursor; | ||||
BufBuilder &_buf; | ||||
}; | ||||
/** Build strategy for a cursor returning in order results. */ | ||||
class OrderedBuildStrategy : public ResponseBuildStrategy { | ||||
public: | ||||
OrderedBuildStrategy( const ParsedQuery &parsedQuery, const shared_ | ||||
ptr<Cursor> &cursor, | ||||
BufBuilder &buf, const QueryPlanSummary &query | ||||
Plan ); | ||||
virtual bool handleMatch( bool &orderedMatch ); | ||||
virtual int bufferedMatches() const { return _bufferedMatches; } | ||||
private: | private: | |||
void init( const BSONObj& q ) { | int _skip; | |||
_reset(); | int _bufferedMatches; | |||
uassert( 10105 , "bad skip value in query", _ntoskip >= 0); | }; | |||
if ( _ntoreturn < 0 ) { | ||||
/* _ntoreturn greater than zero is simply a hint on how man | ||||
y objects to send back per | ||||
"cursor batch". | ||||
A negative number indicates a hard limit. | ||||
*/ | ||||
_wantMore = false; | ||||
_ntoreturn = -_ntoreturn; | ||||
} | ||||
BSONElement e = q["query"]; | ||||
if ( ! e.isABSONObj() ) | ||||
e = q["$query"]; | ||||
if ( e.isABSONObj() ) { | ||||
_filter = e.embeddedObject(); | ||||
_initTop( q ); | ||||
} | ||||
else { | ||||
_filter = q; | ||||
} | ||||
} | ||||
void _reset() { | class ScanAndOrder; | |||
_wantMore = true; | ||||
_explain = false; | ||||
_snapshot = false; | ||||
_returnKey = false; | ||||
_showDiskLoc = false; | ||||
_maxScan = 0; | ||||
} | ||||
void _initTop( const BSONObj& top ) { | /** Build strategy for a cursor returning out of order results. */ | |||
BSONObjIterator i( top ); | class ReorderBuildStrategy : public ResponseBuildStrategy { | |||
while ( i.more() ) { | public: | |||
BSONElement e = i.next(); | ReorderBuildStrategy( const ParsedQuery &parsedQuery, | |||
const char * name = e.fieldName(); | const shared_ptr<Cursor> &cursor, | |||
BufBuilder &buf, | ||||
if ( strcmp( "$orderby" , name ) == 0 || | const QueryPlanSummary &queryPlan ); | |||
strcmp( "orderby" , name ) == 0 ) { | virtual bool handleMatch( bool &orderedMatch ); | |||
if ( e.type() == Object ) { | /** Handle a match without performing deduping. */ | |||
_order = e.embeddedObject(); | void _handleMatchNoDedup(); | |||
} | virtual int rewriteMatches(); | |||
else if ( e.type() == Array ) { | virtual int bufferedMatches() const { return _bufferedMatches; } | |||
_order = transformOrderFromArrayFormat( _order ); | private: | |||
} | ScanAndOrder *newScanAndOrder( const QueryPlanSummary &queryPlan ) | |||
else { | const; | |||
uasserted(13513, "sort must be an object or array") | shared_ptr<ScanAndOrder> _scanAndOrder; | |||
; | int _bufferedMatches; | |||
} | }; | |||
continue; | ||||
} | ||||
if( *name == '$' ) { | ||||
name++; | ||||
if ( strcmp( "explain" , name ) == 0 ) | ||||
_explain = e.trueValue(); | ||||
else if ( strcmp( "snapshot" , name ) == 0 ) | ||||
_snapshot = e.trueValue(); | ||||
else if ( strcmp( "min" , name ) == 0 ) | ||||
_min = e.embeddedObject(); | ||||
else if ( strcmp( "max" , name ) == 0 ) | ||||
_max = e.embeddedObject(); | ||||
else if ( strcmp( "hint" , name ) == 0 ) | ||||
_hint = e; | ||||
else if ( strcmp( "returnKey" , name ) == 0 ) | ||||
_returnKey = e.trueValue(); | ||||
else if ( strcmp( "maxScan" , name ) == 0 ) | ||||
_maxScan = e.numberInt(); | ||||
else if ( strcmp( "showDiskLoc" , name ) == 0 ) | ||||
_showDiskLoc = e.trueValue(); | ||||
else if ( strcmp( "comment" , name ) == 0 ) { | ||||
; // no-op | ||||
} | ||||
} | ||||
} | ||||
if ( _snapshot ) { | ||||
uassert( 12001 , "E12001 can't sort with $snapshot", _order | ||||
.isEmpty() ); | ||||
uassert( 12002 , "E12002 can't use hint with $snapshot", _h | ||||
int.eoo() ); | ||||
} | ||||
/** Helper class for deduping DiskLocs */ | ||||
class DiskLocDupSet { | ||||
public: | ||||
/** @return true if dup, otherwise return false and insert. */ | ||||
bool getsetdup( const DiskLoc &loc ) { | ||||
pair<set<DiskLoc>::iterator, bool> p = _dups.insert(loc); | ||||
return !p.second; | ||||
} | } | |||
private: | ||||
set<DiskLoc> _dups; | ||||
}; | ||||
void initFields( const BSONObj& fields ) { | /** | |||
if ( fields.isEmpty() ) | * Build strategy for a QueryOptimizerCursor containing some in order a | |||
return; | nd some out of order | |||
_fields.reset( new Projection() ); | * candidate plans. | |||
_fields->init( fields ); | */ | |||
} | class HybridBuildStrategy : public ResponseBuildStrategy { | |||
public: | ||||
HybridBuildStrategy( const ParsedQuery &parsedQuery, | ||||
const shared_ptr<QueryOptimizerCursor> &cursor, | ||||
BufBuilder &buf ); | ||||
private: | ||||
virtual bool handleMatch( bool &orderedMatch ); | ||||
virtual int rewriteMatches(); | ||||
virtual int bufferedMatches() const; | ||||
virtual void finishedFirstBatch(); | ||||
bool handleReorderMatch(); | ||||
DiskLocDupSet _scanAndOrderDups; | ||||
OrderedBuildStrategy _orderedBuild; | ||||
ReorderBuildStrategy _reorderBuild; | ||||
bool _reorderedMatches; | ||||
}; | ||||
const char * const _ns; | /** | |||
const int _ntoskip; | * Builds a query response with the help of an ExplainRecordingStrategy | |||
int _ntoreturn; | and a | |||
BSONObj _filter; | * ResponseBuildStrategy. | |||
BSONObj _order; | */ | |||
const int _options; | class QueryResponseBuilder { | |||
shared_ptr< Projection > _fields; | public: | |||
bool _wantMore; | /** | |||
bool _explain; | * @param queryPlan must be supplied if @param cursor is not a Quer | |||
bool _snapshot; | yOptimizerCursor and | |||
bool _returnKey; | * results must be sorted or read with a covered index. | |||
bool _showDiskLoc; | */ | |||
BSONObj _min; | QueryResponseBuilder( const ParsedQuery &parsedQuery, const shared_ | |||
BSONObj _max; | ptr<Cursor> &cursor, | |||
BSONElement _hint; | const QueryPlanSummary &queryPlan, const BSONO | |||
int _maxScan; | bj &oldPlan ); | |||
/** @return true if the current iterate matches and is added. */ | ||||
bool addMatch(); | ||||
/** Note that a yield occurred. */ | ||||
void noteYield(); | ||||
/** @return true if there are enough results to return the first ba | ||||
tch. */ | ||||
bool enoughForFirstBatch() const; | ||||
/** @return true if there are enough results to return the full res | ||||
ult set. */ | ||||
bool enoughTotalResults() const; | ||||
/** | ||||
* Callback when enough results have been read for the first batch, | ||||
with potential handoff | ||||
* to getMore. | ||||
*/ | ||||
void finishedFirstBatch(); | ||||
/** | ||||
* Set the data portion of the supplied Message to a buffer contain | ||||
ing the query results. | ||||
* @return the number of results in the buffer. | ||||
*/ | ||||
int handoff( Message &result ); | ||||
/** A chunk manager found at the beginning of the query. */ | ||||
ShardChunkManagerPtr chunkManager() const { return _chunkManager; } | ||||
private: | ||||
ShardChunkManagerPtr newChunkManager() const; | ||||
shared_ptr<ExplainRecordingStrategy> newExplainRecordingStrategy | ||||
( const QueryPlanSummary &queryPlan, const BSONObj &oldPlan ) const | ||||
; | ||||
shared_ptr<ResponseBuildStrategy> newResponseBuildStrategy | ||||
( const QueryPlanSummary &queryPlan ); | ||||
bool currentMatches(); | ||||
bool chunkMatches(); | ||||
const ParsedQuery &_parsedQuery; | ||||
shared_ptr<Cursor> _cursor; | ||||
shared_ptr<QueryOptimizerCursor> _queryOptimizerCursor; | ||||
BufBuilder _buf; | ||||
ShardChunkManagerPtr _chunkManager; | ||||
shared_ptr<ExplainRecordingStrategy> _explain; | ||||
shared_ptr<ResponseBuildStrategy> _builder; | ||||
}; | }; | |||
} // namespace mongo | } // namespace mongo | |||
End of changes. 20 change blocks. | ||||
203 lines changed or deleted | 267 lines changed or added | |||
queryoptimizer.h | queryoptimizer.h | |||
---|---|---|---|---|
skipping to change at line 22 | skipping to change at line 22 | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Affero General Public License for more details. | * GNU Affero General Public License for more details. | |||
* | * | |||
* You should have received a copy of the GNU Affero General Public 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 "queryutil.h" | #include "queryutil.h" | |||
#include "matcher.h" | #include "matcher.h" | |||
#include "explain.h" | ||||
#include "../util/net/listen.h" | #include "../util/net/listen.h" | |||
#include <queue> | #include "mongo/db/querypattern.h" | |||
namespace mongo { | namespace mongo { | |||
class IndexDetails; | class IndexDetails; | |||
class IndexType; | class IndexType; | |||
class ElapsedTracker; | class QueryPlanSummary; | |||
/** A plan for executing a query using the given index spec and FieldRa ngeSet. */ | /** A plan for executing a query using the given index spec and FieldRa ngeSet. */ | |||
class QueryPlan : boost::noncopyable { | class QueryPlan : boost::noncopyable { | |||
public: | public: | |||
/** | /** | |||
* @param originalFrsp - original constraints for this query clause . If null, frsp will be used instead. | * @param originalFrsp - original constraints for this query clause . If null, frsp will be used instead. | |||
*/ | */ | |||
QueryPlan(NamespaceDetails *d, | QueryPlan(NamespaceDetails *d, | |||
int idxNo, // -1 = no index | int idxNo, // -1 = no index | |||
const FieldRangeSetPair &frsp, | const FieldRangeSetPair &frsp, | |||
const FieldRangeSetPair *originalFrsp, | const FieldRangeSetPair *originalFrsp, | |||
const BSONObj &originalQuery, | const BSONObj &originalQuery, | |||
const BSONObj &order, | const BSONObj &order, | |||
bool mustAssertOnYieldFailure = true, | const shared_ptr<const ParsedQuery> &parsedQuery = | |||
shared_ptr<const ParsedQuery>(), | ||||
const BSONObj &startKey = BSONObj(), | const BSONObj &startKey = BSONObj(), | |||
const BSONObj &endKey = BSONObj(), | const BSONObj &endKey = BSONObj(), | |||
string special="" ); | string special="" ); | |||
/** @return true iff this plan cannot return any documents. */ | /** @return true iff this plan cannot return any documents. */ | |||
bool impossible() const { return _impossible; } | bool impossible() const { return _impossible; } | |||
/** | /** | |||
* @return true iff this plan should run as the only candidate plan in the absence of an | * @return true iff this plan should run as the only candidate plan in the absence of an | |||
* impossible plan. | * impossible plan. | |||
*/ | */ | |||
skipping to change at line 74 | skipping to change at line 75 | |||
* @return true iff the index we are using has keys such that it ca n completely resolve the | * @return true iff the index we are using has keys such that it ca n completely resolve the | |||
* query expression to match by itself without ever checking the ma in object. | * query expression to match by itself without ever checking the ma in object. | |||
*/ | */ | |||
bool exactKeyMatch() const { return _exactKeyMatch; } | bool exactKeyMatch() const { return _exactKeyMatch; } | |||
/** @return true iff this QueryPlan would perform an unindexed scan . */ | /** @return true iff this QueryPlan would perform an unindexed scan . */ | |||
bool willScanTable() const { return _idxNo < 0 && !_impossible; } | bool willScanTable() const { return _idxNo < 0 && !_impossible; } | |||
/** @return 'special' attribute of the plan, which was either set e xplicitly or generated from the index. */ | /** @return 'special' attribute of the plan, which was either set e xplicitly or generated from the index. */ | |||
const string &special() const { return _special; } | const string &special() const { return _special; } | |||
/** @return a new cursor based on this QueryPlan's index and FieldR angeSet. */ | /** @return a new cursor based on this QueryPlan's index and FieldR angeSet. */ | |||
shared_ptr<Cursor> newCursor( const DiskLoc &startLoc = DiskLoc() , int numWanted=0 ) const; | shared_ptr<Cursor> newCursor( const DiskLoc &startLoc = DiskLoc() ) const; | |||
/** @return a new reverse cursor if this is an unindexed plan. */ | /** @return a new reverse cursor if this is an unindexed plan. */ | |||
shared_ptr<Cursor> newReverseCursor() const; | shared_ptr<Cursor> newReverseCursor() const; | |||
/** Register this plan as a winner for its QueryPattern, with speci fied 'nscanned'. */ | /** Register this plan as a winner for its QueryPattern, with speci fied 'nscanned'. */ | |||
void registerSelf( long long nScanned ) const; | void registerSelf( long long nScanned, CandidatePlanCharacter candi datePlans ) const; | |||
int direction() const { return _direction; } | int direction() const { return _direction; } | |||
BSONObj indexKey() const; | BSONObj indexKey() const; | |||
bool indexed() const { return _index; } | bool indexed() const { return _index != 0; } | |||
const IndexDetails *index() const { return _index; } | ||||
int idxNo() const { return _idxNo; } | int idxNo() const { return _idxNo; } | |||
const char *ns() const { return _frs.ns(); } | const char *ns() const { return _frs.ns(); } | |||
NamespaceDetails *nsd() const { return _d; } | NamespaceDetails *nsd() const { return _d; } | |||
BSONObj originalQuery() const { return _originalQuery; } | BSONObj originalQuery() const { return _originalQuery; } | |||
BSONObj simplifiedQuery( const BSONObj& fields = BSONObj() ) const | ||||
{ return _frs.simplifiedQuery( fields ); } | ||||
const FieldRange &range( const char *fieldName ) const { return _fr | ||||
s.range( fieldName ); } | ||||
shared_ptr<FieldRangeVector> originalFrv() const { return _original Frv; } | shared_ptr<FieldRangeVector> originalFrv() const { return _original Frv; } | |||
const FieldRangeSet &multikeyFrs() const { return _frsMulti; } | const FieldRangeSet &multikeyFrs() const { return _frsMulti; } | |||
bool mustAssertOnYieldFailure() const { return _mustAssertOnYieldFa ilure; } | shared_ptr<Projection::KeyOnly> keyFieldsOnly() const { return _key FieldsOnly; } | |||
/** The following member functions are just for testing. */ | QueryPlanSummary summary() const; | |||
/** The following member functions are for testing, or public for t | ||||
esting. */ | ||||
shared_ptr<FieldRangeVector> frv() const { return _frv; } | shared_ptr<FieldRangeVector> frv() const { return _frv; } | |||
bool isMultiKey() const; | bool isMultiKey() const; | |||
string toString() const; | ||||
bool queryFiniteSetOrderSuffix() const; | ||||
private: | private: | |||
void checkTableScanAllowed() const; | void checkTableScanAllowed() const; | |||
void warnOnCappedIdTableScan() const; | int independentRangesSingleIntervalLimit() const; | |||
NamespaceDetails * _d; | NamespaceDetails * _d; | |||
int _idxNo; | int _idxNo; | |||
const FieldRangeSet &_frs; | const FieldRangeSet &_frs; | |||
const FieldRangeSet &_frsMulti; | const FieldRangeSet &_frsMulti; | |||
const BSONObj &_originalQuery; | const BSONObj _originalQuery; | |||
const BSONObj &_order; | const BSONObj _order; | |||
shared_ptr<const ParsedQuery> _parsedQuery; | ||||
const IndexDetails * _index; | const IndexDetails * _index; | |||
bool _optimal; | bool _optimal; | |||
bool _scanAndOrderRequired; | bool _scanAndOrderRequired; | |||
bool _exactKeyMatch; | bool _exactKeyMatch; | |||
int _direction; | int _direction; | |||
shared_ptr<FieldRangeVector> _frv; | shared_ptr<FieldRangeVector> _frv; | |||
shared_ptr<FieldRangeVector> _originalFrv; | shared_ptr<FieldRangeVector> _originalFrv; | |||
BSONObj _startKey; | BSONObj _startKey; | |||
BSONObj _endKey; | BSONObj _endKey; | |||
bool _endKeyInclusive; | bool _endKeyInclusive; | |||
bool _unhelpful; | bool _unhelpful; | |||
bool _impossible; | bool _impossible; | |||
string _special; | string _special; | |||
IndexType * _type; | IndexType * _type; | |||
bool _startOrEndSpec; | bool _startOrEndSpec; | |||
bool _mustAssertOnYieldFailure; | shared_ptr<Projection::KeyOnly> _keyFieldsOnly; | |||
}; | ||||
/** | ||||
* A QueryPlanSummary owns its own attributes and may be shared. Curre | ||||
ntly a QueryPlan | ||||
* should only be owned by a QueryPlanSet. | ||||
*/ | ||||
class QueryPlanSummary { | ||||
public: | ||||
QueryPlanSummary() : | ||||
_scanAndOrderRequired() { | ||||
} | ||||
QueryPlanSummary( const QueryPlan &queryPlan ) : | ||||
_fieldRangeSetMulti( new FieldRangeSet( queryPlan.multikeyFrs() ) ) | ||||
, | ||||
_keyFieldsOnly( queryPlan.keyFieldsOnly() ), | ||||
_scanAndOrderRequired( queryPlan.scanAndOrderRequired() ) { | ||||
} | ||||
bool valid() const { return _fieldRangeSetMulti; } | ||||
shared_ptr<FieldRangeSet> _fieldRangeSetMulti; | ||||
shared_ptr<Projection::KeyOnly> _keyFieldsOnly; | ||||
bool _scanAndOrderRequired; | ||||
}; | }; | |||
/** | /** | |||
* 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. | |||
* | * | |||
* Normal sequence of events: | * Normal sequence of events: | |||
* 1) A new QueryOp is generated using createChild(). | * 1) A new QueryOp is generated using createChild(). | |||
* 2) A QueryPlan is assigned to this QueryOp with setQueryPlan(). | * 2) A QueryPlan is assigned to this QueryOp with setQueryPlan(). | |||
skipping to change at line 163 | skipping to change at line 188 | |||
const QueryPlan &qp() const { return *_qp; } | const QueryPlan &qp() const { return *_qp; } | |||
/** Advance to next potential matching document (eg using a cursor) . */ | /** Advance to next potential matching document (eg using a cursor) . */ | |||
virtual void next() = 0; | virtual void next() = 0; | |||
/** | /** | |||
* @return current 'nscanned' metric for this QueryOp. Used to com pare | * @return current 'nscanned' metric for this QueryOp. Used to com pare | |||
* cost to other QueryOps. | * cost to other QueryOps. | |||
*/ | */ | |||
virtual long long nscanned() = 0; | virtual long long nscanned() = 0; | |||
/** Take any steps necessary before the db mutex is yielded. */ | /** Take any steps necessary before the db mutex is yielded. */ | |||
virtual bool prepareToYield() { massert( 13335, "yield not supporte d", false ); return false; } | virtual void prepareToYield() = 0; | |||
/** Recover once the db mutex is regained. */ | /** Recover once the db mutex is regained. */ | |||
virtual void recoverFromYield() { massert( 13336, "yield not suppor ted", false ); } | virtual void recoverFromYield() = 0; | |||
/** | /** | |||
* @return true iff the QueryPlan for this QueryOp may be registere d | * @return true iff the QueryPlan for this QueryOp may be registere d | |||
* as a winning plan. | * as a winning plan. | |||
*/ | */ | |||
virtual bool mayRecordPlan() const = 0; | virtual bool mayRecordPlan() const = 0; | |||
/** @return true iff the implementation called setComplete() or set Stop(). */ | /** @return true iff the implementation called setComplete() or set Stop(). */ | |||
bool complete() const { return _complete; } | bool complete() const { return _complete; } | |||
/** @return true iff the implementation called steStop(). */ | /** @return true iff the implementation called steStop(). */ | |||
bool stopRequested() const { return _stopRequested; } | bool stopRequested() const { return _stopRequested; } | |||
bool completeWithoutStop() const { return complete() && !stopReques ted(); } | ||||
/** @return true iff the implementation threw an exception. */ | /** @return true iff the implementation threw an exception. */ | |||
bool error() const { return _error; } | bool error() const { return _error; } | |||
/** @return the exception thrown by implementation if one was throw n. */ | /** @return the exception thrown by implementation if one was throw n. */ | |||
ExceptionInfo exception() const { return _exception; } | ExceptionInfo exception() const { return _exception; } | |||
/** To be called by QueryPlanSet::Runner only. */ | /** To be called by QueryPlanSet::Runner only. */ | |||
QueryOp *createChild(); | QueryOp *createChild(); | |||
void setQueryPlan( const QueryPlan *qp ) { _qp = qp; assert( _qp != NULL ); } | void setQueryPlan( const QueryPlan *qp ) { _qp = qp; verify( _qp != NULL ); } | |||
void init(); | void init(); | |||
void setException( const DBException &e ) { | void setException( const DBException &e ) { | |||
_error = true; | _error = true; | |||
_exception = e.getInfo(); | _exception = e.getInfo(); | |||
} | } | |||
shared_ptr<CoveredIndexMatcher> matcher( const shared_ptr<Cursor>& c ) const { | shared_ptr<CoveredIndexMatcher> matcher( const shared_ptr<Cursor>& c ) const { | |||
return matcher( c.get() ); | return matcher( c.get() ); | |||
} | } | |||
shared_ptr<CoveredIndexMatcher> matcher( Cursor* c ) const { | shared_ptr<CoveredIndexMatcher> matcher( Cursor* c ) const { | |||
if( ! c ) return _matcher; | if( ! c ) return _matcher; | |||
return c->matcher() ? c->matcherPtr() : _matcher; | return c->matcher() ? c->matcherPtr() : _matcher; | |||
} | } | |||
/** @return an ExplainPlanInfo object that will be updated as the q | ||||
uery runs. */ | ||||
virtual shared_ptr<ExplainPlanInfo> generateExplainInfo() { | ||||
return shared_ptr<ExplainPlanInfo>( new ExplainPlanInfo() ); | ||||
} | ||||
protected: | protected: | |||
/** Call if all results have been found. */ | /** Call if all results have been found. */ | |||
void setComplete() { | void setComplete() { | |||
_orConstraint = qp().originalFrv(); | _orConstraint = qp().originalFrv(); | |||
_complete = true; | _complete = true; | |||
} | } | |||
/** Call if the scan is complete even if not all results have been found. */ | /** Call if the scan is complete even if not all results have been found. */ | |||
void setStop() { setComplete(); _stopRequested = true; } | void setStop() { setComplete(); _stopRequested = true; } | |||
/** Handle initialization after a QueryPlan has been set. */ | /** Handle initialization after a QueryPlan has been set. */ | |||
virtual void _init() = 0; | virtual void _init() = 0; | |||
/** @return a copy of the inheriting class, which will be run with its own query plan. */ | /** @return a copy of the inheriting class, which will be run with its own query plan. */ | |||
virtual QueryOp *_createChild() const = 0; | virtual QueryOp *_createChild() const = 0; | |||
virtual bool alwaysUseRecord() const { return false; } | ||||
private: | private: | |||
bool _complete; | bool _complete; | |||
bool _stopRequested; | bool _stopRequested; | |||
ExceptionInfo _exception; | ExceptionInfo _exception; | |||
const QueryPlan *_qp; | const QueryPlan *_qp; | |||
bool _error; | bool _error; | |||
shared_ptr<CoveredIndexMatcher> _matcher; | shared_ptr<CoveredIndexMatcher> _matcher; | |||
shared_ptr<CoveredIndexMatcher> _oldMatcher; | shared_ptr<CoveredIndexMatcher> _oldMatcher; | |||
shared_ptr<FieldRangeVector> _orConstraint; | shared_ptr<FieldRangeVector> _orConstraint; | |||
}; | }; | |||
skipping to change at line 255 | skipping to change at line 284 | |||
for( size_t i = 1; i < v.size(); i++ ) { | for( size_t i = 1; i < v.size(); i++ ) { | |||
if( v[t] < v[i] ) | if( v[t] < v[i] ) | |||
t = i; | t = i; | |||
} | } | |||
T ret = v[t]; | T ret = v[t]; | |||
v.erase(v.begin()+t); | v.erase(v.begin()+t); | |||
return ret; | return ret; | |||
} | } | |||
}; | }; | |||
class QueryPlanSet; | ||||
/** Populates a provided QueryPlanSet with candidate query plans, when | ||||
requested. */ | ||||
class QueryPlanGenerator { | ||||
public: | ||||
/** Policies for utilizing recorded plans. */ | ||||
typedef enum { | ||||
Ignore, // Ignore the recorded plan and try all candidate plans | ||||
. | ||||
UseIfInOrder, // Use the recorded plan if it is properly ordere | ||||
d. | ||||
Use // Always use the recorded plan. | ||||
} RecordedPlanPolicy; | ||||
/** @param qps The QueryPlanSet to which plans will be provided. */ | ||||
QueryPlanGenerator( QueryPlanSet &qps, | ||||
auto_ptr<FieldRangeSetPair> originalFrsp, | ||||
const shared_ptr<const ParsedQuery> &parsedQuery | ||||
, | ||||
const BSONObj &hint, | ||||
RecordedPlanPolicy recordedPlanPolicy, | ||||
const BSONObj &min, | ||||
const BSONObj &max ); | ||||
/** Populate the provided QueryPlanSet with an initial set of plans | ||||
. */ | ||||
void addInitialPlans(); | ||||
/** Supplement a cached plan provided earlier by adding additional | ||||
query plans. */ | ||||
void addFallbackPlans(); | ||||
private: | ||||
bool addShortCircuitPlan( NamespaceDetails *d ); | ||||
bool addHintPlan( NamespaceDetails *d ); | ||||
bool addSpecialPlan( NamespaceDetails *d ); | ||||
void addStandardPlans( NamespaceDetails *d ); | ||||
bool addCachedPlan( NamespaceDetails *d ); | ||||
shared_ptr<QueryPlan> newPlan( NamespaceDetails *d, | ||||
int idxNo, | ||||
const BSONObj &min = BSONObj(), | ||||
const BSONObj &max = BSONObj(), | ||||
const string &special = "" ) const; | ||||
bool setUnindexedPlanIf( bool set, NamespaceDetails *d ); | ||||
void setSingleUnindexedPlan( NamespaceDetails *d ); | ||||
void setHintedPlan( IndexDetails &id ); | ||||
void warnOnCappedIdTableScan() const; | ||||
QueryPlanSet &_qps; | ||||
auto_ptr<FieldRangeSetPair> _originalFrsp; | ||||
shared_ptr<const ParsedQuery> _parsedQuery; | ||||
BSONObj _hint; | ||||
RecordedPlanPolicy _recordedPlanPolicy; | ||||
BSONObj _min; | ||||
BSONObj _max; | ||||
}; | ||||
/** | /** | |||
* A set of candidate query plans for a query. This class can return a best buess plan or run a | * A set of candidate query plans for a query. This class can return a best guess plan or run a | |||
* QueryOp on all the plans. | * QueryOp on all the plans. | |||
*/ | */ | |||
class QueryPlanSet { | class QueryPlanSet { | |||
public: | public: | |||
typedef boost::shared_ptr<QueryPlan> QueryPlanPtr; | typedef boost::shared_ptr<QueryPlan> QueryPlanPtr; | |||
typedef vector<QueryPlanPtr> PlanSet; | typedef vector<QueryPlanPtr> PlanSet; | |||
/** | /** | |||
* @param originalFrsp - original constraints for this query clause | * @param originalFrsp - original constraints for this query clause | |||
; if null, frsp will be used. | ; if null, frsp will be | |||
* used. | ||||
*/ | */ | |||
QueryPlanSet( const char *ns, | QueryPlanSet( const char *ns, | |||
auto_ptr<FieldRangeSetPair> frsp, | auto_ptr<FieldRangeSetPair> frsp, | |||
auto_ptr<FieldRangeSetPair> originalFrsp, | auto_ptr<FieldRangeSetPair> originalFrsp, | |||
const BSONObj &originalQuery, | const BSONObj &originalQuery, | |||
const BSONObj &order, | const BSONObj &order, | |||
bool mustAssertOnYieldFailure = true, | const shared_ptr<const ParsedQuery> &parsedQuery = | |||
const BSONElement *hint = 0, | shared_ptr<const ParsedQuery>(), | |||
bool honorRecordedPlan = true, | const BSONObj &hint = BSONObj(), | |||
const BSONObj &min = BSONObj(), | QueryPlanGenerator::RecordedPlanPolicy recordedPlanPol | |||
const BSONObj &max = BSONObj(), | icy = | |||
bool bestGuessOnly = false, | QueryPlanGenerator::Use, | |||
bool mayYield = false); | const BSONObj &min = BSONObj(), | |||
const BSONObj &max = BSONObj() ); | ||||
/** @return number of candidate plans. */ | /** @return number of candidate plans. */ | |||
int nPlans() const { return _plans.size(); } | int nPlans() const { return _plans.size(); } | |||
/** | ||||
* Clone op for each query plan, and @return the first cloned op to | ||||
call | ||||
* setComplete() or setStop(). | ||||
*/ | ||||
shared_ptr<QueryOp> runOp( QueryOp &op ); | ||||
template<class T> | ||||
shared_ptr<T> runOp( T &op ) { | ||||
return dynamic_pointer_cast<T>( runOp( static_cast<QueryOp&>( o | ||||
p ) ) ); | ||||
} | ||||
/** Initialize or iterate a runner generated from @param originalOp | ||||
. */ | ||||
shared_ptr<QueryOp> nextOp( QueryOp &originalOp, bool retried = fal | ||||
se ); | ||||
/** Yield the runner member. */ | ||||
bool prepareToYield(); | ||||
void recoverFromYield(); | ||||
/** Clear the runner member. */ | ||||
void clearRunner(); | ||||
QueryPlanPtr firstPlan() const { return _plans[ 0 ]; } | QueryPlanPtr firstPlan() const { return _plans[ 0 ]; } | |||
/** @return metadata about cursors and index bounds for all plans, | /** @return true if a plan is selected based on previous success of | |||
suitable for explain output. */ | this plan. */ | |||
BSONObj explain() const; | ||||
/** @return true iff a plan is selected based on previous success o | ||||
f this plan. */ | ||||
bool usingCachedPlan() const { return _usingCachedPlan; } | bool usingCachedPlan() const { return _usingCachedPlan; } | |||
/** @return true if some candidate plans may have been excluded due | ||||
to plan caching. */ | ||||
bool hasPossiblyExcludedPlans() const; | ||||
/** @return a single plan that may work well for the specified quer y. */ | /** @return a single plan that may work well for the specified quer y. */ | |||
QueryPlanPtr getBestGuess() const; | QueryPlanPtr getBestGuess() const; | |||
//for testing | ||||
const FieldRangeSetPair &frsp() const { return *_frsp; } | const FieldRangeSetPair &frsp() const { return *_frsp; } | |||
const FieldRangeSetPair *originalFrsp() const { return _originalFrs | BSONObj originalQuery() const { return _originalQuery; } | |||
p.get(); } | BSONObj order() const { return _order; } | |||
/** @return true if an active plan is in order. */ | ||||
bool haveInOrderPlan() const; | ||||
/** @return true if an active or fallback plan is in order. */ | ||||
bool possibleInOrderPlan() const; | ||||
/** @return true if an active or fallback plan is out of order. */ | ||||
bool possibleOutOfOrderPlan() const; | ||||
CandidatePlanCharacter characterizeCandidatePlans() const; | ||||
bool prepareToRetryQuery(); | ||||
string toString() const; | ||||
/** Configure a single query plan if one has not already been provi | ||||
ded. */ | ||||
void setSinglePlan( const QueryPlanPtr &plan ); | ||||
/** Configure a query plan from the plan cache. */ | ||||
void setCachedPlan( const QueryPlanPtr &plan, const CachedQueryPlan | ||||
&cachedPlan ); | ||||
/** Add a candidate query plan, potentially one of many. */ | ||||
void addCandidatePlan( const QueryPlanPtr &plan ); | ||||
//for testing | ||||
bool modifiedKeys() const; | bool modifiedKeys() const; | |||
bool hasMultiKey() const; | bool hasMultiKey() const; | |||
private: | ||||
void addOtherPlans( bool checkFirst ); | ||||
void addPlan( QueryPlanPtr plan, bool checkFirst ) { | ||||
if ( checkFirst && plan->indexKey().woCompare( _plans[ 0 ]->ind | ||||
exKey() ) == 0 ) | ||||
return; | ||||
_plans.push_back( plan ); | ||||
} | ||||
void init(); | ||||
void addHint( IndexDetails &id ); | ||||
class Runner { | class Runner { | |||
public: | public: | |||
Runner( QueryPlanSet &plans, QueryOp &op ); | Runner( QueryPlanSet &plans, QueryOp &op ); | |||
/** | /** | |||
* Iterate interactively through candidate documents on all pla | * Advance the runner, if it is not done(). | |||
ns. | * @return the next non error op if there is one, otherwise an | |||
* QueryOp objects are returned at each interleaved step. | error op. | |||
*/ | * If the returned op is complete() or error(), the Runner beco | |||
mes done(). | ||||
/** @return a plan that has completed, otherwise an arbitrary p | ||||
lan. */ | ||||
shared_ptr<QueryOp> init(); | ||||
/** | ||||
* Move the Runner forward one iteration, and @return the plan | ||||
for | ||||
* this iteration. | ||||
*/ | */ | |||
shared_ptr<QueryOp> next(); | shared_ptr<QueryOp> next(); | |||
/** @return next non error op if there is one, otherwise an err | /** @return true if done iterating. */ | |||
or op. */ | bool done() const { return _done; } | |||
shared_ptr<QueryOp> nextNonError(); | ||||
bool prepareToYield(); | void prepareToYield(); | |||
void recoverFromYield(); | void recoverFromYield(); | |||
/** Run until first op completes. */ | /** @return an ExplainClauseInfo object that will be updated as | |||
shared_ptr<QueryOp> runUntilFirstCompletes(); | the query runs. */ | |||
shared_ptr<ExplainClauseInfo> generateExplainInfo() { | ||||
_explainClauseInfo.reset( new ExplainClauseInfo() ); | ||||
return _explainClauseInfo; | ||||
} | ||||
void mayYield(); | private: | |||
QueryOp &_op; | QueryOp &_op; | |||
QueryPlanSet &_plans; | QueryPlanSet &_plans; | |||
static void initOp( QueryOp &op ); | static void initOp( QueryOp &op ); | |||
static void nextOp( QueryOp &op ); | static void nextOp( QueryOp &op ); | |||
static bool prepareToYieldOp( QueryOp &op ); | static void prepareToYieldOp( QueryOp &op ); | |||
static void recoverFromYieldOp( QueryOp &op ); | static void recoverFromYieldOp( QueryOp &op ); | |||
private: | ||||
/** Initialize the Runner. */ | ||||
shared_ptr<QueryOp> init(); | ||||
/** Move the Runner forward one iteration, and @return the plan | ||||
for the iteration. */ | ||||
shared_ptr<QueryOp> _next(); | ||||
vector<shared_ptr<QueryOp> > _ops; | vector<shared_ptr<QueryOp> > _ops; | |||
struct OpHolder { | struct OpHolder { | |||
OpHolder( const shared_ptr<QueryOp> &op ) : _op( op ), _off set() {} | OpHolder( const shared_ptr<QueryOp> &op ) : _op( op ), _off set() {} | |||
shared_ptr<QueryOp> _op; | shared_ptr<QueryOp> _op; | |||
long long _offset; | long long _offset; | |||
bool operator<( const OpHolder &other ) const { | bool operator<( const OpHolder &other ) const { | |||
return _op->nscanned() + _offset > other._op->nscanned( ) + other._offset; | return _op->nscanned() + _offset > other._op->nscanned( ) + other._offset; | |||
} | } | |||
}; | }; | |||
our_priority_queue<OpHolder> _queue; | our_priority_queue<OpHolder> _queue; | |||
shared_ptr<ExplainClauseInfo> _explainClauseInfo; | ||||
bool _done; | ||||
}; | }; | |||
const char *_ns; | private: | |||
void addFallbackPlans(); | ||||
void init(); | ||||
QueryPlanGenerator _generator; | ||||
BSONObj _originalQuery; | BSONObj _originalQuery; | |||
auto_ptr<FieldRangeSetPair> _frsp; | auto_ptr<FieldRangeSetPair> _frsp; | |||
auto_ptr<FieldRangeSetPair> _originalFrsp; | ||||
PlanSet _plans; | PlanSet _plans; | |||
bool _mayRecordPlan; | bool _mayRecordPlan; | |||
bool _usingCachedPlan; | bool _usingCachedPlan; | |||
BSONObj _hint; | CandidatePlanCharacter _cachedPlanCharacter; | |||
BSONObj _order; | BSONObj _order; | |||
long long _oldNScanned; | long long _oldNScanned; | |||
bool _honorRecordedPlan; | ||||
BSONObj _min; | ||||
BSONObj _max; | ||||
string _special; | ||||
bool _bestGuessOnly; | ||||
bool _mayYield; | ||||
ElapsedTracker _yieldSometimesTracker; | ElapsedTracker _yieldSometimesTracker; | |||
shared_ptr<Runner> _runner; | ||||
bool _mustAssertOnYieldFailure; | ||||
}; | }; | |||
/** Handles $or type queries by generating a QueryPlanSet for each $or clause. */ | /** Handles $or type queries by generating a QueryPlanSet for each $or clause. */ | |||
class MultiPlanScanner { | class MultiPlanScanner { | |||
public: | public: | |||
MultiPlanScanner( const char *ns, | MultiPlanScanner( const char *ns, | |||
const BSONObj &query, | const BSONObj &query, | |||
const BSONObj &order, | const BSONObj &order, | |||
const BSONElement *hint = 0, | const shared_ptr<const ParsedQuery> &parsedQuery | |||
bool honorRecordedPlan = true, | = | |||
shared_ptr<const ParsedQuery>(), | ||||
const BSONObj &hint = BSONObj(), | ||||
QueryPlanGenerator::RecordedPlanPolicy recordedPl | ||||
anPolicy = | ||||
QueryPlanGenerator::Use, | ||||
const BSONObj &min = BSONObj(), | const BSONObj &min = BSONObj(), | |||
const BSONObj &max = BSONObj(), | const BSONObj &max = BSONObj() ); | |||
bool bestGuessOnly = false, | ||||
bool mayYield = false); | ||||
/** Set the initial QueryOp for QueryPlanSet iteration. */ | ||||
void initialOp( const shared_ptr<QueryOp> &originalOp ) { _baseOp = | ||||
originalOp; } | ||||
/** | /** | |||
* Clone op for each query plan of a single $or clause, and @return | * Advance to the next QueryOp, if not doneOps(). | |||
the first cloned op | * @return the next non error op if there is one, otherwise an erro | |||
* to call setComplete() or setStop(). | r op. | |||
* If the returned op is complete() or error(), the MultiPlanScanne | ||||
r becomes doneOps() and | ||||
* no further QueryOp iteration is possible. | ||||
*/ | */ | |||
shared_ptr<QueryOp> nextOp(); | ||||
shared_ptr<QueryOp> runOpOnce( QueryOp &op ); | /** @return true if done with QueryOp iteration. */ | |||
template<class T> | bool doneOps() const { return _doneOps; } | |||
shared_ptr<T> runOpOnce( T &op ) { | ||||
return dynamic_pointer_cast<T>( runOpOnce( static_cast<QueryOp& | ||||
>( op ) ) ); | ||||
} | ||||
/** | /** | |||
* For each $or clause, calls runOpOnce on the child QueryOp cloned | * Advance to the next $or clause; hasMoreClauses() must be true. | |||
from the winning QueryOp | * @param currentPlan QueryPlan of the current $or clause | |||
* of the previous $or clause (or from the supplied 'op' for the fi | * @return best guess query plan of the next $or clause, 0 if there | |||
rst $or clause). | is no such plan. | |||
*/ | */ | |||
const QueryPlan *nextClauseBestGuessPlan( const QueryPlan ¤tP lan ); | ||||
shared_ptr<QueryOp> runOp( QueryOp &op ); | /** Add explain information for a new clause. */ | |||
template<class T> | void addClauseInfo( const shared_ptr<ExplainClauseInfo> &clauseInfo | |||
shared_ptr<T> runOp( T &op ) { | ) { | |||
return dynamic_pointer_cast<T>( runOp( static_cast<QueryOp&>( o | verify( _explainQueryInfo ); | |||
p ) ) ); | _explainQueryInfo->addClauseInfo( clauseInfo ); | |||
} | } | |||
/** Initialize or iterate a runner generated from @param originalOp | /** @return an ExplainQueryInfo object that will be updated as the | |||
. */ | query runs. */ | |||
shared_ptr<ExplainQueryInfo> generateExplainInfo() { | ||||
void initialOp( const shared_ptr<QueryOp> &originalOp ) { _baseOp = | _explainQueryInfo.reset( new ExplainQueryInfo() ); | |||
originalOp; } | return _explainQueryInfo; | |||
shared_ptr<QueryOp> nextOp(); | } | |||
/** Yield the runner member. */ | /** Yield the runner member. */ | |||
bool prepareToYield(); | void prepareToYield(); | |||
void recoverFromYield(); | void recoverFromYield(); | |||
/** Clear the runner member. */ | /** Clear the runner member. */ | |||
void clearRunner(); | void clearRunner(); | |||
int currentNPlans() const; | void setRecordedPlanPolicy( QueryPlanGenerator::RecordedPlanPolicy | |||
recordedPlanPolicy ) { | ||||
_recordedPlanPolicy = recordedPlanPolicy; | ||||
} | ||||
/** | int currentNPlans() const; | |||
* @return a single simple cursor if the scanner would run a single | ||||
cursor | ||||
* for this query, otherwise return an empty shared_ptr. | ||||
*/ | ||||
shared_ptr<Cursor> singleCursor() const; | ||||
/** | /** | |||
* @return the query plan that would be used if the scanner would r un a single | * @return the query plan that would be used if the scanner would r un a single | |||
* cursor for this query, otherwise 0. The returned plan is invali d if this | * cursor for this query, otherwise 0. The returned plan is invali d if this | |||
* MultiPlanScanner is destroyed, hence we return a raw pointer. | * MultiPlanScanner is destroyed, hence we return a raw pointer. | |||
*/ | */ | |||
const QueryPlan *singlePlan() const; | const QueryPlan *singlePlan() const; | |||
/** @return true iff more $or clauses need to be scanned. */ | /** @return true if more $or clauses need to be scanned. */ | |||
bool mayRunMore() const { | bool hasMoreClauses() const { | |||
return _or ? ( !_tableScanned && !_org->orRangesExhausted() ) : _i == 0; | return _or ? ( !_tableScanned && !_org->orRangesExhausted() ) : _i == 0; | |||
} | } | |||
/** @return non-$or version of explain output. */ | /** | |||
BSONObj oldExplain() const { assertNotOr(); return _currentQps->exp | * @return plan information if there is a cached plan for a non $or | |||
lain(); } | query, otherwise an | |||
/** @return true iff this is not a $or query and a plan is selected | * empty object. | |||
based on previous success of this plan. */ | */ | |||
bool usingCachedPlan() const { return !_or && _currentQps->usingCac | BSONObj cachedPlanExplainSummary() const; | |||
hedPlan(); } | /** | |||
/** Don't attempt to scan multiple plans, just use the best guess. | * @return true if this is not a $or query and some candidate plans | |||
*/ | may have been excluded | |||
void setBestGuessOnly() { _bestGuessOnly = true; } | * due to plan caching. | |||
/** Yielding is allowed while running each QueryPlan. */ | */ | |||
void mayYield( bool val ) { _mayYield = val; } | bool hasPossiblyExcludedPlans() const { | |||
bool modifiedKeys() const { return _currentQps->modifiedKeys(); } | return !_or && _currentQps->hasPossiblyExcludedPlans(); | |||
} | ||||
bool hasMultiKey() const { return _currentQps->hasMultiKey(); } | bool hasMultiKey() const { return _currentQps->hasMultiKey(); } | |||
/** Clear recorded indexes for the current QueryPlanSet's patterns. | ||||
*/ | ||||
void clearIndexesForPatterns() const; | ||||
/** @return true if an active plan of _currentQps is in order. */ | ||||
bool haveInOrderPlan() const; | ||||
/** @return true if an active or fallback plan of _currentQps is in | ||||
order. */ | ||||
bool possibleInOrderPlan() const; | ||||
/** @return true if an active or fallback plan of _currentQps is ou | ||||
t of order. */ | ||||
bool possibleOutOfOrderPlan() const; | ||||
int i() const { return _i; } | ||||
string toString() const; | ||||
private: | private: | |||
/** Initialize or iterate a runner generated from @param originalOp | ||||
. */ | ||||
shared_ptr<QueryOp> iterateRunner( QueryOp &originalOp, bool retrie | ||||
d = false ); | ||||
shared_ptr<QueryOp> nextOpSimple(); | ||||
shared_ptr<QueryOp> nextOpOr(); | ||||
void updateCurrentQps( QueryPlanSet *qps ); | ||||
void assertNotOr() const { | void assertNotOr() const { | |||
massert( 13266, "not implemented for $or query", !_or ); | massert( 13266, "not implemented for $or query", !_or ); | |||
} | } | |||
void assertMayRunMore() const { | void assertHasMoreClauses() const { | |||
massert( 13271, "can't run more ops", mayRunMore() ); | massert( 13271, "no more clauses", hasMoreClauses() ); | |||
} | } | |||
shared_ptr<QueryOp> nextOpBeginningClause(); | ||||
shared_ptr<QueryOp> nextOpHandleEndOfClause(); | void handleEndOfClause( const QueryPlan &clausePlan ); | |||
bool uselessOr( const BSONElement &hint ) const; | void handleBeginningOfClause(); | |||
bool mayHandleBeginningOfClause(); | ||||
bool haveUselessOr() const; | ||||
const string _ns; | const string _ns; | |||
bool _or; | bool _or; | |||
BSONObj _query; | BSONObj _query; | |||
shared_ptr<OrRangeGenerator> _org; // May be null in certain non $o | shared_ptr<const ParsedQuery> _parsedQuery; | |||
r query cases. | scoped_ptr<OrRangeGenerator> _org; // May be null in certain non $o | |||
auto_ptr<QueryPlanSet> _currentQps; | r query cases. | |||
scoped_ptr<QueryPlanSet> _currentQps; | ||||
int _i; | int _i; | |||
bool _honorRecordedPlan; | QueryPlanGenerator::RecordedPlanPolicy _recordedPlanPolicy; | |||
bool _bestGuessOnly; | ||||
BSONObj _hint; | BSONObj _hint; | |||
bool _mayYield; | ||||
bool _tableScanned; | bool _tableScanned; | |||
shared_ptr<QueryOp> _baseOp; | shared_ptr<QueryOp> _baseOp; | |||
scoped_ptr<QueryPlanSet::Runner> _runner; | ||||
shared_ptr<ExplainQueryInfo> _explainQueryInfo; | ||||
bool _doneOps; | ||||
}; | }; | |||
/** Provides a cursor interface for certain limited uses of a MultiPlan | /** | |||
Scanner. */ | * Provides a cursor interface for serial single Cursor iteration using | |||
a MultiPlanScanner. | ||||
* Currently used internally by a QueryOptimizerCursor. | ||||
* | ||||
* A MultiCursor is backed by one BasicCursor or BtreeCursor at a time | ||||
and forwards calls for | ||||
* ensuring a consistent state after a write to its backing Cursor. Th | ||||
ere is a known issue in | ||||
* some cases when advance() causes a switch to a new BasicCursor backi | ||||
ng (SERVER-5198). | ||||
*/ | ||||
class MultiCursor : public Cursor { | class MultiCursor : public Cursor { | |||
public: | public: | |||
class CursorOp : public QueryOp { | /** @param nscanned is the initial nscanned value. */ | |||
public: | MultiCursor( auto_ptr<MultiPlanScanner> mps, const shared_ptr<Curso | |||
CursorOp() {} | r> &c, | |||
CursorOp( const QueryOp &other ) : QueryOp( other ) {} | const shared_ptr<CoveredIndexMatcher> &matcher, | |||
virtual shared_ptr<Cursor> newCursor() const = 0; | const shared_ptr<ExplainPlanInfo> &explainPlanInfo, | |||
}; | const QueryOp &op, long long nscanned ); | |||
/** takes ownership of 'op' */ | ||||
MultiCursor( const char *ns, const BSONObj &pattern, const BSONObj | ||||
&order, shared_ptr<CursorOp> op = shared_ptr<CursorOp>(), bool mayYield = f | ||||
alse ); | ||||
/** | ||||
* Used | ||||
* 1. To handoff a query to a getMore() | ||||
* 2. To handoff a QueryOptimizerCursor | ||||
* @param nscanned is an optional initial value, if not supplied ns | ||||
canned() | ||||
* will always return -1 | ||||
*/ | ||||
MultiCursor( auto_ptr<MultiPlanScanner> mps, const shared_ptr<Curso | ||||
r> &c, const shared_ptr<CoveredIndexMatcher> &matcher, const QueryOp &op, l | ||||
ong long nscanned = -1 ); | ||||
virtual bool ok() { return _c->ok(); } | virtual bool ok() { return _c->ok(); } | |||
virtual Record* _current() { return _c->_current(); } | virtual Record* _current() { return _c->_current(); } | |||
virtual BSONObj current() { return _c->current(); } | virtual BSONObj current() { return _c->current(); } | |||
virtual DiskLoc currLoc() { return _c->currLoc(); } | virtual DiskLoc currLoc() { return _c->currLoc(); } | |||
virtual bool advance() { | virtual bool advance(); | |||
_c->advance(); | ||||
while( !ok() && _mps->mayRunMore() ) { | ||||
nextClause(); | ||||
} | ||||
return ok(); | ||||
} | ||||
virtual BSONObj currKey() const { return _c->currKey(); } | virtual BSONObj currKey() const { return _c->currKey(); } | |||
virtual DiskLoc refLoc() { return _c->refLoc(); } | virtual DiskLoc refLoc() { return _c->refLoc(); } | |||
virtual void noteLocation() { _c->noteLocation(); } | virtual void noteLocation() { _c->noteLocation(); } | |||
virtual void checkLocation() { _c->checkLocation(); } | virtual void checkLocation() { _c->checkLocation(); } | |||
virtual void recoverFromYield(); | ||||
virtual bool supportGetMore() { return true; } | virtual bool supportGetMore() { return true; } | |||
virtual bool supportYields() { return _c->supportYields(); } | virtual bool supportYields() { return true; } | |||
virtual BSONObj indexKeyPattern() { return _c->indexKeyPattern(); } | virtual BSONObj indexKeyPattern() { return _c->indexKeyPattern(); } | |||
/** | /** Deduping documents from a prior cursor is handled by the matche | |||
* with update we could potentially get the same document on multip | r. */ | |||
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 bool getsetdup(DiskLoc loc) { return _c->getsetdup( loc ); } | |||
virtual bool autoDedup() const { return _c->autoDedup(); } | virtual bool autoDedup() const { return _c->autoDedup(); } | |||
virtual bool modifiedKeys() const { return _mps->modifiedKeys(); } | virtual bool modifiedKeys() const { return true; } | |||
virtual bool isMultiKey() const { return _mps->hasMultiKey(); } | virtual bool isMultiKey() const { return _mps->hasMultiKey(); } | |||
virtual shared_ptr< CoveredIndexMatcher > matcherPtr() const { retu rn _matcher; } | virtual shared_ptr< CoveredIndexMatcher > matcherPtr() const { retu rn _matcher; } | |||
virtual CoveredIndexMatcher* matcher() const { return _matcher.get( ); } | virtual CoveredIndexMatcher* matcher() const { return _matcher.get( ); } | |||
virtual bool capped() const { return _c->capped(); } | virtual bool capped() const { return _c->capped(); } | |||
/** return -1 if we're a getmore handoff */ | virtual long long nscanned() { return _nscanned + _c->nscanned(); } | |||
virtual long long nscanned() { return _nscanned >= 0 ? _nscanned + | ||||
_c->nscanned() : _nscanned; } | void noteIterate( bool match, bool loadedRecord ); | |||
/** just for testing */ | ||||
shared_ptr<Cursor> sub_c() const { return _c; } | void noteYield(); | |||
const QueryPlan &queryPlan() const { | ||||
verify( _c->ok() && _queryPlan ); | ||||
return *_queryPlan; | ||||
} | ||||
const Projection::KeyOnly *keyFieldsOnly() const { | ||||
verify( _c->ok() && _queryPlan ); | ||||
return _queryPlan->keyFieldsOnly().get(); | ||||
} | ||||
private: | private: | |||
class NoOp : public CursorOp { | ||||
public: | ||||
NoOp() {} | ||||
NoOp( const QueryOp &other ) : CursorOp( other ) {} | ||||
virtual void _init() { setComplete(); } | ||||
virtual void next() {} | ||||
virtual bool mayRecordPlan() const { return false; } | ||||
virtual QueryOp *_createChild() const { return new NoOp(); } | ||||
virtual shared_ptr<Cursor> newCursor() const { return qp().newC | ||||
ursor(); } | ||||
virtual long long nscanned() { assert( false ); return 0; } | ||||
}; | ||||
void nextClause(); | void nextClause(); | |||
shared_ptr<CursorOp> _op; | ||||
shared_ptr<Cursor> _c; | ||||
auto_ptr<MultiPlanScanner> _mps; | auto_ptr<MultiPlanScanner> _mps; | |||
shared_ptr<Cursor> _c; | ||||
shared_ptr<CoveredIndexMatcher> _matcher; | shared_ptr<CoveredIndexMatcher> _matcher; | |||
const QueryPlan *_queryPlan; | ||||
long long _nscanned; | long long _nscanned; | |||
shared_ptr<ExplainPlanInfo> _explainPlanInfo; | ||||
}; | }; | |||
/** 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 ); | |||
class CachedQueryPlan; | ||||
/** | /** | |||
* Add-on functionality for queryutil classes requiring access to index ing | * Add-on functionality for queryutil classes requiring access to index ing | |||
* functionality not currently linked to mongos. | * functionality not currently linked to mongos. | |||
* TODO Clean this up a bit, possibly with separate sharded and non sha rded | * TODO Clean this up a bit, possibly with separate sharded and non sha rded | |||
* implementations for the appropriate queryutil classes or by pulling index | * implementations for the appropriate queryutil classes or by pulling index | |||
* related functionality into separate wrapper classes. | * related functionality into separate wrapper classes. | |||
*/ | */ | |||
struct QueryUtilIndexed { | struct QueryUtilIndexed { | |||
/** @return true if the index may be useful according to its KeySpe c. */ | /** @return true if the index may be useful according to its KeySpe c. */ | |||
static bool indexUseful( const FieldRangeSetPair &frsp, NamespaceDe tails *d, int idxNo, const BSONObj &order ); | static bool indexUseful( const FieldRangeSetPair &frsp, NamespaceDe tails *d, int idxNo, const BSONObj &order ); | |||
/** Clear any indexes recorded as the best for either the single or multi key pattern. */ | /** Clear any indexes recorded as the best for either the single or multi key pattern. */ | |||
static void clearIndexesForPatterns( const FieldRangeSetPair &frsp, const BSONObj &order ); | static void clearIndexesForPatterns( const FieldRangeSetPair &frsp, const BSONObj &order ); | |||
/** Return a recorded best index for the single or multi key patter n. */ | /** Return a recorded best index for the single or multi key patter n. */ | |||
static pair< BSONObj, long long > bestIndexForPatterns( const Field RangeSetPair &frsp, const BSONObj &order ); | static CachedQueryPlan bestIndexForPatterns( const FieldRangeSetPai r &frsp, const BSONObj &order ); | |||
static bool uselessOr( const OrRangeGenerator& org, NamespaceDetail s *d, int hintIdx ); | static bool uselessOr( const OrRangeGenerator& org, NamespaceDetail s *d, int hintIdx ); | |||
}; | }; | |||
} // namespace mongo | } // namespace mongo | |||
End of changes. 82 change blocks. | ||||
236 lines changed or deleted | 335 lines changed or added | |||
queryoptimizercursor.h | queryoptimizercursor.h | |||
---|---|---|---|---|
// @file queryoptimizercursor.h | // @file queryoptimizercursor.h - Interface for a cursor interleaving multi ple candidate cursors. | |||
/** | /** | |||
* Copyright (C) 2011 10gen Inc. | * Copyright (C) 2011 10gen Inc. | |||
* | * | |||
* This program is free software: you can redistribute it and/or modify | * This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Affero General Public License, version 3, | * it under the terms of the GNU Affero General Public License, version 3, | |||
* as published by the Free Software Foundation. | * as published by the Free Software Foundation. | |||
* | * | |||
* This program is distributed in the hope that it will be useful, | * This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Affero General Public License for more details. | * GNU Affero General Public License for more details. | |||
* | * | |||
* You should have received a copy of the GNU Affero General Public Lice nse | * You should have received a copy of the GNU Affero General Public Lice nse | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | */ | |||
#pragma once | ||||
#include "cursor.h" | ||||
#include "diskloc.h" | ||||
namespace mongo { | namespace mongo { | |||
/** Helper class for caching and counting matches during execution of a | class QueryPlan; | |||
QueryPlan. */ | class CandidatePlanCharacter; | |||
class CachedMatchCounter { | ||||
/** | ||||
* An interface for policies overriding the query optimizer's default q | ||||
uery plan selection | ||||
* behavior. | ||||
*/ | ||||
class QueryPlanSelectionPolicy { | ||||
public: | public: | |||
/** | virtual ~QueryPlanSelectionPolicy() {} | |||
* @param aggregateNscanned - shared count of nscanned for this and | virtual string name() const = 0; | |||
othe plans. | virtual bool permitOptimalNaturalPlan() const { return true; } | |||
* @param cumulativeCount - starting point for accumulated count ov | virtual bool permitOptimalIdPlan() const { return true; } | |||
er a series of plans. | virtual bool permitPlan( const QueryPlan &plan ) const { return tru | |||
*/ | e; } | |||
CachedMatchCounter( long long &aggregateNscanned, int cumulativeCou | virtual BSONObj planHint( const char *ns ) const { return BSONObj() | |||
nt ) : _aggregateNscanned( aggregateNscanned ), _nscanned(), _cumulativeCou | ; } | |||
nt( cumulativeCount ), _count(), _checkDups(), _match( Unknown ), _counted( | ||||
) {} | /** Allow any query plan selection, permitting the query optimizer' | |||
s default behavior. */ | ||||
static const QueryPlanSelectionPolicy &any(); | ||||
/** Set whether dup checking is enabled when counting. */ | /** Prevent unindexed collection scans. */ | |||
void setCheckDups( bool checkDups ) { _checkDups = checkDups; } | static const QueryPlanSelectionPolicy &indexOnly(); | |||
/** | /** | |||
* Usual sequence of events: | * Generally hints to use the _id plan, falling back to the $natura | |||
* 1) resetMatch() - reset stored match value to Unkonwn. | l plan. However, the | |||
* 2) setMatch() - set match value to a definite true/false value. | * $natural plan will always be used if optimal for the query. | |||
* 3) knowMatch() - check if setMatch() has been called. | ||||
* 4) countMatch() - increment count if match is true. | ||||
*/ | */ | |||
static const QueryPlanSelectionPolicy &idElseNatural(); | ||||
void resetMatch() { | ||||
_match = Unknown; | ||||
_counted = false; | ||||
} | ||||
void setMatch( bool match ) { _match = match ? True : False; } | ||||
bool knowMatch() const { return _match != Unknown; } | ||||
void countMatch( const DiskLoc &loc ) { | ||||
if ( !_counted && _match == True && !getsetdup( loc ) ) { | ||||
++_cumulativeCount; | ||||
++_count; | ||||
_counted = true; | ||||
} | ||||
} | ||||
bool enoughCumulativeMatchesToChooseAPlan() const { | ||||
// This is equivalent to the default condition for switching fr | ||||
om | ||||
// a query to a getMore, which was the historical default match | ||||
count for | ||||
// choosing a plan. | ||||
return _cumulativeCount >= 101; | ||||
} | ||||
bool enoughMatchesToRecordPlan() const { | ||||
// Recording after 50 matches is a historical default (101 defa | ||||
ult limit / 2). | ||||
return _count > 50; | ||||
} | ||||
int cumulativeCount() const { return _cumulativeCount; } | ||||
int count() const { return _count; } | ||||
/** Update local and aggregate nscanned counts. */ | ||||
void updateNscanned( long long nscanned ) { | ||||
_aggregateNscanned += ( nscanned - _nscanned ); | ||||
_nscanned = nscanned; | ||||
} | ||||
long long nscanned() const { return _nscanned; } | ||||
long long &aggregateNscanned() const { return _aggregateNscanned; } | ||||
private: | private: | |||
bool getsetdup( const DiskLoc &loc ) { | class Any; | |||
if ( !_checkDups ) { | static Any __any; | |||
return false; | class IndexOnly; | |||
} | static IndexOnly __indexOnly; | |||
pair<set<DiskLoc>::iterator, bool> p = _dups.insert( loc ); | class IdElseNatural; | |||
return !p.second; | static IdElseNatural __idElseNatural; | |||
} | ||||
long long &_aggregateNscanned; | ||||
long long _nscanned; | ||||
int _cumulativeCount; | ||||
int _count; | ||||
bool _checkDups; | ||||
enum MatchState { Unknown, False, True }; | ||||
MatchState _match; | ||||
bool _counted; | ||||
set<DiskLoc> _dups; | ||||
}; | }; | |||
/** Dup tracking class, optimizing one common case with small set and f | class QueryPlanSelectionPolicy::Any : public QueryPlanSelectionPolicy { | |||
ew initial reads. */ | ||||
class SmallDupSet { | ||||
public: | public: | |||
SmallDupSet() : _accesses() { | virtual string name() const { return "any"; } | |||
_vec.reserve( 250 ); | ||||
} | ||||
/** @return true if @param 'loc' already added to the set, false if | ||||
adding to the set in this call. */ | ||||
bool getsetdup( const DiskLoc &loc ) { | ||||
access(); | ||||
return vec() ? getsetdupVec( loc ) : getsetdupSet( loc ); | ||||
} | ||||
/** @return true when @param loc in the set. */ | ||||
bool getdup( const DiskLoc &loc ) { | ||||
access(); | ||||
return vec() ? getdupVec( loc ) : getdupSet( loc ); | ||||
} | ||||
private: | ||||
void access() { | ||||
++_accesses; | ||||
mayUpgrade(); | ||||
} | ||||
void mayUpgrade() { | ||||
if ( vec() && _accesses > 500 ) { | ||||
_set.insert( _vec.begin(), _vec.end() ); | ||||
} | ||||
} | ||||
bool vec() const { | ||||
return _set.size() == 0; | ||||
} | ||||
bool getsetdupVec( const DiskLoc &loc ) { | ||||
if ( getdupVec( loc ) ) { | ||||
return true; | ||||
} | ||||
_vec.push_back( loc ); | ||||
return false; | ||||
} | ||||
bool getdupVec( const DiskLoc &loc ) const { | ||||
for( vector<DiskLoc>::const_iterator i = _vec.begin(); i != _ve | ||||
c.end(); ++i ) { | ||||
if ( *i == loc ) { | ||||
return true; | ||||
} | ||||
} | ||||
return false; | ||||
} | ||||
bool getsetdupSet( const DiskLoc &loc ) { | ||||
pair<set<DiskLoc>::iterator, bool> p = _set.insert(loc); | ||||
return !p.second; | ||||
} | ||||
bool getdupSet( const DiskLoc &loc ) { | ||||
return _set.count( loc ) > 0; | ||||
} | ||||
vector<DiskLoc> _vec; | ||||
set<DiskLoc> _set; | ||||
long long _accesses; | ||||
}; | }; | |||
class QueryPlanSelectionPolicy::IndexOnly : public QueryPlanSelectionPo | ||||
licy { | ||||
public: | ||||
virtual string name() const { return "indexOnly"; } | ||||
virtual bool permitOptimalNaturalPlan() const { return false; } | ||||
virtual bool permitPlan( const QueryPlan &plan ) const; | ||||
}; | ||||
class QueryPlanSelectionPolicy::IdElseNatural : public QueryPlanSelecti | ||||
onPolicy { | ||||
public: | ||||
virtual string name() const { return "idElseNatural"; } | ||||
virtual bool permitPlan( const QueryPlan &plan ) const; | ||||
virtual BSONObj planHint( const char *ns ) const; | ||||
}; | ||||
class FieldRangeSet; | ||||
class ExplainQueryInfo; | ||||
/** | ||||
* Adds functionality to Cursor for running multiple plans, running out | ||||
of order plans, | ||||
* utilizing covered indexes, and generating explain output. | ||||
*/ | ||||
class QueryOptimizerCursor : public Cursor { | ||||
public: | ||||
/** Candidate plans for the query before it begins running. */ | ||||
virtual CandidatePlanCharacter initialCandidatePlans() const = 0; | ||||
/** FieldRangeSet for the query before it begins running. */ | ||||
virtual const FieldRangeSet *initialFieldRangeSet() const = 0; | ||||
/** @return true if the plan for the current iterate is out of orde | ||||
r. */ | ||||
virtual bool currentPlanScanAndOrderRequired() const = 0; | ||||
/** @return true when there may be multiple plans running and some | ||||
are in order. */ | ||||
virtual bool runningInitialInOrderPlan() const = 0; | ||||
/** | ||||
* @return true when some query plans may have been excluded due to | ||||
plan caching, for a | ||||
* non-$or query. | ||||
*/ | ||||
virtual bool hasPossiblyExcludedPlans() const = 0; | ||||
/** | ||||
* @return true when both in order and out of order candidate plans | ||||
were available, and | ||||
* an out of order candidate plan completed iteration. | ||||
*/ | ||||
virtual bool completePlanOfHybridSetScanAndOrderRequired() const = | ||||
0; | ||||
/** Clear recorded indexes for the current clause's query patterns. | ||||
*/ | ||||
virtual void clearIndexesForPatterns() = 0; | ||||
/** Stop returning results from out of order plans and do not allow | ||||
them to complete. */ | ||||
virtual void abortOutOfOrderPlans() = 0; | ||||
/** Note match information for the current iterate, to generate exp | ||||
lain output. */ | ||||
virtual void noteIterate( bool match, bool loadedDocument, bool chu | ||||
nkSkip ) = 0; | ||||
/** @return explain output for the query run by this cursor. */ | ||||
virtual shared_ptr<ExplainQueryInfo> explainQueryInfo() const = 0; | ||||
}; | ||||
} // namespace mongo | } // namespace mongo | |||
End of changes. 12 change blocks. | ||||
131 lines changed or deleted | 111 lines changed or added | |||
querypattern.h | querypattern.h | |||
---|---|---|---|---|
skipping to change at line 21 | skipping to change at line 21 | |||
* Unless required by applicable law or agreed to in writing, software | * Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | * distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. | |||
* See the License for the specific language governing permissions and | * See the License for the specific language governing permissions and | |||
* limitations under the License. | * limitations under the License. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "jsobj.h" | #include "jsobj.h" | |||
#include "queryutil.h" | ||||
namespace mongo { | namespace mongo { | |||
class FieldRangeSet; | ||||
/** | /** | |||
* 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. | |||
* | * | |||
* Two queries will generate the same QueryPattern, and therefore match each | * Two queries will generate the same QueryPattern, and therefore match each | |||
* other, if their fields have the same Types and they have the same so rt | * other, if their fields have the same Types and they have the same so rt | |||
* spec. | * spec. | |||
*/ | */ | |||
class QueryPattern { | class QueryPattern { | |||
public: | public: | |||
skipping to change at line 58 | skipping to change at line 59 | |||
bool operator!=( const QueryPattern &other ) const; | bool operator!=( const QueryPattern &other ) const; | |||
/** for development / debugging */ | /** for development / debugging */ | |||
string toString() const; | string toString() const; | |||
private: | private: | |||
void setSort( const BSONObj sort ); | void setSort( const BSONObj sort ); | |||
static BSONObj normalizeSort( const BSONObj &spec ); | static BSONObj normalizeSort( const BSONObj &spec ); | |||
map<string,Type> _fieldTypes; | map<string,Type> _fieldTypes; | |||
BSONObj _sort; | BSONObj _sort; | |||
}; | }; | |||
/** Summarizes the candidate plans that may run for a query. */ | ||||
class CandidatePlanCharacter { | ||||
public: | ||||
CandidatePlanCharacter( bool mayRunInOrderPlan, bool mayRunOutOfOrd | ||||
erPlan ) : | ||||
_mayRunInOrderPlan( mayRunInOrderPlan ), | ||||
_mayRunOutOfOrderPlan( mayRunOutOfOrderPlan ) { | ||||
} | ||||
CandidatePlanCharacter() : | ||||
_mayRunInOrderPlan(), | ||||
_mayRunOutOfOrderPlan() { | ||||
} | ||||
bool mayRunInOrderPlan() const { return _mayRunInOrderPlan; } | ||||
bool mayRunOutOfOrderPlan() const { return _mayRunOutOfOrderPlan; } | ||||
bool valid() const { return mayRunInOrderPlan() || mayRunOutOfOrder | ||||
Plan(); } | ||||
bool hybridPlanSet() const { return mayRunInOrderPlan() && mayRunOu | ||||
tOfOrderPlan(); } | ||||
private: | ||||
bool _mayRunInOrderPlan; | ||||
bool _mayRunOutOfOrderPlan; | ||||
}; | ||||
/** Information about a query plan that ran successfully for a QueryPat | ||||
tern. */ | ||||
class CachedQueryPlan { | ||||
public: | ||||
CachedQueryPlan() : | ||||
_nScanned() { | ||||
} | ||||
CachedQueryPlan( const BSONObj &indexKey, long long nScanned, | ||||
CandidatePlanCharacter planCharacter ); | ||||
BSONObj indexKey() const { return _indexKey; } | ||||
long long nScanned() const { return _nScanned; } | ||||
CandidatePlanCharacter planCharacter() const { return _planCharacte | ||||
r; } | ||||
private: | ||||
BSONObj _indexKey; | ||||
long long _nScanned; | ||||
CandidatePlanCharacter _planCharacter; | ||||
}; | ||||
inline bool QueryPattern::operator<( const QueryPattern &other ) const { | inline bool QueryPattern::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; | |||
End of changes. 3 change blocks. | ||||
1 lines changed or deleted | 44 lines changed or added | |||
queryutil-inl.h | queryutil-inl.h | |||
---|---|---|---|---|
skipping to change at line 35 | skipping to change at line 35 | |||
} | } | |||
inline bool FieldRange::equality() const { | inline bool FieldRange::equality() const { | |||
return | return | |||
!empty() && | !empty() && | |||
min().woCompare( max(), false ) == 0 && | min().woCompare( max(), false ) == 0 && | |||
maxInclusive() && | maxInclusive() && | |||
minInclusive(); | minInclusive(); | |||
} | } | |||
inline bool FieldRange::inQuery() const { | ||||
if ( equality() ) { | ||||
return true; | ||||
} | ||||
for( vector<FieldInterval>::const_iterator i = _intervals.begin(); | ||||
i != _intervals.end(); ++i ) { | ||||
if ( !i->equality() ) { | ||||
return false; | ||||
} | ||||
} | ||||
return true; | ||||
} | ||||
inline const FieldRange &FieldRangeSet::range( const char *fieldName ) const { | inline const FieldRange &FieldRangeSet::range( const char *fieldName ) const { | |||
map<string,FieldRange>::const_iterator f = _ranges.find( fieldName ); | map<string,FieldRange>::const_iterator f = _ranges.find( fieldName ); | |||
if ( f == _ranges.end() ) | if ( f == _ranges.end() ) | |||
return universalRange(); | return universalRange(); | |||
return f->second; | return f->second; | |||
} | } | |||
inline FieldRange &FieldRangeSet::range( const char *fieldName ) { | inline FieldRange &FieldRangeSet::range( const char *fieldName ) { | |||
map<string,FieldRange>::iterator f = _ranges.find( fieldName ); | map<string,FieldRange>::iterator f = _ranges.find( fieldName ); | |||
if ( f == _ranges.end() ) { | if ( f == _ranges.end() ) { | |||
skipping to change at line 89 | skipping to change at line 77 | |||
if ( e.fieldName() == string( "$natural" ) ) { | if ( e.fieldName() == string( "$natural" ) ) { | |||
return true; | return true; | |||
} | } | |||
if ( range( e.fieldName() ).empty() ) { | if ( range( e.fieldName() ).empty() ) { | |||
return false; | return false; | |||
} | } | |||
} | } | |||
return true; | return true; | |||
} | } | |||
inline long long FieldRangeVector::size() { | inline unsigned FieldRangeVector::size() { | |||
long long ret = 1; | unsigned ret = 1; | |||
for( vector<FieldRange>::const_iterator i = _ranges.begin(); i != _ ranges.end(); ++i ) { | for( vector<FieldRange>::const_iterator i = _ranges.begin(); i != _ ranges.end(); ++i ) { | |||
ret *= i->intervals().size(); | unsigned long long product = | |||
(unsigned long long)ret * (unsigned long long)i->interv | ||||
als().size(); | ||||
// Check for overflow SERVER-5415. | ||||
verify( ( product >> 32 ) == 0 ); | ||||
ret = static_cast<unsigned>( product ); | ||||
} | } | |||
return ret; | return ret; | |||
} | } | |||
inline void FieldRangeVectorIterator::CompoundRangeCounter::set( int i, | ||||
int newVal ) { | ||||
resetIntervalCount(); | ||||
_i[ i ] = newVal; | ||||
} | ||||
inline void FieldRangeVectorIterator::CompoundRangeCounter::inc( int i | ||||
) { | ||||
resetIntervalCount(); | ||||
++_i[ i ]; | ||||
} | ||||
inline void FieldRangeVectorIterator::CompoundRangeCounter::setZeroes( | ||||
int i ) { | ||||
resetIntervalCount(); | ||||
for( int j = i; j < (int)_i.size(); ++j ) _i[ j ] = 0; | ||||
} | ||||
inline void FieldRangeVectorIterator::CompoundRangeCounter::setUnknowns | ||||
( int i ) { | ||||
resetIntervalCount(); | ||||
for( int j = i; j < (int)_i.size(); ++j ) _i[ j ] = -1; | ||||
} | ||||
inline FieldRangeSetPair *OrRangeGenerator::topFrsp() const { | inline FieldRangeSetPair *OrRangeGenerator::topFrsp() const { | |||
FieldRangeSetPair *ret = new FieldRangeSetPair( _baseSet ); | FieldRangeSetPair *ret = new FieldRangeSetPair( _baseSet ); | |||
if (_orSets.size()) { | if (_orSets.size()) { | |||
*ret &= _orSets.front(); | *ret &= _orSets.front(); | |||
} | } | |||
return ret; | return ret; | |||
} | } | |||
inline FieldRangeSetPair *OrRangeGenerator::topFrspOriginal() const { | inline FieldRangeSetPair *OrRangeGenerator::topFrspOriginal() const { | |||
FieldRangeSetPair *ret = new FieldRangeSetPair( _baseSet ); | FieldRangeSetPair *ret = new FieldRangeSetPair( _baseSet ); | |||
End of changes. 4 change blocks. | ||||
16 lines changed or deleted | 32 lines changed or added | |||
queryutil.h | queryutil.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 "jsobj.h" | #include "jsobj.h" | |||
#include "indexkey.h" | #include "indexkey.h" | |||
#include "projection.h" | ||||
namespace mongo { | namespace mongo { | |||
extern const int MaxBytesToReturnToClientAtOnce; | ||||
/* This is for languages whose "objects" are not well ordered (JSON is | ||||
well ordered). | ||||
[ { a : ... } , { b : ... } ] -> { a : ..., b : ... } | ||||
*/ | ||||
inline BSONObj transformOrderFromArrayFormat(BSONObj order) { | ||||
/* note: this is slow, but that is ok as order will have very few p | ||||
ieces */ | ||||
BSONObjBuilder b; | ||||
char p[2] = "0"; | ||||
while ( 1 ) { | ||||
BSONObj j = order.getObjectField(p); | ||||
if ( j.isEmpty() ) | ||||
break; | ||||
BSONElement e = j.firstElement(); | ||||
uassert( 10102 , "bad order array", !e.eoo()); | ||||
uassert( 10103 , "bad order array [2]", e.isNumber()); | ||||
b.append(e); | ||||
(*p)++; | ||||
uassert( 10104 , "too many ordering elements", *p <= '9'); | ||||
} | ||||
return b.obj(); | ||||
} | ||||
class QueryMessage; | ||||
/** | /** | |||
* One side of an interval of valid BSONElements, specified by a value | * this represents a total user query | |||
and a | * includes fields from the query message, both possible query levels | |||
* boolean indicating whether the interval includes the value. | * parses everything up front | |||
*/ | ||||
class ParsedQuery : boost::noncopyable { | ||||
public: | ||||
ParsedQuery( QueryMessage& qm ); | ||||
ParsedQuery( const char* ns , int ntoskip , int ntoreturn , int que | ||||
ryoptions , const BSONObj& query , const BSONObj& fields ) | ||||
: _ns( ns ) , _ntoskip( ntoskip ) , _ntoreturn( ntoreturn ) , _opti | ||||
ons( queryoptions ) { | ||||
init( query ); | ||||
initFields( fields ); | ||||
} | ||||
const char * ns() const { return _ns; } | ||||
bool isLocalDB() const { return strncmp(_ns, "local.", 6) == 0; } | ||||
const BSONObj& getFilter() const { return _filter; } | ||||
Projection* getFields() const { return _fields.get(); } | ||||
shared_ptr<Projection> getFieldPtr() const { return _fields; } | ||||
int getSkip() const { return _ntoskip; } | ||||
int getNumToReturn() const { return _ntoreturn; } | ||||
bool wantMore() const { return _wantMore; } | ||||
int getOptions() const { return _options; } | ||||
bool hasOption( int x ) const { return ( x & _options ) != 0; } | ||||
bool isExplain() const { return _explain; } | ||||
bool isSnapshot() const { return _snapshot; } | ||||
bool returnKey() const { return _returnKey; } | ||||
bool showDiskLoc() const { return _showDiskLoc; } | ||||
const BSONObj& getMin() const { return _min; } | ||||
const BSONObj& getMax() const { return _max; } | ||||
const BSONObj& getOrder() const { return _order; } | ||||
const BSONObj& getHint() const { return _hint; } | ||||
int getMaxScan() const { return _maxScan; } | ||||
bool couldBeCommand() const { | ||||
/* we assume you are using findOne() for running a cmd... */ | ||||
return _ntoreturn == 1 && strstr( _ns , ".$cmd" ); | ||||
} | ||||
bool hasIndexSpecifier() const { | ||||
return ! _hint.isEmpty() || ! _min.isEmpty() || ! _max.isEmpty( | ||||
); | ||||
} | ||||
/* if ntoreturn is zero, we return up to 101 objects. on the subse | ||||
quent getmore, there | ||||
is only a size limit. The idea is that on a find() where one does | ||||
n't use much results, | ||||
we don't return much, but once getmore kicks in, we start pushing | ||||
significant quantities. | ||||
The n limit (vs. size) is important when someone fetches only one | ||||
small field from big | ||||
objects, which causes massive scanning server-side. | ||||
*/ | ||||
bool enoughForFirstBatch( int n , int len ) const { | ||||
if ( _ntoreturn == 0 ) | ||||
return ( len > 1024 * 1024 ) || n >= 101; | ||||
return n >= _ntoreturn || len > MaxBytesToReturnToClientAtOnce; | ||||
} | ||||
bool enough( int n ) const { | ||||
if ( _ntoreturn == 0 ) | ||||
return false; | ||||
return n >= _ntoreturn; | ||||
} | ||||
bool enoughForExplain( long long n ) const { | ||||
if ( _wantMore || _ntoreturn == 0 ) { | ||||
return false; | ||||
} | ||||
return n >= _ntoreturn; | ||||
} | ||||
private: | ||||
void init( const BSONObj& q ) { | ||||
_reset(); | ||||
uassert( 10105 , "bad skip value in query", _ntoskip >= 0); | ||||
if ( _ntoreturn < 0 ) { | ||||
/* _ntoreturn greater than zero is simply a hint on how man | ||||
y objects to send back per | ||||
"cursor batch". | ||||
A negative number indicates a hard limit. | ||||
*/ | ||||
_wantMore = false; | ||||
_ntoreturn = -_ntoreturn; | ||||
} | ||||
BSONElement e = q["query"]; | ||||
if ( ! e.isABSONObj() ) | ||||
e = q["$query"]; | ||||
if ( e.isABSONObj() ) { | ||||
_filter = e.embeddedObject(); | ||||
_initTop( q ); | ||||
} | ||||
else { | ||||
_filter = q; | ||||
} | ||||
} | ||||
void _reset() { | ||||
_wantMore = true; | ||||
_explain = false; | ||||
_snapshot = false; | ||||
_returnKey = false; | ||||
_showDiskLoc = false; | ||||
_maxScan = 0; | ||||
} | ||||
void _initTop( const BSONObj& top ) { | ||||
BSONObjIterator i( top ); | ||||
while ( i.more() ) { | ||||
BSONElement e = i.next(); | ||||
const char * name = e.fieldName(); | ||||
if ( strcmp( "$orderby" , name ) == 0 || | ||||
strcmp( "orderby" , name ) == 0 ) { | ||||
if ( e.type() == Object ) { | ||||
_order = e.embeddedObject(); | ||||
} | ||||
else if ( e.type() == Array ) { | ||||
_order = transformOrderFromArrayFormat( _order ); | ||||
} | ||||
else { | ||||
uasserted(13513, "sort must be an object or array") | ||||
; | ||||
} | ||||
continue; | ||||
} | ||||
if( *name == '$' ) { | ||||
name++; | ||||
if ( strcmp( "explain" , name ) == 0 ) | ||||
_explain = e.trueValue(); | ||||
else if ( strcmp( "snapshot" , name ) == 0 ) | ||||
_snapshot = e.trueValue(); | ||||
else if ( strcmp( "min" , name ) == 0 ) | ||||
_min = e.embeddedObject(); | ||||
else if ( strcmp( "max" , name ) == 0 ) | ||||
_max = e.embeddedObject(); | ||||
else if ( strcmp( "hint" , name ) == 0 ) | ||||
_hint = e.wrap(); | ||||
else if ( strcmp( "returnKey" , name ) == 0 ) | ||||
_returnKey = e.trueValue(); | ||||
else if ( strcmp( "maxScan" , name ) == 0 ) | ||||
_maxScan = e.numberInt(); | ||||
else if ( strcmp( "showDiskLoc" , name ) == 0 ) | ||||
_showDiskLoc = e.trueValue(); | ||||
else if ( strcmp( "comment" , name ) == 0 ) { | ||||
; // no-op | ||||
} | ||||
} | ||||
} | ||||
if ( _snapshot ) { | ||||
uassert( 12001 , "E12001 can't sort with $snapshot", _order | ||||
.isEmpty() ); | ||||
uassert( 12002 , "E12002 can't use hint with $snapshot", _h | ||||
int.isEmpty() ); | ||||
} | ||||
} | ||||
void initFields( const BSONObj& fields ) { | ||||
if ( fields.isEmpty() ) | ||||
return; | ||||
_fields.reset( new Projection() ); | ||||
_fields->init( fields ); | ||||
} | ||||
const char * const _ns; | ||||
const int _ntoskip; | ||||
int _ntoreturn; | ||||
BSONObj _filter; | ||||
BSONObj _order; | ||||
const int _options; | ||||
shared_ptr< Projection > _fields; | ||||
bool _wantMore; | ||||
bool _explain; | ||||
bool _snapshot; | ||||
bool _returnKey; | ||||
bool _showDiskLoc; | ||||
BSONObj _min; | ||||
BSONObj _max; | ||||
BSONObj _hint; | ||||
int _maxScan; | ||||
}; | ||||
/** | ||||
* One side of an interval of BSONElements, defined by a value and a bo | ||||
olean indicating if the | ||||
* interval includes the value. | ||||
*/ | */ | |||
struct FieldBound { | struct FieldBound { | |||
BSONElement _bound; | BSONElement _bound; | |||
bool _inclusive; | bool _inclusive; | |||
bool operator==( const FieldBound &other ) const { | bool operator==( const FieldBound &other ) const { | |||
return _bound.woCompare( other._bound ) == 0 && | return _bound.woCompare( other._bound ) == 0 && | |||
_inclusive == other._inclusive; | _inclusive == other._inclusive; | |||
} | } | |||
void flipInclusive() { _inclusive = !_inclusive; } | void flipInclusive() { _inclusive = !_inclusive; } | |||
}; | }; | |||
/** A closed interval composed of a lower and an upper FieldBound. */ | /** An interval defined between a lower and an upper FieldBound. */ | |||
struct FieldInterval { | struct FieldInterval { | |||
FieldInterval() : _cachedEquality( -1 ) {} | FieldInterval() : _cachedEquality( -1 ) {} | |||
FieldInterval( const BSONElement& e ) : _cachedEquality( -1 ) { | FieldInterval( const BSONElement& e ) : _cachedEquality( -1 ) { | |||
_lower._bound = _upper._bound = e; | _lower._bound = _upper._bound = e; | |||
_lower._inclusive = _upper._inclusive = true; | _lower._inclusive = _upper._inclusive = true; | |||
} | } | |||
FieldBound _lower; | FieldBound _lower; | |||
FieldBound _upper; | FieldBound _upper; | |||
/** @return true iff no single element can be contained in the inte rval. */ | /** @return true iff no single element can be contained in the inte rval. */ | |||
bool strictValid() const { | bool strictValid() const { | |||
skipping to change at line 82 | skipping to change at line 295 | |||
/** @return Range of elements elements included in 'this' but not ' other'. */ | /** @return Range of elements elements included in 'this' but not ' other'. */ | |||
const FieldRange &operator-=( const FieldRange &other ); | const FieldRange &operator-=( const FieldRange &other ); | |||
/** @return true iff this range is a subset of 'other'. */ | /** @return true iff this range is a subset of 'other'. */ | |||
bool operator<=( const FieldRange &other ) const; | bool operator<=( const FieldRange &other ) const; | |||
/** | /** | |||
* If there are any valid values for this range, the extreme values can | * If there are any valid values for this range, the extreme values can | |||
* be extracted. | * be extracted. | |||
*/ | */ | |||
BSONElement min() const { assert( !empty() ); return _intervals[ 0 | BSONElement min() const { verify( !empty() ); return _intervals[ 0 | |||
]._lower._bound; } | ]._lower._bound; } | |||
BSONElement max() const { assert( !empty() ); return _intervals[ _i | BSONElement max() const { verify( !empty() ); return _intervals[ _i | |||
ntervals.size() - 1 ]._upper._bound; } | ntervals.size() - 1 ]._upper._bound; } | |||
bool minInclusive() const { assert( !empty() ); return _intervals[ | bool minInclusive() const { verify( !empty() ); return _intervals[ | |||
0 ]._lower._inclusive; } | 0 ]._lower._inclusive; } | |||
bool maxInclusive() const { assert( !empty() ); return _intervals[ | bool maxInclusive() const { verify( !empty() ); return _intervals[ | |||
_intervals.size() - 1 ]._upper._inclusive; } | _intervals.size() - 1 ]._upper._inclusive; } | |||
/** @return true iff this range expresses a single equality interva l. */ | /** @return true iff this range expresses a single equality interva l. */ | |||
bool equality() const; | bool equality() const; | |||
/** @return true if all the intervals for this range are equalities | ||||
*/ | ||||
bool inQuery() const; | ||||
/** | /** | |||
* @return true iff this range includes all BSONElements | * @return true iff this range includes all BSONElements | |||
* (the range is the universal set of BSONElements). | * (the range is the universal set of BSONElements). | |||
*/ | */ | |||
bool universal() const; | bool universal() const; | |||
/** @return true iff this range includes no BSONElements. */ | /** @return true iff this range includes no BSONElements. */ | |||
bool empty() const { return _intervals.empty(); } | bool empty() const { return _intervals.empty(); } | |||
/** | ||||
* @return true in many cases when this FieldRange describes a fini | ||||
te set of BSONElements, | ||||
* all of which will be matched by the query BSONElement that gener | ||||
ated this FieldRange. | ||||
* This attribute is used to implement higher level optimizations a | ||||
nd is computed with a | ||||
* simple implementation that identifies common (but not all) cases | ||||
satisfying the stated | ||||
* properties. | ||||
*/ | ||||
bool simpleFiniteSet() const { return _simpleFiniteSet; } | ||||
/** Empty the range so it includes no BSONElements. */ | /** Empty the range so it includes no BSONElements. */ | |||
void makeEmpty() { _intervals.clear(); } | void makeEmpty() { _intervals.clear(); } | |||
const vector<FieldInterval> &intervals() const { return _intervals; } | const vector<FieldInterval> &intervals() const { return _intervals; } | |||
string getSpecial() const { return _special; } | string getSpecial() const { return _special; } | |||
/** Make component intervals noninclusive. */ | /** Make component intervals noninclusive. */ | |||
void setExclusiveBounds(); | void setExclusiveBounds(); | |||
/** | /** | |||
* Constructs a range where all FieldIntervals and FieldBounds are in | * Constructs a range where all FieldIntervals and FieldBounds are in | |||
* the opposite order of the current range. | * the opposite order of the current range. | |||
* NOTE the resulting intervals might not be strictValid(). | * NOTE the resulting intervals might not be strictValid(). | |||
*/ | */ | |||
void reverse( FieldRange &ret ) const; | void reverse( FieldRange &ret ) const; | |||
string toString() const; | string toString() const; | |||
private: | private: | |||
BSONObj addObj( const BSONObj &o ); | BSONObj addObj( const BSONObj &o ); | |||
void finishOperation( const vector<FieldInterval> &newIntervals, co | void finishOperation( const vector<FieldInterval> &newIntervals, co | |||
nst FieldRange &other ); | nst FieldRange &other, | |||
bool simpleFiniteSet ); | ||||
vector<FieldInterval> _intervals; | vector<FieldInterval> _intervals; | |||
// Owns memory for our BSONElements. | // Owns memory for our BSONElements. | |||
vector<BSONObj> _objData; | vector<BSONObj> _objData; | |||
string _special; | string _special; | |||
bool _singleKey; | bool _singleKey; | |||
bool _simpleFiniteSet; | ||||
}; | }; | |||
/** | /** | |||
* 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; | |||
skipping to change at line 164 | skipping to change at line 385 | |||
* is not useful information for a single key FieldRangeSet and | * is not useful information for a single key FieldRangeSet and | |||
* matchPossibleForIndex() should be used instead. | * matchPossibleForIndex() should be used instead. | |||
*/ | */ | |||
bool matchPossible() const; | bool matchPossible() const; | |||
/** | /** | |||
* @return true if a match could be possible given the value of _si ngleKey | * @return true if a match could be possible given the value of _si ngleKey | |||
* and index key 'keyPattern'. | * and index key 'keyPattern'. | |||
* @param keyPattern May be {} or {$natural:1} for a non index scan . | * @param keyPattern May be {} or {$natural:1} for a non index scan . | |||
*/ | */ | |||
bool matchPossibleForIndex( const BSONObj &keyPattern ) const; | bool matchPossibleForIndex( const BSONObj &keyPattern ) const; | |||
/** | ||||
* @return true in many cases when this FieldRangeSet describes a f | ||||
inite set of BSONObjs, | ||||
* all of which will be matched by the query BSONObj that generated | ||||
this FieldRangeSet. | ||||
* This attribute is used to implement higher level optimizations a | ||||
nd is computed with a | ||||
* simple implementation that identifies common (but not all) cases | ||||
satisfying the stated | ||||
* properties. | ||||
*/ | ||||
bool simpleFiniteSet() const { return _simpleFiniteSet; } | ||||
const char *ns() const { return _ns; } | const char *ns() const { return _ns.c_str(); } | |||
/** | /** | |||
* @return a simplified query from the extreme values of the non un iversal | * @return a simplified query from the extreme values of the non un iversal | |||
* fields. | * fields. | |||
* @param fields If specified, the fields of the returned object ar e | * @param fields If specified, the fields of the returned object ar e | |||
* ordered to match those of 'fields'. | * ordered to match those of 'fields'. | |||
*/ | */ | |||
BSONObj simplifiedQuery( const BSONObj &fields = BSONObj() ) const; | BSONObj simplifiedQuery( const BSONObj &fields = BSONObj() ) const; | |||
QueryPattern pattern( const BSONObj &sort = BSONObj() ) const; | QueryPattern pattern( const BSONObj &sort = BSONObj() ) const; | |||
skipping to change at line 210 | skipping to change at line 439 | |||
* @return - A new FieldRangeSet based on this FieldRangeSet, but w ith only | * @return - A new FieldRangeSet based on this FieldRangeSet, but w ith only | |||
* a subset of the fields. | * a subset of the fields. | |||
* @param fields - Only fields which are represented as field names in this object | * @param fields - Only fields which are represented as field names in this object | |||
* will be included in the returned FieldRangeSet. | * will be included in the returned FieldRangeSet. | |||
*/ | */ | |||
FieldRangeSet *subset( const BSONObj &fields ) const; | FieldRangeSet *subset( const BSONObj &fields ) const; | |||
bool singleKey() const { return _singleKey; } | bool singleKey() const { return _singleKey; } | |||
BSONObj originalQuery() const { return _queries[ 0 ]; } | BSONObj originalQuery() const { return _queries[ 0 ]; } | |||
string toString() const; | ||||
private: | private: | |||
void appendQueries( const FieldRangeSet &other ); | void appendQueries( const FieldRangeSet &other ); | |||
void makeEmpty(); | void makeEmpty(); | |||
void processQueryField( const BSONElement &e, bool optimize ); | void processQueryField( const BSONElement &e, bool optimize ); | |||
void processOpElement( const char *fieldName, const BSONElement &f, bool isNot, bool optimize ); | void processOpElement( const char *fieldName, const BSONElement &f, bool isNot, bool optimize ); | |||
/** Must be called when a match element is skipped or modified to g | ||||
enerate a FieldRange. */ | ||||
void adjustMatchField(); | ||||
void intersectMatchField( const char *fieldName, const BSONElement | ||||
&matchElement, | ||||
bool isNot, bool optimize ); | ||||
static FieldRange *__singleKeyUniversalRange; | static FieldRange *__singleKeyUniversalRange; | |||
static FieldRange *__multiKeyUniversalRange; | static FieldRange *__multiKeyUniversalRange; | |||
const FieldRange &universalRange() const; | const FieldRange &universalRange() const; | |||
map<string,FieldRange> _ranges; | map<string,FieldRange> _ranges; | |||
const char *_ns; | string _ns; | |||
// Owns memory for FieldRange BSONElements. | // Owns memory for FieldRange BSONElements. | |||
vector<BSONObj> _queries; | vector<BSONObj> _queries; | |||
bool _singleKey; | bool _singleKey; | |||
bool _simpleFiniteSet; | ||||
}; | }; | |||
class NamespaceDetails; | class NamespaceDetails; | |||
/** | /** | |||
* A pair of FieldRangeSets, one representing constraints for single ke y | * A pair of FieldRangeSets, one representing constraints for single ke y | |||
* indexes and the other representing constraints for multi key indexes and | * indexes and the other representing constraints for multi key indexes and | |||
* unindexed scans. In several member functions the caller is asked to | * unindexed scans. In several member functions the caller is asked to | |||
* supply an index so that the implementation may utilize the proper | * supply an index so that the implementation may utilize the proper | |||
* FieldRangeSet and return results that are appropriate with respect t o that | * FieldRangeSet and return results that are appropriate with respect t o that | |||
skipping to change at line 247 | skipping to change at line 483 | |||
FieldRangeSetPair( const char *ns, const BSONObj &query, bool optim ize=true ) | FieldRangeSetPair( const char *ns, const BSONObj &query, bool optim ize=true ) | |||
:_singleKey( ns, query, true, optimize ), _multiKey( ns, query, fal se, optimize ) {} | :_singleKey( ns, query, true, optimize ), _multiKey( ns, query, fal se, optimize ) {} | |||
/** | /** | |||
* @return the appropriate single or multi key FieldRangeSet for th e specified index. | * @return the appropriate single or multi key FieldRangeSet for th e specified index. | |||
* @param idxNo -1 for non index scan. | * @param idxNo -1 for non index scan. | |||
*/ | */ | |||
const FieldRangeSet &frsForIndex( const NamespaceDetails* nsd, int idxNo ) const; | const FieldRangeSet &frsForIndex( const NamespaceDetails* nsd, int idxNo ) const; | |||
/** @return a field range in the single key FieldRangeSet. */ | /** @return a field range in the single key FieldRangeSet. */ | |||
const FieldRange &singleKeyRange( const char *fieldName ) const { | const FieldRange &shardKeyRange( const char *fieldName ) const { | |||
return _singleKey.range( fieldName ); | return _singleKey.range( fieldName ); | |||
} | } | |||
/** @return true if the range limits are equivalent to an empty que ry. */ | /** @return true if the range limits are equivalent to an empty que ry. */ | |||
bool noNonUniversalRanges() const; | bool noNonUniversalRanges() const; | |||
/** @return false if a match is impossible regardless of index. */ | /** @return false if a match is impossible regardless of index. */ | |||
bool matchPossible() const { return _multiKey.matchPossible(); } | bool matchPossible() const { return _multiKey.matchPossible(); } | |||
/** | /** | |||
* @return false if a match is impossible on the specified index. | * @return false if a match is impossible on the specified index. | |||
* @param idxNo -1 for non index scan. | * @param idxNo -1 for non index scan. | |||
*/ | */ | |||
skipping to change at line 272 | skipping to change at line 508 | |||
string getSpecial() const { return _singleKey.getSpecial(); } | string getSpecial() const { return _singleKey.getSpecial(); } | |||
/** Intersect with another FieldRangeSetPair. */ | /** Intersect with another FieldRangeSetPair. */ | |||
FieldRangeSetPair &operator&=( const FieldRangeSetPair &other ); | FieldRangeSetPair &operator&=( const FieldRangeSetPair &other ); | |||
/** | /** | |||
* Subtract a FieldRangeSet, generally one expressing a range that has | * Subtract a FieldRangeSet, generally one expressing a range that has | |||
* already been scanned. | * already been scanned. | |||
*/ | */ | |||
FieldRangeSetPair &operator-=( const FieldRangeSet &scanned ); | FieldRangeSetPair &operator-=( const FieldRangeSet &scanned ); | |||
BoundList singleKeyIndexBounds( const BSONObj &keyPattern, int dire | BoundList shardKeyIndexBounds( const BSONObj &keyPattern ) const { | |||
ction ) const { | return _singleKey.indexBounds( keyPattern, 1 ); | |||
return _singleKey.indexBounds( keyPattern, direction ); | } | |||
bool matchPossibleForShardKey( const BSONObj &keyPattern ) const { | ||||
return _singleKey.matchPossibleForIndex( keyPattern ); | ||||
} | } | |||
BSONObj originalQuery() const { return _singleKey.originalQuery(); } | BSONObj originalQuery() const { return _singleKey.originalQuery(); } | |||
string toString() const; | ||||
private: | private: | |||
FieldRangeSetPair( const FieldRangeSet &singleKey, const FieldRange Set &multiKey ) | FieldRangeSetPair( const FieldRangeSet &singleKey, const FieldRange Set &multiKey ) | |||
:_singleKey( singleKey ), _multiKey( multiKey ) {} | :_singleKey( singleKey ), _multiKey( multiKey ) {} | |||
void assertValidIndex( const NamespaceDetails *d, int idxNo ) const ; | void assertValidIndex( const NamespaceDetails *d, int idxNo ) const ; | |||
void assertValidIndexOrNoIndex( const NamespaceDetails *d, int idxN o ) const; | void assertValidIndexOrNoIndex( const NamespaceDetails *d, int idxN o ) const; | |||
/** matchPossibleForIndex() must be true. */ | /** matchPossibleForIndex() must be true. */ | |||
BSONObj simplifiedQueryForIndex( NamespaceDetails *d, int idxNo, co nst BSONObj &keyPattern ) const; | BSONObj simplifiedQueryForIndex( NamespaceDetails *d, int idxNo, co nst BSONObj &keyPattern ) const; | |||
FieldRangeSet _singleKey; | FieldRangeSet _singleKey; | |||
FieldRangeSet _multiKey; | FieldRangeSet _multiKey; | |||
friend class OrRangeGenerator; | friend class OrRangeGenerator; | |||
skipping to change at line 308 | skipping to change at line 549 | |||
public: | public: | |||
/** | /** | |||
* @param frs The valid ranges for all fields, as defined by the qu ery spec. None of the | * @param frs The valid ranges for all fields, as defined by the qu ery spec. None of the | |||
* fields in indexSpec may be empty() ranges of frs. | * fields in indexSpec may be empty() ranges of frs. | |||
* @param indexSpec The index spec (key pattern and info) | * @param indexSpec The index spec (key pattern and info) | |||
* @param direction The direction of index traversal | * @param direction The direction of index traversal | |||
*/ | */ | |||
FieldRangeVector( const FieldRangeSet &frs, const IndexSpec &indexS pec, int direction ); | FieldRangeVector( const FieldRangeSet &frs, const IndexSpec &indexS pec, int direction ); | |||
/** @return the number of index ranges represented by 'this' */ | /** @return the number of index ranges represented by 'this' */ | |||
long long size(); | unsigned size(); | |||
/** @return starting point for an index traversal. */ | /** @return starting point for an index traversal. */ | |||
BSONObj startKey() const; | BSONObj startKey() const; | |||
/** @return end point for an index traversal. */ | /** @return end point for an index traversal. */ | |||
BSONObj endKey() const; | BSONObj endKey() const; | |||
/** @return a client readable representation of 'this' */ | /** @return a client readable representation of 'this' */ | |||
BSONObj obj() const; | BSONObj obj() const; | |||
const IndexSpec& getSpec(){ return _indexSpec; } | const IndexSpec& getSpec(){ return _indexSpec; } | |||
/** | /** | |||
skipping to change at line 332 | skipping to change at line 573 | |||
* FieldRangeVector. This function is used for $or clause deduping . | * FieldRangeVector. This function is used for $or clause deduping . | |||
*/ | */ | |||
bool matches( const BSONObj &obj ) const; | bool matches( const BSONObj &obj ) const; | |||
/** | /** | |||
* @return first key of 'obj' that would be encountered by a forwar d | * @return first key of 'obj' that would be encountered by a forwar d | |||
* index scan using this FieldRangeVector, BSONObj() if no such key . | * index scan using this FieldRangeVector, BSONObj() if no such key . | |||
*/ | */ | |||
BSONObj firstMatch( const BSONObj &obj ) const; | BSONObj firstMatch( const BSONObj &obj ) const; | |||
string toString() const; | ||||
private: | private: | |||
int matchingLowElement( const BSONElement &e, int i, bool direction , bool &lowEquality ) const; | int matchingLowElement( const BSONElement &e, int i, bool direction , bool &lowEquality ) const; | |||
bool matchesElement( const BSONElement &e, int i, bool direction ) const; | bool matchesElement( const BSONElement &e, int i, bool direction ) const; | |||
bool matchesKey( const BSONObj &key ) const; | bool matchesKey( const BSONObj &key ) const; | |||
vector<FieldRange> _ranges; | vector<FieldRange> _ranges; | |||
const IndexSpec _indexSpec; | const IndexSpec _indexSpec; | |||
int _direction; | int _direction; | |||
vector<BSONObj> _queries; // make sure mem owned | vector<BSONObj> _queries; // make sure mem owned | |||
friend class FieldRangeVectorIterator; | friend class FieldRangeVectorIterator; | |||
}; | }; | |||
/** | /** | |||
* Helper class for iterating through an ordered representation of keys | * Helper class for iterating through an ordered representation of keys | |||
* to find those keys that match a specified FieldRangeVector. | * to find those keys that match a specified FieldRangeVector. | |||
*/ | */ | |||
class FieldRangeVectorIterator { | class FieldRangeVectorIterator { | |||
public: | public: | |||
FieldRangeVectorIterator( const FieldRangeVector &v ) : _v( v ), _i | /** | |||
( _v._ranges.size(), -1 ), _cmp( _v._ranges.size(), 0 ), _inc( _v._ranges.s | * @param v - a FieldRangeVector representing matching keys. | |||
ize(), false ), _after() { | * @param singleIntervalLimit - The maximum number of keys to match | |||
} | a single (compound) | |||
static BSONObj minObject() { | * interval before advancing to the next interval. Limit check | |||
BSONObjBuilder b; b.appendMinKey( "" ); | ing is disabled if 0 and | |||
return b.obj(); | * must be disabled if v contains FieldIntervals that are not e | |||
} | quality(). | |||
static BSONObj maxObject() { | */ | |||
BSONObjBuilder b; b.appendMaxKey( "" ); | FieldRangeVectorIterator( const FieldRangeVector &v, int singleInte | |||
return b.obj(); | rvalLimit ); | |||
} | ||||
/** | /** | |||
* @return Suggested advance method, based on current key. | * @return Suggested advance method through an ordered list of keys | |||
* -2 Iteration is complete, no need to advance. | with lookup support | |||
* -1 Advance to the next key, without skipping. | * (generally a btree). | |||
* >=0 Skip parameter. If @return is r, skip to the key comprised | * -2 Iteration is complete, no need to advance further. | |||
* of the first r elements of curr followed by the (r+1)th and | * -1 Advance to the next ordered key, without skipping. | |||
* remaining elements of cmp() (with inclusivity specified by | * >=0 Skip parameter, let's call it 'r'. If after() is true, ski | |||
* the (r+1)th and remaining elements of inc()). If after() i | p past the key prefix | |||
s | * comprised of the first r elements of curr. For example, if | |||
* true, skip past this key not to it. | curr is {a:1,b:1}, the | |||
* index is {a:1,b:1}, the direction is 1, and r == 1, skip pa | ||||
st {a:1,b:MaxKey}. If | ||||
* after() is false, skip to the key comprised of the first r | ||||
elements of curr followed | ||||
* by the (r+1)th and greater elements of cmp() (with inclusiv | ||||
ity specified by the | ||||
* (r+1)th and greater elements of inc()). For example, if cu | ||||
rr is {a:1,b:1}, the | ||||
* index is {a:1,b:1}, the direction is 1, r == 1, cmp()[1] == | ||||
b:4, and inc()[1] == | ||||
* true, then skip to {a:1,b:4}. Note that the element field | ||||
names in curr and cmp() | ||||
* should generally be ignored when performing index key compa | ||||
risons. | ||||
* @param curr The key at the current position in the list of keys. | ||||
Values of curr must be | ||||
* supplied in order. | ||||
*/ | */ | |||
int advance( const BSONObj &curr ); | int advance( const BSONObj &curr ); | |||
const vector<const BSONElement *> &cmp() const { return _cmp; } | const vector<const BSONElement *> &cmp() const { return _cmp; } | |||
const vector<bool> &inc() const { return _inc; } | const vector<bool> &inc() const { return _inc; } | |||
bool after() const { return _after; } | bool after() const { return _after; } | |||
void prepDive(); | void prepDive(); | |||
void setZero( int i ) { for( int j = i; j < (int)_i.size(); ++j ) _ | ||||
i[ j ] = 0; } | /** | |||
void setMinus( int i ) { for( int j = i; j < (int)_i.size(); ++j ) | * Helper class representing a position within a vector of ranges. | |||
_i[ j ] = -1; } | Public for testing. | |||
bool ok() { return _i[ 0 ] < (int)_v._ranges[ 0 ].intervals().size( | */ | |||
); } | class CompoundRangeCounter { | |||
BSONObj startKey(); | public: | |||
// temp | CompoundRangeCounter( int size, int singleIntervalLimit ); | |||
BSONObj endKey(); | int size() const { return (int)_i.size(); } | |||
int get( int i ) const { return _i[ i ]; } | ||||
void set( int i, int newVal ); | ||||
void inc( int i ); | ||||
void setZeroes( int i ); | ||||
void setUnknowns( int i ); | ||||
void incSingleIntervalCount() { | ||||
if ( isTrackingIntervalCounts() ) ++_singleIntervalCount; | ||||
} | ||||
bool hasSingleIntervalCountReachedLimit() const { | ||||
return isTrackingIntervalCounts() && _singleIntervalCount > | ||||
= _singleIntervalLimit; | ||||
} | ||||
void resetIntervalCount() { _singleIntervalCount = 0; } | ||||
private: | ||||
bool isTrackingIntervalCounts() const { return _singleIntervalL | ||||
imit > 0; } | ||||
vector<int> _i; | ||||
int _singleIntervalCount; | ||||
int _singleIntervalLimit; | ||||
}; | ||||
/** | ||||
* Helper class for matching a BSONElement with the bounds of a Fie | ||||
ldInterval. Some | ||||
* internal comparison results are cached. Public for testing. | ||||
*/ | ||||
class FieldIntervalMatcher { | ||||
public: | ||||
FieldIntervalMatcher( const FieldInterval &interval, const BSON | ||||
Element &element, | ||||
bool reverse ); | ||||
bool isEqInclusiveUpperBound() const { | ||||
return upperCmp() == 0 && _interval._upper._inclusive; | ||||
} | ||||
bool isGteUpperBound() const { return upperCmp() >= 0; } | ||||
bool isEqExclusiveLowerBound() const { | ||||
return lowerCmp() == 0 && !_interval._lower._inclusive; | ||||
} | ||||
bool isLtLowerBound() const { return lowerCmp() < 0; } | ||||
private: | ||||
struct BoundCmp { | ||||
BoundCmp() : _cmp(), _valid() {} | ||||
void set( int cmp ) { _cmp = cmp; _valid = true; } | ||||
int _cmp; | ||||
bool _valid; | ||||
}; | ||||
int mayReverse( int val ) const { return _reverse ? -val : val; | ||||
} | ||||
int cmp( const BSONElement &bound ) const { | ||||
return mayReverse( _element.woCompare( bound, false ) ); | ||||
} | ||||
void setCmp( BoundCmp &boundCmp, const BSONElement &bound ) con | ||||
st { | ||||
boundCmp.set( cmp( bound ) ); | ||||
} | ||||
int lowerCmp() const; | ||||
int upperCmp() const; | ||||
const FieldInterval &_interval; | ||||
const BSONElement &_element; | ||||
bool _reverse; | ||||
mutable BoundCmp _lowerCmp; | ||||
mutable BoundCmp _upperCmp; | ||||
}; | ||||
private: | private: | |||
/** | ||||
* @return values similar to advance() | ||||
* -2 Iteration is complete for the current interval. | ||||
* -1 Iteration is not complete for the current interval. | ||||
* >=0 Return value to be forwarded by advance(). | ||||
*/ | ||||
int validateCurrentInterval( int intervalIdx, const BSONElement &cu | ||||
rrElt, | ||||
bool reverse, bool first, bool &eqInclu | ||||
siveUpperBound ); | ||||
/** Skip to curr / i / nextbounds. */ | ||||
int advanceToLowerBound( int i ); | ||||
/** Skip to curr / i / superlative. */ | ||||
int advancePast( int i ); | ||||
/** Skip to curr / i / superlative and reset following interval pos | ||||
itions. */ | ||||
int advancePastZeroed( int i ); | ||||
bool hasReachedLimitForLastInterval( int intervalIdx ) const { | ||||
return _i.hasSingleIntervalCountReachedLimit() && ( intervalIdx | ||||
+ 1 == _i.size() ); | ||||
} | ||||
const FieldRangeVector &_v; | const FieldRangeVector &_v; | |||
vector<int> _i; | CompoundRangeCounter _i; | |||
vector<const BSONElement*> _cmp; | vector<const BSONElement*> _cmp; | |||
vector<bool> _inc; | vector<bool> _inc; | |||
bool _after; | bool _after; | |||
}; | }; | |||
/** | /** | |||
* As we iterate through $or clauses this class generates a FieldRangeS etPair | * As we iterate through $or clauses this class generates a FieldRangeS etPair | |||
* for the current $or clause, in some cases by excluding ranges that w ere | * for the current $or clause, in some cases by excluding ranges that w ere | |||
* included in a previous clause. | * included in a previous clause. | |||
*/ | */ | |||
End of changes. 24 change blocks. | ||||
56 lines changed or deleted | 425 lines changed or added | |||
queue.h | queue.h | |||
---|---|---|---|---|
skipping to change at line 20 | skipping to change at line 20 | |||
* | * | |||
* Unless required by applicable law or agreed to in writing, software | * Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | * distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. | |||
* See the License for the specific language governing permissions and | * See the License for the specific language governing permissions and | |||
* limitations under the License. | * limitations under the License. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../pch.h" | #include "pch.h" | |||
#include <limits> | ||||
#include <queue> | #include <queue> | |||
#include "../util/timer.h" | #include "mongo/util/timer.h" | |||
namespace mongo { | namespace mongo { | |||
/** | /** | |||
* simple blocking queue | * simple blocking queue | |||
*/ | */ | |||
template<typename T> class BlockingQueue : boost::noncopyable { | template<typename T> | |||
class BlockingQueue : boost::noncopyable { | ||||
public: | public: | |||
BlockingQueue() : _lock("BlockingQueue") { } | BlockingQueue() : | |||
_lock("BlockingQueue"), | ||||
_size(std::numeric_limits<std::size_t>::max()) { } | ||||
BlockingQueue(size_t size) : | ||||
_lock("BlockingQueue(bounded)"), | ||||
_size(size) { } | ||||
void push(T const& t) { | void push(T const& t) { | |||
scoped_lock l( _lock ); | scoped_lock l( _lock ); | |||
while (_queue.size() >= _size) { | ||||
_cvNoLongerFull.wait( l.boost() ); | ||||
} | ||||
_queue.push( t ); | _queue.push( t ); | |||
_condition.notify_one(); | _cvNoLongerEmpty.notify_one(); | |||
} | } | |||
bool empty() const { | bool empty() const { | |||
scoped_lock l( _lock ); | scoped_lock l( _lock ); | |||
return _queue.empty(); | return _queue.empty(); | |||
} | } | |||
size_t size() const { | size_t size() const { | |||
scoped_lock l( _lock ); | scoped_lock l( _lock ); | |||
return _queue.size(); | return _queue.size(); | |||
} | } | |||
bool tryPop( T & t ) { | bool tryPop( T & t ) { | |||
scoped_lock l( _lock ); | scoped_lock l( _lock ); | |||
if ( _queue.empty() ) | if ( _queue.empty() ) | |||
return false; | return false; | |||
t = _queue.front(); | t = _queue.front(); | |||
_queue.pop(); | _queue.pop(); | |||
_cvNoLongerFull.notify_one(); | ||||
return true; | return true; | |||
} | } | |||
T blockingPop() { | T blockingPop() { | |||
scoped_lock l( _lock ); | scoped_lock l( _lock ); | |||
while( _queue.empty() ) | while( _queue.empty() ) | |||
_condition.wait( l.boost() ); | _cvNoLongerEmpty.wait( l.boost() ); | |||
T t = _queue.front(); | T t = _queue.front(); | |||
_queue.pop(); | _queue.pop(); | |||
_cvNoLongerFull.notify_one(); | ||||
return t; | return t; | |||
} | } | |||
/** | /** | |||
* blocks waiting for an object until maxSecondsToWait passes | * blocks waiting for an object until maxSecondsToWait passes | |||
* if got one, return true and set in t | * if got one, return true and set in t | |||
* otherwise return false and t won't be changed | * otherwise return false and t won't be changed | |||
*/ | */ | |||
bool blockingPop( T& t , int maxSecondsToWait ) { | bool blockingPop( T& t , int maxSecondsToWait ) { | |||
Timer timer; | Timer timer; | |||
boost::xtime xt; | boost::xtime xt; | |||
boost::xtime_get(&xt, boost::TIME_UTC); | boost::xtime_get(&xt, boost::TIME_UTC); | |||
xt.sec += maxSecondsToWait; | xt.sec += maxSecondsToWait; | |||
scoped_lock l( _lock ); | scoped_lock l( _lock ); | |||
while( _queue.empty() ) { | while( _queue.empty() ) { | |||
if ( ! _condition.timed_wait( l.boost() , xt ) ) | if ( ! _cvNoLongerEmpty.timed_wait( l.boost() , xt ) ) | |||
return false; | return false; | |||
} | } | |||
t = _queue.front(); | t = _queue.front(); | |||
_queue.pop(); | _queue.pop(); | |||
_cvNoLongerFull.notify_one(); | ||||
return true; | return true; | |||
} | } | |||
private: | private: | |||
std::queue<T> _queue; | ||||
mutable mongo::mutex _lock; | mutable mongo::mutex _lock; | |||
boost::condition _condition; | std::queue<T> _queue; | |||
size_t _size; | ||||
boost::condition _cvNoLongerFull; | ||||
boost::condition _cvNoLongerEmpty; | ||||
}; | }; | |||
} | } | |||
End of changes. 14 change blocks. | ||||
10 lines changed or deleted | 25 lines changed or added | |||
redef_macros.h | redef_macros.h | |||
---|---|---|---|---|
/** @file redef_macros.h macros the implementation uses. | /** @file redef_macros.h macros for mongo internals | |||
@see undef_macros.h undefines these after use to minimize name pollutio n. | @see undef_macros.h undefines these after use to minimize name pollutio n. | |||
*/ | */ | |||
/* Copyright 2009 10gen Inc. | /* Copyright 2009 10gen Inc. | |||
* | * | |||
* Licensed under the Apache License, Version 2.0 (the "License"); | * Licensed under the Apache License, Version 2.0 (the "License"); | |||
* you may not use this file except in compliance with the License. | * you may not use this file except in compliance with the License. | |||
* You may obtain a copy of the License at | * You may obtain a copy of the License at | |||
* | * | |||
skipping to change at line 23 | skipping to change at line 23 | |||
* | * | |||
* 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. | |||
*/ | */ | |||
// If you define a new global un-prefixed macro, please add it here and in undef_macros | // If you define a new global un-prefixed macro, please add it here and in undef_macros | |||
// #pragma once // this file is intended to be processed multiple times | #define MONGO_MACROS_PUSHED 1 | |||
#if defined(MONGO_MACROS_CLEANED) | ||||
// util/allocator.h | // util/allocator.h | |||
#pragma push_macro("malloc") | ||||
#undef malloc | ||||
#define malloc MONGO_malloc | #define malloc MONGO_malloc | |||
#pragma push_macro("realloc") | ||||
#undef realloc | ||||
#define realloc MONGO_realloc | #define realloc MONGO_realloc | |||
// util/assert_util.h | // util/assert_util.h | |||
#define assert MONGO_assert | #pragma push_macro("verify") | |||
#undef verify | ||||
#define verify MONGO_verify | ||||
#pragma push_macro("dassert") | ||||
#undef dassert | ||||
#define dassert MONGO_dassert | #define dassert MONGO_dassert | |||
#pragma push_macro("wassert") | ||||
#undef wassert | ||||
#define wassert MONGO_wassert | #define wassert MONGO_wassert | |||
#pragma push_macro("massert") | ||||
#undef massert | ||||
#define massert MONGO_massert | #define massert MONGO_massert | |||
#pragma push_macro("uassert") | ||||
#undef uassert | ||||
#define uassert MONGO_uassert | #define uassert MONGO_uassert | |||
#define BOOST_CHECK_EXCEPTION MONGO_BOOST_CHECK_EXCEPTION | #pragma push_macro("DESTRUCTOR_GUARD") | |||
#undef DESTRUCTOR_GUARD | ||||
#define DESTRUCTOR_GUARD MONGO_DESTRUCTOR_GUARD | #define DESTRUCTOR_GUARD MONGO_DESTRUCTOR_GUARD | |||
// util/goodies.h | // util/goodies.h | |||
#pragma push_macro("PRINT") | ||||
#undef PRINT | ||||
#define PRINT MONGO_PRINT | #define PRINT MONGO_PRINT | |||
#pragma push_macro("PRINTFL") | ||||
#undef PRINTFL | ||||
#define PRINTFL MONGO_PRINTFL | #define PRINTFL MONGO_PRINTFL | |||
#define asctime MONGO_asctime | ||||
#define gmtime MONGO_gmtime | ||||
#define localtime MONGO_localtime | ||||
#define ctime MONGO_ctime | ||||
// util/debug_util.h | // util/debug_util.h | |||
#pragma push_macro("DEV") | ||||
#undef DEV | ||||
#define DEV MONGO_DEV | #define DEV MONGO_DEV | |||
#pragma push_macro("DEBUGGING") | ||||
#undef DEBUGGING | ||||
#define DEBUGGING MONGO_DEBUGGING | #define DEBUGGING MONGO_DEBUGGING | |||
#pragma push_macro("SOMETIMES") | ||||
#undef SOMETIMES | ||||
#define SOMETIMES MONGO_SOMETIMES | #define SOMETIMES MONGO_SOMETIMES | |||
#pragma push_macro("OCCASIONALLY") | ||||
#undef OCCASIONALLY | ||||
#define OCCASIONALLY MONGO_OCCASIONALLY | #define OCCASIONALLY MONGO_OCCASIONALLY | |||
#pragma push_macro("RARELY") | ||||
#undef RARELY | ||||
#define RARELY MONGO_RARELY | #define RARELY MONGO_RARELY | |||
#pragma push_macro("ONCE") | ||||
#undef ONCE | ||||
#define ONCE MONGO_ONCE | #define ONCE MONGO_ONCE | |||
// util/log.h | // util/log.h | |||
#pragma push_macro("LOG") | ||||
#undef LOG | ||||
#define LOG MONGO_LOG | #define LOG MONGO_LOG | |||
#undef MONGO_MACROS_CLEANED | ||||
#endif | ||||
End of changes. 20 change blocks. | ||||
10 lines changed or deleted | 37 lines changed or added | |||
repl.h | repl.h | |||
---|---|---|---|---|
skipping to change at line 33 | skipping to change at line 33 | |||
at the master: | at the master: | |||
local.oplog.$<source> | local.oplog.$<source> | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "pdfile.h" | #include "pdfile.h" | |||
#include "db.h" | #include "db.h" | |||
#include "dbhelpers.h" | #include "dbhelpers.h" | |||
#include "../client/dbclient.h" | ||||
#include "../util/optime.h" | #include "../util/optime.h" | |||
#include "oplog.h" | #include "oplog.h" | |||
#include "../util/concurrency/thread_pool.h" | #include "../util/concurrency/thread_pool.h" | |||
#include "oplogreader.h" | #include "oplogreader.h" | |||
#include "cloner.h" | #include "cloner.h" | |||
namespace mongo { | namespace mongo { | |||
/* replication slave? (possibly with slave) | /* replication slave? (possibly with slave) | |||
--slave cmd line setting -> SimpleSlave | --slave cmd line setting -> SimpleSlave | |||
End of changes. 1 change blocks. | ||||
1 lines changed or deleted | 0 lines changed or added | |||
replutil.h | replutil.h | |||
---|---|---|---|---|
skipping to change at line 22 | skipping to change at line 22 | |||
* | * | |||
* 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 "db.h" | #include "db.h" | |||
#include "dbhelpers.h" | #include "dbhelpers.h" | |||
#include "json.h" | #include "json.h" | |||
#include "../client/dbclient.h" | ||||
#include "repl.h" | #include "repl.h" | |||
#include "cmdline.h" | #include "cmdline.h" | |||
#include "repl/rs.h" | #include "repl/rs.h" | |||
#include "ops/query.h" | #include "mongo/db/queryutil.h" | |||
namespace mongo { | namespace mongo { | |||
extern const char *replAllDead; | extern const char *replAllDead; | |||
/* note we always return true for the "local" namespace. | /* note we always return true for the "local" namespace. | |||
we should not allow most operations when not the master | we should not allow most operations when not the master | |||
also we report not master if we are "dead". | also we report not master if we are "dead". | |||
skipping to change at line 69 | skipping to change at line 68 | |||
if ( cc().isGod() ) | if ( cc().isGod() ) | |||
return true; | return true; | |||
return false; | return false; | |||
} | } | |||
inline bool isMaster(const char * dbname = 0) { | inline bool isMaster(const char * dbname = 0) { | |||
if( _isMaster() ) | if( _isMaster() ) | |||
return true; | return true; | |||
if ( ! dbname ) { | if ( ! dbname ) { | |||
Database *database = cc().database(); | Database *database = cc().database(); | |||
assert( database ); | verify( database ); | |||
dbname = database->name.c_str(); | dbname = database->name.c_str(); | |||
} | } | |||
return strcmp( dbname , "local" ) == 0; | return strcmp( dbname , "local" ) == 0; | |||
} | } | |||
inline bool isMasterNs( const char *ns ) { | inline bool isMasterNs( const char *ns ) { | |||
if ( _isMaster() ) | if ( _isMaster() ) | |||
return true; | return true; | |||
assert( ns ); | verify( ns ); | |||
if ( ! str::startsWith( ns , "local" ) ) | if ( ! str::startsWith( ns , "local" ) ) | |||
return false; | return false; | |||
return ns[5] == 0 || ns[5] == '.'; | return ns[5] == 0 || ns[5] == '.'; | |||
} | } | |||
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 */ | /** we allow queries to SimpleSlave's */ | |||
inline void replVerifyReadsOk(ParsedQuery& pq) { | inline void replVerifyReadsOk(ParsedQuery* pq = 0) { | |||
if( replSet ) { | if( replSet ) { | |||
/* todo: speed up the secondary case. as written here there ar e 2 mutex entries, it can b 1. */ | /* todo: speed up the secondary case. as written here there ar e 2 mutex entries, it can b 1. */ | |||
if( isMaster() ) return; | if( isMaster() ) return; | |||
uassert(13435, "not master and slaveOk=false", pq.hasOption(Que ryOption_SlaveOk)); | uassert(13435, "not master and slaveOk=false", !pq || pq->hasOp tion(QueryOption_SlaveOk)); | |||
uassert(13436, "not master or secondary; cannot currently read from this replSet member", theReplSet && theReplSet->isSecondary() ); | uassert(13436, "not master or secondary; cannot currently read from this replSet member", theReplSet && theReplSet->isSecondary() ); | |||
} | } | |||
else { | else { | |||
notMasterUnless(isMaster() || pq.hasOption(QueryOption_SlaveOk) || replSettings.slave == SimpleSlave ); | notMasterUnless(isMaster() || (!pq || pq->hasOption(QueryOption _SlaveOk)) || replSettings.slave == SimpleSlave ); | |||
} | } | |||
} | } | |||
} // namespace mongo | } // namespace mongo | |||
End of changes. 7 change blocks. | ||||
7 lines changed or deleted | 6 lines changed or added | |||
request.h | request.h | |||
---|---|---|---|---|
skipping to change at line 53 | skipping to change at line 53 | |||
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; | |||
} | } | |||
DBConfigPtr getConfig() const { | DBConfigPtr getConfig() const { | |||
assert( _didInit ); | verify( _didInit ); | |||
return _config; | return _config; | |||
} | } | |||
bool isShardingEnabled() const { | bool isShardingEnabled() const { | |||
assert( _didInit ); | verify( _didInit ); | |||
return _config->isShardingEnabled(); | return _config->isShardingEnabled(); | |||
} | } | |||
ChunkManagerPtr getChunkManager() const { | ChunkManagerPtr getChunkManager() const { | |||
assert( _didInit ); | verify( _didInit ); | |||
return _chunkManager; | return _chunkManager; | |||
} | } | |||
ClientInfo * getClientInfo() const { | ClientInfo * getClientInfo() const { | |||
return _clientInfo; | return _clientInfo; | |||
} | } | |||
/** | /** | |||
* @param ns - 0=use ns from message | * @param ns - 0=use ns from message | |||
*/ | */ | |||
End of changes. 3 change blocks. | ||||
3 lines changed or deleted | 3 lines changed or added | |||
rs.h | rs.h | |||
---|---|---|---|---|
skipping to change at line 218 | skipping to change at line 218 | |||
/** | /** | |||
* most operations on a ReplSet object should be done while locked. tha t | * most operations on a ReplSet object should be done while locked. tha t | |||
* logic implemented here. | * logic implemented here. | |||
* | * | |||
* Order of locking: lock the replica set, then take a rwlock. | * Order of locking: lock the replica set, then take a rwlock. | |||
*/ | */ | |||
class RSBase : boost::noncopyable { | class RSBase : boost::noncopyable { | |||
public: | public: | |||
const unsigned magic; | const unsigned magic; | |||
void assertValid() { assert( magic == 0x12345677 ); } | void assertValid() { verify( magic == 0x12345677 ); } | |||
private: | private: | |||
mongo::mutex m; | mongo::mutex m; | |||
int _locked; | int _locked; | |||
ThreadLocalValue<bool> _lockedByMe; | ThreadLocalValue<bool> _lockedByMe; | |||
protected: | protected: | |||
RSBase() : magic(0x12345677), m("RSBase"), _locked(0) { } | RSBase() : magic(0x12345677), m("RSBase"), _locked(0) { } | |||
~RSBase() { | ~RSBase() { | |||
/* this can happen if we throw in the constructor; otherwise ne ver happens. thus we log it as it is quite unusual. */ | /* this can happen if we throw in the constructor; otherwise ne ver happens. thus we log it as it is quite unusual. */ | |||
log() << "replSet ~RSBase called" << rsLog; | log() << "replSet ~RSBase called" << rsLog; | |||
} | } | |||
skipping to change at line 240 | skipping to change at line 240 | |||
public: | public: | |||
class lock { | class lock { | |||
RSBase& rsbase; | RSBase& rsbase; | |||
auto_ptr<scoped_lock> sl; | auto_ptr<scoped_lock> sl; | |||
public: | public: | |||
lock(RSBase* b) : rsbase(*b) { | lock(RSBase* b) : rsbase(*b) { | |||
if( rsbase._lockedByMe.get() ) | if( rsbase._lockedByMe.get() ) | |||
return; // recursive is ok... | return; // recursive is ok... | |||
sl.reset( new scoped_lock(rsbase.m) ); | sl.reset( new scoped_lock(rsbase.m) ); | |||
DEV assert(rsbase._locked == 0); | DEV verify(rsbase._locked == 0); | |||
rsbase._locked++; | rsbase._locked++; | |||
rsbase._lockedByMe.set(true); | rsbase._lockedByMe.set(true); | |||
} | } | |||
~lock() { | ~lock() { | |||
if( sl.get() ) { | if( sl.get() ) { | |||
assert( rsbase._lockedByMe.get() ); | verify( rsbase._lockedByMe.get() ); | |||
DEV assert(rsbase._locked == 1); | DEV verify(rsbase._locked == 1); | |||
rsbase._lockedByMe.set(false); | rsbase._lockedByMe.set(false); | |||
rsbase._locked--; | rsbase._locked--; | |||
} | } | |||
} | } | |||
}; | }; | |||
/* for asserts */ | /* for asserts */ | |||
bool locked() const { return _locked != 0; } | bool locked() const { return _locked != 0; } | |||
/* if true, is locked, and was locked by this thread. note if false , it could be in the lock or not for another | /* if true, is locked, and was locked by this thread. note if false , it could be in the lock or not for another | |||
skipping to change at line 308 | skipping to change at line 308 | |||
} | } | |||
} | } | |||
void set(MemberState s, const Member *p) { | void set(MemberState s, const Member *p) { | |||
rwlock lk(m, true); | rwlock lk(m, true); | |||
sp.state = s; | sp.state = s; | |||
sp.primary = p; | sp.primary = p; | |||
} | } | |||
void setSelfPrimary(const Member *self) { change(MemberState::RS_PR IMARY, self); } | void setSelfPrimary(const Member *self) { change(MemberState::RS_PR IMARY, self); } | |||
void setOtherPrimary(const Member *mem) { | void setOtherPrimary(const Member *mem) { | |||
rwlock lk(m, true); | rwlock lk(m, true); | |||
assert( !sp.state.primary() ); | verify( !sp.state.primary() ); | |||
sp.primary = mem; | sp.primary = mem; | |||
} | } | |||
void noteRemoteIsPrimary(const Member *remote) { | void noteRemoteIsPrimary(const Member *remote) { | |||
rwlock lk(m, true); | rwlock lk(m, true); | |||
if( !sp.state.secondary() && !sp.state.fatal() ) | if( !sp.state.secondary() && !sp.state.fatal() ) | |||
sp.state = MemberState::RS_RECOVERING; | sp.state = MemberState::RS_RECOVERING; | |||
sp.primary = remote; | sp.primary = remote; | |||
} | } | |||
StateBox() : m("StateBox") { } | StateBox() : m("StateBox") { } | |||
private: | private: | |||
skipping to change at line 358 | skipping to change at line 358 | |||
static DiagStr startupStatusMsg; | static DiagStr startupStatusMsg; | |||
static string stateAsHtml(MemberState state); | static string stateAsHtml(MemberState state); | |||
/* todo thread */ | /* todo thread */ | |||
void msgUpdateHBInfo(HeartbeatInfo); | void msgUpdateHBInfo(HeartbeatInfo); | |||
StateBox box; | StateBox box; | |||
OpTime lastOpTimeWritten; | OpTime lastOpTimeWritten; | |||
long long lastH; // hash we use to make sure we are reading the rig ht flow of ops and aren't on an out-of-date "fork" | long long lastH; // hash we use to make sure we are reading the rig ht flow of ops and aren't on an out-of-date "fork" | |||
bool forceSyncFrom(const string& host, string& errmsg, BSONObjBuild er& result); | ||||
private: | private: | |||
set<ReplSetHealthPollTask*> healthTasks; | set<ReplSetHealthPollTask*> healthTasks; | |||
void endOldHealthTasks(); | void endOldHealthTasks(); | |||
void startHealthTaskFor(Member *m); | void startHealthTaskFor(Member *m); | |||
Consensus elect; | Consensus elect; | |||
void relinquish(); | void relinquish(); | |||
void forgetPrimary(); | void forgetPrimary(); | |||
protected: | protected: | |||
bool _stepDown(int secs); | bool _stepDown(int secs); | |||
skipping to change at line 380 | skipping to change at line 381 | |||
void assumePrimary(); | void assumePrimary(); | |||
void loadLastOpTimeWritten(bool quiet=false); | void loadLastOpTimeWritten(bool quiet=false); | |||
void changeState(MemberState s); | void changeState(MemberState s); | |||
/** | /** | |||
* Find the closest member (using ping time) with a higher latest o ptime. | * Find the closest member (using ping time) with a higher latest o ptime. | |||
*/ | */ | |||
Member* getMemberToSyncTo(); | Member* getMemberToSyncTo(); | |||
void veto(const string& host, unsigned secs=10); | void veto(const string& host, unsigned secs=10); | |||
Member* _currentSyncTarget; | Member* _currentSyncTarget; | |||
Member* _forceSyncTarget; | ||||
bool _blockSync; | bool _blockSync; | |||
void blockSync(bool block); | void blockSync(bool block); | |||
// set of electable members' _ids | // set of electable members' _ids | |||
set<unsigned> _electableSet; | set<unsigned> _electableSet; | |||
protected: | protected: | |||
// "heartbeat message" | // "heartbeat message" | |||
// sent in requestHeartbeat respond in field "hbm" | // sent in requestHeartbeat respond in field "hbm" | |||
char _hbmsg[256]; // we change this unlocked, thus not an stl::stri ng | char _hbmsg[256]; // we change this unlocked, thus not an stl::stri ng | |||
skipping to change at line 556 | skipping to change at line 558 | |||
public: | public: | |||
ReplSet(ReplSetCmdline& replSetCmdline) : ReplSetImpl(replSetCmdlin e) { } | ReplSet(ReplSetCmdline& replSetCmdline) : ReplSetImpl(replSetCmdlin e) { } | |||
// for the replSetStepDown command | // for the replSetStepDown command | |||
bool stepDown(int secs) { return _stepDown(secs); } | bool stepDown(int secs) { return _stepDown(secs); } | |||
// for the replSetFreeze command | // for the replSetFreeze command | |||
bool freeze(int secs) { return _freeze(secs); } | bool freeze(int secs) { return _freeze(secs); } | |||
string selfFullName() { | string selfFullName() { | |||
assert( _self ); | verify( _self ); | |||
return _self->fullName(); | return _self->fullName(); | |||
} | } | |||
bool buildIndexes() const { return _buildIndexes; } | bool buildIndexes() const { return _buildIndexes; } | |||
/* call after constructing to start - returns fairly quickly after la[unching its threads */ | /* call after constructing to start - returns fairly quickly after la[unching its threads */ | |||
void go() { _go(); } | void go() { _go(); } | |||
void fatal() { _fatal(); } | void fatal() { _fatal(); } | |||
bool isPrimary() { return box.getState().primary(); } | bool isPrimary() { return box.getState().primary(); } | |||
skipping to change at line 662 | skipping to change at line 664 | |||
/** | /** | |||
* does local authentication | * does local authentication | |||
* directly authorizes against AuthenticationInfo | * directly authorizes against AuthenticationInfo | |||
*/ | */ | |||
void replLocalAuth(); | void replLocalAuth(); | |||
/** inlines ----------------- */ | /** inlines ----------------- */ | |||
inline Member::Member(HostAndPort h, unsigned ord, const ReplSetConfig: :MemberCfg *c, bool self) : | inline Member::Member(HostAndPort h, unsigned ord, const ReplSetConfig: :MemberCfg *c, bool self) : | |||
_config(*c), _h(h), _hbinfo(ord) { | _config(*c), _h(h), _hbinfo(ord) { | |||
assert(c); | verify(c); | |||
if( self ) | if( self ) | |||
_hbinfo.health = 1.0; | _hbinfo.health = 1.0; | |||
} | } | |||
} | } | |||
End of changes. 8 change blocks. | ||||
7 lines changed or deleted | 9 lines changed or added | |||
rs_config.h | rs_config.h | |||
---|---|---|---|---|
skipping to change at line 34 | skipping to change at line 34 | |||
#include "../../util/concurrency/race.h" | #include "../../util/concurrency/race.h" | |||
#include "health.h" | #include "health.h" | |||
namespace mongo { | namespace mongo { | |||
class Member; | class Member; | |||
const string rsConfigNs = "local.system.replset"; | const string rsConfigNs = "local.system.replset"; | |||
class ReplSetConfig { | class ReplSetConfig { | |||
enum { EMPTYCONFIG = -2 }; | enum { EMPTYCONFIG = -2 }; | |||
struct TagSubgroup; | struct TagSubgroup; | |||
// Protects _groups. | ||||
static mongo::mutex groupMx; | ||||
public: | public: | |||
/** | /** | |||
* This contacts the given host and tries to get a config from them . | * This contacts the given host and tries to get a config from them . | |||
* | * | |||
* This sends a test heartbeat to the host and, if all goes well an d the | * This sends a test heartbeat to the host and, if all goes well an d the | |||
* host has a more recent config, fetches the config and loads it ( see | * host has a more recent config, fetches the config and loads it ( see | |||
* from(). | * from(). | |||
* | * | |||
* If it's contacting itself, it skips the heartbeat (for obvious | * If it's contacting itself, it skips the heartbeat (for obvious | |||
* reasons.) If something is misconfigured, throws an exception. If the | * reasons.) If something is misconfigured, throws an exception. If the | |||
skipping to change at line 79 | skipping to change at line 82 | |||
return _groups; | return _groups; | |||
} | } | |||
set<TagSubgroup*>& groupsw() { | set<TagSubgroup*>& groupsw() { | |||
return _groups; | return _groups; | |||
} | } | |||
void check() const; /* check validity, assert if not. */ | void check() const; /* check validity, assert if not. */ | |||
BSONObj asBson() const; | BSONObj asBson() const; | |||
bool potentiallyHot() const { return !arbiterOnly && priority > 0; } | bool potentiallyHot() const { return !arbiterOnly && priority > 0; } | |||
void updateGroups(const OpTime& last) { | void updateGroups(const OpTime& last) { | |||
RACECHECK | RACECHECK | |||
scoped_lock lk(ReplSetConfig::groupMx); | ||||
for (set<TagSubgroup*>::const_iterator it = groups().begin( ); it != groups().end(); it++) { | for (set<TagSubgroup*>::const_iterator it = groups().begin( ); it != groups().end(); it++) { | |||
(*it)->updateLast(last); | (*it)->updateLast(last); | |||
} | } | |||
} | } | |||
bool operator==(const MemberCfg& r) const { | bool operator==(const MemberCfg& r) const { | |||
if (!tags.empty() || !r.tags.empty()) { | if (!tags.empty() || !r.tags.empty()) { | |||
if (tags.size() != r.tags.size()) { | if (tags.size() != r.tags.size()) { | |||
return false; | return false; | |||
} | } | |||
End of changes. 2 change blocks. | ||||
0 lines changed or deleted | 4 lines changed or added | |||
rs_member.h | rs_member.h | |||
---|---|---|---|---|
skipping to change at line 45 | skipping to change at line 45 | |||
enum MS { | enum MS { | |||
RS_STARTUP = 0, | RS_STARTUP = 0, | |||
RS_PRIMARY = 1, | RS_PRIMARY = 1, | |||
RS_SECONDARY = 2, | RS_SECONDARY = 2, | |||
RS_RECOVERING = 3, | RS_RECOVERING = 3, | |||
RS_FATAL = 4, | RS_FATAL = 4, | |||
RS_STARTUP2 = 5, | RS_STARTUP2 = 5, | |||
RS_UNKNOWN = 6, /* remote node not yet reached */ | RS_UNKNOWN = 6, /* remote node not yet reached */ | |||
RS_ARBITER = 7, | RS_ARBITER = 7, | |||
RS_DOWN = 8, /* node not reachable for a report */ | RS_DOWN = 8, /* node not reachable for a report */ | |||
RS_ROLLBACK = 9 | RS_ROLLBACK = 9, | |||
RS_SHUNNED = 10, /* node shunned from replica set */ | ||||
} s; | } s; | |||
MemberState(MS ms = RS_UNKNOWN) : s(ms) { } | MemberState(MS ms = RS_UNKNOWN) : s(ms) { } | |||
explicit MemberState(int ms) : s((MS) ms) { } | explicit MemberState(int ms) : s((MS) ms) { } | |||
bool startup() const { return s == RS_STARTUP; } | bool startup() const { return s == RS_STARTUP; } | |||
bool primary() const { return s == RS_PRIMARY; } | bool primary() const { return s == RS_PRIMARY; } | |||
bool secondary() const { return s == RS_SECONDARY; } | bool secondary() const { return s == RS_SECONDARY; } | |||
bool recovering() const { return s == RS_RECOVERING; } | bool recovering() const { return s == RS_RECOVERING; } | |||
bool startup2() const { return s == RS_STARTUP2; } | bool startup2() const { return s == RS_STARTUP2; } | |||
bool fatal() const { return s == RS_FATAL; } | bool fatal() const { return s == RS_FATAL; } | |||
bool rollback() const { return s == RS_ROLLBACK; } | bool rollback() const { return s == RS_ROLLBACK; } | |||
bool readable() const { return s == RS_PRIMARY || s == RS_SECONDARY ; } | bool readable() const { return s == RS_PRIMARY || s == RS_SECONDARY ; } | |||
bool shunned() const { return s == RS_SHUNNED; } | ||||
string toString() const; | string toString() const; | |||
bool operator==(const MemberState& r) const { return s == r.s; } | bool operator==(const MemberState& r) const { return s == r.s; } | |||
bool operator!=(const MemberState& r) const { return s != r.s; } | bool operator!=(const MemberState& r) const { return s != r.s; } | |||
}; | }; | |||
/* this is supposed to be just basic information on a member, | /* this is supposed to be just basic information on a member, | |||
and copy constructable. */ | and copy constructable. */ | |||
class HeartbeatInfo { | class HeartbeatInfo { | |||
skipping to change at line 126 | skipping to change at line 128 | |||
case RS_STARTUP: return "STARTUP"; | case RS_STARTUP: return "STARTUP"; | |||
case RS_PRIMARY: return "PRIMARY"; | case RS_PRIMARY: return "PRIMARY"; | |||
case RS_SECONDARY: return "SECONDARY"; | case RS_SECONDARY: return "SECONDARY"; | |||
case RS_RECOVERING: return "RECOVERING"; | case RS_RECOVERING: return "RECOVERING"; | |||
case RS_FATAL: return "FATAL"; | case RS_FATAL: return "FATAL"; | |||
case RS_STARTUP2: return "STARTUP2"; | case RS_STARTUP2: return "STARTUP2"; | |||
case RS_ARBITER: return "ARBITER"; | case RS_ARBITER: return "ARBITER"; | |||
case RS_DOWN: return "DOWN"; | case RS_DOWN: return "DOWN"; | |||
case RS_ROLLBACK: return "ROLLBACK"; | case RS_ROLLBACK: return "ROLLBACK"; | |||
case RS_UNKNOWN: return "UNKNOWN"; | case RS_UNKNOWN: return "UNKNOWN"; | |||
case RS_SHUNNED: return "REMOVED"; | ||||
} | } | |||
return ""; | return ""; | |||
} | } | |||
} | } | |||
End of changes. 3 change blocks. | ||||
1 lines changed or deleted | 4 lines changed or added | |||
rs_optime.h | rs_optime.h | |||
---|---|---|---|---|
skipping to change at line 27 | skipping to change at line 27 | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../../util/optime.h" | #include "../../util/optime.h" | |||
namespace mongo { | namespace mongo { | |||
const char rsoplog[] = "local.oplog.rs"; | const char rsoplog[] = "local.oplog.rs"; | |||
/* | ||||
class RSOpTime : public OpTime { | ||||
public: | ||||
bool initiated() const { return getSecs() != 0; } | ||||
};*/ | ||||
/*struct RSOpTime { | ||||
unsigned long long ord; | ||||
RSOpTime() : ord(0) { } | ||||
bool initiated() const { return ord > 0; } | ||||
void initiate() { | ||||
assert( !initiated() ); | ||||
ord = 1000000; | ||||
} | ||||
ReplTime inc() { | ||||
DEV assertInWriteLock(); | ||||
return ++ord; | ||||
} | ||||
string toString() const { return str::stream() << ord; } | ||||
// query the oplog and set the highest value herein. acquires a db | ||||
read lock. throws. | ||||
void load(); | ||||
}; | ||||
extern RSOpTime rsOpTime;*/ | ||||
} | } | |||
End of changes. 1 change blocks. | ||||
32 lines changed or deleted | 0 lines changed or added | |||
rwlock.h | rwlock.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 Lice nse | * You should have received a copy of the GNU Affero General Public Lice nse | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "mutex.h" | #include "mutex.h" | |||
#include "../time_support.h" | #include "../time_support.h" | |||
#include "rwlockimpl.h" | #include "rwlockimpl.h" | |||
#if defined(_DEBUG) | #if defined(_DEBUG) | |||
#include "mutexdebugger.h" | #include "mutexdebugger.h" | |||
#endif | #endif | |||
#include "simplerwlock.h" | ||||
namespace mongo { | namespace mongo { | |||
/** separated out as later the implementation of this may be different | ||||
than RWLock, | ||||
depending on OS, as there is no upgrade etc. facility herein. | ||||
*/ | ||||
class SimpleRWLock : public RWLockBase { | ||||
public: | ||||
explicit SimpleRWLock(const char *) { } | ||||
SimpleRWLock() { } | ||||
void lock() { RWLockBase::lock(); } | ||||
void unlock() { RWLockBase::unlock(); } | ||||
void lock_shared() { RWLockBase::lock_shared(); } | ||||
void unlock_shared() { RWLockBase::unlock_shared(); } | ||||
class Shared : boost::noncopyable { | ||||
SimpleRWLock& _r; | ||||
public: | ||||
Shared(SimpleRWLock& rwlock) : _r(rwlock) {_r.lock_shared(); } | ||||
~Shared() { _r.unlock_shared(); } | ||||
}; | ||||
class Exclusive : boost::noncopyable { | ||||
SimpleRWLock& _r; | ||||
public: | ||||
Exclusive(SimpleRWLock& rwlock) : _r(rwlock) {_r.lock(); } | ||||
~Exclusive() { _r.unlock(); } | ||||
}; | ||||
}; | ||||
class RWLock : public RWLockBase { | class RWLock : public RWLockBase { | |||
enum { NilState, UpgradableState, Exclusive } x; // only bother to set when doing upgradable related things | enum { NilState, UpgradableState, Exclusive } x; // only bother to set when doing upgradable related things | |||
public: | public: | |||
const char * const _name; | const char * const _name; | |||
RWLock(const char *name) : _name(name) { | RWLock(const char *name) : _name(name) { | |||
x = NilState; | x = NilState; | |||
} | } | |||
void lock() { | void lock() { | |||
RWLockBase::lock(); | RWLockBase::lock(); | |||
#if defined(_DEBUG) | #if defined(_DEBUG) | |||
skipping to change at line 85 | skipping to change at line 60 | |||
void lock_shared() { RWLockBase::lock_shared(); } | void lock_shared() { RWLockBase::lock_shared(); } | |||
void unlock_shared() { RWLockBase::unlock_shared(); } | void unlock_shared() { RWLockBase::unlock_shared(); } | |||
private: | private: | |||
void lockAsUpgradable() { RWLockBase::lockAsUpgradable(); } | void lockAsUpgradable() { RWLockBase::lockAsUpgradable(); } | |||
void unlockFromUpgradable() { // upgradable -> unlocked | void unlockFromUpgradable() { // upgradable -> unlocked | |||
RWLockBase::unlockFromUpgradable(); | RWLockBase::unlockFromUpgradable(); | |||
} | } | |||
public: | public: | |||
void upgrade() { // upgradable -> exclusive lock | void upgrade() { // upgradable -> exclusive lock | |||
assert( x == UpgradableState ); | verify( x == UpgradableState ); | |||
RWLockBase::upgrade(); | RWLockBase::upgrade(); | |||
x = Exclusive; | x = Exclusive; | |||
} | } | |||
bool lock_shared_try( int millis ) { return RWLockBase::lock_shared _try(millis); } | bool lock_shared_try( int millis ) { return RWLockBase::lock_shared _try(millis); } | |||
bool lock_try( int millis = 0 ) { | bool lock_try( int millis = 0 ) { | |||
if( RWLockBase::lock_try(millis) ) { | if( RWLockBase::lock_try(millis) ) { | |||
#if defined(_DEBUG) | #if defined(_DEBUG) | |||
mutexDebugger.entering(_name); | mutexDebugger.entering(_name); | |||
skipping to change at line 111 | skipping to change at line 86 | |||
/** acquire upgradable state. You must be unlocked before creating . | /** acquire upgradable state. You must be unlocked before creating . | |||
unlocks on destruction, whether in upgradable state or upgraded to exclusive | unlocks on destruction, whether in upgradable state or upgraded to exclusive | |||
in the interim. | in the interim. | |||
*/ | */ | |||
class Upgradable : boost::noncopyable { | class Upgradable : boost::noncopyable { | |||
RWLock& _r; | RWLock& _r; | |||
public: | public: | |||
Upgradable(RWLock& r) : _r(r) { | Upgradable(RWLock& r) : _r(r) { | |||
r.lockAsUpgradable(); | r.lockAsUpgradable(); | |||
assert( _r.x == NilState ); | verify( _r.x == NilState ); | |||
_r.x = RWLock::UpgradableState; | _r.x = RWLock::UpgradableState; | |||
} | } | |||
~Upgradable() { | ~Upgradable() { | |||
if( _r.x == RWLock::UpgradableState ) { | if( _r.x == RWLock::UpgradableState ) { | |||
_r.x = NilState; | _r.x = NilState; | |||
_r.unlockFromUpgradable(); | _r.unlockFromUpgradable(); | |||
} | } | |||
else { | else { | |||
//TEMP assert( _r.x == Exclusive ); // has been upgraded | //TEMP verify( _r.x == Exclusive ); // has been upgraded | |||
_r.x = NilState; | _r.x = NilState; | |||
_r.unlock(); | _r.unlock(); | |||
} | } | |||
} | } | |||
}; | }; | |||
}; | }; | |||
/** throws on failure to acquire in the specified time period. */ | /** throws on failure to acquire in the specified time period. */ | |||
class rwlock_try_write : boost::noncopyable { | class rwlock_try_write : boost::noncopyable { | |||
public: | public: | |||
skipping to change at line 192 | skipping to change at line 167 | |||
protected: | protected: | |||
ThreadLocalValue<int> _state; | ThreadLocalValue<int> _state; | |||
void lock(); // not implemented - Lock() should be used; didn't ove rload this name to avoid mistakes | void lock(); // not implemented - Lock() should be used; didn't ove rload this name to avoid mistakes | |||
virtual void Lock() { RWLockBase::lock(); } | virtual void Lock() { RWLockBase::lock(); } | |||
public: | public: | |||
virtual ~RWLockRecursive() { } | virtual ~RWLockRecursive() { } | |||
const char * const _name; | const char * const _name; | |||
RWLockRecursive(const char *name) : _name(name) { } | RWLockRecursive(const char *name) : _name(name) { } | |||
void assertExclusivelyLocked() { | void assertExclusivelyLocked() { | |||
assert( _state.get() < 0 ); | verify( _state.get() < 0 ); | |||
} | } | |||
class Exclusive : boost::noncopyable { | class Exclusive : boost::noncopyable { | |||
RWLockRecursive& _r; | RWLockRecursive& _r; | |||
public: | public: | |||
Exclusive(RWLockRecursive& r) : _r(r) { | Exclusive(RWLockRecursive& r) : _r(r) { | |||
int s = _r._state.get(); | int s = _r._state.get(); | |||
dassert( s <= 0 ); | dassert( s <= 0 ); | |||
if( s == 0 ) | if( s == 0 ) | |||
_r.Lock(); | _r.Lock(); | |||
End of changes. 7 change blocks. | ||||
31 lines changed or deleted | 5 lines changed or added | |||
rwlockimpl.h | rwlockimpl.h | |||
---|---|---|---|---|
// @file rwlockimpl.h | // @file rwlockimpl.h | |||
#pragma once | #pragma once | |||
#include "mutex.h" | #include "mutex.h" | |||
//#define RWLOCK_TEST 1 | ||||
namespace mongo { | ||||
class RWLockBase1 : boost::noncopyable { | ||||
unsigned reading; | ||||
unsigned writing; | ||||
unsigned wantToWrite; | ||||
boost::mutex m; | ||||
boost::condition m_cond; | ||||
boost::mutex writer; | ||||
public: | ||||
RWLockBase1(); | ||||
~RWLockBase1(); | ||||
const char * implType() const { return "mongo"; } | ||||
void lock(); | ||||
void unlock(); | ||||
void lock_shared(); | ||||
void unlock_shared(); | ||||
bool lock_shared_try(int millis); | ||||
bool lock_try(int millis = 0); | ||||
void lockAsUpgradable(); | ||||
void unlockFromUpgradable(); | ||||
void upgrade(); | ||||
}; | ||||
} | ||||
#if defined(RWLOCK_TEST) | #if defined(RWLOCK_TEST) | |||
namespace mongo { | namespace mongo { | |||
typedef RWLockBase1 RWLockBase; | typedef RWLockBase1 RWLockBase; | |||
} | } | |||
#elif defined(MONGO_USE_SRW_ON_WINDOWS) && defined(_WIN32) | #elif defined(MONGO_USE_SRW_ON_WINDOWS) && defined(_WIN32) | |||
// windows slimreaderwriter version. newer windows versions only | // windows slimreaderwriter version. newer windows versions only | |||
namespace mongo { | namespace mongo { | |||
unsigned long long curTimeMicros64(); | unsigned long long curTimeMicros64(); | |||
class RWLockBase : boost::noncopyable { | class RWLockBase : boost::noncopyable { | |||
friend class SimpleRWLock; | ||||
SRWLOCK _lock; | SRWLOCK _lock; | |||
protected: | protected: | |||
RWLockBase() { InitializeSRWLock(&_lock); } | RWLockBase() { InitializeSRWLock(&_lock); } | |||
~RWLockBase() { | ~RWLockBase() { | |||
// no special action needed to destroy a SRWLOCK | // no special action needed to destroy a SRWLOCK | |||
} | } | |||
void lock() { AcquireSRWLockExclusive(&_lock); } | void lock() { AcquireSRWLockExclusive(&_lock); } | |||
void unlock() { ReleaseSRWLockExclusive(&_lock); } | void unlock() { ReleaseSRWLockExclusive(&_lock); } | |||
void lock_shared() { AcquireSRWLockShared(&_lock); } | void lock_shared() { AcquireSRWLockShared(&_lock); } | |||
void unlock_shared() { ReleaseSRWLockShared(&_lock); } | void unlock_shared() { ReleaseSRWLockShared(&_lock); } | |||
skipping to change at line 110 | skipping to change at line 83 | |||
# error need boost >= 1.35 for windows | # error need boost >= 1.35 for windows | |||
# endif | # endif | |||
// pthreads version | // pthreads version | |||
# include <pthread.h> | # include <pthread.h> | |||
# include <errno.h> | # include <errno.h> | |||
namespace mongo { | namespace mongo { | |||
class RWLockBase : boost::noncopyable { | class RWLockBase : boost::noncopyable { | |||
friend class SimpleRWLock; | ||||
pthread_rwlock_t _lock; | pthread_rwlock_t _lock; | |||
static void check( int x ) { | static void check( int x ) { | |||
assert( x == 0 ); | verify( x == 0 ); | |||
} | } | |||
protected: | protected: | |||
~RWLockBase() { | ~RWLockBase() { | |||
if ( ! StaticObserver::_destroyingStatics ) { | if ( ! StaticObserver::_destroyingStatics ) { | |||
wassert( pthread_rwlock_destroy( &_lock ) == 0 ); // wasser t as don't want to throw from a destructor | wassert( pthread_rwlock_destroy( &_lock ) == 0 ); // wasser t as don't want to throw from a destructor | |||
} | } | |||
} | } | |||
RWLockBase() { | RWLockBase() { | |||
check( pthread_rwlock_init( &_lock , 0 ) ); | check( pthread_rwlock_init( &_lock , 0 ) ); | |||
} | } | |||
void lock() { check( pthread_rwlock_wrlock( &_lock ) ); } | void lock() { check( pthread_rwlock_wrlock( &_lock ) ); } | |||
void unlock() { check( pthread_rwlock_unlock( &_lock ) ); } | void unlock() { check( pthread_rwlock_unlock( &_lock ) ); } | |||
void lock_shared() { check( pthread_rwlock_rdlock( &_lock ) ); } | void lock_shared() { check( pthread_rwlock_rdlock( &_lock ) ); } | |||
void unlock_shared() { check( pthread_rwlock_unlock( &_lock ) ); } | void unlock_shared() { check( pthread_rwlock_unlock( &_lock ) ); } | |||
bool lock_shared_try( int millis ) { return _try( millis , false ); } | bool lock_shared_try( int millis ) { return _try( millis , false ); } | |||
bool lock_try( int millis = 0 ) { return _try( millis , true ); } | bool lock_try( int millis = 0 ) { return _try( millis , true ); } | |||
bool _try( int millis , bool write ) { | bool _try( int millis , bool write ) { | |||
while ( true ) { | while ( true ) { | |||
int x = write ? | int x = write ? | |||
pthread_rwlock_trywrlock( &_lock ) : | pthread_rwlock_trywrlock( &_lock ) : | |||
skipping to change at line 169 | skipping to change at line 140 | |||
// Boost version | // Boost version | |||
# if defined(_WIN32) | # if defined(_WIN32) | |||
# include "shared_mutex_win.hpp" | # include "shared_mutex_win.hpp" | |||
namespace mongo { typedef boost::modified_shared_mutex shared_mutex; } | namespace mongo { typedef boost::modified_shared_mutex shared_mutex; } | |||
# else | # else | |||
# include <boost/thread/shared_mutex.hpp> | # include <boost/thread/shared_mutex.hpp> | |||
namespace mongo { using boost::shared_mutex; } | namespace mongo { using boost::shared_mutex; } | |||
# endif | # endif | |||
# undef assert | ||||
# define assert MONGO_assert | ||||
namespace mongo { | namespace mongo { | |||
class RWLockBase : boost::noncopyable { | class RWLockBase : boost::noncopyable { | |||
friend class SimpleRWLock; | ||||
shared_mutex _m; | shared_mutex _m; | |||
protected: | protected: | |||
void lock() { | void lock() { | |||
_m.lock(); | _m.lock(); | |||
} | } | |||
void unlock() { | void unlock() { | |||
_m.unlock(); | _m.unlock(); | |||
} | } | |||
void lockAsUpgradable() { | void lockAsUpgradable() { | |||
_m.lock_upgrade(); | _m.lock_upgrade(); | |||
End of changes. 9 change blocks. | ||||
34 lines changed or deleted | 4 lines changed or added | |||
scanandorder.h | scanandorder.h | |||
---|---|---|---|---|
skipping to change at line 37 | skipping to change at line 37 | |||
namespace mongo { | namespace mongo { | |||
static const int ScanAndOrderMemoryLimitExceededAssertionCode = 10128; | static const int ScanAndOrderMemoryLimitExceededAssertionCode = 10128; | |||
class KeyType : boost::noncopyable { | class KeyType : boost::noncopyable { | |||
public: | public: | |||
IndexSpec _spec; | IndexSpec _spec; | |||
FieldRangeVector _keyCutter; | FieldRangeVector _keyCutter; | |||
public: | public: | |||
KeyType(const BSONObj &pattern, const FieldRangeSet &frs): | KeyType(const BSONObj &pattern, const FieldRangeSet &frs): | |||
_spec((assert(!pattern.isEmpty()),pattern)), | _spec((verify(!pattern.isEmpty()),pattern)), | |||
_keyCutter(frs, _spec, 1) { | _keyCutter(frs, _spec, 1) { | |||
} | } | |||
/** | /** | |||
* @return first key of the object that would be encountered while | * @return first key of the object that would be encountered while | |||
* scanning index with keySpec 'pattern' using constraints 'frs', o r | * scanning an index with keySpec 'pattern' using constraints 'frs' , or | |||
* BSONObj() if no such key. | * BSONObj() if no such key. | |||
*/ | */ | |||
BSONObj getKeyFromObject(const BSONObj &o) const { | BSONObj getKeyFromObject(const BSONObj &o) const { | |||
return _keyCutter.firstMatch(o); | return _keyCutter.firstMatch(o); | |||
} | } | |||
}; | }; | |||
/* todo: | /* todo: | |||
_ response size limit from runquery; push it up a bit. | _ response size limit from runquery; push it up a bit. | |||
*/ | */ | |||
End of changes. 2 change blocks. | ||||
2 lines changed or deleted | 2 lines changed or added | |||
scopeguard.h | scopeguard.h | |||
---|---|---|---|---|
skipping to change at line 18 | skipping to change at line 18 | |||
// purpose is hereby granted without fee, provided that the above copyr ight | // purpose is hereby granted without fee, provided that the above copyr ight | |||
// notice appear in all copies and that both that copyright notice and this | // notice appear in all copies and that both that copyright notice and this | |||
// permission notice appear in supporting documentation. | // permission notice appear in supporting documentation. | |||
// The author makes no representations about the | // The author makes no representations about the | |||
// suitability of this software for any purpose. It is provided "as is" | // suitability of this software for any purpose. It is provided "as is" | |||
// without express or implied warranty. | // without express or implied warranty. | |||
/////////////////////////////////////////////////////////////////////////// ///// | /////////////////////////////////////////////////////////////////////////// ///// | |||
#ifndef LOKI_SCOPEGUARD_H_ | #ifndef LOKI_SCOPEGUARD_H_ | |||
#define LOKI_SCOPEGUARD_H_ | #define LOKI_SCOPEGUARD_H_ | |||
#include "mongo/platform/compiler.h" | ||||
namespace mongo | namespace mongo | |||
{ | { | |||
/////////////////////////////////////////////////////////////////////// ///////// | /////////////////////////////////////////////////////////////////////// ///////// | |||
/// \class RefToValue | /// \class RefToValue | |||
/// | /// | |||
/// Transports a reference as a value | /// Transports a reference as a value | |||
/// Serves to implement the Colvin/Gibbons trick for SmartPtr/ScopeGua rd | /// Serves to implement the Colvin/Gibbons trick for SmartPtr/ScopeGua rd | |||
/////////////////////////////////////////////////////////////////////// ///////// | /////////////////////////////////////////////////////////////////////// ///////// | |||
skipping to change at line 417 | skipping to change at line 419 | |||
{ | { | |||
return ObjScopeGuardImpl2<Obj1,Ret(Obj2::*)(P1a,P2a),P1b,P2b>::Make ObjGuard(*obj,memFun,p1,p2); | return ObjScopeGuardImpl2<Obj1,Ret(Obj2::*)(P1a,P2a),P1b,P2b>::Make ObjGuard(*obj,memFun,p1,p2); | |||
} | } | |||
} // namespace Loki | } // namespace Loki | |||
#define LOKI_CONCATENATE_DIRECT(s1, s2) s1##s2 | #define LOKI_CONCATENATE_DIRECT(s1, s2) s1##s2 | |||
#define LOKI_CONCATENATE(s1, s2) LOKI_CONCATENATE_DIRECT(s1, s2) | #define LOKI_CONCATENATE(s1, s2) LOKI_CONCATENATE_DIRECT(s1, s2) | |||
#define LOKI_ANONYMOUS_VARIABLE(str) LOKI_CONCATENATE(str, __LINE__) | #define LOKI_ANONYMOUS_VARIABLE(str) LOKI_CONCATENATE(str, __LINE__) | |||
#ifdef __GNUC__ | #define ON_BLOCK_EXIT \ | |||
#define ON_BLOCK_EXIT ScopeGuard LOKI_ANONYMOUS_VARIABLE(scopeGuard) __attr | MONGO_COMPILER_VARIABLE_UNUSED ScopeGuard LOKI_ANONYMOUS_VARIABLE(scope | |||
ibute__ ((unused)) = MakeGuard | Guard) = MakeGuard | |||
#define ON_BLOCK_EXIT_OBJ ScopeGuard LOKI_ANONYMOUS_VARIABLE(scopeGuard) __ | #define ON_BLOCK_EXIT_OBJ \ | |||
attribute__ ((unused)) = MakeObjGuard | MONGO_COMPILER_VARIABLE_UNUSED ScopeGuard LOKI_ANONYMOUS_VARIABLE(scope | |||
#else | Guard) = MakeObjGuard | |||
#define ON_BLOCK_EXIT ScopeGuard LOKI_ANONYMOUS_VARIABLE(scopeGuard) = Make | ||||
Guard | ||||
#define ON_BLOCK_EXIT_OBJ ScopeGuard LOKI_ANONYMOUS_VARIABLE(scopeGuard) = | ||||
MakeObjGuard | ||||
#endif | ||||
#endif //LOKI_SCOPEGUARD_H_ | #endif //LOKI_SCOPEGUARD_H_ | |||
End of changes. 2 change blocks. | ||||
11 lines changed or deleted | 8 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 "nonce.h" | #include "mongo/db/authlevel.h" | |||
#include "concurrency.h" | #include "mongo/db/nonce.h" | |||
#include "security_common.h" | #include "mongo/db/security_common.h" | |||
#include "../util/concurrency/spin_lock.h" | #include "mongo/util/concurrency/spin_lock.h" | |||
// this is used by both mongos and mongod | // this is used by both mongos and mongod | |||
namespace mongo { | namespace mongo { | |||
/* | /** An AuthenticationInfo object is present within every mongo::Client | |||
* for a particular db | object */ | |||
* levels | ||||
* 0 : none | ||||
* 1 : read | ||||
* 2 : write | ||||
*/ | ||||
struct Auth { | ||||
enum Level { NONE = 0 , READ = 1 , WRITE = 2 }; | ||||
Auth() { level = NONE; } | ||||
Level level; | ||||
string user; | ||||
}; | ||||
class AuthenticationInfo : boost::noncopyable { | class AuthenticationInfo : boost::noncopyable { | |||
bool _isLocalHost; | ||||
bool _isLocalHostAndLocalHostIsAuthorizedForAll; | ||||
public: | public: | |||
bool isLocalHost; | void startRequest(); // need to call at the beginning of each reque | |||
st | ||||
AuthenticationInfo(){ isLocalHost = false; } | void setIsALocalHostConnectionWithSpecialAuthPowers(); // called, i | |||
f localhost, when conneciton established. | ||||
AuthenticationInfo() { | ||||
_isLocalHost = false; | ||||
_isLocalHostAndLocalHostIsAuthorizedForAll = false; | ||||
} | ||||
~AuthenticationInfo() {} | ~AuthenticationInfo() {} | |||
bool isLocalHost() const { return _isLocalHost; } // why are you ca lling this? makes no sense to be externalized | ||||
// -- modifiers ---- | // -- modifiers ---- | |||
void logout(const string& dbname ) { | void logout(const string& dbname ) { | |||
scoped_spinlock lk(_lock); | scoped_spinlock lk(_lock); | |||
_dbs.erase(dbname); | _dbs.erase(dbname); | |||
} | } | |||
void authorize(const string& dbname , const string& user ) { | void authorize(const string& dbname , const string& user ) { | |||
scoped_spinlock lk(_lock); | scoped_spinlock lk(_lock); | |||
_dbs[dbname].level = Auth::WRITE; | _dbs[dbname].level = Auth::WRITE; | |||
skipping to change at line 95 | skipping to change at line 86 | |||
} | } | |||
bool isAuthorizedForLevel( const string& dbname , Auth::Level level ) const { | bool isAuthorizedForLevel( const string& dbname , Auth::Level level ) const { | |||
return _isAuthorized( dbname , level ); | return _isAuthorized( dbname , level ); | |||
} | } | |||
string getUser( const string& dbname ) const; | string getUser( const string& dbname ) const; | |||
void print() const; | void print() const; | |||
protected: | private: | |||
void _checkLocalHostSpecialAdmin(); | ||||
/** takes a lock */ | /** takes a lock */ | |||
bool _isAuthorized(const string& dbname, Auth::Level level) const; | bool _isAuthorized(const string& dbname, Auth::Level level) const; | |||
bool _isAuthorizedSingle_inlock(const string& dbname, Auth::Level l evel) const; | bool _isAuthorizedSingle_inlock(const string& dbname, Auth::Level l evel) const; | |||
/** cannot call this locked */ | /** cannot call this locked */ | |||
bool _isAuthorizedSpecialChecks( const string& dbname ) const ; | bool _isAuthorizedSpecialChecks( const string& dbname ) const ; | |||
private: | private: | |||
// while most access to _dbs is from our thread (the TLS thread), c | ||||
urrentOp() inspects | ||||
// it too thus we need this | ||||
mutable SpinLock _lock; | mutable SpinLock _lock; | |||
// todo: caching should not last forever | ||||
typedef map<string,Auth> MA; | typedef map<string,Auth> MA; | |||
MA _dbs; // dbname -> auth | MA _dbs; // dbname -> auth | |||
static bool _warned; | static bool _warned; | |||
}; | }; | |||
} // namespace mongo | } // namespace mongo | |||
End of changes. 8 change blocks. | ||||
24 lines changed or deleted | 24 lines changed or added | |||
security_common.h | security_common.h | |||
---|---|---|---|---|
skipping to change at line 22 | skipping to change at line 22 | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Affero General Public License for more details. | * GNU Affero General Public License for more details. | |||
* | * | |||
* You should have received a copy of the GNU Affero General Public 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 "commands.h" | #include "commands.h" | |||
#include "concurrency.h" | ||||
#include "../util/concurrency/spin_lock.h" | #include "../util/concurrency/spin_lock.h" | |||
namespace mongo { | namespace mongo { | |||
/** | /** | |||
* Internal secret key info. | * Internal secret key info. | |||
*/ | */ | |||
struct AuthInfo { | struct AuthInfo { | |||
AuthInfo() { | AuthInfo() { | |||
user = "__system"; | user = "__system"; | |||
End of changes. 1 change blocks. | ||||
1 lines changed or deleted | 0 lines changed or added | |||
shard.h | shard.h | |||
---|---|---|---|---|
skipping to change at line 21 | skipping to change at line 21 | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Affero General Public License for more details. | * GNU Affero General Public License for more details. | |||
* | * | |||
* You should have received a copy of the GNU Affero General Public Licen se | * You should have received a copy of the GNU Affero General Public Licen se | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../pch.h" | #include "pch.h" | |||
#include "../client/connpool.h" | ||||
#include "mongo/client/connpool.h" | ||||
#include "mongo/client/dbclient_rs.h" | ||||
namespace mongo { | namespace mongo { | |||
class ShardConnection; | class ShardConnection; | |||
class ShardStatus; | class ShardStatus; | |||
/* | /* | |||
* A "shard" one partition of the overall database (and a replica set t ypically). | * A "shard" one partition of the overall database (and a replica set t ypically). | |||
*/ | */ | |||
skipping to change at line 74 | skipping to change at line 76 | |||
/** | /** | |||
* @param ident either name or address | * @param ident either name or address | |||
*/ | */ | |||
void reset( const string& ident ); | void reset( const string& ident ); | |||
void setAddress( const ConnectionString& cs ); | void setAddress( const ConnectionString& cs ); | |||
ConnectionString getAddress() const { return _cs; } | ConnectionString getAddress() const { return _cs; } | |||
string getName() const { | string getName() const { | |||
assert( _name.size() ); | verify( _name.size() ); | |||
return _name; | return _name; | |||
} | } | |||
string getConnString() const { | string getConnString() const { | |||
assert( _addr.size() ); | verify( _addr.size() ); | |||
return _addr; | return _addr; | |||
} | } | |||
long long getMaxSize() const { | long long getMaxSize() const { | |||
return _maxSize; | return _maxSize; | |||
} | } | |||
bool isDraining() const { | bool isDraining() const { | |||
return _isDraining; | return _isDraining; | |||
} | } | |||
skipping to change at line 103 | skipping to change at line 105 | |||
} | } | |||
friend ostream& operator << (ostream& out, const Shard& s) { | friend ostream& operator << (ostream& out, const Shard& s) { | |||
return (out << s.toString()); | return (out << s.toString()); | |||
} | } | |||
bool operator==( const Shard& s ) const { | bool operator==( const Shard& s ) const { | |||
bool n = _name == s._name; | bool n = _name == s._name; | |||
bool a = _addr == s._addr; | bool a = _addr == s._addr; | |||
assert( n == a ); // names and address are 1 to 1 | verify( n == a ); // names and address are 1 to 1 | |||
return n; | return n; | |||
} | } | |||
bool operator!=( const Shard& s ) const { | bool operator!=( const Shard& s ) const { | |||
bool n = _name == s._name; | bool n = _name == s._name; | |||
bool a = _addr == s._addr; | bool a = _addr == s._addr; | |||
return ! ( n && a ); | return ! ( n && a ); | |||
} | } | |||
bool operator==( const string& s ) const { | bool operator==( const string& s ) const { | |||
skipping to change at line 227 | skipping to change at line 229 | |||
ShardConnection( const Shard& s , const string& ns, ChunkManagerPtr manager = ChunkManagerPtr() ); | ShardConnection( const Shard& s , const string& ns, ChunkManagerPtr manager = ChunkManagerPtr() ); | |||
ShardConnection( const string& addr , const string& ns, ChunkManage rPtr manager = ChunkManagerPtr() ); | ShardConnection( const string& addr , const string& ns, ChunkManage rPtr manager = ChunkManagerPtr() ); | |||
~ShardConnection(); | ~ShardConnection(); | |||
void done(); | void done(); | |||
void kill(); | void kill(); | |||
DBClientBase& conn() { | DBClientBase& conn() { | |||
_finishInit(); | _finishInit(); | |||
assert( _conn ); | verify( _conn ); | |||
return *_conn; | return *_conn; | |||
} | } | |||
DBClientBase* operator->() { | DBClientBase* operator->() { | |||
_finishInit(); | _finishInit(); | |||
assert( _conn ); | verify( _conn ); | |||
return _conn; | return _conn; | |||
} | } | |||
DBClientBase* get() { | DBClientBase* get() { | |||
_finishInit(); | _finishInit(); | |||
assert( _conn ); | verify( _conn ); | |||
return _conn; | return _conn; | |||
} | } | |||
string getHost() const { | string getHost() const { | |||
return _addr; | return _addr; | |||
} | } | |||
string getNS() const { | string getNS() const { | |||
return _ns; | return _ns; | |||
} | } | |||
End of changes. 7 change blocks. | ||||
8 lines changed or deleted | 10 lines changed or added | |||
shardkey.h | shardkey.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 "../client/dbclient.h" | ||||
namespace mongo { | namespace mongo { | |||
class Chunk; | class Chunk; | |||
/* A ShardKeyPattern is a pattern indicating what data to extract from the object to make the shard key from. | /* A ShardKeyPattern is a pattern indicating what data to extract from the object to make the shard key from. | |||
Analogous to an index key pattern. | Analogous to an index key pattern. | |||
*/ | */ | |||
class ShardKeyPattern { | class ShardKeyPattern { | |||
public: | public: | |||
ShardKeyPattern( BSONObj p = BSONObj() ); | ShardKeyPattern( BSONObj p = BSONObj() ); | |||
skipping to change at line 68 | skipping to change at line 66 | |||
l < r negative | l < r negative | |||
l == r 0 | l == r 0 | |||
l > r positive | l > r positive | |||
*/ | */ | |||
int compare( const BSONObj& l , const BSONObj& r ) const; | int compare( const BSONObj& l , const BSONObj& r ) const; | |||
/** | /** | |||
@return whether or not obj has all fields in this shard key patt ern | @return whether or not obj has all fields in this shard key patt ern | |||
e.g. | e.g. | |||
ShardKey({num:1}).hasShardKey({ name:"joe", num:3 }) is true | ShardKey({num:1}).hasShardKey({ name:"joe", num:3 }) is true | |||
ShardKey({"a.b":1}).hasShardKey({ "a.b":"joe"}) is true | ||||
ShardKey({"a.b":1}).hasShardKey({ "a": {"b":"joe"}}) is true | ||||
ShardKey({num:1}).hasShardKey({ name:"joe"}) is false | ||||
ShardKey({num:1}).hasShardKey({ name:"joe", num:{$gt:3} }) is | ||||
false | ||||
see unit test for more examples | ||||
*/ | */ | |||
bool hasShardKey( const BSONObj& obj ) const; | bool hasShardKey( const BSONObj& obj ) const; | |||
BSONObj key() const { return pattern; } | BSONObj key() const { return pattern; } | |||
string toString() const; | string toString() const; | |||
BSONObj extractKey(const BSONObj& from) const; | BSONObj extractKey(const BSONObj& from) const; | |||
bool partOfShardKey(const char* key ) const { | bool partOfShardKey(const char* key ) const { | |||
End of changes. 2 change blocks. | ||||
2 lines changed or deleted | 8 lines changed or added | |||
signal_handlers.h | signal_handlers.h | |||
---|---|---|---|---|
skipping to change at line 21 | skipping to change at line 21 | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Affero General Public License for more details. | * GNU Affero General Public License for more details. | |||
* | * | |||
* You should have received a copy of the GNU Affero General Public Licen se | * You should have received a copy of the GNU Affero General Public Licen se | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../pch.h" | ||||
namespace mongo { | namespace mongo { | |||
/** | /** | |||
* Obtains the log file handler and writes the current thread's stack t race to | * Obtains the log file handler and writes the current thread's stack t race to | |||
* it. This call issues an exit(). The function can safely be called fr om within a | * it. This call issues an exit(). The function can safely be called fr om within a | |||
* signal handler. | * signal handler. | |||
* | * | |||
* @param signal that this hadler is called for | * @param signal that this hadler is called for | |||
*/ | */ | |||
void printStackAndExit( int signalNum ); | void printStackAndExit( int signalNum ); | |||
End of changes. 1 change blocks. | ||||
2 lines changed or deleted | 0 lines changed or added | |||
sock.h | sock.h | |||
---|---|---|---|---|
skipping to change at line 174 | skipping to change at line 174 | |||
bool _client; | bool _client; | |||
SSL_CTX* _context; | SSL_CTX* _context; | |||
string _password; | string _password; | |||
}; | }; | |||
#endif | #endif | |||
/** | /** | |||
* thin wrapped around file descriptor and system calls | * thin wrapped around file descriptor and system calls | |||
* todo: ssl | * todo: ssl | |||
*/ | */ | |||
class Socket { | class Socket : boost::noncopyable { | |||
public: | public: | |||
Socket(int sock, const SockAddr& farEnd); | Socket(int sock, const SockAddr& farEnd); | |||
/** In some cases the timeout will actually be 2x this value - eg w e do a partial send, | /** In some cases the timeout will actually be 2x this value - eg w e do a partial send, | |||
then the timeout fires, then we try to send again, then the tim eout fires again with | then the timeout fires, then we try to send again, then the tim eout fires again with | |||
no data sent, then we detect that the other side is down. | no data sent, then we detect that the other side is down. | |||
Generally you don't want a timeout, you should be very prepared for errors if you set one. | Generally you don't want a timeout, you should be very prepared for errors if you set one. | |||
*/ | */ | |||
Socket(double so_timeout = 0, int logLevel = 0 ); | Socket(double so_timeout = 0, int logLevel = 0 ); | |||
~Socket() { | ||||
close(); | ||||
} | ||||
bool connect(SockAddr& farEnd); | bool connect(SockAddr& farEnd); | |||
void close(); | void close(); | |||
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 ); | 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 ); | |||
skipping to change at line 248 | skipping to change at line 252 | |||
SockAddr _remote; | SockAddr _remote; | |||
double _timeout; | double _timeout; | |||
long long _bytesIn; | long long _bytesIn; | |||
long long _bytesOut; | long long _bytesOut; | |||
#ifdef MONGO_SSL | #ifdef MONGO_SSL | |||
SSL* _ssl; | SSL* _ssl; | |||
SSLManager * _sslAccepted; | SSLManager * _sslAccepted; | |||
#endif | #endif | |||
protected: | ||||
int _logLevel; // passed to log() when logging errors | int _logLevel; // passed to log() when logging errors | |||
}; | }; | |||
} // namespace mongo | } // namespace mongo | |||
End of changes. 3 change blocks. | ||||
3 lines changed or deleted | 5 lines changed or added | |||
spin_lock.h | spin_lock.h | |||
---|---|---|---|---|
skipping to change at line 20 | skipping to change at line 20 | |||
* This program is distributed in the hope that it will be useful, | * This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Affero General Public License for more details. | * GNU Affero General Public License for more details. | |||
* | * | |||
* You should have received a copy of the GNU Affero General Public Licen se | * You should have received a copy of the GNU Affero General Public Licen se | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#ifdef _WIN32 | ||||
#include <windows.h> | ||||
#endif | ||||
#include "mutex.h" | #include "mutex.h" | |||
namespace mongo { | namespace mongo { | |||
/** | /** | |||
* The spinlock currently requires late GCC support routines to be effi cient. | * The spinlock currently requires late GCC support routines to be effi cient. | |||
* Other platforms default to a mutex implemenation. | * Other platforms default to a mutex implemenation. | |||
*/ | */ | |||
class SpinLock : boost::noncopyable { | class SpinLock : boost::noncopyable { | |||
skipping to change at line 48 | skipping to change at line 51 | |||
CRITICAL_SECTION _cs; | CRITICAL_SECTION _cs; | |||
public: | public: | |||
void lock() {EnterCriticalSection(&_cs); } | void lock() {EnterCriticalSection(&_cs); } | |||
void unlock() { LeaveCriticalSection(&_cs); } | void unlock() { LeaveCriticalSection(&_cs); } | |||
#elif defined(__USE_XOPEN2K) | #elif defined(__USE_XOPEN2K) | |||
pthread_spinlock_t _lock; | pthread_spinlock_t _lock; | |||
void _lk(); | void _lk(); | |||
public: | public: | |||
void unlock() { pthread_spin_unlock(&_lock); } | void unlock() { pthread_spin_unlock(&_lock); } | |||
void lock() { | void lock() { | |||
if ( pthread_spin_trylock( &_lock ) == 0 ) | if ( MONGO_likely( pthread_spin_trylock( &_lock ) == 0 ) ) | |||
return; | return; | |||
_lk(); | _lk(); | |||
} | } | |||
#elif defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) | #elif defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) | |||
volatile bool _locked; | volatile bool _locked; | |||
public: | public: | |||
void unlock() {__sync_lock_release(&_locked); } | void unlock() {__sync_lock_release(&_locked); } | |||
void lock(); | void lock(); | |||
#else | #else | |||
// default to a mutex if not implemented | // default to a mutex if not implemented | |||
End of changes. 2 change blocks. | ||||
1 lines changed or deleted | 4 lines changed or added | |||
strategy.h | strategy.h | |||
---|---|---|---|---|
skipping to change at line 42 | skipping to change at line 42 | |||
virtual void writeOp( int op , Request& r ) = 0; | virtual void writeOp( int op , Request& r ) = 0; | |||
void insert( const Shard& shard , const char * ns , const BSONObj& obj , int flags=0 , bool safe=false ); | void insert( const Shard& shard , const char * ns , const BSONObj& obj , int flags=0 , bool safe=false ); | |||
virtual void commandOp( const string& db, const BSONObj& command, i nt options, | virtual void commandOp( const string& db, const BSONObj& command, i nt options, | |||
const string& versionedNS, const BSONObj& f ilter, | const string& versionedNS, const BSONObj& f ilter, | |||
map<Shard,BSONObj>& results ) | map<Shard,BSONObj>& results ) | |||
{ | { | |||
// Only call this from sharded, for now. | // Only call this from sharded, for now. | |||
// TODO: Refactor all this. | // TODO: Refactor all this. | |||
assert( false ); | verify( false ); | |||
} | } | |||
// These interfaces will merge soon, so make it easy to share logic | ||||
friend class ShardStrategy; | ||||
friend class SingleStrategy; | ||||
protected: | protected: | |||
void doWrite( int op , Request& r , const Shard& shard , bool check Version = true ); | void doWrite( int op , Request& r , const Shard& shard , bool check Version = true ); | |||
void doQuery( Request& r , const Shard& shard ); | void doQuery( Request& r , const Shard& shard ); | |||
void broadcastWrite(int op, Request& r); // Sends to all shards in cluster. DOESN'T CHECK VERSION | ||||
void insert( const Shard& shard , const char * ns , const vector<BS ONObj>& v , int flags=0 , bool safe=false ); | void insert( const Shard& shard , const char * ns , const vector<BS ONObj>& v , int flags=0 , bool safe=false ); | |||
void update( const Shard& shard , const char * ns , const BSONObj& query , const BSONObj& toupdate , int flags=0, bool safe=false ); | void update( const Shard& shard , const char * ns , const BSONObj& query , const BSONObj& toupdate , int flags=0, bool safe=false ); | |||
}; | }; | |||
extern Strategy * SINGLE; | extern Strategy * SINGLE; | |||
extern Strategy * SHARDED; | extern Strategy * SHARDED; | |||
} | } | |||
End of changes. 3 change blocks. | ||||
1 lines changed or deleted | 6 lines changed or added | |||
stringdata.h | stringdata.h | |||
---|---|---|---|---|
skipping to change at line 45 | skipping to change at line 45 | |||
class StringData { | class StringData { | |||
public: | public: | |||
/** Construct a StringData, for the case where the length of | /** Construct a StringData, for the case where the length of | |||
* string is not known. 'c' must be a pointer to a null-terminated string. | * string is not known. 'c' must be a pointer to a null-terminated string. | |||
*/ | */ | |||
StringData( const char* c ) | StringData( const char* c ) | |||
: _data(c), _size((unsigned) strlen(c)) {} | : _data(c), _size((unsigned) strlen(c)) {} | |||
/** Construct a StringData explicitly, for the case where the lengt h of the string | /** Construct a StringData explicitly, for the case where the lengt h of the string | |||
* is already known. 'c' must be a pointer to a null-terminated str ing, and strlenOfc | * is already known. 'c' must be a pointer to a null-terminated str ing, and strlenOfc | |||
* must be the length that std::strlen(c) would return, a.k.a the i ndex of the | * must be the length that strlen(c) would return, a.k.a the index of the | |||
* terminator in c. | * terminator in c. | |||
*/ | */ | |||
StringData( const char* c, unsigned len ) | StringData( const char* c, unsigned len ) | |||
: _data(c), _size(len) {} | : _data(c), _size(len) {} | |||
/** Construct a StringData, for the case of a std::string. */ | /** Construct a StringData, for the case of a string. */ | |||
StringData( const string& s ) | StringData( const std::string& s ) | |||
: _data(s.c_str()), _size((unsigned) s.size()) {} | : _data(s.c_str()), _size((unsigned) s.size()) {} | |||
// Construct a StringData explicitly, for the case of a literal who se size is | // Construct a StringData explicitly, for the case of a literal who se size is | |||
// known at compile time. | // known at compile time. | |||
struct LiteralTag {}; | struct LiteralTag {}; | |||
template<size_t N> | template<size_t N> | |||
StringData( const char (&val)[N], LiteralTag ) | StringData( const char (&val)[N], LiteralTag ) | |||
: _data(&val[0]), _size(N-1) {} | : _data(&val[0]), _size(N-1) {} | |||
// accessors | // accessors | |||
End of changes. 2 change blocks. | ||||
3 lines changed or deleted | 3 lines changed or added | |||
stringutils.h | stringutils.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 <string> | ||||
#include <vector> | ||||
#include <boost/scoped_array.hpp> | ||||
namespace mongo { | namespace mongo { | |||
// see also mongoutils/str.h - perhaps move these there? | // see also mongoutils/str.h - perhaps move these there? | |||
// see also text.h | // see also text.h | |||
void splitStringDelim( const string& str , vector<string>* res , char d elim ); | void splitStringDelim( const string& str , std::vector<std::string>* re s , char delim ); | |||
void joinStringDelim( const vector<string>& strs , string* res , char d elim ); | void joinStringDelim( const std::vector<std::string>& strs , std::strin g* res , char delim ); | |||
inline string tolowerString( const string& input ) { | inline std::string tolowerString( const std::string& input ) { | |||
string::size_type sz = input.size(); | std::string::size_type sz = input.size(); | |||
boost::scoped_array<char> line(new char[sz+1]); | boost::scoped_array<char> line(new char[sz+1]); | |||
char * copy = line.get(); | char * copy = line.get(); | |||
for ( string::size_type i=0; i<sz; i++ ) { | for ( std::string::size_type i=0; i<sz; i++ ) { | |||
char c = input[i]; | char c = input[i]; | |||
copy[i] = (char)tolower( (int)c ); | copy[i] = (char)tolower( (int)c ); | |||
} | } | |||
copy[sz] = 0; | copy[sz] = 0; | |||
return string(copy); | return copy; | |||
} | } | |||
/** | /** Functor for combining lexical and numeric comparisons. */ | |||
* Non numeric characters are compared lexicographically; numeric subst | class LexNumCmp { | |||
rings | public: | |||
* are compared numerically; dots separate ordered comparable subunits. | /** @param lexOnly - compare all characters lexically, including di | |||
* For convenience, character 255 is greater than anything else. | gits. */ | |||
*/ | LexNumCmp( bool lexOnly ); | |||
inline int lexNumCmp( const char *s1, const char *s2 ) { | /** | |||
//cout << "START : " << s1 << "\t" << s2 << endl; | * Non numeric characters are compared lexicographically; numeric s | |||
ubstrings | ||||
bool startWord = true; | * are compared numerically; dots separate ordered comparable subun | |||
its. | ||||
while( *s1 && *s2 ) { | * For convenience, character 255 is greater than anything else. | |||
* @param lexOnly - compare all characters lexically, including dig | ||||
bool d1 = ( *s1 == '.' ); | its. | |||
bool d2 = ( *s2 == '.' ); | */ | |||
if ( d1 && !d2 ) | static int cmp( const char *s1, const char *s2, bool lexOnly ); | |||
return -1; | int cmp( const char *s1, const char *s2 ) const; | |||
if ( d2 && !d1 ) | bool operator()( const char *s1, const char *s2 ) const; | |||
return 1; | bool operator()( const std::string &s1, const std::string &s2 ) con | |||
if ( d1 && d2 ) { | st; | |||
++s1; ++s2; | private: | |||
startWord = true; | bool _lexOnly; | |||
continue; | }; | |||
} | ||||
bool p1 = ( *s1 == (char)255 ); | ||||
bool p2 = ( *s2 == (char)255 ); | ||||
//cout << "\t\t " << p1 << "\t" << p2 << endl; | ||||
if ( p1 && !p2 ) | ||||
return 1; | ||||
if ( p2 && !p1 ) | ||||
return -1; | ||||
bool n1 = isNumber( *s1 ); | ||||
bool n2 = isNumber( *s2 ); | ||||
if ( n1 && n2 ) { | ||||
// get rid of leading 0s | ||||
if ( startWord ) { | ||||
while ( *s1 == '0' ) s1++; | ||||
while ( *s2 == '0' ) s2++; | ||||
} | ||||
char * e1 = (char*)s1; | ||||
char * e2 = (char*)s2; | ||||
// find length | ||||
// if end of string, will break immediately ('\0') | ||||
while ( isNumber (*e1) ) e1++; | ||||
while ( isNumber (*e2) ) e2++; | ||||
int len1 = (int)(e1-s1); | ||||
int len2 = (int)(e2-s2); | ||||
int result; | ||||
// if one is longer than the other, return | ||||
if ( len1 > len2 ) { | ||||
return 1; | ||||
} | ||||
else if ( len2 > len1 ) { | ||||
return -1; | ||||
} | ||||
// if the lengths are equal, just strcmp | ||||
else if ( (result = strncmp(s1, s2, len1)) != 0 ) { | ||||
return result; | ||||
} | ||||
// otherwise, the numbers are equal | ||||
s1 = e1; | ||||
s2 = e2; | ||||
startWord = false; | ||||
continue; | ||||
} | ||||
if ( n1 ) | ||||
return 1; | ||||
if ( n2 ) | ||||
return -1; | ||||
if ( *s1 > *s2 ) | ||||
return 1; | ||||
if ( *s2 > *s1 ) | ||||
return -1; | ||||
s1++; s2++; | ||||
startWord = false; | ||||
} | ||||
if ( *s1 ) | ||||
return 1; | ||||
if ( *s2 ) | ||||
return -1; | ||||
return 0; | ||||
} | ||||
} // namespace mongo | } // namespace mongo | |||
End of changes. 7 change blocks. | ||||
102 lines changed or deleted | 34 lines changed or added | |||
syncclusterconnection.h | syncclusterconnection.h | |||
---|---|---|---|---|
skipping to change at line 21 | skipping to change at line 21 | |||
* | * | |||
* Unless required by applicable law or agreed to in writing, software | * Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | * distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. | |||
* See the License for the specific language governing permissions and | * See the License for the specific language governing permissions and | |||
* limitations under the License. | * limitations under the License. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../pch.h" | #include "mongo/bson/bsonelement.h" | |||
#include "dbclient.h" | #include "mongo/bson/bsonobj.h" | |||
#include "redef_macros.h" | #include "mongo/client/dbclientinterface.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 | * 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 | * 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, | * rollback if a problem occurs during the second phase. Naturally, wi th all these fsyncs, | |||
* these operations will be quite slow -- use sparingly. | * these operations will be quite slow -- use sparingly. | |||
* | * | |||
* Read operations are sent to a single random node. | * Read operations are sent to a single random node. | |||
* | * | |||
* The class checks if a command is read or write style, and sends to a single | * The class checks if a command is read or write style, and sends to a single | |||
* node if a read lock command and to all in two phases with a write st yle command. | * node if a read lock command and to all in two phases with a write st yle command. | |||
*/ | */ | |||
class SyncClusterConnection : public DBClientBase { | class SyncClusterConnection : public DBClientBase { | |||
public: | public: | |||
using DBClientBase::query; | ||||
/** | /** | |||
* @param commaSeparated should be 3 hosts comma separated | * @param commaSeparated should be 3 hosts comma separated | |||
*/ | */ | |||
SyncClusterConnection( const list<HostAndPort> &, double socketTime out = 0); | SyncClusterConnection( const list<HostAndPort> &, double socketTime out = 0); | |||
SyncClusterConnection( string commaSeparated, double socketTimeout = 0); | SyncClusterConnection( string commaSeparated, double socketTimeout = 0); | |||
SyncClusterConnection( string a , string b , string c, double socke tTimeout = 0 ); | SyncClusterConnection( string a , string b , string c, double socke tTimeout = 0 ); | |||
~SyncClusterConnection(); | ~SyncClusterConnection(); | |||
/** | /** | |||
* @return true if all servers are up and ready for writes | * @return true if all servers are up and ready for writes | |||
skipping to change at line 125 | skipping to change at line 128 | |||
vector<BSONObj> _lastErrors; | vector<BSONObj> _lastErrors; | |||
double _socketTimeout; | double _socketTimeout; | |||
}; | }; | |||
class UpdateNotTheSame : public UserException { | class UpdateNotTheSame : public UserException { | |||
public: | public: | |||
UpdateNotTheSame( int code , const string& msg , const vector<strin g>& addrs , const vector<BSONObj>& lastErrors ) | UpdateNotTheSame( int code , const string& msg , const vector<strin g>& addrs , const vector<BSONObj>& lastErrors ) | |||
: UserException( code , msg ) , _addrs( addrs ) , _lastErrors( lastErrors ) { | : UserException( code , msg ) , _addrs( addrs ) , _lastErrors( lastErrors ) { | |||
assert( _addrs.size() == _lastErrors.size() ); | verify( _addrs.size() == _lastErrors.size() ); | |||
} | } | |||
virtual ~UpdateNotTheSame() throw() { | virtual ~UpdateNotTheSame() throw() { | |||
} | } | |||
unsigned size() const { | unsigned size() const { | |||
return _addrs.size(); | return _addrs.size(); | |||
} | } | |||
pair<string,BSONObj> operator[](unsigned i) const { | pair<string,BSONObj> operator[](unsigned i) const { | |||
return make_pair( _addrs[i] , _lastErrors[i] ); | return make_pair( _addrs[i] , _lastErrors[i] ); | |||
} | } | |||
private: | private: | |||
vector<string> _addrs; | vector<string> _addrs; | |||
vector<BSONObj> _lastErrors; | vector<BSONObj> _lastErrors; | |||
}; | }; | |||
}; | }; | |||
#include "undef_macros.h" | ||||
End of changes. 4 change blocks. | ||||
4 lines changed or deleted | 7 lines changed or added | |||
task.h | task.h | |||
---|---|---|---|---|
skipping to change at line 32 | skipping to change at line 32 | |||
namespace mongo { | namespace mongo { | |||
namespace task { | namespace task { | |||
/** abstraction around threads. simpler than BackgroundJob which i s used behind the scenes. | /** abstraction around threads. simpler than BackgroundJob which i s used behind the scenes. | |||
allocate the Task dynamically. when the thread terminates, the Task object will delete itself. | allocate the Task dynamically. when the thread terminates, the Task object will delete itself. | |||
*/ | */ | |||
class Task : private BackgroundJob { | class Task : private BackgroundJob { | |||
protected: | protected: | |||
virtual void setUp(); // Override to perform any do-once work for the task. | ||||
virtual void doWork() = 0; // implement the ta sk here. | virtual void doWork() = 0; // implement the ta sk here. | |||
virtual string name() const = 0; // name the threada | virtual string name() const = 0; // name the thread | |||
public: | public: | |||
Task(); | Task(); | |||
/** for a repeating task, stop after current invocation ends. c an be called by other threads | /** for a repeating task, stop after current invocation ends. c an be called by other threads | |||
as long as the Task is still in scope. | as long as the Task is still in scope. | |||
*/ | */ | |||
void halt(); | void halt(); | |||
private: | private: | |||
unsigned n, repeat; | unsigned n, repeat; | |||
friend void fork(Task* t); | friend void fork(Task* t); | |||
End of changes. 2 change blocks. | ||||
1 lines changed or deleted | 2 lines changed or added | |||
taskqueue.h | taskqueue.h | |||
---|---|---|---|---|
skipping to change at line 23 | skipping to change at line 23 | |||
* GNU Affero General Public License for more details. | * GNU Affero General Public License for more details. | |||
* | * | |||
* You should have received a copy of the GNU Affero General Public Lice nse | * You should have received a copy of the GNU Affero General Public Lice nse | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "mongomutex.h" | #include "mongomutex.h" | |||
// if you turn this back on be sure to enable TaskQueueTest again | ||||
#if 0 | ||||
namespace mongo { | namespace mongo { | |||
/** defer work items by queueing them for invocation by another thread. presumption is that | /** defer work items by queueing them for invocation by another thread. presumption is that | |||
consumer thread is outside of locks more than the source thread. A dditional presumption | consumer thread is outside of locks more than the source thread. A dditional presumption | |||
is that several objects or micro-tasks will be queued and that havi ng a single thread | is that several objects or micro-tasks will be queued and that havi ng a single thread | |||
processing them in batch is hepful as they (in the first use case) use a common data | processing them in batch is hepful as they (in the first use case) use a common data | |||
structure that can then be in local cpu classes. | structure that can then be in local cpu classes. | |||
this class is in db/ as it is dbMutex (mongomutex) specific (so far ). | this class is in db/ as it is dbMutex (mongomutex) specific (so far ). | |||
skipping to change at line 49 | skipping to change at line 52 | |||
see DefInvoke in dbtests/ for an example. | see DefInvoke in dbtests/ for an example. | |||
*/ | */ | |||
template< class MT > | template< class MT > | |||
class TaskQueue { | class TaskQueue { | |||
public: | public: | |||
TaskQueue() : _which(0), _invokeMutex("deferredinvoker") { } | TaskQueue() : _which(0), _invokeMutex("deferredinvoker") { } | |||
void defer(MT mt) { | void defer(MT mt) { | |||
// only one writer allowed. however the invoke processing belo w can occur concurrently with | // only one writer allowed. however the invoke processing belo w can occur concurrently with | |||
// writes (for the most part) | // writes (for the most part) | |||
DEV d.dbMutex.assertWriteLocked(); | DEV verify( Lock::isW() ); | |||
_queues[_which].push_back(mt); | _queues[_which].push_back(mt); | |||
} | } | |||
/** call to process deferrals. | /** call to process deferrals. | |||
concurrency: handled herein. multiple threads could call invok e(), but their efforts will be | concurrency: handled herein. multiple threads could call invok e(), but their efforts will be | |||
serialized. the common case is that there is a si ngle processor calling invoke(). | serialized. the common case is that there is a si ngle processor calling invoke(). | |||
normally, you call this outside of any lock. but if you want t o fully drain the queue, | normally, you call this outside of any lock. but if you want t o fully drain the queue, | |||
skipping to change at line 75 | skipping to change at line 78 | |||
d.invoke(); | d.invoke(); | |||
... | ... | |||
} | } | |||
you can also call invoke periodically to do some work and then pick up later on more. | you can also call invoke periodically to do some work and then pick up later on more. | |||
*/ | */ | |||
void invoke() { | void invoke() { | |||
mutex::scoped_lock lk2(_invokeMutex); | mutex::scoped_lock lk2(_invokeMutex); | |||
int toDrain = 0; | int toDrain = 0; | |||
{ | { | |||
// flip queueing to the other queue (we are double buffered ) | // flip queueing to the other queue (we are double buffered ) | |||
readlocktry lk("", 5); | readlocktry lk(5); | |||
if( !lk.got() ) | if( !lk.got() ) | |||
return; | return; | |||
toDrain = _which; | toDrain = _which; | |||
_which = _which ^ 1; | _which = _which ^ 1; | |||
wassert( _queues[_which].empty() ); // we are in dbMutex, s o it should be/stay empty til we exit dbMutex | wassert( _queues[_which].empty() ); // we are in dbMutex, s o it should be/stay empty til we exit dbMutex | |||
} | } | |||
_drain( _queues[toDrain] ); | _drain( _queues[toDrain] ); | |||
assert( _queues[toDrain].empty() ); | verify( _queues[toDrain].empty() ); | |||
} | } | |||
private: | private: | |||
int _which; // 0 or 1 | int _which; // 0 or 1 | |||
typedef vector< MT > Queue; | typedef vector< MT > Queue; | |||
Queue _queues[2]; | Queue _queues[2]; | |||
// lock order when multiple locks: dbMutex, _invokeMutex | // lock order when multiple locks: dbMutex, _invokeMutex | |||
mongo::mutex _invokeMutex; | mongo::mutex _invokeMutex; | |||
void _drain(Queue& queue) { | void _drain(Queue& queue) { | |||
unsigned oldCap = queue.capacity(); | unsigned oldCap = queue.capacity(); | |||
for( typename Queue::iterator i = queue.begin(); i != queue.end (); i++ ) { | for( typename Queue::iterator i = queue.begin(); i != queue.end (); i++ ) { | |||
const MT& v = *i; | const MT& v = *i; | |||
MT::go(v); | MT::go(v); | |||
} | } | |||
queue.clear(); | queue.clear(); | |||
DEV assert( queue.capacity() == oldCap ); // just checking that clear() doesn't deallocate, we don't want that | DEV verify( queue.capacity() == oldCap ); // just checking that clear() doesn't deallocate, we don't want that | |||
} | } | |||
}; | }; | |||
} | } | |||
#endif | ||||
End of changes. 6 change blocks. | ||||
4 lines changed or deleted | 7 lines changed or added | |||
text.h | text.h | |||
---|---|---|---|---|
skipping to change at line 148 | skipping to change at line 148 | |||
endLen = 0; | endLen = 0; | |||
} | } | |||
uassert( 13306, "could not convert string to long long", endLen != 0 && n[ endLen ] == 0 ); | uassert( 13306, "could not convert string to long long", endLen != 0 && n[ endLen ] == 0 ); | |||
#else // stoll() wasn't introduced until VS 2010. | #else // stoll() wasn't introduced until VS 2010. | |||
char* endPtr = 0; | char* endPtr = 0; | |||
ret = _strtoi64( n, &endPtr, 10 ); | ret = _strtoi64( n, &endPtr, 10 ); | |||
uassert( 13310, "could not convert string to long long", (*endPtr = = 0) && (ret != _I64_MAX) && (ret != _I64_MIN) ); | uassert( 13310, "could not convert string to long long", (*endPtr = = 0) && (ret != _I64_MAX) && (ret != _I64_MIN) ); | |||
#endif // !defined(_WIN32) | #endif // !defined(_WIN32) | |||
return ret; | return ret; | |||
} | } | |||
} | ||||
#if defined(_WIN32) | ||||
class WindowsCommandLine { | ||||
char** _argv; | ||||
public: | ||||
WindowsCommandLine( int argc, wchar_t* argvW[] ); | ||||
~WindowsCommandLine(); | ||||
char** argv( void ) const { return _argv; }; | ||||
}; | ||||
#endif // #if defined(_WIN32) | ||||
} // namespace mongo | ||||
End of changes. 1 change blocks. | ||||
0 lines changed or deleted | 0 lines changed or added | |||
thread_pool.h | thread_pool.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/function.hpp> | #include <list> | |||
#include <boost/bind.hpp> | #include <boost/bind.hpp> | |||
#undef assert | #include <boost/function.hpp> | |||
#define assert MONGO_assert | #include <boost/thread/condition.hpp> | |||
namespace mongo { | namespace mongo { | |||
namespace threadpool { | namespace threadpool { | |||
class Worker; | class Worker; | |||
typedef boost::function<void(void)> Task; //nullary function or fun ctor | typedef boost::function<void(void)> Task; //nullary function or fun ctor | |||
// exported to the mongo namespace | // exported to the mongo namespace | |||
class ThreadPool : boost::noncopyable { | class ThreadPool : boost::noncopyable { | |||
skipping to change at line 68 | skipping to change at line 69 | |||
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, typena me D, typename E> | template<typename F, typename A, typename B, typename C, typena me D, typename E> | |||
void schedule(F f, A a, B b, C c, D d, E e) { schedule(boost::b ind(f,a,b,c,d,e)); } | void schedule(F f, A a, B b, C c, D d, E e) { schedule(boost::b ind(f,a,b,c,d,e)); } | |||
int tasks_remaining() { return _tasksRemaining; } | int tasks_remaining() { return _tasksRemaining; } | |||
private: | private: | |||
mongo::mutex _mutex; | mongo::mutex _mutex; | |||
boost::condition _condition; | boost::condition _condition; | |||
list<Worker*> _freeWorkers; //used as LIFO stack (always front) | std::list<Worker*> _freeWorkers; //used as LIFO stack (always f | |||
list<Task> _tasks; //used as FIFO queue (push_back, pop_front) | ront) | |||
std::list<Task> _tasks; //used as FIFO queue (push_back, pop_fr | ||||
ont) | ||||
int _tasksRemaining; // in queue + currently processing | int _tasksRemaining; // in queue + currently processing | |||
int _nThreads; // only used for sanity checking. could be remov ed in the future. | int _nThreads; // only used for sanity checking. could be remov ed in the future. | |||
// should only be called by a worker from the worker's thread | // should only be called by a worker from the worker's thread | |||
void task_done(Worker* worker); | void task_done(Worker* worker); | |||
friend class Worker; | friend class Worker; | |||
}; | }; | |||
} //namespace threadpool | } //namespace threadpool | |||
End of changes. 3 change blocks. | ||||
5 lines changed or deleted | 8 lines changed or added | |||
threadlocal.h | threadlocal.h | |||
---|---|---|---|---|
skipping to change at line 19 | skipping to change at line 19 | |||
* | * | |||
* This program is distributed in the hope that it will be useful, | * This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Affero General Public License for more details. | * GNU Affero General Public License for more details. | |||
* | * | |||
* You should have received a copy of the GNU Affero General Public Licen se | * You should have received a copy of the GNU Affero General Public Licen se | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | */ | |||
#include "mongo/client/undef_macros.h" | ||||
#include <boost/thread/tss.hpp> | #include <boost/thread/tss.hpp> | |||
#include "mongo/client/redef_macros.h" | ||||
namespace mongo { | namespace mongo { | |||
using boost::thread_specific_ptr; | using boost::thread_specific_ptr; | |||
/* thread local "value" rather than a pointer | /* thread local "value" rather than a pointer | |||
good for things which have copy constructors (and the copy construct or is fast enough) | good for things which have copy constructors (and the copy construct or is fast enough) | |||
e.g. | e.g. | |||
ThreadLocalValue<int> myint; | ThreadLocalValue<int> myint; | |||
*/ | */ | |||
skipping to change at line 81 | skipping to change at line 83 | |||
get's are fast. | get's are fast. | |||
*/ | */ | |||
#if defined(_WIN32) || (defined(__GNUC__) && defined(__linux__)) | #if defined(_WIN32) || (defined(__GNUC__) && defined(__linux__)) | |||
template< class T > | template< class T > | |||
struct TSP { | struct TSP { | |||
boost::thread_specific_ptr<T> tsp; | boost::thread_specific_ptr<T> tsp; | |||
public: | public: | |||
T* get() const; | T* get() const; | |||
void reset(T* v); | void reset(T* v); | |||
T* getMake() { | ||||
T *t = get(); | ||||
if( t == 0 ) | ||||
reset( t = new T() ); | ||||
return t; | ||||
} | ||||
}; | }; | |||
# if defined(_WIN32) | # if defined(_WIN32) | |||
# define TSP_DECLARE(T,p) extern TSP<T> p; | # define TSP_DECLARE(T,p) extern TSP<T> p; | |||
# define TSP_DEFINE(T,p) __declspec( thread ) T* _ ## p; \ | # define TSP_DEFINE(T,p) __declspec( thread ) T* _ ## p; \ | |||
TSP<T> p; \ | TSP<T> p; \ | |||
template<> T* TSP<T>::get() const { return _ ## p; } \ | template<> T* TSP<T>::get() const { return _ ## p; } \ | |||
void TSP<T>::reset(T* v) { \ | void TSP<T>::reset(T* v) { \ | |||
skipping to change at line 118 | skipping to change at line 126 | |||
# endif | # endif | |||
#else | #else | |||
template< class T > | template< class T > | |||
struct TSP { | struct TSP { | |||
thread_specific_ptr<T> tsp; | thread_specific_ptr<T> tsp; | |||
public: | public: | |||
T* get() const { return tsp.get(); } | T* get() const { return tsp.get(); } | |||
void reset(T* v) { tsp.reset(v); } | void reset(T* v) { tsp.reset(v); } | |||
T* getMake() { | ||||
T *t = get(); | ||||
if( t == 0 ) | ||||
reset( t = new T() ); | ||||
return t; | ||||
} | ||||
}; | }; | |||
# define TSP_DECLARE(T,p) extern TSP<T> p; | # define TSP_DECLARE(T,p) extern TSP<T> p; | |||
# define TSP_DEFINE(T,p) TSP<T> p; | # define TSP_DEFINE(T,p) TSP<T> p; | |||
#endif | #endif | |||
} | } | |||
End of changes. 4 change blocks. | ||||
0 lines changed or deleted | 14 lines changed or added | |||
time_support.h | time_support.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 "mongo/platform/basic.h" | ||||
#include <cstdio> // sscanf | #include <cstdio> // sscanf | |||
#include <ctime> | #include <ctime> | |||
#include <string> | ||||
#include <iostream> | ||||
#include <boost/thread/thread.hpp> | #include <boost/thread/thread.hpp> | |||
#include <boost/thread/tss.hpp> | #include <boost/thread/tss.hpp> | |||
#include <boost/date_time/posix_time/posix_time.hpp> | #include <boost/date_time/posix_time/posix_time.hpp> | |||
#include <boost/thread/xtime.hpp> | #include <boost/thread/xtime.hpp> | |||
#undef assert | ||||
#define assert MONGO_assert | ||||
#include "../bson/util/misc.h" // Date_t | #include "../bson/util/misc.h" // Date_t | |||
namespace mongo { | namespace mongo { | |||
using std::string; | ||||
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 54 | skipping to change at line 58 | |||
} | } | |||
// uses ISO 8601 dates without trailing Z | // uses ISO 8601 dates without trailing Z | |||
// colonsOk should be false when creating filenames | // colonsOk should be false when creating filenames | |||
inline string terseCurrentTime(bool colonsOk=true) { | inline string terseCurrentTime(bool colonsOk=true) { | |||
struct tm t; | struct tm t; | |||
time_t_to_Struct( time(0) , &t ); | time_t_to_Struct( time(0) , &t ); | |||
const char* fmt = (colonsOk ? "%Y-%m-%dT%H:%M:%S" : "%Y-%m-%dT%H-%M -%S"); | const char* fmt = (colonsOk ? "%Y-%m-%dT%H:%M:%S" : "%Y-%m-%dT%H-%M -%S"); | |||
char buf[32]; | char buf[32]; | |||
assert(strftime(buf, sizeof(buf), fmt, &t) == 19); | verify(strftime(buf, sizeof(buf), fmt, &t) == 19); | |||
return buf; | return buf; | |||
} | } | |||
inline string timeToISOString(time_t time) { | inline string timeToISOString(time_t time) { | |||
struct tm t; | struct tm t; | |||
time_t_to_Struct( time, &t ); | time_t_to_Struct( time, &t ); | |||
const char* fmt = "%Y-%m-%dT%H:%M:%SZ"; | const char* fmt = "%Y-%m-%dT%H:%M:%SZ"; | |||
char buf[32]; | char buf[32]; | |||
assert(strftime(buf, sizeof(buf), fmt, &t) == 20); | verify(strftime(buf, sizeof(buf), fmt, &t) == 20); | |||
return buf; | return buf; | |||
} | } | |||
inline boost::gregorian::date currentDate() { | inline boost::gregorian::date currentDate() { | |||
boost::posix_time::ptime now = boost::posix_time::second_clock::loc al_time(); | boost::posix_time::ptime now = boost::posix_time::second_clock::loc al_time(); | |||
return now.date(); | return now.date(); | |||
} | } | |||
// parses time of day in "hh:mm" format assuming 'hh' is 00-23 | // parses time of day in "hh:mm" format assuming 'hh' is 00-23 | |||
inline bool toPointInTime( const string& str , boost::posix_time::ptime * timeOfDay ) { | inline bool toPointInTime( const string& str , boost::posix_time::ptime * timeOfDay ) { | |||
skipping to change at line 91 | skipping to change at line 95 | |||
// verify that time is well formed | // verify that time is well formed | |||
if ( ( hh / 24 ) || ( mm / 60 ) ) { | if ( ( hh / 24 ) || ( mm / 60 ) ) { | |||
return false; | return false; | |||
} | } | |||
boost::posix_time::ptime res( currentDate() , boost::posix_time::ho urs( hh ) + boost::posix_time::minutes( mm ) ); | boost::posix_time::ptime res( currentDate() , boost::posix_time::ho urs( hh ) + boost::posix_time::minutes( mm ) ); | |||
*timeOfDay = res; | *timeOfDay = res; | |||
return true; | return true; | |||
} | } | |||
#define MONGO_asctime _asctime_not_threadsafe_ | ||||
#define asctime MONGO_asctime | ||||
#define MONGO_gmtime _gmtime_not_threadsafe_ | ||||
#define gmtime MONGO_gmtime | ||||
#define MONGO_localtime _localtime_not_threadsafe_ | ||||
#define localtime MONGO_localtime | ||||
#define MONGO_ctime _ctime_is_not_threadsafe_ | ||||
#define ctime MONGO_ctime | ||||
#if defined(_WIN32) | #if defined(_WIN32) | |||
inline void sleepsecs(int s) { | inline void sleepsecs(int s) { | |||
// todo : add an assert here that we are not locked in d.dbMutex. there may be debugging things where we | // todo : add an assert here that we are not locked in d.dbMutex. there may be debugging things where we | |||
// are but otherwise it's quite likely that would be a mista ke. | // are but otherwise it's quite likely that would be a mista ke. | |||
Sleep(s*1000); | Sleep(s*1000); | |||
} | } | |||
inline void sleepmillis(long long s) { | inline void sleepmillis(long long s) { | |||
assert( s <= 0xffffffff ); | verify( s <= 0xffffffff ); | |||
Sleep((DWORD) s); | Sleep((DWORD) s); | |||
} | } | |||
inline void sleepmicros(long long 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 += (int)( s / 1000000 ); | xt.sec += (int)( s / 1000000 ); | |||
xt.nsec += (int)(( s % 1000000 ) * 1000); | xt.nsec += (int)(( s % 1000000 ) * 1000); | |||
if ( xt.nsec >= 1000000000 ) { | if ( xt.nsec >= 1000000000 ) { | |||
skipping to change at line 160 | skipping to change at line 155 | |||
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; | std::cout << "nanosleep failed" << std::endl; | |||
} | } | |||
} | } | |||
inline void sleepmicros(long long 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; | std::cout << "nanosleep failed" << std::endl; | |||
} | } | |||
} | } | |||
inline void sleepmillis(long long s) { | inline void sleepmillis(long long s) { | |||
sleepmicros( s * 1000 ); | sleepmicros( s * 1000 ); | |||
} | } | |||
#endif | #endif | |||
extern long long jsTime_virtual_skew; | extern long long jsTime_virtual_skew; | |||
extern boost::thread_specific_ptr<long long> jsTime_virtual_thread_skew ; | extern boost::thread_specific_ptr<long long> jsTime_virtual_thread_skew ; | |||
skipping to change at line 258 | skipping to change at line 253 | |||
return (((unsigned long long) tv.tv_sec) * 1000*1000) + tv.tv_usec; | return (((unsigned long long) tv.tv_sec) * 1000*1000) + tv.tv_usec; | |||
} | } | |||
inline unsigned curTimeMicros() { | inline unsigned curTimeMicros() { | |||
timeval tv; | timeval tv; | |||
gettimeofday(&tv, NULL); | gettimeofday(&tv, NULL); | |||
unsigned secs = tv.tv_sec % 1024; | unsigned secs = tv.tv_sec % 1024; | |||
return secs*1000*1000 + tv.tv_usec; | return secs*1000*1000 + tv.tv_usec; | |||
} | } | |||
#endif | #endif | |||
// these are so that if you use one of them compilation will fail | ||||
char *asctime(const struct tm *tm); | ||||
char *ctime(const time_t *timep); | ||||
struct tm *gmtime(const time_t *timep); | ||||
struct tm *localtime(const time_t *timep); | ||||
} // namespace mongo | } // namespace mongo | |||
End of changes. 11 change blocks. | ||||
16 lines changed or deleted | 17 lines changed or added | |||
timer.h | timer.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 "time_support.h" | ||||
namespace mongo { | namespace mongo { | |||
#if !defined(_WIN32) | ||||
/** | /** | |||
* simple scoped timer | * Time tracking object. | |||
* | ||||
* Should be of reasonably high performance, though the implementations | ||||
are platform-specific. | ||||
* Each platform provides a distinct implementation of the now() method | ||||
, and sets the | ||||
* _countsPerSecond static field to the constant number of ticks per se | ||||
cond that now() counts | ||||
* in. The maximum span measurable by the counter and convertible to m | ||||
icroseconds is about 10 | ||||
* trillion ticks. As long as there are fewer than 100 ticks per nanos | ||||
econd, timer durations of | ||||
* 2.5 years will be supported. Since a typical tick duration will be | ||||
under 10 per nanosecond, | ||||
* if not below 1 per nanosecond, this should not be an issue. | ||||
*/ | */ | |||
class Timer /*copyable*/ { | class Timer /*copyable*/ { | |||
public: | public: | |||
static const unsigned long long millisPerSecond = 1000; | ||||
static const unsigned long long microsPerSecond = 1000 * millisPerS | ||||
econd; | ||||
static const unsigned long long nanosPerSecond = 1000 * microsPerSe | ||||
cond; | ||||
Timer() { reset(); } | Timer() { reset(); } | |||
int seconds() const { return (int)(micros() / 1000000); } | int seconds() const { return (int)(micros() / 1000000); } | |||
int millis() const { return (int)(micros() / 1000); } | int millis() const { return (int)(micros() / 1000); } | |||
int minutes() const { return seconds() / 60; } | int minutes() const { return seconds() / 60; } | |||
/** gets time interval and resets at the same time. this way we ca | /** Get the time interval and reset at the same time. | |||
n call curTimeMicros | * @return time in milliseconds. | |||
once instead of twice if one wanted millis() and then reset() | */ | |||
. | inline int millisReset() { | |||
@return time in millis | unsigned long long nextNow = now(); | |||
*/ | unsigned long long deltaMicros = | |||
int millisReset() { | ((nextNow - _old) * microsPerSecond) / _countsPerSecond; | |||
unsigned long long now = curTimeMicros64(); | ||||
int m = (int)((now-old)/1000); | _old = nextNow; | |||
old = now; | return static_cast<int>(deltaMicros / 1000); | |||
return m; | } | |||
} | ||||
inline unsigned long long micros() const { | ||||
// note: dubious that the resolution is as anywhere near as high as | return ((now() - _old) * microsPerSecond) / _countsPerSecond; | |||
ethod name implies! | } | |||
unsigned long long micros() const { | ||||
unsigned long long n = curTimeMicros64(); | inline void reset() { _old = now(); } | |||
return n - old; | ||||
} | /** | |||
unsigned long long micros(unsigned long long & n) const { // return | * Internally, the timer counts platform-dependent ticks of some so | |||
s cur time in addition to timer result | rt, and | |||
n = curTimeMicros64(); | * must then convert those ticks to microseconds and their ilk. Th | |||
return n - old; | is field | |||
} | * stores the frequency of the platform-dependent counter. | |||
* | ||||
* This value is initialized at program startup, and never changed | ||||
after. | ||||
* It should be treated as private. | ||||
*/ | ||||
static unsigned long long _countsPerSecond; | ||||
void reset() { old = curTimeMicros64(); } | ||||
private: | private: | |||
unsigned long long old; | inline unsigned long long now() const; | |||
}; | ||||
#else | unsigned long long _old; | |||
class Timer /*copyable*/ { | ||||
public: | ||||
Timer() { reset(); } | ||||
int seconds() const { | ||||
int s = static_cast<int>((now() - old) / countsPerSecond); | ||||
return s; | ||||
} | ||||
int millis() const { | ||||
return (int) | ||||
((now() - old) * 1000.0 / countsPerSecond); | ||||
} | ||||
int minutes() const { return seconds() / 60; } | ||||
/** gets time interval and resets at the same time. this way we ca | ||||
n call curTimeMicros | ||||
once instead of twice if one wanted millis() and then reset() | ||||
. | ||||
@return time in millis | ||||
*/ | ||||
int millisReset() { | ||||
unsigned long long nw = now(); | ||||
int m = static_cast<int>((nw - old) * 1000.0 / countsPerSecond) | ||||
; | ||||
old = nw; | ||||
return m; | ||||
} | ||||
void reset() { | ||||
old = now(); | ||||
} | ||||
unsigned long long micros() const { | ||||
return (unsigned long long) | ||||
((now() - old) * 1000000.0 / countsPerSecond); | ||||
} | ||||
static unsigned long long countsPerSecond; | ||||
private: | ||||
unsigned long long now() const { | ||||
LARGE_INTEGER i; | ||||
QueryPerformanceCounter(&i); | ||||
return i.QuadPart; | ||||
} | ||||
unsigned long long old; | ||||
}; | }; | |||
#endif | ||||
} // namespace mongo | } // namespace mongo | |||
#include "mongo/util/timer-inl.h" | ||||
End of changes. 10 change blocks. | ||||
86 lines changed or deleted | 53 lines changed or added | |||
tool.h | tool.h | |||
---|---|---|---|---|
skipping to change at line 29 | skipping to change at line 29 | |||
#pragma once | #pragma once | |||
#include <string> | #include <string> | |||
#include <boost/program_options.hpp> | #include <boost/program_options.hpp> | |||
#if defined(_WIN32) | #if defined(_WIN32) | |||
#include <io.h> | #include <io.h> | |||
#endif | #endif | |||
#include "client/dbclient.h" | ||||
#include "db/instance.h" | #include "db/instance.h" | |||
#include "db/matcher.h" | #include "db/matcher.h" | |||
#include "db/security.h" | #include "db/security.h" | |||
using std::string; | using std::string; | |||
namespace mongo { | namespace mongo { | |||
class Tool { | class Tool { | |||
public: | public: | |||
End of changes. 1 change blocks. | ||||
1 lines changed or deleted | 0 lines changed or added | |||
top.h | top.h | |||
---|---|---|---|---|
skipping to change at line 21 | skipping to change at line 21 | |||
* Unless required by applicable law or agreed to in writing, software | * Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | * distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. | |||
* See the License for the specific language governing permissions and | * See the License for the specific language governing permissions and | |||
* limitations under the License. | * limitations under the License. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include <boost/date_time/posix_time/posix_time.hpp> | #include <boost/date_time/posix_time/posix_time.hpp> | |||
#undef 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") { } | Top() : _lock("Top") { } | |||
End of changes. 1 change blocks. | ||||
2 lines changed or deleted | 0 lines changed or added | |||
ucp.h | ucp.h | |||
---|---|---|---|---|
/************************************************* | /************************************************* | |||
* Unicode Property Table handler * | * Unicode Property Table handler * | |||
*************************************************/ | *************************************************/ | |||
#ifndef _UCP_H | #ifndef _UCP_H | |||
#define _UCP_H | #define _UCP_H | |||
/* This file contains definitions of the property values that are returned by | /* This file contains definitions of the property values that are returned by | |||
the function _pcre_ucp_findprop(). New values that are added for new releas | the UCD access macros. New values that are added for new releases of Unicod | |||
es | e | |||
of Unicode should always be at the end of each enum, for backwards | should always be at the end of each enum, for backwards compatibility. */ | |||
compatibility. */ | ||||
/* These are the general character categories. */ | /* These are the general character categories. */ | |||
enum { | enum { | |||
ucp_C, /* Other */ | ucp_C, /* Other */ | |||
ucp_L, /* Letter */ | ucp_L, /* Letter */ | |||
ucp_M, /* Mark */ | ucp_M, /* Mark */ | |||
ucp_N, /* Number */ | ucp_N, /* Number */ | |||
ucp_P, /* Punctuation */ | ucp_P, /* Punctuation */ | |||
ucp_S, /* Symbol */ | ucp_S, /* Symbol */ | |||
skipping to change at line 124 | skipping to change at line 123 | |||
ucp_Tagbanwa, | ucp_Tagbanwa, | |||
ucp_Tai_Le, | ucp_Tai_Le, | |||
ucp_Tamil, | ucp_Tamil, | |||
ucp_Telugu, | ucp_Telugu, | |||
ucp_Thaana, | ucp_Thaana, | |||
ucp_Thai, | ucp_Thai, | |||
ucp_Tibetan, | ucp_Tibetan, | |||
ucp_Tifinagh, | ucp_Tifinagh, | |||
ucp_Ugaritic, | ucp_Ugaritic, | |||
ucp_Yi, | ucp_Yi, | |||
ucp_Balinese, /* New for Unicode 5.0.0 */ | /* New for Unicode 5.0: */ | |||
ucp_Cuneiform, /* New for Unicode 5.0.0 */ | ucp_Balinese, | |||
ucp_Nko, /* New for Unicode 5.0.0 */ | ucp_Cuneiform, | |||
ucp_Phags_Pa, /* New for Unicode 5.0.0 */ | ucp_Nko, | |||
ucp_Phoenician /* New for Unicode 5.0.0 */ | ucp_Phags_Pa, | |||
ucp_Phoenician, | ||||
/* New for Unicode 5.1: */ | ||||
ucp_Carian, | ||||
ucp_Cham, | ||||
ucp_Kayah_Li, | ||||
ucp_Lepcha, | ||||
ucp_Lycian, | ||||
ucp_Lydian, | ||||
ucp_Ol_Chiki, | ||||
ucp_Rejang, | ||||
ucp_Saurashtra, | ||||
ucp_Sundanese, | ||||
ucp_Vai, | ||||
/* New for Unicode 5.2: */ | ||||
ucp_Avestan, | ||||
ucp_Bamum, | ||||
ucp_Egyptian_Hieroglyphs, | ||||
ucp_Imperial_Aramaic, | ||||
ucp_Inscriptional_Pahlavi, | ||||
ucp_Inscriptional_Parthian, | ||||
ucp_Javanese, | ||||
ucp_Kaithi, | ||||
ucp_Lisu, | ||||
ucp_Meetei_Mayek, | ||||
ucp_Old_South_Arabian, | ||||
ucp_Old_Turkic, | ||||
ucp_Samaritan, | ||||
ucp_Tai_Tham, | ||||
ucp_Tai_Viet, | ||||
/* New for Unicode 6.0.0: */ | ||||
ucp_Batak, | ||||
ucp_Brahmi, | ||||
ucp_Mandaic | ||||
}; | }; | |||
#endif | #endif | |||
/* End of ucp.h */ | /* End of ucp.h */ | |||
End of changes. 2 change blocks. | ||||
9 lines changed or deleted | 41 lines changed or added | |||
undef_macros.h | undef_macros.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. | |||
*/ | */ | |||
// If you define a new global un-prefixed macro, please add it here and in redef_macros | // If you define a new global un-prefixed macro, please add it here and in redef_macros | |||
// #pragma once // this file is intended to be processed multiple times | // #pragma once // this file is intended to be processed multiple times | |||
/** MONGO_EXPOSE_MACROS - when defined, indicates that you are compiling a | #ifdef MONGO_MACROS_PUSHED | |||
mongo program rather | ||||
than just using the C++ driver. | ||||
*/ | ||||
#if !defined(MONGO_EXPOSE_MACROS) && !defined(MONGO_MACROS_CLEANED) | ||||
// util/allocator.h | // util/allocator.h | |||
#undef malloc | #undef malloc | |||
#pragma pop_macro("malloc") | ||||
#undef realloc | #undef realloc | |||
#pragma pop_macro("realloc") | ||||
// util/assert_util.h | // util/assert_util.h | |||
#undef assert | ||||
#undef dassert | #undef dassert | |||
#pragma pop_macro("dassert") | ||||
#undef wassert | #undef wassert | |||
#pragma pop_macro("wassert") | ||||
#undef massert | #undef massert | |||
#pragma pop_macro("massert") | ||||
#undef uassert | #undef uassert | |||
#undef BOOST_CHECK_EXCEPTION | #pragma pop_macro("uassert") | |||
#undef verify | ||||
#pragma pop_macro("verify") | ||||
#undef DESTRUCTOR_GUARD | #undef DESTRUCTOR_GUARD | |||
#pragma pop_macro("DESTRUCTOR_GUARD") | ||||
// util/goodies.h | // util/goodies.h | |||
#undef PRINT | #undef PRINT | |||
#pragma pop_macro("PRINT") | ||||
#undef PRINTFL | #undef PRINTFL | |||
#undef asctime | #pragma pop_macro("PRINTFL") | |||
#undef gmtime | ||||
#undef localtime | ||||
#undef ctime | ||||
// util/debug_util.h | // util/debug_util.h | |||
#undef DEV | #undef DEV | |||
#pragma pop_macro("DEV") | ||||
#undef DEBUGGING | #undef DEBUGGING | |||
#pragma pop_macro("DEBUGGING") | ||||
#undef SOMETIMES | #undef SOMETIMES | |||
#pragma pop_macro("SOMETIMES") | ||||
#undef OCCASIONALLY | #undef OCCASIONALLY | |||
#pragma pop_macro("OCCASIONALLY") | ||||
#undef RARELY | #undef RARELY | |||
#pragma pop_macro("RARELY") | ||||
#undef ONCE | #undef ONCE | |||
#pragma pop_macro("ONCE") | ||||
// util/log.h | // util/log.h | |||
#undef LOG | #undef LOG | |||
#pragma pop_macro("LOG") | ||||
#define MONGO_MACROS_CLEANED | #undef MONGO_MACROS_PUSHED | |||
#endif | #endif | |||
End of changes. 19 change blocks. | ||||
12 lines changed or deleted | 20 lines changed or added | |||
unittest.h | unittest.h | |||
---|---|---|---|---|
// unittest.h | // mongo/unittest/unittest.h | |||
/* Copyright 2009 10gen Inc. | /** | |||
* | * Copyright (C) 2008 10gen Inc. | |||
* Licensed under the Apache License, Version 2.0 (the "License"); | * | |||
* you may not use this file except in compliance with the License. | * This program is free software: you can redistribute it and/or modify | |||
* You may obtain a copy of the License at | * it under the terms of the GNU Affero General Public License, version 3 | |||
* | , | |||
* http://www.apache.org/licenses/LICENSE-2.0 | * as published by the Free Software Foundation. | |||
* | * | |||
* Unless required by applicable law or agreed to in writing, software | * This program is distributed in the hope that it will be useful, | |||
* distributed under the License is distributed on an "AS IS" BASIS, | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
ed. | * GNU Affero General Public License for more details. | |||
* See the License for the specific language governing permissions and | * | |||
* limitations under the License. | * 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/>. | ||||
*/ | ||||
/* | ||||
simple portable regression system | ||||
*/ | */ | |||
#pragma once | #include <sstream> | |||
#include <string> | ||||
#include <vector> | ||||
#include <boost/noncopyable.hpp> | ||||
#include <boost/scoped_ptr.hpp> | ||||
#include "mongo/util/assert_util.h" | ||||
#include "mongo/util/log.h" | ||||
#define ASSERT_THROWS(a,b) \ | ||||
try { \ | ||||
a; \ | ||||
mongo::unittest::assert_fail( #a , __FILE__ , __LINE__ ); \ | ||||
} catch ( b& ){ \ | ||||
mongo::unittest::assert_pass(); \ | ||||
} | ||||
#define ASSERT_EQUALS(a,b) mongo::unittest::MyAsserts( #a , #b , __FILE__ , | ||||
__LINE__ ).ae( (a) , (b) ) | ||||
#define ASSERT_NOT_EQUALS(a,b) mongo::unittest::MyAsserts( #a , #b , __FILE | ||||
__ , __LINE__ ).nae( (a) , (b) ) | ||||
#define ASSERT(x) (void)( (!(!(x))) ? mongo::unittest::assert_pass() : FAIL | ||||
(x) ) | ||||
#define FAIL(x) mongo::unittest::assert_fail( #x , __FILE__ , __LINE__ ) | ||||
namespace mongo { | namespace mongo { | |||
/* The idea here is to let all initialization of global variables (clas | namespace unittest { | |||
ses inheriting from UnitTest) | ||||
complete before we run the tests -- otherwise order of initilization | class Result; | |||
being arbitrary may mess | ||||
us up. The app's main() function should call runTests(). | class TestCase { | |||
public: | ||||
To define a unit test, inherit from this and implement run. instanti | virtual ~TestCase(); | |||
ate one object for the new class | virtual void setUp(); | |||
as a global. | virtual void tearDown(); | |||
virtual void run() = 0; | ||||
These tests are ran on *every* startup of mongod, so they have to be | virtual std::string getName() = 0; | |||
very lightweight. But it is a | }; | |||
good quick check for a bad build. | ||||
*/ | class Test : public TestCase { | |||
struct UnitTest { | public: | |||
UnitTest() { | Test(); | |||
registerTest(this); | virtual ~Test(); | |||
} | virtual std::string getName(); | |||
virtual ~UnitTest() {} | ||||
protected: | ||||
// assert if fails | void setTestName( const std::string &name ); | |||
virtual void run() = 0; | ||||
private: | ||||
static bool testsInProgress() { return running; } | std::string _name; | |||
private: | }; | |||
static vector<UnitTest*> *tests; | ||||
static bool running; | template< class T > | |||
public: | class TestHolderBase : public TestCase { | |||
static void registerTest(UnitTest *t) { | public: | |||
if ( tests == 0 ) | TestHolderBase() {} | |||
tests = new vector<UnitTest*>(); | virtual ~TestHolderBase() {} | |||
tests->push_back(t); | virtual void run() { | |||
} | boost::scoped_ptr<T> t; | |||
t.reset( create() ); | ||||
static void runTests() { | t->run(); | |||
running = true; | } | |||
for ( vector<UnitTest*>::iterator i = tests->begin(); i != test | virtual T * create() = 0; | |||
s->end(); i++ ) { | virtual std::string getName() { | |||
(*i)->run(); | return demangleName( typeid(T) ); | |||
} | } | |||
running = false; | }; | |||
} | ||||
}; | template< class T > | |||
class TestHolder0 : public TestHolderBase<T> { | ||||
public: | ||||
virtual T * create() { | ||||
return new T(); | ||||
} | ||||
}; | ||||
template< class T , typename A > | ||||
class TestHolder1 : public TestHolderBase<T> { | ||||
public: | ||||
TestHolder1( const A& a ) : _a(a) {} | ||||
virtual T * create() { | ||||
return new T( _a ); | ||||
} | ||||
const A _a; | ||||
}; | ||||
class Suite { | ||||
public: | ||||
Suite( const string &name ); | ||||
virtual ~Suite(); | ||||
template<class T> | ||||
void add() { | ||||
_tests.push_back( new TestHolder0<T>() ); | ||||
} | ||||
template<class T , typename A > | ||||
void add( const A& a ) { | ||||
_tests.push_back( new TestHolder1<T,A>(a) ); | ||||
} | ||||
Result * run( const std::string& filter ); | ||||
static int run( const std::vector<std::string> &suites , const | ||||
std::string& filter ); | ||||
protected: | ||||
virtual void setupTests() = 0; | ||||
private: | ||||
typedef std::vector<TestCase *> TestCaseList; | ||||
std::string _name; | ||||
TestCaseList _tests; | ||||
bool _ran; | ||||
void registerSuite( const std::string &name , Suite *s ); | ||||
}; | ||||
void assert_pass(); | ||||
void assert_fail( const char * exp , const char * file , unsigned l | ||||
ine ); | ||||
class MyAssertionException : private boost::noncopyable { | ||||
public: | ||||
MyAssertionException() { | ||||
ss << "assertion: "; | ||||
} | ||||
std::stringstream ss; | ||||
}; | ||||
class MyAsserts : private boost::noncopyable { | ||||
public: | ||||
MyAsserts( const char * aexp , const char * bexp , const char * | ||||
file , unsigned line ) | ||||
: _aexp( aexp ) , _bexp( bexp ) , _file( file ) , _line( li | ||||
ne ) { | ||||
} | ||||
template<typename A,typename B> | ||||
void ae( const A &a , const B &b ) { | ||||
_gotAssert(); | ||||
if ( a == b ) | ||||
return; | ||||
printLocation(); | ||||
MyAssertionException * e = getBase(); | ||||
e->ss << a << " != " << b << std::endl; | ||||
log() << e->ss.str() << std::endl; | ||||
throw e; | ||||
} | ||||
template<typename A,typename B> | ||||
void nae( const A &a , const B &b ) { | ||||
_gotAssert(); | ||||
if ( a != b ) | ||||
return; | ||||
printLocation(); | ||||
MyAssertionException * e = getBase(); | ||||
e->ss << a << " == " << b << std::endl; | ||||
log() << e->ss.str() << std::endl; | ||||
throw e; | ||||
} | ||||
void printLocation(); | ||||
private: | ||||
void _gotAssert(); | ||||
MyAssertionException * getBase(); | ||||
std::string _aexp; | ||||
std::string _bexp; | ||||
std::string _file; | ||||
unsigned _line; | ||||
}; | ||||
/** | ||||
* Returns the name of the currently executing unit test | ||||
*/ | ||||
std::string getExecutingTestName(); | ||||
/** | ||||
* Return a list of suite names. | ||||
*/ | ||||
std::vector<std::string> getAllSuiteNames(); | ||||
} // namespace mongo | } // namespace unittest | |||
} // namespace mongo | ||||
End of changes. 5 change blocks. | ||||
59 lines changed or deleted | 212 lines changed or added | |||
update.h | update.h | |||
---|---|---|---|---|
skipping to change at line 22 | skipping to change at line 22 | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Affero General Public License for more details. | * GNU Affero General Public License for more details. | |||
* | * | |||
* You should have received a copy of the GNU Affero General Public Lice nse | * You should have received a copy of the GNU Affero General Public Lice nse | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | */ | |||
#include "../../pch.h" | #include "../../pch.h" | |||
#include "../jsobj.h" | #include "../jsobj.h" | |||
#include "../../util/embedded_builder.h" | #include "../../util/embedded_builder.h" | |||
#include "../../util/stringutils.h" | ||||
#include "../matcher.h" | #include "../matcher.h" | |||
namespace mongo { | namespace mongo { | |||
// ---------- public ------------- | // ---------- public ------------- | |||
struct UpdateResult { | struct UpdateResult { | |||
const bool existing; // if existing objects were modified | const bool existing; // if existing objects were modified | |||
const bool mod; // was this a $ mod | const bool mod; // was this a $ mod | |||
const long long num; // how many objects touched | const long long num; // how many objects touched | |||
skipping to change at line 50 | skipping to change at line 51 | |||
} | } | |||
} | } | |||
}; | }; | |||
class RemoveSaver; | class RemoveSaver; | |||
/* returns true if an existing object was updated, false if no existing object was found. | /* returns true if an existing object was updated, false if no existing object was found. | |||
multi - update multiple objects - mostly useful with things like $se t | multi - update multiple objects - mostly useful with things like $se t | |||
god - allow access to system namespaces | god - allow access to system namespaces | |||
*/ | */ | |||
UpdateResult updateObjects(const char *ns, const BSONObj& updateobj, BS | UpdateResult updateObjects(const char *ns, | |||
ONObj pattern, bool upsert, bool multi , bool logop , OpDebug& debug ); | const BSONObj& updateobj, | |||
UpdateResult _updateObjects(bool god, const char *ns, const BSONObj& up | BSONObj pattern, | |||
dateobj, BSONObj pattern, | bool upsert, | |||
bool upsert, bool multi , bool logop , OpDe | bool multi , | |||
bug& debug , RemoveSaver * rs = 0 ); | bool logop , | |||
OpDebug& debug, | ||||
bool fromMigrate = false, | ||||
const QueryPlanSelectionPolicy &planPolicy = | ||||
QueryPlanSelectionPolicy::any()); | ||||
UpdateResult _updateObjects(bool god, | ||||
const char *ns, | ||||
const BSONObj& updateobj, | ||||
BSONObj pattern, | ||||
bool upsert, | ||||
bool multi , | ||||
bool logop , | ||||
OpDebug& debug , | ||||
RemoveSaver * rs = 0, | ||||
bool fromMigrate = false, | ||||
const QueryPlanSelectionPolicy &planPolicy | ||||
= | ||||
QueryPlanSelectionPolicy::any()); | ||||
// ---------- private ------------- | // ---------- private ------------- | |||
class ModState; | class ModState; | |||
class ModSetState; | class ModSetState; | |||
/* Used for modifiers such as $inc, $set, $push, ... | /* Used for modifiers such as $inc, $set, $push, ... | |||
* stores the info about a single operation | * stores the info about a single operation | |||
* once created should never be modified | * once created should never be modified | |||
*/ | */ | |||
skipping to change at line 119 | skipping to change at line 139 | |||
case NumberDouble: | case NumberDouble: | |||
manip.setNumber( elt.numberDouble() + in.numberDouble() ); | manip.setNumber( elt.numberDouble() + in.numberDouble() ); | |||
break; | break; | |||
case NumberLong: | case NumberLong: | |||
manip.setLong( elt.numberLong() + in.numberLong() ); | manip.setLong( elt.numberLong() + in.numberLong() ); | |||
break; | break; | |||
case NumberInt: | case NumberInt: | |||
manip.setInt( elt.numberInt() + in.numberInt() ); | manip.setInt( elt.numberInt() + in.numberInt() ); | |||
break; | break; | |||
default: | default: | |||
assert(0); | verify(0); | |||
} | } | |||
} | } | |||
void IncrementMe( BSONElement& in ) const { | void IncrementMe( BSONElement& in ) const { | |||
BSONElementManipulator manip( in ); | BSONElementManipulator manip( in ); | |||
switch ( in.type() ) { | switch ( in.type() ) { | |||
case NumberDouble: | case NumberDouble: | |||
manip.SetNumber( elt.numberDouble() + in.numberDouble() ); | manip.SetNumber( elt.numberDouble() + in.numberDouble() ); | |||
break; | break; | |||
case NumberLong: | case NumberLong: | |||
manip.SetLong( elt.numberLong() + in.numberLong() ); | manip.SetLong( elt.numberLong() + in.numberLong() ); | |||
break; | break; | |||
case NumberInt: | case NumberInt: | |||
manip.SetInt( elt.numberInt() + in.numberInt() ); | manip.SetInt( elt.numberInt() + in.numberInt() ); | |||
break; | break; | |||
default: | default: | |||
assert(0); | verify(0); | |||
} | } | |||
} | } | |||
template< class Builder > | template< class Builder > | |||
void appendIncremented( Builder& bb , const BSONElement& in, ModSta te& ms ) const; | void appendIncremented( Builder& bb , const BSONElement& in, ModSta te& ms ) const; | |||
bool operator<( const Mod &other ) const { | bool operator<( const Mod &other ) const { | |||
return strcmp( fieldName, other.fieldName ) < 0; | return strcmp( fieldName, other.fieldName ) < 0; | |||
} | } | |||
skipping to change at line 280 | skipping to change at line 300 | |||
/** | /** | |||
* stores a set of Mods | * stores a set of Mods | |||
* once created, should never be changed | * once created, should never be changed | |||
*/ | */ | |||
class ModSet : boost::noncopyable { | class ModSet : boost::noncopyable { | |||
typedef map<string,Mod> ModHolder; | typedef map<string,Mod> ModHolder; | |||
ModHolder _mods; | ModHolder _mods; | |||
int _isIndexed; | int _isIndexed; | |||
bool _hasDynamicArray; | bool _hasDynamicArray; | |||
static void extractFields( map< string, BSONElement > &fields, cons | ||||
t BSONElement &top, const string &base ); | ||||
FieldCompareResult compare( const ModHolder::iterator &m, map< stri | ||||
ng, BSONElement >::iterator &p, const map< string, BSONElement >::iterator | ||||
&pEnd ) const { | ||||
bool mDone = ( m == _mods.end() ); | ||||
bool pDone = ( p == pEnd ); | ||||
assert( ! mDone ); | ||||
assert( ! pDone ); | ||||
if ( mDone && pDone ) | ||||
return SAME; | ||||
// If one iterator is done we want to read from the other one, | ||||
so say the other one is lower. | ||||
if ( mDone ) | ||||
return RIGHT_BEFORE; | ||||
if ( pDone ) | ||||
return LEFT_BEFORE; | ||||
return compareDottedFieldNames( m->first, p->first.c_str() ); | ||||
} | ||||
bool mayAddEmbedded( map< string, BSONElement > &existing, string r | ||||
ight ) { | ||||
for( string left = EmbeddedBuilder::splitDot( right ); | ||||
left.length() > 0 && left[ left.length() - 1 ] != '.'; | ||||
left += "." + EmbeddedBuilder::splitDot( right ) ) { | ||||
if ( existing.count( left ) > 0 && existing[ left ].type() | ||||
!= Object ) | ||||
return false; | ||||
if ( haveModForField( left.c_str() ) ) | ||||
return false; | ||||
} | ||||
return true; | ||||
} | ||||
static Mod::Op opFromStr( const char *fn ) { | static Mod::Op opFromStr( const char *fn ) { | |||
assert( fn[0] == '$' ); | verify( fn[0] == '$' ); | |||
switch( fn[1] ) { | switch( fn[1] ) { | |||
case 'i': { | case 'i': { | |||
if ( fn[2] == 'n' && fn[3] == 'c' && fn[4] == 0 ) | if ( fn[2] == 'n' && fn[3] == 'c' && fn[4] == 0 ) | |||
return Mod::INC; | return Mod::INC; | |||
break; | break; | |||
} | } | |||
case 's': { | case 's': { | |||
if ( fn[2] == 'e' && fn[3] == 't' && fn[4] == 0 ) | if ( fn[2] == 'e' && fn[3] == 't' && fn[4] == 0 ) | |||
return Mod::SET; | return Mod::SET; | |||
break; | break; | |||
skipping to change at line 395 | skipping to change at line 386 | |||
} | } | |||
public: | public: | |||
ModSet( const BSONObj &from , | ModSet( const BSONObj &from , | |||
const set<string>& idxKeys = set<string>(), | const set<string>& idxKeys = set<string>(), | |||
const set<string>* backgroundKeys = 0 | const set<string>* backgroundKeys = 0 | |||
); | ); | |||
// TODO: this is inefficient - should probably just handle when ite rating | // TODO: this is inefficient - should probably just handle when ite rating | |||
ModSet * fixDynamicArray( const char * elemMatchKey ) const; | ModSet * fixDynamicArray( const string &elemMatchKey ) const; | |||
bool hasDynamicArray() const { return _hasDynamicArray; } | bool hasDynamicArray() const { return _hasDynamicArray; } | |||
/** | /** | |||
* creates a ModSetState suitable for operation on obj | * creates a ModSetState suitable for operation on obj | |||
* doesn't change or modify this ModSet or any underying Mod | * doesn't change or modify this ModSet or any underying Mod | |||
*/ | */ | |||
auto_ptr<ModSetState> prepare( const BSONObj& obj ) const; | auto_ptr<ModSetState> prepare( const BSONObj& obj ) const; | |||
/** | /** | |||
skipping to change at line 431 | skipping to change at line 422 | |||
return _mods.find( fieldName ) != _mods.end(); | return _mods.find( fieldName ) != _mods.end(); | |||
} | } | |||
bool haveConflictingMod( const string& fieldName ) { | bool haveConflictingMod( const string& fieldName ) { | |||
size_t idx = fieldName.find( '.' ); | size_t idx = fieldName.find( '.' ); | |||
if ( idx == string::npos ) | if ( idx == string::npos ) | |||
idx = fieldName.size(); | idx = fieldName.size(); | |||
ModHolder::const_iterator start = _mods.lower_bound(fieldName.s ubstr(0,idx)); | ModHolder::const_iterator start = _mods.lower_bound(fieldName.s ubstr(0,idx)); | |||
for ( ; start != _mods.end(); start++ ) { | for ( ; start != _mods.end(); start++ ) { | |||
FieldCompareResult r = compareDottedFieldNames( fieldName , | FieldCompareResult r = compareDottedFieldNames( fieldName , | |||
start->first ); | start->first , | |||
LexNumCmp( t | ||||
rue ) ); | ||||
switch ( r ) { | switch ( r ) { | |||
case LEFT_SUBFIELD: return true; | case LEFT_SUBFIELD: return true; | |||
case LEFT_BEFORE: return false; | case LEFT_BEFORE: return false; | |||
case SAME: return true; | case SAME: return true; | |||
case RIGHT_BEFORE: return false; | case RIGHT_BEFORE: return false; | |||
case RIGHT_SUBFIELD: return true; | case RIGHT_SUBFIELD: return true; | |||
} | } | |||
} | } | |||
return false; | return false; | |||
} | } | |||
}; | }; | |||
/** | /** | |||
* stores any information about a single Mod operating on a single Obje ct | * stores any information about a single Mod operating on a single Obje ct | |||
*/ | */ | |||
class ModState { | class ModState : boost::noncopyable { | |||
public: | public: | |||
const Mod * m; | const Mod * m; | |||
BSONElement old; | BSONElement old; | |||
BSONElement newVal; | BSONElement newVal; | |||
BSONObj _objData; | BSONObj _objData; | |||
const char * fixedOpName; | const char * fixedOpName; | |||
BSONElement * fixed; | BSONElement * fixed; | |||
int pushStartSize; | int pushStartSize; | |||
skipping to change at line 523 | skipping to change at line 515 | |||
const char * n = useFullName ? m->fieldName : m->shortFieldName ; | const char * n = useFullName ? m->fieldName : m->shortFieldName ; | |||
switch ( incType ) { | switch ( incType ) { | |||
case NumberDouble: | case NumberDouble: | |||
b.append( n , incdouble ); break; | b.append( n , incdouble ); break; | |||
case NumberLong: | case NumberLong: | |||
b.append( n , inclong ); break; | b.append( n , inclong ); break; | |||
case NumberInt: | case NumberInt: | |||
b.append( n , incint ); break; | b.append( n , incint ); break; | |||
default: | default: | |||
assert(0); | verify(0); | |||
} | } | |||
} | } | |||
string toString() const; | string toString() const; | |||
template< class Builder > | template< class Builder > | |||
void handleRename( Builder &newObjBuilder, const char *shortFieldNa me ); | void handleRename( Builder &newObjBuilder, const char *shortFieldNa me ); | |||
}; | }; | |||
/** | /** | |||
* this is used to hold state, meta data while applying a ModSet to a B SONObj | * this is used to hold state, meta data while applying a ModSet to a B SONObj | |||
* the goal is to make ModSet const so its re-usable | * the goal is to make ModSet const so its re-usable | |||
*/ | */ | |||
class ModSetState : boost::noncopyable { | class ModSetState : boost::noncopyable { | |||
struct FieldCmp { | typedef map<string,shared_ptr<ModState>,LexNumCmp> ModStateHolder; | |||
bool operator()( const string &l, const string &r ) const; | typedef pair<const ModStateHolder::iterator,const ModStateHolder::i | |||
}; | terator> ModStateRange; | |||
typedef map<string,ModState,FieldCmp> ModStateHolder; | ||||
const BSONObj& _obj; | const BSONObj& _obj; | |||
ModStateHolder _mods; | ModStateHolder _mods; | |||
bool _inPlacePossible; | bool _inPlacePossible; | |||
BSONObj _newFromMods; // keep this data alive, as oplog generation may depend on it | BSONObj _newFromMods; // keep this data alive, as oplog generation may depend on it | |||
ModSetState( const BSONObj& obj ) | ModSetState( const BSONObj& obj ) | |||
: _obj( obj ) , _inPlacePossible(true) { | : _obj( obj ) , _mods( LexNumCmp( true ) ) , _inPlacePossible(t rue) { | |||
} | } | |||
/** | /** | |||
* @return if in place is still possible | * @return if in place is still possible | |||
*/ | */ | |||
bool amIInPlacePossible( bool inPlacePossible ) { | bool amIInPlacePossible( bool inPlacePossible ) { | |||
if ( ! inPlacePossible ) | if ( ! inPlacePossible ) | |||
_inPlacePossible = false; | _inPlacePossible = false; | |||
return _inPlacePossible; | return _inPlacePossible; | |||
} | } | |||
ModStateRange modsForRoot( const string &root ); | ||||
void createNewObjFromMods( const string &root, BSONObjBuilder &b, c | ||||
onst BSONObj &obj ); | ||||
void createNewArrayFromMods( const string &root, BSONArrayBuilder & | ||||
b, | ||||
const BSONArray &arr ); | ||||
template< class Builder > | template< class Builder > | |||
void createNewFromMods( const string& root , Builder& b , const BSO | void createNewFromMods( const string& root , Builder& b , BSONItera | |||
NObj &obj ); | torSorted& es , | |||
const ModStateRange& modRange , const LexNum | ||||
Cmp& lexNumCmp ); | ||||
template< class Builder > | template< class Builder > | |||
void _appendNewFromMods( const string& root , ModState& m , Builder & b , set<string>& onedownseen ); | void _appendNewFromMods( const string& root , ModState& m , Builder & b , set<string>& onedownseen ); | |||
template< class Builder > | template< class Builder > | |||
void appendNewFromMod( ModState& ms , Builder& b ) { | void appendNewFromMod( ModState& ms , Builder& b ) { | |||
if ( ms.dontApply ) { | if ( ms.dontApply ) { | |||
return; | return; | |||
} | } | |||
skipping to change at line 642 | skipping to change at line 639 | |||
ms.handleRename( b, m.shortFieldName ); | ms.handleRename( b, m.shortFieldName ); | |||
break; | break; | |||
default: | default: | |||
stringstream ss; | stringstream ss; | |||
ss << "unknown mod in appendNewFromMod: " << m.op; | ss << "unknown mod in appendNewFromMod: " << m.op; | |||
throw UserException( 9015, ss.str() ); | throw UserException( 9015, ss.str() ); | |||
} | } | |||
} | } | |||
/** @return true iff the elements aren't eoo(), are distinct, and s | ||||
hare a field name. */ | ||||
static bool duplicateFieldName( const BSONElement &a, const BSONEle | ||||
ment &b ); | ||||
public: | public: | |||
bool canApplyInPlace() const { | bool canApplyInPlace() const { | |||
return _inPlacePossible; | return _inPlacePossible; | |||
} | } | |||
/** | /** | |||
* modified underlying _obj | * modified underlying _obj | |||
* @param isOnDisk - true means this is an on disk object, and this update needs to be made durable | * @param isOnDisk - true means this is an on disk object, and this update needs to be made durable | |||
*/ | */ | |||
void applyModsInPlace( bool isOnDisk ); | void applyModsInPlace( bool isOnDisk ); | |||
BSONObj createNewFromMods(); | BSONObj createNewFromMods(); | |||
// re-writing for oplog | // re-writing for oplog | |||
bool needOpLogRewrite() const { | bool needOpLogRewrite() const { | |||
for ( ModStateHolder::const_iterator i = _mods.begin(); i != _m ods.end(); i++ ) | for ( ModStateHolder::const_iterator i = _mods.begin(); i != _m ods.end(); i++ ) | |||
if ( i->second.needOpLogRewrite() ) | if ( i->second->needOpLogRewrite() ) | |||
return true; | return true; | |||
return false; | return false; | |||
} | } | |||
BSONObj getOpLogRewrite() const { | BSONObj getOpLogRewrite() const { | |||
BSONObjBuilder b; | BSONObjBuilder b; | |||
for ( ModStateHolder::const_iterator i = _mods.begin(); i != _m ods.end(); i++ ) | for ( ModStateHolder::const_iterator i = _mods.begin(); i != _m ods.end(); i++ ) | |||
i->second.appendForOpLog( b ); | i->second->appendForOpLog( b ); | |||
return b.obj(); | return b.obj(); | |||
} | } | |||
bool haveArrayDepMod() const { | bool haveArrayDepMod() const { | |||
for ( ModStateHolder::const_iterator i = _mods.begin(); i != _m ods.end(); i++ ) | for ( ModStateHolder::const_iterator i = _mods.begin(); i != _m ods.end(); i++ ) | |||
if ( i->second.m->arrayDep() ) | if ( i->second->m->arrayDep() ) | |||
return true; | return true; | |||
return false; | return false; | |||
} | } | |||
void appendSizeSpecForArrayDepMods( BSONObjBuilder &b ) const { | void appendSizeSpecForArrayDepMods( BSONObjBuilder &b ) const { | |||
for ( ModStateHolder::const_iterator i = _mods.begin(); i != _m ods.end(); i++ ) { | for ( ModStateHolder::const_iterator i = _mods.begin(); i != _m ods.end(); i++ ) { | |||
const ModState& m = i->second; | const ModState& m = *i->second; | |||
if ( m.m->arrayDep() ) { | if ( m.m->arrayDep() ) { | |||
if ( m.pushStartSize == -1 ) | if ( m.pushStartSize == -1 ) | |||
b.appendNull( m.fieldName() ); | b.appendNull( m.fieldName() ); | |||
else | else | |||
b << m.fieldName() << BSON( "$size" << m.pushStartS ize ); | b << m.fieldName() << BSON( "$size" << m.pushStartS ize ); | |||
} | } | |||
} | } | |||
} | } | |||
string toString() const; | string toString() const; | |||
End of changes. 19 change blocks. | ||||
60 lines changed or deleted | 59 lines changed or added | |||
util.h | util.h | |||
---|---|---|---|---|
skipping to change at line 21 | skipping to change at line 21 | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Affero General Public License for more details. | * GNU Affero General Public License for more details. | |||
* | * | |||
* You should have received a copy of the GNU Affero General Public Licen se | * You should have received a copy of the GNU Affero General Public Licen se | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../pch.h" | #include "mongo/pch.h" | |||
#include "../client/dbclient.h" | #include "mongo/db/jsobj.h" | |||
#include "../db/jsobj.h" | #include "mongo/util/mongoutils/str.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 { | |||
struct ShardChunkVersion { | struct ShardChunkVersion { | |||
union { | union { | |||
struct { | struct { | |||
int _minor; | int _minor; | |||
skipping to change at line 58 | skipping to change at line 57 | |||
ShardChunkVersion( const BSONElement& e ) { | ShardChunkVersion( const BSONElement& e ) { | |||
if ( e.type() == Date || e.type() == Timestamp ) { | if ( e.type() == Date || e.type() == Timestamp ) { | |||
_combined = e._numberLong(); | _combined = e._numberLong(); | |||
} | } | |||
else if ( e.eoo() ) { | else if ( e.eoo() ) { | |||
_combined = 0; | _combined = 0; | |||
} | } | |||
else { | else { | |||
_combined = 0; | _combined = 0; | |||
log() << "ShardChunkVersion can't handle type (" << (int)(e .type()) << ") " << e << endl; | log() << "ShardChunkVersion can't handle type (" << (int)(e .type()) << ") " << e << endl; | |||
assert(0); | verify(0); | |||
} | } | |||
} | } | |||
void inc( bool major ) { | void inc( bool major ) { | |||
if ( major ) | if ( major ) | |||
incMajor(); | incMajor(); | |||
else | else | |||
incMinor(); | incMinor(); | |||
} | } | |||
skipping to change at line 108 | skipping to change at line 107 | |||
switch ( elem.type() ) { | switch ( elem.type() ) { | |||
case Timestamp: | case Timestamp: | |||
case NumberLong: | case NumberLong: | |||
case Date: | case Date: | |||
_combined = elem._numberLong(); | _combined = elem._numberLong(); | |||
break; | break; | |||
case EOO: | case EOO: | |||
_combined = 0; | _combined = 0; | |||
break; | break; | |||
default: | default: | |||
massert( 13657 , str::stream() << "unknown type for ShardCh unkVersion: " << elem , 0 ); | massert( 13657 , mongoutils::str::stream() << "unknown type for ShardChunkVersion: " << elem , 0 ); | |||
} | } | |||
return *this; | return *this; | |||
} | } | |||
}; | }; | |||
inline ostream& operator<<( ostream &s , const ShardChunkVersion& v) { | inline ostream& operator<<( ostream &s , const ShardChunkVersion& v) { | |||
s << v._major << "|" << v._minor; | s << v._major << "|" << v._minor; | |||
return s; | return s; | |||
} | } | |||
/** | /** | |||
* your config info for a given shard/chunk is out of date | * your config info for a given shard/chunk is out of date | |||
*/ | */ | |||
class StaleConfigException : public AssertionException { | class StaleConfigException : public AssertionException { | |||
public: | public: | |||
StaleConfigException( const string& ns , const string& raw , int co | StaleConfigException( const string& ns , const string& raw , int co | |||
de, bool justConnection = false ) | de, ShardChunkVersion received, ShardChunkVersion wanted, bool justConnecti | |||
: AssertionException( (string)"ns: " + ns + " " + raw , code ) | on = false ) | |||
, | : AssertionException( | |||
mongoutils::str::stream() << raw << " ( ns : " << ns << | ||||
", received : " << received.to | ||||
String() << | ||||
", wanted : " << wanted.toStri | ||||
ng() << | ||||
", " << ( code == SendStaleCon | ||||
figCode ? "send" : "recv" ) << " )", | ||||
code ), | ||||
_justConnection(justConnection) , | _justConnection(justConnection) , | |||
_ns(ns) { | _ns(ns), | |||
} | _received( received ), | |||
_wanted( wanted ) | ||||
{} | ||||
// Preferred if we're rebuilding this from a thrown exception | ||||
StaleConfigException( const string& raw , int code, const BSONObj& | ||||
error, bool justConnection = false ) | ||||
: AssertionException( | ||||
mongoutils::str::stream() << raw << " ( ns : " << error | ||||
["ns"].String() << // Note, this will fail if we don't have a ns | ||||
", received : " << ShardChunkV | ||||
ersion( error["vReceived"] ).toString() << | ||||
", wanted : " << ShardChunkVer | ||||
sion( error["vWanted"] ).toString() << | ||||
", " << ( code == SendStaleCon | ||||
figCode ? "send" : "recv" ) << " )", | ||||
code ), | ||||
_justConnection(justConnection) , | ||||
_ns( error["ns"].String() ), | ||||
_received( ShardChunkVersion( error["vReceived"] ) ), | ||||
_wanted( ShardChunkVersion( error["vWanted"] ) ) | ||||
{} | ||||
StaleConfigException() : AssertionException( "", 0 ) {} | ||||
virtual ~StaleConfigException() throw() {} | virtual ~StaleConfigException() throw() {} | |||
virtual void appendPrefix( stringstream& ss ) const { ss << "stale sharding config exception: "; } | virtual void appendPrefix( stringstream& ss ) const { ss << "stale sharding config exception: "; } | |||
bool justConnection() const { return _justConnection; } | bool justConnection() const { return _justConnection; } | |||
string getns() const { return _ns; } | string getns() const { return _ns; } | |||
static bool parse( const string& big , string& ns , string& raw ) { | static bool parse( const string& big , string& ns , string& raw ) { | |||
skipping to change at line 150 | skipping to change at line 172 | |||
if ( start == string::npos ) | if ( start == string::npos ) | |||
return false; | return false; | |||
string::size_type end = big.find( ']' ,start ); | string::size_type end = big.find( ']' ,start ); | |||
if ( end == string::npos ) | if ( end == string::npos ) | |||
return false; | return false; | |||
ns = big.substr( start + 1 , ( end - start ) - 1 ); | ns = big.substr( start + 1 , ( end - start ) - 1 ); | |||
raw = big.substr( end + 1 ); | raw = big.substr( end + 1 ); | |||
return true; | return true; | |||
} | } | |||
ShardChunkVersion getVersionReceived() const { return _received; } | ||||
ShardChunkVersion getVersionWanted() const { return _wanted; } | ||||
StaleConfigException& operator=( const StaleConfigException& elem ) | ||||
{ | ||||
this->_ei.msg = elem._ei.msg; | ||||
this->_ei.code = elem._ei.code; | ||||
this->_justConnection = elem._justConnection; | ||||
this->_ns = elem._ns; | ||||
this->_received = elem._received; | ||||
this->_wanted = elem._wanted; | ||||
return *this; | ||||
} | ||||
private: | private: | |||
bool _justConnection; | bool _justConnection; | |||
string _ns; | string _ns; | |||
ShardChunkVersion _received; | ||||
ShardChunkVersion _wanted; | ||||
}; | }; | |||
class SendStaleConfigException : public StaleConfigException { | class SendStaleConfigException : public StaleConfigException { | |||
public: | public: | |||
SendStaleConfigException( const string& ns , const string& raw , bo | SendStaleConfigException( const string& ns , const string& raw , Sh | |||
ol justConnection = false ) | ardChunkVersion received, ShardChunkVersion wanted, bool justConnection = f | |||
: StaleConfigException( ns, raw + "(send)", SendStaleConfigCode | alse ) | |||
, justConnection ) {} | : StaleConfigException( ns, raw, SendStaleConfigCode, received, | |||
wanted, justConnection ) {} | ||||
SendStaleConfigException( const string& raw , const BSONObj& error, | ||||
bool justConnection = false ) | ||||
: StaleConfigException( raw, SendStaleConfigCode, error, justCo | ||||
nnection ) {} | ||||
}; | }; | |||
class RecvStaleConfigException : public StaleConfigException { | class RecvStaleConfigException : public StaleConfigException { | |||
public: | public: | |||
RecvStaleConfigException( const string& ns , const string& raw , bo | RecvStaleConfigException( const string& ns , const string& raw , Sh | |||
ol justConnection = false ) | ardChunkVersion received, ShardChunkVersion wanted, bool justConnection = f | |||
: StaleConfigException( ns, raw + "(recv)", RecvStaleConfigCode | alse ) | |||
, justConnection ) {} | : StaleConfigException( ns, raw, RecvStaleConfigCode, received, | |||
wanted, justConnection ) {} | ||||
RecvStaleConfigException( const string& raw , const BSONObj& error, | ||||
bool justConnection = false ) | ||||
: StaleConfigException( raw, RecvStaleConfigCode, error, justCo | ||||
nnection ) {} | ||||
}; | }; | |||
class ShardConnection; | class ShardConnection; | |||
class DBClientBase; | ||||
class VersionManager { | class VersionManager { | |||
public: | public: | |||
VersionManager(){}; | VersionManager(){}; | |||
bool isVersionableCB( DBClientBase* ); | bool isVersionableCB( DBClientBase* ); | |||
bool initShardVersionCB( DBClientBase*, BSONObj& ); | bool initShardVersionCB( DBClientBase*, BSONObj& ); | |||
bool forceRemoteCheckShardVersionCB( const string& ); | bool forceRemoteCheckShardVersionCB( const string& ); | |||
bool checkShardVersionCB( DBClientBase*, const string&, bool, int ) ; | bool checkShardVersionCB( DBClientBase*, const string&, bool, int ) ; | |||
bool checkShardVersionCB( ShardConnection*, bool, int ); | bool checkShardVersionCB( ShardConnection*, bool, int ); | |||
void resetShardVersionCB( DBClientBase* ); | void resetShardVersionCB( DBClientBase* ); | |||
End of changes. 10 change blocks. | ||||
20 lines changed or deleted | 80 lines changed or added | |||
v8_db.h | v8_db.h | |||
---|---|---|---|---|
skipping to change at line 26 | skipping to change at line 26 | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include <v8.h> | #include <v8.h> | |||
#include <cstring> | #include <cstring> | |||
#include <cstdio> | #include <cstdio> | |||
#include <cstdlib> | #include <cstdlib> | |||
#include "engine_v8.h" | #include "engine_v8.h" | |||
#include "../client/dbclient.h" | ||||
namespace mongo { | namespace mongo { | |||
class DBClientBase; | ||||
// These functions may depend on the caller creating a handle scope and context scope. | // These functions may depend on the caller creating a handle scope and context scope. | |||
v8::Handle<v8::FunctionTemplate> getMongoFunctionTemplate( V8Scope * sc ope, bool local ); | v8::Handle<v8::FunctionTemplate> getMongoFunctionTemplate( V8Scope * sc ope, bool local ); | |||
// void installDBTypes( V8Scope * scope, v8::Handle<v8::ObjectTemplate>& global ); | // void installDBTypes( V8Scope * scope, v8::Handle<v8::ObjectTemplate>& global ); | |||
void installDBTypes( V8Scope * scope, v8::Handle<v8::Object>& global ); | void installDBTypes( V8Scope * scope, v8::Handle<v8::Object>& global ); | |||
// the actual globals | // the actual globals | |||
mongo::DBClientBase * getConnection( const v8::Arguments& args ); | mongo::DBClientBase * getConnection( const v8::Arguments& args ); | |||
End of changes. 2 change blocks. | ||||
2 lines changed or deleted | 1 lines changed or added | |||
v8_utils.h | v8_utils.h | |||
---|---|---|---|---|
skipping to change at line 32 | skipping to change at line 32 | |||
#include <cstring> | #include <cstring> | |||
#include <cstdio> | #include <cstdio> | |||
#include <cstdlib> | #include <cstdlib> | |||
#include <assert.h> | #include <assert.h> | |||
#include <iostream> | #include <iostream> | |||
namespace mongo { | namespace mongo { | |||
void ReportException(v8::TryCatch* handler); | void ReportException(v8::TryCatch* handler); | |||
#define jsassert(x,msg) assert(x) | #define jsassert(x,msg) verify(x) | |||
std::ostream& operator<<( std::ostream &s, const v8::Handle<v8::Value> & o ); | std::ostream& operator<<( std::ostream &s, const v8::Handle<v8::Value> & o ); | |||
std::ostream& operator<<( std::ostream &s, const v8::Handle<v8::TryCatc h> * try_catch ); | std::ostream& operator<<( std::ostream &s, const v8::Handle<v8::TryCatc h> * try_catch ); | |||
std::string toSTLString( const v8::Handle<v8::Value> & o ); | std::string toSTLString( const v8::Handle<v8::Value> & o ); | |||
std::string toSTLString( const v8::TryCatch * try_catch ); | std::string toSTLString( const v8::TryCatch * try_catch ); | |||
class V8Scope; | class V8Scope; | |||
void installFork( V8Scope* scope, v8::Handle< v8::Object > &global, v8: :Handle< v8::Context > &context ); | void installFork( V8Scope* scope, v8::Handle< v8::Object > &global, v8: :Handle< v8::Context > &context ); | |||
} | } | |||
End of changes. 1 change blocks. | ||||
1 lines changed or deleted | 1 lines changed or added | |||
value.h | value.h | |||
---|---|---|---|---|
skipping to change at line 411 | skipping to change at line 411 | |||
/* ======================= INLINED IMPLEMENTATIONS ======================== == */ | /* ======================= INLINED IMPLEMENTATIONS ======================== == */ | |||
namespace mongo { | namespace mongo { | |||
inline BSONType Value::getType() const { | inline BSONType Value::getType() const { | |||
return type; | return type; | |||
} | } | |||
inline size_t Value::getArrayLength() const { | inline size_t Value::getArrayLength() const { | |||
assert(getType() == Array); | verify(getType() == Array); | |||
return vpValue.size(); | return vpValue.size(); | |||
} | } | |||
inline intrusive_ptr<const Value> Value::getUndefined() { | inline intrusive_ptr<const Value> Value::getUndefined() { | |||
return pFieldUndefined; | return pFieldUndefined; | |||
} | } | |||
inline intrusive_ptr<const Value> Value::getNull() { | inline intrusive_ptr<const Value> Value::getNull() { | |||
return pFieldNull; | return pFieldNull; | |||
} | } | |||
End of changes. 1 change blocks. | ||||
1 lines changed or deleted | 1 lines changed or added | |||
version.h | version.h | |||
---|---|---|---|---|
#ifndef UTIL_VERSION_HEADER | #ifndef UTIL_VERSION_HEADER | |||
#define UTIL_VERSION_HEADER | #define UTIL_VERSION_HEADER | |||
#include <string> | #include <string> | |||
#include "mongo/bson/stringdata.h" | ||||
namespace mongo { | namespace mongo { | |||
struct BSONArray; | struct BSONArray; | |||
using std::string; | ||||
// mongo version | // mongo version | |||
extern const char versionString[]; | extern const char versionString[]; | |||
extern const BSONArray versionArray; | extern const BSONArray versionArray; | |||
string mongodVersion(); | std::string mongodVersion(); | |||
int versionCmp(StringData rhs, StringData lhs); // like strcmp | int versionCmp(StringData rhs, StringData lhs); // like strcmp | |||
const char * gitVersion(); | const char * gitVersion(); | |||
void printGitVersion(); | void printGitVersion(); | |||
string sysInfo(); | std::string sysInfo(); | |||
void printSysInfo(); | void printSysInfo(); | |||
void show_warnings(); | void show_warnings(); | |||
} // namespace mongo | } // namespace mongo | |||
#endif // UTIL_VERSION_HEADER | #endif // UTIL_VERSION_HEADER | |||
End of changes. 4 change blocks. | ||||
4 lines changed or deleted | 4 lines changed or added | |||