| bsoninlines.h | | bsoninlines.h | |
| | | | |
| skipping to change at line 22 | | skipping to change at line 22 | |
| * distributed under the License is distributed on an "AS IS" BASIS, | | * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli
ed. | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli
ed. | |
| * See the License for the specific language governing permissions and | | * See the License for the specific language governing permissions and | |
| * limitations under the License. | | * limitations under the License. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include <map> | | #include <map> | |
| #include "util/atomic_int.h" | | #include "util/atomic_int.h" | |
|
| | | #include "../util/hex.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| inline BSONObjIterator BSONObj::begin() { | | inline BSONObjIterator BSONObj::begin() { | |
| return BSONObjIterator(*this); | | return BSONObjIterator(*this); | |
| } | | } | |
| | | | |
| inline BSONObj BSONElement::embeddedObjectUserCheck() const { | | inline BSONObj BSONElement::embeddedObjectUserCheck() const { | |
| uassert( 10065 , "invalid parameter: expected an object", isABSONO
bj() ); | | uassert( 10065 , "invalid parameter: expected an object", isABSONO
bj() ); | |
| return BSONObj(value()); | | return BSONObj(value()); | |
| | | | |
| skipping to change at line 212 | | skipping to change at line 213 | |
| | | | |
| typedef set<BSONElement, BSONElementFieldNameCmp> BSONSortedElements; | | typedef set<BSONElement, BSONElementFieldNameCmp> BSONSortedElements; | |
| 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 ) const { | | inline string BSONObj::toString( bool isArray, bool full ) const { | |
| if ( isEmpty() ) return "{}"; | | if ( isEmpty() ) return "{}"; | |
| | | | |
| stringstream s; | | stringstream s; | |
| 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
() ); | |
| BSONElement e = i.next( true ); | | BSONElement e = i.next( true ); | |
| massert( 10328 , "Invalid element size", e.size() > 0 ); | | massert( 10328 , "Invalid element size", e.size() > 0 ); | |
| | | | |
| skipping to change at line 237 | | skipping to change at line 238 | |
| 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 << ", "; | |
|
| s << e.toString( !isArray ); | | s << e.toString( !isArray, full ); | |
| } | | } | |
| s << ( isArray ? " ]" : " }" ); | | s << ( isArray ? " ]" : " }" ); | |
| return s.str(); | | return s.str(); | |
| } | | } | |
| | | | |
| extern unsigned getRandomNumber(); | | extern unsigned getRandomNumber(); | |
| | | | |
| inline void BSONElement::validate() const { | | inline void BSONElement::validate() const { | |
| const BSONType t = type(); | | const BSONType t = type(); | |
| | | | |
| | | | |
| skipping to change at line 364 | | skipping to change at line 365 | |
| ss << "BSONElement: bad type " << (int) type(); | | ss << "BSONElement: bad type " << (int) type(); | |
| string msg = ss.str(); | | 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 ) const { | | inline string BSONElement::toString( bool includeFieldName, bool full )
const { | |
| stringstream s; | | stringstream s; | |
| if ( includeFieldName && type() != EOO ) | | if ( includeFieldName && type() != EOO ) | |
| s << fieldName() << ": "; | | s << fieldName() << ": "; | |
| switch ( type() ) { | | switch ( type() ) { | |
| case EOO: | | case EOO: | |
| return "EOO"; | | return "EOO"; | |
| case mongo::Date: | | case mongo::Date: | |
| s << "new Date(" << date() << ')'; | | s << "new Date(" << date() << ')'; | |
| break; | | break; | |
| case RegEx: | | case RegEx: | |
| | | | |
| skipping to change at line 403 | | skipping to change at line 404 | |
| 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: | |
|
| s << embeddedObject().toString(); | | s << embeddedObject().toString(false, full); | |
| break; | | break; | |
| case mongo::Array: | | case mongo::Array: | |
|
| s << embeddedObject().toString( true ); | | s << embeddedObject().toString(true, full); | |
| 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; | |
| case MinKey: | | case MinKey: | |
| s << "MinKey"; | | s << "MinKey"; | |
| break; | | break; | |
| case CodeWScope: | | case CodeWScope: | |
| s << "CodeWScope( " | | s << "CodeWScope( " | |
|
| << codeWScopeCode() << ", " << codeWScopeObject().toString(
) << ")"; | | << codeWScopeCode() << ", " << codeWScopeObject().toString(
false, full) << ")"; | |
| break; | | break; | |
| case Code: | | case Code: | |
|
| if ( valuestrsize() > 80 ) { | | if ( !full && valuestrsize() > 80 ) { | |
| s.write(valuestr(), 70); | | s.write(valuestr(), 70); | |
| s << "..."; | | s << "..."; | |
| } else { | | } else { | |
| s.write(valuestr(), valuestrsize()-1); | | s.write(valuestr(), valuestrsize()-1); | |
| } | | } | |
| break; | | break; | |
| case Symbol: | | case Symbol: | |
| case mongo::String: | | case mongo::String: | |
| s << '"'; | | s << '"'; | |
|
| if ( valuestrsize() > 80 ) { | | if ( !full && valuestrsize() > 80 ) { | |
| s.write(valuestr(), 70); | | s.write(valuestr(), 70); | |
| s << "...\""; | | s << "...\""; | |
| } else { | | } else { | |
| s.write(valuestr(), valuestrsize()-1); | | s.write(valuestr(), valuestrsize()-1); | |
| s << '"'; | | s << '"'; | |
| } | | } | |
| break; | | break; | |
| case DBRef: | | case DBRef: | |
| s << "DBRef('" << valuestr() << "',"; | | s << "DBRef('" << valuestr() << "',"; | |
| { | | { | |
| mongo::OID *x = (mongo::OID *) (valuestr() + valuestrsize()
); | | mongo::OID *x = (mongo::OID *) (valuestr() + valuestrsize()
); | |
| s << *x << ')'; | | s << *x << ')'; | |
| } | | } | |
| break; | | break; | |
| case jstOID: | | case jstOID: | |
| s << "ObjectId('"; | | s << "ObjectId('"; | |
| s << __oid() << "')"; | | s << __oid() << "')"; | |
| break; | | break; | |
| case BinData: | | case BinData: | |
| s << "BinData"; | | s << "BinData"; | |
|
| | | if (full){ | |
| | | int len; | |
| | | const char* data = binDataClean(len); | |
| | | s << '(' << binDataType() << ", " << toHex(data, len) << ') | |
| | | '; | |
| | | } | |
| break; | | break; | |
| case Timestamp: | | case Timestamp: | |
| s << "Timestamp " << timestampTime() << "|" << timestampInc(); | | s << "Timestamp " << timestampTime() << "|" << timestampInc(); | |
| break; | | break; | |
| default: | | default: | |
| s << "?type=" << type(); | | s << "?type=" << type(); | |
| break; | | break; | |
| } | | } | |
| return s.str(); | | return s.str(); | |
| } | | } | |
| | | | |
End of changes. 10 change blocks. |
| 8 lines changed or deleted | | 15 lines changed or added | |
|
| btree.h | | btree.h | |
| | | | |
| skipping to change at line 237 | | skipping to change at line 237 | |
| | | | |
| /** | | /** | |
| * find the first instance of the key | | * find the first instance of the key | |
| * does not handle dups | | * does not handle dups | |
| * returned DiskLock isNull if can't find anything with that | | * returned DiskLock isNull if can't find anything with that | |
| */ | | */ | |
| DiskLoc findSingle( const IndexDetails& , const DiskLoc& thisLoc, c
onst BSONObj& key ); | | DiskLoc findSingle( const IndexDetails& , const DiskLoc& thisLoc, c
onst BSONObj& key ); | |
| | | | |
| /* advance one key position in the index: */ | | /* advance one key position in the index: */ | |
| DiskLoc advance(const DiskLoc& thisLoc, int& keyOfs, int direction,
const char *caller); | | DiskLoc advance(const DiskLoc& thisLoc, int& keyOfs, int direction,
const char *caller); | |
|
| | | | |
| | | void advanceTo(const IndexDetails &id, DiskLoc &thisLoc, int &keyOf | |
| | | s, const BSONObj &keyBegin, int keyBeginLen, const vector< const BSONElemen | |
| | | t * > &keyEnd, const Ordering &order, int direction ); | |
| | | | |
| DiskLoc getHead(const DiskLoc& thisLoc); | | DiskLoc getHead(const DiskLoc& thisLoc); | |
| | | | |
| /* get tree shape */ | | /* get tree shape */ | |
| void shape(stringstream&); | | void shape(stringstream&); | |
| | | | |
| static void a_test(IndexDetails&); | | static void a_test(IndexDetails&); | |
| | | | |
| private: | | private: | |
| void fixParentPtrs(const DiskLoc& thisLoc); | | void fixParentPtrs(const DiskLoc& thisLoc); | |
| void delBucket(const DiskLoc& thisLoc, IndexDetails&); | | void delBucket(const DiskLoc& thisLoc, IndexDetails&); | |
| | | | |
| skipping to change at line 259 | | skipping to change at line 262 | |
| return keyOfs >= n ? BSONObj() : keyNode(keyOfs).key; | | return keyOfs >= n ? BSONObj() : keyNode(keyOfs).key; | |
| } | | } | |
| static BtreeBucket* allocTemp(); /* caller must release with free()
*/ | | static BtreeBucket* allocTemp(); /* caller must release with free()
*/ | |
| void insertHere(DiskLoc thisLoc, int keypos, | | void insertHere(DiskLoc thisLoc, int keypos, | |
| DiskLoc recordLoc, const BSONObj& key, const Orderi
ng &order, | | DiskLoc recordLoc, const BSONObj& key, const Orderi
ng &order, | |
| DiskLoc lchild, DiskLoc rchild, IndexDetails&); | | DiskLoc lchild, DiskLoc rchild, IndexDetails&); | |
| int _insert(DiskLoc thisLoc, DiskLoc recordLoc, | | int _insert(DiskLoc thisLoc, DiskLoc recordLoc, | |
| const BSONObj& key, const Ordering &order, bool dupsAll
owed, | | const BSONObj& key, const Ordering &order, bool dupsAll
owed, | |
| DiskLoc lChild, DiskLoc rChild, IndexDetails&); | | DiskLoc lChild, DiskLoc rChild, IndexDetails&); | |
| bool find(const IndexDetails& idx, const BSONObj& key, DiskLoc reco
rdLoc, const Ordering &order, int& pos, bool assertIfDup); | | bool find(const IndexDetails& idx, const BSONObj& key, DiskLoc reco
rdLoc, const Ordering &order, int& pos, bool assertIfDup); | |
|
| | | bool customFind( int l, int h, const BSONObj &keyBegin, int keyBegi
nLen, const vector< const BSONElement * > &keyEnd, const Ordering &order, i
nt direction, DiskLoc &thisLoc, int &keyOfs, pair< DiskLoc, int > &bestPare
nt ); | |
| 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, const vector< const BSONElement * > &rEnd, const Ordering &o
); | |
| public: | | public: | |
| // simply builds and returns a dup key error message string | | // simply builds and returns a dup key error message string | |
| static string dupKeyError( const IndexDetails& idx , const BSONObj&
key ); | | static string dupKeyError( const IndexDetails& idx , const BSONObj&
key ); | |
| }; | | }; | |
| #pragma pack() | | #pragma pack() | |
| | | | |
| class BtreeCursor : public Cursor { | | class BtreeCursor : public Cursor { | |
| public: | | public: | |
| BtreeCursor( NamespaceDetails *_d, int _idxNo, const IndexDetails&,
const BSONObj &startKey, const BSONObj &endKey, bool endKeyInclusive, int
direction ); | | BtreeCursor( NamespaceDetails *_d, int _idxNo, const IndexDetails&,
const BSONObj &startKey, const BSONObj &endKey, bool endKeyInclusive, int
direction ); | |
| | | | |
|
| BtreeCursor( NamespaceDetails *_d, int _idxNo, const IndexDetails&
_id, const BoundList &_bounds, int _direction ); | | BtreeCursor( NamespaceDetails *_d, int _idxNo, const IndexDetails&
_id, const shared_ptr< FieldRangeVector > &_bounds, int _direction ); | |
| ~BtreeCursor(){ | | ~BtreeCursor(){ | |
| } | | } | |
| virtual bool ok() { | | virtual bool ok() { | |
| return !bucket.isNull(); | | return !bucket.isNull(); | |
| } | | } | |
| bool eof() { | | bool eof() { | |
| return !ok(); | | return !ok(); | |
| } | | } | |
| virtual bool advance(); | | virtual bool advance(); | |
| | | | |
| | | | |
| skipping to change at line 337 | | skipping to change at line 342 | |
| } | | } | |
| virtual Record* _current() { | | virtual Record* _current() { | |
| return currLoc().rec(); | | return currLoc().rec(); | |
| } | | } | |
| virtual BSONObj current() { | | virtual BSONObj current() { | |
| return BSONObj(_current()); | | return BSONObj(_current()); | |
| } | | } | |
| virtual string toString() { | | virtual string toString() { | |
| string s = string("BtreeCursor ") + indexDetails.indexName(); | | string s = string("BtreeCursor ") + indexDetails.indexName(); | |
| if ( direction < 0 ) s += " reverse"; | | if ( direction < 0 ) s += " reverse"; | |
|
| if ( bounds_.size() > 1 ) s += " multi"; | | if ( bounds_.get() && bounds_->size() > 1 ) s += " multi"; | |
| return s; | | return s; | |
| } | | } | |
| | | | |
| BSONObj prettyKey( const BSONObj &key ) const { | | BSONObj prettyKey( const BSONObj &key ) const { | |
| return key.replaceFieldNames( indexDetails.keyPattern() ).clien
tReadable(); | | return key.replaceFieldNames( indexDetails.keyPattern() ).clien
tReadable(); | |
| } | | } | |
| | | | |
|
| virtual BSONArray prettyIndexBounds() const { | | virtual BSONObj prettyIndexBounds() const { | |
| BSONArrayBuilder ba; | | if ( !_independentFieldRanges ) { | |
| if ( bounds_.size() == 0 ) { | | return BSON( "start" << prettyKey( startKey ) << "end" << p | |
| ba << BSON_ARRAY( prettyKey( startKey ) << prettyKey( endKe | | rettyKey( endKey ) ); | |
| y ) ); | | | |
| } else { | | } else { | |
|
| for( BoundList::const_iterator i = bounds_.begin(); i != bo | | return bounds_->obj(); | |
| unds_.end(); ++i ) { | | | |
| ba << BSON_ARRAY( prettyKey( i->first ) << prettyKey( i | | | |
| ->second ) ); | | | |
| } | | | |
| } | | } | |
|
| return ba.arr(); | | | |
| } | | } | |
| | | | |
| void forgetEndKey() { endKey = BSONObj(); } | | void forgetEndKey() { endKey = BSONObj(); } | |
| | | | |
| virtual CoveredIndexMatcher *matcher() const { return _matcher.get(
); } | | virtual CoveredIndexMatcher *matcher() const { return _matcher.get(
); } | |
| | | | |
| virtual void setMatcher( shared_ptr< CoveredIndexMatcher > matcher
) { | | virtual void setMatcher( shared_ptr< CoveredIndexMatcher > matcher
) { | |
| _matcher = matcher; | | _matcher = matcher; | |
| } | | } | |
| | | | |
| // for debugging only | | // for debugging only | |
| DiskLoc getBucket() const { return bucket; } | | DiskLoc getBucket() const { return bucket; } | |
| | | | |
| private: | | private: | |
| /* Our btrees may (rarely) have "unused" keys when items are delete
d. | | /* Our btrees may (rarely) have "unused" keys when items are delete
d. | |
| Skip past them. | | Skip past them. | |
| */ | | */ | |
|
| void skipUnusedKeys(); | | bool skipUnusedKeys( bool mayJump ); | |
| | | bool skipOutOfRangeKeysAndCheckEnd(); | |
| /* Check if the current key is beyond endKey. */ | | void skipAndCheck(); | |
| void checkEnd(); | | void checkEnd(); | |
| | | | |
| // selective audits on construction | | // selective audits on construction | |
| void audit(); | | void audit(); | |
| | | | |
| // set initial bucket | | // set initial bucket | |
| void init(); | | void init(); | |
| | | | |
|
| // init start / end keys with a new range | | void advanceTo( const BSONObj &keyBegin, int keyBeginLen, const vec | |
| void initInterval(); | | tor< const BSONElement * > &keyEnd); | |
| | | | |
| friend class BtreeBucket; | | friend class BtreeBucket; | |
| set<DiskLoc> dups; | | set<DiskLoc> dups; | |
| NamespaceDetails *d; | | NamespaceDetails *d; | |
| int idxNo; | | int idxNo; | |
|
| | | | |
| BSONObj startKey; | | BSONObj startKey; | |
| BSONObj endKey; | | BSONObj endKey; | |
| bool endKeyInclusive_; | | bool endKeyInclusive_; | |
|
| | | | |
| bool multikey; // note this must be updated every getmore batch in
case someone added a multikey... | | bool multikey; // note this must be updated every getmore batch in
case someone added a multikey... | |
| | | | |
| const IndexDetails& indexDetails; | | const IndexDetails& indexDetails; | |
| BSONObj order; | | BSONObj order; | |
|
| | | Ordering _ordering; | |
| DiskLoc bucket; | | DiskLoc bucket; | |
| int keyOfs; | | int keyOfs; | |
| int direction; // 1=fwd,-1=reverse | | int direction; // 1=fwd,-1=reverse | |
| BSONObj keyAtKeyOfs; // so we can tell if things moved around on us
between the query and the getMore call | | BSONObj keyAtKeyOfs; // so we can tell if things moved around on us
between the query and the getMore call | |
| DiskLoc locAtKeyOfs; | | DiskLoc locAtKeyOfs; | |
|
| BoundList bounds_; | | shared_ptr< FieldRangeVector > bounds_; | |
| unsigned boundIndex_; | | auto_ptr< FieldRangeVector::Iterator > _boundsIterator; | |
| const IndexSpec& _spec; | | const IndexSpec& _spec; | |
| shared_ptr< CoveredIndexMatcher > _matcher; | | shared_ptr< CoveredIndexMatcher > _matcher; | |
|
| | | bool _independentFieldRanges; | |
| }; | | }; | |
| | | | |
| inline bool IndexDetails::hasKey(const BSONObj& key) { | | inline bool IndexDetails::hasKey(const BSONObj& key) { | |
| return head.btree()->exists(*this, head, key, Ordering::make(keyPat
tern())); | | return head.btree()->exists(*this, head, key, Ordering::make(keyPat
tern())); | |
| } | | } | |
| inline bool IndexDetails::wouldCreateDup(const BSONObj& key, DiskLoc se
lf) { | | inline bool IndexDetails::wouldCreateDup(const BSONObj& key, DiskLoc se
lf) { | |
| return head.btree()->wouldCreateDup(*this, head, key, Ordering::mak
e(keyPattern()), self); | | return head.btree()->wouldCreateDup(*this, head, key, Ordering::mak
e(keyPattern()), self); | |
| } | | } | |
| | | | |
| /* build btree from the bottom up */ | | /* build btree from the bottom up */ | |
| | | | |
End of changes. 15 change blocks. |
| 20 lines changed or deleted | | 25 lines changed or added | |
|
| chunk.h | | chunk.h | |
| | | | |
| skipping to change at line 33 | | skipping to change at line 33 | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include "../pch.h" | | #include "../pch.h" | |
| #include "../client/dbclient.h" | | #include "../client/dbclient.h" | |
| #include "../client/model.h" | | #include "../client/model.h" | |
| #include "../bson/util/atomic_int.h" | | #include "../bson/util/atomic_int.h" | |
| #include "shardkey.h" | | #include "shardkey.h" | |
| #include "shard.h" | | #include "shard.h" | |
| #include "config.h" | | #include "config.h" | |
|
| | | #include "util.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| class DBConfig; | | class DBConfig; | |
| class Chunk; | | class Chunk; | |
| class ChunkRange; | | class ChunkRange; | |
| class ChunkManager; | | class ChunkManager; | |
| class ChunkRangeMangager; | | class ChunkRangeMangager; | |
| class ChunkObjUnitTest; | | class ChunkObjUnitTest; | |
| | | | |
|
| struct ShardChunkVersion { | | | |
| union { | | | |
| struct { | | | |
| int _minor; | | | |
| int _major; | | | |
| }; | | | |
| unsigned long long _combined; | | | |
| }; | | | |
| | | | |
| ShardChunkVersion( int major=0, int minor=0 ) | | | |
| : _minor(minor),_major(major){ | | | |
| } | | | |
| | | | |
| ShardChunkVersion( unsigned long long ll ) | | | |
| : _combined( ll ){ | | | |
| } | | | |
| | | | |
| ShardChunkVersion incMajor() const { | | | |
| return ShardChunkVersion( _major + 1 , 0 ); | | | |
| } | | | |
| | | | |
| void operator++(){ | | | |
| _minor++; | | | |
| } | | | |
| | | | |
| unsigned long long toLong() const { | | | |
| return _combined; | | | |
| } | | | |
| | | | |
| bool isSet() const { | | | |
| return _combined > 0; | | | |
| } | | | |
| | | | |
| string toString() const { | | | |
| stringstream ss; | | | |
| ss << _major << "|" << _minor; | | | |
| return ss.str(); | | | |
| } | | | |
| | | | |
| operator unsigned long long() const { return _combined; } | | | |
| operator string() const { return toString(); } | | | |
| | | | |
| ShardChunkVersion& operator=( const BSONElement& elem ){ | | | |
| switch ( elem.type() ){ | | | |
| case Timestamp: | | | |
| case NumberLong: | | | |
| case Date: | | | |
| _combined = elem._numberLong(); | | | |
| break; | | | |
| case EOO: | | | |
| _combined = 0; | | | |
| break; | | | |
| default: | | | |
| assert(0); | | | |
| } | | | |
| return *this; | | | |
| } | | | |
| }; | | | |
| | | | |
| typedef shared_ptr<Chunk> ChunkPtr; | | typedef shared_ptr<Chunk> ChunkPtr; | |
| | | | |
| // key is max for each Chunk or ChunkRange | | // key is max for each Chunk or ChunkRange | |
| typedef map<BSONObj,ChunkPtr,BSONObjCmp> ChunkMap; | | typedef map<BSONObj,ChunkPtr,BSONObjCmp> ChunkMap; | |
| typedef map<BSONObj,shared_ptr<ChunkRange>,BSONObjCmp> ChunkRangeMap; | | typedef map<BSONObj,shared_ptr<ChunkRange>,BSONObjCmp> ChunkRangeMap; | |
| | | | |
| /** | | /** | |
| config.chunks | | config.chunks | |
| { ns : "alleyinsider.fs.chunks" , min : {} , max : {} , server : "lo
calhost:30001" } | | { ns : "alleyinsider.fs.chunks" , min : {} , max : {} , server : "lo
calhost:30001" } | |
| | | | |
| | | | |
| skipping to change at line 148 | | skipping to change at line 90 | |
| string toString() const; | | string toString() const; | |
| operator string() const { return toString(); } | | operator string() const { return toString(); } | |
| friend ostream& operator << (ostream& out, const Chunk& c){ return
(out << c.toString()); } | | friend ostream& operator << (ostream& out, const Chunk& c){ return
(out << c.toString()); } | |
| | | | |
| bool operator==(const Chunk& s) const; | | bool operator==(const Chunk& s) const; | |
| | | | |
| bool operator!=(const Chunk& s) const{ | | bool operator!=(const Chunk& s) const{ | |
| return ! ( *this == s ); | | return ! ( *this == s ); | |
| } | | } | |
| | | | |
|
| void getFilter( BSONObjBuilder& b ) const; | | | |
| BSONObj getFilter() const{ BSONObjBuilder b; getFilter( b ); return | | | |
| b.obj(); } | | | |
| | | | |
| // if min/max key is pos/neg infinity | | // if min/max key is pos/neg infinity | |
| bool minIsInf() const; | | bool minIsInf() const; | |
| bool maxIsInf() const; | | bool maxIsInf() const; | |
| | | | |
| BSONObj pickSplitPoint() const; | | BSONObj pickSplitPoint() const; | |
| ChunkPtr split(); | | ChunkPtr split(); | |
|
| ChunkPtr split( const BSONObj& middle ); | | | |
| | | void pickSplitVector( vector<BSONObj>* splitPoints ) const; | |
| | | ChunkPtr multiSplit( const vector<BSONObj>& splitPoints ); | |
| | | | |
| /** | | /** | |
| * @return size of shard in bytes | | * @return size of shard in bytes | |
| * talks to mongod to do this | | * talks to mongod to do this | |
| */ | | */ | |
| long getPhysicalSize() const; | | long getPhysicalSize() const; | |
| | | | |
|
| long countObjects( const BSONObj& filter = BSONObj() ) const; | | int countObjects(int maxcount=0) const; | |
| | | | |
| /** | | /** | |
| * if the amount of data written nears the max size of a shard | | * if the amount of data written nears the max size of a shard | |
| * then we check the real size, and if its too big, we split | | * then we check the real size, and if its too big, we split | |
| */ | | */ | |
| bool splitIfShould( long dataWritten ); | | bool splitIfShould( long dataWritten ); | |
| | | | |
| /* | | /* | |
| * moves either this shard or newShard if it makes sense too | | * moves either this shard or newShard if it makes sense too | |
| * @return whether or not a shard was moved | | * @return whether or not a shard was moved | |
| */ | | */ | |
| bool moveIfShould( ChunkPtr newShard = ChunkPtr() ); | | bool moveIfShould( ChunkPtr newShard = ChunkPtr() ); | |
| | | | |
| bool moveAndCommit( const Shard& to , string& errmsg ); | | bool moveAndCommit( const Shard& to , string& errmsg ); | |
| | | | |
| const char * getNS(){ return "config.chunks"; } | | const char * getNS(){ return "config.chunks"; } | |
| void serialize(BSONObjBuilder& to, ShardChunkVersion myLastMod=0); | | void serialize(BSONObjBuilder& to, ShardChunkVersion myLastMod=0); | |
| void unserialize(const BSONObj& from); | | void unserialize(const BSONObj& from); | |
|
| string modelServer(); | | string modelServer() const; | |
| | | | |
| void appendShortVersion( const char * name , BSONObjBuilder& b ); | | void appendShortVersion( const char * name , BSONObjBuilder& b ); | |
| | | | |
| void ensureIndex(); | | void ensureIndex(); | |
| | | | |
| void _markModified(); | | void _markModified(); | |
| | | | |
| static int MaxChunkSize; | | static int MaxChunkSize; | |
| | | | |
|
| string genID(); | | string genID() const; | |
| static string genID( const string& ns , const BSONObj& min ); | | static string genID( const string& ns , const BSONObj& min ); | |
| | | | |
| const ChunkManager* getManager() const { return _manager; } | | const ChunkManager* getManager() const { return _manager; } | |
| | | | |
| bool modified(); | | bool modified(); | |
|
| | | | |
| | | ShardChunkVersion getVersionOnConfigServer() const; | |
| private: | | private: | |
| | | | |
|
| | | bool _splitIfShould( long dataWritten ); | |
| | | | |
| // main shard info | | // main shard info | |
| | | | |
| ChunkManager * _manager; | | ChunkManager * _manager; | |
| ShardKeyPattern skey() const; | | ShardKeyPattern skey() const; | |
| | | | |
| BSONObj _min; | | BSONObj _min; | |
| BSONObj _max; | | BSONObj _max; | |
| Shard _shard; | | Shard _shard; | |
| ShardChunkVersion _lastmod; | | ShardChunkVersion _lastmod; | |
| | | | |
| | | | |
| skipping to change at line 236 | | skipping to change at line 181 | |
| class ChunkRange{ | | class ChunkRange{ | |
| public: | | public: | |
| const ChunkManager* getManager() const{ return _manager; } | | const ChunkManager* getManager() const{ return _manager; } | |
| Shard getShard() const{ return _shard; } | | Shard getShard() const{ return _shard; } | |
| | | | |
| const BSONObj& getMin() const { return _min; } | | const BSONObj& getMin() const { return _min; } | |
| const BSONObj& getMax() const { return _max; } | | const BSONObj& getMax() const { return _max; } | |
| | | | |
| // clones of Chunk methods | | // clones of Chunk methods | |
| bool contains(const BSONObj& obj) const; | | bool contains(const BSONObj& obj) const; | |
|
| void getFilter( BSONObjBuilder& b ) const; | | | |
| BSONObj getFilter() const{ BSONObjBuilder b; getFilter( b ); return | | | |
| b.obj(); } | | | |
| long countObjects( const BSONObj& filter = BSONObj() ) const; | | | |
| | | | |
| ChunkRange(ChunkMap::const_iterator begin, const ChunkMap::const_it
erator end) | | ChunkRange(ChunkMap::const_iterator begin, const ChunkMap::const_it
erator end) | |
| : _manager(begin->second->getManager()) | | : _manager(begin->second->getManager()) | |
| , _shard(begin->second->getShard()) | | , _shard(begin->second->getShard()) | |
| , _min(begin->second->getMin()) | | , _min(begin->second->getMin()) | |
| , _max(prior(end)->second->getMax()) | | , _max(prior(end)->second->getMax()) | |
| { | | { | |
| assert( begin != end ); | | assert( begin != end ); | |
| | | | |
| DEV while (begin != end){ | | DEV while (begin != end){ | |
| | | | |
| skipping to change at line 324 | | skipping to change at line 266 | |
| int numChunks(){ rwlock lk( _lock , false ); return _chunkMap.size(
); } | | int numChunks(){ rwlock lk( _lock , false ); return _chunkMap.size(
); } | |
| bool hasShardKey( const BSONObj& obj ); | | bool hasShardKey( const BSONObj& obj ); | |
| | | | |
| ChunkPtr findChunk( const BSONObj& obj , bool retry = false ); | | ChunkPtr findChunk( const BSONObj& obj , bool retry = false ); | |
| ChunkPtr findChunkOnServer( const Shard& shard ) const; | | ChunkPtr findChunkOnServer( const Shard& shard ) const; | |
| | | | |
| ShardKeyPattern& getShardKey(){ return _key; } | | ShardKeyPattern& getShardKey(){ return _key; } | |
| const ShardKeyPattern& getShardKey() const { return _key; } | | const ShardKeyPattern& getShardKey() const { return _key; } | |
| bool isUnique(){ return _unique; } | | bool isUnique(){ return _unique; } | |
| | | | |
|
| | | void maybeChunkCollection(); | |
| | | | |
| /** | | /** | |
| * makes sure the shard index is on all servers | | * makes sure the shard index is on all servers | |
| */ | | */ | |
| void ensureIndex(); | | void ensureIndex(); | |
| | | | |
|
| /** | | void getShardsForQuery( set<Shard>& shards , const BSONObj& query ) | |
| * @return number of Chunk added to the vector | | ; | |
| */ | | | |
| int getChunksForQuery( vector<shared_ptr<ChunkRange> >& chunks , co | | | |
| nst BSONObj& query ); | | | |
| | | | |
| /** | | | |
| * @return number of Shards added to the set | | | |
| */ | | | |
| int getShardsForQuery( set<Shard>& shards , const BSONObj& query ); | | | |
| | | | |
| void getAllShards( set<Shard>& all ); | | void getAllShards( set<Shard>& all ); | |
| | | | |
| void save(); | | void save(); | |
| | | | |
| string toString() const; | | string toString() const; | |
| operator string() const { return toString(); } | | operator string() const { return toString(); } | |
| | | | |
| ShardChunkVersion getVersion( const Shard& shard ) const; | | ShardChunkVersion getVersion( const Shard& shard ) const; | |
| ShardChunkVersion getVersion() const; | | ShardChunkVersion getVersion() const; | |
| | | | |
| /** | | /** | |
|
| | | * actually does a query on the server | |
| | | * doesn't look at any local data | |
| | | */ | |
| | | ShardChunkVersion getVersionOnConfigServer() const; | |
| | | | |
| | | /** | |
| * this is just an increasing number of how many ChunkManagers we h
ave so we know if something has been updated | | * this is just an increasing number of how many ChunkManagers we h
ave so we know if something has been updated | |
| */ | | */ | |
| unsigned long long getSequenceNumber(){ | | unsigned long long getSequenceNumber(){ | |
| return _sequenceNumber; | | return _sequenceNumber; | |
| } | | } | |
| | | | |
| /** | | /** | |
| * @param me - so i don't get deleted before i'm done | | * @param me - so i don't get deleted before i'm done | |
| */ | | */ | |
| void drop( ChunkManagerPtr me ); | | void drop( ChunkManagerPtr me ); | |
| | | | |
| skipping to change at line 397 | | skipping to change at line 339 | |
| RWLock _lock; | | RWLock _lock; | |
| | | | |
| // This should only be called from Chunk after it has been migrated | | // This should only be called from Chunk after it has been migrated | |
| void _migrationNotification(Chunk* c); | | void _migrationNotification(Chunk* c); | |
| | | | |
| friend class Chunk; | | friend class Chunk; | |
| friend class ChunkRangeManager; // only needed for CRM::assertValid
() | | friend class ChunkRangeManager; // only needed for CRM::assertValid
() | |
| static AtomicUInt NextSequenceNumber; | | static AtomicUInt NextSequenceNumber; | |
| | | | |
| bool _isValid() const; | | bool _isValid() const; | |
|
| | | | |
| /** | | | |
| * @return number of Chunk matching the query or -1 for all chunks. | | | |
| */ | | | |
| int _getChunksForQuery( vector<shared_ptr<ChunkRange> >& chunks , c | | | |
| onst BSONObj& query ); | | | |
| }; | | }; | |
| | | | |
| // like BSONObjCmp. for use as an STL comparison functor | | // like BSONObjCmp. for use as an STL comparison functor | |
| // key-order in "order" argument must match key-order in shardkey | | // key-order in "order" argument must match key-order in shardkey | |
| class ChunkCmp { | | class ChunkCmp { | |
| public: | | public: | |
| ChunkCmp( const BSONObj &order = BSONObj() ) : _cmp( order ) {} | | ChunkCmp( const BSONObj &order = BSONObj() ) : _cmp( order ) {} | |
| bool operator()( const Chunk &l, const Chunk &r ) const { | | bool operator()( const Chunk &l, const Chunk &r ) const { | |
| return _cmp(l.getMin(), r.getMin()); | | return _cmp(l.getMin(), r.getMin()); | |
| } | | } | |
| | | | |
| skipping to change at line 436 | | skipping to change at line 373 | |
| | | | |
| /* | | /* | |
| struct chunk_lock { | | struct chunk_lock { | |
| chunk_lock( const Chunk* c ){ | | chunk_lock( const Chunk* c ){ | |
| | | | |
| } | | } | |
| | | | |
| Chunk _c; | | Chunk _c; | |
| }; | | }; | |
| */ | | */ | |
|
| inline string Chunk::genID(){ return genID(_manager->getns(), _min); } | | inline string Chunk::genID() const { return genID(_manager->getns(), _m
in); } | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 15 change blocks. |
| 88 lines changed or deleted | | 22 lines changed or added | |
|
| d_logic.h | | d_logic.h | |
| | | | |
| skipping to change at line 22 | | skipping to change at line 22 | |
| * GNU Affero General Public License for more details. | | * GNU Affero General Public License for more details. | |
| * | | * | |
| * You should have received a copy of the GNU Affero General Public Lice
nse | | * You should have received a copy of the GNU Affero General Public Lice
nse | |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. | | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include "../pch.h" | | #include "../pch.h" | |
| #include "../db/jsobj.h" | | #include "../db/jsobj.h" | |
|
| | | #include "util.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| class ShardingState; | | class ShardingState; | |
| | | | |
|
| typedef unsigned long long ConfigVersion; | | typedef ShardChunkVersion ConfigVersion; | |
| typedef map<string,ConfigVersion> NSVersionMap; | | typedef map<string,ConfigVersion> NSVersionMap; | |
| | | | |
| // ----------- | | // ----------- | |
| | | | |
|
| /** | | | |
| * TODO: this only works with single fields at the moment | | | |
| */ | | | |
| class ChunkMatcher { | | class ChunkMatcher { | |
| typedef map<BSONObj,pair<BSONObj,BSONObj>,BSONObjCmp> MyMap; | | typedef map<BSONObj,pair<BSONObj,BSONObj>,BSONObjCmp> MyMap; | |
| public: | | public: | |
| | | | |
| bool belongsToMe( const BSONObj& key , const DiskLoc& loc ) const; | | bool belongsToMe( const BSONObj& key , const DiskLoc& loc ) const; | |
| | | | |
| private: | | private: | |
| ChunkMatcher( ConfigVersion version ); | | ChunkMatcher( ConfigVersion version ); | |
| | | | |
| void gotRange( const BSONObj& min , const BSONObj& max ); | | void gotRange( const BSONObj& min , const BSONObj& max ); | |
| | | | |
| ConfigVersion _version; | | ConfigVersion _version; | |
|
| string _field; | | BSONObj _key; | |
| MyMap _map; | | MyMap _map; | |
| | | | |
| friend class ShardingState; | | friend class ShardingState; | |
| }; | | }; | |
| | | | |
| typedef shared_ptr<ChunkMatcher> ChunkMatcherPtr; | | typedef shared_ptr<ChunkMatcher> ChunkMatcherPtr; | |
| | | | |
| // -------------- | | // -------------- | |
| // --- global state --- | | // --- global state --- | |
| // -------------- | | // -------------- | |
| | | | |
| skipping to change at line 79 | | skipping to change at line 77 | |
| | | | |
| bool hasVersion( const string& ns ); | | bool hasVersion( const string& ns ); | |
| bool hasVersion( const string& ns , ConfigVersion& version ); | | bool hasVersion( const string& ns , ConfigVersion& version ); | |
| ConfigVersion& getVersion( const string& ns ); // TODO: this is dan
geroues | | ConfigVersion& getVersion( const string& ns ); // TODO: this is dan
geroues | |
| void setVersion( const string& ns , const ConfigVersion& version ); | | void setVersion( const string& ns , const ConfigVersion& version ); | |
| | | | |
| void appendInfo( BSONObjBuilder& b ); | | void appendInfo( BSONObjBuilder& b ); | |
| | | | |
| ChunkMatcherPtr getChunkMatcher( const string& ns ); | | ChunkMatcherPtr getChunkMatcher( const string& ns ); | |
| | | | |
|
| | | bool inCriticalMigrateSection(); | |
| private: | | private: | |
| | | | |
| bool _enabled; | | bool _enabled; | |
| | | | |
| string _configServer; | | string _configServer; | |
| | | | |
| string _shardName; | | string _shardName; | |
| string _shardHost; | | string _shardHost; | |
| | | | |
| mongo::mutex _mutex; | | mongo::mutex _mutex; | |
| | | | |
| skipping to change at line 112 | | skipping to change at line 111 | |
| | | | |
| const OID& getID() const { return _id; } | | const OID& getID() const { return _id; } | |
| bool hasID() const { return _id.isSet(); } | | bool hasID() const { return _id.isSet(); } | |
| void setID( const OID& id ); | | void setID( const OID& id ); | |
| | | | |
| ConfigVersion& getVersion( const string& ns ); // TODO: this is dan
geroues | | ConfigVersion& getVersion( const string& ns ); // TODO: this is dan
geroues | |
| void setVersion( const string& ns , const ConfigVersion& version ); | | void setVersion( const string& ns , const ConfigVersion& version ); | |
| | | | |
| static ShardedConnectionInfo* get( bool create ); | | static ShardedConnectionInfo* get( bool create ); | |
| | | | |
|
| | | bool inForceMode() const { | |
| | | return _forceMode; | |
| | | } | |
| | | | |
| | | void enterForceMode(){ _forceMode = true; } | |
| | | void leaveForceMode(){ _forceMode = false; } | |
| | | | |
| private: | | private: | |
| | | | |
| OID _id; | | OID _id; | |
| NSVersionMap _versions; | | NSVersionMap _versions; | |
|
| | | bool _forceMode; | |
| | | | |
| static boost::thread_specific_ptr<ShardedConnectionInfo> _tl; | | static boost::thread_specific_ptr<ShardedConnectionInfo> _tl; | |
| }; | | }; | |
| | | | |
|
| | | struct ShardForceModeBlock { | |
| | | ShardForceModeBlock(){ | |
| | | info = ShardedConnectionInfo::get( false ); | |
| | | if ( info ) | |
| | | info->enterForceMode(); | |
| | | } | |
| | | ~ShardForceModeBlock(){ | |
| | | if ( info ) | |
| | | info->leaveForceMode(); | |
| | | } | |
| | | | |
| | | ShardedConnectionInfo * info; | |
| | | }; | |
| | | | |
| // ----------------- | | // ----------------- | |
| // --- core --- | | // --- core --- | |
| // ----------------- | | // ----------------- | |
| | | | |
| unsigned long long extractVersion( BSONElement e , string& errmsg ); | | unsigned long long extractVersion( BSONElement e , string& errmsg ); | |
| | | | |
| /** | | /** | |
| * @return true if we have any shard info for the ns | | * @return true if we have any shard info for the ns | |
| */ | | */ | |
| bool haveLocalShardingInfo( const string& ns ); | | bool haveLocalShardingInfo( const string& ns ); | |
| | | | |
| skipping to change at line 141 | | skipping to change at line 162 | |
| /** | | /** | |
| * @return true if the current threads shard version is ok, or not in s
harded version | | * @return true if the current threads shard version is ok, or not in s
harded version | |
| */ | | */ | |
| bool shardVersionOk( const string& ns , string& errmsg ); | | bool shardVersionOk( const string& ns , string& errmsg ); | |
| | | | |
| /** | | /** | |
| * @return true if we took care of the message and nothing else should
be done | | * @return true if we took care of the message and nothing else should
be done | |
| */ | | */ | |
| bool handlePossibleShardedMessage( Message &m, DbResponse &dbresponse )
; | | bool handlePossibleShardedMessage( Message &m, DbResponse &dbresponse )
; | |
| | | | |
|
| | | void logOpForSharding( const char * opstr , const char * ns , const BSO | |
| | | NObj& obj , BSONObj * patt ); | |
| | | | |
| // ----------------- | | // ----------------- | |
| // --- writeback --- | | // --- writeback --- | |
| // ----------------- | | // ----------------- | |
| | | | |
| /* queue a write back on a remote server for a failed write */ | | /* queue a write back on a remote server for a failed write */ | |
| void queueWriteBack( const string& remote , const BSONObj& o ); | | void queueWriteBack( const string& remote , const BSONObj& o ); | |
| | | | |
| } | | } | |
| | | | |
End of changes. 9 change blocks. |
| 5 lines changed or deleted | | 29 lines changed or added | |
|
| dbclient.h | | dbclient.h | |
| | | | |
| skipping to change at line 71 | | skipping to change at line 71 | |
| */ | | */ | |
| QueryOption_AwaitData = 1 << 5, | | QueryOption_AwaitData = 1 << 5, | |
| | | | |
| /** Stream the data down full blast in multiple "more" packages, on
the assumption that the client | | /** Stream the data down full blast in multiple "more" packages, on
the assumption that the client | |
| will fully read all data queried. Faster when you are pulling
a lot of data and know you want to | | will fully read all data queried. Faster when you are pulling
a lot of data and know you want to | |
| pull it all down. Note: it is not allowed to not read all the
data unless you close the connection. | | pull it all down. Note: it is not allowed to not read all the
data unless you close the connection. | |
| | | | |
| Use the query( boost::function<void(const BSONObj&)> f, ... ) v
ersion of the connection's query() | | Use the query( boost::function<void(const BSONObj&)> f, ... ) v
ersion of the connection's query() | |
| method, and it will take care of all the details for you. | | method, and it will take care of all the details for you. | |
| */ | | */ | |
|
| QueryOption_Exhaust = 1 << 6 | | QueryOption_Exhaust = 1 << 6, | |
| | | | |
| | | QueryOption_AllSupported = QueryOption_CursorTailable | QueryOption | |
| | | _SlaveOk | QueryOption_OplogReplay | QueryOption_NoCursorTimeout | QueryOpt | |
| | | ion_AwaitData | QueryOption_Exhaust | |
| | | | |
| }; | | }; | |
| | | | |
| enum UpdateOptions { | | enum UpdateOptions { | |
| /** Upsert - that is, insert the item if no matching item is found.
*/ | | /** Upsert - that is, insert the item if no matching item is found.
*/ | |
| UpdateOption_Upsert = 1 << 0, | | UpdateOption_Upsert = 1 << 0, | |
| | | | |
| /** Update multiple documents (if multiple documents match query ex
pression). | | /** Update multiple documents (if multiple documents match query ex
pression). | |
| (Default is update a single document and stop.) */ | | (Default is update a single document and stop.) */ | |
| UpdateOption_Multi = 1 << 1 | | UpdateOption_Multi = 1 << 1 | |
| | | | |
| skipping to change at line 177 | | skipping to change at line 179 | |
| */ | | */ | |
| enum WriteConcern { | | enum WriteConcern { | |
| W_NONE = 0 , // TODO: not every connection type fully supports this | | W_NONE = 0 , // TODO: not every connection type fully supports this | |
| W_NORMAL = 1 | | W_NORMAL = 1 | |
| // TODO SAFE = 2 | | // TODO SAFE = 2 | |
| }; | | }; | |
| | | | |
| class BSONObj; | | class BSONObj; | |
| class ScopedDbConnection; | | class ScopedDbConnection; | |
| class DBClientCursor; | | class DBClientCursor; | |
|
| | | class DBClientCursorBatchIterator; | |
| | | | |
| /** Represents a Mongo query expression. Typically one uses the QUERY(
...) macro to construct a Query object. | | /** Represents a Mongo query expression. Typically one uses the QUERY(
...) macro to construct a Query object. | |
| Examples: | | Examples: | |
| QUERY( "age" << 33 << "school" << "UCLA" ).sort("name") | | QUERY( "age" << 33 << "school" << "UCLA" ).sort("name") | |
| QUERY( "age" << GT << 30 << LT << 50 ) | | QUERY( "age" << GT << 30 << LT << 50 ) | |
| */ | | */ | |
| class Query { | | class Query { | |
| public: | | public: | |
| BSONObj obj; | | BSONObj obj; | |
| Query() : obj(BSONObj()) { } | | Query() : obj(BSONObj()) { } | |
| | | | |
| skipping to change at line 344 | | skipping to change at line 347 | |
| /** | | /** | |
| DB "commands" | | DB "commands" | |
| Basically just invocations of connection.$cmd.findOne({...}); | | Basically just invocations of connection.$cmd.findOne({...}); | |
| */ | | */ | |
| class DBClientWithCommands : public DBClientInterface { | | class DBClientWithCommands : public DBClientInterface { | |
| set<string> _seenIndexes; | | set<string> _seenIndexes; | |
| public: | | public: | |
| /** controls how chatty the client is about network errors & such.
See log.h */ | | /** controls how chatty the client is about network errors & such.
See log.h */ | |
| int _logLevel; | | int _logLevel; | |
| | | | |
|
| DBClientWithCommands() : _logLevel(0) { } | | DBClientWithCommands() : _logLevel(0), _cachedAvailableOptions( (en
um QueryOptions)0 ), _haveCachedAvailableOptions(false) { } | |
| | | | |
| /** helper function. run a simple command where the command expres
sion is simply | | /** helper function. run a simple command where the command expres
sion is simply | |
| { command : 1 } | | { command : 1 } | |
| @param info -- where to put result object. may be null if call
er doesn't need that info | | @param info -- where to put result object. may be null if call
er doesn't need that info | |
| @param command -- command name | | @param command -- command name | |
| @return true if the command returned "ok". | | @return true if the command returned "ok". | |
| */ | | */ | |
| bool simpleCommand(const string &dbname, BSONObj *info, const strin
g &command); | | bool simpleCommand(const string &dbname, BSONObj *info, const strin
g &command); | |
| | | | |
| /** Run a database command. Database commands are represented as B
SON objects. Common database | | /** Run a database command. Database commands are represented as B
SON objects. Common database | |
| | | | |
| skipping to change at line 643 | | skipping to change at line 646 | |
| string::size_type pos = ns.find( "." ); | | string::size_type pos = ns.find( "." ); | |
| if ( pos == string::npos ) | | if ( pos == string::npos ) | |
| return ""; | | return ""; | |
| | | | |
| return ns.substr( pos + 1 ); | | return ns.substr( pos + 1 ); | |
| } | | } | |
| | | | |
| protected: | | protected: | |
| bool isOk(const BSONObj&); | | bool isOk(const BSONObj&); | |
| | | | |
|
| | | enum QueryOptions availableOptions(); | |
| | | | |
| | | private: | |
| | | enum QueryOptions _cachedAvailableOptions; | |
| | | bool _haveCachedAvailableOptions; | |
| }; | | }; | |
| | | | |
| /** | | /** | |
| abstract class that implements the core db operations | | abstract class that implements the core db operations | |
| */ | | */ | |
| class DBClientBase : public DBClientWithCommands, public DBConnector { | | class DBClientBase : public DBClientWithCommands, public DBConnector { | |
| protected: | | protected: | |
| WriteConcern _writeConcern; | | WriteConcern _writeConcern; | |
| | | | |
| public: | | public: | |
| | | | |
| skipping to change at line 790 | | skipping to change at line 798 | |
| virtual bool auth(const string &dbname, const string &username, con
st string &pwd, string& errmsg, bool digestPassword = true); | | virtual bool auth(const string &dbname, const string &username, con
st string &pwd, string& errmsg, bool digestPassword = true); | |
| | | | |
| virtual auto_ptr<DBClientCursor> query(const string &ns, Query quer
y=Query(), int nToReturn = 0, int nToSkip = 0, | | virtual auto_ptr<DBClientCursor> query(const string &ns, Query quer
y=Query(), int nToReturn = 0, int nToSkip = 0, | |
| const BSONObj *fieldsToRetur
n = 0, int queryOptions = 0 , int batchSize = 0 ) { | | const BSONObj *fieldsToRetur
n = 0, int queryOptions = 0 , int batchSize = 0 ) { | |
| checkConnection(); | | checkConnection(); | |
| return DBClientBase::query( ns, query, nToReturn, nToSkip, fiel
dsToReturn, queryOptions , batchSize ); | | return DBClientBase::query( ns, query, nToReturn, nToSkip, fiel
dsToReturn, queryOptions , batchSize ); | |
| } | | } | |
| | | | |
| /** uses QueryOption_Exhaust | | /** uses QueryOption_Exhaust | |
| */ | | */ | |
|
| unsigned long long query( boost::function<void(const BSONObj&)> f, | | unsigned long long query( boost::function<void(const BSONObj&)> f, | |
| const string& ns, Query query, const BSONObj *fieldsToReturn = 0); | | 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); | |
| | | | |
| /** | | /** | |
| @return true if this connection is currently in a failed state.
When autoreconnect is on, | | @return true if this connection is currently in a failed state.
When autoreconnect is on, | |
| a connection will transition back to an ok state after r
econnecting. | | a connection will transition back to an ok state after r
econnecting. | |
| */ | | */ | |
| bool isFailed() const { | | bool isFailed() const { | |
| return failed; | | return failed; | |
| } | | } | |
| | | | |
| MessagingPort& port() { | | MessagingPort& port() { | |
| | | | |
End of changes. 5 change blocks. |
| 4 lines changed or deleted | | 18 lines changed or added | |
|
| dbclientcursor.h | | dbclientcursor.h | |
| | | | |
| skipping to change at line 41 | | skipping to change at line 41 | |
| class DBClientCursor : boost::noncopyable { | | class DBClientCursor : boost::noncopyable { | |
| public: | | public: | |
| /** If true, safe to call next(). Requests more from server
if necessary. */ | | /** If true, safe to call next(). Requests more from server
if necessary. */ | |
| bool more(); | | bool more(); | |
| | | | |
| /** If true, there is more in our local buffers to be fetched via n
ext(). Returns | | /** If true, there is more in our local buffers to be fetched via n
ext(). Returns | |
| false when a getMore request back to server would be required.
You can use this | | false when a getMore request back to server would be required.
You can use this | |
| if you want to exhaust whatever data has been fetched to the cl
ient already but | | if you want to exhaust whatever data has been fetched to the cl
ient already but | |
| then perhaps stop. | | then perhaps stop. | |
| */ | | */ | |
|
| bool moreInCurrentBatch() { return !_putBack.empty() || pos < nRetu
rned; } | | bool moreInCurrentBatch() { _assertIfNull(); return !_putBack.empty
() || pos < nReturned; } | |
| | | | |
| /** next | | /** next | |
| @return next object in the result cursor. | | @return next object in the result cursor. | |
| on an error at the remote server, you will get back: | | on an error at the remote server, you will get back: | |
| { $err: <string> } | | { $err: <string> } | |
| if you do not want to handle that yourself, call nextSafe(). | | if you do not want to handle that yourself, call nextSafe(). | |
| */ | | */ | |
| BSONObj next(); | | BSONObj next(); | |
| | | | |
| /** | | /** | |
| | | | |
| skipping to change at line 68 | | skipping to change at line 68 | |
| BSONObj o = next(); | | BSONObj o = next(); | |
| BSONElement e = o.firstElement(); | | BSONElement e = o.firstElement(); | |
| if( strcmp(e.fieldName(), "$err") == 0 ) { | | if( strcmp(e.fieldName(), "$err") == 0 ) { | |
| if( logLevel >= 5 ) | | if( logLevel >= 5 ) | |
| log() << "nextSafe() error " << o.toString() << endl; | | log() << "nextSafe() error " << o.toString() << endl; | |
| uassert(13106, "nextSafe(): " + o.toString(), false); | | uassert(13106, "nextSafe(): " + o.toString(), false); | |
| } | | } | |
| return o; | | return o; | |
| } | | } | |
| | | | |
|
| | | /** peek ahead at items buffered for future next() calls. | |
| | | never requests new data from the server. so peek only effectiv | |
| | | e | |
| | | with what is already buffered. | |
| | | WARNING: no support for _putBack yet! | |
| | | */ | |
| | | void peek(vector<BSONObj>&, int atMost); | |
| | | | |
| /** | | /** | |
| iterate the rest of the cursor and return the number if items | | iterate the rest of the cursor and return the number if items | |
| */ | | */ | |
| int itcount(){ | | int itcount(){ | |
| int c = 0; | | int c = 0; | |
| while ( more() ){ | | while ( more() ){ | |
| next(); | | next(); | |
| c++; | | c++; | |
| } | | } | |
| return c; | | return c; | |
| } | | } | |
| | | | |
| /** cursor no longer valid -- use with tailable cursors. | | /** cursor no longer valid -- use with tailable cursors. | |
| note you should only rely on this once more() returns false; | | note you should only rely on this once more() returns false; | |
| 'dead' may be preset yet some data still queued and locally | | 'dead' may be preset yet some data still queued and locally | |
| available from the dbclientcursor. | | available from the dbclientcursor. | |
| */ | | */ | |
| bool isDead() const { | | bool isDead() const { | |
|
| return cursorId == 0; | | return !this || cursorId == 0; | |
| } | | } | |
| | | | |
| bool tailable() const { | | bool tailable() const { | |
| return (opts & QueryOption_CursorTailable) != 0; | | return (opts & QueryOption_CursorTailable) != 0; | |
| } | | } | |
| | | | |
| /** see QueryResult::ResultFlagType (db/dbmessage.h) for flag value
s | | /** see QueryResult::ResultFlagType (db/dbmessage.h) for flag value
s | |
| mostly these flags are for internal purposes - | | mostly these flags are for internal purposes - | |
| ResultFlag_ErrSet is the possible exception to that | | ResultFlag_ErrSet is the possible exception to that | |
| */ | | */ | |
| bool hasResultFlag( int flag ){ | | bool hasResultFlag( int flag ){ | |
|
| | | _assertIfNull(); | |
| return (resultFlags & flag) != 0; | | return (resultFlags & flag) != 0; | |
| } | | } | |
| | | | |
| DBClientCursor( DBConnector *_connector, const string &_ns, BSONObj
_query, int _nToReturn, | | DBClientCursor( DBConnector *_connector, const string &_ns, BSONObj
_query, int _nToReturn, | |
| int _nToSkip, const BSONObj *_fieldsToReturn, int q
ueryOptions , int bs ) : | | int _nToSkip, const BSONObj *_fieldsToReturn, int q
ueryOptions , int bs ) : | |
| connector(_connector), | | connector(_connector), | |
| ns(_ns), | | ns(_ns), | |
| query(_query), | | query(_query), | |
| nToReturn(_nToReturn), | | nToReturn(_nToReturn), | |
| haveLimit( _nToReturn > 0 && !(queryOptions & QueryOption_C
ursorTailable)), | | haveLimit( _nToReturn > 0 && !(queryOptions & QueryOption_C
ursorTailable)), | |
| | | | |
| skipping to change at line 172 | | skipping to change at line 180 | |
| int resultFlags; | | int resultFlags; | |
| long long cursorId; | | long long cursorId; | |
| int nReturned; | | int nReturned; | |
| int pos; | | int pos; | |
| const char *data; | | const char *data; | |
| void dataReceived(); | | void dataReceived(); | |
| void requestMore(); | | void requestMore(); | |
| void exhaustReceiveMore(); // for exhaust | | void exhaustReceiveMore(); // for exhaust | |
| bool _ownCursor; // see decouple() | | bool _ownCursor; // see decouple() | |
| string _scopedHost; | | string _scopedHost; | |
|
| | | | |
| | | // Don't call from a virtual function | |
| | | void _assertIfNull() { uassert(13348, "connection died", this); } | |
| | | }; | |
| | | | |
| | | /** iterate over objects in current batch only - will not cause a netwo | |
| | | rk call | |
| | | */ | |
| | | class DBClientCursorBatchIterator { | |
| | | public: | |
| | | DBClientCursorBatchIterator( DBClientCursor &c ) : _c( c ), _n() {} | |
| | | bool moreInCurrentBatch() { return _c.moreInCurrentBatch(); } | |
| | | BSONObj nextSafe() { | |
| | | massert( 13383, "BatchIterator empty", moreInCurrentBatch() ); | |
| | | ++_n; | |
| | | return _c.nextSafe(); | |
| | | } | |
| | | int n() const { return _n; } | |
| | | private: | |
| | | DBClientCursor &_c; | |
| | | int _n; | |
| }; | | }; | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
| #include "undef_macros.h" | | #include "undef_macros.h" | |
| | | | |
End of changes. 5 change blocks. |
| 2 lines changed or deleted | | 32 lines changed or added | |
|
| log.h | | log.h | |
| | | | |
| skipping to change at line 153 | | skipping to change at line 153 | |
| } | | } | |
| virtual void flush(Tee *t = 0) {} | | virtual void flush(Tee *t = 0) {} | |
| }; | | }; | |
| extern Nullstream nullstream; | | extern Nullstream nullstream; | |
| | | | |
| class Logstream : public Nullstream { | | class Logstream : public Nullstream { | |
| static mongo::mutex mutex; | | static mongo::mutex mutex; | |
| static int doneSetup; | | static int doneSetup; | |
| stringstream ss; | | stringstream ss; | |
| LogLevel logLevel; | | LogLevel logLevel; | |
|
| | | static boost::scoped_ptr<ostream> stream; | |
| | | static FILE* logfile; | |
| public: | | public: | |
|
| | | | |
| | | static void setLogFile(FILE* f){ | |
| | | scoped_lock lk(mutex); | |
| | | logfile = f; | |
| | | } | |
| | | | |
| static int magicNumber(){ | | static int magicNumber(){ | |
| return 1717; | | return 1717; | |
| } | | } | |
|
| | | | |
| void flush(Tee *t = 0) { | | void flush(Tee *t = 0) { | |
| // this ensures things are sane | | // this ensures things are sane | |
| if ( doneSetup == 1717 ) { | | if ( doneSetup == 1717 ) { | |
| BufBuilder b(512); | | BufBuilder b(512); | |
| time_t_to_String( time(0) , b.grow(20) ); | | time_t_to_String( time(0) , b.grow(20) ); | |
| b.append( ss.str() ); | | b.append( ss.str() ); | |
| const char *s = b.buf(); | | const char *s = b.buf(); | |
| | | | |
| string threadName = getThreadName(); | | string threadName = getThreadName(); | |
| const char * type = logLevelToString(logLevel); | | const char * type = logLevelToString(logLevel); | |
| | | | |
| skipping to change at line 168 | | skipping to change at line 177 | |
| // this ensures things are sane | | // this ensures things are sane | |
| if ( doneSetup == 1717 ) { | | if ( doneSetup == 1717 ) { | |
| BufBuilder b(512); | | BufBuilder b(512); | |
| time_t_to_String( time(0) , b.grow(20) ); | | time_t_to_String( time(0) , b.grow(20) ); | |
| b.append( ss.str() ); | | b.append( ss.str() ); | |
| const char *s = b.buf(); | | const char *s = b.buf(); | |
| | | | |
| string threadName = getThreadName(); | | string threadName = getThreadName(); | |
| const char * type = logLevelToString(logLevel); | | const char * type = logLevelToString(logLevel); | |
| | | | |
|
| | | StringBuilder sb; | |
| | | if (!threadName.empty()){ | |
| | | sb << "[" << threadName << "] "; | |
| | | } | |
| | | sb << type << ( type[0] ? ": " : "" ); | |
| | | sb << s; | |
| | | string out = sb.str(); | |
| | | | |
| scoped_lock lk(mutex); | | scoped_lock lk(mutex); | |
| | | | |
| if( t ) t->write(logLevel,s); | | if( t ) t->write(logLevel,s); | |
| | | | |
| #ifndef _WIN32 | | #ifndef _WIN32 | |
| //syslog( LOG_INFO , "%s" , cc ); | | //syslog( LOG_INFO , "%s" , cc ); | |
| #endif | | #endif | |
|
| | | fwrite(out.data(), out.size(), 1, logfile); | |
| if ( ! threadName.empty() ){ | | fflush(logfile); | |
| cout << "[" << threadName << "] "; | | | |
| } | | | |
| cout << type << ( type[0] ? ": " : "" ); | | | |
| cout << s; | | | |
| cout.flush(); | | | |
| } | | } | |
| _init(); | | _init(); | |
| } | | } | |
| | | | |
| Nullstream& setLogLevel(LogLevel l){ | | Nullstream& setLogLevel(LogLevel l){ | |
| logLevel = l; | | logLevel = l; | |
| return *this; | | return *this; | |
| } | | } | |
| | | | |
| /** note these are virtual */ | | /** note these are virtual */ | |
| | | | |
| skipping to change at line 326 | | skipping to change at line 338 | |
| 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 ); | | void initLogging( const string& logpath , bool append ); | |
| void rotateLogs( int signal = 0 ); | | void rotateLogs( int signal = 0 ); | |
| | | | |
|
| | | std::string toUtf8String(const std::wstring& wide); | |
| | | | |
| inline string errnoWithDescription(int x = errno) { | | inline string errnoWithDescription(int x = errno) { | |
| stringstream s; | | stringstream s; | |
|
| s << "errno:" << x << ' ' << strerror(x); | | s << "errno:" << x << ' '; | |
| | | | |
| | | #if defined(_WIN32) | |
| | | LPTSTR errorText = NULL; | |
| | | FormatMessage( | |
| | | FORMAT_MESSAGE_FROM_SYSTEM | |
| | | |FORMAT_MESSAGE_ALLOCATE_BUFFER | |
| | | |FORMAT_MESSAGE_IGNORE_INSERTS, | |
| | | NULL, | |
| | | x, 0, | |
| | | (LPTSTR) &errorText, // output | |
| | | 0, // minimum size for output buffer | |
| | | NULL); | |
| | | if( errorText ) { | |
| | | string x = toUtf8String(errorText); | |
| | | s << x; | |
| | | LocalFree(errorText); | |
| | | } | |
| | | else | |
| | | s << strerror(x); | |
| | | /* | |
| | | DWORD n = FormatMessage( | |
| | | FORMAT_MESSAGE_ALLOCATE_BUFFER | | |
| | | FORMAT_MESSAGE_FROM_SYSTEM | | |
| | | FORMAT_MESSAGE_IGNORE_INSERTS, | |
| | | NULL, x, | |
| | | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), | |
| | | (LPTSTR) &lpMsgBuf, 0, NULL); | |
| | | */ | |
| | | #else | |
| | | s << strerror(x); | |
| | | #endif | |
| return s.str(); | | return s.str(); | |
| } | | } | |
| | | | |
| /** output the error # and error message with prefix. | | /** output the error # and error message with prefix. | |
| handy for use as parm in uassert/massert. | | handy for use as parm in uassert/massert. | |
| */ | | */ | |
| string errnoWithPrefix( const char * prefix = 0 ); | | string errnoWithPrefix( const char * prefix = 0 ); | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 7 change blocks. |
| 8 lines changed or deleted | | 53 lines changed or added | |
|
| matcher.h | | matcher.h | |
| | | | |
| skipping to change at line 31 | | skipping to change at line 31 | |
| #pragma once | | #pragma once | |
| | | | |
| #include "jsobj.h" | | #include "jsobj.h" | |
| #include <pcrecpp.h> | | #include <pcrecpp.h> | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| class Cursor; | | class Cursor; | |
| class CoveredIndexMatcher; | | class CoveredIndexMatcher; | |
| class Matcher; | | class Matcher; | |
|
| | | class FieldRangeVector; | |
| | | | |
| class RegexMatcher { | | class RegexMatcher { | |
| public: | | public: | |
| const char *fieldName; | | const char *fieldName; | |
| const char *regex; | | const char *regex; | |
| const char *flags; | | const char *flags; | |
| string prefix; | | string prefix; | |
| shared_ptr< pcrecpp::RE > re; | | shared_ptr< pcrecpp::RE > re; | |
| bool isNot; | | bool isNot; | |
| RegexMatcher() : isNot() {} | | RegexMatcher() : isNot() {} | |
| | | | |
| skipping to change at line 155 | | skipping to change at line 156 | |
| bool keyMatch() const { return !all && !haveSize && !hasArray && !h
aveNeg; } | | bool keyMatch() const { return !all && !haveSize && !hasArray && !h
aveNeg; } | |
| | | | |
| bool atomic() const { return _atomic; } | | bool atomic() const { return _atomic; } | |
| | | | |
| bool hasType( BSONObj::MatchType type ) const; | | bool hasType( BSONObj::MatchType type ) const; | |
| | | | |
| string toString() const { | | string toString() const { | |
| return jsobj.toString(); | | return jsobj.toString(); | |
| } | | } | |
| | | | |
|
| void addOrConstraint( const BSONObj &o ) { | | void addOrConstraint( const shared_ptr< FieldRangeVector > &frv ) { | |
| _norMatchers.push_back( shared_ptr< Matcher >( new Matcher( o ) | | _orConstraints.push_back( frv ); | |
| ) ); | | | |
| } | | } | |
| | | | |
| bool sameCriteriaCount( const Matcher &other ) const; | | bool sameCriteriaCount( const Matcher &other ) const; | |
| | | | |
| private: | | private: | |
| // Only specify constrainIndexKey if matches() will be called with | | // Only specify constrainIndexKey if matches() will be called with | |
| // index keys having empty string field names. | | // index keys having empty string field names. | |
| Matcher( const Matcher &other, const BSONObj &constrainIndexKey ); | | Matcher( const Matcher &other, const BSONObj &constrainIndexKey ); | |
| | | | |
| void addBasic(const BSONElement &e, int c, bool isNot) { | | void addBasic(const BSONElement &e, int c, bool isNot) { | |
| | | | |
| skipping to change at line 204 | | skipping to change at line 205 | |
| */ | | */ | |
| bool _atomic; | | bool _atomic; | |
| | | | |
| RegexMatcher regexs[4]; | | RegexMatcher regexs[4]; | |
| int nRegex; | | int nRegex; | |
| | | | |
| // so we delete the mem when we're done: | | // so we delete the mem when we're done: | |
| vector< shared_ptr< BSONObjBuilder > > _builders; | | vector< shared_ptr< BSONObjBuilder > > _builders; | |
| list< shared_ptr< Matcher > > _orMatchers; | | list< shared_ptr< Matcher > > _orMatchers; | |
| list< shared_ptr< Matcher > > _norMatchers; | | list< shared_ptr< Matcher > > _norMatchers; | |
|
| | | vector< shared_ptr< FieldRangeVector > > _orConstraints; | |
| | | | |
| friend class CoveredIndexMatcher; | | friend class CoveredIndexMatcher; | |
| }; | | }; | |
| | | | |
| // If match succeeds on index key, then attempt to match full document. | | // If match succeeds on index key, then attempt to match full document. | |
| class CoveredIndexMatcher : boost::noncopyable { | | class CoveredIndexMatcher : boost::noncopyable { | |
| public: | | public: | |
| CoveredIndexMatcher(const BSONObj &pattern, const BSONObj &indexKey
Pattern , bool alwaysUseRecord=false ); | | CoveredIndexMatcher(const BSONObj &pattern, const BSONObj &indexKey
Pattern , bool alwaysUseRecord=false ); | |
| bool matches(const BSONObj &o){ return _docMatcher->matches( o ); } | | bool matches(const BSONObj &o){ return _docMatcher->matches( o ); } | |
| bool matches(const BSONObj &key, const DiskLoc &recLoc , MatchDetai
ls * details = 0 ); | | bool matches(const BSONObj &key, const DiskLoc &recLoc , MatchDetai
ls * details = 0 ); | |
| bool matchesCurrent( Cursor * cursor , MatchDetails * details = 0 )
; | | bool matchesCurrent( Cursor * cursor , MatchDetails * details = 0 )
; | |
| bool needRecord(){ return _needRecord; } | | bool needRecord(){ return _needRecord; } | |
| | | | |
| Matcher& docMatcher() { return *_docMatcher; } | | Matcher& docMatcher() { return *_docMatcher; } | |
| | | | |
| // 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 addOrConstraint( const BSONObj &o ) { | | void addOrConstraint( const shared_ptr< FieldRangeVector > &frv ) { | |
| _docMatcher->addOrConstraint( o ); | | _docMatcher->addOrConstraint( frv ); | |
| } | | } | |
| | | | |
| CoveredIndexMatcher *nextClauseMatcher( const BSONObj &indexKeyPatt
ern, bool alwaysUseRecord=false ) { | | CoveredIndexMatcher *nextClauseMatcher( const BSONObj &indexKeyPatt
ern, bool alwaysUseRecord=false ) { | |
| return new CoveredIndexMatcher( _docMatcher, indexKeyPattern, a
lwaysUseRecord ); | | return new CoveredIndexMatcher( _docMatcher, indexKeyPattern, a
lwaysUseRecord ); | |
| } | | } | |
| private: | | private: | |
| CoveredIndexMatcher(const shared_ptr< Matcher > &docMatcher, const
BSONObj &indexKeyPattern , bool alwaysUseRecord=false ); | | CoveredIndexMatcher(const shared_ptr< Matcher > &docMatcher, const
BSONObj &indexKeyPattern , bool alwaysUseRecord=false ); | |
| void init( bool alwaysUseRecord ); | | void init( bool alwaysUseRecord ); | |
| shared_ptr< Matcher > _docMatcher; | | shared_ptr< Matcher > _docMatcher; | |
| Matcher _keyMatcher; | | Matcher _keyMatcher; | |
| | | | |
End of changes. 4 change blocks. |
| 5 lines changed or deleted | | 6 lines changed or added | |
|
| oplog.h | | oplog.h | |
| | | | |
| skipping to change at line 98 | | skipping to change at line 98 | |
| return; | | return; | |
| } | | } | |
| _findingStartCursor->c->advance(); | | _findingStartCursor->c->advance(); | |
| RARELY { | | RARELY { | |
| if ( _findingStartTimer.seconds() >= __findingStart
InitialTimeout ) { | | if ( _findingStartTimer.seconds() >= __findingStart
InitialTimeout ) { | |
| createClientCursor( startLoc( _findingStartCurs
or->c->currLoc() ) ); | | createClientCursor( startLoc( _findingStartCurs
or->c->currLoc() ) ); | |
| _findingStartMode = FindExtent; | | _findingStartMode = FindExtent; | |
| return; | | return; | |
| } | | } | |
| } | | } | |
|
| maybeRelease(); | | | |
| return; | | return; | |
| } | | } | |
| case FindExtent: { | | case FindExtent: { | |
| if ( !_matcher->matches( _findingStartCursor->c->currKe
y(), _findingStartCursor->c->currLoc() ) ) { | | if ( !_matcher->matches( _findingStartCursor->c->currKe
y(), _findingStartCursor->c->currLoc() ) ) { | |
| _findingStartMode = InExtent; | | _findingStartMode = InExtent; | |
| return; | | return; | |
| } | | } | |
| DiskLoc prev = prevLoc( _findingStartCursor->c->currLoc
() ); | | DiskLoc prev = prevLoc( _findingStartCursor->c->currLoc
() ); | |
| if ( prev.isNull() ) { // hit beginning, so start scann
ing from here | | if ( prev.isNull() ) { // hit beginning, so start scann
ing from here | |
| createClientCursor(); | | createClientCursor(); | |
| _findingStartMode = InExtent; | | _findingStartMode = InExtent; | |
| return; | | return; | |
| } | | } | |
| // There might be a more efficient implementation than
creating new cursor & client cursor each time, | | // There might be a more efficient implementation than
creating new cursor & client cursor each time, | |
| // not worrying about that for now | | // not worrying about that for now | |
| createClientCursor( prev ); | | createClientCursor( prev ); | |
|
| maybeRelease(); | | | |
| return; | | return; | |
| } | | } | |
| case InExtent: { | | case InExtent: { | |
| if ( _matcher->matches( _findingStartCursor->c->currKey
(), _findingStartCursor->c->currLoc() ) ) { | | if ( _matcher->matches( _findingStartCursor->c->currKey
(), _findingStartCursor->c->currLoc() ) ) { | |
| _findingStart = false; // found first record in que
ry range, so scan normally | | _findingStart = false; // found first record in que
ry range, so scan normally | |
| _c = _qp.newCursor( _findingStartCursor->c->currLoc
() ); | | _c = _qp.newCursor( _findingStartCursor->c->currLoc
() ); | |
| destroyClientCursor(); | | destroyClientCursor(); | |
| return; | | return; | |
| } | | } | |
| _findingStartCursor->c->advance(); | | _findingStartCursor->c->advance(); | |
|
| maybeRelease(); | | | |
| return; | | return; | |
| } | | } | |
| default: { | | default: { | |
| massert( 12600, "invalid _findingStartMode", false ); | | massert( 12600, "invalid _findingStartMode", false ); | |
| } | | } | |
| } | | } | |
| } | | } | |
|
| | | void prepareToYield() { | |
| | | if ( _findingStartCursor ) { | |
| | | _findingStartCursor->prepareToYield( _yieldData ); | |
| | | } | |
| | | } | |
| | | void recoverFromYield() { | |
| | | if ( _findingStartCursor ) { | |
| | | if ( !ClientCursor::recoverFromYield( _yieldData ) ) { | |
| | | _findingStartCursor = 0; | |
| | | } | |
| | | } | |
| | | } | |
| private: | | private: | |
| enum FindingStartMode { Initial, FindExtent, InExtent }; | | enum FindingStartMode { Initial, FindExtent, InExtent }; | |
| const QueryPlan &_qp; | | const QueryPlan &_qp; | |
| bool _findingStart; | | bool _findingStart; | |
| FindingStartMode _findingStartMode; | | FindingStartMode _findingStartMode; | |
| auto_ptr< CoveredIndexMatcher > _matcher; | | auto_ptr< CoveredIndexMatcher > _matcher; | |
| Timer _findingStartTimer; | | Timer _findingStartTimer; | |
| ClientCursor * _findingStartCursor; | | ClientCursor * _findingStartCursor; | |
| shared_ptr<Cursor> _c; | | shared_ptr<Cursor> _c; | |
|
| | | ClientCursor::YieldData _yieldData; | |
| DiskLoc startLoc( const DiskLoc &rec ) { | | DiskLoc startLoc( const DiskLoc &rec ) { | |
| Extent *e = rec.rec()->myExtent( rec ); | | Extent *e = rec.rec()->myExtent( rec ); | |
| if ( !_qp.nsd()->capLooped() || ( e->myLoc != _qp.nsd()->capExt
ent ) ) | | if ( !_qp.nsd()->capLooped() || ( e->myLoc != _qp.nsd()->capExt
ent ) ) | |
| return e->firstRecord; | | return e->firstRecord; | |
| // Likely we are on the fresh side of capExtent, so return firs
t fresh record. | | // Likely we are on the fresh side of capExtent, so return firs
t fresh record. | |
| // If we are on the stale side of capExtent, then the collectio
n is small and it | | // If we are on the stale side of capExtent, then the collectio
n is small and it | |
| // doesn't matter if we start the extent scan with capFirstNewR
ecord. | | // doesn't matter if we start the extent scan with capFirstNewR
ecord. | |
| return _qp.nsd()->capFirstNewRecord; | | return _qp.nsd()->capFirstNewRecord; | |
| } | | } | |
| | | | |
| | | | |
| skipping to change at line 181 | | skipping to change at line 191 | |
| void createClientCursor( const DiskLoc &startLoc = DiskLoc() ) { | | void createClientCursor( const DiskLoc &startLoc = DiskLoc() ) { | |
| shared_ptr<Cursor> c = _qp.newCursor( startLoc ); | | shared_ptr<Cursor> c = _qp.newCursor( startLoc ); | |
| _findingStartCursor = new ClientCursor(QueryOption_NoCursorTime
out, c, _qp.ns()); | | _findingStartCursor = new ClientCursor(QueryOption_NoCursorTime
out, c, _qp.ns()); | |
| } | | } | |
| void destroyClientCursor() { | | void destroyClientCursor() { | |
| if ( _findingStartCursor ) { | | if ( _findingStartCursor ) { | |
| ClientCursor::erase( _findingStartCursor->cursorid ); | | ClientCursor::erase( _findingStartCursor->cursorid ); | |
| _findingStartCursor = 0; | | _findingStartCursor = 0; | |
| } | | } | |
| } | | } | |
|
| void maybeRelease() { | | | |
| if ( ! _findingStartCursor->yieldSometimes() ){ | | | |
| _findingStartCursor = 0; | | | |
| } | | | |
| } | | | |
| void init() { | | void init() { | |
| // Use a ClientCursor here so we can release db mutex while sca
nning | | // Use a ClientCursor here so we can release db mutex while sca
nning | |
| // oplog (can take quite a while with large oplogs). | | // oplog (can take quite a while with large oplogs). | |
| shared_ptr<Cursor> c = _qp.newReverseCursor(); | | shared_ptr<Cursor> c = _qp.newReverseCursor(); | |
| _findingStartCursor = new ClientCursor(QueryOption_NoCursorTime
out, c, _qp.ns()); | | _findingStartCursor = new ClientCursor(QueryOption_NoCursorTime
out, c, _qp.ns()); | |
| _findingStartTimer.reset(); | | _findingStartTimer.reset(); | |
| _findingStartMode = Initial; | | _findingStartMode = Initial; | |
| BSONElement tsElt = _qp.originalQuery()[ "ts" ]; | | BSONElement tsElt = _qp.originalQuery()[ "ts" ]; | |
| massert( 13044, "no ts field in query", !tsElt.eoo() ); | | massert( 13044, "no ts field in query", !tsElt.eoo() ); | |
| BSONObjBuilder b; | | BSONObjBuilder b; | |
| b.append( tsElt ); | | b.append( tsElt ); | |
| BSONObj tsQuery = b.obj(); | | BSONObj tsQuery = b.obj(); | |
| _matcher.reset(new CoveredIndexMatcher(tsQuery, _qp.indexKey())
); | | _matcher.reset(new CoveredIndexMatcher(tsQuery, _qp.indexKey())
); | |
| } | | } | |
| }; | | }; | |
| | | | |
| void pretouchOperation(const BSONObj& op); | | void pretouchOperation(const BSONObj& op); | |
|
| | | void pretouchN(vector<BSONObj>&, unsigned a, unsigned b); | |
| | | | |
| void applyOperation_inlock(const BSONObj& op); | | void applyOperation_inlock(const BSONObj& op); | |
| } | | } | |
| | | | |
End of changes. 7 change blocks. |
| 8 lines changed or deleted | | 14 lines changed or added | |
|
| pdfile.h | | pdfile.h | |
| | | | |
| skipping to change at line 55 | | skipping to change at line 55 | |
| | | | |
| /* low level - only drops this ns */ | | /* low level - only drops this ns */ | |
| void dropNS(const string& dropNs); | | void dropNS(const string& dropNs); | |
| | | | |
| /* deletes this ns, indexes and cursors */ | | /* deletes this ns, indexes and cursors */ | |
| void dropCollection( const string &name, string &errmsg, BSONObjBuilder
&result ); | | void dropCollection( const string &name, string &errmsg, BSONObjBuilder
&result ); | |
| bool userCreateNS(const char *ns, BSONObj j, string& err, bool logForRe
plication, bool *deferIdIndex = 0); | | bool userCreateNS(const char *ns, BSONObj j, string& err, bool logForRe
plication, bool *deferIdIndex = 0); | |
| shared_ptr<Cursor> findTableScan(const char *ns, const BSONObj& order,
const DiskLoc &startLoc=DiskLoc()); | | shared_ptr<Cursor> findTableScan(const char *ns, const BSONObj& order,
const DiskLoc &startLoc=DiskLoc()); | |
| | | | |
| // -1 if library unavailable. | | // -1 if library unavailable. | |
|
| boost::intmax_t freeSpace(); | | boost::intmax_t freeSpace( const string &path = dbpath ); | |
| | | | |
| /*---------------------------------------------------------------------
*/ | | /*---------------------------------------------------------------------
*/ | |
| | | | |
| class MongoDataFile { | | class MongoDataFile { | |
| friend class DataFileMgr; | | friend class DataFileMgr; | |
| friend class BasicCursor; | | friend class BasicCursor; | |
| public: | | public: | |
| MongoDataFile(int fn) : fileNo(fn) { } | | MongoDataFile(int fn) : fileNo(fn) { } | |
| void open(const char *filename, int requestedDataSize = 0, bool pre
allocateOnly = false); | | void open(const char *filename, int requestedDataSize = 0, bool pre
allocateOnly = false); | |
| | | | |
| | | | |
| skipping to change at line 109 | | skipping to change at line 109 | |
| /* see if we can find an extent of the right size in the freelist.
*/ | | /* see if we can find an extent of the right size in the freelist.
*/ | |
| static Extent* allocFromFreeList(const char *ns, int approxSize, bo
ol capped = false); | | static Extent* allocFromFreeList(const char *ns, int approxSize, bo
ol capped = false); | |
| | | | |
| /** @return DiskLoc where item ends up */ | | /** @return DiskLoc where item ends up */ | |
| // changedId should be initialized to false | | // changedId should be initialized to false | |
| const DiskLoc updateRecord( | | const DiskLoc updateRecord( | |
| const char *ns, | | const char *ns, | |
| NamespaceDetails *d, | | NamespaceDetails *d, | |
| NamespaceDetailsTransient *nsdt, | | NamespaceDetailsTransient *nsdt, | |
| Record *toupdate, const DiskLoc& dl, | | Record *toupdate, const DiskLoc& dl, | |
|
| const char *buf, int len, OpDebug& debug, bool &changedId); | | const char *buf, int len, OpDebug& debug, bool &changedId, bool | |
| | | god=false); | |
| | | | |
| // The object o may be updated if modified on insert. | | // The object o may be updated if modified on insert. | |
| void insertAndLog( const char *ns, const BSONObj &o, bool god = fal
se ); | | void insertAndLog( const char *ns, const BSONObj &o, bool god = fal
se ); | |
| | | | |
| /** @param obj both and in and out param -- insert can sometimes mo
dify an object (such as add _id). */ | | /** @param obj both and in and out param -- insert can sometimes mo
dify an object (such as add _id). */ | |
| DiskLoc insertWithObjMod(const char *ns, BSONObj &o, bool god = fal
se); | | DiskLoc insertWithObjMod(const char *ns, BSONObj &o, bool god = fal
se); | |
| | | | |
| /** @param obj in value only for this version. */ | | /** @param obj in value only for this version. */ | |
| void insertNoReturnVal(const char *ns, BSONObj o, bool god = false)
; | | void insertNoReturnVal(const char *ns, BSONObj o, bool god = false)
; | |
| | | | |
| DiskLoc insert(const char *ns, const void *buf, int len, bool god =
false, const BSONElement &writeId = BSONElement(), bool mayAddIndex = true
); | | DiskLoc insert(const char *ns, const void *buf, int len, bool god =
false, const BSONElement &writeId = BSONElement(), bool mayAddIndex = true
); | |
| | | | |
End of changes. 2 change blocks. |
| 2 lines changed or deleted | | 4 lines changed or added | |
|
| queryoptimizer.h | | queryoptimizer.h | |
| | | | |
| skipping to change at line 25 | | skipping to change at line 25 | |
| * You should have received a copy of the GNU Affero General Public Licen
se | | * You should have received a copy of the GNU Affero General Public Licen
se | |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. | | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include "cursor.h" | | #include "cursor.h" | |
| #include "jsobj.h" | | #include "jsobj.h" | |
| #include "queryutil.h" | | #include "queryutil.h" | |
| #include "matcher.h" | | #include "matcher.h" | |
|
| | | #include "../util/message.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| class IndexDetails; | | class IndexDetails; | |
| class IndexType; | | class IndexType; | |
| | | | |
| class QueryPlan : boost::noncopyable { | | class QueryPlan : boost::noncopyable { | |
| public: | | public: | |
| QueryPlan(NamespaceDetails *_d, | | QueryPlan(NamespaceDetails *_d, | |
| int _idxNo, // -1 = no index | | int _idxNo, // -1 = no index | |
| | | | |
| skipping to change at line 60 | | skipping to change at line 61 | |
| /* If true, the startKey and endKey are unhelpful and the index ord
er doesn't match the | | /* If true, the startKey and endKey are unhelpful and the index ord
er doesn't match the | |
| requested sort order */ | | requested sort order */ | |
| bool unhelpful() const { return unhelpful_; } | | bool unhelpful() const { return unhelpful_; } | |
| int direction() const { return direction_; } | | int direction() const { return direction_; } | |
| shared_ptr<Cursor> newCursor( const DiskLoc &startLoc = DiskLoc() ,
int numWanted=0 ) const; | | shared_ptr<Cursor> newCursor( const DiskLoc &startLoc = DiskLoc() ,
int numWanted=0 ) const; | |
| shared_ptr<Cursor> newReverseCursor() const; | | shared_ptr<Cursor> newReverseCursor() const; | |
| BSONObj indexKey() const; | | BSONObj indexKey() const; | |
| const char *ns() const { return fbs_.ns(); } | | const char *ns() const { return fbs_.ns(); } | |
| NamespaceDetails *nsd() const { return d; } | | NamespaceDetails *nsd() const { return d; } | |
| BSONObj originalQuery() const { return _originalQuery; } | | BSONObj originalQuery() const { return _originalQuery; } | |
|
| BSONObj simplifiedQuery( const BSONObj& fields = BSONObj(), bool ex
pandIn = false ) const { return fbs_.simplifiedQuery( fields, expandIn ); } | | BSONObj simplifiedQuery( const BSONObj& fields = BSONObj() ) const
{ return fbs_.simplifiedQuery( fields ); } | |
| const FieldRange &range( const char *fieldName ) const { return fbs
_.range( fieldName ); } | | const FieldRange &range( const char *fieldName ) const { return fbs
_.range( fieldName ); } | |
| void registerSelf( long long nScanned ) const; | | void registerSelf( long long nScanned ) const; | |
|
| // just for testing | | shared_ptr< FieldRangeVector > frv() const { return _frv; } | |
| BoundList indexBounds() const { return indexBounds_; } | | | |
| private: | | private: | |
| NamespaceDetails *d; | | NamespaceDetails *d; | |
| int idxNo; | | int idxNo; | |
| const FieldRangeSet &fbs_; | | const FieldRangeSet &fbs_; | |
| const BSONObj &_originalQuery; | | const BSONObj &_originalQuery; | |
| const BSONObj &order_; | | const BSONObj &order_; | |
| const IndexDetails *index_; | | const IndexDetails *index_; | |
| bool optimal_; | | bool optimal_; | |
| bool scanAndOrderRequired_; | | bool scanAndOrderRequired_; | |
| bool exactKeyMatch_; | | bool exactKeyMatch_; | |
| int direction_; | | int direction_; | |
|
| BoundList indexBounds_; | | shared_ptr< FieldRangeVector > _frv; | |
| | | BSONObj _startKey; | |
| | | BSONObj _endKey; | |
| bool endKeyInclusive_; | | bool endKeyInclusive_; | |
| bool unhelpful_; | | bool unhelpful_; | |
| string _special; | | string _special; | |
| IndexType * _type; | | IndexType * _type; | |
|
| | | bool _startOrEndSpec; | |
| }; | | }; | |
| | | | |
| // Inherit from this interface to implement a new query operation. | | // Inherit from this interface to implement a new query operation. | |
| // The query optimizer will clone the QueryOp that is provided, giving | | // The query optimizer will clone the QueryOp that is provided, giving | |
| // each clone its own query plan. | | // each clone its own query plan. | |
| class QueryOp { | | class QueryOp { | |
| public: | | public: | |
|
| QueryOp() : _complete(), _stopRequested(), _qp(), _error(), _haveOr
Constraint() {} | | QueryOp() : _complete(), _stopRequested(), _qp(), _error() {} | |
| | | | |
| // Used when handing off from one QueryOp type to another | | // Used when handing off from one QueryOp type to another | |
| QueryOp( const QueryOp &other ) : | | QueryOp( const QueryOp &other ) : | |
| _complete(), _stopRequested(), _qp(), _error(), _matcher( other._ma
tcher ), | | _complete(), _stopRequested(), _qp(), _error(), _matcher( other._ma
tcher ), | |
|
| _haveOrConstraint( other._haveOrConstraint ), _orConstraint( other.
_orConstraint ) {} | | _orConstraint( other._orConstraint ) {} | |
| | | | |
| virtual ~QueryOp() {} | | virtual ~QueryOp() {} | |
| | | | |
| /** these gets called after a query plan is set */ | | /** these gets called after a query plan is set */ | |
| void init() { | | void init() { | |
| if ( _oldMatcher.get() ) { | | if ( _oldMatcher.get() ) { | |
| _matcher.reset( _oldMatcher->nextClauseMatcher( qp().indexK
ey() ) ); | | _matcher.reset( _oldMatcher->nextClauseMatcher( qp().indexK
ey() ) ); | |
| } else { | | } else { | |
| _matcher.reset( new CoveredIndexMatcher( qp().originalQuery
(), qp().indexKey(), alwaysUseRecord() ) ); | | _matcher.reset( new CoveredIndexMatcher( qp().originalQuery
(), qp().indexKey(), alwaysUseRecord() ) ); | |
| } | | } | |
| _init(); | | _init(); | |
| } | | } | |
| virtual void next() = 0; | | virtual void next() = 0; | |
| | | | |
| virtual bool mayRecordPlan() const = 0; | | virtual bool mayRecordPlan() const = 0; | |
| | | | |
|
| | | virtual void prepareToYield() { massert( 13335, "yield not supporte | |
| | | d", false ); } | |
| | | virtual void recoverFromYield() { massert( 13336, "yield not suppor | |
| | | ted", false ); } | |
| | | | |
| /** @return a copy of the inheriting class, which will be run with
its own | | /** @return a copy of the inheriting class, which will be run with
its own | |
| query plan. If multiple plan sets are required for an
$or query, | | query plan. If multiple plan sets are required for an
$or query, | |
| the QueryOp of the winning plan from a given set will b
e cloned | | the QueryOp of the winning plan from a given set will b
e cloned | |
| to generate QueryOps for the subsequent plan set. This
function | | to generate QueryOps for the subsequent plan set. This
function | |
| should only be called after the query op has completed
executing. | | should only be called after the query op has completed
executing. | |
| */ | | */ | |
| QueryOp *createChild() { | | QueryOp *createChild() { | |
|
| if( _haveOrConstraint ) { | | if( _orConstraint.get() ) { | |
| _matcher->addOrConstraint( _orConstraint ); | | _matcher->addOrConstraint( _orConstraint ); | |
|
| _haveOrConstraint = false; | | _orConstraint.reset(); | |
| } | | } | |
| QueryOp *ret = _createChild(); | | QueryOp *ret = _createChild(); | |
| ret->_oldMatcher = _matcher; | | ret->_oldMatcher = _matcher; | |
| return ret; | | return ret; | |
| } | | } | |
| bool complete() const { return _complete; } | | bool complete() const { return _complete; } | |
| bool error() const { return _error; } | | bool error() const { return _error; } | |
| bool stopRequested() const { return _stopRequested; } | | bool stopRequested() const { return _stopRequested; } | |
| ExceptionInfo exception() const { return _exception; } | | ExceptionInfo exception() const { return _exception; } | |
| const QueryPlan &qp() const { return *_qp; } | | const QueryPlan &qp() const { return *_qp; } | |
| // To be called by QueryPlanSet::Runner only. | | // To be called by QueryPlanSet::Runner only. | |
| void setQueryPlan( const QueryPlan *qp ) { _qp = qp; } | | void setQueryPlan( const QueryPlan *qp ) { _qp = qp; } | |
| void setException( const DBException &e ) { | | void setException( const DBException &e ) { | |
| _error = true; | | _error = true; | |
| _exception = e.getInfo(); | | _exception = e.getInfo(); | |
| } | | } | |
| shared_ptr< CoveredIndexMatcher > matcher() const { return _matcher
; } | | shared_ptr< CoveredIndexMatcher > matcher() const { return _matcher
; } | |
| protected: | | protected: | |
| void setComplete() { | | void setComplete() { | |
|
| _haveOrConstraint = true; | | _orConstraint = qp().frv(); | |
| _orConstraint = qp().simplifiedQuery( qp().indexKey(), true ); | | | |
| _complete = true; | | _complete = true; | |
| } | | } | |
| void setStop() { setComplete(); _stopRequested = true; } | | void setStop() { setComplete(); _stopRequested = true; } | |
| | | | |
| virtual void _init() = 0; | | virtual void _init() = 0; | |
| | | | |
| virtual QueryOp *_createChild() const = 0; | | virtual QueryOp *_createChild() const = 0; | |
| | | | |
| virtual bool alwaysUseRecord() const { return false; } | | virtual bool alwaysUseRecord() const { return false; } | |
| | | | |
| private: | | private: | |
| bool _complete; | | bool _complete; | |
| bool _stopRequested; | | bool _stopRequested; | |
| ExceptionInfo _exception; | | ExceptionInfo _exception; | |
| const QueryPlan *_qp; | | const QueryPlan *_qp; | |
| bool _error; | | bool _error; | |
| shared_ptr< CoveredIndexMatcher > _matcher; | | shared_ptr< CoveredIndexMatcher > _matcher; | |
| shared_ptr< CoveredIndexMatcher > _oldMatcher; | | shared_ptr< CoveredIndexMatcher > _oldMatcher; | |
|
| bool _haveOrConstraint; | | shared_ptr< FieldRangeVector > _orConstraint; | |
| BSONObj _orConstraint; | | | |
| }; | | }; | |
| | | | |
| // Set of candidate query plans for a particular query. Used for runni
ng | | // Set of candidate query plans for a particular query. Used for runni
ng | |
| // a QueryOp on these plans. | | // a QueryOp on these plans. | |
| class QueryPlanSet { | | class QueryPlanSet { | |
| public: | | public: | |
| | | | |
| typedef boost::shared_ptr< QueryPlan > PlanPtr; | | typedef boost::shared_ptr< QueryPlan > PlanPtr; | |
| typedef vector< PlanPtr > PlanSet; | | typedef vector< PlanPtr > PlanSet; | |
| | | | |
| QueryPlanSet( const char *ns, | | QueryPlanSet( const char *ns, | |
| auto_ptr< FieldRangeSet > frs, | | auto_ptr< FieldRangeSet > frs, | |
| const BSONObj &originalQuery, | | const BSONObj &originalQuery, | |
| const BSONObj &order, | | const BSONObj &order, | |
| const BSONElement *hint = 0, | | const BSONElement *hint = 0, | |
| bool honorRecordedPlan = true, | | bool honorRecordedPlan = true, | |
| const BSONObj &min = BSONObj(), | | const BSONObj &min = BSONObj(), | |
| const BSONObj &max = BSONObj(), | | const BSONObj &max = BSONObj(), | |
|
| bool bestGuessOnly = false ); | | bool bestGuessOnly = false, | |
| | | bool mayYield = false); | |
| int nPlans() const { return plans_.size(); } | | int nPlans() const { return plans_.size(); } | |
| shared_ptr< QueryOp > runOp( QueryOp &op ); | | shared_ptr< QueryOp > runOp( QueryOp &op ); | |
| template< class T > | | template< class T > | |
| shared_ptr< T > runOp( T &op ) { | | shared_ptr< T > runOp( T &op ) { | |
| return dynamic_pointer_cast< T >( runOp( static_cast< QueryOp&
>( op ) ) ); | | return dynamic_pointer_cast< T >( runOp( static_cast< QueryOp&
>( op ) ) ); | |
| } | | } | |
| BSONObj explain() const; | | BSONObj explain() const; | |
| bool usingPrerecordedPlan() const { return usingPrerecordedPlan_; } | | bool usingPrerecordedPlan() const { return usingPrerecordedPlan_; } | |
| PlanPtr getBestGuess() const; | | PlanPtr getBestGuess() const; | |
| //for testing | | //for testing | |
| | | | |
| skipping to change at line 203 | | skipping to change at line 208 | |
| void addPlan( PlanPtr plan, bool checkFirst ) { | | void addPlan( PlanPtr plan, bool checkFirst ) { | |
| if ( checkFirst && plan->indexKey().woCompare( plans_[ 0 ]->ind
exKey() ) == 0 ) | | if ( checkFirst && plan->indexKey().woCompare( plans_[ 0 ]->ind
exKey() ) == 0 ) | |
| return; | | return; | |
| plans_.push_back( plan ); | | plans_.push_back( plan ); | |
| } | | } | |
| void init(); | | void init(); | |
| void addHint( IndexDetails &id ); | | void addHint( IndexDetails &id ); | |
| struct Runner { | | struct Runner { | |
| Runner( QueryPlanSet &plans, QueryOp &op ); | | Runner( QueryPlanSet &plans, QueryOp &op ); | |
| shared_ptr< QueryOp > run(); | | shared_ptr< QueryOp > run(); | |
|
| | | void mayYield( const vector< shared_ptr< QueryOp > > &ops ); | |
| 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 void prepareToYield( QueryOp &op ); | |
| | | static void recoverFromYield( QueryOp &op ); | |
| }; | | }; | |
| const char *ns; | | const char *ns; | |
| BSONObj _originalQuery; | | BSONObj _originalQuery; | |
| auto_ptr< FieldRangeSet > fbs_; | | auto_ptr< FieldRangeSet > fbs_; | |
| PlanSet plans_; | | PlanSet plans_; | |
| bool mayRecordPlan_; | | bool mayRecordPlan_; | |
| bool usingPrerecordedPlan_; | | bool usingPrerecordedPlan_; | |
| BSONObj hint_; | | BSONObj hint_; | |
| BSONObj order_; | | BSONObj order_; | |
| long long oldNScanned_; | | long long oldNScanned_; | |
| bool honorRecordedPlan_; | | bool honorRecordedPlan_; | |
| BSONObj min_; | | BSONObj min_; | |
| BSONObj max_; | | BSONObj max_; | |
| string _special; | | string _special; | |
| bool _bestGuessOnly; | | bool _bestGuessOnly; | |
|
| | | bool _mayYield; | |
| | | ElapsedTracker _yieldSometimesTracker; | |
| }; | | }; | |
| | | | |
| // Handles $or type queries by generating a QueryPlanSet for each $or c
lause | | // Handles $or type queries by generating a QueryPlanSet for each $or c
lause | |
| // NOTE on our $or implementation: In our current qo implementation we
don't | | // NOTE on our $or implementation: In our current qo implementation we
don't | |
| // keep statistics on our data, but we can conceptualize the problem of | | // keep statistics on our data, but we can conceptualize the problem of | |
| // selecting an index when statistics exist for all index ranges. The | | // selecting an index when statistics exist for all index ranges. The | |
| // d-hitting set problem on k sets and n elements can be reduced to the | | // d-hitting set problem on k sets and n elements can be reduced to the | |
| // problem of index selection on k $or clauses and n index ranges (wher
e | | // problem of index selection on k $or clauses and n index ranges (wher
e | |
| // d is the max number of indexes, and the number of ranges n is unboun
ded). | | // d is the max number of indexes, and the number of ranges n is unboun
ded). | |
| // In light of the fact that d-hitting set is np complete, and we don't
even | | // In light of the fact that d-hitting set is np complete, and we don't
even | |
| | | | |
| skipping to change at line 244 | | skipping to change at line 254 | |
| // at a time and treat each as a separate query for index selection pur
poses. | | // at a time and treat each as a separate query for index selection pur
poses. | |
| // But if an index range is scanned for a particular $or clause, we eli
minate | | // But if an index range is scanned for a particular $or clause, we eli
minate | |
| // that range from all subsequent clauses. One could imagine an opposi
te | | // that range from all subsequent clauses. One could imagine an opposi
te | |
| // implementation where we select indexes based on the union of index r
anges | | // implementation where we select indexes based on the union of index r
anges | |
| // for all $or clauses, but this can have much poorer worst case behavi
or. | | // for all $or clauses, but this can have much poorer worst case behavi
or. | |
| // (An index range that suits one $or clause may not suit another, and
this | | // (An index range that suits one $or clause may not suit another, and
this | |
| // is worse than the typical case of index range choice staleness becau
se | | // is worse than the typical case of index range choice staleness becau
se | |
| // with $or the clauses may likely be logically distinct.) The greedy | | // with $or the clauses may likely be logically distinct.) The greedy | |
| // implementation won't do any worse than all the $or clauses individua
lly, | | // implementation won't do any worse than all the $or clauses individua
lly, | |
| // and it can often do better. In the first cut we are intentionally u
sing | | // and it can often do better. In the first cut we are intentionally u
sing | |
|
| // QueryPattern tracking to record successful plans on $or queries for | | // QueryPattern tracking to record successful plans on $or clauses for | |
| use by | | use by | |
| // subsequent $or queries, even though there may be a significant aggre | | // subsequent $or clauses, even though there may be a significant aggre | |
| gate | | gate | |
| // $nor component that would not be represented in QueryPattern. | | // $nor component that would not be represented in QueryPattern. | |
| class MultiPlanScanner { | | class MultiPlanScanner { | |
| public: | | public: | |
| MultiPlanScanner( const char *ns, | | MultiPlanScanner( const char *ns, | |
| const BSONObj &query, | | const BSONObj &query, | |
| const BSONObj &order, | | const BSONObj &order, | |
| const BSONElement *hint = 0, | | const BSONElement *hint = 0, | |
| bool honorRecordedPlan = true, | | bool honorRecordedPlan = true, | |
| const BSONObj &min = BSONObj(), | | const BSONObj &min = BSONObj(), | |
| const BSONObj &max = BSONObj(), | | const BSONObj &max = BSONObj(), | |
|
| bool bestGuessOnly = false ); | | bool bestGuessOnly = false, | |
| | | bool mayYield = false); | |
| shared_ptr< QueryOp > runOp( QueryOp &op ); | | shared_ptr< QueryOp > runOp( QueryOp &op ); | |
| template< class T > | | template< class T > | |
| shared_ptr< T > runOp( T &op ) { | | shared_ptr< T > runOp( T &op ) { | |
| return dynamic_pointer_cast< T >( runOp( static_cast< QueryOp&
>( op ) ) ); | | return dynamic_pointer_cast< T >( runOp( static_cast< QueryOp&
>( op ) ) ); | |
| } | | } | |
| shared_ptr< QueryOp > runOpOnce( QueryOp &op ); | | shared_ptr< QueryOp > runOpOnce( QueryOp &op ); | |
| template< class T > | | template< class T > | |
| shared_ptr< T > runOpOnce( T &op ) { | | shared_ptr< T > runOpOnce( T &op ) { | |
| return dynamic_pointer_cast< T >( runOpOnce( static_cast< Query
Op& >( op ) ) ); | | return dynamic_pointer_cast< T >( runOpOnce( static_cast< Query
Op& >( op ) ) ); | |
| } | | } | |
| bool mayRunMore() const { return _or ? !_fros.orFinished() : _i ==
0; } | | bool mayRunMore() const { return _or ? !_fros.orFinished() : _i ==
0; } | |
| BSONObj oldExplain() const { assertNotOr(); return _currentQps->exp
lain(); } | | BSONObj oldExplain() const { assertNotOr(); return _currentQps->exp
lain(); } | |
| // just report this when only one query op | | // just report this when only one query op | |
| bool usingPrerecordedPlan() const { | | bool usingPrerecordedPlan() const { | |
| return !_or && _currentQps->usingPrerecordedPlan(); | | return !_or && _currentQps->usingPrerecordedPlan(); | |
| } | | } | |
| void setBestGuessOnly() { _bestGuessOnly = true; } | | void setBestGuessOnly() { _bestGuessOnly = true; } | |
|
| | | void mayYield( bool val ) { _mayYield = val; } | |
| private: | | private: | |
| void assertNotOr() const { | | void assertNotOr() const { | |
| massert( 13266, "not implemented for $or query", !_or ); | | massert( 13266, "not implemented for $or query", !_or ); | |
| } | | } | |
| bool uselessOr( const BSONElement &hint ) const; | | bool uselessOr( const BSONElement &hint ) const; | |
| const char * _ns; | | const char * _ns; | |
| bool _or; | | bool _or; | |
| BSONObj _query; | | BSONObj _query; | |
| FieldRangeOrSet _fros; | | FieldRangeOrSet _fros; | |
| auto_ptr< QueryPlanSet > _currentQps; | | auto_ptr< QueryPlanSet > _currentQps; | |
| int _i; | | int _i; | |
| bool _honorRecordedPlan; | | bool _honorRecordedPlan; | |
| bool _bestGuessOnly; | | bool _bestGuessOnly; | |
| BSONObj _hint; | | BSONObj _hint; | |
|
| | | bool _mayYield; | |
| }; | | }; | |
| | | | |
| class MultiCursor : public Cursor { | | class MultiCursor : public Cursor { | |
| public: | | public: | |
| class CursorOp : public QueryOp { | | class CursorOp : public QueryOp { | |
| public: | | public: | |
| CursorOp() {} | | CursorOp() {} | |
| CursorOp( const QueryOp &other ) : QueryOp( other ) {} | | CursorOp( const QueryOp &other ) : QueryOp( other ) {} | |
| virtual shared_ptr< Cursor > newCursor() const = 0; | | virtual shared_ptr< Cursor > newCursor() const = 0; | |
| }; | | }; | |
| // takes ownership of 'op' | | // takes ownership of 'op' | |
|
| MultiCursor( const char *ns, const BSONObj &pattern, const BSONObj | | MultiCursor( const char *ns, const BSONObj &pattern, const BSONObj | |
| &order, shared_ptr< CursorOp > op = shared_ptr< CursorOp >() ) | | &order, shared_ptr< CursorOp > op = shared_ptr< CursorOp >(), bool mayYield | |
| : _mps( new MultiPlanScanner( ns, pattern, order, 0, true, BSONObj( | | = false ) | |
| ), BSONObj(), !op.get() ) ) { | | : _mps( new MultiPlanScanner( ns, pattern, order, 0, true, BSONObj( | |
| | | ), BSONObj(), !op.get(), mayYield ) ) { | |
| if ( op.get() ) { | | if ( op.get() ) { | |
| _op = op; | | _op = op; | |
| } else { | | } else { | |
| _op.reset( new NoOp() ); | | _op.reset( new NoOp() ); | |
| } | | } | |
| if ( _mps->mayRunMore() ) { | | if ( _mps->mayRunMore() ) { | |
| nextClause(); | | nextClause(); | |
| if ( !ok() ) { | | if ( !ok() ) { | |
| advance(); | | advance(); | |
| } | | } | |
| } else { | | } else { | |
| _c.reset( new BasicCursor( DiskLoc() ) ); | | _c.reset( new BasicCursor( DiskLoc() ) ); | |
| } | | } | |
| } | | } | |
| // used to handoff a query to a getMore() | | // used to handoff a query to a getMore() | |
| MultiCursor( auto_ptr< MultiPlanScanner > mps, const shared_ptr< Cu
rsor > &c, const shared_ptr< CoveredIndexMatcher > &matcher, const QueryOp
&op ) | | MultiCursor( auto_ptr< MultiPlanScanner > mps, const shared_ptr< Cu
rsor > &c, const shared_ptr< CoveredIndexMatcher > &matcher, const QueryOp
&op ) | |
| : _op( new NoOp( op ) ), _c( c ), _mps( mps ), _matcher( matcher )
{ | | : _op( new NoOp( op ) ), _c( c ), _mps( mps ), _matcher( matcher )
{ | |
| _mps->setBestGuessOnly(); | | _mps->setBestGuessOnly(); | |
|
| | | _mps->mayYield( false ); // with a NoOp, there's no need to yie
ld in QueryPlanSet | |
| if ( !ok() ) { | | if ( !ok() ) { | |
| // would have been advanced by UserQueryOp if possible | | // would have been advanced by UserQueryOp if possible | |
| advance(); | | advance(); | |
| } | | } | |
| } | | } | |
| virtual bool ok() { return _c->ok(); } | | virtual bool ok() { return _c->ok(); } | |
| virtual Record* _current() { return _c->_current(); } | | virtual Record* _current() { return _c->_current(); } | |
| virtual BSONObj current() { return _c->current(); } | | virtual BSONObj current() { return _c->current(); } | |
| virtual DiskLoc currLoc() { return _c->currLoc(); } | | virtual DiskLoc currLoc() { return _c->currLoc(); } | |
| virtual bool advance() { | | virtual bool advance() { | |
| | | | |
End of changes. 22 change blocks. |
| 22 lines changed or deleted | | 39 lines changed or added | |
|
| queryutil.h | | queryutil.h | |
| | | | |
| skipping to change at line 35 | | skipping to change at line 35 | |
| 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; } | |
| }; | | }; | |
| | | | |
| struct FieldInterval { | | struct FieldInterval { | |
|
| FieldInterval(){} | | FieldInterval() : _cachedEquality( -1 ) {} | |
| FieldInterval( const BSONElement& e ){ | | 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; | |
|
| bool valid() const { | | bool strictValid() const { | |
| int cmp = _lower._bound.woCompare( _upper._bound, false ); | | int cmp = _lower._bound.woCompare( _upper._bound, false ); | |
| return ( cmp < 0 || ( cmp == 0 && _lower._inclusive && _upper._
inclusive ) ); | | return ( cmp < 0 || ( cmp == 0 && _lower._inclusive && _upper._
inclusive ) ); | |
| } | | } | |
|
| bool equality() const { return _lower._inclusive && _upper._inclusi | | bool equality() const { | |
| ve && _lower._bound.woCompare( _upper._bound, false ) == 0; } | | if ( _cachedEquality == -1 ) { | |
| | | _cachedEquality = ( _lower._inclusive && _upper._inclusive | |
| | | && _lower._bound.woCompare( _upper._bound, false ) == 0 ); | |
| | | } | |
| | | return _cachedEquality; | |
| | | } | |
| | | mutable int _cachedEquality; | |
| }; | | }; | |
| | | | |
| // range of a field's value that may be determined from query -- used t
o | | // range of a field's value that may be determined from query -- used t
o | |
| // determine index limits | | // determine index limits | |
| class FieldRange { | | class FieldRange { | |
| public: | | public: | |
| FieldRange( const BSONElement &e = BSONObj().firstElement() , bool
isNot=false , bool optimize=true ); | | FieldRange( const BSONElement &e = BSONObj().firstElement() , bool
isNot=false , bool optimize=true ); | |
| const FieldRange &operator&=( const FieldRange &other ); | | const FieldRange &operator&=( const FieldRange &other ); | |
| const FieldRange &operator|=( const FieldRange &other ); | | const FieldRange &operator|=( const FieldRange &other ); | |
| // does not remove fully contained ranges (eg [1,3] - [2,2] doesn't
remove anything) | | // does not remove fully contained ranges (eg [1,3] - [2,2] doesn't
remove anything) | |
| | | | |
| skipping to change at line 96 | | skipping to change at line 102 | |
| } | | } | |
| bool empty() const { return _intervals.empty(); } | | bool empty() const { return _intervals.empty(); } | |
| const vector< FieldInterval > &intervals() const { return _i
ntervals; } | | const vector< FieldInterval > &intervals() const { return _i
ntervals; } | |
| string getSpecial() const { return _special; } | | string getSpecial() const { return _special; } | |
| void setExclusiveBounds() { | | void setExclusiveBounds() { | |
| for( vector< FieldInterval >::iterator i = _intervals.begin();
i != _intervals.end(); ++i ) { | | for( vector< FieldInterval >::iterator i = _intervals.begin();
i != _intervals.end(); ++i ) { | |
| i->_lower._inclusive = false; | | i->_lower._inclusive = false; | |
| i->_upper._inclusive = false; | | i->_upper._inclusive = false; | |
| } | | } | |
| } | | } | |
|
| // reconstructs $in, regex, inequality matches | | // constructs a range which is the reverse of the current one | |
| // this is a hack - we should submit FieldRange directly to a Match | | // note - the resulting intervals may not be strictValid() | |
| er instead | | void reverse( FieldRange &ret ) const { | |
| BSONObj simplifiedComplex() const; | | assert( _special.empty() ); | |
| | | ret._intervals.clear(); | |
| | | ret._objData = _objData; | |
| | | for( vector< FieldInterval >::const_reverse_iterator i = _inter | |
| | | vals.rbegin(); i != _intervals.rend(); ++i ) { | |
| | | FieldInterval fi; | |
| | | fi._lower = i->_upper; | |
| | | fi._upper = i->_lower; | |
| | | ret._intervals.push_back( fi ); | |
| | | } | |
| | | } | |
| private: | | private: | |
| BSONObj addObj( const BSONObj &o ); | | BSONObj addObj( const BSONObj &o ); | |
| void finishOperation( const vector< FieldInterval > &newIntervals,
const FieldRange &other ); | | void finishOperation( const vector< FieldInterval > &newIntervals,
const FieldRange &other ); | |
| vector< FieldInterval > _intervals; | | vector< FieldInterval > _intervals; | |
| vector< BSONObj > _objData; | | vector< BSONObj > _objData; | |
| string _special; | | string _special; | |
| }; | | }; | |
| | | | |
| // implements query pattern matching, used to determine if a query is | | // implements query pattern matching, used to determine if a query is | |
| // similar to an earlier query and should use the same plan | | // similar to an earlier query and should use the same plan | |
| | | | |
| skipping to change at line 184 | | skipping to change at line 200 | |
| // the specified direction of traversal. For example, given a simple i
ndex {i:1} | | // the specified direction of traversal. For example, given a simple i
ndex {i:1} | |
| // and direction +1, one valid BoundList is: (1, 2); (4, 6). The same
BoundList | | // and direction +1, one valid BoundList is: (1, 2); (4, 6). The same
BoundList | |
| // would be valid for index {i:-1} with direction -1. | | // would be valid for index {i:-1} with direction -1. | |
| typedef vector< pair< BSONObj, BSONObj > > BoundList; | | typedef vector< pair< BSONObj, BSONObj > > BoundList; | |
| | | | |
| // ranges of fields' value that may be determined from query -- used to | | // ranges of fields' value that may be determined from query -- used to | |
| // determine index limits | | // determine index limits | |
| class FieldRangeSet { | | class FieldRangeSet { | |
| public: | | public: | |
| friend class FieldRangeOrSet; | | friend class FieldRangeOrSet; | |
|
| | | friend class FieldRangeVector; | |
| FieldRangeSet( const char *ns, const BSONObj &query , bool optimize
=true ); | | FieldRangeSet( const char *ns, const BSONObj &query , bool optimize
=true ); | |
| bool hasRange( const char *fieldName ) const { | | bool hasRange( const char *fieldName ) const { | |
| map< string, FieldRange >::const_iterator f = _ranges.find( fie
ldName ); | | map< string, FieldRange >::const_iterator f = _ranges.find( fie
ldName ); | |
| return f != _ranges.end(); | | return f != _ranges.end(); | |
| } | | } | |
| const FieldRange &range( const char *fieldName ) const { | | const FieldRange &range( const char *fieldName ) const { | |
| map< string, FieldRange >::const_iterator f = _ranges.find( fie
ldName ); | | map< string, FieldRange >::const_iterator f = _ranges.find( fie
ldName ); | |
| if ( f == _ranges.end() ) | | if ( f == _ranges.end() ) | |
| return trivialRange(); | | return trivialRange(); | |
| return f->second; | | return f->second; | |
| | | | |
| skipping to change at line 210 | | skipping to change at line 227 | |
| } | | } | |
| int nNontrivialRanges() const { | | int nNontrivialRanges() const { | |
| int count = 0; | | int count = 0; | |
| for( map< string, FieldRange >::const_iterator i = _ranges.begi
n(); i != _ranges.end(); ++i ) | | for( map< string, FieldRange >::const_iterator i = _ranges.begi
n(); i != _ranges.end(); ++i ) | |
| if ( i->second.nontrivial() ) | | if ( i->second.nontrivial() ) | |
| ++count; | | ++count; | |
| return count; | | return count; | |
| } | | } | |
| const char *ns() const { return _ns; } | | const char *ns() const { return _ns; } | |
| // if fields is specified, order fields of returned object to match
those of 'fields' | | // if fields is specified, order fields of returned object to match
those of 'fields' | |
|
| BSONObj simplifiedQuery( const BSONObj &fields = BSONObj(), bool ex
pandIn = false ) const; | | BSONObj simplifiedQuery( const BSONObj &fields = BSONObj() ) const; | |
| bool matchPossible() const { | | bool matchPossible() const { | |
| for( map< string, FieldRange >::const_iterator i = _ranges.begi
n(); i != _ranges.end(); ++i ) | | for( map< string, FieldRange >::const_iterator i = _ranges.begi
n(); i != _ranges.end(); ++i ) | |
| if ( i->second.empty() ) | | if ( i->second.empty() ) | |
| return false; | | return false; | |
| return true; | | return true; | |
| } | | } | |
| QueryPattern pattern( const BSONObj &sort = BSONObj() ) const; | | QueryPattern pattern( const BSONObj &sort = BSONObj() ) const; | |
|
| BoundList indexBounds( const BSONObj &keyPattern, int direction ) c
onst; | | | |
| string getSpecial() const; | | string getSpecial() const; | |
| const FieldRangeSet &operator-=( const FieldRangeSet &other ) { | | const FieldRangeSet &operator-=( const FieldRangeSet &other ) { | |
| map< string, FieldRange >::iterator i = _ranges.begin(); | | map< string, FieldRange >::iterator i = _ranges.begin(); | |
| map< string, FieldRange >::const_iterator j = other._ranges.beg
in(); | | map< string, FieldRange >::const_iterator j = other._ranges.beg
in(); | |
| while( i != _ranges.end() && j != other._ranges.end() ) { | | while( i != _ranges.end() && j != other._ranges.end() ) { | |
| int cmp = i->first.compare( j->first ); | | int cmp = i->first.compare( j->first ); | |
| if ( cmp == 0 ) { | | if ( cmp == 0 ) { | |
| i->second -= j->second; | | i->second -= j->second; | |
| ++i; | | ++i; | |
| ++j; | | ++j; | |
| } else if ( cmp < 0 ) { | | } else if ( cmp < 0 ) { | |
| ++i; | | ++i; | |
| } else { | | } else { | |
| ++j; | | ++j; | |
| } | | } | |
| } | | } | |
|
| | | appendQueries( other ); | |
| return *this; | | return *this; | |
| } | | } | |
| const FieldRangeSet &operator&=( const FieldRangeSet &other ) { | | const FieldRangeSet &operator&=( const FieldRangeSet &other ) { | |
| map< string, FieldRange >::iterator i = _ranges.begin(); | | map< string, FieldRange >::iterator i = _ranges.begin(); | |
| map< string, FieldRange >::const_iterator j = other._ranges.beg
in(); | | map< string, FieldRange >::const_iterator j = other._ranges.beg
in(); | |
| while( i != _ranges.end() && j != other._ranges.end() ) { | | while( i != _ranges.end() && j != other._ranges.end() ) { | |
| int cmp = i->first.compare( j->first ); | | int cmp = i->first.compare( j->first ); | |
| if ( cmp == 0 ) { | | if ( cmp == 0 ) { | |
| i->second &= j->second; | | i->second &= j->second; | |
| ++i; | | ++i; | |
| | | | |
| skipping to change at line 257 | | skipping to change at line 274 | |
| ++i; | | ++i; | |
| } else { | | } else { | |
| _ranges[ j->first ] = j->second; | | _ranges[ j->first ] = j->second; | |
| ++j; | | ++j; | |
| } | | } | |
| } | | } | |
| while( j != other._ranges.end() ) { | | while( j != other._ranges.end() ) { | |
| _ranges[ j->first ] = j->second; | | _ranges[ j->first ] = j->second; | |
| ++j; | | ++j; | |
| } | | } | |
|
| | | appendQueries( other ); | |
| return *this; | | return *this; | |
| } | | } | |
|
| | | // TODO get rid of this | |
| | | BoundList indexBounds( const BSONObj &keyPattern, int direction ) c | |
| | | onst; | |
| private: | | private: | |
|
| | | void appendQueries( const FieldRangeSet &other ) { | |
| | | for( vector< BSONObj >::const_iterator i = other._queries.begin | |
| | | (); i != other._queries.end(); ++i ) { | |
| | | _queries.push_back( *i ); | |
| | | } | |
| | | } | |
| void processQueryField( const BSONElement &e, bool optimize ); | | void processQueryField( const BSONElement &e, bool optimize ); | |
| void processOpElement( const char *fieldName, const BSONElement &f,
bool isNot, bool optimize ); | | void processOpElement( const char *fieldName, const BSONElement &f,
bool isNot, bool optimize ); | |
| static FieldRange *trivialRange_; | | static FieldRange *trivialRange_; | |
| static FieldRange &trivialRange(); | | static FieldRange &trivialRange(); | |
| mutable map< string, FieldRange > _ranges; | | mutable map< string, FieldRange > _ranges; | |
| const char *_ns; | | const char *_ns; | |
| // make sure memory for FieldRange BSONElements is owned | | // make sure memory for FieldRange BSONElements is owned | |
|
| BSONObj _query; | | vector< BSONObj > _queries; | |
| | | }; | |
| | | | |
| | | class FieldRangeVector { | |
| | | public: | |
| | | FieldRangeVector( const FieldRangeSet &frs, const BSONObj &keyPatte | |
| | | rn, int direction ) | |
| | | :_keyPattern( keyPattern ), _direction( direction >= 0 ? 1 : -1 ) | |
| | | { | |
| | | _queries = frs._queries; | |
| | | BSONObjIterator i( _keyPattern ); | |
| | | while( i.more() ) { | |
| | | BSONElement e = i.next(); | |
| | | int number = (int) e.number(); // returns 0.0 if not numeri | |
| | | c | |
| | | bool forward = ( ( number >= 0 ? 1 : -1 ) * ( direction >= | |
| | | 0 ? 1 : -1 ) > 0 ); | |
| | | if ( forward ) { | |
| | | _ranges.push_back( frs.range( e.fieldName() ) ); | |
| | | } else { | |
| | | _ranges.push_back( FieldRange() ); | |
| | | frs.range( e.fieldName() ).reverse( _ranges.back() ); | |
| | | } | |
| | | assert( !_ranges.back().empty() ); | |
| | | } | |
| | | uassert( 13385, "combinatorial limit of $in partitioning of res | |
| | | ult set exceeded", size() < 1000000 ); | |
| | | } | |
| | | long long size() { | |
| | | long long ret = 1; | |
| | | for( vector< FieldRange >::const_iterator i = _ranges.begin(); | |
| | | i != _ranges.end(); ++i ) { | |
| | | ret *= i->intervals().size(); | |
| | | } | |
| | | return ret; | |
| | | } | |
| | | BSONObj startKey() const { | |
| | | BSONObjBuilder b; | |
| | | for( vector< FieldRange >::const_iterator i = _ranges.begin(); | |
| | | i != _ranges.end(); ++i ) { | |
| | | const FieldInterval &fi = i->intervals().front(); | |
| | | b.appendAs( fi._lower._bound, "" ); | |
| | | } | |
| | | return b.obj(); | |
| | | } | |
| | | BSONObj endKey() const { | |
| | | BSONObjBuilder b; | |
| | | for( vector< FieldRange >::const_iterator i = _ranges.begin(); | |
| | | i != _ranges.end(); ++i ) { | |
| | | const FieldInterval &fi = i->intervals().back(); | |
| | | b.appendAs( fi._upper._bound, "" ); | |
| | | } | |
| | | return b.obj(); | |
| | | } | |
| | | BSONObj obj() const { | |
| | | BSONObjBuilder b; | |
| | | BSONObjIterator k( _keyPattern ); | |
| | | for( int i = 0; i < (int)_ranges.size(); ++i ) { | |
| | | BSONArrayBuilder a( b.subarrayStart( k.next().fieldName() ) | |
| | | ); | |
| | | for( vector< FieldInterval >::const_iterator j = _ranges[ i | |
| | | ].intervals().begin(); | |
| | | j != _ranges[ i ].intervals().end(); ++j ) { | |
| | | a << BSONArray( BSON_ARRAY( j->_lower._bound << j->_upp | |
| | | er._bound ).clientReadable() ); | |
| | | } | |
| | | a.done(); | |
| | | } | |
| | | return b.obj(); | |
| | | } | |
| | | bool matches( const BSONObj &obj ) const; | |
| | | class Iterator { | |
| | | public: | |
| | | Iterator( const FieldRangeVector &v ) : _v( v ), _i( _v._ranges | |
| | | .size(), -1 ), _cmp( _v._ranges.size(), 0 ), _superlative( _v._ranges.size( | |
| | | ), 0 ) { | |
| | | static BSONObj minObj = minObject(); | |
| | | static BSONElement minElt = minObj.firstElement(); | |
| | | static BSONObj maxObj = maxObject(); | |
| | | static BSONElement maxElt = maxObj.firstElement(); | |
| | | BSONObjIterator i( _v._keyPattern ); | |
| | | for( int j = 0; j < (int)_superlative.size(); ++j ) { | |
| | | int number = (int) i.next().number(); | |
| | | bool forward = ( ( number >= 0 ? 1 : -1 ) * ( _v._direc | |
| | | tion >= 0 ? 1 : -1 ) > 0 ); | |
| | | _superlative[ j ] = forward ? &maxElt : &minElt; | |
| | | } | |
| | | } | |
| | | static BSONObj minObject() { | |
| | | BSONObjBuilder b; | |
| | | b.appendMinKey( "" ); | |
| | | return b.obj(); | |
| | | } | |
| | | static BSONObj maxObject() { | |
| | | BSONObjBuilder b; | |
| | | b.appendMaxKey( "" ); | |
| | | return b.obj(); | |
| | | } | |
| | | bool advance() { | |
| | | int i = _i.size() - 1; | |
| | | while( i >= 0 && _i[ i ] >= ( (int)_v._ranges[ i ].interval | |
| | | s().size() - 1 ) ) { | |
| | | --i; | |
| | | } | |
| | | if( i >= 0 ) { | |
| | | _i[ i ]++; | |
| | | for( unsigned j = i + 1; j < _i.size(); ++j ) { | |
| | | _i[ j ] = 0; | |
| | | } | |
| | | } else { | |
| | | _i[ 0 ] = _v._ranges[ 0 ].intervals().size(); | |
| | | } | |
| | | return ok(); | |
| | | } | |
| | | // return value | |
| | | // -2 end of iteration | |
| | | // -1 no skipping | |
| | | // >= 0 skip parameter | |
| | | int advance( const BSONObj &curr ); | |
| | | const vector< const BSONElement * > &cmp() const { return _cmp; | |
| | | } | |
| | | void setZero( int i ) { | |
| | | for( int j = i; j < (int)_i.size(); ++j ) { | |
| | | _i[ j ] = 0; | |
| | | } | |
| | | } | |
| | | void setMinus( int i ) { | |
| | | for( int j = i; j < (int)_i.size(); ++j ) { | |
| | | _i[ j ] = -1; | |
| | | } | |
| | | } | |
| | | bool ok() { | |
| | | return _i[ 0 ] < (int)_v._ranges[ 0 ].intervals().size(); | |
| | | } | |
| | | BSONObj startKey() { | |
| | | BSONObjBuilder b; | |
| | | for( int unsigned i = 0; i < _i.size(); ++i ) { | |
| | | const FieldInterval &fi = _v._ranges[ i ].intervals()[ | |
| | | _i[ i ] ]; | |
| | | b.appendAs( fi._lower._bound, "" ); | |
| | | } | |
| | | return b.obj(); | |
| | | } | |
| | | // temp | |
| | | BSONObj endKey() { | |
| | | BSONObjBuilder b; | |
| | | for( int unsigned i = 0; i < _i.size(); ++i ) { | |
| | | const FieldInterval &fi = _v._ranges[ i ].intervals()[ | |
| | | _i[ i ] ]; | |
| | | b.appendAs( fi._upper._bound, "" ); | |
| | | } | |
| | | return b.obj(); | |
| | | } | |
| | | // check | |
| | | private: | |
| | | const FieldRangeVector &_v; | |
| | | vector< int > _i; | |
| | | vector< const BSONElement* > _cmp; | |
| | | vector< const BSONElement* > _superlative; | |
| | | }; | |
| | | private: | |
| | | int matchingLowElement( const BSONElement &e, int i, bool direction | |
| | | ) const; | |
| | | bool matchesElement( const BSONElement &e, int i, bool direction ) | |
| | | const; | |
| | | vector< FieldRange > _ranges; | |
| | | BSONObj _keyPattern; | |
| | | int _direction; | |
| | | vector< BSONObj > _queries; // make sure mem owned | |
| }; | | }; | |
| | | | |
| // generages FieldRangeSet objects, accounting for or clauses | | // generages FieldRangeSet objects, accounting for or clauses | |
| class FieldRangeOrSet { | | class FieldRangeOrSet { | |
| public: | | public: | |
| FieldRangeOrSet( const char *ns, const BSONObj &query , bool optimi
ze=true ); | | FieldRangeOrSet( const char *ns, const BSONObj &query , bool optimi
ze=true ); | |
| // if there's a useless or clause, we won't use or ranges to help w
ith scanning | | // if there's a useless or clause, we won't use or ranges to help w
ith scanning | |
| bool orFinished() const { return _orFound && _orSets.empty(); } | | bool orFinished() const { return _orFound && _orSets.empty(); } | |
| // removes first or clause, and removes the field ranges it covers
from all subsequent or clauses | | // removes first or clause, and removes the field ranges it covers
from all subsequent or clauses | |
| // this could invalidate the result of the last topFrs() | | // this could invalidate the result of the last topFrs() | |
| | | | |
| skipping to change at line 312 | | skipping to change at line 486 | |
| _oldOrSets.push_front( toPop ); | | _oldOrSets.push_front( toPop ); | |
| _orSets.pop_front(); | | _orSets.pop_front(); | |
| } | | } | |
| FieldRangeSet *topFrs() const { | | FieldRangeSet *topFrs() const { | |
| FieldRangeSet *ret = new FieldRangeSet( _baseSet ); | | FieldRangeSet *ret = new FieldRangeSet( _baseSet ); | |
| *ret &= _orSets.front(); | | *ret &= _orSets.front(); | |
| return ret; | | return ret; | |
| } | | } | |
| void allClausesSimplified( vector< BSONObj > &ret ) const { | | void allClausesSimplified( vector< BSONObj > &ret ) const { | |
| for( list< FieldRangeSet >::const_iterator i = _orSets.begin();
i != _orSets.end(); ++i ) { | | for( list< FieldRangeSet >::const_iterator i = _orSets.begin();
i != _orSets.end(); ++i ) { | |
|
| ret.push_back( i->simplifiedQuery() ); | | if ( i->matchPossible() ) { | |
| | | ret.push_back( i->simplifiedQuery() ); | |
| | | } | |
| } | | } | |
| } | | } | |
| string getSpecial() const { return _baseSet.getSpecial(); } | | string getSpecial() const { return _baseSet.getSpecial(); } | |
| private: | | private: | |
| FieldRangeSet _baseSet; | | FieldRangeSet _baseSet; | |
| list< FieldRangeSet > _orSets; | | list< FieldRangeSet > _orSets; | |
| list< FieldRangeSet > _oldOrSets; // make sure memory is owned | | list< FieldRangeSet > _oldOrSets; // make sure memory is owned | |
| bool _orFound; | | bool _orFound; | |
| }; | | }; | |
| | | | |
| | | | |
End of changes. 13 change blocks. |
| 13 lines changed or deleted | | 210 lines changed or added | |
|
| repl.h | | repl.h | |
| | | | |
| skipping to change at line 35 | | skipping to change at line 35 | |
| local.oplog.$<source> | | local.oplog.$<source> | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include "pdfile.h" | | #include "pdfile.h" | |
| #include "db.h" | | #include "db.h" | |
| #include "dbhelpers.h" | | #include "dbhelpers.h" | |
| #include "query.h" | | #include "query.h" | |
| #include "queryoptimizer.h" | | #include "queryoptimizer.h" | |
|
| | | | |
| #include "../client/dbclient.h" | | #include "../client/dbclient.h" | |
|
| | | | |
| #include "../util/optime.h" | | #include "../util/optime.h" | |
|
| | | | |
| #include "oplog.h" | | #include "oplog.h" | |
|
| | | #include "../util/concurrency/thread_pool.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| /* replication slave? (possibly with slave or repl pair nonmaster) | | /* replication slave? (possibly with slave or repl pair nonmaster) | |
| --slave cmd line setting -> SimpleSlave | | --slave cmd line setting -> SimpleSlave | |
| */ | | */ | |
| typedef enum { NotSlave=0, SimpleSlave, ReplPairSlave } SlaveTypes; | | typedef enum { NotSlave=0, SimpleSlave, ReplPairSlave } SlaveTypes; | |
| | | | |
| class ReplSettings { | | class ReplSettings { | |
| public: | | public: | |
| | | | |
| skipping to change at line 83 | | skipping to change at line 81 | |
| | | | |
| bool cloneFrom(const char *masterHost, string& errmsg, const string& fr
omdb, bool logForReplication, | | bool cloneFrom(const char *masterHost, string& errmsg, const string& fr
omdb, bool logForReplication, | |
| bool slaveOk, bool useReplAuth, bool snap
shot); | | bool slaveOk, bool useReplAuth, bool snap
shot); | |
| | | | |
| /* A replication exception */ | | /* A replication exception */ | |
| class SyncException : public DBException { | | class SyncException : public DBException { | |
| public: | | public: | |
| SyncException() : DBException( "sync exception" , 10001 ){} | | SyncException() : DBException( "sync exception" , 10001 ){} | |
| }; | | }; | |
| | | | |
|
| | | /* started abstracting out the querying of the primary/master's oplog | |
| | | still fairly awkward but a start. | |
| | | */ | |
| | | class OplogReader { | |
| | | auto_ptr<DBClientConnection> _conn; | |
| | | auto_ptr<DBClientCursor> cursor; | |
| | | public: | |
| | | | |
| | | void reset() { | |
| | | cursor.reset(); | |
| | | } | |
| | | void resetConnection() { | |
| | | cursor.reset(); | |
| | | _conn.reset(); | |
| | | } | |
| | | DBClientConnection* conn() { return _conn.get(); } | |
| | | BSONObj findOne(const char *ns, Query& q) { | |
| | | return conn()->findOne(ns, q); | |
| | | } | |
| | | | |
| | | /* ok to call if already connected */ | |
| | | bool connect(string hostname); | |
| | | | |
| | | void getReady() { | |
| | | if( cursor.get() && cursor->isDead() ) { | |
| | | log() << "repl: old cursor isDead, initiating a new one" << | |
| | | endl; | |
| | | reset(); | |
| | | } | |
| | | } | |
| | | | |
| | | bool haveCursor() { return cursor.get() != 0; } | |
| | | | |
| | | void tailingQuery(const char *ns, BSONObj& query) { | |
| | | assert( !haveCursor() ); | |
| | | log(2) << "repl: " << ns << ".find(" << query.toString() << ')' | |
| | | << endl; | |
| | | cursor = _conn->query( ns, query, 0, 0, 0, | |
| | | QueryOption_CursorTailable | QueryOption_ | |
| | | SlaveOk | QueryOption_OplogReplay | | |
| | | QueryOption_AwaitData | |
| | | ); | |
| | | } | |
| | | | |
| | | bool more() { | |
| | | assert( cursor.get() ); | |
| | | return cursor->more(); | |
| | | } | |
| | | | |
| | | /* old mongod's can't do the await flag... */ | |
| | | bool awaitCapable() { | |
| | | return cursor->hasResultFlag(QueryResult::ResultFlag_AwaitCapab | |
| | | le); | |
| | | } | |
| | | | |
| | | void peek(vector<BSONObj>& v, int n) { | |
| | | if( cursor.get() ) | |
| | | cursor->peek(v,n); | |
| | | } | |
| | | | |
| | | BSONObj next() { | |
| | | return cursor->next(); | |
| | | } | |
| | | | |
| | | void putBack(BSONObj op) { | |
| | | cursor->putBack(op); | |
| | | } | |
| | | }; | |
| | | | |
| /* A Source is a source from which we can pull (replicate) data. | | /* A Source is a source from which we can pull (replicate) data. | |
| stored in collection local.sources. | | stored in collection local.sources. | |
| | | | |
| Can be a group of things to replicate for several databases. | | Can be a group of things to replicate for several databases. | |
| | | | |
| { host: ..., source: ..., only: ..., syncedTo: ..., localLogTs: .
.., dbsNextPass: { ... }, incompleteCloneDbs: { ... } } | | { host: ..., source: ..., only: ..., syncedTo: ..., localLogTs: .
.., dbsNextPass: { ... }, incompleteCloneDbs: { ... } } | |
| | | | |
| 'source' defaults to 'main'; support for multiple source names is | | 'source' defaults to 'main'; support for multiple source names is | |
| not done (always use main for now). | | not done (always use main for now). | |
| */ | | */ | |
| class ReplSource { | | class ReplSource { | |
|
| | | auto_ptr<ThreadPool> tp; | |
| | | | |
| bool resync(string db); | | bool resync(string db); | |
| | | | |
| /* pull some operations from the master's oplog, and apply them. */ | | /* pull some operations from the master's oplog, and apply them. */ | |
| int sync_pullOpLog(int& nApplied); | | int sync_pullOpLog(int& nApplied); | |
| | | | |
| void sync_pullOpLog_applyOperation(BSONObj& op, OpTime *localLogTai
l); | | void sync_pullOpLog_applyOperation(BSONObj& op, OpTime *localLogTai
l); | |
| | | | |
|
| auto_ptr<DBClientConnection> conn; | | | |
| auto_ptr<DBClientCursor> cursor; | | | |
| | | | |
| /* we only clone one database per pass, even if a lot need done. T
his helps us | | /* we only clone one database per pass, even if a lot need done. T
his helps us | |
| avoid overflowing the master's transaction log by doing too much
work before going | | avoid overflowing the master's transaction log by doing too much
work before going | |
| back to read more transactions. (Imagine a scenario of slave sta
rtup where we try to | | back to read more transactions. (Imagine a scenario of slave sta
rtup where we try to | |
| clone 100 databases in one pass.) | | clone 100 databases in one pass.) | |
| */ | | */ | |
| set<string> addDbNextPass; | | set<string> addDbNextPass; | |
| | | | |
| set<string> incompleteCloneDbs; | | set<string> incompleteCloneDbs; | |
| | | | |
| ReplSource(); | | ReplSource(); | |
| | | | |
| // returns the dummy ns used to do the drop | | // returns the dummy ns used to do the drop | |
| string resyncDrop( const char *db, const char *requester ); | | string resyncDrop( const char *db, const char *requester ); | |
|
| // returns true if connected on return | | | |
| bool connect(); | | | |
| // returns possibly unowned id spec for the operation. | | // returns possibly unowned id spec for the operation. | |
| static BSONObj idForOp( const BSONObj &op, bool &mod ); | | static BSONObj idForOp( const BSONObj &op, bool &mod ); | |
| static void updateSetsWithOp( const BSONObj &op, bool mayUpdateStor
age ); | | static void updateSetsWithOp( const BSONObj &op, bool mayUpdateStor
age ); | |
| // call without the db mutex | | // call without the db mutex | |
| void syncToTailOfRemoteLog(); | | void syncToTailOfRemoteLog(); | |
| // call with the db mutex | | // call with the db mutex | |
| OpTime nextLastSavedLocalTs() const; | | OpTime nextLastSavedLocalTs() const; | |
| void setLastSavedLocalTs( const OpTime &nextLocalTs ); | | void setLastSavedLocalTs( const OpTime &nextLocalTs ); | |
| // call without the db mutex | | // call without the db mutex | |
| void resetSlave(); | | void resetSlave(); | |
| // call with the db mutex | | // call with the db mutex | |
| // returns false if the slave has been reset | | // returns false if the slave has been reset | |
| bool updateSetsWithLocalOps( OpTime &localLogTail, bool mayUnlock )
; | | bool updateSetsWithLocalOps( OpTime &localLogTail, bool mayUnlock )
; | |
| string ns() const { return string( "local.oplog.$" ) + sourceName()
; } | | string ns() const { return string( "local.oplog.$" ) + sourceName()
; } | |
| unsigned _sleepAdviceTime; | | unsigned _sleepAdviceTime; | |
| | | | |
| public: | | public: | |
|
| | | OplogReader oplogReader; | |
| | | | |
| static void applyOperation(const BSONObj& op); | | static void applyOperation(const BSONObj& op); | |
| bool replacing; // in "replace mode" -- see CmdReplacePeer | | bool replacing; // in "replace mode" -- see CmdReplacePeer | |
| bool paired; // --pair in use | | bool paired; // --pair in use | |
| string hostName; // ip addr or hostname plus optionally, ":<port
>" | | string hostName; // ip addr or hostname plus optionally, ":<port
>" | |
| string _sourceName; // a logical source name. | | string _sourceName; // a logical source name. | |
| string sourceName() const { | | string sourceName() const { | |
| return _sourceName.empty() ? "main" : _sourceName; | | return _sourceName.empty() ? "main" : _sourceName; | |
| } | | } | |
| string only; // only a certain db. note that in the sources collect
ion, this may not be changed once you start replicating. | | string only; // only a certain db. note that in the sources collect
ion, this may not be changed once you start replicating. | |
| | | | |
| | | | |
| skipping to change at line 167 | | skipping to change at line 229 | |
| int nClonedThisPass; | | int nClonedThisPass; | |
| | | | |
| typedef vector< shared_ptr< ReplSource > > SourceVector; | | typedef vector< shared_ptr< ReplSource > > SourceVector; | |
| static void loadAll(SourceVector&); | | static void loadAll(SourceVector&); | |
| explicit ReplSource(BSONObj); | | explicit ReplSource(BSONObj); | |
| | | | |
| /* -1 = error */ | | /* -1 = error */ | |
| int sync(int& nApplied); | | int sync(int& nApplied); | |
| | | | |
| void save(); // write ourself to local.sources | | void save(); // write ourself to local.sources | |
|
| void resetConnection() { | | | |
| cursor = auto_ptr<DBClientCursor>(0); | | | |
| conn = auto_ptr<DBClientConnection>(0); | | | |
| } | | | |
| | | | |
| // make a jsobj from our member fields of the form | | // make a jsobj from our member fields of the form | |
| // { host: ..., source: ..., syncedTo: ... } | | // { host: ..., source: ..., syncedTo: ... } | |
| BSONObj jsobj(); | | BSONObj jsobj(); | |
| | | | |
| bool operator==(const ReplSource&r) const { | | bool operator==(const ReplSource&r) const { | |
| return hostName == r.hostName && sourceName() == r.sourceName()
; | | return hostName == r.hostName && sourceName() == r.sourceName()
; | |
| } | | } | |
| operator string() const { return sourceName() + "@" + hostName; } | | operator string() const { return sourceName() + "@" + hostName; } | |
| | | | |
| | | | |
End of changes. 10 change blocks. |
| 12 lines changed or deleted | | 74 lines changed or added | |
|
| rs.h | | rs.h | |
| | | | |
| skipping to change at line 28 | | skipping to change at line 28 | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include "../../util/concurrency/list.h" | | #include "../../util/concurrency/list.h" | |
| #include "../../util/concurrency/value.h" | | #include "../../util/concurrency/value.h" | |
| #include "../../util/concurrency/msg.h" | | #include "../../util/concurrency/msg.h" | |
| #include "../../util/hostandport.h" | | #include "../../util/hostandport.h" | |
| #include "../commands.h" | | #include "../commands.h" | |
| #include "rs_exception.h" | | #include "rs_exception.h" | |
| #include "rs_optime.h" | | #include "rs_optime.h" | |
|
| #include "rsmember.h" | | #include "rs_member.h" | |
| #include "rs_config.h" | | #include "rs_config.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| struct Target; | | struct Target; | |
| class ReplSetImpl; | | class ReplSetImpl; | |
| extern bool replSet; // true if using repl sets | | extern bool replSet; // true if using repl sets | |
| extern class ReplSet *theReplSet; // null until initialized | | extern class ReplSet *theReplSet; // null until initialized | |
| extern Tee *rsLog; | | extern Tee *rsLog; | |
| | | | |
|
| | | /* member of a replica set */ | |
| class Member : public List1<Member>::Base { | | class Member : public List1<Member>::Base { | |
| public: | | public: | |
| Member(HostAndPort h, unsigned ord, const ReplSetConfig::MemberCfg
*c); | | Member(HostAndPort h, unsigned ord, const ReplSetConfig::MemberCfg
*c); | |
| | | | |
| string fullName() const { return h().toString(); } | | string fullName() const { return h().toString(); } | |
| const ReplSetConfig::MemberCfg& config() const { return *_config; } | | const ReplSetConfig::MemberCfg& config() const { return *_config; } | |
| const HeartbeatInfo& hbinfo() const { return _hbinfo; } | | const HeartbeatInfo& hbinfo() const { return _hbinfo; } | |
| string lhb() { return _hbinfo.lastHeartbeatMsg; } | | string lhb() { return _hbinfo.lastHeartbeatMsg; } | |
| MemberState state() const { return _hbinfo.hbstate; } | | MemberState state() const { return _hbinfo.hbstate; } | |
| const HostAndPort& h() const { return _h; } | | const HostAndPort& h() const { return _h; } | |
| | | | |
| skipping to change at line 97 | | skipping to change at line 99 | |
| Consensus(ReplSetImpl *t) : rs(*t) { | | Consensus(ReplSetImpl *t) : rs(*t) { | |
| sleptLast = false; | | sleptLast = false; | |
| } | | } | |
| int totalVotes() const; | | int totalVotes() const; | |
| bool aMajoritySeemsToBeUp() const; | | bool aMajoritySeemsToBeUp() const; | |
| void electSelf(); | | void electSelf(); | |
| void electCmdReceived(BSONObj, BSONObjBuilder*); | | void electCmdReceived(BSONObj, BSONObjBuilder*); | |
| void multiCommand(BSONObj cmd, list<Target>& L); | | void multiCommand(BSONObj cmd, list<Target>& L); | |
| }; | | }; | |
| | | | |
|
| /** most operations on a ReplSet object should be done while locked. */ | | /** most operations on a ReplSet object should be done while locked. th
at logic implemented here. */ | |
| class RSBase : boost::noncopyable { | | class RSBase : boost::noncopyable { | |
| private: | | private: | |
| mutex m; | | mutex m; | |
| int _locked; | | int _locked; | |
| ThreadLocalValue<bool> _lockedByMe; | | ThreadLocalValue<bool> _lockedByMe; | |
| protected: | | protected: | |
| RSBase() : m("RSBase"), _locked(0) { } | | RSBase() : m("RSBase"), _locked(0) { } | |
|
| class lock : scoped_lock { | | | |
| RSBase& _b; | | class lock { | |
| | | RSBase& rsbase; | |
| | | auto_ptr<scoped_lock> sl; | |
| public: | | public: | |
|
| lock(RSBase* b) : scoped_lock(b->m), _b(*b) { | | lock(RSBase* b) : rsbase(*b) { | |
| DEV assert(_b._locked == 0); | | if( rsbase._lockedByMe.get() ) | |
| _b._locked++; | | return; // recursive is ok... | |
| _b._lockedByMe.set(true); | | | |
| cout << "RSLOCKED" << endl; | | sl.reset( new scoped_lock(rsbase.m) ); | |
| | | DEV assert(rsbase._locked == 0); | |
| | | rsbase._locked++; | |
| | | rsbase._lockedByMe.set(true); | |
| } | | } | |
| ~lock() { | | ~lock() { | |
|
| cout << "RSUNLOCKED" << endl; | | if( sl.get() ) { | |
| assert( _b._lockedByMe.get() ); | | assert( rsbase._lockedByMe.get() ); | |
| DEV assert(_b._locked == 1); | | DEV assert(rsbase._locked == 1); | |
| _b._lockedByMe.set(false); | | rsbase._lockedByMe.set(false); | |
| _b._locked--; | | rsbase._locked--; | |
| | | } | |
| } | | } | |
| }; | | }; | |
|
| | | | |
| public: | | public: | |
| /* for asserts */ | | /* for asserts */ | |
| bool locked() const { return _locked != 0; } | | bool locked() const { return _locked != 0; } | |
| | | | |
| /* if true, is locked, and was locked by this thread. note if false
, it could be in the lock or not for another | | /* if true, is locked, and was locked by this thread. note if false
, it could be in the lock or not for another | |
| just for asserts & such so we can make the contracts clear on wh
o locks what when. | | just for asserts & such so we can make the contracts clear on wh
o locks what when. | |
| we don't use these locks that frequently, so the little bit of o
verhead is fine. | | we don't use these locks that frequently, so the little bit of o
verhead is fine. | |
| */ | | */ | |
| bool lockedByMe() { return _lockedByMe.get(); } | | bool lockedByMe() { return _lockedByMe.get(); } | |
| }; | | }; | |
| | | | |
| skipping to change at line 133 | | skipping to change at line 142 | |
| /* for asserts */ | | /* for asserts */ | |
| bool locked() const { return _locked != 0; } | | bool locked() const { return _locked != 0; } | |
| | | | |
| /* if true, is locked, and was locked by this thread. note if false
, it could be in the lock or not for another | | /* if true, is locked, and was locked by this thread. note if false
, it could be in the lock or not for another | |
| just for asserts & such so we can make the contracts clear on wh
o locks what when. | | just for asserts & such so we can make the contracts clear on wh
o locks what when. | |
| we don't use these locks that frequently, so the little bit of o
verhead is fine. | | we don't use these locks that frequently, so the little bit of o
verhead is fine. | |
| */ | | */ | |
| bool lockedByMe() { return _lockedByMe.get(); } | | bool lockedByMe() { return _lockedByMe.get(); } | |
| }; | | }; | |
| | | | |
|
| | | class ReplSetHealthPollTask; | |
| | | | |
| /* information about the entire repl set, such as the various servers i
n the set, and their state */ | | /* information about the entire repl set, such as the various servers i
n the set, and their state */ | |
| /* note: We currently do not free mem when the set goes away - it is as
sumed the replset is a | | /* note: We currently do not free mem when the set goes away - it is as
sumed the replset is a | |
| singleton and long lived. | | singleton and long lived. | |
| */ | | */ | |
|
| class ReplSetImpl : RSBase { | | class ReplSetImpl : protected RSBase { | |
| public: | | public: | |
| /** info on our state if the replset isn't yet "up". for example,
if we are pre-initiation. */ | | /** info on our state if the replset isn't yet "up". for example,
if we are pre-initiation. */ | |
| enum StartupStatus { | | enum StartupStatus { | |
| PRESTART=0, LOADINGCONFIG=1, BADCONFIG=2, EMPTYCONFIG=3, | | PRESTART=0, LOADINGCONFIG=1, BADCONFIG=2, EMPTYCONFIG=3, | |
| EMPTYUNREACHABLE=4, STARTED=5, SOON=6 | | EMPTYUNREACHABLE=4, STARTED=5, SOON=6 | |
| }; | | }; | |
| static StartupStatus startupStatus; | | static StartupStatus startupStatus; | |
| static string startupStatusMsg; | | static string startupStatusMsg; | |
| static string stateAsStr(MemberState state); | | static string stateAsStr(MemberState state); | |
| static string stateAsHtml(MemberState state); | | static string stateAsHtml(MemberState state); | |
| | | | |
| skipping to change at line 161 | | skipping to change at line 172 | |
| bool isPrimary() const { return _myState == PRIMARY; } | | bool isPrimary() const { return _myState == PRIMARY; } | |
| bool isSecondary() const { return _myState == SECONDARY; } | | bool isSecondary() const { return _myState == SECONDARY; } | |
| | | | |
| //bool initiated() const { return curOpTime.initiated(); } | | //bool initiated() const { return curOpTime.initiated(); } | |
| | | | |
| OpTime lastOpTimeWritten; | | OpTime lastOpTimeWritten; | |
| long long h; | | long long h; | |
| private: | | private: | |
| unsigned _selfId; // stored redundantly we hit this a lot | | unsigned _selfId; // stored redundantly we hit this a lot | |
| | | | |
|
| | | set<ReplSetHealthPollTask*> healthTasks; | |
| | | void endOldHealthTasks(); | |
| | | void startHealthTaskFor(Member *m); | |
| | | | |
| private: | | private: | |
| Consensus elect; | | Consensus elect; | |
| bool ok() const { return _myState != FATAL; } | | bool ok() const { return _myState != FATAL; } | |
| | | | |
| void relinquish(); | | void relinquish(); | |
| void assumePrimary(); | | void assumePrimary(); | |
| void loadLastOpTimeWritten(); | | void loadLastOpTimeWritten(); | |
| | | | |
| protected: | | protected: | |
|
| | | // "heartbeat message" | |
| | | // sent in requestHeartbeat respond in field "hbm" | |
| | | char _hbmsg[256]; | |
| | | void sethbmsg(string s) { | |
| | | assert(s.size() < sizeof(_hbmsg)); | |
| | | strcpy(_hbmsg, s.c_str()); | |
| | | } | |
| | | | |
| | | bool initFromConfig(ReplSetConfig& c); // true if ok; throws if con | |
| | | fig really bad; false if config doesn't include self | |
| void _fillIsMaster(BSONObjBuilder&); | | void _fillIsMaster(BSONObjBuilder&); | |
|
| | | void _fillIsMasterHost(const Member*, vector<string>&, vector<strin
g>&, vector<string>&); | |
| const ReplSetConfig& config() { return *_cfg; } | | const ReplSetConfig& config() { return *_cfg; } | |
| string name() const { return _name; } /* @return replica set's logi
cal name */ | | string name() const { return _name; } /* @return replica set's logi
cal name */ | |
| MemberState state() const { return _myState; } | | MemberState state() const { return _myState; } | |
| void _fatal(); | | void _fatal(); | |
| void _getOplogDiagsAsHtml(unsigned server_id, stringstream& ss) con
st; | | void _getOplogDiagsAsHtml(unsigned server_id, stringstream& ss) con
st; | |
| void _summarizeAsHtml(stringstream&) const; | | void _summarizeAsHtml(stringstream&) const; | |
| void _summarizeStatus(BSONObjBuilder&) const; // for replSetGetStat
us command | | void _summarizeStatus(BSONObjBuilder&) const; // for replSetGetStat
us command | |
| | | | |
| /* cfgString format is | | /* cfgString format is | |
| replsetname/host1,host2:port,... | | replsetname/host1,host2:port,... | |
| where :port is optional. | | where :port is optional. | |
| throws exception if a problem initializing. */ | | throws exception if a problem initializing. */ | |
| ReplSetImpl(string cfgString); | | ReplSetImpl(string cfgString); | |
| | | | |
|
| /* call after constructing to start - returns fairly quickly after
launching its threads */ | | /* call afer constructing to start - returns fairly quickly after l
aunching its threads */ | |
| void _go(); | | void _go(); | |
| | | | |
| private: | | private: | |
| | | | |
| MemberState _myState; | | MemberState _myState; | |
| string _name; | | string _name; | |
| const vector<HostAndPort> *_seeds; | | const vector<HostAndPort> *_seeds; | |
| ReplSetConfig *_cfg; | | ReplSetConfig *_cfg; | |
| | | | |
| /** load our configuration from admin.replset. try seed machines t
oo. | | /** load our configuration from admin.replset. try seed machines t
oo. | |
|
| throws exception if a problem. | | @return true if ok; throws if config really bad; false if confi
g doesn't include self | |
| */ | | */ | |
|
| void _loadConfigFinish(vector<ReplSetConfig>& v); | | bool _loadConfigFinish(vector<ReplSetConfig>& v); | |
| void loadConfig(); | | void loadConfig(); | |
|
| void initFromConfig(ReplSetConfig& c);//, bool save); | | | |
| | | | |
| list<HostAndPort> memberHostnames() const; | | list<HostAndPort> memberHostnames() const; | |
| const Member* currentPrimary() const { return _currentPrimary; } | | const Member* currentPrimary() const { return _currentPrimary; } | |
| const ReplSetConfig::MemberCfg& myConfig() const { return _self->co
nfig(); } | | const ReplSetConfig::MemberCfg& myConfig() const { return _self->co
nfig(); } | |
|
| | | | |
| private: | | | |
| const Member *_currentPrimary; | | const Member *_currentPrimary; | |
| Member *_self; | | Member *_self; | |
| List1<Member> _members; /* all members of the set EXCEPT self. */ | | List1<Member> _members; /* all members of the set EXCEPT self. */ | |
| | | | |
| public: | | public: | |
| unsigned selfId() const { return _selfId; } | | unsigned selfId() const { return _selfId; } | |
| shared_ptr<Manager> mgr; | | shared_ptr<Manager> mgr; | |
| | | | |
| private: | | private: | |
| Member* head() const { return _members.head(); } | | Member* head() const { return _members.head(); } | |
| Member* findById(unsigned id) const; | | Member* findById(unsigned id) const; | |
| void _getTargets(list<Target>&, int &configVersion); | | void _getTargets(list<Target>&, int &configVersion); | |
| void getTargets(list<Target>&, int &configVersion); | | void getTargets(list<Target>&, int &configVersion); | |
| void startThreads(); | | void startThreads(); | |
| friend class FeedbackThread; | | friend class FeedbackThread; | |
| friend class CmdReplSetElect; | | friend class CmdReplSetElect; | |
| friend class Member; | | friend class Member; | |
| friend class Manager; | | friend class Manager; | |
| friend class Consensus; | | friend class Consensus; | |
|
| | | | |
| | | private: | |
| | | /* pulling data from primary related - see rs_sync.cpp */ | |
| | | void syncDoInitialSync(); | |
| }; | | }; | |
| | | | |
| class ReplSet : public ReplSetImpl { | | class ReplSet : public ReplSetImpl { | |
| public: | | public: | |
|
| ReplSet(string cfgString) : ReplSetImpl(cfgString) { } | | ReplSet(string cfgString) : ReplSetImpl(cfgString) { | |
| /* call after constructing to start - returns fairly quickly after | | } | |
| launching 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 isMaster(const char *client); | | bool isMaster(const char *client); | |
| MemberState state() const { return ReplSetImpl::state(); } | | MemberState state() const { return ReplSetImpl::state(); } | |
| string name() const { return ReplSetImpl::name(); } | | string name() const { return ReplSetImpl::name(); } | |
| const ReplSetConfig& config() { return ReplSetImpl::config(); } | | const ReplSetConfig& config() { return ReplSetImpl::config(); } | |
| void getOplogDiagsAsHtml(unsigned server_id, stringstream& ss) cons
t { _getOplogDiagsAsHtml(server_id,ss); } | | void getOplogDiagsAsHtml(unsigned server_id, stringstream& ss) cons
t { _getOplogDiagsAsHtml(server_id,ss); } | |
| void summarizeAsHtml(stringstream& ss) const { _summarizeAsHtml(ss)
; } | | void summarizeAsHtml(stringstream& ss) const { _summarizeAsHtml(ss)
; } | |
| void summarizeStatus(BSONObjBuilder& b) const { _summarizeStatus(b
); } | | void summarizeStatus(BSONObjBuilder& b) const { _summarizeStatus(b
); } | |
| void fillIsMaster(BSONObjBuilder& b) { _fillIsMaster(b); } | | void fillIsMaster(BSONObjBuilder& b) { _fillIsMaster(b); } | |
|
| | | | |
| | | /* we have a new config (reconfig) - apply it. */ | |
| | | void haveNewConfig(ReplSetConfig& c); | |
| | | | |
| | | /* if we delete old configs, this needs to assure locking. currentl | |
| | | y we don't so it is ok. */ | |
| | | const ReplSetConfig& getConfig() { return config(); } | |
| | | | |
| | | bool lockedByMe() { return RSBase::lockedByMe(); } | |
| | | | |
| | | // heartbeat msg to send to others; descriptive diagnostic info | |
| | | string hbmsg() const { return _hbmsg; } | |
| }; | | }; | |
| | | | |
| /** base class for repl set commands. checks basic things such as in r
s mode before the command | | /** base class for repl set commands. checks basic things such as in r
s mode before the command | |
| does its real work | | does its real work | |
| */ | | */ | |
| class ReplSetCommand : public Command { | | class ReplSetCommand : public Command { | |
| protected: | | protected: | |
| ReplSetCommand(const char * s, bool show=false) : Command(s) { } | | ReplSetCommand(const char * s, bool show=false) : Command(s) { } | |
| virtual bool slaveOk() const { return true; } | | virtual bool slaveOk() const { return true; } | |
| virtual bool adminOnly() const { return true; } | | virtual bool adminOnly() const { return true; } | |
| | | | |
| skipping to change at line 269 | | skipping to change at line 308 | |
| } | | } | |
| if( theReplSet == 0 ) { | | if( theReplSet == 0 ) { | |
| result.append("startupStatus", ReplSet::startupStatus); | | result.append("startupStatus", ReplSet::startupStatus); | |
| errmsg = ReplSet::startupStatusMsg.empty() ? "replset unkno
wn error 2" : ReplSet::startupStatusMsg; | | errmsg = ReplSet::startupStatusMsg.empty() ? "replset unkno
wn error 2" : ReplSet::startupStatusMsg; | |
| return false; | | return false; | |
| } | | } | |
| return true; | | return true; | |
| } | | } | |
| }; | | }; | |
| | | | |
|
| | | /** helpers ----------------- */ | |
| | | | |
| | | void parseReplsetCmdLine(string cfgString, string& setname, vector<Host | |
| | | AndPort>& seeds, set<HostAndPort>& seedSet ); | |
| | | | |
| /** inlines ----------------- */ | | /** inlines ----------------- */ | |
| | | | |
| inline Member::Member(HostAndPort h, unsigned ord, const ReplSetConfig:
:MemberCfg *c) : | | inline Member::Member(HostAndPort h, unsigned ord, const ReplSetConfig:
:MemberCfg *c) : | |
| _config(c), _h(h), _hbinfo(ord) { } | | _config(c), _h(h), _hbinfo(ord) { } | |
| | | | |
| inline bool ReplSet::isMaster(const char *client) { | | inline bool ReplSet::isMaster(const char *client) { | |
| /* todo replset */ | | /* todo replset */ | |
|
| return false; | | return isPrimary(); | |
| } | | } | |
| | | | |
| } | | } | |
| | | | |
End of changes. 22 change blocks. |
| 25 lines changed or deleted | | 70 lines changed or added | |
|