| balancer_policy.h | | balancer_policy.h | |
| | | | |
| skipping to change at line 22 | | skipping to change at line 22 | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU Affero General Public License for more details. | | * GNU Affero General Public License for more details. | |
| * | | * | |
| * You should have received a copy of the GNU Affero General Public Licen
se | | * You should have received a copy of the GNU Affero General Public Licen
se | |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. | | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| */ | | */ | |
| | | | |
| #ifndef S_BALANCER_POLICY_HEADER | | #ifndef S_BALANCER_POLICY_HEADER | |
| #define S_BALANCER_POLICY_HEADER | | #define S_BALANCER_POLICY_HEADER | |
| | | | |
|
| #include "../pch.h" | | #include "mongo/db/jsobj.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
|
| class BalancerPolicy { | | struct ChunkInfo { | |
| public: | | const BSONObj min; | |
| struct ChunkInfo; | | const BSONObj max; | |
| | | | |
|
| /** | | ChunkInfo( const BSONObj& a_min, const BSONObj& a_max ) | |
| * Returns a suggested chunk to move whithin a collection's shards, | | : min( a_min.getOwned() ), max( a_max.getOwned() ){} | |
| given information about | | | |
| * space usage and number of chunks for that collection. If the pol | | | |
| icy doesn't recommend | | | |
| * moving, it returns NULL. | | | |
| * | | | |
| * @param ns is the collections namepace. | | | |
| * @param shardLimitMap is a map from shardId to an object that des | | | |
| cribes (for now) space | | | |
| * cap and usage. E.g.: { "maxSize" : <size_in_MB> , "usedSize" : < | | | |
| size_in_MB> }. | | | |
| * @param shardToChunksMap is a map from shardId to chunks that liv | | | |
| e there. A chunk's format | | | |
| * is { }. | | | |
| * @param balancedLastTime is the number of chunks effectively move | | | |
| d in the last round. | | | |
| * @returns NULL or ChunkInfo of the best move to make towards bala | | | |
| cing the collection. | | | |
| */ | | | |
| typedef map< string,BSONObj > ShardToLimitsMap; | | | |
| typedef map< string,vector<BSONObj> > ShardToChunksMap; | | | |
| static ChunkInfo* balance( const string& ns, const ShardToLimitsMap | | | |
| & shardToLimitsMap, | | | |
| const ShardToChunksMap& shardToChunksMap | | | |
| , int balancedLastTime ); | | | |
| | | | |
|
| // below exposed for testing purposes only -- treat it as private - | | ChunkInfo( const BSONObj& chunk ) | |
| - | | : min( chunk["min"].Obj().getOwned() ), max( chunk["max"].Obj() | |
| | | .getOwned() ) { | |
| | | } | |
| | | | |
|
| static BSONObj pickChunk( const vector<BSONObj>& from, const vector | | string toString() const; | |
| <BSONObj>& to ); | | }; | |
| | | | |
| | | struct TagRange { | |
| | | BSONObj min; | |
| | | BSONObj max; | |
| | | string tag; | |
| | | | |
| | | TagRange(){} | |
| | | | |
| | | TagRange( const BSONObj& a_min, const BSONObj& a_max, const string& | |
| | | a_tag ) | |
| | | : min( a_min.getOwned() ), max( a_max.getOwned() ), tag( a_tag | |
| | | ){} | |
| | | string toString() const; | |
| | | }; | |
| | | | |
| | | class ShardInfo { | |
| | | public: | |
| | | ShardInfo(); | |
| | | ShardInfo( long long maxSize, long long currSize, | |
| | | bool draining, bool opsQueued, | |
| | | const set<string>& tags = set<string>() ); | |
| | | | |
| | | void addTag( const string& tag ); | |
| | | | |
| | | /** @return true if we have the tag OR if the tag is "" */ | |
| | | bool hasTag( const string& tag ) const; | |
| | | | |
| /** | | /** | |
|
| * Returns true if a shard cannot receive any new chunks bacause it
reache 'shardLimits'. | | * @return true if a shard cannot receive any new chunks bacause it
reache 'shardLimits'. | |
| * Expects the optional fields "maxSize", can in size in MB, and "u
sedSize", currently used size | | * Expects the optional fields "maxSize", can in size in MB, and "u
sedSize", currently used size | |
| * in MB, on 'shardLimits'. | | * in MB, on 'shardLimits'. | |
| */ | | */ | |
|
| static bool isSizeMaxed( BSONObj shardLimits ); | | bool isSizeMaxed() const; | |
| | | | |
| /** | | /** | |
|
| * Returns true if 'shardLimist' contains a field "draining". Expec
ts the optional field | | * @return true if 'shardLimist' contains a field "draining". Expec
ts the optional field | |
| * "isDraining" on 'shrdLimits'. | | * "isDraining" on 'shrdLimits'. | |
| */ | | */ | |
|
| static bool isDraining( BSONObj shardLimits ); | | bool isDraining() const { return _draining; } | |
| | | | |
| /** | | /** | |
|
| * Returns true if a shard currently has operations in any of its w
riteback queues | | * @return true if a shard currently has operations in any of its w
riteback queues | |
| */ | | */ | |
|
| static bool hasOpsQueued( BSONObj shardLimits ); | | bool hasOpsQueued() const { return _hasOpsQueued; } | |
| | | | |
|
| private: | | string toString() const; | |
| // Convenience types | | | |
| typedef ShardToChunksMap::const_iterator ShardToChunksIter; | | | |
| typedef ShardToLimitsMap::const_iterator ShardToLimitsIter; | | | |
| | | | |
|
| | | private: | |
| | | long long _maxSize; | |
| | | long long _currSize; | |
| | | bool _draining; | |
| | | bool _hasOpsQueued; | |
| | | set<string> _tags; | |
| }; | | }; | |
| | | | |
|
| struct BalancerPolicy::ChunkInfo { | | struct MigrateInfo { | |
| const string ns; | | const string ns; | |
| const string to; | | const string to; | |
| const string from; | | const string from; | |
|
| const BSONObj chunk; | | const ChunkInfo chunk; | |
| | | | |
|
| ChunkInfo( const string& a_ns , const string& a_to , const string&
a_from , const BSONObj& a_chunk ) | | MigrateInfo( const string& a_ns , const string& a_to , const string
& a_from , const BSONObj& a_chunk ) | |
| : ns( a_ns ) , to( a_to ) , from( a_from ), chunk( a_chunk ) {} | | : ns( a_ns ) , to( a_to ) , from( a_from ), chunk( a_chunk ) {} | |
|
| | | | |
| }; | | }; | |
| | | | |
|
| /** | | typedef map< string,ShardInfo > ShardInfoMap; | |
| * Field names used in the 'limits' map. | | typedef map< string,vector<BSONObj> > ShardToChunksMap; | |
| */ | | | |
| struct LimitsFields { | | class DistributionStatus : boost::noncopyable { | |
| // we use 'draining' and 'maxSize' from the 'shards' collection plu | | public: | |
| s the following | | DistributionStatus( const ShardInfoMap& shardInfo, | |
| static BSONField<long long> currSize; // currently used disk space | | const ShardToChunksMap& shardToChunksMap ); | |
| in bytes | | | |
| static BSONField<bool> hasOpsQueued; // writeback queue is not emp | | // only used when building | |
| ty? | | | |
| | | /** | |
| | | * @return if range is valid | |
| | | */ | |
| | | bool addTagRange( const TagRange& range ); | |
| | | | |
| | | // ---- these methods might be better suiting in BalancerPolicy | |
| | | | |
| | | /** | |
| | | * @param forTag "" if you don't care, or a tag | |
| | | * @return shard best suited to receive a chunk | |
| | | */ | |
| | | string getBestReceieverShard( const string& forTag ) const; | |
| | | | |
| | | /** | |
| | | * @return the shard with the most chunks | |
| | | * based on # of chunks with the given tag | |
| | | */ | |
| | | string getMostOverloadedShard( const string& forTag ) const; | |
| | | | |
| | | // ---- basic accessors, counters, etc... | |
| | | | |
| | | /** @return total number of chunks */ | |
| | | unsigned totalChunks() const; | |
| | | | |
| | | /** @return number of chunks in this shard */ | |
| | | unsigned numberOfChunksInShard( const string& shard ) const; | |
| | | | |
| | | /** @return number of chunks in this shard with the given tag */ | |
| | | unsigned numberOfChunksInShardWithTag( const string& shard, const s | |
| | | tring& tag ) const; | |
| | | | |
| | | /** @return chunks for the shard */ | |
| | | const vector<BSONObj>& getChunks( const string& shard ) const; | |
| | | | |
| | | /** @return all tags we know about, not include "" */ | |
| | | const set<string>& tags() const { return _allTags; } | |
| | | | |
| | | /** @return the right tag for chunk, possibly "" */ | |
| | | string getTagForChunk( const BSONObj& chunk ) const; | |
| | | | |
| | | /** @return all shards we know about */ | |
| | | const set<string>& shards() const { return _shards; } | |
| | | | |
| | | /** @return the ShardInfo for the shard */ | |
| | | const ShardInfo& shardInfo( const string& shard ) const; | |
| | | | |
| | | /** writes all state to log() */ | |
| | | void dump() const; | |
| | | | |
| | | private: | |
| | | const ShardInfoMap& _shardInfo; | |
| | | const ShardToChunksMap& _shardChunks; | |
| | | map<BSONObj,TagRange> _tagRanges; | |
| | | set<string> _allTags; | |
| | | set<string> _shards; | |
| | | }; | |
| | | | |
| | | class BalancerPolicy { | |
| | | public: | |
| | | | |
| | | /** | |
| | | * Returns a suggested chunk to move whithin a collection's shards, | |
| | | given information about | |
| | | * space usage and number of chunks for that collection. If the pol | |
| | | icy doesn't recommend | |
| | | * moving, it returns NULL. | |
| | | * | |
| | | * @param ns is the collections namepace. | |
| | | * @param DistributionStatus holds all the info about the current s | |
| | | tate of the cluster/namespace | |
| | | * @param balancedLastTime is the number of chunks effectively move | |
| | | d in the last round. | |
| | | * @returns NULL or MigrateInfo of the best move to make towards ba | |
| | | lacing the collection. | |
| | | * caller owns the MigrateInfo instance | |
| | | */ | |
| | | static MigrateInfo* balance( const string& ns, | |
| | | const DistributionStatus& distribution | |
| | | , | |
| | | int balancedLastTime ); | |
| | | | |
| }; | | }; | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
| #endif // S_BALANCER_POLICY_HEADER | | #endif // S_BALANCER_POLICY_HEADER | |
| | | | |
End of changes. 18 change blocks. |
| 57 lines changed or deleted | | 146 lines changed or added | |
|
| bsonelement.h | | bsonelement.h | |
|
| // BSONElement | | // bsonelement.h | |
| | | | |
| /* Copyright 2009 10gen Inc. | | /* Copyright 2009 10gen Inc. | |
| * | | * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | | * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | | * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | | * You may obtain a copy of the License at | |
| * | | * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | | * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | | * | |
| * Unless required by applicable law or agreed to in writing, software | | * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | | * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli
ed. | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli
ed. | |
| * See the License for the specific language governing permissions and | | * See the License for the specific language governing permissions and | |
| * limitations under the License. | | * limitations under the License. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
|
| | | #include <string.h> // strlen | |
| | | #include <string> | |
| #include <vector> | | #include <vector> | |
|
| #include <string.h> | | | |
| #include "util/builder.h" | | #include "mongo/bson/bsontypes.h" | |
| #include "bsontypes.h" | | #include "mongo/bson/oid.h" | |
| #include "oid.h" | | #include "mongo/platform/float_utils.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| class OpTime; | | class OpTime; | |
| class BSONObj; | | class BSONObj; | |
| class BSONElement; | | class BSONElement; | |
| class BSONObjBuilder; | | class BSONObjBuilder; | |
| } | | } | |
| | | | |
| namespace bson { | | namespace bson { | |
| typedef mongo::BSONElement be; | | typedef mongo::BSONElement be; | |
| | | | |
| skipping to change at line 76 | | skipping to change at line 78 | |
| double Number() const { return chk(isNumber()).number()
; } | | double Number() const { return chk(isNumber()).number()
; } | |
| double Double() const { return chk(NumberDouble)._numbe
rDouble(); } | | double Double() const { return chk(NumberDouble)._numbe
rDouble(); } | |
| long long Long() const { return chk(NumberLong)._numberL
ong(); } | | long long Long() const { return chk(NumberLong)._numberL
ong(); } | |
| int Int() const { return chk(NumberInt)._numberIn
t(); } | | int Int() const { return chk(NumberInt)._numberIn
t(); } | |
| bool Bool() const { return chk(mongo::Bool).boolean
(); } | | bool Bool() const { return chk(mongo::Bool).boolean
(); } | |
| std::vector<BSONElement> Array() const; // see implementation for d
etailed comments | | std::vector<BSONElement> Array() const; // see implementation for d
etailed comments | |
| mongo::OID OID() const { return chk(jstOID).__oid(); } | | mongo::OID OID() const { return chk(jstOID).__oid(); } | |
| void Null() const { chk(isNull()); } // throw UserE
xception if not null | | void Null() const { chk(isNull()); } // throw UserE
xception if not null | |
| void OK() const { chk(ok()); } // throw UserE
xception if element DNE | | void OK() const { chk(ok()); } // throw UserE
xception if element DNE | |
| | | | |
|
| /** @return the embedded object associated with this field. | | /** @return the embedded object associated with this field. | |
| Note the returned object is a reference to within the parent bs
on object. If that | | Note the returned object is a reference to within the parent bs
on object. If that | |
|
| object is out of scope, this pointer will no longer be valid. Ca | | object is out of scope, this pointer will no longer be valid. C | |
| ll getOwned() on the | | all getOwned() on the | |
| returned BSONObj if you need your own copy. | | returned BSONObj if you need your own copy. | |
| throws UserException if the element is not of type object. | | throws UserException if the element is not of type object. | |
| */ | | */ | |
| BSONObj Obj() const; | | BSONObj Obj() const; | |
| | | | |
| /** populate v with the value of the element. If type does not mat
ch, throw exception. | | /** populate v with the value of the element. If type does not mat
ch, throw exception. | |
| useful in templates -- see also BSONObj::Vals(). | | useful in templates -- see also BSONObj::Vals(). | |
| */ | | */ | |
| void Val(Date_t& v) const { v = Date(); } | | void Val(Date_t& v) const { v = Date(); } | |
| void Val(long long& v) const { v = Long(); } | | void Val(long long& v) const { v = Long(); } | |
| void Val(bool& v) const { v = Bool(); } | | void Val(bool& v) const { v = Bool(); } | |
| void Val(BSONObj& v) const; | | void Val(BSONObj& v) const; | |
| void Val(mongo::OID& v) const { v = OID(); } | | void Val(mongo::OID& v) const { v = OID(); } | |
| | | | |
| skipping to change at line 146 | | skipping to change at line 148 | |
| | | | |
| /** field name of the element. e.g., for | | /** field name of the element. e.g., for | |
| name : "Joe" | | name : "Joe" | |
| "name" is the fieldname | | "name" is the fieldname | |
| */ | | */ | |
| const char * fieldName() const { | | const char * fieldName() const { | |
| if ( eoo() ) return ""; // no fieldname for it. | | if ( eoo() ) return ""; // no fieldname for it. | |
| return data + 1; | | return data + 1; | |
| } | | } | |
| | | | |
|
| | | int fieldNameSize() const { | |
| | | if ( fieldNameSize_ == -1 ) | |
| | | fieldNameSize_ = (int)strlen( fieldName() ) + 1; | |
| | | return fieldNameSize_; | |
| | | } | |
| | | | |
| /** raw data of the element's value (so be careful). */ | | /** raw data of the element's value (so be careful). */ | |
| const char * value() const { | | const char * value() const { | |
| return (data + fieldNameSize() + 1); | | return (data + fieldNameSize() + 1); | |
| } | | } | |
| /** size in bytes of the element's value (when applicable). */ | | /** size in bytes of the element's value (when applicable). */ | |
| int valuesize() const { | | int valuesize() const { | |
| return size() - fieldNameSize() - 1; | | return size() - fieldNameSize() - 1; | |
| } | | } | |
| | | | |
| bool isBoolean() const { return type() == mongo::Bool; } | | bool isBoolean() const { return type() == mongo::Bool; } | |
| | | | |
| skipping to change at line 194 | | skipping to change at line 202 | |
| | | | |
| /** Return double value for this field. MUST be NumberDouble type.
*/ | | /** Return double value for this field. MUST be NumberDouble type.
*/ | |
| double _numberDouble() const {return (reinterpret_cast< const Packe
dDouble* >( value() ))->d; } | | double _numberDouble() const {return (reinterpret_cast< const Packe
dDouble* >( value() ))->d; } | |
| /** Return int value for this field. MUST be NumberInt type. */ | | /** Return int value for this field. MUST be NumberInt type. */ | |
| int _numberInt() const {return *reinterpret_cast< const int* >( val
ue() ); } | | int _numberInt() const {return *reinterpret_cast< const int* >( val
ue() ); } | |
| /** Return long long value for this field. MUST be NumberLong type.
*/ | | /** Return long long value for this field. MUST be NumberLong type.
*/ | |
| long long _numberLong() const {return *reinterpret_cast< const long
long* >( value() ); } | | long long _numberLong() const {return *reinterpret_cast< const long
long* >( value() ); } | |
| | | | |
| /** Retrieve int value for the element safely. Zero returned if no
t a number. */ | | /** Retrieve int value for the element safely. Zero returned if no
t a number. */ | |
| int numberInt() const; | | int numberInt() const; | |
|
| /** Retrieve long value for the element safely. Zero returned if n | | /** Retrieve long value for the element safely. Zero returned if n | |
| ot a number. */ | | ot a number. | |
| | | * Behavior is not defined for double values that are NaNs, or too | |
| | | large/small | |
| | | * to be represented by long longs */ | |
| long long numberLong() const; | | long long numberLong() const; | |
|
| | | | |
| | | /** Like numberLong() but with well-defined behavior for doubles th | |
| | | at | |
| | | * are NaNs, or too large/small to be represented as long longs. | |
| | | * NaNs -> 0 | |
| | | * very large doubles -> LLONG_MAX | |
| | | * very small doubles -> LLONG_MIN */ | |
| | | long long safeNumberLong() const; | |
| | | | |
| /** Retrieve the numeric value of the element. If not of a numeric
type, returns 0. | | /** Retrieve the numeric value of the element. If not of a numeric
type, returns 0. | |
| Note: casts to double, data loss may occur with large (>52 bit)
NumberLong values. | | Note: casts to double, data loss may occur with large (>52 bit)
NumberLong values. | |
| */ | | */ | |
| double numberDouble() const; | | double numberDouble() const; | |
| /** Retrieve the numeric value of the element. If not of a numeric
type, returns 0. | | /** Retrieve the numeric value of the element. If not of a numeric
type, returns 0. | |
| Note: casts to double, data loss may occur with large (>52 bit)
NumberLong values. | | Note: casts to double, data loss may occur with large (>52 bit)
NumberLong values. | |
| */ | | */ | |
| double number() const { return numberDouble(); } | | double number() const { return numberDouble(); } | |
| | | | |
| /** Retrieve the object ID stored in the object. | | /** Retrieve the object ID stored in the object. | |
| | | | |
| skipping to change at line 245 | | skipping to change at line 263 | |
| const char *valuestrsafe() const { | | const char *valuestrsafe() const { | |
| return type() == mongo::String ? valuestr() : ""; | | return type() == mongo::String ? valuestr() : ""; | |
| } | | } | |
| /** Get the string value of the element. If not a string returns "
". */ | | /** Get the string value of the element. If not a string returns "
". */ | |
| std::string str() const { | | std::string str() const { | |
| return type() == mongo::String ? std::string(valuestr(), values
trsize()-1) : std::string(); | | return type() == mongo::String ? std::string(valuestr(), values
trsize()-1) : std::string(); | |
| } | | } | |
| | | | |
| /** Get javascript code of a CodeWScope data element. */ | | /** Get javascript code of a CodeWScope data element. */ | |
| const char * codeWScopeCode() const { | | const char * codeWScopeCode() const { | |
|
| return value() + 8; | | massert( 16177 , "not codeWScope" , type() == CodeWScope ); | |
| | | return value() + 4 + 4; //two ints precede code (see BSON spec) | |
| } | | } | |
|
| /** Get the scope SavedContext of a CodeWScope data element. */ | | | |
| const char * codeWScopeScopeData() const { | | /** Get length of the code part of the CodeWScope object | |
| // TODO fix | | * This INCLUDES the null char at the end */ | |
| | | int codeWScopeCodeLen() const { | |
| | | massert( 16178 , "not codeWScope" , type() == CodeWScope ); | |
| | | return *(int *)( value() + 4 ); | |
| | | } | |
| | | | |
| | | /** Get the scope SavedContext of a CodeWScope data element. | |
| | | * | |
| | | * This function is DEPRECATED, since it can error if there are | |
| | | * null chars in the codeWScopeCode. However, some existing indexe | |
| | | s | |
| | | * may be based on an incorrect ordering derived from this functio | |
| | | n, | |
| | | * so it may still need to be used in certain cases. | |
| | | * */ | |
| | | const char * codeWScopeScopeDataUnsafe() const { | |
| | | //This can error if there are null chars in the codeWScopeCode | |
| return codeWScopeCode() + strlen( codeWScopeCode() ) + 1; | | return codeWScopeCode() + strlen( codeWScopeCode() ) + 1; | |
| } | | } | |
| | | | |
|
| | | /* Get the scope SavedContext of a CodeWScope data element. | |
| | | * | |
| | | * This is the corrected version of codeWScopeScopeDataUnsafe(), | |
| | | * but note that existing uses might rely on the behavior of | |
| | | * that function so be careful in choosing which version to use. | |
| | | */ | |
| | | const char * codeWScopeScopeData() const { | |
| | | return codeWScopeCode() + codeWScopeCodeLen(); | |
| | | } | |
| | | | |
| /** Get the embedded object this element holds. */ | | /** Get the embedded object this element holds. */ | |
| BSONObj embeddedObject() const; | | BSONObj embeddedObject() const; | |
| | | | |
| /* uasserts if not an object */ | | /* uasserts if not an object */ | |
| BSONObj embeddedObjectUserCheck() const; | | BSONObj embeddedObjectUserCheck() const; | |
| | | | |
| BSONObj codeWScopeObject() const; | | BSONObj codeWScopeObject() const; | |
| | | | |
| /** Get raw binary data. Element must be of type BinData. Doesn't
handle type 2 specially */ | | /** Get raw binary data. Element must be of type BinData. Doesn't
handle type 2 specially */ | |
| const char *binData(int& len) const { | | const char *binData(int& len) const { | |
| | | | |
| skipping to change at line 415 | | skipping to change at line 458 | |
| totalSize = 1; | | totalSize = 1; | |
| } | | } | |
| } | | } | |
| | | | |
| std::string _asCode() const; | | std::string _asCode() const; | |
| OpTime _opTime() const; | | OpTime _opTime() const; | |
| | | | |
| private: | | private: | |
| const char *data; | | const char *data; | |
| mutable int fieldNameSize_; // cached value | | mutable int fieldNameSize_; // cached value | |
|
| int fieldNameSize() const { | | | |
| if ( fieldNameSize_ == -1 ) | | | |
| fieldNameSize_ = (int)strlen( fieldName() ) + 1; | | | |
| return fieldNameSize_; | | | |
| } | | | |
| mutable int totalSize; /* caches the computed size */ | | mutable int totalSize; /* caches the computed size */ | |
| | | | |
| friend class BSONObjIterator; | | friend class BSONObjIterator; | |
| friend class BSONObj; | | friend class BSONObj; | |
| const BSONElement& chk(int t) const { | | const BSONElement& chk(int t) const { | |
| if ( t != type() ) { | | if ( t != type() ) { | |
| StringBuilder ss; | | StringBuilder ss; | |
| if( eoo() ) | | if( eoo() ) | |
| ss << "field not found, expected type " << t; | | ss << "field not found, expected type " << t; | |
| else | | else | |
| | | | |
| skipping to change at line 575 | | skipping to change at line 614 | |
| return (long long) _numberDouble(); | | return (long long) _numberDouble(); | |
| case NumberInt: | | case NumberInt: | |
| return _numberInt(); | | return _numberInt(); | |
| case NumberLong: | | case NumberLong: | |
| return _numberLong(); | | return _numberLong(); | |
| default: | | default: | |
| return 0; | | return 0; | |
| } | | } | |
| } | | } | |
| | | | |
|
| | | /** Like numberLong() but with well-defined behavior for doubles that | |
| | | * are NaNs, or too large/small to be represented as long longs. | |
| | | * NaNs -> 0 | |
| | | * very large doubles -> LLONG_MAX | |
| | | * very small doubles -> LLONG_MIN */ | |
| | | inline long long BSONElement::safeNumberLong() const { | |
| | | double d; | |
| | | switch( type() ) { | |
| | | case NumberDouble: | |
| | | d = numberDouble(); | |
| | | if ( isNaN( d ) ){ | |
| | | return 0; | |
| | | } | |
| | | if ( d > (double) std::numeric_limits<long long>::max() ){ | |
| | | return std::numeric_limits<long long>::max(); | |
| | | } | |
| | | if ( d < std::numeric_limits<long long>::min() ){ | |
| | | return std::numeric_limits<long long>::min(); | |
| | | } | |
| | | default: | |
| | | return numberLong(); | |
| | | } | |
| | | } | |
| | | | |
| inline BSONElement::BSONElement() { | | inline BSONElement::BSONElement() { | |
| static char z = 0; | | static char z = 0; | |
| data = &z; | | data = &z; | |
| fieldNameSize_ = 0; | | fieldNameSize_ = 0; | |
| totalSize = 1; | | totalSize = 1; | |
| } | | } | |
| | | | |
| } | | } | |
| | | | |
End of changes. 13 change blocks. |
| 22 lines changed or deleted | | 89 lines changed or added | |
|
| bsonobj.h | | bsonobj.h | |
| | | | |
| skipping to change at line 20 | | skipping to change at line 20 | |
| * | | * | |
| * Unless required by applicable law or agreed to in writing, software | | * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | | * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli
ed. | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli
ed. | |
| * See the License for the specific language governing permissions and | | * See the License for the specific language governing permissions and | |
| * limitations under the License. | | * limitations under the License. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
|
| #include <boost/noncopyable.hpp> | | | |
| #include <boost/intrusive_ptr.hpp> | | #include <boost/intrusive_ptr.hpp> | |
|
| | | #include <boost/noncopyable.hpp> | |
| #include <set> | | #include <set> | |
| #include <list> | | #include <list> | |
|
| | | #include <string> | |
| #include <vector> | | #include <vector> | |
|
| #include "util/atomic_int.h" | | | |
| #include "util/builder.h" | | #include "mongo/bson/bsonelement.h" | |
| #include "stringdata.h" | | #include "mongo/bson/stringdata.h" | |
| | | #include "mongo/bson/util/atomic_int.h" | |
| | | #include "mongo/bson/util/builder.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| typedef std::set< BSONElement, BSONElementCmpWithoutField > BSONElement
Set; | | typedef std::set< BSONElement, BSONElementCmpWithoutField > BSONElement
Set; | |
| typedef std::multiset< BSONElement, BSONElementCmpWithoutField > BSONEl
ementMSet; | | typedef std::multiset< BSONElement, BSONElementCmpWithoutField > BSONEl
ementMSet; | |
| | | | |
| /** | | /** | |
| C++ representation of a "BSON" object -- that is, an extended JSON-s
tyle | | C++ representation of a "BSON" object -- that is, an extended JSON-s
tyle | |
| object in a binary representation. | | object in a binary representation. | |
| | | | |
| | | | |
| skipping to change at line 91 | | skipping to change at line 94 | |
| | | | |
| /** Construct a BSONObj from data in the proper format. | | /** Construct a BSONObj from data in the proper format. | |
| * Use this constructor when you want BSONObj to free(holder) when
it is no longer needed | | * Use this constructor when you want BSONObj to free(holder) when
it is no longer needed | |
| * BSONObj::Holder has an extra 4 bytes for a ref-count before the
start of the object | | * BSONObj::Holder has an extra 4 bytes for a ref-count before the
start of the object | |
| */ | | */ | |
| class Holder; | | class Holder; | |
| explicit BSONObj(Holder* holder) { | | explicit BSONObj(Holder* holder) { | |
| init(holder); | | init(holder); | |
| } | | } | |
| | | | |
|
| explicit BSONObj(const Record *r); | | | |
| | | | |
| /** Construct an empty BSONObj -- that is, {}. */ | | /** Construct an empty BSONObj -- that is, {}. */ | |
| BSONObj(); | | BSONObj(); | |
| | | | |
|
| | | static BSONObj make( const Record* r ); | |
| | | | |
| ~BSONObj() { | | ~BSONObj() { | |
| _objdata = 0; // defensive | | _objdata = 0; // defensive | |
| } | | } | |
| | | | |
| /** | | /** | |
| A BSONObj can use a buffer it "owns" or one it does not. | | A BSONObj can use a buffer it "owns" or one it does not. | |
| | | | |
| OWNED CASE | | OWNED CASE | |
| If the BSONObj owns the buffer, the buffer can be shared among s
everal BSONObj's (by assignment). | | If the BSONObj owns the buffer, the buffer can be shared among s
everal BSONObj's (by assignment). | |
| In this case the buffer is basically implemented as a shared_ptr
. | | In this case the buffer is basically implemented as a shared_ptr
. | |
| | | | |
| skipping to change at line 242 | | skipping to change at line 245 | |
| BSONObj getObjectField(const char *name) const; | | BSONObj getObjectField(const char *name) const; | |
| | | | |
| /** @return INT_MIN if not present - does some type conversions */ | | /** @return INT_MIN if not present - does some type conversions */ | |
| int getIntField(const char *name) const; | | int getIntField(const char *name) const; | |
| | | | |
| /** @return false if not present | | /** @return false if not present | |
| @see BSONElement::trueValue() | | @see BSONElement::trueValue() | |
| */ | | */ | |
| bool getBoolField(const char *name) const; | | bool getBoolField(const char *name) const; | |
| | | | |
|
| /** | | /** @param pattern a BSON obj indicating a set of (un-dotted) field | |
| sets element field names to empty string | | * names. Element values are ignored. | |
| If a field in pattern is missing, it is omitted from the returne | | * @return a BSON obj constructed by taking the elements of this o | |
| d | | bj | |
| object. | | * that correspond to the fields in pattern. Field names of the | |
| | | * returned object are replaced with the empty string. If field in | |
| | | * pattern is missing, it is omitted from the returned object. | |
| | | * | |
| | | * Example: if this = {a : 4 , b : 5 , c : 6}) | |
| | | * this.extractFieldsUnDotted({a : 1 , c : 1}) -> {"" : 4 , "" : | |
| | | 6 } | |
| | | * this.extractFieldsUnDotted({b : "blah"}) -> {"" : 5} | |
| | | * | |
| */ | | */ | |
|
| BSONObj extractFieldsUnDotted(BSONObj pattern) const; | | BSONObj extractFieldsUnDotted(const BSONObj& pattern) const; | |
| | | | |
| /** extract items from object which match a pattern object. | | /** extract items from object which match a pattern object. | |
| e.g., if pattern is { x : 1, y : 1 }, builds an object with | | e.g., if pattern is { x : 1, y : 1 }, builds an object with | |
| x and y elements of this object, if they are present. | | x and y elements of this object, if they are present. | |
| returns elements with original field names | | returns elements with original field names | |
| */ | | */ | |
| BSONObj extractFields(const BSONObj &pattern , bool fillWithNull=fa
lse) const; | | BSONObj extractFields(const BSONObj &pattern , bool fillWithNull=fa
lse) const; | |
| | | | |
| BSONObj filterFieldsUndotted(const BSONObj &filter, bool inFilter)
const; | | BSONObj filterFieldsUndotted(const BSONObj &filter, bool inFilter)
const; | |
| | | | |
| | | | |
| skipping to change at line 316 | | skipping to change at line 326 | |
| bool operator>( const BSONObj& other ) const { return woCompare( ot
her ) > 0; } | | bool operator>( const BSONObj& other ) const { return woCompare( ot
her ) > 0; } | |
| bool operator>=( const BSONObj& other ) const { return woCompare( o
ther ) >= 0; } | | bool operator>=( const BSONObj& other ) const { return woCompare( o
ther ) >= 0; } | |
| | | | |
| /** | | /** | |
| * @param useDotted whether to treat sort key fields as possibly do
tted and expand into them | | * @param useDotted whether to treat sort key fields as possibly do
tted and expand into them | |
| */ | | */ | |
| int woSortOrder( const BSONObj& r , const BSONObj& sortKey , bool u
seDotted=false ) const; | | int woSortOrder( const BSONObj& r , const BSONObj& sortKey , bool u
seDotted=false ) const; | |
| | | | |
| bool equal(const BSONObj& r) const; | | bool equal(const BSONObj& r) const; | |
| | | | |
|
| | | /** | |
| | | * @param otherObj | |
| | | * @return true if 'this' is a prefix of otherObj- in other words i | |
| | | f | |
| | | * otherObj contains the same field names and field vals in the sam | |
| | | e | |
| | | * order as 'this', plus optionally some additional elements. | |
| | | */ | |
| | | bool isPrefixOf( const BSONObj& otherObj ) const; | |
| | | | |
| /** This is "shallow equality" -- ints and doubles won't match. fo
r a | | /** This is "shallow equality" -- ints and doubles won't match. fo
r a | |
| deep equality test use woCompare (which is slower). | | deep equality test use woCompare (which is slower). | |
| */ | | */ | |
| bool binaryEqual(const BSONObj& r) const { | | bool binaryEqual(const BSONObj& r) const { | |
| int os = objsize(); | | int os = objsize(); | |
| if ( os == r.objsize() ) { | | if ( os == r.objsize() ) { | |
| return (os == 0 || memcmp(objdata(),r.objdata(),os)==0); | | return (os == 0 || memcmp(objdata(),r.objdata(),os)==0); | |
| } | | } | |
| return false; | | return false; | |
| } | | } | |
| | | | |
End of changes. 9 change blocks. |
| 12 lines changed or deleted | | 33 lines changed or added | |
|
| bsonobjbuilder.h | | bsonobjbuilder.h | |
| | | | |
| skipping to change at line 25 | | skipping to change at line 25 | |
| * | | * | |
| * Unless required by applicable law or agreed to in writing, software | | * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | | * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli
ed. | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli
ed. | |
| * See the License for the specific language governing permissions and | | * See the License for the specific language governing permissions and | |
| * limitations under the License. | | * limitations under the License. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
|
| #include <limits> | | | |
| #include <cmath> | | | |
| #include <boost/static_assert.hpp> | | #include <boost/static_assert.hpp> | |
|
| #if defined(MONGO_EXPOSE_MACROS) | | #include <cmath> | |
| #define verify MONGO_verify | | #include <limits> | |
| #endif | | | |
| #include "bsonelement.h" | | #include "mongo/bson/bsonelement.h" | |
| #include "bsonobj.h" | | #include "mongo/bson/bsonobj.h" | |
| #include "bsonmisc.h" | | #include "mongo/bson/bsonmisc.h" | |
| | | #include "mongo/bson/bson_builder_base.h" | |
| | | | |
| #if defined(_DEBUG) && defined(MONGO_EXPOSE_MACROS) | | #if defined(_DEBUG) && defined(MONGO_EXPOSE_MACROS) | |
|
| #include "../util/log.h" | | #include "mongo/util/log.h" | |
| #endif | | #endif | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| #if defined(_WIN32) | | #if defined(_WIN32) | |
| // warning: 'this' : used in base member initializer list | | // warning: 'this' : used in base member initializer list | |
| #pragma warning( disable : 4355 ) | | #pragma warning( disable : 4355 ) | |
| #endif | | #endif | |
| | | | |
| template<typename T> | | template<typename T> | |
| | | | |
| skipping to change at line 90 | | skipping to change at line 90 | |
| } | | } | |
| | | | |
| private: | | private: | |
| std::string _name; | | std::string _name; | |
| std::string _longName; | | std::string _longName; | |
| }; | | }; | |
| | | | |
| /** Utility for creating a BSONObj. | | /** Utility for creating a BSONObj. | |
| See also the BSON() and BSON_ARRAY() macros. | | See also the BSON() and BSON_ARRAY() macros. | |
| */ | | */ | |
|
| class BSONObjBuilder : boost::noncopyable { | | class BSONObjBuilder : public BSONBuilderBase, private boost::noncopyab
le { | |
| public: | | public: | |
| /** @param initsize this is just a hint as to the final size of the
object */ | | /** @param initsize this is just a hint as to the final size of the
object */ | |
| BSONObjBuilder(int initsize=512) : _b(_buf), _buf(initsize + sizeof
(unsigned)), _offset( sizeof(unsigned) ), _s( this ) , _tracker(0) , _doneC
alled(false) { | | BSONObjBuilder(int initsize=512) : _b(_buf), _buf(initsize + sizeof
(unsigned)), _offset( sizeof(unsigned) ), _s( this ) , _tracker(0) , _doneC
alled(false) { | |
| _b.appendNum((unsigned)0); // ref-count | | _b.appendNum((unsigned)0); // ref-count | |
| _b.skip(4); /*leave room for size field and ref-count*/ | | _b.skip(4); /*leave room for size field and ref-count*/ | |
| } | | } | |
| | | | |
| /** @param baseBuilder construct a BSONObjBuilder using an existing
BufBuilder | | /** @param baseBuilder construct a BSONObjBuilder using an existing
BufBuilder | |
| * This is for more efficient adding of subobjects/arrays. See doc
s for subobjStart for example. | | * This is for more efficient adding of subobjects/arrays. See doc
s for subobjStart for example. | |
| */ | | */ | |
| | | | |
| skipping to change at line 261 | | skipping to change at line 261 | |
| */ | | */ | |
| BSONObjBuilder& appendNumber( const StringData& fieldName , int n )
{ | | BSONObjBuilder& appendNumber( const StringData& fieldName , int n )
{ | |
| return append( fieldName , n ); | | return append( fieldName , n ); | |
| } | | } | |
| | | | |
| BSONObjBuilder& appendNumber( const StringData& fieldName , double
d ) { | | BSONObjBuilder& appendNumber( const StringData& fieldName , double
d ) { | |
| return append( fieldName , d ); | | return append( fieldName , d ); | |
| } | | } | |
| | | | |
| BSONObjBuilder& appendNumber( const StringData& fieldName , size_t
n ) { | | BSONObjBuilder& appendNumber( const StringData& fieldName , size_t
n ) { | |
|
| static size_t maxInt = (size_t)pow( 2.0 , 30.0 ); | | static const size_t maxInt = ( 1 << 30 ); | |
| | | | |
| if ( n < maxInt ) | | if ( n < maxInt ) | |
|
| append( fieldName , (int)n ); | | append( fieldName, static_cast<int>( n ) ); | |
| else | | else | |
|
| append( fieldName , (long long)n ); | | append( fieldName, static_cast<long long>( n ) ); | |
| return *this; | | return *this; | |
| } | | } | |
| | | | |
|
| BSONObjBuilder& appendNumber( const StringData& fieldName , long lo | | BSONObjBuilder& appendNumber( const StringData& fieldName, long lon | |
| ng l ) { | | g llNumber ) { | |
| static long long maxInt = (int)pow( 2.0 , 30.0 ); | | static const long long maxInt = ( 1LL << 30 ); | |
| static long long maxDouble = (long long)pow( 2.0 , 40.0 ); | | static const long long maxDouble = ( 1LL << 40 ); | |
| long long x = l >= 0 ? l : -l; | | | |
| if ( x < maxInt ) | | long long nonNegative = llNumber >= 0 ? llNumber : -llNumber; | |
| append( fieldName , (int)l ); | | if ( nonNegative < maxInt ) | |
| else if ( x < maxDouble ) | | append( fieldName, static_cast<int>( llNumber ) ); | |
| append( fieldName , (double)l ); | | else if ( nonNegative < maxDouble ) | |
| | | append( fieldName, static_cast<double>( llNumber ) ); | |
| else | | else | |
|
| append( fieldName , l ); | | append( fieldName, llNumber ); | |
| return *this; | | return *this; | |
| } | | } | |
| | | | |
| /** Append a double element */ | | /** Append a double element */ | |
| BSONObjBuilder& append(const StringData& fieldName, double n) { | | BSONObjBuilder& append(const StringData& fieldName, double n) { | |
| _b.appendNum((char) NumberDouble); | | _b.appendNum((char) NumberDouble); | |
| _b.appendStr(fieldName); | | _b.appendStr(fieldName); | |
| _b.appendNum(n); | | _b.appendNum(n); | |
| return *this; | | return *this; | |
| } | | } | |
| | | | |
| skipping to change at line 414 | | skipping to change at line 415 | |
| } | | } | |
| | | | |
| BSONObjBuilder& appendSymbol(const StringData& fieldName, const Str
ingData& symbol) { | | BSONObjBuilder& appendSymbol(const StringData& fieldName, const Str
ingData& symbol) { | |
| _b.appendNum((char) Symbol); | | _b.appendNum((char) Symbol); | |
| _b.appendStr(fieldName); | | _b.appendStr(fieldName); | |
| _b.appendNum((int) symbol.size()+1); | | _b.appendNum((int) symbol.size()+1); | |
| _b.appendStr(symbol); | | _b.appendStr(symbol); | |
| return *this; | | return *this; | |
| } | | } | |
| | | | |
|
| | | /** Implements builder interface but no-op in ObjBuilder */ | |
| | | void appendNull() { | |
| | | msgasserted(16234, "Invalid call to appendNull in BSONObj Build | |
| | | er."); | |
| | | } | |
| | | | |
| /** Append a Null element to the object */ | | /** Append a Null element to the object */ | |
| BSONObjBuilder& appendNull( const StringData& fieldName ) { | | BSONObjBuilder& appendNull( const StringData& fieldName ) { | |
| _b.appendNum( (char) jstNULL ); | | _b.appendNum( (char) jstNULL ); | |
| _b.appendStr( fieldName ); | | _b.appendStr( fieldName ); | |
| return *this; | | return *this; | |
| } | | } | |
| | | | |
| // Append an element that is less than all other keys. | | // Append an element that is less than all other keys. | |
| BSONObjBuilder& appendMinKey( const StringData& fieldName ) { | | BSONObjBuilder& appendMinKey( const StringData& fieldName ) { | |
| _b.appendNum( (char) MinKey ); | | _b.appendNum( (char) MinKey ); | |
| | | | |
| skipping to change at line 644 | | skipping to change at line 650 | |
| BSONObjBuilder& operator<<( const BSONFieldValue<T>& v ) { | | BSONObjBuilder& operator<<( const BSONFieldValue<T>& v ) { | |
| append( v.name().c_str() , v.value() ); | | append( v.name().c_str() , v.value() ); | |
| return *this; | | return *this; | |
| } | | } | |
| | | | |
| BSONObjBuilder& operator<<( const BSONElement& e ){ | | BSONObjBuilder& operator<<( const BSONElement& e ){ | |
| append( e ); | | append( e ); | |
| return *this; | | return *this; | |
| } | | } | |
| | | | |
|
| | | bool isArray() const { | |
| | | return false; | |
| | | } | |
| | | | |
| /** @return true if we are using our own bufbuilder, and not an alt
ernate that was given to us in our constructor */ | | /** @return true if we are using our own bufbuilder, and not an alt
ernate that was given to us in our constructor */ | |
| bool owned() const { return &_b == &_buf; } | | bool owned() const { return &_b == &_buf; } | |
| | | | |
| BSONObjIterator iterator() const ; | | BSONObjIterator iterator() const ; | |
| | | | |
| bool hasField( const StringData& name ) const ; | | bool hasField( const StringData& name ) const ; | |
| | | | |
| int len() const { return _b.len(); } | | int len() const { return _b.len(); } | |
| | | | |
| BufBuilder& bb() { return _b; } | | BufBuilder& bb() { return _b; } | |
| | | | |
| skipping to change at line 682 | | skipping to change at line 692 | |
| BufBuilder _buf; | | BufBuilder _buf; | |
| int _offset; | | int _offset; | |
| BSONObjBuilderValueStream _s; | | BSONObjBuilderValueStream _s; | |
| BSONSizeTracker * _tracker; | | BSONSizeTracker * _tracker; | |
| bool _doneCalled; | | bool _doneCalled; | |
| | | | |
| static const std::string numStrs[100]; // cache of 0 to 99 inclusiv
e | | static const std::string numStrs[100]; // cache of 0 to 99 inclusiv
e | |
| static bool numStrsReady; // for static init safety. see comments i
n db/jsobj.cpp | | static bool numStrsReady; // for static init safety. see comments i
n db/jsobj.cpp | |
| }; | | }; | |
| | | | |
|
| class BSONArrayBuilder : boost::noncopyable { | | class BSONArrayBuilder : public BSONBuilderBase, private boost::noncopy
able { | |
| public: | | public: | |
| BSONArrayBuilder() : _i(0), _b() {} | | BSONArrayBuilder() : _i(0), _b() {} | |
| BSONArrayBuilder( BufBuilder &_b ) : _i(0), _b(_b) {} | | BSONArrayBuilder( BufBuilder &_b ) : _i(0), _b(_b) {} | |
| BSONArrayBuilder( int initialSize ) : _i(0), _b(initialSize) {} | | BSONArrayBuilder( int initialSize ) : _i(0), _b(initialSize) {} | |
| | | | |
| template <typename T> | | template <typename T> | |
| BSONArrayBuilder& append(const T& x) { | | BSONArrayBuilder& append(const T& x) { | |
| _b.append(num(), x); | | _b.append(num(), x); | |
| return *this; | | return *this; | |
| } | | } | |
| | | | |
| BSONArrayBuilder& append(const BSONElement& e) { | | BSONArrayBuilder& append(const BSONElement& e) { | |
| _b.appendAs(e, num()); | | _b.appendAs(e, num()); | |
| return *this; | | return *this; | |
| } | | } | |
| | | | |
|
| | | BSONArrayBuilder& operator<<(const BSONElement& e) { | |
| | | return append(e); | |
| | | } | |
| | | | |
| template <typename T> | | template <typename T> | |
| BSONArrayBuilder& operator<<(const T& x) { | | BSONArrayBuilder& operator<<(const T& x) { | |
| return append(x); | | return append(x); | |
| } | | } | |
| | | | |
| void appendNull() { | | void appendNull() { | |
| _b.appendNull(num()); | | _b.appendNull(num()); | |
| } | | } | |
| | | | |
| /** | | /** | |
| * destructive - ownership moves to returned BSONArray | | * destructive - ownership moves to returned BSONArray | |
| * @return owned BSONArray | | * @return owned BSONArray | |
| */ | | */ | |
| BSONArray arr() { return BSONArray(_b.obj()); } | | BSONArray arr() { return BSONArray(_b.obj()); } | |
|
| | | BSONObj obj() { return _b.obj(); } | |
| | | | |
| BSONObj done() { return _b.done(); } | | BSONObj done() { return _b.done(); } | |
| | | | |
| void doneFast() { _b.doneFast(); } | | void doneFast() { _b.doneFast(); } | |
| | | | |
|
| | | BSONArrayBuilder& append(const StringData& name, int n) { | |
| | | fill( name ); | |
| | | append( n ); | |
| | | return *this; | |
| | | } | |
| | | | |
| | | BSONArrayBuilder& append(const StringData& name, long long n) { | |
| | | fill( name ); | |
| | | append( n ); | |
| | | return *this; | |
| | | } | |
| | | | |
| | | BSONArrayBuilder& append(const StringData& name, double n) { | |
| | | fill( name ); | |
| | | append( n ); | |
| | | return *this; | |
| | | } | |
| | | | |
| template <typename T> | | template <typename T> | |
| BSONArrayBuilder& append(const StringData& name, const T& x) { | | BSONArrayBuilder& append(const StringData& name, const T& x) { | |
| fill( name ); | | fill( name ); | |
| append( x ); | | append( x ); | |
| return *this; | | return *this; | |
| } | | } | |
| | | | |
| template < class T > | | template < class T > | |
| BSONArrayBuilder& append( const std::list< T >& vals ); | | BSONArrayBuilder& append( const std::list< T >& vals ); | |
| | | | |
| | | | |
| skipping to change at line 752 | | skipping to change at line 785 | |
| return _b.subarrayStart( num() ); | | return _b.subarrayStart( num() ); | |
| } | | } | |
| | | | |
| // These should only be used where you really need interface compat
ability with BSONObjBuilder | | // These should only be used where you really need interface compat
ability with BSONObjBuilder | |
| // Currently they are only used by update.cpp and it should probabl
y stay that way | | // Currently they are only used by update.cpp and it should probabl
y stay that way | |
| BufBuilder &subobjStart( const StringData& name ) { | | BufBuilder &subobjStart( const StringData& name ) { | |
| fill( name ); | | fill( name ); | |
| return _b.subobjStart( num() ); | | return _b.subobjStart( num() ); | |
| } | | } | |
| | | | |
|
| BufBuilder &subarrayStart( const char *name ) { | | BufBuilder &subarrayStart( const StringData& name ) { | |
| fill( name ); | | fill( name ); | |
| return _b.subarrayStart( num() ); | | return _b.subarrayStart( num() ); | |
| } | | } | |
| | | | |
|
| void appendArray( const StringData& name, BSONObj subObj ) { | | BSONArrayBuilder& appendArray( const StringData& name, const BSONOb
j& subObj ) { | |
| fill( name ); | | fill( name ); | |
| _b.appendArray( num(), subObj ); | | _b.appendArray( num(), subObj ); | |
|
| | | return *this; | |
| } | | } | |
| | | | |
|
| void appendAs( const BSONElement &e, const char *name) { | | BSONArrayBuilder& appendAs( const BSONElement &e, const StringData&
name) { | |
| fill( name ); | | fill( name ); | |
| append( e ); | | append( e ); | |
|
| | | return *this; | |
| | | } | |
| | | | |
| | | bool isArray() const { | |
| | | return true; | |
| } | | } | |
| | | | |
| int len() const { return _b.len(); } | | int len() const { return _b.len(); } | |
| int arrSize() const { return _i; } | | int arrSize() const { return _i; } | |
| | | | |
| private: | | private: | |
| // These two are undefined privates to prevent their accidental | | // These two are undefined privates to prevent their accidental | |
| // use as we don't support unsigned ints in BSON | | // use as we don't support unsigned ints in BSON | |
| BSONObjBuilder& append(const StringData& fieldName, unsigned int va
l); | | BSONObjBuilder& append(const StringData& fieldName, unsigned int va
l); | |
| BSONObjBuilder& append(const StringData& fieldName, unsigned long l
ong val); | | BSONObjBuilder& append(const StringData& fieldName, unsigned long l
ong val); | |
| | | | |
| skipping to change at line 791 | | skipping to change at line 830 | |
| fill(n); | | fill(n); | |
| } | | } | |
| | | | |
| void fill (int upTo){ | | void fill (int upTo){ | |
| // if this is changed make sure to update error message and jst
ests/set7.js | | // if this is changed make sure to update error message and jst
ests/set7.js | |
| const int maxElems = 1500000; | | const int maxElems = 1500000; | |
| BOOST_STATIC_ASSERT(maxElems < (BSONObjMaxUserSize/10)); | | BOOST_STATIC_ASSERT(maxElems < (BSONObjMaxUserSize/10)); | |
| uassert(15891, "can't backfill array to larger than 1,500,000 e
lements", upTo <= maxElems); | | uassert(15891, "can't backfill array to larger than 1,500,000 e
lements", upTo <= maxElems); | |
| | | | |
| while( _i < upTo ) | | while( _i < upTo ) | |
|
| append( nullElt() ); | | appendNull(); | |
| } | | | |
| | | | |
| static BSONElement nullElt() { | | | |
| static BSONObj n = nullObj(); | | | |
| return n.firstElement(); | | | |
| } | | | |
| | | | |
| static BSONObj nullObj() { | | | |
| BSONObjBuilder _b; | | | |
| _b.appendNull( "" ); | | | |
| return _b.obj(); | | | |
| } | | } | |
| | | | |
| std::string num() { return _b.numStr(_i++); } | | std::string num() { return _b.numStr(_i++); } | |
| int _i; | | int _i; | |
| BSONObjBuilder _b; | | BSONObjBuilder _b; | |
| }; | | }; | |
| | | | |
| template < class T > | | template < class T > | |
| inline BSONObjBuilder& BSONObjBuilder::append( const StringData& fieldN
ame, const std::vector< T >& vals ) { | | inline BSONObjBuilder& BSONObjBuilder::append( const StringData& fieldN
ame, const std::vector< T >& vals ) { | |
| BSONObjBuilder arrBuilder; | | BSONObjBuilder arrBuilder; | |
| | | | |
End of changes. 21 change blocks. |
| 39 lines changed or deleted | | 68 lines changed or added | |
|
| btree.h | | btree.h | |
| | | | |
| skipping to change at line 1018 | | skipping to change at line 1018 | |
| * position after a write (checkLoc()). A recorded btree position cons
ists of a btree bucket, | | * position after a write (checkLoc()). A recorded btree position cons
ists of a btree bucket, | |
| * bucket key offset, and unique btree key. To relocate a unique btree
key, a BtreeCursor first | | * bucket key offset, and unique btree key. To relocate a unique btree
key, a BtreeCursor first | |
| * checks the btree key at its recorded btree bucket and bucket key off
set. If the key at that | | * checks the btree key at its recorded btree bucket and bucket key off
set. If the key at that | |
| * location does not match the recorded btree key, and an adjacent key
also fails to match, | | * location does not match the recorded btree key, and an adjacent key
also fails to match, | |
| * the recorded key (or the next existing key following it) is located
in the btree using binary | | * the recorded key (or the next existing key following it) is located
in the btree using binary | |
| * search. If the recorded btree bucket is invalidated, the initial re
corded bucket check is | | * search. If the recorded btree bucket is invalidated, the initial re
corded bucket check is | |
| * not attempted (see SERVER-4575). | | * not attempted (see SERVER-4575). | |
| */ | | */ | |
| class BtreeCursor : public Cursor { | | class BtreeCursor : public Cursor { | |
| protected: | | protected: | |
|
| | | BtreeCursor( NamespaceDetails* nsd , int theIndexNo, const IndexDet | |
| | | ails& idxDetails ); | |
| | | /* | |
| 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, | | BtreeCursor( NamespaceDetails *_d, int _idxNo, const IndexDetails&
_id, | |
| const shared_ptr< FieldRangeVector > &_bounds, int sing
leIntervalLimit, | | const shared_ptr< FieldRangeVector > &_bounds, int sing
leIntervalLimit, | |
| int _direction ); | | int _direction ); | |
|
| | | */ | |
| | | | |
| | | virtual void init( const BSONObj &startKey, const BSONObj &endKey, | |
| | | bool endKeyInclusive, int direction ); | |
| | | virtual void init( const shared_ptr< FieldRangeVector > &_bounds, i | |
| | | nt singleIntervalLimit, int _direction ); | |
| | | | |
| | | private: | |
| | | void _finishConstructorInit(); | |
| | | static BtreeCursor* make( NamespaceDetails * nsd , int idxNo , cons | |
| | | t IndexDetails& indexDetails ); | |
| | | | |
| public: | | public: | |
| virtual ~BtreeCursor(); | | virtual ~BtreeCursor(); | |
| /** makes an appropriate subclass depending on the index version */ | | /** makes an appropriate subclass depending on the index version */ | |
| static BtreeCursor* make( NamespaceDetails *_d, const IndexDetails&
, const BSONObj &startKey, const BSONObj &endKey, bool endKeyInclusive, int
direction ); | | static BtreeCursor* make( NamespaceDetails *_d, const IndexDetails&
, const BSONObj &startKey, const BSONObj &endKey, bool endKeyInclusive, int
direction ); | |
| static BtreeCursor* make( NamespaceDetails *_d, const IndexDetails&
_id, const shared_ptr< FieldRangeVector > &_bounds, int _direction ); | | static BtreeCursor* make( NamespaceDetails *_d, const IndexDetails&
_id, const shared_ptr< FieldRangeVector > &_bounds, int _direction ); | |
| static BtreeCursor* make( NamespaceDetails *_d, int _idxNo, const I
ndexDetails&, const BSONObj &startKey, const BSONObj &endKey, bool endKeyIn
clusive, int direction ); | | static BtreeCursor* make( NamespaceDetails *_d, int _idxNo, const I
ndexDetails&, const BSONObj &startKey, const BSONObj &endKey, bool endKeyIn
clusive, int direction ); | |
| static BtreeCursor* make( NamespaceDetails *_d, int _idxNo, const I
ndexDetails& _id, | | static BtreeCursor* make( NamespaceDetails *_d, int _idxNo, const I
ndexDetails& _id, | |
| const shared_ptr< FieldRangeVector > &_bou
nds, | | const shared_ptr< FieldRangeVector > &_bou
nds, | |
| int singleIntervalLimit, int _direction ); | | int singleIntervalLimit, int _direction ); | |
| | | | |
| | | | |
| skipping to change at line 1068 | | skipping to change at line 1079 | |
| verify( !bucket.isNull() ); | | verify( !bucket.isNull() ); | |
| const _KeyNode& kn = keyNode(keyOfs); | | const _KeyNode& kn = keyNode(keyOfs); | |
| verify( kn.isUsed() ); | | verify( kn.isUsed() ); | |
| return kn; | | return kn; | |
| }*/ | | }*/ | |
| | | | |
| /** returns BSONObj() if ofs is out of range */ | | /** returns BSONObj() if ofs is out of range */ | |
| virtual BSONObj keyAt(int ofs) const = 0; | | virtual BSONObj keyAt(int ofs) const = 0; | |
| | | | |
| virtual BSONObj currKey() const = 0; | | virtual BSONObj currKey() const = 0; | |
|
| virtual BSONObj indexKeyPattern() { return indexDetails.keyPattern(
); } | | virtual BSONObj indexKeyPattern() { return _order; } | |
| | | | |
| virtual void aboutToDeleteBucket(const DiskLoc& b) { | | virtual void aboutToDeleteBucket(const DiskLoc& b) { | |
| if ( bucket == b ) | | if ( bucket == b ) | |
| keyOfs = -1; | | keyOfs = -1; | |
| } | | } | |
| | | | |
| virtual DiskLoc currLoc() = 0; // { return !bucket.isNull() ? _cur
rKeyNode().recordLoc : DiskLoc(); } | | virtual DiskLoc currLoc() = 0; // { return !bucket.isNull() ? _cur
rKeyNode().recordLoc : DiskLoc(); } | |
| virtual DiskLoc refLoc() { return currLoc(); } | | virtual DiskLoc refLoc() { return currLoc(); } | |
| virtual Record* _current() { return currLoc().rec(); } | | virtual Record* _current() { return currLoc().rec(); } | |
|
| virtual BSONObj current() { return BSONObj(_current()); } | | virtual BSONObj current() { return BSONObj::make(_current()); } | |
| virtual string toString(); | | virtual string toString(); | |
| | | | |
| BSONObj prettyKey( const BSONObj &key ) const { | | BSONObj prettyKey( const BSONObj &key ) const { | |
| return key.replaceFieldNames( indexDetails.keyPattern() ).clien
tReadable(); | | return key.replaceFieldNames( indexDetails.keyPattern() ).clien
tReadable(); | |
| } | | } | |
| | | | |
| virtual BSONObj prettyIndexBounds() const; | | virtual BSONObj prettyIndexBounds() const; | |
| | | | |
| virtual CoveredIndexMatcher *matcher() const { return _matcher.get(
); } | | virtual CoveredIndexMatcher *matcher() const { return _matcher.get(
); } | |
| virtual shared_ptr< CoveredIndexMatcher > matcherPtr() const { retu
rn _matcher; } | | virtual shared_ptr< CoveredIndexMatcher > matcherPtr() const { retu
rn _matcher; } | |
| | | | |
| skipping to change at line 1132 | | skipping to change at line 1143 | |
| virtual DiskLoc _locate(const BSONObj& key, const DiskLoc& loc) = 0
; | | virtual DiskLoc _locate(const BSONObj& key, const DiskLoc& loc) = 0
; | |
| virtual DiskLoc _advance(const DiskLoc& thisLoc, int& keyOfs, int d
irection, const char *caller) = 0; | | virtual DiskLoc _advance(const DiskLoc& thisLoc, int& keyOfs, int d
irection, const char *caller) = 0; | |
| virtual void _advanceTo(DiskLoc &thisLoc, int &keyOfs, const BSONOb
j &keyBegin, int keyBeginLen, bool afterKey, const vector< const BSONElemen
t * > &keyEnd, const vector< bool > &keyEndInclusive, const Ordering &order
, int direction ) = 0; | | virtual void _advanceTo(DiskLoc &thisLoc, int &keyOfs, const BSONOb
j &keyBegin, int keyBeginLen, bool afterKey, const vector< const BSONElemen
t * > &keyEnd, const vector< bool > &keyEndInclusive, const Ordering &order
, int direction ) = 0; | |
| | | | |
| /** set initial bucket */ | | /** set initial bucket */ | |
| void initWithoutIndependentFieldRanges(); | | void initWithoutIndependentFieldRanges(); | |
| | | | |
| /** if afterKey is true, we want the first key with values of the k
eyBegin fields greater than keyBegin */ | | /** if afterKey is true, we want the first key with values of the k
eyBegin fields greater than keyBegin */ | |
| void advanceTo( const BSONObj &keyBegin, int keyBeginLen, bool afte
rKey, const vector< const BSONElement * > &keyEnd, const vector< bool > &ke
yEndInclusive ); | | void advanceTo( const BSONObj &keyBegin, int keyBeginLen, bool afte
rKey, const vector< const BSONElement * > &keyEnd, const vector< bool > &ke
yEndInclusive ); | |
| | | | |
|
| set<DiskLoc> _dups; | | // these are set in the construtor | |
| NamespaceDetails * const d; | | NamespaceDetails * const d; | |
| const int idxNo; | | const int idxNo; | |
|
| | | const IndexDetails& indexDetails; | |
| | | | |
| | | // these are all set in init() | |
| | | set<DiskLoc> _dups; | |
| BSONObj startKey; | | BSONObj startKey; | |
| BSONObj endKey; | | BSONObj endKey; | |
| bool _endKeyInclusive; | | bool _endKeyInclusive; | |
| bool _multikey; // this must be updated every getmore batch in case
someone added a multikey | | bool _multikey; // this must be updated every getmore batch in case
someone added a multikey | |
|
| const IndexDetails& indexDetails; | | BSONObj _order; // this is the same as indexDetails.keyPattern() | |
| const BSONObj _order; | | Ordering _ordering; | |
| const Ordering _ordering; | | | |
| DiskLoc bucket; | | DiskLoc bucket; | |
| int keyOfs; | | int keyOfs; | |
|
| const 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; | |
|
| const shared_ptr< FieldRangeVector > _bounds; | | shared_ptr< FieldRangeVector > _bounds; | |
| auto_ptr< FieldRangeVectorIterator > _boundsIterator; | | auto_ptr< FieldRangeVectorIterator > _boundsIterator; | |
| shared_ptr< CoveredIndexMatcher > _matcher; | | shared_ptr< CoveredIndexMatcher > _matcher; | |
| shared_ptr<Projection::KeyOnly> _keyFieldsOnly; | | shared_ptr<Projection::KeyOnly> _keyFieldsOnly; | |
| bool _independentFieldRanges; | | bool _independentFieldRanges; | |
| long long _nscanned; | | long long _nscanned; | |
| }; | | }; | |
| | | | |
|
| /** Renames the index namespace for this btree's index. */ | | | |
| void renameIndexNamespace(const char *oldNs, const char *newNs); | | | |
| | | | |
| /** | | /** | |
| * give us a writable version of the btree bucket (declares write inten
t). | | * give us a writable version of the btree bucket (declares write inten
t). | |
| * note it is likely more efficient to declare write intent on somethin
g smaller when you can. | | * note it is likely more efficient to declare write intent on somethin
g smaller when you can. | |
| */ | | */ | |
| template< class V > | | template< class V > | |
| BtreeBucket<V> * DiskLoc::btreemod() const { | | BtreeBucket<V> * DiskLoc::btreemod() const { | |
| verify( _a != -1 ); | | verify( _a != -1 ); | |
| BtreeBucket<V> *b = const_cast< BtreeBucket<V> * >( btree<V>() ); | | BtreeBucket<V> *b = const_cast< BtreeBucket<V> * >( btree<V>() ); | |
| return static_cast< BtreeBucket<V>* >( getDur().writingPtr( b, V::B
ucketSize ) ); | | return static_cast< BtreeBucket<V>* >( getDur().writingPtr( b, V::B
ucketSize ) ); | |
| } | | } | |
| | | | |
End of changes. 10 change blocks. |
| 11 lines changed or deleted | | 26 lines changed or added | |
|
| chunk.h | | chunk.h | |
| | | | |
| skipping to change at line 57 | | skipping to change at line 57 | |
| /** | | /** | |
| config.chunks | | config.chunks | |
| { ns : "alleyinsider.fs.chunks" , min : {} , max : {} , server : "lo
calhost:30001" } | | { ns : "alleyinsider.fs.chunks" , min : {} , max : {} , server : "lo
calhost:30001" } | |
| | | | |
| x is in a shard iff | | x is in a shard iff | |
| min <= x < max | | min <= x < max | |
| */ | | */ | |
| class Chunk : boost::noncopyable { | | class Chunk : boost::noncopyable { | |
| public: | | public: | |
| Chunk( const ChunkManager * info , BSONObj from); | | Chunk( const ChunkManager * info , BSONObj from); | |
|
| Chunk( const ChunkManager * info , const BSONObj& min, const BSONOb | | Chunk( const ChunkManager * info , | |
| j& max, const Shard& shard); | | const BSONObj& min, | |
| | | const BSONObj& max, | |
| | | const Shard& shard, | |
| | | ShardChunkVersion lastmod = ShardChunkVersion() ); | |
| | | | |
| // | | // | |
| // serialization support | | // serialization support | |
| // | | // | |
| | | | |
|
| void serialize(BSONObjBuilder& to, ShardChunkVersion myLastMod=0); | | void serialize(BSONObjBuilder& to, ShardChunkVersion myLastMod=Shar
dChunkVersion(0,OID())); | |
| | | | |
| // | | // | |
| // chunk boundary support | | // chunk boundary support | |
| // | | // | |
| | | | |
| const BSONObj& getMin() const { return _min; } | | const BSONObj& getMin() const { return _min; } | |
| const BSONObj& getMax() const { return _max; } | | const BSONObj& getMax() const { return _max; } | |
| | | | |
| // if min/max key is pos/neg infinity | | // if min/max key is pos/neg infinity | |
| bool minIsInf() const; | | bool minIsInf() const; | |
| | | | |
| skipping to change at line 299 | | skipping to change at line 303 | |
| /* config.sharding | | /* config.sharding | |
| { ns: 'alleyinsider.fs.chunks' , | | { ns: 'alleyinsider.fs.chunks' , | |
| key: { ts : 1 } , | | key: { ts : 1 } , | |
| shards: [ { min: 1, max: 100, server: a } , { min: 101, max: 200
, server : b } ] | | shards: [ { min: 1, max: 100, server: a } , { min: 101, max: 200
, server : b } ] | |
| } | | } | |
| */ | | */ | |
| class ChunkManager { | | class ChunkManager { | |
| public: | | public: | |
| typedef map<Shard,ShardChunkVersion> ShardVersionMap; | | typedef map<Shard,ShardChunkVersion> ShardVersionMap; | |
| | | | |
|
| ChunkManager( string ns , ShardKeyPattern pattern , bool unique ); | | // Loads a new chunk manager from a collection document | |
| | | ChunkManager( const BSONObj& collDoc ); | |
| | | | |
| | | // Creates an empty chunk manager for the namespace | |
| | | ChunkManager( const string& ns, const ShardKeyPattern& pattern, boo | |
| | | l unique ); | |
| | | | |
| | | // Updates a chunk manager based on an older manager | |
| | | ChunkManager( ChunkManagerPtr oldManager ); | |
| | | | |
| string getns() const { return _ns; } | | string getns() const { return _ns; } | |
| | | | |
|
| int numChunks() const { return _chunkMap.size(); } | | const ShardKeyPattern& getShardKey() const { return _key; } | |
| | | | |
| bool hasShardKey( const BSONObj& obj ) const; | | bool hasShardKey( const BSONObj& obj ) const; | |
| | | | |
|
| void createFirstChunks( const Shard& primary , vector<BSONObj>* ini | | bool isUnique() const { return _unique; } | |
| tPoints , vector<Shard>* initShards ) const; // only call from DBConfig::sh | | | |
| ardCollection | | /** | |
| | | * 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() const { return _sequenceNumb | |
| | | er; } | |
| | | | |
| | | // | |
| | | // After constructor is invoked, we need to call loadExistingRanges | |
| | | . If this is a new | |
| | | // sharded collection, we can call createFirstChunks first. | |
| | | // | |
| | | | |
| | | // Creates new chunks based on info in chunk manager | |
| | | void createFirstChunks( const string& config, | |
| | | const Shard& primary, | |
| | | const vector<BSONObj>* initPoints, | |
| | | const vector<Shard>* initShards ); | |
| | | | |
| | | // Loads existing ranges based on info in chunk manager | |
| | | void loadExistingRanges( const string& config ); | |
| | | | |
| | | // Helpers for load | |
| | | void calcInitSplitsAndShards( const Shard& primary, | |
| | | const vector<BSONObj>* initPoints, | |
| | | const vector<Shard>* initShards, | |
| | | vector<BSONObj>* splitPoints, | |
| | | vector<Shard>* shards ) const; | |
| | | | |
| | | // | |
| | | // Methods to use once loaded / created | |
| | | // | |
| | | | |
| | | int numChunks() const { return _chunkMap.size(); } | |
| | | | |
| ChunkPtr findChunk( const BSONObj& obj ) const; | | ChunkPtr findChunk( const BSONObj& obj ) const; | |
| ChunkPtr findChunkOnServer( const Shard& shard ) const; | | ChunkPtr findChunkOnServer( const Shard& shard ) const; | |
| | | | |
|
| const ShardKeyPattern& getShardKey() const { return _key; } | | | |
| bool isUnique() const { return _unique; } | | | |
| | | | |
| void getShardsForQuery( set<Shard>& shards , const BSONObj& query )
const; | | void getShardsForQuery( set<Shard>& shards , const BSONObj& query )
const; | |
| void getAllShards( set<Shard>& all ) const; | | void getAllShards( set<Shard>& all ) const; | |
| /** @param shards set to the shards covered by the interval [min, m
ax], see SERVER-4791 */ | | /** @param shards set to the shards covered by the interval [min, m
ax], see SERVER-4791 */ | |
| void getShardsForRange(set<Shard>& shards, const BSONObj& min, cons
t BSONObj& max, bool fullKeyReq = true) const; | | void getShardsForRange(set<Shard>& shards, const BSONObj& min, cons
t BSONObj& max, bool fullKeyReq = true) const; | |
| | | | |
| ChunkMap getChunkMap() const { return _chunkMap; } | | ChunkMap getChunkMap() const { return _chunkMap; } | |
| | | | |
| /** | | /** | |
| * Returns true if, for this shard, the chunks are identical in bot
h chunk managers | | * Returns true if, for this shard, the chunks are identical in bot
h chunk managers | |
| */ | | */ | |
| | | | |
| skipping to change at line 334 | | skipping to change at line 376 | |
| bool compatibleWith( ChunkManagerPtr other, const Shard& shard ) co
nst { if( ! other ) return false; return compatibleWith( *other, shard ); } | | bool compatibleWith( ChunkManagerPtr other, const Shard& shard ) co
nst { if( ! other ) return false; return compatibleWith( *other, shard ); } | |
| | | | |
| bool compatibleWith( const Chunk& other ) const; | | bool compatibleWith( const Chunk& other ) const; | |
| bool compatibleWith( ChunkPtr other ) const { if( ! other ) return
false; return compatibleWith( *other ); } | | bool compatibleWith( ChunkPtr other ) const { if( ! other ) return
false; return compatibleWith( *other ); } | |
| | | | |
| string toString() const; | | string toString() const; | |
| | | | |
| ShardChunkVersion getVersion( const Shard& shard ) const; | | ShardChunkVersion getVersion( const Shard& shard ) const; | |
| ShardChunkVersion getVersion() const; | | ShardChunkVersion getVersion() const; | |
| | | | |
|
| /** | | | |
| * this is just an increasing number of how many ChunkManagers we h | | | |
| ave so we know if something has been updated | | | |
| */ | | | |
| unsigned long long getSequenceNumber() const { return _sequenceNumb | | | |
| er; } | | | |
| | | | |
| void getInfo( BSONObjBuilder& b ) const { | | void getInfo( BSONObjBuilder& b ) const { | |
| b.append( "key" , _key.key() ); | | b.append( "key" , _key.key() ); | |
| b.appendBool( "unique" , _unique ); | | b.appendBool( "unique" , _unique ); | |
|
| | | _version.addEpochToBSON( b, "lastmod" ); | |
| } | | } | |
| | | | |
| /** | | /** | |
| * @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 ) const; | | void drop( ChunkManagerPtr me ) const; | |
| | | | |
| void _printChunks() const; | | void _printChunks() const; | |
| | | | |
| int getCurrentDesiredChunkSize() const; | | int getCurrentDesiredChunkSize() const; | |
| | | | |
|
| private: | | | |
| ChunkManagerPtr reload(bool force=true) const; // doesn't modify se
lf! | | ChunkManagerPtr reload(bool force=true) const; // doesn't modify se
lf! | |
| | | | |
|
| // helpers for constructor | | void markMinorForReload( ShardChunkVersion majorVersion ) const; | |
| void _load(ChunkMap& chunks, set<Shard>& shards, ShardVersionMap& s | | void getMarkedMinorVersions( set<ShardChunkVersion>& minorVersions | |
| hardVersions); | | ) const; | |
| | | | |
| | | private: | |
| | | | |
| | | // helpers for loading | |
| | | | |
| | | // returns true if load was consistent | |
| | | bool _load( const string& config, ChunkMap& chunks, set<Shard>& sha | |
| | | rds, | |
| | | ShardVersionMap& shardVersions, ChunkMa | |
| | | nagerPtr oldManager); | |
| static bool _isValid(const ChunkMap& chunks); | | static bool _isValid(const ChunkMap& chunks); | |
| | | | |
|
| | | // end helpers | |
| | | | |
| // All members should be const for thread-safety | | // All members should be const for thread-safety | |
| const string _ns; | | const string _ns; | |
| const ShardKeyPattern _key; | | const ShardKeyPattern _key; | |
| const bool _unique; | | const bool _unique; | |
| | | | |
| const ChunkMap _chunkMap; | | const ChunkMap _chunkMap; | |
| const ChunkRangeManager _chunkRanges; | | const ChunkRangeManager _chunkRanges; | |
| | | | |
| const set<Shard> _shards; | | const set<Shard> _shards; | |
| | | | |
| const ShardVersionMap _shardVersions; // max version per shard | | const ShardVersionMap _shardVersions; // max version per shard | |
| | | | |
|
| ShardChunkVersion _version; // max version of any chunk | | // max version of any chunk | |
| | | ShardChunkVersion _version; | |
| | | | |
| | | // the previous manager this was based on | |
| | | // cleared after loading chunks | |
| | | ChunkManagerPtr _oldManager; | |
| | | | |
| mutable mutex _mutex; // only used with _nsLock | | mutable mutex _mutex; // only used with _nsLock | |
|
| mutable DistributedLock _nsLock; | | | |
| | | | |
| const unsigned long long _sequenceNumber; | | const unsigned long long _sequenceNumber; | |
| | | | |
|
| mutable TicketHolder _splitTickets; // number of concurrent splitVe | | // | |
| ctor we can do from a splitIfShould per collection | | // Split Heuristic info | |
| | | // | |
| | | | |
| | | class SplitHeuristics { | |
| | | public: | |
| | | | |
| | | SplitHeuristics() : | |
| | | _splitTickets( maxParallelSplits ), | |
| | | _staleMinorSetMutex( "SplitHeuristics::staleMinorSet" ), | |
| | | _staleMinorCount( 0 ) {} | |
| | | | |
| | | void markMinorForReload( const string& ns, ShardChunkVersion ma | |
| | | jorVersion ); | |
| | | void getMarkedMinorVersions( set<ShardChunkVersion>& minorVersi | |
| | | ons ); | |
| | | | |
| | | TicketHolder _splitTickets; | |
| | | | |
| | | mutex _staleMinorSetMutex; | |
| | | | |
| | | // mutex protects below | |
| | | int _staleMinorCount; | |
| | | set<ShardChunkVersion> _staleMinorSet; | |
| | | | |
| | | // Test whether we should split once data * splitTestFactor > c | |
| | | hunkSize (approximately) | |
| | | static const int splitTestFactor = 5; | |
| | | // Maximum number of parallel threads requesting a split | |
| | | static const int maxParallelSplits = 5; | |
| | | | |
| | | // The idea here is that we're over-aggressive on split testing | |
| | | by a factor of | |
| | | // splitTestFactor, so we can safely wait until we get to split | |
| | | TestFactor invalid splits | |
| | | // before changing. Unfortunately, we also potentially over-re | |
| | | quest the splits by a | |
| | | // factor of maxParallelSplits, but since the factors are ident | |
| | | ical it works out | |
| | | // (for now) for parallel or sequential oversplitting. | |
| | | // TODO: Make splitting a separate thread with notifications? | |
| | | static const int staleMinorReloadThreshold = maxParallelSplits; | |
| | | | |
| | | }; | |
| | | | |
| | | mutable SplitHeuristics _splitHeuristics; | |
| | | | |
| | | // | |
| | | // End split heuristics | |
| | | // | |
| | | | |
| friend class Chunk; | | friend class Chunk; | |
| friend class ChunkRangeManager; // only needed for CRM::assertValid
() | | friend class ChunkRangeManager; // only needed for CRM::assertValid
() | |
| static AtomicUInt NextSequenceNumber; | | static AtomicUInt NextSequenceNumber; | |
| | | | |
| /** Just for testing */ | | /** Just for testing */ | |
| friend class TestableChunkManager; | | friend class TestableChunkManager; | |
| ChunkManager(); | | ChunkManager(); | |
| }; | | }; | |
| | | | |
| | | | |
End of changes. 14 change blocks. |
| 26 lines changed or deleted | | 126 lines changed or added | |
|
| clientcursor.h | | clientcursor.h | |
| | | | |
| skipping to change at line 46 | | skipping to change at line 46 | |
| #include "diskloc.h" | | #include "diskloc.h" | |
| #include "dbhelpers.h" | | #include "dbhelpers.h" | |
| #include "matcher.h" | | #include "matcher.h" | |
| #include "projection.h" | | #include "projection.h" | |
| #include "s/d_chunk_manager.h" | | #include "s/d_chunk_manager.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| typedef boost::recursive_mutex::scoped_lock recursive_scoped_lock; | | typedef boost::recursive_mutex::scoped_lock recursive_scoped_lock; | |
| typedef long long CursorId; /* passed to the client so it can send back
on getMore */ | | typedef long long CursorId; /* passed to the client so it can send back
on getMore */ | |
|
| | | static const CursorId INVALID_CURSOR_ID = -1; // But see SERVER-5726. | |
| class Cursor; /* internal server cursor base class */ | | class Cursor; /* internal server cursor base class */ | |
| class ClientCursor; | | class ClientCursor; | |
| class ParsedQuery; | | class ParsedQuery; | |
| | | | |
| struct ByLocKey { | | struct ByLocKey { | |
| | | | |
| ByLocKey( const DiskLoc & l , const CursorId& i ) : loc(l), id(i) {
} | | ByLocKey( const DiskLoc & l , const CursorId& i ) : loc(l), id(i) {
} | |
| | | | |
| static ByLocKey min( const DiskLoc& l ) { return ByLocKey( l , nume
ric_limits<long long>::min() ); } | | static ByLocKey min( const DiskLoc& l ) { return ByLocKey( l , nume
ric_limits<long long>::min() ); } | |
| static ByLocKey max( const DiskLoc& l ) { return ByLocKey( l , nume
ric_limits<long long>::max() ); } | | static ByLocKey max( const DiskLoc& l ) { return ByLocKey( l , nume
ric_limits<long long>::max() ); } | |
| | | | |
| skipping to change at line 88 | | skipping to change at line 89 | |
| friend class CmdCursorInfo; | | friend class CmdCursorInfo; | |
| public: | | public: | |
| static void assertNoCursors(); | | static void assertNoCursors(); | |
| | | | |
| /* use this to assure we don't in the background time out cursor wh
ile it is under use. | | /* use this to assure we don't in the background time out cursor wh
ile it is under use. | |
| if you are using noTimeout() already, there is no risk anyway. | | if you are using noTimeout() already, there is no risk anyway. | |
| Further, this mechanism guards against two getMore requests on t
he same cursor executing | | Further, this mechanism guards against two getMore requests on t
he same cursor executing | |
| at the same time - which might be bad. That should never happen
, but if a client driver | | at the same time - which might be bad. That should never happen
, but if a client driver | |
| had a bug, it could (or perhaps some sort of attack situation). | | had a bug, it could (or perhaps some sort of attack situation). | |
| */ | | */ | |
|
| class Pointer : boost::noncopyable { | | class Pin : boost::noncopyable { | |
| ClientCursor *_c; | | | |
| public: | | public: | |
|
| ClientCursor * c() { return _c; } | | Pin( long long cursorid ) : | |
| void release() { | | _cursorid( INVALID_CURSOR_ID ) { | |
| if( _c ) { | | recursive_scoped_lock lock( ccmutex ); | |
| verify( _c->_pinValue >= 100 ); | | ClientCursor *cursor = ClientCursor::find_inlock( cursorid, | |
| _c->_pinValue -= 100; | | true ); | |
| _c = 0; | | if ( cursor ) { | |
| | | uassert( 12051, "clientcursor already in use? driver pr | |
| | | oblem?", | |
| | | cursor->_pinValue < 100 ); | |
| | | cursor->_pinValue += 100; | |
| | | _cursorid = cursorid; | |
| } | | } | |
| } | | } | |
|
| /** | | void release() { | |
| * call this if during a yield, the cursor got deleted | | ClientCursor *cursor = c(); | |
| * if so, we don't want to use the point address | | _cursorid = INVALID_CURSOR_ID; | |
| */ | | if ( cursor ) { | |
| void deleted() { | | verify( cursor->_pinValue >= 100 ); | |
| _c = 0; | | cursor->_pinValue -= 100; | |
| } | | | |
| ~Pointer() { release(); } | | | |
| Pointer(long long cursorid) { | | | |
| recursive_scoped_lock lock(ccmutex); | | | |
| _c = ClientCursor::find_inlock(cursorid, true); | | | |
| if( _c ) { | | | |
| if( _c->_pinValue >= 100 ) { | | | |
| _c = 0; | | | |
| uasserted(12051, "clientcursor already in use? driv | | | |
| er problem?"); | | | |
| } | | | |
| _c->_pinValue += 100; | | | |
| } | | } | |
| } | | } | |
|
| | | ~Pin() { DESTRUCTOR_GUARD( release(); ) } | |
| | | ClientCursor *c() const { return ClientCursor::find( _cursorid | |
| | | ); } | |
| | | private: | |
| | | CursorId _cursorid; | |
| }; | | }; | |
| | | | |
|
| // This object assures safe and reliable cleanup of a ClientCursor. | | /** Assures safe and reliable cleanup of a ClientCursor. */ | |
| class CleanupPointer : boost::noncopyable { | | class Holder : boost::noncopyable { | |
| public: | | public: | |
|
| CleanupPointer() : _c( 0 ), _id( -1 ) {} | | Holder( ClientCursor *c = 0 ) : | |
| | | _c( 0 ), | |
| | | _id( INVALID_CURSOR_ID ) { | |
| | | reset( c ); | |
| | | } | |
| void reset( ClientCursor *c = 0 ) { | | void reset( ClientCursor *c = 0 ) { | |
| if ( c == _c ) | | if ( c == _c ) | |
| return; | | return; | |
| if ( _c ) { | | if ( _c ) { | |
| // be careful in case cursor was deleted by someone els
e | | // be careful in case cursor was deleted by someone els
e | |
| ClientCursor::erase( _id ); | | ClientCursor::erase( _id ); | |
| } | | } | |
| if ( c ) { | | if ( c ) { | |
| _c = c; | | _c = c; | |
| _id = c->_cursorid; | | _id = c->_cursorid; | |
| } | | } | |
| else { | | else { | |
| _c = 0; | | _c = 0; | |
|
| _id = -1; | | _id = INVALID_CURSOR_ID; | |
| } | | } | |
| } | | } | |
|
| ~CleanupPointer() { | | ~Holder() { | |
| DESTRUCTOR_GUARD ( reset(); ); | | DESTRUCTOR_GUARD ( reset(); ); | |
| } | | } | |
| operator bool() { return _c; } | | operator bool() { return _c; } | |
| ClientCursor * operator-> () { return _c; } | | ClientCursor * operator-> () { return _c; } | |
|
| | | const ClientCursor * operator-> () const { return _c; } | |
| /** Release ownership of the ClientCursor. */ | | /** Release ownership of the ClientCursor. */ | |
| void release() { | | void release() { | |
| _c = 0; | | _c = 0; | |
|
| _id = -1; | | _id = INVALID_CURSOR_ID; | |
| } | | } | |
| private: | | private: | |
| ClientCursor *_c; | | ClientCursor *_c; | |
| CursorId _id; | | CursorId _id; | |
| }; | | }; | |
| | | | |
| /** | | /** | |
| * Iterates through all ClientCursors, under its own ccmutex lock. | | * Iterates through all ClientCursors, under its own ccmutex lock. | |
| * Also supports deletion on the fly. | | * Also supports deletion on the fly. | |
| */ | | */ | |
| | | | |
| skipping to change at line 289 | | skipping to change at line 290 | |
| BSONElement getFieldDotted( const string& name , BSONObj& holder ,
bool * fromKey = 0 ) ; | | BSONElement getFieldDotted( const string& name , BSONObj& holder ,
bool * fromKey = 0 ) ; | |
| | | | |
| /** extract items from object which match a pattern object. | | /** extract items from object which match a pattern object. | |
| * e.g., if pattern is { x : 1, y : 1 }, builds an object with | | * e.g., if pattern is { x : 1, y : 1 }, builds an object with | |
| * x and y elements of this object, if they are present. | | * x and y elements of this object, if they are present. | |
| * returns elements with original field names | | * returns elements with original field names | |
| * NOTE: copied from BSONObj::extractFields | | * NOTE: copied from BSONObj::extractFields | |
| */ | | */ | |
| BSONObj extractFields(const BSONObj &pattern , bool fillWithNull =
false) ; | | BSONObj extractFields(const BSONObj &pattern , bool fillWithNull =
false) ; | |
| | | | |
|
| void fillQueryResultFromObj( BufBuilder &b ) const; | | void fillQueryResultFromObj( BufBuilder &b, const MatchDetails* det
ails = NULL ) const; | |
| | | | |
| bool currentIsDup() { return _c->getsetdup( _c->currLoc() ); } | | bool currentIsDup() { return _c->getsetdup( _c->currLoc() ); } | |
| | | | |
| bool currentMatches() { | | bool currentMatches() { | |
| if ( ! _c->matcher() ) | | if ( ! _c->matcher() ) | |
| return true; | | return true; | |
| return _c->matcher()->matchesCurrent( _c.get() ); | | return _c->matcher()->matchesCurrent( _c.get() ); | |
| } | | } | |
| | | | |
| void setChunkManager( ShardChunkManagerPtr manager ){ _chunkManager
= manager; } | | void setChunkManager( ShardChunkManagerPtr manager ){ _chunkManager
= manager; } | |
| | | | |
| skipping to change at line 420 | | skipping to change at line 421 | |
| unsigned _pinValue; | | unsigned _pinValue; | |
| | | | |
| bool _doingDeletes; // when true we are the delete and aboutToDelet
e shouldn't manipulate us | | bool _doingDeletes; // when true we are the delete and aboutToDelet
e shouldn't manipulate us | |
| ElapsedTracker _yieldSometimesTracker; | | ElapsedTracker _yieldSometimesTracker; | |
| | | | |
| ShardChunkManagerPtr _chunkManager; | | ShardChunkManagerPtr _chunkManager; | |
| | | | |
| public: | | public: | |
| shared_ptr<ParsedQuery> pq; | | shared_ptr<ParsedQuery> pq; | |
| shared_ptr<Projection> fields; // which fields query wants returned | | shared_ptr<Projection> fields; // which fields query wants returned | |
|
| // TODO Maybe helper objects should manage their own memory rather | | | |
| than rely on the | | | |
| // original message being valid. | | | |
| Message originalMessage; // this is effectively an auto ptr for dat | | | |
| a the matcher points to | | | |
| | | | |
| private: // static members | | private: // static members | |
| | | | |
| static CCById clientCursorsById; | | static CCById clientCursorsById; | |
| static long long numberTimedOut; | | static long long numberTimedOut; | |
| static boost::recursive_mutex& ccmutex; // must use this for all
statics above! | | static boost::recursive_mutex& ccmutex; // must use this for all
statics above! | |
| static CursorId allocCursorId_inlock(); | | static CursorId allocCursorId_inlock(); | |
| | | | |
| }; | | }; | |
| | | | |
| | | | |
| skipping to change at line 445 | | skipping to change at line 443 | |
| string name() const { return "ClientCursorMonitor"; } | | string name() const { return "ClientCursorMonitor"; } | |
| void run(); | | void run(); | |
| }; | | }; | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
| // ClientCursor should only be used with auto_ptr because it needs to be | | // ClientCursor should only be used with auto_ptr because it needs to be | |
| // release()ed after a yield if stillOk() returns false and these pointer t
ypes | | // release()ed after a yield if stillOk() returns false and these pointer t
ypes | |
| // do not support releasing. This will prevent them from being used acciden
tally | | // do not support releasing. This will prevent them from being used acciden
tally | |
| // Instead of auto_ptr<>, which still requires some degree of manual manage
ment | | // Instead of auto_ptr<>, which still requires some degree of manual manage
ment | |
|
| // of this, consider using ClientCursor::CleanupPointer which handles | | // of this, consider using ClientCursor::Holder which handles ClientCursor' | |
| // ClientCursor's unusual self-deletion mechanics | | s | |
| | | // unusual self-deletion mechanics. | |
| namespace boost{ | | namespace boost{ | |
| template<> class scoped_ptr<mongo::ClientCursor> {}; | | template<> class scoped_ptr<mongo::ClientCursor> {}; | |
| template<> class shared_ptr<mongo::ClientCursor> {}; | | template<> class shared_ptr<mongo::ClientCursor> {}; | |
| } | | } | |
| | | | |
End of changes. 14 change blocks. |
| 40 lines changed or deleted | | 39 lines changed or added | |
|
| connpool.h | | connpool.h | |
| | | | |
| skipping to change at line 59 | | skipping to change at line 59 | |
| void createdOne( DBClientBase * base ); | | void createdOne( DBClientBase * base ); | |
| long long numCreated() const { return _created; } | | long long numCreated() const { return _created; } | |
| | | | |
| ConnectionString::ConnectionType type() const { verify(_created); r
eturn _type; } | | ConnectionString::ConnectionType type() const { verify(_created); r
eturn _type; } | |
| | | | |
| /** | | /** | |
| * gets a connection or return NULL | | * gets a connection or return NULL | |
| */ | | */ | |
| DBClientBase * get( DBConnectionPool * pool , double socketTimeout
); | | DBClientBase * get( DBConnectionPool * pool , double socketTimeout
); | |
| | | | |
|
| | | // Deletes all connections in the pool | |
| | | void clear(); | |
| | | | |
| void done( DBConnectionPool * pool , DBClientBase * c ); | | void done( DBConnectionPool * pool , DBClientBase * c ); | |
| | | | |
| void flush(); | | void flush(); | |
| | | | |
| void getStaleConnections( vector<DBClientBase*>& stale ); | | void getStaleConnections( vector<DBClientBase*>& stale ); | |
| | | | |
| static void setMaxPerHost( unsigned max ) { _maxPerHost = max; } | | static void setMaxPerHost( unsigned max ) { _maxPerHost = max; } | |
| static unsigned getMaxPerHost() { return _maxPerHost; } | | static unsigned getMaxPerHost() { return _maxPerHost; } | |
| private: | | private: | |
| | | | |
| | | | |
| skipping to change at line 133 | | skipping to change at line 136 | |
| void flush(); | | void flush(); | |
| | | | |
| DBClientBase *get(const string& host, double socketTimeout = 0); | | DBClientBase *get(const string& host, double socketTimeout = 0); | |
| DBClientBase *get(const ConnectionString& host, double socketTimeou
t = 0); | | DBClientBase *get(const ConnectionString& host, double socketTimeou
t = 0); | |
| | | | |
| void release(const string& host, DBClientBase *c); | | void release(const string& host, DBClientBase *c); | |
| | | | |
| void addHook( DBConnectionHook * hook ); // we take ownership | | void addHook( DBConnectionHook * hook ); // we take ownership | |
| void appendInfo( BSONObjBuilder& b ); | | void appendInfo( BSONObjBuilder& b ); | |
| | | | |
|
| | | // Removes and deletes all connections from the pool for the host ( | |
| | | regardless of timeout) | |
| | | void removeHost( const string& host ); | |
| | | | |
| /** compares server namees, but is smart about replica set names */ | | /** compares server namees, but is smart about replica set names */ | |
| struct serverNameCompare { | | struct serverNameCompare { | |
| bool operator()( const string& a , const string& b ) const; | | bool operator()( const string& a , const string& b ) const; | |
| }; | | }; | |
| | | | |
| virtual string taskName() const { return "DBConnectionPool-cleaner"
; } | | virtual string taskName() const { return "DBConnectionPool-cleaner"
; } | |
| virtual void taskDoWork(); | | virtual void taskDoWork(); | |
| | | | |
| private: | | private: | |
| DBConnectionPool( DBConnectionPool& p ); | | DBConnectionPool( DBConnectionPool& p ); | |
| | | | |
| skipping to change at line 201 | | skipping to change at line 207 | |
| | | | |
| private: | | private: | |
| static AtomicUInt _numConnections; | | static AtomicUInt _numConnections; | |
| }; | | }; | |
| | | | |
| /** Use to get a connection from the pool. On exceptions things | | /** Use to get a connection from the pool. On exceptions things | |
| clean up nicely (i.e. the socket gets closed automatically when the | | clean up nicely (i.e. the socket gets closed automatically when the | |
| scopeddbconnection goes out of scope). | | scopeddbconnection goes out of scope). | |
| */ | | */ | |
| class ScopedDbConnection : public AScopedConnection { | | class ScopedDbConnection : public AScopedConnection { | |
|
| public: | | private: | |
| /** the main constructor you want to use | | /** the main constructor you want to use | |
| throws UserException if can't connect | | throws UserException if can't connect | |
| */ | | */ | |
| explicit ScopedDbConnection(const string& host, double socketTimeou
t = 0) : _host(host), _conn( pool.get(host, socketTimeout) ), _socketTimeou
t( socketTimeout ) { | | explicit ScopedDbConnection(const string& host, double socketTimeou
t = 0) : _host(host), _conn( pool.get(host, socketTimeout) ), _socketTimeou
t( socketTimeout ) { | |
| _setSocketTimeout(); | | _setSocketTimeout(); | |
| } | | } | |
| | | | |
| ScopedDbConnection() : _host( "" ) , _conn(0), _socketTimeout( 0 )
{} | | ScopedDbConnection() : _host( "" ) , _conn(0), _socketTimeout( 0 )
{} | |
| | | | |
| /* @param conn - bind to an existing connection */ | | /* @param conn - bind to an existing connection */ | |
| ScopedDbConnection(const string& host, DBClientBase* conn, double s
ocketTimeout = 0 ) : _host( host ) , _conn( conn ), _socketTimeout( socketT
imeout ) { | | ScopedDbConnection(const string& host, DBClientBase* conn, double s
ocketTimeout = 0 ) : _host( host ) , _conn( conn ), _socketTimeout( socketT
imeout ) { | |
| _setSocketTimeout(); | | _setSocketTimeout(); | |
| } | | } | |
|
| | | public: | |
| | | | |
|
| /** throws UserException if can't connect */ | | // Factory functions for getting ScopedDbConnections. The caller o | |
| explicit ScopedDbConnection(const ConnectionString& url, double soc | | wns the resulting object | |
| ketTimeout = 0 ) : _host(url.toString()), _conn( pool.get(url, socketTimeou | | // and is responsible for deleting it when finished. This should be | |
| t) ), _socketTimeout( socketTimeout ) { | | used when running a | |
| _setSocketTimeout(); | | // command on a shard from the mongos and the command should run wi | |
| } | | th the client's | |
| | | // authentication. If the command should be run with full permissi | |
| | | ons regardless | |
| | | // of whether or not the user is authorized, then use getInternalSc | |
| | | opedDbConnection(). | |
| | | static ScopedDbConnection* getScopedDbConnection(const string& host | |
| | | , | |
| | | double socketTimeo | |
| | | ut = 0); | |
| | | static ScopedDbConnection* getScopedDbConnection(); | |
| | | | |
|
| /** throws UserException if can't connect */ | | // Gets a ScopedDbConnection designed to be used for internal commu | |
| explicit ScopedDbConnection(const Shard& shard, double socketTimeou | | nication within a cluster | |
| t = 0 ); | | // The mongod/mongos implementations of these set the Authenticatio | |
| explicit ScopedDbConnection(const Shard* shard, double socketTimeou | | nTable on the underlying | |
| t = 0 ); | | // connection to the internalSecurity permissions. All commands ru | |
| | | n on the shard mongods | |
| | | // using this connection will have full access. If the command sho | |
| | | uld only be run on the | |
| | | // shard if the client has permission to do so, then use getScopedD | |
| | | bConnection(). | |
| | | // These functions should not be called by consumers of the C++ cli | |
| | | ent library. | |
| | | static ScopedDbConnection* getInternalScopedDbConnection(const stri | |
| | | ng& host, | |
| | | double soc | |
| | | ketTimeout = 0); | |
| | | static ScopedDbConnection* getInternalScopedDbConnection(); | |
| | | | |
| ~ScopedDbConnection(); | | ~ScopedDbConnection(); | |
| | | | |
| /** get the associated connection object */ | | /** get the associated connection object */ | |
| DBClientBase* operator->() { | | DBClientBase* operator->() { | |
| uassert( 11004 , "connection was returned to the pool already"
, _conn ); | | uassert( 11004 , "connection was returned to the pool already"
, _conn ); | |
| return _conn; | | return _conn; | |
| } | | } | |
| | | | |
| /** get the associated connection object */ | | /** get the associated connection object */ | |
| | | | |
| skipping to change at line 272 | | skipping to change at line 289 | |
| */ | | */ | |
| void done() { | | void done() { | |
| if ( ! _conn ) | | if ( ! _conn ) | |
| return; | | return; | |
| | | | |
| /* we could do this, but instead of assume one is using autorec
onnect mode on the connection | | /* we could do this, but instead of assume one is using autorec
onnect mode on the connection | |
| if ( _conn->isFailed() ) | | if ( _conn->isFailed() ) | |
| kill(); | | kill(); | |
| else | | else | |
| */ | | */ | |
|
| | | _conn->clearAuthenticationTable(); | |
| pool.release(_host, _conn); | | pool.release(_host, _conn); | |
| _conn = 0; | | _conn = 0; | |
| } | | } | |
| | | | |
|
| ScopedDbConnection * steal(); | | | |
| | | | |
| private: | | private: | |
| | | | |
| void _setSocketTimeout(); | | void _setSocketTimeout(); | |
| | | | |
| const string _host; | | const string _host; | |
| DBClientBase *_conn; | | DBClientBase *_conn; | |
| const double _socketTimeout; | | const double _socketTimeout; | |
| | | | |
| }; | | }; | |
| | | | |
| | | | |
End of changes. 8 change blocks. |
| 14 lines changed or deleted | | 42 lines changed or added | |
|
| curop.h | | curop.h | |
| | | | |
| skipping to change at line 40 | | skipping to change at line 40 | |
| | | | |
| class CurOp; | | class CurOp; | |
| | | | |
| /* lifespan is different than CurOp because of recursives with DBDirect
Client */ | | /* lifespan is different than CurOp because of recursives with DBDirect
Client */ | |
| class OpDebug { | | class OpDebug { | |
| public: | | public: | |
| OpDebug() : ns(""){ reset(); } | | OpDebug() : ns(""){ reset(); } | |
| | | | |
| void reset(); | | void reset(); | |
| | | | |
|
| string toString() const; | | string report( const CurOp& curop ) const; | |
| void append( const CurOp& curop, BSONObjBuilder& b ) const; | | void append( const CurOp& curop, BSONObjBuilder& b ) const; | |
| | | | |
| // ------------------- | | // ------------------- | |
| | | | |
| StringBuilder extra; // weird things we need to fix later | | StringBuilder extra; // weird things we need to fix later | |
| | | | |
| // basic options | | // basic options | |
| int op; | | int op; | |
| bool iscommand; | | bool iscommand; | |
| Namespace ns; | | Namespace ns; | |
| | | | |
| skipping to change at line 157 | | skipping to change at line 157 | |
| */ | | */ | |
| class CurOp : boost::noncopyable { | | class CurOp : boost::noncopyable { | |
| public: | | public: | |
| CurOp( Client * client , CurOp * wrapped = 0 ); | | CurOp( Client * client , CurOp * wrapped = 0 ); | |
| ~CurOp(); | | ~CurOp(); | |
| | | | |
| bool haveQuery() const { return _query.have(); } | | bool haveQuery() const { return _query.have(); } | |
| BSONObj query() { return _query.get(); } | | BSONObj query() { return _query.get(); } | |
| void appendQuery( BSONObjBuilder& b , const StringData& name ) cons
t { _query.append( b , name ); } | | void appendQuery( BSONObjBuilder& b , const StringData& name ) cons
t { _query.append( b , name ); } | |
| | | | |
|
| void ensureStarted() { | | void ensureStarted(); | |
| if ( _start == 0 ) | | | |
| _start = _checkpoint = curTimeMicros64(); | | | |
| } | | | |
| bool isStarted() const { return _start > 0; } | | bool isStarted() const { return _start > 0; } | |
| void enter( Client::Context * context ); | | void enter( Client::Context * context ); | |
| void leave( Client::Context * context ); | | void leave( Client::Context * context ); | |
| void reset(); | | void reset(); | |
| void reset( const HostAndPort& remote, int op ); | | void reset( const HostAndPort& remote, int op ); | |
| void markCommand() { _command = true; } | | void markCommand() { _command = true; } | |
|
| void waitingForLock( char type ) { | | | |
| _waitingForLock = true; | | | |
| _lockType = type; | | | |
| } | | | |
| void gotLock() { _waitingForLock = false; } | | | |
| OpDebug& debug() { return _debug; } | | OpDebug& debug() { return _debug; } | |
| int profileLevel() const { return _dbprofile; } | | int profileLevel() const { return _dbprofile; } | |
| const char * getNS() const { return _ns; } | | const char * getNS() const { return _ns; } | |
| | | | |
| bool shouldDBProfile( int ms ) const { | | bool shouldDBProfile( int ms ) const { | |
| if ( _dbprofile <= 0 ) | | if ( _dbprofile <= 0 ) | |
| return false; | | return false; | |
| | | | |
| return _dbprofile >= 2 || ms >= cmdLine.slowMS; | | return _dbprofile >= 2 || ms >= cmdLine.slowMS; | |
| } | | } | |
| | | | |
| AtomicUInt opNum() const { return _opNum; } | | AtomicUInt opNum() const { return _opNum; } | |
| | | | |
| /** if this op is running */ | | /** if this op is running */ | |
| bool active() const { return _active; } | | bool active() const { return _active; } | |
| | | | |
|
| char lockType() const { return _lockType; } | | | |
| bool displayInCurop() const { return _active && ! _suppressFromCuro
p; } | | bool displayInCurop() const { return _active && ! _suppressFromCuro
p; } | |
|
| bool isWaitingForLock() const { return _waitingForLock; } | | | |
| int getOp() const { return _op; } | | int getOp() const { return _op; } | |
| unsigned long long startTime() { // micros | | unsigned long long startTime() { // micros | |
| ensureStarted(); | | ensureStarted(); | |
| return _start; | | return _start; | |
| } | | } | |
| void done() { | | void done() { | |
| _active = false; | | _active = false; | |
| _end = curTimeMicros64(); | | _end = curTimeMicros64(); | |
| } | | } | |
| unsigned long long totalTimeMicros() { | | unsigned long long totalTimeMicros() { | |
| | | | |
| skipping to change at line 222 | | skipping to change at line 212 | |
| BSONObj info(); | | BSONObj info(); | |
| BSONObj infoNoauth(); | | BSONObj infoNoauth(); | |
| string getRemoteString( bool includePort = true ) { return _remote.
toString(includePort); } | | string getRemoteString( bool includePort = true ) { return _remote.
toString(includePort); } | |
| ProgressMeter& setMessage( const char * msg , unsigned long long pr
ogressMeterTotal = 0 , int secondsBetween = 3 ); | | ProgressMeter& setMessage( const char * msg , unsigned long long pr
ogressMeterTotal = 0 , int secondsBetween = 3 ); | |
| string getMessage() const { return _message.toString(); } | | string getMessage() const { return _message.toString(); } | |
| ProgressMeter& getProgressMeter() { return _progressMeter; } | | ProgressMeter& getProgressMeter() { return _progressMeter; } | |
| CurOp *parent() const { return _wrapped; } | | CurOp *parent() const { return _wrapped; } | |
| void kill() { _killed = true; } | | void kill() { _killed = true; } | |
| bool killed() const { return _killed; } | | bool killed() const { return _killed; } | |
| void yielded() { _numYields++; } | | void yielded() { _numYields++; } | |
|
| void setNS(const char *ns) { | | int numYields() const { return _numYields; } | |
| strncpy(_ns, ns, Namespace::MaxNsLen); | | | |
| _ns[Namespace::MaxNsLen] = 0; | | | |
| } | | | |
| | | | |
| void suppressFromCurop() { _suppressFromCurop = true; } | | void suppressFromCurop() { _suppressFromCurop = true; } | |
| | | | |
| long long getExpectedLatencyMs() const { return _expectedLatencyMs;
} | | long long getExpectedLatencyMs() const { return _expectedLatencyMs;
} | |
| void setExpectedLatencyMs( long long latency ) { _expectedLatencyMs
= latency; } | | void setExpectedLatencyMs( long long latency ) { _expectedLatencyMs
= latency; } | |
| | | | |
|
| | | void recordGlobalTime( long long micros ) const; | |
| | | | |
| | | const LockStat& lockStat() const { return _lockStat; } | |
| | | LockStat& lockStat() { return _lockStat; } | |
| private: | | private: | |
| friend class Client; | | friend class Client; | |
| void _reset(); | | void _reset(); | |
| | | | |
| static AtomicUInt _nextOpNum; | | static AtomicUInt _nextOpNum; | |
| Client * _client; | | Client * _client; | |
| CurOp * _wrapped; | | CurOp * _wrapped; | |
| unsigned long long _start; | | unsigned long long _start; | |
|
| unsigned long long _checkpoint; | | | |
| unsigned long long _end; | | unsigned long long _end; | |
| bool _active; | | bool _active; | |
| bool _suppressFromCurop; // unless $all is set | | bool _suppressFromCurop; // unless $all is set | |
| int _op; | | int _op; | |
| bool _command; | | bool _command; | |
|
| char _lockType; // r w R W | | | |
| bool _waitingForLock; | | | |
| int _dbprofile; // 0=off, 1=slow, 2=all | | int _dbprofile; // 0=off, 1=slow, 2=all | |
| AtomicUInt _opNum; // todo: simple being "unsigned" m
ay make more sense here | | AtomicUInt _opNum; // todo: simple being "unsigned" m
ay make more sense here | |
| char _ns[Namespace::MaxNsLen+2]; | | char _ns[Namespace::MaxNsLen+2]; | |
| HostAndPort _remote; // CAREFUL here with thread safety | | HostAndPort _remote; // CAREFUL here with thread safety | |
| CachedBSONObj _query; // CachedBSONObj is thread safe | | CachedBSONObj _query; // CachedBSONObj is thread safe | |
| OpDebug _debug; | | OpDebug _debug; | |
| ThreadSafeString _message; | | ThreadSafeString _message; | |
| ProgressMeter _progressMeter; | | ProgressMeter _progressMeter; | |
| volatile bool _killed; | | volatile bool _killed; | |
| int _numYields; | | int _numYields; | |
|
| | | LockStat _lockStat; | |
| | | | |
| // this is how much "extra" time a query might take | | // this is how much "extra" time a query might take | |
| // a writebacklisten for example will block for 30s | | // a writebacklisten for example will block for 30s | |
| // so this should be 30000 in that case | | // so this should be 30000 in that case | |
| long long _expectedLatencyMs; | | long long _expectedLatencyMs; | |
| | | | |
| }; | | }; | |
| | | | |
| /* _globalKill: we are shutting down | | /* _globalKill: we are shutting down | |
| otherwise kill attribute set on specified CurOp | | otherwise kill attribute set on specified CurOp | |
| | | | |
End of changes. 10 change blocks. |
| 20 lines changed or deleted | | 8 lines changed or added | |
|
| cursor.h | | cursor.h | |
| | | | |
| skipping to change at line 48 | | skipping to change at line 48 | |
| * | | * | |
| * Two general techniques may be used to ensure a Cursor is in a consis
tent state after a write. | | * Two general techniques may be used to ensure a Cursor is in a consis
tent state after a write. | |
| * - The Cursor may be advanced before the document at its current
position is deleted. | | * - The Cursor may be advanced before the document at its current
position is deleted. | |
| * - The Cursor may record its position and then relocate this posi
tion. | | * - The Cursor may record its position and then relocate this posi
tion. | |
| * A particular Cursor may potentially utilize only one of the above te
chniques, but a client | | * A particular Cursor may potentially utilize only one of the above te
chniques, but a client | |
| * that is Cursor subclass agnostic must implement a pattern handling b
oth techniques. | | * that is Cursor subclass agnostic must implement a pattern handling b
oth techniques. | |
| * | | * | |
| * When the document at a Cursor's current position is deleted (or move
d to a new location) the | | * When the document at a Cursor's current position is deleted (or move
d to a new location) the | |
| * following pattern is used: | | * following pattern is used: | |
| * DiskLoc toDelete = cursor->currLoc(); | | * DiskLoc toDelete = cursor->currLoc(); | |
|
| * cursor->advance(); | | * while( cursor->ok() && cursor->currLoc() == toDelete ) { | |
| | | * cursor->advance(); | |
| | | * } | |
| * cursor->prepareToTouchEarlierIterate(); | | * cursor->prepareToTouchEarlierIterate(); | |
| * delete( toDelete ); | | * delete( toDelete ); | |
| * cursor->recoverFromTouchingEarlierIterate(); | | * cursor->recoverFromTouchingEarlierIterate(); | |
| * | | * | |
| * When a cursor yields, the following pattern is used: | | * When a cursor yields, the following pattern is used: | |
| * cursor->prepareToYield(); | | * cursor->prepareToYield(); | |
| * while( Op theOp = nextOp() ) { | | * while( Op theOp = nextOp() ) { | |
| * if ( theOp.type() == INSERT || theOp.type() == UPDATE_IN_PLA
CE ) { | | * if ( theOp.type() == INSERT || theOp.type() == UPDATE_IN_PLA
CE ) { | |
| * theOp.run(); | | * theOp.run(); | |
| * } | | * } | |
| * else if ( theOp.type() == DELETE ) { | | * else if ( theOp.type() == DELETE ) { | |
| * if ( cursor->refLoc() == theOp.toDelete() ) { | | * if ( cursor->refLoc() == theOp.toDelete() ) { | |
| * cursor->recoverFromYield(); | | * cursor->recoverFromYield(); | |
|
| * cursor->advance(); | | * while ( cursor->ok() && cursor->refLoc() == theOp.to | |
| | | Delete() ) { | |
| | | * cursor->advance(); | |
| | | * } | |
| * cursor->prepareToYield(); | | * cursor->prepareToYield(); | |
| * } | | * } | |
| * theOp.run(); | | * theOp.run(); | |
| * } | | * } | |
| * } | | * } | |
| * cursor->recoverFromYield(); | | * cursor->recoverFromYield(); | |
| * | | * | |
| * The break before a getMore request is typically treated as a yield,
but if a Cursor supports | | * The break before a getMore request is typically treated as a yield,
but if a Cursor supports | |
| * getMore but not yield the following pattern is currently used: | | * getMore but not yield the following pattern is currently used: | |
| * cursor->noteLocation(); | | * cursor->noteLocation(); | |
| * runOtherOps(); | | * runOtherOps(); | |
| * cursor->checkLocation(); | | * cursor->checkLocation(); | |
| * | | * | |
|
| | | * But see SERVER-5725. | |
| | | * | |
| * A Cursor may rely on additional callbacks not listed above to reloca
te its position after a | | * A Cursor may rely on additional callbacks not listed above to reloca
te its position after a | |
| * write. | | * write. | |
| */ | | */ | |
| class Cursor : boost::noncopyable { | | class Cursor : boost::noncopyable { | |
| public: | | public: | |
| virtual ~Cursor() {} | | virtual ~Cursor() {} | |
| virtual bool ok() = 0; | | virtual bool ok() = 0; | |
| bool eof() { return !ok(); } | | bool eof() { return !ok(); } | |
| virtual Record* _current() = 0; | | virtual Record* _current() = 0; | |
| virtual BSONObj current() = 0; | | virtual BSONObj current() = 0; | |
| | | | |
| skipping to change at line 244 | | skipping to change at line 250 | |
| BasicCursor(const AdvanceStrategy *_s = forward()) : s( _s ), _nsca
nned() { | | BasicCursor(const AdvanceStrategy *_s = forward()) : s( _s ), _nsca
nned() { | |
| init(); | | init(); | |
| } | | } | |
| bool ok() { return !curr.isNull(); } | | bool ok() { return !curr.isNull(); } | |
| Record* _current() { | | Record* _current() { | |
| verify( ok() ); | | verify( ok() ); | |
| return curr.rec(); | | return curr.rec(); | |
| } | | } | |
| BSONObj current() { | | BSONObj current() { | |
| Record *r = _current(); | | Record *r = _current(); | |
|
| BSONObj j(r); | | return BSONObj::make(r); | |
| return j; | | | |
| } | | } | |
| virtual DiskLoc currLoc() { return curr; } | | virtual DiskLoc currLoc() { return curr; } | |
| virtual DiskLoc refLoc() { return curr.isNull() ? last : curr; } | | virtual DiskLoc refLoc() { return curr.isNull() ? last : curr; } | |
| bool advance(); | | bool advance(); | |
| virtual string toString() { return "BasicCursor"; } | | virtual string toString() { return "BasicCursor"; } | |
| virtual void setTailable() { | | virtual void setTailable() { | |
| if ( !curr.isNull() || !last.isNull() ) | | if ( !curr.isNull() || !last.isNull() ) | |
| tailable_ = true; | | tailable_ = true; | |
| } | | } | |
| virtual bool tailable() { return tailable_; } | | virtual bool tailable() { return tailable_; } | |
| | | | |
| skipping to change at line 292 | | skipping to change at line 297 | |
| /* used for order { $natural: -1 } */ | | /* used for order { $natural: -1 } */ | |
| class ReverseCursor : public BasicCursor { | | class ReverseCursor : public BasicCursor { | |
| public: | | public: | |
| ReverseCursor(DiskLoc dl) : BasicCursor( dl, reverse() ) { } | | ReverseCursor(DiskLoc dl) : BasicCursor( dl, reverse() ) { } | |
| ReverseCursor() : BasicCursor( reverse() ) { } | | ReverseCursor() : BasicCursor( reverse() ) { } | |
| virtual string toString() { return "ReverseCursor"; } | | virtual string toString() { return "ReverseCursor"; } | |
| }; | | }; | |
| | | | |
| class ForwardCappedCursor : public BasicCursor, public AdvanceStrategy
{ | | class ForwardCappedCursor : public BasicCursor, public AdvanceStrategy
{ | |
| public: | | public: | |
|
| ForwardCappedCursor( NamespaceDetails *nsd = 0, const DiskLoc &star | | static ForwardCappedCursor* make( NamespaceDetails* nsd = 0, | |
| tLoc = DiskLoc() ); | | const DiskLoc& startLoc = DiskLoc | |
| | | () ); | |
| virtual string toString() { | | virtual string toString() { | |
| return "ForwardCappedCursor"; | | return "ForwardCappedCursor"; | |
| } | | } | |
| virtual DiskLoc next( const DiskLoc &prev ) const; | | virtual DiskLoc next( const DiskLoc &prev ) const; | |
| virtual bool capped() const { return true; } | | virtual bool capped() const { return true; } | |
| private: | | private: | |
|
| | | ForwardCappedCursor( NamespaceDetails* nsd ); | |
| | | void init( const DiskLoc& startLoc ); | |
| NamespaceDetails *nsd; | | NamespaceDetails *nsd; | |
| }; | | }; | |
| | | | |
| class ReverseCappedCursor : public BasicCursor, public AdvanceStrategy
{ | | class ReverseCappedCursor : public BasicCursor, public AdvanceStrategy
{ | |
| public: | | public: | |
| ReverseCappedCursor( NamespaceDetails *nsd = 0, const DiskLoc &star
tLoc = DiskLoc() ); | | ReverseCappedCursor( NamespaceDetails *nsd = 0, const DiskLoc &star
tLoc = DiskLoc() ); | |
| virtual string toString() { | | virtual string toString() { | |
| return "ReverseCappedCursor"; | | return "ReverseCappedCursor"; | |
| } | | } | |
| virtual DiskLoc next( const DiskLoc &prev ) const; | | virtual DiskLoc next( const DiskLoc &prev ) const; | |
| | | | |
End of changes. 6 change blocks. |
| 6 lines changed or deleted | | 15 lines changed or added | |
|
| d_concurrency.h | | d_concurrency.h | |
| // @file d_concurrency.h | | // @file d_concurrency.h | |
|
| | | | |
| | | /** | |
| | | * Copyright (C) 2008 10gen Inc. | |
| | | * | |
| | | * This program is free software: you can redistribute it and/or modify | |
| | | * it under the terms of the GNU Affero General Public License, version 3 | |
| | | , | |
| | | * as published by the Free Software Foundation. | |
| | | * | |
| | | * This program is distributed in the hope that it will be useful, | |
| | | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| | | * GNU Affero General Public License for more details. | |
| | | * | |
| | | * You should have received a copy of the GNU Affero General Public Licen | |
| | | se | |
| | | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| | | */ | |
| | | | |
| // only used by mongod, thus the name ('d') | | // only used by mongod, thus the name ('d') | |
| // (also used by dbtests test binary, which is running mongod test code) | | // (also used by dbtests test binary, which is running mongod test code) | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
|
| #include "mongo/util/concurrency/qlock.h" | | | |
| #include "mongo/util/concurrency/mutex.h" | | | |
| #include "mongo/bson/stringdata.h" | | #include "mongo/bson/stringdata.h" | |
| #include "mongo/db/jsobj.h" | | #include "mongo/db/jsobj.h" | |
| #include "mongo/db/lockstat.h" | | #include "mongo/db/lockstat.h" | |
|
| | | #include "mongo/util/concurrency/mutex.h" | |
| | | #include "mongo/util/concurrency/rwlock.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| class WrapperForRWLock; | | class WrapperForRWLock; | |
| class LockState; | | class LockState; | |
| | | | |
| class Lock : boost::noncopyable { | | class Lock : boost::noncopyable { | |
| public: | | public: | |
| enum Nestable { notnestable=0, local, admin }; | | enum Nestable { notnestable=0, local, admin }; | |
| static int isLocked(); // true if *anything* is locked (by us) | | static int isLocked(); // true if *anything* is locked (by us) | |
| | | | |
| skipping to change at line 35 | | skipping to change at line 52 | |
| static bool isR(); | | static bool isR(); | |
| static bool isRW(); // R or W. i.e., we are write-exclusive | | static bool isRW(); // R or W. i.e., we are write-exclusive | |
| static bool nested(); | | static bool nested(); | |
| static bool isWriteLocked(const StringData& ns); | | static bool isWriteLocked(const StringData& ns); | |
| static bool atLeastReadLocked(const StringData& ns); // true if thi
s db is locked | | static bool atLeastReadLocked(const StringData& ns); // true if thi
s db is locked | |
| static void assertAtLeastReadLocked(const StringData& ns); | | static void assertAtLeastReadLocked(const StringData& ns); | |
| static void assertWriteLocked(const StringData& ns); | | static void assertWriteLocked(const StringData& ns); | |
| | | | |
| static bool dbLevelLockingEnabled(); | | static bool dbLevelLockingEnabled(); | |
| | | | |
|
| | | static LockStat* globalLockStat(); | |
| | | static LockStat* nestableLockStat( Nestable db ); | |
| | | | |
| class ScopedLock; | | class ScopedLock; | |
| | | | |
| // note: avoid TempRelease when possible. not a good thing. | | // note: avoid TempRelease when possible. not a good thing. | |
| struct TempRelease { | | struct TempRelease { | |
| TempRelease(); | | TempRelease(); | |
| ~TempRelease(); | | ~TempRelease(); | |
| const bool cant; // true if couldn't because of recursive locki
ng | | const bool cant; // true if couldn't because of recursive locki
ng | |
| ScopedLock *scopedLk; | | ScopedLock *scopedLk; | |
| }; | | }; | |
| | | | |
|
| | | /** turn on "parallel batch writer mode". blocks all other threads | |
| | | . this mode is off | |
| | | by default. note only one thread creates a ParallelBatchWriterM | |
| | | ode object; the rest just | |
| | | call iAmABatchParticipant(). Note that this lock is not releas | |
| | | ed on a temprelease, just | |
| | | the normal lock things below. | |
| | | */ | |
| | | class ParallelBatchWriterMode : boost::noncopyable { | |
| | | RWLockRecursive::Exclusive _lk; | |
| | | public: | |
| | | ParallelBatchWriterMode() : _lk(_batchLock) {} | |
| | | static void iAmABatchParticipant(); | |
| | | static RWLockRecursive &_batchLock; | |
| | | }; | |
| | | | |
| | | private: | |
| | | class ParallelBatchWriterSupport : boost::noncopyable { | |
| | | scoped_ptr<RWLockRecursive::Shared> _lk; | |
| | | public: | |
| | | ParallelBatchWriterSupport(); | |
| | | }; | |
| | | | |
| | | public: | |
| class ScopedLock : boost::noncopyable { | | class ScopedLock : boost::noncopyable { | |
|
| protected: | | | |
| friend struct TempRelease; | | | |
| ScopedLock(); | | | |
| virtual void tempRelease() = 0; | | | |
| virtual void relock() = 0; | | | |
| public: | | public: | |
| virtual ~ScopedLock(); | | virtual ~ScopedLock(); | |
|
| | | | |
| | | /** @return micros since we started acquiring */ | |
| | | long long acquireFinished( LockStat* stat ); | |
| | | | |
| | | protected: | |
| | | friend struct TempRelease; | |
| | | | |
| | | explicit ScopedLock( char type ); | |
| | | | |
| | | void tempRelease(); | |
| | | void relock(); | |
| | | | |
| | | void _recordTime( long long micros ); | |
| | | | |
| | | virtual void _tempRelease() = 0; | |
| | | virtual void _relock() = 0; | |
| | | | |
| | | ParallelBatchWriterSupport _lk; | |
| | | | |
| | | private: | |
| | | Timer _timer; // this is counting the current state | |
| | | char _type; | |
| | | LockStat* _stat; // the stat for the relevant lock to increment | |
| | | when we're done | |
| }; | | }; | |
| | | | |
| // note that for these classes recursive locking is ok if the recur
sive locking "makes sense" | | // note that for these classes recursive locking is ok if the recur
sive locking "makes sense" | |
| // i.e. you could grab globalread after globalwrite. | | // i.e. you could grab globalread after globalwrite. | |
| | | | |
| class GlobalWrite : public ScopedLock { | | class GlobalWrite : public ScopedLock { | |
|
| bool stoppedGreed; | | | |
| bool noop; | | bool noop; | |
| protected: | | protected: | |
|
| void tempRelease(); | | void _tempRelease(); | |
| void relock(); | | void _relock(); | |
| public: | | public: | |
|
| /** @param stopGreed after acquisition stop greediness of other | | // stopGreed is removed and does NOT work | |
| threads for write locks. this | | | |
| should generally not be used it is for exceptional circumst | | | |
| ances. journaling uses it. | | | |
| perhaps this should go away it makes the software more comp | | | |
| licated. | | | |
| */ | | | |
| // timeoutms is only for writelocktry -- deprecated -- do not u
se | | // timeoutms is only for writelocktry -- deprecated -- do not u
se | |
| GlobalWrite(bool stopGreed = false, int timeoutms = -1 ); | | GlobalWrite(bool stopGreed = false, int timeoutms = -1 ); | |
| virtual ~GlobalWrite(); | | virtual ~GlobalWrite(); | |
| void downgrade(); // W -> R | | void downgrade(); // W -> R | |
|
| bool upgrade(); // caution see notes | | void upgrade(); // caution see notes | |
| }; | | }; | |
| class GlobalRead : public ScopedLock { // recursive is ok | | class GlobalRead : public ScopedLock { // recursive is ok | |
| public: | | public: | |
| bool noop; | | bool noop; | |
| protected: | | protected: | |
|
| void tempRelease(); | | void _tempRelease(); | |
| void relock(); | | void _relock(); | |
| public: | | public: | |
| // timeoutms is only for readlocktry -- deprecated -- do not us
e | | // timeoutms is only for readlocktry -- deprecated -- do not us
e | |
| GlobalRead( int timeoutms = -1 ); | | GlobalRead( int timeoutms = -1 ); | |
| virtual ~GlobalRead(); | | virtual ~GlobalRead(); | |
| }; | | }; | |
|
| | | | |
| // lock this database. do not shared_lock globally first, that is h
andledin herein. | | // lock this database. do not shared_lock globally first, that is h
andledin herein. | |
| class DBWrite : public ScopedLock { | | class DBWrite : public ScopedLock { | |
|
| bool isW(LockState&) const; | | /** | |
| | | * flow | |
| | | * 1) lockDB | |
| | | * a) lockTop | |
| | | * b) lockNestable or lockOther | |
| | | * 2) unlockDB | |
| | | */ | |
| | | | |
| void lockTop(LockState&); | | void lockTop(LockState&); | |
| void lockNestable(Nestable db); | | void lockNestable(Nestable db); | |
| void lockOther(const string& db); | | void lockOther(const string& db); | |
|
| bool locked_w; | | | |
| bool locked_W; | | | |
| WrapperForRWLock *weLocked; | | | |
| const string what; | | | |
| bool _nested; | | | |
| void lockDB(const string& ns); | | void lockDB(const string& ns); | |
| void unlockDB(); | | void unlockDB(); | |
|
| | | | |
| protected: | | protected: | |
|
| void tempRelease(); | | void _tempRelease(); | |
| void relock(); | | void _relock(); | |
| | | | |
| public: | | public: | |
| DBWrite(const StringData& dbOrNs); | | DBWrite(const StringData& dbOrNs); | |
| virtual ~DBWrite(); | | virtual ~DBWrite(); | |
|
| | | | |
| | | class UpgradeToExclusive : private boost::noncopyable { | |
| | | public: | |
| | | UpgradeToExclusive(); | |
| | | ~UpgradeToExclusive(); | |
| | | | |
| | | bool gotUpgrade() const { return _gotUpgrade; } | |
| | | private: | |
| | | bool _gotUpgrade; | |
| | | }; | |
| | | | |
| | | private: | |
| | | bool _locked_w; | |
| | | bool _locked_W; | |
| | | WrapperForRWLock *_weLocked; | |
| | | const string _what; | |
| | | bool _nested; | |
| }; | | }; | |
|
| | | | |
| // lock this database for reading. do not shared_lock globally firs
t, that is handledin herein. | | // lock this database for reading. do not shared_lock globally firs
t, that is handledin herein. | |
| class DBRead : public ScopedLock { | | class DBRead : public ScopedLock { | |
|
| bool isRW(LockState&) const; | | | |
| void lockTop(LockState&); | | void lockTop(LockState&); | |
| void lockNestable(Nestable db); | | void lockNestable(Nestable db); | |
| void lockOther(const string& db); | | void lockOther(const string& db); | |
|
| bool locked_r; | | | |
| WrapperForRWLock *weLocked; | | | |
| string what; | | | |
| bool _nested; | | | |
| void lockDB(const string& ns); | | void lockDB(const string& ns); | |
| void unlockDB(); | | void unlockDB(); | |
|
| | | | |
| protected: | | protected: | |
|
| void tempRelease(); | | void _tempRelease(); | |
| void relock(); | | void _relock(); | |
| | | | |
| public: | | public: | |
| DBRead(const StringData& dbOrNs); | | DBRead(const StringData& dbOrNs); | |
| virtual ~DBRead(); | | virtual ~DBRead(); | |
|
| | | | |
| | | private: | |
| | | bool _locked_r; | |
| | | WrapperForRWLock *_weLocked; | |
| | | string _what; | |
| | | bool _nested; | |
| | | | |
| }; | | }; | |
| | | | |
| }; | | }; | |
| | | | |
| class readlocktry : boost::noncopyable { | | class readlocktry : boost::noncopyable { | |
| bool _got; | | bool _got; | |
| scoped_ptr<Lock::GlobalRead> _dbrlock; | | scoped_ptr<Lock::GlobalRead> _dbrlock; | |
| public: | | public: | |
| readlocktry( int tryms ); | | readlocktry( int tryms ); | |
| ~readlocktry(); | | ~readlocktry(); | |
| | | | |
| skipping to change at line 155 | | skipping to change at line 237 | |
| | | | |
| /** a mutex, but reported in curop() - thus a "high level" (HL) one | | /** a mutex, but reported in curop() - thus a "high level" (HL) one | |
| some overhead so we don't use this for everything. the externalobj
sort mutex | | some overhead so we don't use this for everything. the externalobj
sort mutex | |
| uses this, as it can be held for eons. implementation still needed.
*/ | | uses this, as it can be held for eons. implementation still needed.
*/ | |
| class HLMutex : public SimpleMutex { | | class HLMutex : public SimpleMutex { | |
| LockStat ls; | | LockStat ls; | |
| public: | | public: | |
| HLMutex(const char *name); | | HLMutex(const char *name); | |
| }; | | }; | |
| | | | |
|
| // implementation stuff | | | |
| // per thread | | | |
| class LockState { | | | |
| public: | | | |
| LockState(); | | | |
| void dump(); | | | |
| static void Dump(); | | | |
| void reportState(BSONObjBuilder& b); | | | |
| | | | |
| unsigned recursiveCount() const { return _recursive; } | | | |
| | | | |
| /** | | | |
| * @return 0 rwRW | | | |
| */ | | | |
| char threadState() const { return _threadState; } | | | |
| | | | |
| void locked( char newState ); // RWrw | | | |
| void unlocked(); // _threadState = 0 | | | |
| | | | |
| /** | | | |
| * you have to be locked already to call this | | | |
| * this is mostly for W_to_R or R_to_W | | | |
| */ | | | |
| void changeLockState( char newstate ); | | | |
| | | | |
| Lock::Nestable whichNestable() const { return _whichNestable; } | | | |
| int nestableCount() const { return _nestableCount; } | | | |
| | | | |
| int otherCount() const { return _otherCount; } | | | |
| string otherName() const { return _otherName; } | | | |
| WrapperForRWLock* otherLock() const { return _otherLock; } | | | |
| | | | |
| void enterScopedLock( Lock::ScopedLock* lock ); | | | |
| Lock::ScopedLock* leaveScopedLock(); | | | |
| | | | |
| void lockedNestable( Lock::Nestable what , int type ); | | | |
| void unlockedNestable(); | | | |
| void lockedOther( const string& db , int type , WrapperForRWLock* l | | | |
| ock ); | | | |
| void unlockedOther(); | | | |
| private: | | | |
| unsigned _recursive; // we allow recursively asking for a | | | |
| lock; we track that here | | | |
| | | | |
| // global lock related | | | |
| char _threadState; // 0, 'r', 'w', 'R', 'W' | | | |
| | | | |
| // db level locking related | | | |
| Lock::Nestable _whichNestable; | | | |
| int _nestableCount; // recursive lock count on local or | | | |
| admin db XXX - change name | | | |
| | | | |
| int _otherCount; // >0 means write lock, <0 read lo | | | |
| ck - XXX change name | | | |
| string _otherName; // which database are we locking and | | | |
| working with (besides local/admin) | | | |
| WrapperForRWLock* _otherLock; // so we don't have to check the map | | | |
| too often (the map has a mutex) | | | |
| | | | |
| // for temprelease | | | |
| // for the nonrecursive case. otherwise there would be many | | | |
| // the first lock goes here, which is ok since we can't yield recur | | | |
| sive locks | | | |
| Lock::ScopedLock* _scopedLk; | | | |
| | | | |
| }; | | | |
| | | | |
| } | | } | |
| | | | |
End of changes. 25 change blocks. |
| 102 lines changed or deleted | | 120 lines changed or added | |
|
| dbclient_rs.h | | dbclient_rs.h | |
| | | | |
| skipping to change at line 49 | | skipping to change at line 49 | |
| * can hand a slave to someone for SLAVE_OK | | * can hand a slave to someone for SLAVE_OK | |
| * one instace per process per replica set | | * one instace per process per replica set | |
| * TODO: we might be able to use a regular Node * to avoid _lock | | * TODO: we might be able to use a regular Node * to avoid _lock | |
| */ | | */ | |
| class ReplicaSetMonitor { | | class ReplicaSetMonitor { | |
| public: | | public: | |
| | | | |
| typedef boost::function1<void,const ReplicaSetMonitor*> ConfigChang
eHook; | | typedef boost::function1<void,const ReplicaSetMonitor*> ConfigChang
eHook; | |
| | | | |
| /** | | /** | |
|
| * gets a cached Monitor per name or will create if doesn't exist | | * Data structure for keeping track of the states of individual rep | |
| | | lica | |
| | | * members. This class is not thread-safe so proper measures should | |
| | | be taken | |
| | | * when sharing this object across multiple threads. | |
| | | * | |
| | | * Note: these get copied around in the nodes vector so be sure to | |
| | | maintain | |
| | | * copyable semantics here | |
| */ | | */ | |
|
| static ReplicaSetMonitorPtr get( const string& name , const vector< | | struct Node { | |
| HostAndPort>& servers ); | | Node( const HostAndPort& a , DBClientConnection* c ) : | |
| | | addr( a ), | |
| | | conn(c), | |
| | | ok( c != NULL ), | |
| | | ismaster(false), | |
| | | secondary( false ), | |
| | | hidden( false ), | |
| | | pingTimeMillis( 0 ) { | |
| | | } | |
| | | | |
| | | bool okForSecondaryQueries() const { | |
| | | return ok && secondary && ! hidden; | |
| | | } | |
| | | | |
| | | /** | |
| | | * Checks if the given tag matches the tag attached to this nod | |
| | | e. | |
| | | * | |
| | | * Example: | |
| | | * | |
| | | * Tag of this node: { "dc": "nyc", "region": "na", "rack": "4" | |
| | | } | |
| | | * | |
| | | * match: {} | |
| | | * match: { "dc": "nyc", "rack": 4 } | |
| | | * match: { "region": "na", "dc": "nyc" } | |
| | | * not match: { "dc: "nyc", "rack": 2 } | |
| | | * not match: { "dc": "sf" } | |
| | | * | |
| | | * @param tag the tag to use to compare. Should not contain any | |
| | | * embedded documents. | |
| | | * | |
| | | * @return true if the given tag matches the this node's tag | |
| | | * specification | |
| | | */ | |
| | | bool matchesTag( const BSONObj& tag ) const; | |
| | | | |
| | | /** | |
| | | * @param threshold max ping time (in ms) to be considered lo | |
| | | cal | |
| | | * @return true if node is a local secondary, and can handle qu | |
| | | eries | |
| | | **/ | |
| | | bool isLocalSecondary( const int threshold ) const { | |
| | | return pingTimeMillis < threshold; | |
| | | } | |
| | | | |
| | | BSONObj toBSON() const; | |
| | | | |
| | | string toString() const { | |
| | | return toBSON().toString(); | |
| | | } | |
| | | | |
| | | HostAndPort addr; | |
| | | boost::shared_ptr<DBClientConnection> conn; | |
| | | | |
| | | // if this node is in a failure state | |
| | | // used for slave routing | |
| | | // this is too simple, should make it better | |
| | | bool ok; | |
| | | | |
| | | // as reported by ismaster | |
| | | BSONObj lastIsMaster; | |
| | | | |
| | | bool ismaster; | |
| | | bool secondary; | |
| | | bool hidden; | |
| | | | |
| | | int pingTimeMillis; | |
| | | | |
| | | }; | |
| | | | |
| /** | | /** | |
|
| * gets a cached Monitor per name or will return none if it doesn't | | * Selects the right node given the nodes to pick from and the pref | |
| exist | | erence. | |
| | | * | |
| | | * @param nodes the nodes to select from | |
| | | * @param readPreference the read mode to use | |
| | | * @param readPreferenceTag the tags used for filtering nodes | |
| | | * @param localThresholdMillis the exclusive upper bound of ping ti | |
| | | me to be | |
| | | * considered as a local node. Local nodes are favored over non | |
| | | -local | |
| | | * nodes if multiple nodes matches the other criteria. | |
| | | * @param primaryNodeIndex the index of the primary node | |
| | | * @param nextNodeIndex the index of the next node to begin from ch | |
| | | ecking. | |
| | | * Can advance to a different index (mainly used for doing roun | |
| | | d-robin). | |
| | | * | |
| | | * @return the host object of the node selected. If none of the nod | |
| | | es are | |
| | | * eligible, returns an empty host. | |
| */ | | */ | |
|
| static ReplicaSetMonitorPtr get( const string& name ); | | static HostAndPort selectNode( const std::vector<Node>& nodes, | |
| | | ReadPreference readPreference, | |
| | | const BSONObj& readPreferenceTag, | |
| | | int localThresholdMillis, | |
| | | int primaryNodeIndex, | |
| | | int& nextNodeIndex ); | |
| | | | |
| | | /** | |
| | | * Creates a new ReplicaSetMonitor, if it doesn't already exist. | |
| | | */ | |
| | | static void createIfNeeded( const string& name , const vector<HostA | |
| | | ndPort>& servers ); | |
| | | | |
| | | /** | |
| | | * gets a cached Monitor per name. If the monitor is not found and | |
| | | createFromSeed is false, | |
| | | * it will return none. If createFromSeed is true, it will try to l | |
| | | ook up the last known | |
| | | * servers list for this set and will create a new monitor using th | |
| | | at as the seed list. | |
| | | */ | |
| | | static ReplicaSetMonitorPtr get( const string& name, const bool cre | |
| | | ateFromSeed = false ); | |
| | | | |
| /** | | /** | |
| * checks all sets for current master and new secondaries | | * checks all sets for current master and new secondaries | |
| * usually only called from a BackgroundJob | | * usually only called from a BackgroundJob | |
| */ | | */ | |
| static void checkAll( bool checkAllSecondaries ); | | static void checkAll( bool checkAllSecondaries ); | |
| | | | |
| /** | | /** | |
|
| * deletes the ReplicaSetMonitor for the given set name. | | * Removes the ReplicaSetMonitor for the given set name from _sets, | |
| | | which will delete it. | |
| | | * If clearSeedCache is true, then the cached seed string for this | |
| | | Replica Set will be removed | |
| | | * from _setServers. | |
| */ | | */ | |
|
| static void remove( const string& name ); | | static void remove( const string& name, bool clearSeedCache = false | |
| | | ); | |
| | | | |
| | | static int getMaxFailedChecks() { return _maxFailedChecks; }; | |
| | | static void setMaxFailedChecks(int numChecks) { _maxFailedChecks = | |
| | | numChecks; }; | |
| | | | |
| /** | | /** | |
| * this is called whenever the config of any replica set changes | | * this is called whenever the config of any replica set changes | |
| * currently only 1 globally | | * currently only 1 globally | |
| * asserts if one already exists | | * asserts if one already exists | |
| * ownership passes to ReplicaSetMonitor and the hook will actually
never be deleted | | * ownership passes to ReplicaSetMonitor and the hook will actually
never be deleted | |
| */ | | */ | |
| static void setConfigChangeHook( ConfigChangeHook hook ); | | static void setConfigChangeHook( ConfigChangeHook hook ); | |
| | | | |
| ~ReplicaSetMonitor(); | | ~ReplicaSetMonitor(); | |
| | | | |
| skipping to change at line 90 | | skipping to change at line 197 | |
| HostAndPort getMaster(); | | HostAndPort getMaster(); | |
| | | | |
| /** | | /** | |
| * notify the monitor that server has faild | | * notify the monitor that server has faild | |
| */ | | */ | |
| void notifyFailure( const HostAndPort& server ); | | void notifyFailure( const HostAndPort& server ); | |
| | | | |
| /** @return prev if its still ok, and if not returns a random slave
that is ok for reads */ | | /** @return prev if its still ok, and if not returns a random slave
that is ok for reads */ | |
| HostAndPort getSlave( const HostAndPort& prev ); | | HostAndPort getSlave( const HostAndPort& prev ); | |
| | | | |
|
| /** @return a random slave that is ok for reads */ | | /** | |
| HostAndPort getSlave(); | | * @param preferLocal Prefer a local secondary, otherwise pick an | |
| | | y | |
| | | * secondary, or fall back to primary | |
| | | * @return a random slave that is ok for reads | |
| | | */ | |
| | | HostAndPort getSlave( bool preferLocal = true ); | |
| | | | |
| /** | | /** | |
| * notify the monitor that server has faild | | * notify the monitor that server has faild | |
| */ | | */ | |
| void notifySlaveFailure( const HostAndPort& server ); | | void notifySlaveFailure( const HostAndPort& server ); | |
| | | | |
| /** | | /** | |
| * checks for current master and new secondaries | | * checks for current master and new secondaries | |
| */ | | */ | |
| void check( bool checkAllSecondaries ); | | void check( bool checkAllSecondaries ); | |
| | | | |
| string getName() const { return _name; } | | string getName() const { return _name; } | |
| | | | |
| string getServerAddress() const; | | string getServerAddress() const; | |
| | | | |
| bool contains( const string& server ) const; | | bool contains( const string& server ) const; | |
| | | | |
| void appendInfo( BSONObjBuilder& b ) const; | | void appendInfo( BSONObjBuilder& b ) const; | |
| | | | |
|
| | | /** | |
| | | * Set the threshold value (in ms) for a node to be considered loca | |
| | | l. | |
| | | * NOTE: This function acquires the _lock mutex. | |
| | | **/ | |
| | | void setLocalThresholdMillis( const int millis ); | |
| | | | |
| private: | | private: | |
| /** | | /** | |
| * This populates a list of hosts from the list of seeds (discardin
g the | | * This populates a list of hosts from the list of seeds (discardin
g the | |
|
| * seed list). | | * seed list). Should only be called from within _setsLock. | |
| * @param name set name | | * @param name set name | |
| * @param servers seeds | | * @param servers seeds | |
| */ | | */ | |
| ReplicaSetMonitor( const string& name , const vector<HostAndPort>&
servers ); | | ReplicaSetMonitor( const string& name , const vector<HostAndPort>&
servers ); | |
| | | | |
|
| | | static void _remove_inlock( const string& name, bool clearSeedCache | |
| | | = false ); | |
| | | | |
| /** | | /** | |
| * Checks all connections from the host list and sets the current | | * Checks all connections from the host list and sets the current | |
| * master. | | * master. | |
| * | | * | |
| * @param checkAllSecondaries if set to false, stop immediately whe
n | | * @param checkAllSecondaries if set to false, stop immediately whe
n | |
| * the master is found or when _master is not -1. | | * the master is found or when _master is not -1. | |
| */ | | */ | |
| void _check( bool checkAllSecondaries ); | | void _check( bool checkAllSecondaries ); | |
| | | | |
| /** | | /** | |
| | | | |
| skipping to change at line 159 | | skipping to change at line 278 | |
| * @param maybePrimary OUT | | * @param maybePrimary OUT | |
| * @param verbose | | * @param verbose | |
| * @param nodesOffset - offset into _nodes array, -1 for not in it | | * @param nodesOffset - offset into _nodes array, -1 for not in it | |
| * | | * | |
| * @return true if the connection is good or false if invariant | | * @return true if the connection is good or false if invariant | |
| * is broken | | * is broken | |
| */ | | */ | |
| bool _checkConnection( DBClientConnection* conn, string& maybePrima
ry, | | bool _checkConnection( DBClientConnection* conn, string& maybePrima
ry, | |
| bool verbose, int nodesOffset ); | | bool verbose, int nodesOffset ); | |
| | | | |
|
| | | /** | |
| | | * Save the seed list for the current set into the _setServers map | |
| | | * Should only be called if you're already holding _setsLock and th | |
| | | is | |
| | | * monitor's _lock. | |
| | | */ | |
| | | void _cacheServerAddresses_inlock(); | |
| | | | |
| string _getServerAddress_inlock() const; | | string _getServerAddress_inlock() const; | |
| | | | |
| NodeDiff _getHostDiff_inlock( const BSONObj& hostList ); | | NodeDiff _getHostDiff_inlock( const BSONObj& hostList ); | |
| bool _shouldChangeHosts( const BSONObj& hostList, bool inlock ); | | bool _shouldChangeHosts( const BSONObj& hostList, bool inlock ); | |
| | | | |
| /** | | /** | |
| * @return the index to _nodes corresponding to the server address. | | * @return the index to _nodes corresponding to the server address. | |
| */ | | */ | |
| int _find( const string& server ) const ; | | int _find( const string& server ) const ; | |
| int _find_inlock( const string& server ) const ; | | int _find_inlock( const string& server ) const ; | |
| | | | |
| /** | | /** | |
| * Checks whether the given connection matches the connection store
d in _nodes. | | * Checks whether the given connection matches the connection store
d in _nodes. | |
| * Mainly used for sanity checking to confirm that nodeOffset still | | * Mainly used for sanity checking to confirm that nodeOffset still | |
| * refers to the right connection after releasing and reacquiring | | * refers to the right connection after releasing and reacquiring | |
| * a mutex. | | * a mutex. | |
| */ | | */ | |
| bool _checkConnMatch_inlock( DBClientConnection* conn, size_t nodeO
ffset ) const; | | bool _checkConnMatch_inlock( DBClientConnection* conn, size_t nodeO
ffset ) const; | |
| | | | |
|
| // protects _nodes and indices pointing to it (_master & _nextSlave | | /** | |
| ) | | * Selects the right node given the nodes to pick from and the pref | |
| | | erence. | |
| | | * | |
| | | * @param nodes the nodes to select from | |
| | | * @param readPreferenceTag the tags to use for choosing the right | |
| | | node | |
| | | * @param secOnly never select a primary if true | |
| | | * @param localThresholdMillis the exclusive upper bound of ping ti | |
| | | me to be | |
| | | * considered as a local node. Local nodes are favored over non | |
| | | -local | |
| | | * nodes if multiple nodes matches the other criteria. | |
| | | * @param nextNodeIndex the index of the next node to begin from ch | |
| | | ecking. | |
| | | * Can advance to a different index (mainly used for doing roun | |
| | | d-robin). | |
| | | * | |
| | | * @return the host object of the node selected. If none of the nod | |
| | | es are | |
| | | * eligible, returns an empty host. | |
| | | */ | |
| | | static HostAndPort selectNode( const std::vector<Node>& nodes, | |
| | | const BSONObj& readPreferenceTag, | |
| | | bool secOnly, | |
| | | int localThresholdMillis, | |
| | | int& nextNodeIndex ); | |
| | | | |
| | | /** | |
| | | * @return the primary if it is ok to use, otherwise returns an emp | |
| | | ty | |
| | | * HostAndPort object. | |
| | | */ | |
| | | static HostAndPort checkPrimary( const std::vector<Node>& nodes, | |
| | | int primaryNodeIndex ); | |
| | | | |
| | | // protects _localThresholdMillis, _nodes and refs to _nodes (eg. _ | |
| | | master & _nextSlave) | |
| mutable mongo::mutex _lock; | | mutable mongo::mutex _lock; | |
| | | | |
| /** | | /** | |
| * "Synchronizes" the _checkConnection method. Should ideally be on
e mutex per | | * "Synchronizes" the _checkConnection method. Should ideally be on
e mutex per | |
| * connection object being used. The purpose of this lock is to mak
e sure that | | * connection object being used. The purpose of this lock is to mak
e sure that | |
| * the reply from the connection the lock holder got is the actual
response | | * the reply from the connection the lock holder got is the actual
response | |
| * to what it sent. | | * to what it sent. | |
| * | | * | |
| * Deadlock WARNING: never acquire this while holding _lock | | * Deadlock WARNING: never acquire this while holding _lock | |
| */ | | */ | |
| mutable mongo::mutex _checkConnectionLock; | | mutable mongo::mutex _checkConnectionLock; | |
| | | | |
| string _name; | | string _name; | |
| | | | |
|
| // note these get copied around in the nodes vector so be sure to m | | | |
| aintain copyable semantics here | | | |
| struct Node { | | | |
| Node( const HostAndPort& a , DBClientConnection* c ) | | | |
| : addr( a ) , conn(c) , ok(true) , | | | |
| ismaster(false), secondary( false ) , hidden( false ) , p | | | |
| ingTimeMillis(0) { | | | |
| ok = conn.get() == NULL; | | | |
| } | | | |
| | | | |
| bool okForSecondaryQueries() const { | | | |
| return ok && secondary && ! hidden; | | | |
| } | | | |
| | | | |
| BSONObj toBSON() const { | | | |
| return BSON( "addr" << addr.toString() << | | | |
| "isMaster" << ismaster << | | | |
| "secondary" << secondary << | | | |
| "hidden" << hidden << | | | |
| "ok" << ok ); | | | |
| } | | | |
| | | | |
| string toString() const { | | | |
| return toBSON().toString(); | | | |
| } | | | |
| | | | |
| HostAndPort addr; | | | |
| shared_ptr<DBClientConnection> conn; | | | |
| | | | |
| // if this node is in a failure state | | | |
| // used for slave routing | | | |
| // this is too simple, should make it better | | | |
| bool ok; | | | |
| | | | |
| // as reported by ismaster | | | |
| BSONObj lastIsMaster; | | | |
| | | | |
| bool ismaster; | | | |
| bool secondary; | | | |
| bool hidden; | | | |
| | | | |
| int pingTimeMillis; | | | |
| | | | |
| }; | | | |
| | | | |
| /** | | /** | |
| * Host list. | | * Host list. | |
| */ | | */ | |
|
| vector<Node> _nodes; | | std::vector<Node> _nodes; | |
| | | | |
| int _master; // which node is the current master. -1 means no mast
er is known | | int _master; // which node is the current master. -1 means no mast
er is known | |
| int _nextSlave; // which node is the current slave | | int _nextSlave; // which node is the current slave | |
|
| | | // The number of consecutive times the set has been checked and eve | |
| | | ry member in the set was down. | |
| | | int _failedChecks; | |
| | | | |
|
| static mongo::mutex _setsLock; // protects _sets | | static mongo::mutex _setsLock; // protects _sets and _setServers | |
| static map<string,ReplicaSetMonitorPtr> _sets; // set name to Monit
or | | static map<string,ReplicaSetMonitorPtr> _sets; // set name to Monit
or | |
|
| | | static map<string,vector<HostAndPort> > _setServers; // set name to
seed list. Used to rebuild the monitor if it is cleaned up but then the se
t is accessed again. | |
| | | | |
| static ConfigChangeHook _hook; | | static ConfigChangeHook _hook; | |
|
| | | int _localThresholdMillis; // local ping latency threshold (protect | |
| | | ed by _lock) | |
| | | | |
| | | static int _maxFailedChecks; | |
| }; | | }; | |
| | | | |
| /** Use this class to connect to a replica set of servers. The class w
ill manage | | /** Use this class to connect to a replica set of servers. The class w
ill manage | |
| checking for which server in a replica set is master, and do failove
r automatically. | | checking for which server in a replica set is master, and do failove
r automatically. | |
| | | | |
| This can also be used to connect to replica pairs since pairs are a
subset of sets | | This can also be used to connect to replica pairs since pairs are a
subset of sets | |
| | | | |
| On a failover situation, expect at least one operation to return an
error (throw | | On a failover situation, expect at least one operation to return an
error (throw | |
| an exception) before the failover is complete. Operations are not r
etried. | | an exception) before the failover is complete. Operations are not r
etried. | |
| */ | | */ | |
| class DBClientReplicaSet : public DBClientBase { | | class DBClientReplicaSet : public DBClientBase { | |
| public: | | public: | |
| using DBClientBase::query; | | using DBClientBase::query; | |
|
| | | using DBClientBase::update; | |
| | | using DBClientBase::remove; | |
| | | | |
| /** Call connect() after constructing. autoReconnect is always on f
or DBClientReplicaSet connections. */ | | /** Call connect() after constructing. autoReconnect is always on f
or DBClientReplicaSet connections. */ | |
| DBClientReplicaSet( const string& name , const vector<HostAndPort>&
servers, double so_timeout=0 ); | | DBClientReplicaSet( const string& name , const vector<HostAndPort>&
servers, double so_timeout=0 ); | |
| virtual ~DBClientReplicaSet(); | | virtual ~DBClientReplicaSet(); | |
| | | | |
| /** Returns false if nomember of the set were reachable, or neither
is | | /** Returns false if nomember of the set were reachable, or neither
is | |
| * master, although, | | * master, although, | |
| * when false returned, you can still try to use this connection ob
ject, it will | | * when false returned, you can still try to use this connection ob
ject, it will | |
| * try reconnects. | | * try reconnects. | |
| */ | | */ | |
| | | | |
| skipping to change at line 292 | | skipping to change at line 411 | |
| | | | |
| /** throws userassertion "no master found" */ | | /** throws userassertion "no master found" */ | |
| virtual BSONObj findOne(const string &ns, const Query& query, const
BSONObj *fieldsToReturn = 0, int queryOptions = 0); | | virtual BSONObj findOne(const string &ns, const Query& query, const
BSONObj *fieldsToReturn = 0, int queryOptions = 0); | |
| | | | |
| virtual void insert( const string &ns , BSONObj obj , int flags=0); | | virtual void insert( const string &ns , BSONObj obj , int flags=0); | |
| | | | |
| /** insert multiple objects. Note that single object insert is asy
nchronous, so this version | | /** insert multiple objects. Note that single object insert is asy
nchronous, so this version | |
| is only nominally faster and not worth a special effort to try
to use. */ | | is only nominally faster and not worth a special effort to try
to use. */ | |
| virtual void insert( const string &ns, const vector< BSONObj >& v ,
int flags=0); | | virtual void insert( const string &ns, const vector< BSONObj >& v ,
int flags=0); | |
| | | | |
|
| virtual void remove( const string &ns , Query obj , bool justOne =
0 ); | | virtual void remove( const string &ns , Query obj , int flags ); | |
| | | | |
|
| virtual void update( const string &ns , Query query , BSONObj obj ,
bool upsert = 0 , bool multi = 0 ); | | virtual void update( const string &ns , Query query , BSONObj obj ,
int flags ); | |
| | | | |
| virtual void killCursor( long long cursorID ); | | virtual void killCursor( long long cursorID ); | |
| | | | |
| // ---- access raw connections ---- | | // ---- access raw connections ---- | |
| | | | |
| DBClientConnection& masterConn(); | | DBClientConnection& masterConn(); | |
| DBClientConnection& slaveConn(); | | DBClientConnection& slaveConn(); | |
| | | | |
| // ---- callback pieces ------- | | // ---- callback pieces ------- | |
| | | | |
| | | | |
| skipping to change at line 327 | | skipping to change at line 446 | |
| // ----- status ------ | | // ----- status ------ | |
| | | | |
| virtual bool isFailed() const { return ! _master || _master->isFail
ed(); } | | virtual bool isFailed() const { return ! _master || _master->isFail
ed(); } | |
| | | | |
| // ----- informational ---- | | // ----- informational ---- | |
| | | | |
| double getSoTimeout() const { return _so_timeout; } | | double getSoTimeout() const { return _so_timeout; } | |
| | | | |
| string toString() { return getServerAddress(); } | | string toString() { return getServerAddress(); } | |
| | | | |
|
| string getServerAddress() const { return _monitor->getServerAddress
(); } | | string getServerAddress() const; | |
| | | | |
| virtual ConnectionString::ConnectionType type() const { return Conn
ectionString::SET; } | | virtual ConnectionString::ConnectionType type() const { return Conn
ectionString::SET; } | |
| virtual bool lazySupported() const { return true; } | | virtual bool lazySupported() const { return true; } | |
| | | | |
| // ---- low level ------ | | // ---- low level ------ | |
| | | | |
| virtual bool call( Message &toSend, Message &response, bool assertO
k=true , string * actualServer = 0 ); | | virtual bool call( Message &toSend, Message &response, bool assertO
k=true , string * actualServer = 0 ); | |
| virtual bool callRead( Message& toSend , Message& response ) { retu
rn checkMaster()->callRead( toSend , response ); } | | virtual bool callRead( Message& toSend , Message& response ) { retu
rn checkMaster()->callRead( toSend , response ); } | |
| | | | |
| protected: | | protected: | |
| | | | |
| skipping to change at line 350 | | skipping to change at line 469 | |
| private: | | private: | |
| | | | |
| // Used to simplify slave-handling logic on errors | | // Used to simplify slave-handling logic on errors | |
| auto_ptr<DBClientCursor> checkSlaveQueryResult( auto_ptr<DBClientCu
rsor> result ); | | auto_ptr<DBClientCursor> checkSlaveQueryResult( auto_ptr<DBClientCu
rsor> result ); | |
| | | | |
| DBClientConnection * checkMaster(); | | DBClientConnection * checkMaster(); | |
| DBClientConnection * checkSlave(); | | DBClientConnection * checkSlave(); | |
| | | | |
| void _auth( DBClientConnection * conn ); | | void _auth( DBClientConnection * conn ); | |
| | | | |
|
| ReplicaSetMonitorPtr _monitor; | | // Throws a DBException if the monitor doesn't exist and there isn' | |
| | | t a cached seed to use. | |
| | | ReplicaSetMonitorPtr _getMonitor() const; | |
| | | | |
| | | string _setName; | |
| | | | |
| HostAndPort _masterHost; | | HostAndPort _masterHost; | |
| scoped_ptr<DBClientConnection> _master; | | scoped_ptr<DBClientConnection> _master; | |
| | | | |
| HostAndPort _slaveHost; | | HostAndPort _slaveHost; | |
| scoped_ptr<DBClientConnection> _slave; | | scoped_ptr<DBClientConnection> _slave; | |
| | | | |
| double _so_timeout; | | double _so_timeout; | |
| | | | |
| /** | | /** | |
| | | | |
End of changes. 23 change blocks. |
| 64 lines changed or deleted | | 219 lines changed or added | |
|
| dbclientinterface.h | | dbclientinterface.h | |
| | | | |
| skipping to change at line 25 | | skipping to change at line 25 | |
| * distributed under the License is distributed on an "AS IS" BASIS, | | * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli
ed. | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli
ed. | |
| * See the License for the specific language governing permissions and | | * See the License for the specific language governing permissions and | |
| * limitations under the License. | | * limitations under the License. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include "pch.h" | | #include "pch.h" | |
| | | | |
|
| #include "mongo/db/authlevel.h" | | #include "mongo/client/authlevel.h" | |
| | | #include "mongo/client/authentication_table.h" | |
| #include "mongo/db/jsobj.h" | | #include "mongo/db/jsobj.h" | |
| #include "mongo/util/net/message.h" | | #include "mongo/util/net/message.h" | |
| #include "mongo/util/net/message_port.h" | | #include "mongo/util/net/message_port.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| /** the query field 'options' can have these bits set: */ | | /** the query field 'options' can have these bits set: */ | |
| enum QueryOptions { | | enum QueryOptions { | |
| /** Tailable means cursor is not closed when the last data is retri
eved. rather, the cursor marks | | /** Tailable means cursor is not closed when the last data is retri
eved. rather, the cursor marks | |
| the final object's position. you can resume using the cursor la
ter, from where it was located, | | the final object's position. you can resume using the cursor la
ter, from where it was located, | |
| | | | |
| skipping to change at line 115 | | skipping to change at line 116 | |
| }; | | }; | |
| | | | |
| /** | | /** | |
| * need to put in DbMesssage::ReservedOptions as well | | * need to put in DbMesssage::ReservedOptions as well | |
| */ | | */ | |
| enum InsertOptions { | | enum InsertOptions { | |
| /** With muli-insert keep processing inserts if one fails */ | | /** With muli-insert keep processing inserts if one fails */ | |
| InsertOption_ContinueOnError = 1 << 0 | | InsertOption_ContinueOnError = 1 << 0 | |
| }; | | }; | |
| | | | |
|
| | | /** | |
| | | * Start from *top* of bits, these are generic write options that apply | |
| | | to all | |
| | | */ | |
| | | enum WriteOptions { | |
| | | /** logical writeback option */ | |
| | | WriteOption_FromWriteback = 1 << 31 | |
| | | }; | |
| | | | |
| | | // | |
| | | // For legacy reasons, the reserved field pre-namespace of certain type | |
| | | s of messages is used | |
| | | // to store options as opposed to the flags after the namespace. This | |
| | | should be transparent to | |
| | | // the api user, but we need these constants to disassemble/reassemble | |
| | | the messages correctly. | |
| | | // | |
| | | | |
| | | enum ReservedOptions { | |
| | | Reserved_InsertOption_ContinueOnError = 1 << 0 , | |
| | | Reserved_FromWriteback = 1 << 1 | |
| | | }; | |
| | | | |
| | | enum ReadPreference { | |
| | | /** | |
| | | * Read from primary only. All operations produce an error (throw a | |
| | | n | |
| | | * exception where applicable) if primary is unavailable. Cannot be | |
| | | * combined with tags. | |
| | | */ | |
| | | ReadPreference_PrimaryOnly = 0, | |
| | | | |
| | | /** | |
| | | * Read from primary if available, otherwise a secondary. Tags will | |
| | | * only be applied in the event that the primary is unavailable and | |
| | | * a secondary is read from. In this event only secondaries matchin | |
| | | g | |
| | | * the tags provided would be read from. | |
| | | */ | |
| | | ReadPreference_PrimaryPreferred, | |
| | | | |
| | | /** | |
| | | * Read from secondary if available, otherwise error. | |
| | | */ | |
| | | ReadPreference_SecondaryOnly, | |
| | | | |
| | | /** | |
| | | * Read from a secondary if available, otherwise read from the prim | |
| | | ary. | |
| | | */ | |
| | | ReadPreference_SecondaryPreferred, | |
| | | | |
| | | /** | |
| | | * Read from any member. | |
| | | */ | |
| | | ReadPreference_Nearest, | |
| | | }; | |
| | | | |
| class DBClientBase; | | class DBClientBase; | |
| | | | |
| /** | | /** | |
| * ConnectionString handles parsing different ways to connect to mongo
and determining method | | * ConnectionString handles parsing different ways to connect to mongo
and determining method | |
| * samples: | | * samples: | |
| * server | | * server | |
| * server:port | | * server:port | |
| * foo/server:port,server:port SET | | * foo/server:port,server:port SET | |
| * server,server,server SYNC | | * server,server,server SYNC | |
| * | | * | |
| * tyipcal use | | * tyipcal use | |
| * string errmsg, | | * string errmsg, | |
| * ConnectionString cs = ConnectionString::parse( url , errmsg ); | | * ConnectionString cs = ConnectionString::parse( url , errmsg ); | |
| * if ( ! cs.isValid() ) throw "bad: " + errmsg; | | * if ( ! cs.isValid() ) throw "bad: " + errmsg; | |
| * DBClientBase * conn = cs.connect( errmsg ); | | * DBClientBase * conn = cs.connect( errmsg ); | |
| */ | | */ | |
| class ConnectionString { | | class ConnectionString { | |
| public: | | public: | |
|
| enum ConnectionType { INVALID , MASTER , PAIR , SET , SYNC }; | | enum ConnectionType { INVALID , MASTER , PAIR , SET , SYNC, CUSTOM
}; | |
| | | | |
| ConnectionString() { | | ConnectionString() { | |
| _type = INVALID; | | _type = INVALID; | |
| } | | } | |
| | | | |
| ConnectionString( const HostAndPort& server ) { | | ConnectionString( const HostAndPort& server ) { | |
| _type = MASTER; | | _type = MASTER; | |
| _servers.push_back( server ); | | _servers.push_back( server ); | |
| _finishInit(); | | _finishInit(); | |
| } | | } | |
| | | | |
| skipping to change at line 201 | | skipping to change at line 253 | |
| string getSetName() const { return _setName; } | | string getSetName() const { return _setName; } | |
| | | | |
| vector<HostAndPort> getServers() const { return _servers; } | | vector<HostAndPort> getServers() const { return _servers; } | |
| | | | |
| ConnectionType type() const { return _type; } | | ConnectionType type() const { return _type; } | |
| | | | |
| static ConnectionString parse( const string& url , string& errmsg )
; | | static ConnectionString parse( const string& url , string& errmsg )
; | |
| | | | |
| static string typeToString( ConnectionType type ); | | static string typeToString( ConnectionType type ); | |
| | | | |
|
| | | // | |
| | | // Allow overriding the default connection behavior | |
| | | // This is needed for some tests, which otherwise would fail becaus | |
| | | e they are unable to contact | |
| | | // the correct servers. | |
| | | // | |
| | | | |
| | | class ConnectionHook { | |
| | | public: | |
| | | virtual ~ConnectionHook(){} | |
| | | | |
| | | // Returns an alternative connection object for a string | |
| | | virtual DBClientBase* connect( const ConnectionString& c, | |
| | | string& errmsg, | |
| | | double socketTimeout ) = 0; | |
| | | }; | |
| | | | |
| | | static void setConnectionHook( ConnectionHook* hook ){ | |
| | | scoped_lock lk( _connectHookMutex ); | |
| | | _connectHook = hook; | |
| | | } | |
| | | | |
| private: | | private: | |
| | | | |
| void _fillServers( string s ); | | void _fillServers( string s ); | |
| void _finishInit(); | | void _finishInit(); | |
| | | | |
| ConnectionType _type; | | ConnectionType _type; | |
| vector<HostAndPort> _servers; | | vector<HostAndPort> _servers; | |
| string _string; | | string _string; | |
| string _setName; | | string _setName; | |
|
| | | | |
| | | static mutex _connectHookMutex; | |
| | | static ConnectionHook* _connectHook; | |
| }; | | }; | |
| | | | |
| /** | | /** | |
| * controls how much a clients cares about writes | | * controls how much a clients cares about writes | |
| * default is NORMAL | | * default is NORMAL | |
| */ | | */ | |
| 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 | |
| | | | |
| skipping to change at line 431 | | skipping to change at line 507 | |
| public: | | public: | |
| virtual auto_ptr<DBClientCursor> query(const string &ns, Query quer
y, int nToReturn = 0, int nToSkip = 0, | | virtual auto_ptr<DBClientCursor> query(const string &ns, Query quer
y, int nToReturn = 0, int nToSkip = 0, | |
| const BSONObj *fieldsToRetur
n = 0, int queryOptions = 0 , int batchSize = 0 ) = 0; | | const BSONObj *fieldsToRetur
n = 0, int queryOptions = 0 , int batchSize = 0 ) = 0; | |
| | | | |
| virtual void insert( const string &ns, BSONObj obj , int flags=0) =
0; | | virtual void insert( const string &ns, BSONObj obj , int flags=0) =
0; | |
| | | | |
| virtual void insert( const string &ns, const vector< BSONObj >& v ,
int flags=0) = 0; | | virtual void insert( const string &ns, const vector< BSONObj >& v ,
int flags=0) = 0; | |
| | | | |
| virtual void remove( const string &ns , Query query, bool justOne =
0 ) = 0; | | virtual void remove( const string &ns , Query query, bool justOne =
0 ) = 0; | |
| | | | |
|
| virtual void update( const string &ns , Query query , BSONObj obj , | | virtual void remove( const string &ns , Query query, int flags ) = | |
| bool upsert = 0 , bool multi = 0 ) = 0; | | 0; | |
| | | | |
| | | virtual void update( const string &ns, | |
| | | Query query, | |
| | | BSONObj obj, | |
| | | bool upsert = false, bool multi = false ) = 0; | |
| | | | |
| | | virtual void update( const string &ns, Query query, BSONObj obj, in | |
| | | t flags ) = 0; | |
| | | | |
| virtual ~DBClientInterface() { } | | virtual ~DBClientInterface() { } | |
| | | | |
| /** | | /** | |
| @return a single object that matches the query. if none do, the
n the object is empty | | @return a single object that matches the query. if none do, the
n the object is empty | |
| @throws AssertionException | | @throws AssertionException | |
| */ | | */ | |
| virtual BSONObj findOne(const string &ns, const Query& query, const
BSONObj *fieldsToReturn = 0, int queryOptions = 0); | | virtual BSONObj findOne(const string &ns, const Query& query, const
BSONObj *fieldsToReturn = 0, int queryOptions = 0); | |
| | | | |
| /** query N objects from the database into an array. makes sense m
ostly when you want a small number of results. if a huge number, use | | /** query N objects from the database into an array. makes sense m
ostly when you want a small number of results. if a huge number, use | |
| | | | |
| skipping to change at line 462 | | skipping to change at line 545 | |
| /** | | /** | |
| 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), _cachedAvailableOptions( (en | | DBClientWithCommands() : _logLevel(0), | |
| um QueryOptions)0 ), _haveCachedAvailableOptions(false) { } | | _cachedAvailableOptions( (enum QueryOptions)0 ), | |
| | | _haveCachedAvailableOptions(false), | |
| | | _hasAuthentication(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 | |
| commands have prebuilt helper functions -- see below. If a hel
per is not available you can | | commands have prebuilt helper functions -- see below. If a hel
per is not available you can | |
|
| directly call runCommand. | | directly call runCommand. If _authTable has been set, will app | |
| | | end a BSON representation of | |
| | | that AuthenticationTable to the command object, unless an Authe | |
| | | nticationTable object has been | |
| | | passed to this method directly, in which case it will use that | |
| | | instead of _authTable. | |
| | | | |
| @param dbname database name. Use "admin" for global administra
tive commands. | | @param dbname database name. Use "admin" for global administra
tive commands. | |
| @param cmd the command object to execute. For example, { isma
ster : 1 } | | @param cmd the command object to execute. For example, { isma
ster : 1 } | |
| @param info the result object the database returns. Typically h
as { ok : ..., errmsg : ... } fields | | @param info the result object the database returns. Typically h
as { ok : ..., errmsg : ... } fields | |
| set. | | set. | |
| @param options see enum QueryOptions - normally not needed to r
un a command | | @param options see enum QueryOptions - normally not needed to r
un a command | |
|
| | | @param auth if set, the BSONObj representation will be appended | |
| | | to the command object sent | |
| | | | |
| @return true if the command returned "ok". | | @return true if the command returned "ok". | |
| */ | | */ | |
|
| virtual bool runCommand(const string &dbname, const BSONObj& cmd, B | | virtual bool runCommand(const string &dbname, const BSONObj& cmd, B | |
| SONObj &info, int options=0); | | SONObj &info, | |
| | | int options=0, const AuthenticationTable* a | |
| | | uth = NULL); | |
| | | | |
| /** Authorize access to a particular database. | | /** Authorize access to a particular database. | |
| Authentication is separate for each database on the server -- y
ou may authenticate for any | | Authentication is separate for each database on the server -- y
ou may authenticate for any | |
| number of databases on a single connection. | | number of databases on a single connection. | |
| The "admin" database is special and once authenticated provides
access to all databases on the | | The "admin" database is special and once authenticated provides
access to all databases on the | |
| server. | | server. | |
| @param digestPassword if password is plain text, set this
to true. otherwise assumed to be pre-digested | | @param digestPassword if password is plain text, set this
to true. otherwise assumed to be pre-digested | |
| @param[out] authLevel level of authentication for the giv
en user | | @param[out] authLevel level of authentication for the giv
en user | |
| @return true if successful | | @return true if successful | |
| */ | | */ | |
| | | | |
| skipping to change at line 563 | | skipping to change at line 654 | |
| result.err will be null if no error has occurred. | | result.err will be null if no error has occurred. | |
| */ | | */ | |
| BSONObj getPrevError(); | | BSONObj getPrevError(); | |
| | | | |
| /** Reset the previous error state for this connection (accessed vi
a getLastError and | | /** Reset the previous error state for this connection (accessed vi
a getLastError and | |
| getPrevError). Useful when performing several operations at on
ce and then checking | | getPrevError). Useful when performing several operations at on
ce and then checking | |
| for an error after attempting all operations. | | for an error after attempting all operations. | |
| */ | | */ | |
| bool resetError() { return simpleCommand("admin", 0, "reseterror");
} | | bool resetError() { return simpleCommand("admin", 0, "reseterror");
} | |
| | | | |
|
| /** Delete the specified collection. */ | | /** Delete the specified collection. | |
| virtual bool dropCollection( const string &ns ) { | | * @param info An optional output parameter that receives the resu | |
| | | lt object the database | |
| | | * returns from the drop command. May be null if the caller doesn | |
| | | 't need that info. | |
| | | */ | |
| | | virtual bool dropCollection( const string &ns, BSONObj* info = NULL | |
| | | ) { | |
| string db = nsGetDB( ns ); | | string db = nsGetDB( ns ); | |
| string coll = nsGetCollection( ns ); | | string coll = nsGetCollection( ns ); | |
| uassert( 10011 , "no collection name", coll.size() ); | | uassert( 10011 , "no collection name", coll.size() ); | |
| | | | |
|
| BSONObj info; | | BSONObj temp; | |
| | | if ( info == NULL ) { | |
| | | info = &temp; | |
| | | } | |
| | | | |
|
| bool res = runCommand( db.c_str() , BSON( "drop" << coll ) , in
fo ); | | bool res = runCommand( db.c_str() , BSON( "drop" << coll ) , *i
nfo ); | |
| resetIndexCache(); | | resetIndexCache(); | |
| return res; | | return res; | |
| } | | } | |
| | | | |
| /** Perform a repair and compaction of the specified database. May
take a long time to run. Disk space | | /** Perform a repair and compaction of the specified database. May
take a long time to run. Disk space | |
| must be available equal to the size of the database while repair
ing. | | must be available equal to the size of the database while repair
ing. | |
| */ | | */ | |
| bool repairDatabase(const string &dbname, BSONObj *info = 0) { | | bool repairDatabase(const string &dbname, BSONObj *info = 0) { | |
| return simpleCommand(dbname, info, "repairDatabase"); | | return simpleCommand(dbname, info, "repairDatabase"); | |
| } | | } | |
| | | | |
| skipping to change at line 722 | | skipping to change at line 819 | |
| */ | | */ | |
| list<string> getDatabaseNames(); | | list<string> getDatabaseNames(); | |
| | | | |
| /** | | /** | |
| get a list of all the current collections in db | | get a list of all the current collections in db | |
| */ | | */ | |
| list<string> getCollectionNames( const string& db ); | | list<string> getCollectionNames( const string& db ); | |
| | | | |
| bool exists( const string& ns ); | | bool exists( const string& ns ); | |
| | | | |
|
| | | virtual void setAuthenticationTable( const AuthenticationTable& aut | |
| | | h ); | |
| | | virtual void clearAuthenticationTable(); | |
| | | | |
| /** Create an index if it does not already exist. | | /** Create an index if it does not already exist. | |
| ensureIndex calls are remembered so it is safe/fast to call thi
s function many | | ensureIndex calls are remembered so it is safe/fast to call thi
s function many | |
| times in your code. | | times in your code. | |
| @param ns collection to be indexed | | @param ns collection to be indexed | |
| @param keys the "key pattern" for the index. e.g., { name : 1 } | | @param keys the "key pattern" for the index. e.g., { name : 1 } | |
| @param unique if true, indicates that key uniqueness should be e
nforced for this index | | @param unique if true, indicates that key uniqueness should be e
nforced for this index | |
| @param name if not specified, it will be created from the keys a
utomatically (which is recommended) | | @param name if not specified, it will be created from the keys a
utomatically (which is recommended) | |
| @param cache if set to false, the index cache for the connection
won't remember this call | | @param cache if set to false, the index cache for the connection
won't remember this call | |
| @param background build index in the background (see mongodb doc
s/wiki for details) | | @param background build index in the background (see mongodb doc
s/wiki for details) | |
| @param v index version. leave at default value. (unit tests set
this parameter.) | | @param v index version. leave at default value. (unit tests set
this parameter.) | |
| | | | |
| skipping to change at line 783 | | skipping to change at line 883 | |
| BSONObj _countCmd(const string &ns, const BSONObj& query, int optio
ns, int limit, int skip ); | | BSONObj _countCmd(const string &ns, const BSONObj& query, int optio
ns, int limit, int skip ); | |
| | | | |
| /** | | /** | |
| * Look up the options available on this client. Caches the answer
from | | * Look up the options available on this client. Caches the answer
from | |
| * _lookupAvailableOptions(), below. | | * _lookupAvailableOptions(), below. | |
| */ | | */ | |
| QueryOptions availableOptions(); | | QueryOptions availableOptions(); | |
| | | | |
| virtual QueryOptions _lookupAvailableOptions(); | | virtual QueryOptions _lookupAvailableOptions(); | |
| | | | |
|
| | | bool hasAuthenticationTable(); | |
| | | AuthenticationTable& getAuthenticationTable(); | |
| | | | |
| private: | | private: | |
| enum QueryOptions _cachedAvailableOptions; | | enum QueryOptions _cachedAvailableOptions; | |
| bool _haveCachedAvailableOptions; | | bool _haveCachedAvailableOptions; | |
|
| | | AuthenticationTable _authTable; | |
| | | bool _hasAuthentication; | |
| }; | | }; | |
| | | | |
| /** | | /** | |
| 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 858 | | skipping to change at line 963 | |
| insert an object into the database | | insert an object into the database | |
| */ | | */ | |
| virtual void insert( const string &ns , BSONObj obj , int flags=0); | | virtual void insert( const string &ns , BSONObj obj , int flags=0); | |
| | | | |
| /** | | /** | |
| insert a vector of objects into the database | | insert a vector of objects into the database | |
| */ | | */ | |
| virtual void insert( const string &ns, const vector< BSONObj >& v ,
int flags=0); | | virtual void insert( const string &ns, const vector< BSONObj >& v ,
int flags=0); | |
| | | | |
| /** | | /** | |
|
| | | updates objects matching query | |
| | | */ | |
| | | virtual void update( const string &ns, | |
| | | Query query, | |
| | | BSONObj obj, | |
| | | bool upsert = false, bool multi = false ); | |
| | | | |
| | | virtual void update( const string &ns, Query query, BSONObj obj, in | |
| | | t flags ); | |
| | | | |
| | | /** | |
| remove matching objects from the database | | remove matching objects from the database | |
| @param justOne if this true, then once a single match is found w
ill stop | | @param justOne if this true, then once a single match is found w
ill stop | |
| */ | | */ | |
| virtual void remove( const string &ns , Query q , bool justOne = 0
); | | virtual void remove( const string &ns , Query q , bool justOne = 0
); | |
| | | | |
|
| /** | | virtual void remove( const string &ns , Query query, int flags ); | |
| updates objects matching query | | | |
| */ | | | |
| virtual void update( const string &ns , Query query , BSONObj obj , | | | |
| bool upsert = false , bool multi = false ); | | | |
| | | | |
| virtual bool isFailed() const = 0; | | virtual bool isFailed() const = 0; | |
| | | | |
| virtual void killCursor( long long cursorID ) = 0; | | virtual void killCursor( long long cursorID ) = 0; | |
| | | | |
| virtual bool callRead( Message& toSend , Message& response ) = 0; | | virtual bool callRead( Message& toSend , Message& response ) = 0; | |
| // virtual bool callWrite( Message& toSend , Message& response ) =
0; // TODO: add this if needed | | // virtual bool callWrite( Message& toSend , Message& response ) =
0; // TODO: add this if needed | |
| | | | |
| virtual ConnectionString::ConnectionType type() const = 0; | | virtual ConnectionString::ConnectionType type() const = 0; | |
| | | | |
| | | | |
| skipping to change at line 967 | | skipping to change at line 1079 | |
| checkConnection(); | | checkConnection(); | |
| return DBClientBase::query( ns, query, nToReturn, nToSkip, fiel
dsToReturn, queryOptions , batchSize ); | | return DBClientBase::query( ns, query, nToReturn, nToSkip, fiel
dsToReturn, queryOptions , batchSize ); | |
| } | | } | |
| | | | |
| virtual unsigned long long query( boost::function<void(DBClientCurs
orBatchIterator &)> f, | | virtual unsigned long long query( boost::function<void(DBClientCurs
orBatchIterator &)> f, | |
| const string& ns, | | const string& ns, | |
| Query query, | | Query query, | |
| const BSONObj *fieldsToReturn, | | const BSONObj *fieldsToReturn, | |
| int queryOptions ); | | int queryOptions ); | |
| | | | |
|
| virtual bool runCommand(const string &dbname, const BSONObj& cmd, B | | virtual bool runCommand(const string &dbname, | |
| SONObj &info, int options=0); | | const BSONObj& cmd, | |
| | | BSONObj &info, | |
| | | int options=0, | |
| | | const AuthenticationTable* auth=NULL); | |
| | | | |
| /** | | /** | |
| @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 { return _failed; } | | bool isFailed() const { return _failed; } | |
| | | | |
| MessagingPort& port() { verify(p); return *p; } | | MessagingPort& port() { verify(p); return *p; } | |
| | | | |
| string toStringLong() const { | | string toStringLong() const { | |
| | | | |
End of changes. 19 change blocks. |
| 20 lines changed or deleted | | 152 lines changed or added | |
|
| dbhelpers.h | | dbhelpers.h | |
| | | | |
| skipping to change at line 31 | | skipping to change at line 31 | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include "../pch.h" | | #include "../pch.h" | |
| #include "client.h" | | #include "client.h" | |
| #include "db.h" | | #include "db.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
|
| const BSONObj reverseNaturalObj = BSON( "$natural" << -1 ); | | extern const BSONObj reverseNaturalObj; // {"$natural": -1 } | |
| | | | |
| class Cursor; | | class Cursor; | |
| class CoveredIndexMatcher; | | class CoveredIndexMatcher; | |
| | | | |
| /** | | /** | |
| all helpers assume locking is handled above them | | all helpers assume locking is handled above them | |
| */ | | */ | |
| struct Helpers { | | struct Helpers { | |
| | | | |
| /* ensure the specified index exists. | | /* ensure the specified index exists. | |
| | | | |
| skipping to change at line 65 | | skipping to change at line 65 | |
| set your db SavedContext first. | | set your db SavedContext first. | |
| | | | |
| @param query - the query to perform. note this is the low level
portion of query so "orderby : ..." | | @param query - the query to perform. note this is the low level
portion of query so "orderby : ..." | |
| won't work. | | won't work. | |
| | | | |
| @param requireIndex if true, assert if no index for the query.
a way to guard against | | @param requireIndex if true, assert if no index for the query.
a way to guard against | |
| writing a slow query. | | writing a slow query. | |
| | | | |
| @return true if object found | | @return true if object found | |
| */ | | */ | |
|
| static bool findOne(const char *ns, const BSONObj &query, BSONObj& | | static bool findOne(const StringData& ns, const BSONObj &query, BSO | |
| result, bool requireIndex = false); | | NObj& result, bool requireIndex = false); | |
| static DiskLoc findOne(const char *ns, const BSONObj &query, bool r | | static DiskLoc findOne(const StringData& ns, const BSONObj &query, | |
| equireIndex); | | bool requireIndex); | |
| | | | |
| | | /** | |
| | | * have to be locked already | |
| | | */ | |
| | | static vector<BSONObj> findAll( const string& ns , const BSONObj& q | |
| | | uery ); | |
| | | | |
| /** | | /** | |
| * @param foundIndex if passed in will be set to 1 if ns and index
found | | * @param foundIndex if passed in will be set to 1 if ns and index
found | |
| * @return true if object found | | * @return true if object found | |
| */ | | */ | |
| static bool findById(Client&, const char *ns, BSONObj query, BSONOb
j& result , | | static bool findById(Client&, const char *ns, BSONObj query, BSONOb
j& result , | |
| bool * nsFound = 0 , bool * indexFound = 0 ); | | bool * nsFound = 0 , bool * indexFound = 0 ); | |
| | | | |
| /* uasserts if no _id index. | | /* uasserts if no _id index. | |
| @return null loc if not found */ | | @return null loc if not found */ | |
| | | | |
| skipping to change at line 105 | | skipping to change at line 110 | |
| * o has to have an _id field or will assert | | * o has to have an _id field or will assert | |
| */ | | */ | |
| static void upsert( const string& ns , const BSONObj& o, bool fromM
igrate = false ); | | static void upsert( const string& ns , const BSONObj& o, bool fromM
igrate = false ); | |
| | | | |
| /** You do not need to set the database before calling. | | /** You do not need to set the database before calling. | |
| @return true if collection is empty. | | @return true if collection is empty. | |
| */ | | */ | |
| static bool isEmpty(const char *ns, bool doAuth=true); | | static bool isEmpty(const char *ns, bool doAuth=true); | |
| | | | |
| // TODO: this should be somewhere else probably | | // TODO: this should be somewhere else probably | |
|
| | | /* Takes object o, and returns a new object with the | |
| | | * same field elements but the names stripped out. Also, | |
| | | * fills in "key" with an ascending keyPattern that matches o | |
| | | * Example: | |
| | | * o = {a : 5 , b : 6} --> | |
| | | * sets key= {a : 1, b :1}, returns {"" : 5, "" : 6} | |
| | | */ | |
| static BSONObj toKeyFormat( const BSONObj& o , BSONObj& key ); | | static BSONObj toKeyFormat( const BSONObj& o , BSONObj& key ); | |
| | | | |
|
| | | /* Takes a BSONObj indicating the min or max boundary of a range, | |
| | | * and a keyPattern corresponding to an index that is useful | |
| | | * for locating items in the range, and returns an "extension" | |
| | | * of the bound, modified to fit the given pattern. In other words | |
| | | , | |
| | | * it appends MinKey or MaxKey values to the bound, so that the ext | |
| | | ension | |
| | | * has the same number of fields as keyPattern. | |
| | | * minOrMax should be -1/+1 to indicate whether the extension | |
| | | * corresponds to the min or max bound for the range. | |
| | | * Also, strips out the field names to put the bound in key format. | |
| | | * Examples: | |
| | | * {a : 55}, {a :1}, -1 --> {"" : 55} | |
| | | * {a : 55}, {a : 1, b : 1}, -1 -> {"" : 55, "" : minKey} | |
| | | * {a : 55}, {a : 1, b : 1}, 1 -> {"" : 55, "" : maxKey} | |
| | | * {a : 55}, {a : 1, b : -1}, -1 -> {"" : 55, "" : maxKey} | |
| | | * {a : 55}, {a : 1, b : -1}, 1 -> {"" : 55, "" : minKey} | |
| | | * | |
| | | * This function is useful for modifying chunk ranges in sharding, | |
| | | * when the shard key is a prefix of the index actually used | |
| | | * (also useful when the shard key is equal to the index used, | |
| | | * since it strips out the field names). | |
| | | */ | |
| | | static BSONObj modifiedRangeBound( const BSONObj& bound , | |
| | | const BSONObj& keyPattern , | |
| | | int minOrMax ); | |
| | | | |
| class RemoveCallback { | | class RemoveCallback { | |
| public: | | public: | |
| virtual ~RemoveCallback() {} | | virtual ~RemoveCallback() {} | |
| virtual void goingToDelete( const BSONObj& o ) = 0; | | virtual void goingToDelete( const BSONObj& o ) = 0; | |
| }; | | }; | |
| | | | |
| /** | | /** | |
|
| * Remove all documents in the range. | | * Takes a range, specified by a min and max, and an index, specifi | |
| | | ed by | |
| | | * keyPattern, and removes all the documents in that range found by | |
| | | iterating | |
| | | * over the given index. Caller is responsible for insuring that mi | |
| | | n/max are | |
| | | * compatible with the given keyPattern (e.g min={a:100} is compati | |
| | | ble with | |
| | | * keyPattern={a:1,b:1} since it can be extended to {a:100,b:minKey | |
| | | }, but | |
| | | * min={b:100} is not compatible). | |
| | | * | |
| | | * Caller must hold a write lock on 'ns' | |
| | | * | |
| * Does oplog the individual document deletions. | | * Does oplog the individual document deletions. | |
| */ | | */ | |
| static long long removeRange( const string& ns , | | static long long removeRange( const string& ns , | |
| const BSONObj& min , | | const BSONObj& min , | |
| const BSONObj& max , | | const BSONObj& max , | |
|
| bool yield = false , | | const BSONObj& keyPattern , | |
| bool maxInclusive = false , | | bool maxInclusive = false , | |
| RemoveCallback * callback = 0, | | RemoveCallback * callback = 0, | |
| bool fromMigrate = false ); | | bool fromMigrate = false ); | |
| | | | |
| /** | | /** | |
| * Remove all documents from a collection. | | * Remove all documents from a collection. | |
| * You do not need to set the database before calling. | | * You do not need to set the database before calling. | |
| * Does not oplog the operation. | | * Does not oplog the operation. | |
| */ | | */ | |
| static void emptyCollection(const char *ns); | | static void emptyCollection(const char *ns); | |
| | | | |
End of changes. 6 change blocks. |
| 7 lines changed or deleted | | 60 lines changed or added | |
|
| document_source.h | | document_source.h | |
| | | | |
| skipping to change at line 167 | | skipping to change at line 167 | |
| const intrusive_ptr<DependencyTracker> &pTracker); | | const intrusive_ptr<DependencyTracker> &pTracker); | |
| | | | |
| /** | | /** | |
| Add the DocumentSource to the array builder. | | Add the DocumentSource to the array builder. | |
| | | | |
| The default implementation calls sourceToBson() in order to | | The default implementation calls sourceToBson() in order to | |
| convert the inner part of the object which will be added to the | | convert the inner part of the object which will be added to the | |
| array being built here. | | array being built here. | |
| | | | |
| @param pBuilder the array builder to add the operation to. | | @param pBuilder the array builder to add the operation to. | |
|
| | | @param explain create explain output | |
| */ | | */ | |
|
| virtual void addToBsonArray(BSONArrayBuilder *pBuilder) const; | | virtual void addToBsonArray(BSONArrayBuilder *pBuilder, | |
| | | bool explain = false) const; | |
| | | | |
| protected: | | protected: | |
| /** | | /** | |
| Base constructor. | | Base constructor. | |
| */ | | */ | |
| DocumentSource(const intrusive_ptr<ExpressionContext> &pExpCtx); | | DocumentSource(const intrusive_ptr<ExpressionContext> &pExpCtx); | |
| | | | |
| /** | | /** | |
| Create an object that represents the document source. The object | | Create an object that represents the document source. The object | |
| will have a single field whose name is the source's name. This | | will have a single field whose name is the source's name. This | |
| will be used by the default implementation of addToBsonArray() | | will be used by the default implementation of addToBsonArray() | |
| to add this object to a pipeline being represented in BSON. | | to add this object to a pipeline being represented in BSON. | |
| | | | |
| @param pBuilder a blank object builder to write to | | @param pBuilder a blank object builder to write to | |
|
| | | @param explain create explain output | |
| */ | | */ | |
|
| virtual void sourceToBson(BSONObjBuilder *pBuilder) const = 0; | | virtual void sourceToBson(BSONObjBuilder *pBuilder, | |
| | | bool explain) const = 0; | |
| | | | |
| /* | | /* | |
| Most DocumentSources have an underlying source they get their dat
a | | Most DocumentSources have an underlying source they get their dat
a | |
| from. This is a convenience for them. | | from. This is a convenience for them. | |
| | | | |
| The default implementation of setSource() sets this; if you don't | | The default implementation of setSource() sets this; if you don't | |
| need a source, override that to verify(). The default is to | | need a source, override that to verify(). The default is to | |
| verify() if this has already been set. | | verify() if this has already been set. | |
| */ | | */ | |
| DocumentSource *pSource; | | DocumentSource *pSource; | |
| | | | |
| /* | | /* | |
| The zero-based user-specified pipeline step. Used for diagnostic
s. | | The zero-based user-specified pipeline step. Used for diagnostic
s. | |
| Will be set to -1 for artificial pipeline steps that were not par
t | | Will be set to -1 for artificial pipeline steps that were not par
t | |
| of the original user specification. | | of the original user specification. | |
| */ | | */ | |
| int step; | | int step; | |
| | | | |
| intrusive_ptr<ExpressionContext> pExpCtx; | | intrusive_ptr<ExpressionContext> pExpCtx; | |
|
| | | | |
| | | /* | |
| | | for explain: # of rows returned by this source | |
| | | | |
| | | This is *not* unsigned so it can be passed to BSONObjBuilder.appe | |
| | | nd(). | |
| | | */ | |
| | | long long nRowsOut; | |
| }; | | }; | |
| | | | |
| class DocumentSourceBsonArray : | | class DocumentSourceBsonArray : | |
| public DocumentSource { | | public DocumentSource { | |
| public: | | public: | |
| // virtuals from DocumentSource | | // virtuals from DocumentSource | |
| virtual ~DocumentSourceBsonArray(); | | virtual ~DocumentSourceBsonArray(); | |
| virtual bool eof(); | | virtual bool eof(); | |
| virtual bool advance(); | | virtual bool advance(); | |
| virtual intrusive_ptr<Document> getCurrent(); | | virtual intrusive_ptr<Document> getCurrent(); | |
| | | | |
| skipping to change at line 236 | | skipping to change at line 247 | |
| @param pBsonElement the BSON array to treat as a document source | | @param pBsonElement the BSON array to treat as a document source | |
| @param pExpCtx the expression context for the pipeline | | @param pExpCtx the expression context for the pipeline | |
| @returns the newly created document source | | @returns the newly created document source | |
| */ | | */ | |
| static intrusive_ptr<DocumentSourceBsonArray> create( | | static intrusive_ptr<DocumentSourceBsonArray> create( | |
| BSONElement *pBsonElement, | | BSONElement *pBsonElement, | |
| const intrusive_ptr<ExpressionContext> &pExpCtx); | | const intrusive_ptr<ExpressionContext> &pExpCtx); | |
| | | | |
| protected: | | protected: | |
| // virtuals from DocumentSource | | // virtuals from DocumentSource | |
|
| virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | | virtual void sourceToBson(BSONObjBuilder *pBuilder, bool explain) c
onst; | |
| | | | |
| private: | | private: | |
| DocumentSourceBsonArray(BSONElement *pBsonElement, | | DocumentSourceBsonArray(BSONElement *pBsonElement, | |
| const intrusive_ptr<ExpressionContext> &pExpCtx); | | const intrusive_ptr<ExpressionContext> &pExpCtx); | |
| | | | |
| BSONObj embeddedObject; | | BSONObj embeddedObject; | |
| BSONObjIterator arrayIterator; | | BSONObjIterator arrayIterator; | |
| BSONElement currentElement; | | BSONElement currentElement; | |
| bool haveCurrent; | | bool haveCurrent; | |
| }; | | }; | |
| | | | |
| skipping to change at line 276 | | skipping to change at line 287 | |
| @param pList the list of futures | | @param pList the list of futures | |
| @param pExpCtx the expression context for the pipeline | | @param pExpCtx the expression context for the pipeline | |
| @returns the newly created DocumentSource | | @returns the newly created DocumentSource | |
| */ | | */ | |
| static intrusive_ptr<DocumentSourceCommandFutures> create( | | static intrusive_ptr<DocumentSourceCommandFutures> create( | |
| string &errmsg, FuturesList *pList, | | string &errmsg, FuturesList *pList, | |
| const intrusive_ptr<ExpressionContext> &pExpCtx); | | const intrusive_ptr<ExpressionContext> &pExpCtx); | |
| | | | |
| protected: | | protected: | |
| // virtuals from DocumentSource | | // virtuals from DocumentSource | |
|
| virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | | virtual void sourceToBson(BSONObjBuilder *pBuilder, bool explain) c
onst; | |
| | | | |
| private: | | private: | |
| DocumentSourceCommandFutures(string &errmsg, FuturesList *pList, | | DocumentSourceCommandFutures(string &errmsg, FuturesList *pList, | |
| const intrusive_ptr<ExpressionContext> &pExpCtx); | | const intrusive_ptr<ExpressionContext> &pExpCtx); | |
| | | | |
| /** | | /** | |
| Advance to the next document, setting pCurrent appropriately. | | Advance to the next document, setting pCurrent appropriately. | |
| | | | |
| Adjusts pCurrent, pBsonSource, and iterator, as needed. On exit, | | Adjusts pCurrent, pBsonSource, and iterator, as needed. On exit, | |
| pCurrent is the Document to return, or NULL. If NULL, this | | pCurrent is the Document to return, or NULL. If NULL, this | |
| | | | |
| skipping to change at line 325 | | skipping to change at line 336 | |
| in order to fetch data from the database. | | in order to fetch data from the database. | |
| | | | |
| @param pCursor the cursor to use to fetch data | | @param pCursor the cursor to use to fetch data | |
| @param pExpCtx the expression context for the pipeline | | @param pExpCtx the expression context for the pipeline | |
| */ | | */ | |
| static intrusive_ptr<DocumentSourceCursor> create( | | static intrusive_ptr<DocumentSourceCursor> create( | |
| const shared_ptr<Cursor> &pCursor, | | const shared_ptr<Cursor> &pCursor, | |
| const string &ns, | | const string &ns, | |
| const intrusive_ptr<ExpressionContext> &pExpCtx); | | const intrusive_ptr<ExpressionContext> &pExpCtx); | |
| | | | |
|
| /** | | /* | |
| Add a BSONObj dependency. | | Record the namespace. Required for explain. | |
| | | | |
|
| Some Cursor creation functions rely on BSON objects to specify | | @param namespace the namespace | |
| their query predicate or sort. These often take a BSONObj | | */ | |
| by reference for these, but to not copy it. As a result, the | | void setNamespace(const string &ns); | |
| BSONObjs specified must outlive the Cursor. In order to ensure | | | |
| that, use this to preserve a pointer to the BSONObj here. | | | |
| | | | |
|
| From the outside, you must also make sure the BSONObjBuilder | | /* | |
| creates a lasting copy of the data, otherwise it will go away | | Record the query that was specified for the cursor this wraps, if | |
| when the builder goes out of scope. Therefore, the typical usag | | any. | |
| e | | | |
| pattern for this is | | | |
| { | | | |
| BSONObjBuilder builder; | | | |
| // do stuff to the builder | | | |
| shared_ptr<BSONObj> pBsonObj(new BSONObj(builder.obj())); | | | |
| pDocumentSourceCursor->addBsonDependency(pBsonObj); | | | |
| } | | | |
| | | | |
|
| @param pBsonObj pointer to the BSON object to preserve | | This should be captured after any optimizations are applied to | |
| | | the pipeline so that it reflects what is really used. | |
| | | | |
| | | This gets used for explain output. | |
| | | | |
| | | @param pBsonObj the query to record | |
| */ | | */ | |
|
| void addBsonDependency(const shared_ptr<BSONObj> &pBsonObj); | | void setQuery(const shared_ptr<BSONObj> &pBsonObj); | |
| | | | |
| | | /* | |
| | | Record the sort that was specified for the cursor this wraps, if | |
| | | any. | |
| | | | |
| | | This should be captured after any optimizations are applied to | |
| | | the pipeline so that it reflects what is really used. | |
| | | | |
| | | This gets used for explain output. | |
| | | | |
| | | @param pBsonObj the sort to record | |
| | | */ | |
| | | void setSort(const shared_ptr<BSONObj> &pBsonObj); | |
| | | | |
| | | /** | |
| | | Release the cursor, but without changing the other data. This | |
| | | is used for the explain version of pipeline execution. | |
| | | */ | |
| | | void releaseCursor(); | |
| | | | |
| protected: | | protected: | |
| // virtuals from DocumentSource | | // virtuals from DocumentSource | |
|
| virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | | virtual void sourceToBson(BSONObjBuilder *pBuilder, bool explain) c
onst; | |
| | | | |
| private: | | private: | |
| DocumentSourceCursor( | | DocumentSourceCursor( | |
| const shared_ptr<Cursor> &pTheCursor, const string &ns, | | const shared_ptr<Cursor> &pTheCursor, const string &ns, | |
| const intrusive_ptr<ExpressionContext> &pExpCtx); | | const intrusive_ptr<ExpressionContext> &pExpCtx); | |
| | | | |
| void findNext(); | | void findNext(); | |
| intrusive_ptr<Document> pCurrent; | | intrusive_ptr<Document> pCurrent; | |
| | | | |
|
| | | string ns; // namespace | |
| | | | |
| /* | | /* | |
| The bsonDependencies must outlive the Cursor wrapped by this | | The bsonDependencies must outlive the Cursor wrapped by this | |
| source. Therefore, bsonDependencies must appear before pCursor | | source. Therefore, bsonDependencies must appear before pCursor | |
| in order cause its destructor to be called *after* pCursor's. | | in order cause its destructor to be called *after* pCursor's. | |
| */ | | */ | |
|
| | | shared_ptr<BSONObj> pQuery; | |
| | | shared_ptr<BSONObj> pSort; | |
| vector<shared_ptr<BSONObj> > bsonDependencies; | | vector<shared_ptr<BSONObj> > bsonDependencies; | |
| shared_ptr<Cursor> pCursor; | | shared_ptr<Cursor> pCursor; | |
| | | | |
| /* | | /* | |
| In order to yield, we need a ClientCursor. | | In order to yield, we need a ClientCursor. | |
| */ | | */ | |
|
| ClientCursor::CleanupPointer pClientCursor; | | ClientCursor::Holder pClientCursor; | |
| | | | |
| /* | | /* | |
| Advance the cursor, and yield sometimes. | | Advance the cursor, and yield sometimes. | |
| | | | |
| If the state of the world changed during the yield such that we | | If the state of the world changed during the yield such that we | |
| are unable to continue execution of the query, this will release
the | | are unable to continue execution of the query, this will release
the | |
| client cursor, and throw an error. | | client cursor, and throw an error. | |
| */ | | */ | |
| void advanceAndYield(); | | void advanceAndYield(); | |
| | | | |
| /* | | /* | |
| This document source hangs on to the dependency tracker when it | | This document source hangs on to the dependency tracker when it | |
| gets it so that it can be used for selective reification of | | gets it so that it can be used for selective reification of | |
| fields in order to avoid fields that are not required through the | | fields in order to avoid fields that are not required through the | |
| pipeline. | | pipeline. | |
| */ | | */ | |
| intrusive_ptr<DependencyTracker> pDependencies; | | intrusive_ptr<DependencyTracker> pDependencies; | |
|
| | | | |
| | | /** | |
| | | (5/14/12 - moved this to private because it's not used atm) | |
| | | Add a BSONObj dependency. | |
| | | | |
| | | Some Cursor creation functions rely on BSON objects to specify | |
| | | their query predicate or sort. These often take a BSONObj | |
| | | by reference for these, but do not copy it. As a result, the | |
| | | BSONObjs specified must outlive the Cursor. In order to ensure | |
| | | that, use this to preserve a pointer to the BSONObj here. | |
| | | | |
| | | From the outside, you must also make sure the BSONObjBuilder | |
| | | creates a lasting copy of the data, otherwise it will go away | |
| | | when the builder goes out of scope. Therefore, the typical usag | |
| | | e | |
| | | pattern for this is | |
| | | { | |
| | | BSONObjBuilder builder; | |
| | | // do stuff to the builder | |
| | | shared_ptr<BSONObj> pBsonObj(new BSONObj(builder.obj())); | |
| | | pDocumentSourceCursor->addBsonDependency(pBsonObj); | |
| | | } | |
| | | | |
| | | @param pBsonObj pointer to the BSON object to preserve | |
| | | */ | |
| | | void addBsonDependency(const shared_ptr<BSONObj> &pBsonObj); | |
| }; | | }; | |
| | | | |
| /* | | /* | |
| This contains all the basic mechanics for filtering a stream of | | This contains all the basic mechanics for filtering a stream of | |
| Documents, except for the actual predicate evaluation itself. This w
as | | Documents, except for the actual predicate evaluation itself. This w
as | |
| factored out so we could create DocumentSources that use both Matcher | | factored out so we could create DocumentSources that use both Matcher | |
| style predicates as well as full Expressions. | | style predicates as well as full Expressions. | |
| */ | | */ | |
| class DocumentSourceFilterBase : | | class DocumentSourceFilterBase : | |
| public DocumentSource { | | public DocumentSource { | |
| | | | |
| skipping to change at line 490 | | skipping to change at line 545 | |
| find() Cursor mechanism. | | find() Cursor mechanism. | |
| | | | |
| @param pBuilder the builder to write to | | @param pBuilder the builder to write to | |
| */ | | */ | |
| void toMatcherBson(BSONObjBuilder *pBuilder) const; | | void toMatcherBson(BSONObjBuilder *pBuilder) const; | |
| | | | |
| static const char filterName[]; | | static const char filterName[]; | |
| | | | |
| protected: | | protected: | |
| // virtuals from DocumentSource | | // virtuals from DocumentSource | |
|
| virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | | virtual void sourceToBson(BSONObjBuilder *pBuilder, bool explain) c
onst; | |
| | | | |
| // virtuals from DocumentSourceFilterBase | | // virtuals from DocumentSourceFilterBase | |
| virtual bool accept(const intrusive_ptr<Document> &pDocument) const
; | | virtual bool accept(const intrusive_ptr<Document> &pDocument) const
; | |
| | | | |
| private: | | private: | |
| DocumentSourceFilter(const intrusive_ptr<Expression> &pFilter, | | DocumentSourceFilter(const intrusive_ptr<Expression> &pFilter, | |
| const intrusive_ptr<ExpressionContext> &pExpCt
x); | | const intrusive_ptr<ExpressionContext> &pExpCt
x); | |
| | | | |
| intrusive_ptr<Expression> pFilter; | | intrusive_ptr<Expression> pFilter; | |
| }; | | }; | |
| | | | |
| skipping to change at line 576 | | skipping to change at line 631 | |
| from shards. | | from shards. | |
| | | | |
| @returns the grouping DocumentSource | | @returns the grouping DocumentSource | |
| */ | | */ | |
| intrusive_ptr<DocumentSource> createMerger(); | | intrusive_ptr<DocumentSource> createMerger(); | |
| | | | |
| static const char groupName[]; | | static const char groupName[]; | |
| | | | |
| protected: | | protected: | |
| // virtuals from DocumentSource | | // virtuals from DocumentSource | |
|
| virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | | virtual void sourceToBson(BSONObjBuilder *pBuilder, bool explain) c
onst; | |
| | | | |
| private: | | private: | |
| DocumentSourceGroup(const intrusive_ptr<ExpressionContext> &pExpCtx
); | | DocumentSourceGroup(const intrusive_ptr<ExpressionContext> &pExpCtx
); | |
| | | | |
| /* | | /* | |
| Before returning anything, this source must fetch everything from | | Before returning anything, this source must fetch everything from | |
| the underlying source and group it. populate() is used to do tha
t | | the underlying source and group it. populate() is used to do tha
t | |
| on the first call to any method on this source. The populated | | on the first call to any method on this source. The populated | |
| boolean indicates that this has been done. | | boolean indicates that this has been done. | |
| */ | | */ | |
| | | | |
| skipping to change at line 656 | | skipping to change at line 711 | |
| find() Cursor mechanism. | | find() Cursor mechanism. | |
| | | | |
| @param pBuilder the builder to write to | | @param pBuilder the builder to write to | |
| */ | | */ | |
| void toMatcherBson(BSONObjBuilder *pBuilder) const; | | void toMatcherBson(BSONObjBuilder *pBuilder) const; | |
| | | | |
| static const char matchName[]; | | static const char matchName[]; | |
| | | | |
| protected: | | protected: | |
| // virtuals from DocumentSource | | // virtuals from DocumentSource | |
|
| virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | | virtual void sourceToBson(BSONObjBuilder *pBuilder, bool explain) c
onst; | |
| | | | |
| // virtuals from DocumentSourceFilterBase | | // virtuals from DocumentSourceFilterBase | |
| virtual bool accept(const intrusive_ptr<Document> &pDocument) const
; | | virtual bool accept(const intrusive_ptr<Document> &pDocument) const
; | |
| | | | |
| private: | | private: | |
| DocumentSourceMatch(const BSONObj &query, | | DocumentSourceMatch(const BSONObj &query, | |
| const intrusive_ptr<ExpressionContext> &pExpCtx); | | const intrusive_ptr<ExpressionContext> &pExpCtx); | |
| | | | |
| Matcher matcher; | | Matcher matcher; | |
| }; | | }; | |
| | | | |
| skipping to change at line 696 | | skipping to change at line 751 | |
| @returns the newly created document source | | @returns the newly created document source | |
| */ | | */ | |
| static intrusive_ptr<DocumentSourceOut> createFromBson( | | static intrusive_ptr<DocumentSourceOut> createFromBson( | |
| BSONElement *pBsonElement, | | BSONElement *pBsonElement, | |
| const intrusive_ptr<ExpressionContext> &pExpCtx); | | const intrusive_ptr<ExpressionContext> &pExpCtx); | |
| | | | |
| static const char outName[]; | | static const char outName[]; | |
| | | | |
| protected: | | protected: | |
| // virtuals from DocumentSource | | // virtuals from DocumentSource | |
|
| virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | | virtual void sourceToBson(BSONObjBuilder *pBuilder, bool explain) c
onst; | |
| | | | |
| private: | | private: | |
| DocumentSourceOut(BSONElement *pBsonElement, | | DocumentSourceOut(BSONElement *pBsonElement, | |
| const intrusive_ptr<ExpressionContext> &pExpCtx); | | const intrusive_ptr<ExpressionContext> &pExpCtx); | |
| }; | | }; | |
| | | | |
| class DocumentSourceProject : | | class DocumentSourceProject : | |
| public DocumentSource { | | public DocumentSource { | |
| public: | | public: | |
| // virtuals from DocumentSource | | // virtuals from DocumentSource | |
| | | | |
| skipping to change at line 769 | | skipping to change at line 824 | |
| @returns the created projection | | @returns the created projection | |
| */ | | */ | |
| static intrusive_ptr<DocumentSource> createFromBson( | | static intrusive_ptr<DocumentSource> createFromBson( | |
| BSONElement *pBsonElement, | | BSONElement *pBsonElement, | |
| const intrusive_ptr<ExpressionContext> &pExpCtx); | | const intrusive_ptr<ExpressionContext> &pExpCtx); | |
| | | | |
| static const char projectName[]; | | static const char projectName[]; | |
| | | | |
| protected: | | protected: | |
| // virtuals from DocumentSource | | // virtuals from DocumentSource | |
|
| virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | | virtual void sourceToBson(BSONObjBuilder *pBuilder, bool explain) c
onst; | |
| | | | |
| private: | | private: | |
| DocumentSourceProject(const intrusive_ptr<ExpressionContext> &pExpC
tx); | | DocumentSourceProject(const intrusive_ptr<ExpressionContext> &pExpC
tx); | |
| | | | |
| // configuration state | | // configuration state | |
| bool excludeId; | | bool excludeId; | |
| intrusive_ptr<ExpressionObject> pEO; | | intrusive_ptr<ExpressionObject> pEO; | |
| | | | |
| /* | | /* | |
| Utility object used by manageDependencies(). | | Utility object used by manageDependencies(). | |
| | | | |
| skipping to change at line 903 | | skipping to change at line 958 | |
| @returns the grouping DocumentSource | | @returns the grouping DocumentSource | |
| */ | | */ | |
| static intrusive_ptr<DocumentSource> createFromBson( | | static intrusive_ptr<DocumentSource> createFromBson( | |
| BSONElement *pBsonElement, | | BSONElement *pBsonElement, | |
| const intrusive_ptr<ExpressionContext> &pExpCtx); | | const intrusive_ptr<ExpressionContext> &pExpCtx); | |
| | | | |
| static const char sortName[]; | | static const char sortName[]; | |
| | | | |
| protected: | | protected: | |
| // virtuals from DocumentSource | | // virtuals from DocumentSource | |
|
| virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | | virtual void sourceToBson(BSONObjBuilder *pBuilder, bool explain) c
onst; | |
| | | | |
| private: | | private: | |
| DocumentSourceSort(const intrusive_ptr<ExpressionContext> &pExpCtx)
; | | DocumentSourceSort(const intrusive_ptr<ExpressionContext> &pExpCtx)
; | |
| | | | |
| /* | | /* | |
| Before returning anything, this source must fetch everything from | | Before returning anything, this source must fetch everything from | |
| the underlying source and group it. populate() is used to do tha
t | | the underlying source and group it. populate() is used to do tha
t | |
| on the first call to any method on this source. The populated | | on the first call to any method on this source. The populated | |
| boolean indicates that this has been done. | | boolean indicates that this has been done. | |
| */ | | */ | |
| void populate(); | | void populate(); | |
| bool populated; | | bool populated; | |
| long long count; | | long long count; | |
| | | | |
| /* these two parallel each other */ | | /* these two parallel each other */ | |
| typedef vector<intrusive_ptr<ExpressionFieldPath> > SortPaths; | | typedef vector<intrusive_ptr<ExpressionFieldPath> > SortPaths; | |
| SortPaths vSortKey; | | SortPaths vSortKey; | |
| vector<bool> vAscending; | | vector<bool> vAscending; | |
| | | | |
|
| class Carrier { | | | |
| public: | | | |
| /* | | | |
| We need access to the key for compares, so we have to carry | | | |
| this around. | | | |
| */ | | | |
| DocumentSourceSort *pSort; | | | |
| | | | |
| intrusive_ptr<Document> pDocument; | | | |
| | | | |
| Carrier(DocumentSourceSort *pSort, | | | |
| const intrusive_ptr<Document> &pDocument); | | | |
| | | | |
| static bool lessThan(const Carrier &rL, const Carrier &rR); | | | |
| }; | | | |
| | | | |
| /* | | /* | |
| Compare two documents according to the specified sort key. | | Compare two documents according to the specified sort key. | |
| | | | |
| @param rL reference to the left document | | @param rL reference to the left document | |
| @param rR reference to the right document | | @param rR reference to the right document | |
| @returns a number less than, equal to, or greater than zero, | | @returns a number less than, equal to, or greater than zero, | |
| indicating pL < pR, pL == pR, or pL > pR, respectively | | indicating pL < pR, pL == pR, or pL > pR, respectively | |
| */ | | */ | |
| int compare(const intrusive_ptr<Document> &pL, | | int compare(const intrusive_ptr<Document> &pL, | |
| const intrusive_ptr<Document> &pR); | | const intrusive_ptr<Document> &pR); | |
| | | | |
|
| typedef list<Carrier> ListType; | | /* | |
| ListType documents; | | This is a utility class just for the STL sort that is done | |
| | | inside. | |
| | | */ | |
| | | class Comparator { | |
| | | public: | |
| | | bool operator()( | |
| | | const intrusive_ptr<Document> &pL, | |
| | | const intrusive_ptr<Document> &pR) { | |
| | | return (pSort->compare(pL, pR) < 0); | |
| | | } | |
| | | | |
|
| ListType::iterator listIterator; | | inline Comparator(DocumentSourceSort *pS): | |
| | | pSort(pS) { | |
| | | } | |
| | | | |
| | | private: | |
| | | DocumentSourceSort *pSort; | |
| | | }; | |
| | | | |
| | | typedef vector<intrusive_ptr<Document> > VectorType; | |
| | | VectorType documents; | |
| | | | |
| | | VectorType::iterator docIterator; | |
| intrusive_ptr<Document> pCurrent; | | intrusive_ptr<Document> pCurrent; | |
| }; | | }; | |
| | | | |
| class DocumentSourceLimit : | | class DocumentSourceLimit : | |
| public DocumentSource { | | public DocumentSource { | |
| public: | | public: | |
| // virtuals from DocumentSource | | // virtuals from DocumentSource | |
| virtual ~DocumentSourceLimit(); | | virtual ~DocumentSourceLimit(); | |
| virtual bool eof(); | | virtual bool eof(); | |
| virtual bool advance(); | | virtual bool advance(); | |
| | | | |
| skipping to change at line 996 | | skipping to change at line 1055 | |
| @returns the grouping DocumentSource | | @returns the grouping DocumentSource | |
| */ | | */ | |
| static intrusive_ptr<DocumentSource> createFromBson( | | static intrusive_ptr<DocumentSource> createFromBson( | |
| BSONElement *pBsonElement, | | BSONElement *pBsonElement, | |
| const intrusive_ptr<ExpressionContext> &pExpCtx); | | const intrusive_ptr<ExpressionContext> &pExpCtx); | |
| | | | |
| static const char limitName[]; | | static const char limitName[]; | |
| | | | |
| protected: | | protected: | |
| // virtuals from DocumentSource | | // virtuals from DocumentSource | |
|
| virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | | virtual void sourceToBson(BSONObjBuilder *pBuilder, bool explain) c
onst; | |
| | | | |
| private: | | private: | |
| DocumentSourceLimit( | | DocumentSourceLimit( | |
| const intrusive_ptr<ExpressionContext> &pExpCtx); | | const intrusive_ptr<ExpressionContext> &pExpCtx); | |
| | | | |
| long long limit; | | long long limit; | |
| long long count; | | long long count; | |
| intrusive_ptr<Document> pCurrent; | | intrusive_ptr<Document> pCurrent; | |
| }; | | }; | |
| | | | |
| | | | |
| skipping to change at line 1046 | | skipping to change at line 1105 | |
| @returns the grouping DocumentSource | | @returns the grouping DocumentSource | |
| */ | | */ | |
| static intrusive_ptr<DocumentSource> createFromBson( | | static intrusive_ptr<DocumentSource> createFromBson( | |
| BSONElement *pBsonElement, | | BSONElement *pBsonElement, | |
| const intrusive_ptr<ExpressionContext> &pExpCtx); | | const intrusive_ptr<ExpressionContext> &pExpCtx); | |
| | | | |
| static const char skipName[]; | | static const char skipName[]; | |
| | | | |
| protected: | | protected: | |
| // virtuals from DocumentSource | | // virtuals from DocumentSource | |
|
| virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | | virtual void sourceToBson(BSONObjBuilder *pBuilder, bool explain) c
onst; | |
| | | | |
| private: | | private: | |
| DocumentSourceSkip(const intrusive_ptr<ExpressionContext> &pExpCtx)
; | | DocumentSourceSkip(const intrusive_ptr<ExpressionContext> &pExpCtx)
; | |
| | | | |
| /* | | /* | |
| Skips initial documents. | | Skips initial documents. | |
| */ | | */ | |
| void skipper(); | | void skipper(); | |
| | | | |
| long long skip; | | long long skip; | |
| | | | |
| skipping to change at line 1108 | | skipping to change at line 1167 | |
| @returns the created projection | | @returns the created projection | |
| */ | | */ | |
| static intrusive_ptr<DocumentSource> createFromBson( | | static intrusive_ptr<DocumentSource> createFromBson( | |
| BSONElement *pBsonElement, | | BSONElement *pBsonElement, | |
| const intrusive_ptr<ExpressionContext> &pExpCtx); | | const intrusive_ptr<ExpressionContext> &pExpCtx); | |
| | | | |
| static const char unwindName[]; | | static const char unwindName[]; | |
| | | | |
| protected: | | protected: | |
| // virtuals from DocumentSource | | // virtuals from DocumentSource | |
|
| virtual void sourceToBson(BSONObjBuilder *pBuilder) const; | | virtual void sourceToBson(BSONObjBuilder *pBuilder, bool explain) c
onst; | |
| | | | |
| private: | | private: | |
| DocumentSourceUnwind(const intrusive_ptr<ExpressionContext> &pExpCt
x); | | DocumentSourceUnwind(const intrusive_ptr<ExpressionContext> &pExpCt
x); | |
| | | | |
| // configuration state | | // configuration state | |
| FieldPath unwindPath; | | FieldPath unwindPath; | |
| | | | |
| vector<int> fieldIndex; /* for the current document, the indices | | vector<int> fieldIndex; /* for the current document, the indices | |
| leading down to the field being unwound
*/ | | leading down to the field being unwound
*/ | |
| | | | |
| | | | |
| skipping to change at line 1187 | | skipping to change at line 1246 | |
| pThis(pT) { | | pThis(pT) { | |
| } | | } | |
| | | | |
| inline void DocumentSourceUnwind::resetArray() { | | inline void DocumentSourceUnwind::resetArray() { | |
| pNoUnwindDocument.reset(); | | pNoUnwindDocument.reset(); | |
| pUnwindArray.reset(); | | pUnwindArray.reset(); | |
| pUnwinder.reset(); | | pUnwinder.reset(); | |
| pUnwindValue.reset(); | | pUnwindValue.reset(); | |
| } | | } | |
| | | | |
|
| inline DocumentSourceSort::Carrier::Carrier( | | | |
| DocumentSourceSort *pTheSort, | | | |
| const intrusive_ptr<Document> &pTheDocument): | | | |
| pSort(pTheSort), | | | |
| pDocument(pTheDocument) { | | | |
| } | | | |
| } | | } | |
| | | | |
End of changes. 30 change blocks. |
| 60 lines changed or deleted | | 114 lines changed or added | |
|
| explain.h | | explain.h | |
| | | | |
| skipping to change at line 54 | | skipping to change at line 54 | |
| | | | |
| /** Data describing execution of a query plan. */ | | /** Data describing execution of a query plan. */ | |
| class ExplainPlanInfo { | | class ExplainPlanInfo { | |
| public: | | public: | |
| ExplainPlanInfo(); | | ExplainPlanInfo(); | |
| | | | |
| /** Note information about the plan. */ | | /** Note information about the plan. */ | |
| void notePlan( const Cursor &cursor, bool scanAndOrder, bool indexO
nly ); | | void notePlan( const Cursor &cursor, bool scanAndOrder, bool indexO
nly ); | |
| /** Note an iteration of the plan. */ | | /** Note an iteration of the plan. */ | |
| void noteIterate( bool match, bool loadedRecord, const Cursor &curs
or ); | | void noteIterate( bool match, bool loadedRecord, const Cursor &curs
or ); | |
|
| /** Note that the plan yielded. */ | | | |
| void noteYield(); | | | |
| /** Note that the plan finished execution. */ | | /** Note that the plan finished execution. */ | |
| void noteDone( const Cursor &cursor ); | | void noteDone( const Cursor &cursor ); | |
| /** Note that the plan was chosen over others by the query optimize
r. */ | | /** Note that the plan was chosen over others by the query optimize
r. */ | |
| void notePicked(); | | void notePicked(); | |
| | | | |
| /** BSON summary of the plan. */ | | /** BSON summary of the plan. */ | |
| BSONObj bson() const; | | BSONObj bson() const; | |
| /** Combined details of both the plan and its clause. */ | | /** Combined details of both the plan and its clause. */ | |
| BSONObj pickedPlanBson( const ExplainClauseInfo &clauseInfo ) const
; | | BSONObj pickedPlanBson( const ExplainClauseInfo &clauseInfo ) const
; | |
| | | | |
| | | | |
| skipping to change at line 80 | | skipping to change at line 78 | |
| | | | |
| private: | | private: | |
| void noteCursorUpdate( const Cursor &cursor ); | | void noteCursorUpdate( const Cursor &cursor ); | |
| string _cursorName; | | string _cursorName; | |
| bool _isMultiKey; | | bool _isMultiKey; | |
| long long _n; | | long long _n; | |
| long long _nscannedObjects; | | long long _nscannedObjects; | |
| long long _nscanned; | | long long _nscanned; | |
| bool _scanAndOrder; | | bool _scanAndOrder; | |
| bool _indexOnly; | | bool _indexOnly; | |
|
| int _nYields; | | | |
| BSONObj _indexBounds; | | BSONObj _indexBounds; | |
| bool _picked; | | bool _picked; | |
| bool _done; | | bool _done; | |
| BSONObj _details; | | BSONObj _details; | |
| }; | | }; | |
| | | | |
| /** Data describing execution of a query clause. */ | | /** Data describing execution of a query clause. */ | |
| class ExplainClauseInfo { | | class ExplainClauseInfo { | |
| public: | | public: | |
| ExplainClauseInfo(); | | ExplainClauseInfo(); | |
| | | | |
| /** Note an iteration of the clause. */ | | /** Note an iteration of the clause. */ | |
| void noteIterate( bool match, bool loadedRecord, bool chunkSkip ); | | void noteIterate( bool match, bool loadedRecord, bool chunkSkip ); | |
|
| | | /** Note a yield for the clause. */ | |
| | | void noteYield(); | |
| /** Revise the total number of documents returned to match an exter
nal count. */ | | /** Revise the total number of documents returned to match an exter
nal count. */ | |
| void reviseN( long long n ); | | void reviseN( long long n ); | |
| /** Stop the clauses's timer. */ | | /** Stop the clauses's timer. */ | |
| void stopTimer(); | | void stopTimer(); | |
| | | | |
| /** Add information about a plan to this clause. */ | | /** Add information about a plan to this clause. */ | |
| void addPlanInfo( const shared_ptr<ExplainPlanInfo> &info ); | | void addPlanInfo( const shared_ptr<ExplainPlanInfo> &info ); | |
| BSONObj bson() const; | | BSONObj bson() const; | |
| | | | |
| long long n() const { return _n; } | | long long n() const { return _n; } | |
| long long nscannedObjects() const { return _nscannedObjects; } | | long long nscannedObjects() const { return _nscannedObjects; } | |
| long long nscanned() const; | | long long nscanned() const; | |
| long long nChunkSkips() const { return _nChunkSkips; } | | long long nChunkSkips() const { return _nChunkSkips; } | |
|
| | | int nYields() const { return _nYields; } | |
| int millis() const { return _timer.duration(); } | | int millis() const { return _timer.duration(); } | |
| | | | |
| private: | | private: | |
| const ExplainPlanInfo &virtualPickedPlan() const; | | const ExplainPlanInfo &virtualPickedPlan() const; | |
| list<shared_ptr<const ExplainPlanInfo> > _plans; | | list<shared_ptr<const ExplainPlanInfo> > _plans; | |
| long long _n; | | long long _n; | |
| long long _nscannedObjects; | | long long _nscannedObjects; | |
| long long _nChunkSkips; | | long long _nChunkSkips; | |
|
| | | int _nYields; | |
| DurationTimer _timer; | | DurationTimer _timer; | |
| }; | | }; | |
| | | | |
| /** Data describing execution of a query. */ | | /** Data describing execution of a query. */ | |
| class ExplainQueryInfo { | | class ExplainQueryInfo { | |
| public: | | public: | |
| /** Note an iteration of the query's current clause. */ | | /** Note an iteration of the query's current clause. */ | |
| void noteIterate( bool match, bool loadedRecord, bool chunkSkip ); | | void noteIterate( bool match, bool loadedRecord, bool chunkSkip ); | |
|
| | | /** Note a yield of the query's current clause. */ | |
| | | void noteYield(); | |
| /** Revise the number of documents returned by the current clause.
*/ | | /** Revise the number of documents returned by the current clause.
*/ | |
| void reviseN( long long n ); | | void reviseN( long long n ); | |
| | | | |
| /* Additional information describing the query. */ | | /* Additional information describing the query. */ | |
| struct AncillaryInfo { | | struct AncillaryInfo { | |
| BSONObj _oldPlan; | | BSONObj _oldPlan; | |
| }; | | }; | |
| void setAncillaryInfo( const AncillaryInfo &ancillaryInfo ); | | void setAncillaryInfo( const AncillaryInfo &ancillaryInfo ); | |
| | | | |
| /* Add information about a clause to this query. */ | | /* Add information about a clause to this query. */ | |
| | | | |
| skipping to change at line 158 | | skipping to change at line 161 | |
| | | | |
| /** Note information about the plan. */ | | /** Note information about the plan. */ | |
| void notePlan( const Cursor &cursor, bool scanAndOrder, bool indexO
nly ) { | | void notePlan( const Cursor &cursor, bool scanAndOrder, bool indexO
nly ) { | |
| _planInfo->notePlan( cursor, scanAndOrder, indexOnly ); | | _planInfo->notePlan( cursor, scanAndOrder, indexOnly ); | |
| } | | } | |
| /** Note an iteration of the plan and the clause. */ | | /** Note an iteration of the plan and the clause. */ | |
| void noteIterate( bool match, bool loadedRecord, bool chunkSkip, co
nst Cursor &cursor ) { | | void noteIterate( bool match, bool loadedRecord, bool chunkSkip, co
nst Cursor &cursor ) { | |
| _planInfo->noteIterate( match, loadedRecord, cursor ); | | _planInfo->noteIterate( match, loadedRecord, cursor ); | |
| _queryInfo->noteIterate( match, loadedRecord, chunkSkip ); | | _queryInfo->noteIterate( match, loadedRecord, chunkSkip ); | |
| } | | } | |
|
| /** Note that the plan yielded. */ | | /** Note a yield for the clause. */ | |
| void noteYield() { | | void noteYield() { | |
|
| _planInfo->noteYield(); | | _queryInfo->noteYield(); | |
| } | | } | |
| /** Note that the plan finished execution. */ | | /** Note that the plan finished execution. */ | |
| void noteDone( const Cursor &cursor ) { | | void noteDone( const Cursor &cursor ) { | |
| _planInfo->noteDone( cursor ); | | _planInfo->noteDone( cursor ); | |
| } | | } | |
| | | | |
| /** Return the corresponding ExplainQueryInfo for further use. */ | | /** Return the corresponding ExplainQueryInfo for further use. */ | |
| shared_ptr<ExplainQueryInfo> queryInfo() const { | | shared_ptr<ExplainQueryInfo> queryInfo() const { | |
| return _queryInfo; | | return _queryInfo; | |
| } | | } | |
| | | | |
End of changes. 8 change blocks. |
| 5 lines changed or deleted | | 8 lines changed or added | |
|
| matcher.h | | matcher.h | |
| | | | |
| skipping to change at line 30 | | skipping to change at line 30 | |
| | | | |
| #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 ElementMatcher; | |
| class Matcher; | | class Matcher; | |
| class FieldRangeVector; | | class FieldRangeVector; | |
| | | | |
| class RegexMatcher { | | class RegexMatcher { | |
| public: | | public: | |
| const char *_fieldName; | | const char *_fieldName; | |
| const char *_regex; | | const char *_regex; | |
| const char *_flags; | | const char *_flags; | |
| string _prefix; | | string _prefix; | |
| shared_ptr< pcrecpp::RE > _re; | | shared_ptr< pcrecpp::RE > _re; | |
| | | | |
| skipping to change at line 53 | | skipping to change at line 54 | |
| | | | |
| struct element_lt { | | struct element_lt { | |
| bool operator()(const BSONElement& l, const BSONElement& r) const { | | bool operator()(const BSONElement& l, const BSONElement& r) const { | |
| int x = (int) l.canonicalType() - (int) r.canonicalType(); | | int x = (int) l.canonicalType() - (int) r.canonicalType(); | |
| if ( x < 0 ) return true; | | if ( x < 0 ) return true; | |
| else if ( x > 0 ) return false; | | else if ( x > 0 ) return false; | |
| return compareElementValues(l,r) < 0; | | return compareElementValues(l,r) < 0; | |
| } | | } | |
| }; | | }; | |
| | | | |
|
| | | /** | |
| | | * An interface for visiting a Matcher and all of its nested Matchers a | |
| | | nd ElementMatchers. | |
| | | * RegexMatchers are not visited. | |
| | | */ | |
| | | class MatcherVisitor { | |
| | | public: | |
| | | virtual ~MatcherVisitor() {} | |
| | | virtual void visitMatcher( const Matcher& matcher ) {} | |
| | | virtual void visitElementMatcher( const ElementMatcher& elementMatc | |
| | | her ) {} | |
| | | }; | |
| | | | |
| class ElementMatcher { | | class ElementMatcher { | |
| public: | | public: | |
| | | | |
| ElementMatcher() { | | ElementMatcher() { | |
| } | | } | |
| | | | |
| ElementMatcher( BSONElement e , int op, bool isNot ); | | ElementMatcher( BSONElement e , int op, bool isNot ); | |
| | | | |
| ElementMatcher( BSONElement e , int op , const BSONObj& array, bool
isNot ); | | ElementMatcher( BSONElement e , int op , const BSONObj& array, bool
isNot ); | |
| | | | |
| ~ElementMatcher() { } | | ~ElementMatcher() { } | |
| | | | |
| bool negativeCompareOp() const { return _compareOp == BSONObj::NE |
| _compareOp == BSONObj::NIN; } | | bool negativeCompareOp() const { return _compareOp == BSONObj::NE |
| _compareOp == BSONObj::NIN; } | |
| int inverseOfNegativeCompareOp() const; | | int inverseOfNegativeCompareOp() const; | |
| bool negativeCompareOpContainsNull() const; | | bool negativeCompareOpContainsNull() const; | |
| | | | |
|
| | | void visit( MatcherVisitor& visitor ) const; | |
| | | | |
| BSONElement _toMatch; | | BSONElement _toMatch; | |
| int _compareOp; | | int _compareOp; | |
| bool _isNot; | | bool _isNot; | |
| shared_ptr< set<BSONElement,element_lt> > _myset; | | shared_ptr< set<BSONElement,element_lt> > _myset; | |
| shared_ptr< vector<RegexMatcher> > _myregex; | | shared_ptr< vector<RegexMatcher> > _myregex; | |
| | | | |
| // these are for specific operators | | // these are for specific operators | |
| int _mod; | | int _mod; | |
| int _modm; | | int _modm; | |
| BSONType _type; | | BSONType _type; | |
| | | | |
| skipping to change at line 184 | | skipping to change at line 198 | |
| This can be used to gather a list of all the references made by | | This can be used to gather a list of all the references made by | |
| this matcher. The implementation of this parallels that of | | this matcher. The implementation of this parallels that of | |
| matches() above. | | matches() above. | |
| | | | |
| @param pSink a FieldSink that the caller will use to gather or | | @param pSink a FieldSink that the caller will use to gather or | |
| process the references | | process the references | |
| */ | | */ | |
| void visitReferences(FieldSink *pSink) const; | | void visitReferences(FieldSink *pSink) const; | |
| #endif /* MONGO_LATER_SERVER_4644 */ | | #endif /* MONGO_LATER_SERVER_4644 */ | |
| | | | |
|
| | | /** | |
| | | * Visit this Matcher and all of its nested Matchers and ElementMat | |
| | | chers. All top level | |
| | | * ElementMatchers of a Matcher are visited immediately after the M | |
| | | atcher itself (before any | |
| | | * other Matcher is visited). | |
| | | */ | |
| | | void visit( MatcherVisitor& visitor ) const; | |
| | | | |
| bool atomic() const { return _atomic; } | | bool atomic() const { return _atomic; } | |
| | | | |
| string toString() const { | | string toString() const { | |
| return _jsobj.toString(); | | return _jsobj.toString(); | |
| } | | } | |
| | | | |
|
| void addOrDedupConstraint( const shared_ptr< FieldRangeVector > &fr | | | |
| v ) { | | | |
| _orDedupConstraints.push_back( frv ); | | | |
| } | | | |
| | | | |
| void popOrClause() { | | | |
| _orMatchers.pop_front(); | | | |
| } | | | |
| | | | |
| /** | | /** | |
| * @return true if this key matcher will return the same true/false | | * @return true if this key matcher will return the same true/false | |
| * value as the provided doc matcher. | | * value as the provided doc matcher. | |
| */ | | */ | |
| bool keyMatch( const Matcher &docMatcher ) const; | | bool keyMatch( const Matcher &docMatcher ) const; | |
| | | | |
| bool singleSimpleCriterion() const { | | bool singleSimpleCriterion() const { | |
| return false; // TODO SERVER-958 | | return false; // TODO SERVER-958 | |
| // // TODO Really check, especially if all basics are ok. | | // // TODO Really check, especially if all basics are ok. | |
| // // $all, etc | | // // $all, etc | |
| | | | |
| skipping to change at line 262 | | skipping to change at line 275 | |
| */ | | */ | |
| bool _atomic; | | bool _atomic; | |
| | | | |
| vector<RegexMatcher> _regexs; | | vector<RegexMatcher> _regexs; | |
| | | | |
| // so we delete the mem when we're done: | | // so we delete the mem when we're done: | |
| vector< shared_ptr< BSONObjBuilder > > _builders; | | vector< shared_ptr< BSONObjBuilder > > _builders; | |
| list< shared_ptr< Matcher > > _andMatchers; | | list< shared_ptr< Matcher > > _andMatchers; | |
| list< shared_ptr< Matcher > > _orMatchers; | | list< shared_ptr< Matcher > > _orMatchers; | |
| list< shared_ptr< Matcher > > _norMatchers; | | list< shared_ptr< Matcher > > _norMatchers; | |
|
| vector< shared_ptr< FieldRangeVector > > _orDedupConstraints; | | | |
| | | | |
| friend class CoveredIndexMatcher; | | friend class CoveredIndexMatcher; | |
| }; | | }; | |
| | | | |
| // If match succeeds on index key, then attempt to match full document. | | // If match succeeds on index key, then attempt to match full document. | |
| class CoveredIndexMatcher : boost::noncopyable { | | class CoveredIndexMatcher : boost::noncopyable { | |
| public: | | public: | |
| CoveredIndexMatcher(const BSONObj &pattern, const BSONObj &indexKey
Pattern); | | CoveredIndexMatcher(const BSONObj &pattern, const BSONObj &indexKey
Pattern); | |
|
| bool matchesWithSingleKeyIndex(const BSONObj &key, const DiskLoc &r | | bool matchesWithSingleKeyIndex( const BSONObj& key, const DiskLoc& | |
| ecLoc , MatchDetails * details = 0 ) { | | recLoc, | |
| | | MatchDetails* details = 0 ) const { | |
| return matches( key, recLoc, details, true ); | | return matches( key, recLoc, details, true ); | |
| } | | } | |
| /** | | /** | |
| * This is the preferred method for matching against a cursor, as i
t | | * This is the preferred method for matching against a cursor, as i
t | |
| * can handle both multi and single key cursors. | | * can handle both multi and single key cursors. | |
| */ | | */ | |
|
| bool matchesCurrent( Cursor * cursor , MatchDetails * details = 0 ) | | bool matchesCurrent( Cursor * cursor , MatchDetails * details = 0 ) | |
| ; | | const; | |
| bool needRecord() { return _needRecord; } | | bool needRecord() const { return _needRecord; } | |
| | | | |
| Matcher& docMatcher() { return *_docMatcher; } | | | |
| | | | |
|
| // once this is called, shouldn't use this matcher for matching any | | const Matcher &docMatcher() const { return *_docMatcher; } | |
| more | | | |
| void advanceOrClause( const shared_ptr< FieldRangeVector > &frv ) { | | | |
| _docMatcher->addOrDedupConstraint( frv ); | | | |
| // TODO this is not yet optimal. Since we could skip an entire | | | |
| // or clause (if a match is impossible) between calls to advanc | | | |
| eOrClause() | | | |
| // we may not pop all the clauses we can. | | | |
| _docMatcher->popOrClause(); | | | |
| } | | | |
| | | | |
|
| CoveredIndexMatcher *nextClauseMatcher( const BSONObj &indexKeyPatt | | /** | |
| ern ) { | | * @return a matcher for a following $or clause. | |
| return new CoveredIndexMatcher( _docMatcher, indexKeyPattern ); | | * @param prevClauseFrs The index range scanned by the previous $or | |
| | | clause. May be empty. | |
| | | * @param nextClauseIndexKeyPattern The index key of the following | |
| | | $or clause. | |
| | | */ | |
| | | CoveredIndexMatcher *nextClauseMatcher( const shared_ptr<FieldRange | |
| | | Vector>& prevClauseFrv, | |
| | | const BSONObj& nextClauseIn | |
| | | dexKeyPattern ) const { | |
| | | return new CoveredIndexMatcher( *this, prevClauseFrv, nextClaus | |
| | | eIndexKeyPattern ); | |
| } | | } | |
| | | | |
| string toString() const; | | string toString() const; | |
| | | | |
| private: | | private: | |
|
| bool matches(const BSONObj &key, const DiskLoc &recLoc , MatchDetai | | bool matches( const BSONObj& key, const DiskLoc& recLoc, MatchDetai | |
| ls * details = 0 , bool keyUsable = true ); | | ls* details = 0, | |
| CoveredIndexMatcher(const shared_ptr< Matcher > &docMatcher, const | | bool keyUsable = true ) const; | |
| BSONObj &indexKeyPattern); | | bool isOrClauseDup( const BSONObj &obj ) const; | |
| | | CoveredIndexMatcher( const CoveredIndexMatcher &prevClauseMatcher, | |
| | | const shared_ptr<FieldRangeVector> &prevClauseF | |
| | | rv, | |
| | | const BSONObj &nextClauseIndexKeyPattern ); | |
| void init(); | | void init(); | |
| shared_ptr< Matcher > _docMatcher; | | shared_ptr< Matcher > _docMatcher; | |
| Matcher _keyMatcher; | | Matcher _keyMatcher; | |
|
| | | vector<shared_ptr<FieldRangeVector> > _orDedupConstraints; | |
| | | | |
| bool _needRecord; // if the key itself isn't good enough to determi
ne a positive match | | bool _needRecord; // if the key itself isn't good enough to determi
ne a positive match | |
| }; | | }; | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 12 change blocks. |
| 34 lines changed or deleted | | 54 lines changed or added | |
|
| namespace_details.h | | namespace_details.h | |
| | | | |
| skipping to change at line 218 | | skipping to change at line 218 | |
| private: | | private: | |
| friend class NamespaceDetails; | | friend class NamespaceDetails; | |
| int i, n; | | int i, n; | |
| NamespaceDetails *d; | | NamespaceDetails *d; | |
| IndexIterator(NamespaceDetails *_d); | | IndexIterator(NamespaceDetails *_d); | |
| }; | | }; | |
| | | | |
| IndexIterator ii() { return IndexIterator(this); } | | IndexIterator ii() { return IndexIterator(this); } | |
| | | | |
| /* hackish - find our index # in the indexes array */ | | /* hackish - find our index # in the indexes array */ | |
|
| int idxNo(IndexDetails& idx); | | int idxNo(const IndexDetails& idx); | |
| | | | |
| /* multikey indexes are indexes where there are more than one key i
n the index | | /* multikey indexes are indexes where there are more than one key i
n the index | |
| for a single document. see multikey in wiki. | | for a single document. see multikey in wiki. | |
| for these, we have to do some dedup work on queries. | | for these, we have to do some dedup work on queries. | |
| */ | | */ | |
| bool isMultikey(int i) const { return (multiKeyIndexBits & (((unsig
ned long long) 1) << i)) != 0; } | | bool isMultikey(int i) const { return (multiKeyIndexBits & (((unsig
ned long long) 1) << i)) != 0; } | |
| void setIndexIsMultikey(const char *thisns, int i); | | void setIndexIsMultikey(const char *thisns, int i); | |
| | | | |
| /* add a new index. does not add to system.indexes etc. - just to
NamespaceDetails. | | /* add a new index. does not add to system.indexes etc. - just to
NamespaceDetails. | |
| caller must populate returned object. | | caller must populate returned object. | |
| | | | |
| skipping to change at line 300 | | skipping to change at line 300 | |
| int findIndexByKeyPattern(const BSONObj& keyPattern); | | int findIndexByKeyPattern(const BSONObj& keyPattern); | |
| | | | |
| void findIndexByType( const string& name , vector<int>& matches ) { | | void findIndexByType( const string& name , vector<int>& matches ) { | |
| IndexIterator i = ii(); | | IndexIterator i = ii(); | |
| while ( i.more() ) { | | while ( i.more() ) { | |
| if ( i.next().getSpec().getTypeName() == name ) | | if ( i.next().getSpec().getTypeName() == name ) | |
| matches.push_back( i.pos() - 1 ); | | matches.push_back( i.pos() - 1 ); | |
| } | | } | |
| } | | } | |
| | | | |
|
| | | /* Returns the index entry for the first index whose prefix contain | |
| | | s | |
| | | * 'keyPattern'. If 'requireSingleKey' is true, skip indices that c | |
| | | ontain | |
| | | * array attributes. Otherwise, returns NULL. | |
| | | */ | |
| | | const IndexDetails* findIndexByPrefix( const BSONObj &keyPattern , | |
| | | bool requireSingleKey ); | |
| | | | |
| const int systemFlags() const { return _systemFlags; } | | const int systemFlags() const { return _systemFlags; } | |
| bool isSystemFlagSet( int flag ) const { return _systemFlags & flag
; } | | bool isSystemFlagSet( int flag ) const { return _systemFlags & flag
; } | |
| void setSystemFlag( int flag ); | | void setSystemFlag( int flag ); | |
| void clearSystemFlag( int flag ); | | void clearSystemFlag( int flag ); | |
| | | | |
| const int userFlags() const { return _userFlags; } | | const int userFlags() const { return _userFlags; } | |
| bool isUserFlagSet( int flag ) const { return _userFlags & flag; } | | bool isUserFlagSet( int flag ) const { return _userFlags & flag; } | |
|
| void setUserFlag( int flag ); | | | |
| void clearUserFlag( int flag ); | | /** | |
| | | * these methods only modify NamespaceDetails and do not | |
| | | * sync changes back to system.namespaces | |
| | | * a typical call might | |
| | | if ( nsd->setUserFlag( 4 ) ) { | |
| | | nsd->syncUserFlags(); | |
| | | } | |
| | | * these methods all return true iff only something was modified | |
| | | */ | |
| | | | |
| | | bool setUserFlag( int flag ); | |
| | | bool clearUserFlag( int flag ); | |
| | | bool replaceUserFlags( int flags ); | |
| | | | |
| | | void syncUserFlags( const string& ns ); | |
| | | | |
| /* @return -1 = not found | | /* @return -1 = not found | |
| generally id is first index, so not that expensive an operation
(assuming present). | | generally id is first index, so not that expensive an operation
(assuming present). | |
| */ | | */ | |
| int findIdIndex() { | | int findIdIndex() { | |
| IndexIterator i = ii(); | | IndexIterator i = ii(); | |
| while( i.more() ) { | | while( i.more() ) { | |
| if( i.next().isIdIndex() ) | | if( i.next().isIdIndex() ) | |
| return i.pos()-1; | | return i.pos()-1; | |
| } | | } | |
| | | | |
| skipping to change at line 441 | | skipping to change at line 462 | |
| * @param order - Required ordering spec for documents produced by
this cursor, empty object | | * @param order - Required ordering spec for documents produced by
this cursor, empty object | |
| * default indicates no order requirement. If no index exists that
satisfies the required | | * default indicates no order requirement. If no index exists that
satisfies the required | |
| * sort order, an empty shared_ptr is returned unless parsedQuery i
s also provided. This is | | * sort order, an empty shared_ptr is returned unless parsedQuery i
s also provided. This is | |
| * not copied if unowned. | | * not copied if unowned. | |
| * | | * | |
| * @param planPolicy - A policy for selecting query plans - see que
ryoptimizercursor.h | | * @param planPolicy - A policy for selecting query plans - see que
ryoptimizercursor.h | |
| * | | * | |
| * @param simpleEqualityMatch - Set to true for certain simple quer
ies - see | | * @param simpleEqualityMatch - Set to true for certain simple quer
ies - see | |
| * queryoptimizer.cpp. | | * queryoptimizer.cpp. | |
| * | | * | |
|
| * @param parsedQuery - Additional query parameters, as from a clie | | * @param parsedQuery - Additional query parameters, as from a clie | |
| nt query request. If | | nt query request. | |
| * specified, the resulting cursor may return results from out of o | | * | |
| rder plans. See | | * @param requireOrder - If false, the resulting cursor may return | |
| * queryoptimizercursor.h for information on handling these results | | results in an order | |
| . | | * inconsistent with the @param order spec. See queryoptimizercurs | |
| | | or.h for information on | |
| | | * handling these results properly. | |
| * | | * | |
| * @param singlePlanSummary - Query plan summary information that m
ay be provided when a | | * @param singlePlanSummary - Query plan summary information that m
ay be provided when a | |
| * cursor running a single plan is returned. | | * cursor running a single plan is returned. | |
| * | | * | |
| * The returned cursor may @throw inside of advance() or recoverFro
mYield() in certain error | | * The returned cursor may @throw inside of advance() or recoverFro
mYield() in certain error | |
| * cases, for example if a capped overrun occurred during a yield.
This indicates that the | | * cases, for example if a capped overrun occurred during a yield.
This indicates that the | |
| * cursor was unable to perform a complete scan. | | * cursor was unable to perform a complete scan. | |
| * | | * | |
| * This is a work in progress. Partial list of features not yet im
plemented through this | | * This is a work in progress. Partial list of features not yet im
plemented through this | |
| * interface: | | * interface: | |
| | | | |
| skipping to change at line 465 | | skipping to change at line 488 | |
| * - covered indexes | | * - covered indexes | |
| * - in memory sorting | | * - in memory sorting | |
| */ | | */ | |
| static shared_ptr<Cursor> getCursor( const char *ns, const BSONObj
&query, | | static shared_ptr<Cursor> getCursor( const char *ns, const BSONObj
&query, | |
| const BSONObj &order = BSONObj(
), | | const BSONObj &order = BSONObj(
), | |
| const QueryPlanSelectionPolicy
&planPolicy = | | const QueryPlanSelectionPolicy
&planPolicy = | |
| QueryPlanSelectionPolicy::any()
, | | QueryPlanSelectionPolicy::any()
, | |
| bool *simpleEqualityMatch = 0, | | bool *simpleEqualityMatch = 0, | |
| const shared_ptr<const ParsedQu
ery> &parsedQuery = | | const shared_ptr<const ParsedQu
ery> &parsedQuery = | |
| shared_ptr<const ParsedQuery>()
, | | shared_ptr<const ParsedQuery>()
, | |
|
| | | bool requireOrder = true, | |
| QueryPlanSummary *singlePlanSum
mary = 0 ); | | QueryPlanSummary *singlePlanSum
mary = 0 ); | |
| | | | |
| /** | | /** | |
| * @return a single cursor that may work well for the given query.
A $or style query will | | * @return a single cursor that may work well for the given query.
A $or style query will | |
| * produce a single cursor, not a MultiCursor. | | * produce a single cursor, not a MultiCursor. | |
| * It is possible no cursor is returned if the sort is not supporte
d by an index. Clients are responsible | | * It is possible no cursor is returned if the sort is not supporte
d by an index. Clients are responsible | |
| * for checking this if they are not sure an index for a sort exist
s, and defaulting to a non-sort if | | * for checking this if they are not sure an index for a sort exist
s, and defaulting to a non-sort if | |
| * no suitable indices exist. | | * no suitable indices exist. | |
| */ | | */ | |
| static shared_ptr<Cursor> bestGuessCursor( const char *ns, const BS
ONObj &query, const BSONObj &sort ); | | static shared_ptr<Cursor> bestGuessCursor( const char *ns, const BS
ONObj &query, const BSONObj &sort ); | |
| | | | |
End of changes. 5 change blocks. |
| 9 lines changed or deleted | | 35 lines changed or added | |
|
| pdfile.h | | pdfile.h | |
| | | | |
| skipping to change at line 36 | | skipping to change at line 36 | |
| #pragma once | | #pragma once | |
| | | | |
| #include "mongo/db/client.h" | | #include "mongo/db/client.h" | |
| #include "mongo/db/diskloc.h" | | #include "mongo/db/diskloc.h" | |
| #include "mongo/db/jsobjmanipulator.h" | | #include "mongo/db/jsobjmanipulator.h" | |
| #include "mongo/db/memconcept.h" | | #include "mongo/db/memconcept.h" | |
| #include "mongo/db/mongommf.h" | | #include "mongo/db/mongommf.h" | |
| #include "mongo/db/namespace-inl.h" | | #include "mongo/db/namespace-inl.h" | |
| #include "mongo/db/namespace_details-inl.h" | | #include "mongo/db/namespace_details-inl.h" | |
| #include "mongo/db/namespacestring.h" | | #include "mongo/db/namespacestring.h" | |
|
| | | #include "mongo/util/log.h" | |
| #include "mongo/util/mmap.h" | | #include "mongo/util/mmap.h" | |
|
| #include "mongo/util/util.h" | | | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| // pdfile versions | | // pdfile versions | |
| const int PDFILE_VERSION = 4; | | const int PDFILE_VERSION = 4; | |
| const int PDFILE_VERSION_MINOR = 5; | | const int PDFILE_VERSION_MINOR = 5; | |
| | | | |
| class DataFileHeader; | | class DataFileHeader; | |
| class Extent; | | class Extent; | |
| class Record; | | class Record; | |
| | | | |
| skipping to change at line 85 | | skipping to change at line 85 | |
| /** creates if DNE */ | | /** creates if DNE */ | |
| void open(const char *filename, int requestedDataSize = 0, bool pre
allocateOnly = false); | | void open(const char *filename, int requestedDataSize = 0, bool pre
allocateOnly = false); | |
| | | | |
| /* allocate a new extent from this datafile. | | /* allocate a new extent from this datafile. | |
| @param capped - true if capped collection | | @param capped - true if capped collection | |
| @param loops is our recursion check variable - you want to pass
in zero | | @param loops is our recursion check variable - you want to pass
in zero | |
| */ | | */ | |
| Extent* createExtent(const char *ns, int approxSize, bool capped =
false, int loops = 0); | | Extent* createExtent(const char *ns, int approxSize, bool capped =
false, int loops = 0); | |
| | | | |
| DataFileHeader *getHeader() { return header(); } | | DataFileHeader *getHeader() { return header(); } | |
|
| | | HANDLE getFd() { return mmf.getFd(); } | |
| unsigned long long length() const { return mmf.length(); } | | unsigned long long length() const { return mmf.length(); } | |
| | | | |
| /* return max size an extent may be */ | | /* return max size an extent may be */ | |
| static int maxSize(); | | static int maxSize(); | |
| | | | |
| /** fsync */ | | /** fsync */ | |
| void flush( bool sync ); | | void flush( bool sync ); | |
| | | | |
| /** only use fore debugging */ | | /** only use fore debugging */ | |
| Extent* debug_getExtent(DiskLoc loc) { return _getExtent( loc ); } | | Extent* debug_getExtent(DiskLoc loc) { return _getExtent( loc ); } | |
| | | | |
| skipping to change at line 277 | | skipping to change at line 277 | |
| * its not guaranteed because its possible it gets swapped
out in a very unlucky windows | | * its not guaranteed because its possible it gets swapped
out in a very unlucky windows | |
| */ | | */ | |
| bool likelyInPhysicalMemory() const ; | | bool likelyInPhysicalMemory() const ; | |
| | | | |
| /** | | /** | |
| * tell the cache this Record was accessed | | * tell the cache this Record was accessed | |
| * @return this, for simple chaining | | * @return this, for simple chaining | |
| */ | | */ | |
| Record* accessed(); | | Record* accessed(); | |
| | | | |
|
| | | static bool likelyInPhysicalMemory( const char* data ); | |
| | | | |
| | | static bool blockCheckSupported(); | |
| | | | |
| | | /** | |
| | | * this adds stats about page fault exceptions currently | |
| | | * specically how many times we call _accessing where the record is | |
| | | not in memory | |
| | | * and how many times we throw a PageFaultException | |
| | | */ | |
| | | static void appendStats( BSONObjBuilder& b ); | |
| private: | | private: | |
| | | | |
| int _netLength() const { return _lengthWithHeaders - HeaderSize; } | | int _netLength() const { return _lengthWithHeaders - HeaderSize; } | |
| | | | |
| /** | | /** | |
| * call this when accessing a field which could hit disk | | * call this when accessing a field which could hit disk | |
| */ | | */ | |
| void _accessing() const; | | void _accessing() const; | |
| | | | |
| int _lengthWithHeaders; | | int _lengthWithHeaders; | |
| | | | |
| skipping to change at line 500 | | skipping to change at line 510 | |
| int ofs = dl.getOfs(); | | int ofs = dl.getOfs(); | |
| if( ofs < DataFileHeader::HeaderSize ) badOfs(ofs); // will uassert
- external call to keep out of the normal code path | | if( ofs < DataFileHeader::HeaderSize ) badOfs(ofs); // will uassert
- external call to keep out of the normal code path | |
| return (Record*) (p()+ofs); | | return (Record*) (p()+ofs); | |
| } | | } | |
| | | | |
| inline DiskLoc Record::getNext(const DiskLoc& myLoc) { | | inline DiskLoc Record::getNext(const DiskLoc& myLoc) { | |
| _accessing(); | | _accessing(); | |
| if ( _nextOfs != DiskLoc::NullOfs ) { | | if ( _nextOfs != DiskLoc::NullOfs ) { | |
| /* defensive */ | | /* defensive */ | |
| if ( _nextOfs >= 0 && _nextOfs < 10 ) { | | if ( _nextOfs >= 0 && _nextOfs < 10 ) { | |
|
| sayDbContext("Assertion failure - Record::getNext() referen
cing a deleted record?"); | | logContext("Assertion failure - Record::getNext() referenci
ng a deleted record?"); | |
| return DiskLoc(); | | return DiskLoc(); | |
| } | | } | |
| | | | |
| return DiskLoc(myLoc.a(), _nextOfs); | | return DiskLoc(myLoc.a(), _nextOfs); | |
| } | | } | |
| Extent *e = myExtent(myLoc); | | Extent *e = myExtent(myLoc); | |
| while ( 1 ) { | | while ( 1 ) { | |
| if ( e->xnext.isNull() ) | | if ( e->xnext.isNull() ) | |
| return DiskLoc(); // end of table. | | return DiskLoc(); // end of table. | |
| e = e->xnext.ext(); | | e = e->xnext.ext(); | |
| | | | |
| skipping to change at line 528 | | skipping to change at line 538 | |
| _accessing(); | | _accessing(); | |
| if ( _prevOfs != DiskLoc::NullOfs ) | | if ( _prevOfs != DiskLoc::NullOfs ) | |
| return DiskLoc(myLoc.a(), _prevOfs); | | return DiskLoc(myLoc.a(), _prevOfs); | |
| Extent *e = myExtent(myLoc); | | Extent *e = myExtent(myLoc); | |
| if ( e->xprev.isNull() ) | | if ( e->xprev.isNull() ) | |
| return DiskLoc(); | | return DiskLoc(); | |
| return e->xprev.ext()->lastRecord; | | return e->xprev.ext()->lastRecord; | |
| } | | } | |
| | | | |
| inline BSONObj DiskLoc::obj() const { | | inline BSONObj DiskLoc::obj() const { | |
|
| return BSONObj(rec()->accessed()); | | return BSONObj::make(rec()->accessed()); | |
| } | | } | |
| inline DeletedRecord* DiskLoc::drec() const { | | inline DeletedRecord* DiskLoc::drec() const { | |
| verify( _a != -1 ); | | verify( _a != -1 ); | |
| DeletedRecord* dr = (DeletedRecord*) rec(); | | DeletedRecord* dr = (DeletedRecord*) rec(); | |
| memconcept::is(dr, memconcept::concept::deletedrecord); | | memconcept::is(dr, memconcept::concept::deletedrecord); | |
| return dr; | | return dr; | |
| } | | } | |
| inline Extent* DiskLoc::ext() const { | | inline Extent* DiskLoc::ext() const { | |
| return DataFileMgr::getExtent(*this); | | return DataFileMgr::getExtent(*this); | |
| } | | } | |
| | | | |
| skipping to change at line 604 | | skipping to change at line 614 | |
| | | | |
| BOOST_STATIC_ASSERT( 16 == sizeof(DeletedRecord) ); | | BOOST_STATIC_ASSERT( 16 == sizeof(DeletedRecord) ); | |
| | | | |
| inline DeletedRecord* DataFileMgr::makeDeletedRecord(const DiskLoc& dl,
int len) { | | inline DeletedRecord* DataFileMgr::makeDeletedRecord(const DiskLoc& dl,
int len) { | |
| verify( dl.a() != -1 ); | | verify( dl.a() != -1 ); | |
| return (DeletedRecord*) cc().database()->getFile(dl.a())->makeRecor
d(dl, sizeof(DeletedRecord)); | | return (DeletedRecord*) cc().database()->getFile(dl.a())->makeRecor
d(dl, sizeof(DeletedRecord)); | |
| } | | } | |
| | | | |
| void ensureHaveIdIndex(const char *ns); | | void ensureHaveIdIndex(const char *ns); | |
| | | | |
|
| bool dropIndexes( NamespaceDetails *d, const char *ns, const char *name | | inline BSONObj BSONObj::make(const Record* r ) { | |
| , string &errmsg, BSONObjBuilder &anObjBuilder, bool maydeleteIdIndex ); | | return BSONObj( r->data() ); | |
| | | | |
| inline BSONObj::BSONObj(const Record *r) { | | | |
| init(r->data()); | | | |
| } | | } | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 7 change blocks. |
| 9 lines changed or deleted | | 17 lines changed or added | |
|
| qlock.h | | qlock.h | |
| | | | |
| skipping to change at line 32 | | skipping to change at line 32 | |
| w - i will write, and i will granularly lock after the qlock acqu
isition | | w - i will write, and i will granularly lock after the qlock acqu
isition | |
| r - i will read, and i will granularly lock after the qlock acqu
isition | | r - i will read, and i will granularly lock after the qlock acqu
isition | |
| W - i will write globally. stop the world. | | W - i will write globally. stop the world. | |
| R - i will read globally. stop any writer. | | R - i will read globally. stop any writer. | |
| | | | |
| For example there is a point during journal batch commits where we
wish to block all writers | | For example there is a point during journal batch commits where we
wish to block all writers | |
| but no readers. | | but no readers. | |
| | | | |
| Non-recursive. | | Non-recursive. | |
| | | | |
|
| r w R W <== lock that was around | | r w R W X <== lock that was around | |
| r - - - X | | r * * * - - | |
| w - - X X - allowed | | w * * - - - * allowed | |
| R - X - X X not allowed (blocks) | | R * - * - - - not allowed (blocks) | |
| W X X X X | | W - - - - - ! See NOTE(!). | |
| | | X - ! - - - | |
| ^ | | ^ | |
| lock we are requesting | | lock we are requesting | |
|
| | | | |
| | | NOTE(!): The "X" state can only be reached from the "w" state. A t | |
| | | hread successfully | |
| | | transitions from "w" to "X" when w_to_X() returns true, and fails t | |
| | | o transition to that | |
| | | state (remaining in "w") when that function returns false. For one | |
| | | thread to successfully | |
| | | transition, all threads in the "w" state must be blocked in w_to_X( | |
| | | ). When all threads in | |
| | | the "w" state are blocked in w_to_X(), one thread will be released | |
| | | in the X state. The | |
| | | other threads remain blocked in w_to_X() until the thread in the X | |
| | | state calls X_to_w(). | |
| */ | | */ | |
| class QLock : boost::noncopyable { | | class QLock : boost::noncopyable { | |
| struct Z { | | struct Z { | |
| Z() : n(0) { } | | Z() : n(0) { } | |
| boost::condition c; | | boost::condition c; | |
| int n; | | int n; | |
| }; | | }; | |
| boost::mutex m; | | boost::mutex m; | |
|
| Z r,w,R,W,U,X; // X is used by QLock::runExclusively | | Z r,w,R,W,U,X; | |
| int greed; // >0 if someone wants to acquire a write lock | | int numPendingGlobalWrites; // >0 if someone wants to acquire a wr | |
| int greedyWrites; // 0=no, 1=true | | ite lock | |
| int greedSuspended; | | long long generationX; | |
| void _stop_greed(); // we are already inlock for these underscore | | long long generationXExit; | |
| methods | | | |
| void _lock_W(); | | void _lock_W(); | |
|
| bool W_legal() const { return r.n + w.n + R.n + W.n == 0; } | | void _unlock_R(); | |
| bool R_legal() const { return w.n + + W.n == 0; } | | bool _areQueueJumpingGlobalWritesPending() const { | |
| bool w_legal() const { return R.n + W.n == 0; } | | return numPendingGlobalWrites > 0; | |
| bool r_legal() const { return W.n == 0; } | | } | |
| | | | |
| | | bool W_legal() const { return r.n + w.n + R.n + W.n + X.n == 0; } | |
| | | bool R_legal_ignore_greed() const { return w.n + W.n + X.n == 0; } | |
| | | bool r_legal_ignore_greed() const { return W.n + X.n == 0; } | |
| | | bool w_legal_ignore_greed() const { return R.n + W.n + X.n == 0; } | |
| | | | |
| | | bool R_legal() const { | |
| | | return !_areQueueJumpingGlobalWritesPending() && R_legal_ignore | |
| | | _greed(); | |
| | | } | |
| | | | |
| | | bool w_legal() const { | |
| | | return !_areQueueJumpingGlobalWritesPending() && w_legal_ignore | |
| | | _greed(); | |
| | | } | |
| | | | |
| | | bool r_legal() const { | |
| | | return !_areQueueJumpingGlobalWritesPending() && r_legal_ignore | |
| | | _greed(); | |
| | | } | |
| | | | |
| | | bool X_legal() const { return w.n + r.n + R.n + W.n == 0; } | |
| | | | |
| void notifyWeUnlocked(char me); | | void notifyWeUnlocked(char me); | |
| static bool i_block(char me, char them); | | static bool i_block(char me, char them); | |
| public: | | public: | |
|
| QLock() : greed(0), greedyWrites(1), greedSuspended(0) { } | | QLock() : | |
| | | numPendingGlobalWrites(0), | |
| | | generationX(0), | |
| | | generationXExit(0) { | |
| | | } | |
| | | | |
| void lock_r(); | | void lock_r(); | |
| void lock_w(); | | void lock_w(); | |
| void lock_R(); | | void lock_R(); | |
| bool lock_R_try(int millis); | | bool lock_R_try(int millis); | |
| void lock_W(); | | void lock_W(); | |
| bool lock_W_try(int millis); | | bool lock_W_try(int millis); | |
|
| void lock_W_stop_greed(); | | | |
| void unlock_r(); | | void unlock_r(); | |
| void unlock_w(); | | void unlock_w(); | |
| void unlock_R(); | | void unlock_R(); | |
| void unlock_W(); | | void unlock_W(); | |
|
| void start_greed(); | | | |
| void stop_greed(); | | | |
| void W_to_R(); | | void W_to_R(); | |
|
| bool R_to_W(); // caution see notes below | | void R_to_W(); // caution see notes below | |
| void runExclusively(void (*f)(void)); | | bool w_to_X(); | |
| | | void X_to_w(); | |
| }; | | }; | |
| | | | |
| inline bool QLock::i_block(char me, char them) { | | inline bool QLock::i_block(char me, char them) { | |
| switch( me ) { | | switch( me ) { | |
| case 'W' : return true; | | case 'W' : return true; | |
|
| case 'R' : return them == 'W' || them == 'w'; | | case 'R' : return them == 'W' || them == 'w' || them == 'X'; | |
| case 'w' : return them == 'W' || them == 'R'; | | case 'w' : return them == 'W' || them == 'R' || them == 'X'; | |
| case 'r' : return them == 'W'; | | case 'r' : return them == 'W' || them == 'X'; | |
| default : verify(false); | | case 'X' : return true; | |
| | | default : fassertFailed(16200); | |
| } | | } | |
| return false; | | return false; | |
| } | | } | |
| | | | |
| inline void QLock::notifyWeUnlocked(char me) { | | inline void QLock::notifyWeUnlocked(char me) { | |
|
| verify( W.n == 0 ); | | fassert(16201, W.n == 0); | |
| | | if ( me == 'X' ) { | |
| | | X.c.notify_all(); | |
| | | } | |
| if( U.n ) { | | if( U.n ) { | |
| // U is highest priority | | // U is highest priority | |
|
| if( r.n + w.n + W.n == 0 ) | | if( (r.n + w.n + W.n + X.n == 0) && (R.n == 1) ) { | |
| U.c.notify_one(); | | U.c.notify_one(); | |
|
| return; | | return; | |
| | | } | |
| } | | } | |
|
| if( W_legal() /*&& i_block(me,'W')*/ ) { | | if ( X_legal() && i_block(me, 'X') ) { | |
| int g = greed; | | X.c.notify_one(); | |
| | | } | |
| | | if ( W_legal() && i_block(me, 'W') ) { | |
| W.c.notify_one(); | | W.c.notify_one(); | |
|
| if( g ) // g>0 indicates someone was definitely waiting for W,
so we can stop here | | if( _areQueueJumpingGlobalWritesPending() ) | |
| return; | | return; | |
| } | | } | |
|
| if( R_legal() && i_block(me,'R') ) { | | if ( R_legal_ignore_greed() && i_block(me, 'R') ) { | |
| R.c.notify_all(); | | R.c.notify_all(); | |
| } | | } | |
|
| if( w_legal() && i_block(me,'w') ) { | | if ( w_legal_ignore_greed() && i_block(me, 'w') ) { | |
| w.c.notify_all(); | | w.c.notify_all(); | |
| } | | } | |
|
| if( r_legal() && i_block(me,'r') ) { | | if ( r_legal_ignore_greed() && i_block(me, 'r') ) { | |
| r.c.notify_all(); | | r.c.notify_all(); | |
| } | | } | |
| } | | } | |
| | | | |
|
| inline void QLock::_stop_greed() { | | | |
| if( ++greedSuspended == 1 ) // recursion on stop_greed/start_greed | | | |
| is ok | | | |
| greedyWrites = 0; | | | |
| } | | | |
| inline void QLock::stop_greed() { | | | |
| boost::mutex::scoped_lock lk(m); | | | |
| _stop_greed(); | | | |
| } | | | |
| | | | |
| inline void QLock::start_greed() { | | | |
| boost::mutex::scoped_lock lk(m); | | | |
| if( --greedSuspended == 0 ) | | | |
| greedyWrites = 1; | | | |
| } | | | |
| | | | |
| // "i will be reading. i promise to coordinate my activities with w's a
s i go with more | | // "i will be reading. i promise to coordinate my activities with w's a
s i go with more | |
| // granular locks." | | // granular locks." | |
| inline void QLock::lock_r() { | | inline void QLock::lock_r() { | |
| boost::mutex::scoped_lock lk(m); | | boost::mutex::scoped_lock lk(m); | |
|
| while( greed + W.n ) { | | while( !r_legal() ) { | |
| r.c.wait(m); | | r.c.wait(m); | |
| } | | } | |
| r.n++; | | r.n++; | |
| } | | } | |
| | | | |
| // "i will be writing. i promise to coordinate my activities with w's a
nd r's as i go with more | | // "i will be writing. i promise to coordinate my activities with w's a
nd r's as i go with more | |
| // granular locks." | | // granular locks." | |
| inline void QLock::lock_w() { | | inline void QLock::lock_w() { | |
| boost::mutex::scoped_lock lk(m); | | boost::mutex::scoped_lock lk(m); | |
|
| while( greed + W.n + R.n ) { | | while( !w_legal() ) { | |
| w.c.wait(m); | | w.c.wait(m); | |
| } | | } | |
| w.n++; | | w.n++; | |
| } | | } | |
| | | | |
| // "i will be reading. i will coordinate with no one. you better stop t
hem if they | | // "i will be reading. i will coordinate with no one. you better stop t
hem if they | |
| // are writing." | | // are writing." | |
| inline void QLock::lock_R() { | | inline void QLock::lock_R() { | |
| boost::mutex::scoped_lock lk(m); | | boost::mutex::scoped_lock lk(m); | |
|
| while( greed + W.n + w.n ) { | | while( ! R_legal() ) { | |
| R.c.wait(m); | | R.c.wait(m); | |
| } | | } | |
| R.n++; | | R.n++; | |
| } | | } | |
| | | | |
| inline bool QLock::lock_R_try(int millis) { | | inline bool QLock::lock_R_try(int millis) { | |
| unsigned long long end = curTimeMillis64() + millis; | | unsigned long long end = curTimeMillis64() + millis; | |
| boost::mutex::scoped_lock lk(m); | | boost::mutex::scoped_lock lk(m); | |
|
| while( 1 ) { | | while( !R_legal() && curTimeMillis64() < end ) { | |
| if( greed + W.n + w.n == 0 ) | | | |
| break; | | | |
| R.c.timed_wait(m, boost::posix_time::milliseconds(millis)); | | R.c.timed_wait(m, boost::posix_time::milliseconds(millis)); | |
|
| if( greed + W.n + w.n == 0 ) | | | |
| break; | | | |
| if( curTimeMillis64() >= end ) | | | |
| return false; | | | |
| } | | } | |
|
| R.n++; | | if ( R_legal() ) { | |
| return true; | | R.n++; | |
| | | return true; | |
| | | } | |
| | | return false; | |
| } | | } | |
| | | | |
| inline bool QLock::lock_W_try(int millis) { | | inline bool QLock::lock_W_try(int millis) { | |
| unsigned long long end = curTimeMillis64() + millis; | | unsigned long long end = curTimeMillis64() + millis; | |
| boost::mutex::scoped_lock lk(m); | | boost::mutex::scoped_lock lk(m); | |
|
| int g = greedyWrites; | | | |
| greed += g; | | ++numPendingGlobalWrites; | |
| while( 1 ) { | | while (!W_legal() && curTimeMillis64() < end) { | |
| if( W.n + R.n + w.n + r.n == 0 ) | | | |
| break; | | | |
| W.c.timed_wait(m, boost::posix_time::milliseconds(millis)); | | W.c.timed_wait(m, boost::posix_time::milliseconds(millis)); | |
|
| if( W.n + R.n + w.n + r.n == 0 ) | | | |
| break; | | | |
| if( curTimeMillis64() >= end ) { | | | |
| greed -= g; | | | |
| dassert( greed >= 0 ); | | | |
| // should we do notify_one on W.c so we should be careful n | | | |
| ot to leave someone | | | |
| // else waiting when we give up here perhaps. it is very po | | | |
| ssible this code | | | |
| // is unnecessary: | | | |
| // W.c.notify_one(); | | | |
| return false; | | | |
| } | | | |
| } | | } | |
|
| W.n += 1; | | --numPendingGlobalWrites; | |
| dassert( W.n == 1 ); | | | |
| greed -= g; | | if (W_legal()) { | |
| return true; | | W.n++; | |
| | | fassert( 16202, W.n == 1 ); | |
| | | return true; | |
| | | } | |
| | | | |
| | | return false; | |
| } | | } | |
| | | | |
| // downgrade from W state to R state | | // downgrade from W state to R state | |
| inline void QLock::W_to_R() { | | inline void QLock::W_to_R() { | |
| boost::mutex::scoped_lock lk(m); | | boost::mutex::scoped_lock lk(m); | |
|
| verify( W.n == 1 ); | | fassert(16203, W.n == 1); | |
| verify( R.n == 0 ); | | fassert(16204, R.n == 0); | |
| verify( U.n == 0 ); | | fassert(16205, U.n == 0); | |
| W.n = 0; | | W.n = 0; | |
| R.n = 1; | | R.n = 1; | |
| notifyWeUnlocked('W'); | | notifyWeUnlocked('W'); | |
| } | | } | |
| | | | |
| // upgrade from R to W state. | | // upgrade from R to W state. | |
|
| | | // | |
| | | // This transition takes precedence over all pending requests by thread | |
| | | s to enter | |
| | | // any state other than '\0'. | |
| | | // | |
| // there is no "upgradable" state so this is NOT a classic upgrade - | | // there is no "upgradable" state so this is NOT a classic upgrade - | |
| // if two threads try to do this you will deadlock. | | // if two threads try to do this you will deadlock. | |
|
| inline bool QLock::R_to_W() { | | // | |
| | | // NOTE: ONLY CALL THIS FUNCTION ON A THREAD THAT GOT TO R BY CALLING W | |
| | | _to_R(), OR | |
| | | // YOU MAY DEADLOCK WITH THREADS LEAVING THE X STATE. | |
| | | inline void QLock::R_to_W() { | |
| boost::mutex::scoped_lock lk(m); | | boost::mutex::scoped_lock lk(m); | |
|
| verify( R.n > 0 && W.n == 0 ); | | fassert(16206, R.n > 0); | |
| U.n++; | | fassert(16207, W.n == 0); | |
| fassert( 16136, U.n == 1 ); // for now we only allow one upgrade at | | fassert(16208, U.n == 0); | |
| tempter | | | |
| int pass = 0; | | U.n = 1; | |
| | | | |
| | | ++numPendingGlobalWrites; | |
| | | | |
| while( W.n + R.n + w.n + r.n > 1 ) { | | while( W.n + R.n + w.n + r.n > 1 ) { | |
|
| if( ++pass >= 3 ) { | | U.c.wait(m); | |
| U.n--; | | | |
| return false; | | | |
| } | | | |
| U.c.timed_wait(m, boost::posix_time::milliseconds(300)); | | | |
| } | | } | |
|
| R.n--; | | --numPendingGlobalWrites; | |
| W.n++; | | | |
| U.n--; | | fassert(16209, R.n == 1); | |
| verify( R.n == 0 && W.n == 1 && U.n == 0 ); | | fassert(16210, W.n == 0); | |
| return true; | | fassert(16211, U.n == 1); | |
| | | | |
| | | R.n = 0; | |
| | | W.n = 1; | |
| | | U.n = 0; | |
| | | } | |
| | | | |
| | | inline bool QLock::w_to_X() { | |
| | | boost::mutex::scoped_lock lk(m); | |
| | | | |
| | | fassert( 16212, w.n > 0 ); | |
| | | | |
| | | ++X.n; | |
| | | --w.n; | |
| | | | |
| | | long long myGeneration = generationX; | |
| | | | |
| | | while ( !X_legal() && (myGeneration == generationX) ) | |
| | | X.c.wait(m); | |
| | | | |
| | | if ( myGeneration == generationX ) { | |
| | | fassert( 16214, X_legal() ); | |
| | | fassert( 16215, w.n == 0 ); | |
| | | ++generationX; | |
| | | notifyWeUnlocked('w'); | |
| | | return true; | |
| | | } | |
| | | | |
| | | while ( myGeneration == generationXExit ) | |
| | | X.c.wait(m); | |
| | | | |
| | | fassert( 16216, R.n == 0 ); | |
| | | fassert( 16217, w.n > 0 ); | |
| | | return false; | |
| | | } | |
| | | | |
| | | inline void QLock::X_to_w() { | |
| | | boost::mutex::scoped_lock lk(m); | |
| | | | |
| | | fassert( 16219, W.n == 0 ); | |
| | | fassert( 16220, R.n == 0 ); | |
| | | fassert( 16221, w.n == 0 ); | |
| | | fassert( 16222, X.n > 0 ); | |
| | | | |
| | | w.n = X.n; | |
| | | X.n = 0; | |
| | | ++generationXExit; | |
| | | notifyWeUnlocked('X'); | |
| } | | } | |
| | | | |
| // "i will be writing. i will coordinate with no one. you better stop t
hem all" | | // "i will be writing. i will coordinate with no one. you better stop t
hem all" | |
| inline void QLock::_lock_W() { | | inline void QLock::_lock_W() { | |
|
| int g = greedyWrites; | | ++numPendingGlobalWrites; | |
| greed += g; | | while( !W_legal() ) { | |
| while( W.n + R.n + w.n + r.n ) { | | | |
| W.c.wait(m); | | W.c.wait(m); | |
| } | | } | |
|
| | | --numPendingGlobalWrites; | |
| W.n++; | | W.n++; | |
|
| greed -= g; | | | |
| } | | } | |
| inline void QLock::lock_W() { | | inline void QLock::lock_W() { | |
| boost::mutex::scoped_lock lk(m); | | boost::mutex::scoped_lock lk(m); | |
| _lock_W(); | | _lock_W(); | |
| } | | } | |
|
| inline void QLock::lock_W_stop_greed() { | | | |
| boost::mutex::scoped_lock lk(m); | | | |
| _lock_W(); | | | |
| _stop_greed(); | | | |
| } | | | |
| | | | |
| inline void QLock::unlock_r() { | | inline void QLock::unlock_r() { | |
| boost::mutex::scoped_lock lk(m); | | boost::mutex::scoped_lock lk(m); | |
| fassert(16137, r.n > 0); | | fassert(16137, r.n > 0); | |
|
| if( --r.n == 0 ) | | --r.n; | |
| notifyWeUnlocked('r'); | | notifyWeUnlocked('r'); | |
| } | | } | |
| inline void QLock::unlock_w() { | | inline void QLock::unlock_w() { | |
| boost::mutex::scoped_lock lk(m); | | boost::mutex::scoped_lock lk(m); | |
| fassert(16138, w.n > 0); | | fassert(16138, w.n > 0); | |
|
| if( --w.n == 0 ) | | --w.n; | |
| notifyWeUnlocked('w'); | | notifyWeUnlocked('w'); | |
| X.c.notify_one(); | | | |
| } | | } | |
|
| | | | |
| inline void QLock::unlock_R() { | | inline void QLock::unlock_R() { | |
| boost::mutex::scoped_lock lk(m); | | boost::mutex::scoped_lock lk(m); | |
|
| | | _unlock_R(); | |
| | | } | |
| | | | |
| | | inline void QLock::_unlock_R() { | |
| fassert(16139, R.n > 0); | | fassert(16139, R.n > 0); | |
|
| if( --R.n == 0 ) | | --R.n; | |
| notifyWeUnlocked('R'); | | notifyWeUnlocked('R'); | |
| } | | } | |
|
| | | | |
| inline void QLock::unlock_W() { | | inline void QLock::unlock_W() { | |
| boost::mutex::scoped_lock lk(m); | | boost::mutex::scoped_lock lk(m); | |
| fassert(16140, W.n == 1); | | fassert(16140, W.n == 1); | |
|
| W.n--; | | --W.n; | |
| notifyWeUnlocked('W'); | | notifyWeUnlocked('W'); | |
| } | | } | |
| | | | |
| } | | } | |
| | | | |
End of changes. 44 change blocks. |
| 120 lines changed or deleted | | 191 lines changed or added | |
|
| query.h | | query.h | |
| | | | |
| skipping to change at line 39 | | skipping to change at line 39 | |
| // struct QueryOptions, QueryResult, QueryResultFlags in: | | // struct QueryOptions, QueryResult, QueryResultFlags in: | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| class ParsedQuery; | | class ParsedQuery; | |
| class QueryOptimizerCursor; | | class QueryOptimizerCursor; | |
| class QueryPlanSummary; | | class QueryPlanSummary; | |
| | | | |
| QueryResult* processGetMore(const char *ns, int ntoreturn, long long cu
rsorid , CurOp& op, int pass, bool& exhaust); | | QueryResult* processGetMore(const char *ns, int ntoreturn, long long cu
rsorid , CurOp& op, int pass, bool& exhaust); | |
| | | | |
|
| const char * runQuery(Message& m, QueryMessage& q, CurOp& curop, Messag
e &result); | | string runQuery(Message& m, QueryMessage& q, CurOp& curop, Message &res
ult); | |
| | | | |
| /** Exception indicating that a query should be retried from the beginn
ing. */ | | /** Exception indicating that a query should be retried from the beginn
ing. */ | |
| class QueryRetryException : public DBException { | | class QueryRetryException : public DBException { | |
| public: | | public: | |
| QueryRetryException() : DBException( "query retry exception" , 1608
3 ) { | | QueryRetryException() : DBException( "query retry exception" , 1608
3 ) { | |
| return; | | return; | |
| massert( 16083, "reserve 16083", true ); // Reserve 16083. | | massert( 16083, "reserve 16083", true ); // Reserve 16083. | |
| } | | } | |
| }; | | }; | |
| | | | |
| | | | |
| skipping to change at line 121 | | skipping to change at line 121 | |
| * Record explain events for a QueryOptimizerCursor, which may record s
ome explain information | | * Record explain events for a QueryOptimizerCursor, which may record s
ome explain information | |
| * for multiple clauses and plans through an internal implementation. | | * for multiple clauses and plans through an internal implementation. | |
| */ | | */ | |
| class QueryOptimizerCursorExplainStrategy : public MatchCountingExplain
Strategy { | | class QueryOptimizerCursorExplainStrategy : public MatchCountingExplain
Strategy { | |
| public: | | public: | |
| QueryOptimizerCursorExplainStrategy( const ExplainQueryInfo::Ancill
aryInfo &ancillaryInfo, | | QueryOptimizerCursorExplainStrategy( const ExplainQueryInfo::Ancill
aryInfo &ancillaryInfo, | |
| const shared_ptr<QueryOptimizer
Cursor> &cursor ); | | const shared_ptr<QueryOptimizer
Cursor> &cursor ); | |
| private: | | private: | |
| virtual void _noteIterate( bool match, bool orderedMatch, bool load
edRecord, | | virtual void _noteIterate( bool match, bool orderedMatch, bool load
edRecord, | |
| bool chunkSkip ); | | bool chunkSkip ); | |
|
| | | virtual void noteYield(); | |
| virtual shared_ptr<ExplainQueryInfo> _doneQueryInfo(); | | virtual shared_ptr<ExplainQueryInfo> _doneQueryInfo(); | |
| shared_ptr<QueryOptimizerCursor> _cursor; | | shared_ptr<QueryOptimizerCursor> _cursor; | |
| }; | | }; | |
| | | | |
| /** Interface for building a query response in a supplied BufBuilder. *
/ | | /** Interface for building a query response in a supplied BufBuilder. *
/ | |
| class ResponseBuildStrategy { | | class ResponseBuildStrategy { | |
| public: | | public: | |
| /** | | /** | |
| * @param queryPlan must be supplied if @param cursor is not a Quer
yOptimizerCursor and | | * @param queryPlan must be supplied if @param cursor is not a Quer
yOptimizerCursor and | |
| * results must be sorted or read with a covered index. | | * results must be sorted or read with a covered index. | |
| */ | | */ | |
| ResponseBuildStrategy( const ParsedQuery &parsedQuery, const shared
_ptr<Cursor> &cursor, | | ResponseBuildStrategy( const ParsedQuery &parsedQuery, const shared
_ptr<Cursor> &cursor, | |
|
| BufBuilder &buf, const QueryPlanSummary &quer
yPlan ); | | BufBuilder &buf ); | |
| virtual ~ResponseBuildStrategy() {} | | virtual ~ResponseBuildStrategy() {} | |
| /** | | /** | |
| * Handle the current iterate of the supplied cursor as a (possibly
duplicate) match. | | * Handle the current iterate of the supplied cursor as a (possibly
duplicate) match. | |
| * @return true if a match is found. | | * @return true if a match is found. | |
| * @param orderedMatch set if it is an ordered match. | | * @param orderedMatch set if it is an ordered match. | |
| */ | | */ | |
|
| virtual bool handleMatch( bool &orderedMatch ) = 0; | | virtual bool handleMatch( bool& orderedMatch, MatchDetails& details | |
| | | ) = 0; | |
| | | | |
| /** | | /** | |
| * Write all matches into the buffer, overwriting existing data. | | * Write all matches into the buffer, overwriting existing data. | |
| * @return number of matches written, or -1 if no op. | | * @return number of matches written, or -1 if no op. | |
| */ | | */ | |
| virtual int rewriteMatches() { return -1; } | | virtual int rewriteMatches() { return -1; } | |
| /** @return the number of matches that have been written to the buf
fer. */ | | /** @return the number of matches that have been written to the buf
fer. */ | |
| virtual int bufferedMatches() const = 0; | | virtual int bufferedMatches() const = 0; | |
| /** | | /** | |
| * Callback when enough results have been read for the first batch,
with potential handoff | | * Callback when enough results have been read for the first batch,
with potential handoff | |
| * to getMore. | | * to getMore. | |
| | | | |
| skipping to change at line 171 | | skipping to change at line 173 | |
| const ParsedQuery &_parsedQuery; | | const ParsedQuery &_parsedQuery; | |
| shared_ptr<Cursor> _cursor; | | shared_ptr<Cursor> _cursor; | |
| shared_ptr<QueryOptimizerCursor> _queryOptimizerCursor; | | shared_ptr<QueryOptimizerCursor> _queryOptimizerCursor; | |
| BufBuilder &_buf; | | BufBuilder &_buf; | |
| }; | | }; | |
| | | | |
| /** Build strategy for a cursor returning in order results. */ | | /** Build strategy for a cursor returning in order results. */ | |
| class OrderedBuildStrategy : public ResponseBuildStrategy { | | class OrderedBuildStrategy : public ResponseBuildStrategy { | |
| public: | | public: | |
| OrderedBuildStrategy( const ParsedQuery &parsedQuery, const shared_
ptr<Cursor> &cursor, | | OrderedBuildStrategy( const ParsedQuery &parsedQuery, const shared_
ptr<Cursor> &cursor, | |
|
| BufBuilder &buf, const QueryPlanSummary &query | | BufBuilder &buf ); | |
| Plan ); | | virtual bool handleMatch( bool& orderedMatch, MatchDetails& details | |
| virtual bool handleMatch( bool &orderedMatch ); | | ); | |
| virtual int bufferedMatches() const { return _bufferedMatches; } | | virtual int bufferedMatches() const { return _bufferedMatches; } | |
| private: | | private: | |
| int _skip; | | int _skip; | |
| int _bufferedMatches; | | int _bufferedMatches; | |
| }; | | }; | |
| | | | |
| class ScanAndOrder; | | class ScanAndOrder; | |
| | | | |
| /** Build strategy for a cursor returning out of order results. */ | | /** Build strategy for a cursor returning out of order results. */ | |
| class ReorderBuildStrategy : public ResponseBuildStrategy { | | class ReorderBuildStrategy : public ResponseBuildStrategy { | |
| public: | | public: | |
|
| ReorderBuildStrategy( const ParsedQuery &parsedQuery, | | static ReorderBuildStrategy* make( const ParsedQuery& parsedQuery, | |
| const shared_ptr<Cursor> &cursor, | | const shared_ptr<Cursor>& cursor | |
| BufBuilder &buf, | | , | |
| const QueryPlanSummary &queryPlan ); | | BufBuilder& buf, | |
| virtual bool handleMatch( bool &orderedMatch ); | | const QueryPlanSummary& queryPla | |
| | | n ); | |
| | | virtual bool handleMatch( bool &orderedMatch, MatchDetails& details | |
| | | ); | |
| /** Handle a match without performing deduping. */ | | /** Handle a match without performing deduping. */ | |
| void _handleMatchNoDedup(); | | void _handleMatchNoDedup(); | |
| virtual int rewriteMatches(); | | virtual int rewriteMatches(); | |
| virtual int bufferedMatches() const { return _bufferedMatches; } | | virtual int bufferedMatches() const { return _bufferedMatches; } | |
| private: | | private: | |
|
| | | ReorderBuildStrategy( const ParsedQuery& parsedQuery, | |
| | | const shared_ptr<Cursor>& cursor, | |
| | | BufBuilder& buf ); | |
| | | void init( const QueryPlanSummary& queryPlan ); | |
| ScanAndOrder *newScanAndOrder( const QueryPlanSummary &queryPlan )
const; | | ScanAndOrder *newScanAndOrder( const QueryPlanSummary &queryPlan )
const; | |
| shared_ptr<ScanAndOrder> _scanAndOrder; | | shared_ptr<ScanAndOrder> _scanAndOrder; | |
| int _bufferedMatches; | | int _bufferedMatches; | |
| }; | | }; | |
| | | | |
| /** Helper class for deduping DiskLocs */ | | /** Helper class for deduping DiskLocs */ | |
| class DiskLocDupSet { | | class DiskLocDupSet { | |
| public: | | public: | |
| /** @return true if dup, otherwise return false and insert. */ | | /** @return true if dup, otherwise return false and insert. */ | |
| bool getsetdup( const DiskLoc &loc ) { | | bool getsetdup( const DiskLoc &loc ) { | |
| | | | |
| skipping to change at line 217 | | skipping to change at line 223 | |
| private: | | private: | |
| set<DiskLoc> _dups; | | set<DiskLoc> _dups; | |
| }; | | }; | |
| | | | |
| /** | | /** | |
| * Build strategy for a QueryOptimizerCursor containing some in order a
nd some out of order | | * Build strategy for a QueryOptimizerCursor containing some in order a
nd some out of order | |
| * candidate plans. | | * candidate plans. | |
| */ | | */ | |
| class HybridBuildStrategy : public ResponseBuildStrategy { | | class HybridBuildStrategy : public ResponseBuildStrategy { | |
| public: | | public: | |
|
| | | static HybridBuildStrategy* make( const ParsedQuery& parsedQuery, | |
| | | const shared_ptr<QueryOptimizerCu | |
| | | rsor>& cursor, | |
| | | BufBuilder& buf ); | |
| | | private: | |
| HybridBuildStrategy( const ParsedQuery &parsedQuery, | | HybridBuildStrategy( const ParsedQuery &parsedQuery, | |
| const shared_ptr<QueryOptimizerCursor> &cursor, | | const shared_ptr<QueryOptimizerCursor> &cursor, | |
| BufBuilder &buf ); | | BufBuilder &buf ); | |
|
| private: | | void init(); | |
| virtual bool handleMatch( bool &orderedMatch ); | | virtual bool handleMatch( bool &orderedMatch, MatchDetails &details | |
| | | ); | |
| virtual int rewriteMatches(); | | virtual int rewriteMatches(); | |
| virtual int bufferedMatches() const; | | virtual int bufferedMatches() const; | |
| virtual void finishedFirstBatch(); | | virtual void finishedFirstBatch(); | |
| bool handleReorderMatch(); | | bool handleReorderMatch(); | |
| DiskLocDupSet _scanAndOrderDups; | | DiskLocDupSet _scanAndOrderDups; | |
| OrderedBuildStrategy _orderedBuild; | | OrderedBuildStrategy _orderedBuild; | |
|
| ReorderBuildStrategy _reorderBuild; | | scoped_ptr<ReorderBuildStrategy> _reorderBuild; | |
| bool _reorderedMatches; | | bool _reorderedMatches; | |
| }; | | }; | |
| | | | |
| /** | | /** | |
| * Builds a query response with the help of an ExplainRecordingStrategy
and a | | * Builds a query response with the help of an ExplainRecordingStrategy
and a | |
| * ResponseBuildStrategy. | | * ResponseBuildStrategy. | |
| */ | | */ | |
| class QueryResponseBuilder { | | class QueryResponseBuilder { | |
| public: | | public: | |
| /** | | /** | |
| * @param queryPlan must be supplied if @param cursor is not a Quer
yOptimizerCursor and | | * @param queryPlan must be supplied if @param cursor is not a Quer
yOptimizerCursor and | |
| * results must be sorted or read with a covered index. | | * results must be sorted or read with a covered index. | |
| */ | | */ | |
|
| QueryResponseBuilder( const ParsedQuery &parsedQuery, const shared_ | | static QueryResponseBuilder *make( const ParsedQuery &parsedQuery, | |
| ptr<Cursor> &cursor, | | const shared_ptr<Cursor> &cursor, | |
| const QueryPlanSummary &queryPlan, const BSONO | | const QueryPlanSummary &queryPlan | |
| bj &oldPlan ); | | , | |
| | | const BSONObj &oldPlan ); | |
| /** @return true if the current iterate matches and is added. */ | | /** @return true if the current iterate matches and is added. */ | |
| bool addMatch(); | | bool addMatch(); | |
| /** Note that a yield occurred. */ | | /** Note that a yield occurred. */ | |
| void noteYield(); | | void noteYield(); | |
| /** @return true if there are enough results to return the first ba
tch. */ | | /** @return true if there are enough results to return the first ba
tch. */ | |
| bool enoughForFirstBatch() const; | | bool enoughForFirstBatch() const; | |
| /** @return true if there are enough results to return the full res
ult set. */ | | /** @return true if there are enough results to return the full res
ult set. */ | |
| bool enoughTotalResults() const; | | bool enoughTotalResults() const; | |
| /** | | /** | |
| * Callback when enough results have been read for the first batch,
with potential handoff | | * Callback when enough results have been read for the first batch,
with potential handoff | |
| | | | |
| skipping to change at line 264 | | skipping to change at line 276 | |
| * to getMore. | | * to getMore. | |
| */ | | */ | |
| void finishedFirstBatch(); | | void finishedFirstBatch(); | |
| /** | | /** | |
| * Set the data portion of the supplied Message to a buffer contain
ing the query results. | | * Set the data portion of the supplied Message to a buffer contain
ing the query results. | |
| * @return the number of results in the buffer. | | * @return the number of results in the buffer. | |
| */ | | */ | |
| int handoff( Message &result ); | | int handoff( Message &result ); | |
| /** A chunk manager found at the beginning of the query. */ | | /** A chunk manager found at the beginning of the query. */ | |
| ShardChunkManagerPtr chunkManager() const { return _chunkManager; } | | ShardChunkManagerPtr chunkManager() const { return _chunkManager; } | |
|
| | | | |
| private: | | private: | |
|
| | | QueryResponseBuilder( const ParsedQuery &parsedQuery, const shared_ | |
| | | ptr<Cursor> &cursor ); | |
| | | void init( const QueryPlanSummary &queryPlan, const BSONObj &oldPla | |
| | | n ); | |
| | | | |
| ShardChunkManagerPtr newChunkManager() const; | | ShardChunkManagerPtr newChunkManager() const; | |
| shared_ptr<ExplainRecordingStrategy> newExplainRecordingStrategy | | shared_ptr<ExplainRecordingStrategy> newExplainRecordingStrategy | |
| ( const QueryPlanSummary &queryPlan, const BSONObj &oldPlan ) const
; | | ( const QueryPlanSummary &queryPlan, const BSONObj &oldPlan ) const
; | |
| shared_ptr<ResponseBuildStrategy> newResponseBuildStrategy | | shared_ptr<ResponseBuildStrategy> newResponseBuildStrategy | |
| ( const QueryPlanSummary &queryPlan ); | | ( const QueryPlanSummary &queryPlan ); | |
|
| bool currentMatches(); | | bool currentMatches( MatchDetails& details ); | |
| bool chunkMatches(); | | bool chunkMatches(); | |
| const ParsedQuery &_parsedQuery; | | const ParsedQuery &_parsedQuery; | |
| shared_ptr<Cursor> _cursor; | | shared_ptr<Cursor> _cursor; | |
| shared_ptr<QueryOptimizerCursor> _queryOptimizerCursor; | | shared_ptr<QueryOptimizerCursor> _queryOptimizerCursor; | |
| BufBuilder _buf; | | BufBuilder _buf; | |
| ShardChunkManagerPtr _chunkManager; | | ShardChunkManagerPtr _chunkManager; | |
| shared_ptr<ExplainRecordingStrategy> _explain; | | shared_ptr<ExplainRecordingStrategy> _explain; | |
| shared_ptr<ResponseBuildStrategy> _builder; | | shared_ptr<ResponseBuildStrategy> _builder; | |
| }; | | }; | |
| | | | |
| | | | |
End of changes. 14 change blocks. |
| 19 lines changed or deleted | | 42 lines changed or added | |
|
| queryoptimizer.h | | queryoptimizer.h | |
| | | | |
| skipping to change at line 34 | | skipping to change at line 34 | |
| #include "explain.h" | | #include "explain.h" | |
| #include "../util/net/listen.h" | | #include "../util/net/listen.h" | |
| #include "mongo/db/querypattern.h" | | #include "mongo/db/querypattern.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| class IndexDetails; | | class IndexDetails; | |
| class IndexType; | | class IndexType; | |
| class QueryPlanSummary; | | class QueryPlanSummary; | |
| | | | |
|
| /** A plan for executing a query using the given index spec and FieldRa | | /** | |
| ngeSet. */ | | * A plan for executing a query using the given index spec and FieldRan | |
| | | geSet. An object of this | |
| | | * class may only be used by one thread at a time. | |
| | | */ | |
| class QueryPlan : boost::noncopyable { | | class QueryPlan : boost::noncopyable { | |
| public: | | public: | |
| | | | |
| /** | | /** | |
| * @param originalFrsp - original constraints for this query clause
. If null, frsp will be used instead. | | * @param originalFrsp - original constraints for this query clause
. If null, frsp will be used instead. | |
| */ | | */ | |
|
| QueryPlan(NamespaceDetails *d, | | static QueryPlan *make( NamespaceDetails *d, | |
| int idxNo, // -1 = no index | | int idxNo, // -1 = no index | |
| const FieldRangeSetPair &frsp, | | const FieldRangeSetPair &frsp, | |
| const FieldRangeSetPair *originalFrsp, | | const FieldRangeSetPair *originalFrsp, | |
| const BSONObj &originalQuery, | | const BSONObj &originalQuery, | |
| const BSONObj &order, | | const BSONObj &order, | |
| const shared_ptr<const ParsedQuery> &parsedQuery = | | const shared_ptr<const ParsedQuery> &parsedQ | |
| shared_ptr<const ParsedQuery>(), | | uery = | |
| const BSONObj &startKey = BSONObj(), | | shared_ptr<const ParsedQuery>(), | |
| const BSONObj &endKey = BSONObj(), | | const BSONObj &startKey = BSONObj(), | |
| string special="" ); | | const BSONObj &endKey = BSONObj(), | |
| | | string special="" ); | |
| | | | |
|
| /** @return true iff this plan cannot return any documents. */ | | /** Categorical classification of a QueryPlan's utility. */ | |
| bool impossible() const { return _impossible; } | | enum Utility { | |
| /** | | Impossible, // Cannot produce any matches, so the query must ha | |
| * @return true iff this plan should run as the only candidate plan | | ve an empty result set. | |
| in the absence of an | | // No other plans need to be considered. | |
| * impossible plan. | | Optimal, // Should run as the only candidate plan in the abs | |
| */ | | ence of an Impossible | |
| bool optimal() const { return _optimal; } | | // plan. | |
| /** @return true iff this plan should not be considered at all. */ | | Helpful, // Should be considered. | |
| bool unhelpful() const { return _unhelpful; } | | Unhelpful, // Should not be considered. | |
| /** @return true iff ScanAndOrder processing will be required for r | | Disallowed // Must not be considered unless explicitly hinted. | |
| esult set. */ | | May produce a | |
| | | // semantically incorrect result set. | |
| | | }; | |
| | | | |
| | | Utility utility() const { return _utility; } | |
| | | | |
| | | /** @return true if ScanAndOrder processing will be required for re | |
| | | sult set. */ | |
| bool scanAndOrderRequired() const { return _scanAndOrderRequired; } | | bool scanAndOrderRequired() const { return _scanAndOrderRequired; } | |
| /** | | /** | |
|
| * @return true iff the index we are using has keys such that it ca
n completely resolve the | | * @return true if the index we are using has keys such that it can
completely resolve the | |
| * query expression to match by itself without ever checking the ma
in object. | | * query expression to match by itself without ever checking the ma
in object. | |
| */ | | */ | |
| bool exactKeyMatch() const { return _exactKeyMatch; } | | bool exactKeyMatch() const { return _exactKeyMatch; } | |
|
| /** @return true iff this QueryPlan would perform an unindexed scan | | /** @return true if this QueryPlan would perform an unindexed scan. | |
| . */ | | */ | |
| bool willScanTable() const { return _idxNo < 0 && !_impossible; } | | bool willScanTable() const { return _idxNo < 0 && ( _utility != Imp | |
| | | ossible ); } | |
| /** @return 'special' attribute of the plan, which was either set e
xplicitly or generated from the index. */ | | /** @return 'special' attribute of the plan, which was either set e
xplicitly or generated from the index. */ | |
| const string &special() const { return _special; } | | const string &special() const { return _special; } | |
| | | | |
| /** @return a new cursor based on this QueryPlan's index and FieldR
angeSet. */ | | /** @return a new cursor based on this QueryPlan's index and FieldR
angeSet. */ | |
| shared_ptr<Cursor> newCursor( const DiskLoc &startLoc = DiskLoc() )
const; | | shared_ptr<Cursor> newCursor( const DiskLoc &startLoc = DiskLoc() )
const; | |
| /** @return a new reverse cursor if this is an unindexed plan. */ | | /** @return a new reverse cursor if this is an unindexed plan. */ | |
| shared_ptr<Cursor> newReverseCursor() const; | | shared_ptr<Cursor> newReverseCursor() const; | |
| /** Register this plan as a winner for its QueryPattern, with speci
fied 'nscanned'. */ | | /** Register this plan as a winner for its QueryPattern, with speci
fied 'nscanned'. */ | |
| void registerSelf( long long nScanned, CandidatePlanCharacter candi
datePlans ) const; | | void registerSelf( long long nScanned, CandidatePlanCharacter candi
datePlans ) const; | |
| | | | |
| | | | |
| skipping to change at line 95 | | skipping to change at line 103 | |
| int idxNo() const { return _idxNo; } | | int idxNo() const { return _idxNo; } | |
| const char *ns() const { return _frs.ns(); } | | const char *ns() const { return _frs.ns(); } | |
| NamespaceDetails *nsd() const { return _d; } | | NamespaceDetails *nsd() const { return _d; } | |
| BSONObj originalQuery() const { return _originalQuery; } | | BSONObj originalQuery() const { return _originalQuery; } | |
| shared_ptr<FieldRangeVector> originalFrv() const { return _original
Frv; } | | shared_ptr<FieldRangeVector> originalFrv() const { return _original
Frv; } | |
| | | | |
| const FieldRangeSet &multikeyFrs() const { return _frsMulti; } | | const FieldRangeSet &multikeyFrs() const { return _frsMulti; } | |
| | | | |
| shared_ptr<Projection::KeyOnly> keyFieldsOnly() const { return _key
FieldsOnly; } | | shared_ptr<Projection::KeyOnly> keyFieldsOnly() const { return _key
FieldsOnly; } | |
| | | | |
|
| | | /** @return a shared, lazily initialized matcher for the query plan | |
| | | . */ | |
| | | shared_ptr<CoveredIndexMatcher> matcher() const; | |
| | | | |
| QueryPlanSummary summary() const; | | QueryPlanSummary summary() const; | |
| | | | |
| /** The following member functions are for testing, or public for t
esting. */ | | /** The following member functions are for testing, or public for t
esting. */ | |
| | | | |
| shared_ptr<FieldRangeVector> frv() const { return _frv; } | | shared_ptr<FieldRangeVector> frv() const { return _frv; } | |
| bool isMultiKey() const; | | bool isMultiKey() const; | |
| string toString() const; | | string toString() const; | |
|
| bool queryFiniteSetOrderSuffix() const; | | bool queryBoundsExactOrderSuffix() const; | |
| | | | |
| private: | | private: | |
|
| | | | |
| | | QueryPlan(NamespaceDetails *d, | |
| | | int idxNo, | |
| | | const FieldRangeSetPair &frsp, | |
| | | const BSONObj &originalQuery, | |
| | | const BSONObj &order, | |
| | | const shared_ptr<const ParsedQuery> &parsedQuery, | |
| | | string special ); | |
| | | void init( const FieldRangeSetPair *originalFrsp, | |
| | | const BSONObj &startKey, | |
| | | const BSONObj &endKey ); | |
| | | | |
| void checkTableScanAllowed() const; | | void checkTableScanAllowed() const; | |
| int independentRangesSingleIntervalLimit() const; | | int independentRangesSingleIntervalLimit() const; | |
|
| | | /** @return true when the plan's query may contains an $exists:fals | |
| | | e predicate. */ | |
| | | bool hasPossibleExistsFalsePredicate() const; | |
| | | | |
| NamespaceDetails * _d; | | NamespaceDetails * _d; | |
| int _idxNo; | | int _idxNo; | |
| const FieldRangeSet &_frs; | | const FieldRangeSet &_frs; | |
| const FieldRangeSet &_frsMulti; | | const FieldRangeSet &_frsMulti; | |
| const BSONObj _originalQuery; | | const BSONObj _originalQuery; | |
| const BSONObj _order; | | const BSONObj _order; | |
| shared_ptr<const ParsedQuery> _parsedQuery; | | shared_ptr<const ParsedQuery> _parsedQuery; | |
| const IndexDetails * _index; | | const IndexDetails * _index; | |
|
| bool _optimal; | | | |
| bool _scanAndOrderRequired; | | bool _scanAndOrderRequired; | |
| bool _exactKeyMatch; | | bool _exactKeyMatch; | |
| int _direction; | | int _direction; | |
| shared_ptr<FieldRangeVector> _frv; | | shared_ptr<FieldRangeVector> _frv; | |
| shared_ptr<FieldRangeVector> _originalFrv; | | shared_ptr<FieldRangeVector> _originalFrv; | |
| BSONObj _startKey; | | BSONObj _startKey; | |
| BSONObj _endKey; | | BSONObj _endKey; | |
| bool _endKeyInclusive; | | bool _endKeyInclusive; | |
|
| bool _unhelpful; | | Utility _utility; | |
| bool _impossible; | | | |
| string _special; | | string _special; | |
| IndexType * _type; | | IndexType * _type; | |
| bool _startOrEndSpec; | | bool _startOrEndSpec; | |
| shared_ptr<Projection::KeyOnly> _keyFieldsOnly; | | shared_ptr<Projection::KeyOnly> _keyFieldsOnly; | |
|
| | | mutable shared_ptr<CoveredIndexMatcher> _matcher; // Lazy initializ
ation. | |
| }; | | }; | |
| | | | |
|
| | | std::ostream &operator<< ( std::ostream &out, const QueryPlan::Utility | |
| | | &utility ); | |
| | | | |
| /** | | /** | |
| * A QueryPlanSummary owns its own attributes and may be shared. Curre
ntly a QueryPlan | | * A QueryPlanSummary owns its own attributes and may be shared. Curre
ntly a QueryPlan | |
| * should only be owned by a QueryPlanSet. | | * should only be owned by a QueryPlanSet. | |
| */ | | */ | |
| class QueryPlanSummary { | | class QueryPlanSummary { | |
| public: | | public: | |
| QueryPlanSummary() : | | QueryPlanSummary() : | |
| _scanAndOrderRequired() { | | _scanAndOrderRequired() { | |
| } | | } | |
| QueryPlanSummary( const QueryPlan &queryPlan ) : | | QueryPlanSummary( const QueryPlan &queryPlan ) : | |
| | | | |
| skipping to change at line 154 | | skipping to change at line 180 | |
| _keyFieldsOnly( queryPlan.keyFieldsOnly() ), | | _keyFieldsOnly( queryPlan.keyFieldsOnly() ), | |
| _scanAndOrderRequired( queryPlan.scanAndOrderRequired() ) { | | _scanAndOrderRequired( queryPlan.scanAndOrderRequired() ) { | |
| } | | } | |
| bool valid() const { return _fieldRangeSetMulti; } | | bool valid() const { return _fieldRangeSetMulti; } | |
| shared_ptr<FieldRangeSet> _fieldRangeSetMulti; | | shared_ptr<FieldRangeSet> _fieldRangeSetMulti; | |
| shared_ptr<Projection::KeyOnly> _keyFieldsOnly; | | shared_ptr<Projection::KeyOnly> _keyFieldsOnly; | |
| bool _scanAndOrderRequired; | | bool _scanAndOrderRequired; | |
| }; | | }; | |
| | | | |
| /** | | /** | |
|
| | | * NOTE This interface is deprecated and will be replaced by a special | |
| | | purpose delegation class | |
| | | * for the query optimizer cursor (QueryOptimizerCursorOp). | |
| | | * | |
| * Inherit from this interface to implement a new query operation. | | * Inherit from this interface to implement a new query operation. | |
| * The query optimizer will clone the QueryOp that is provided, giving | | * The query optimizer will clone the QueryOp that is provided, giving | |
| * each clone its own query plan. | | * each clone its own query plan. | |
| * | | * | |
| * Normal sequence of events: | | * Normal sequence of events: | |
| * 1) A new QueryOp is generated using createChild(). | | * 1) A new QueryOp is generated using createChild(). | |
| * 2) A QueryPlan is assigned to this QueryOp with setQueryPlan(). | | * 2) A QueryPlan is assigned to this QueryOp with setQueryPlan(). | |
| * 3) _init() is called on the QueryPlan. | | * 3) _init() is called on the QueryPlan. | |
| * 4) next() is called repeatedly, with nscanned() checked after each c
all. | | * 4) next() is called repeatedly, with nscanned() checked after each c
all. | |
| * 5) In one of these calls to next(), setComplete() is called. | | * 5) In one of these calls to next(), setComplete() is called. | |
| * 6) The QueryPattern for the QueryPlan may be recorded as a winner. | | * 6) The QueryPattern for the QueryPlan may be recorded as a winner. | |
| */ | | */ | |
|
| class QueryOp { | | class QueryOp : private boost::noncopyable { | |
| public: | | public: | |
|
| QueryOp() : _complete(), _stopRequested(), _qp(), _error() {} | | QueryOp() : _complete(), _stopRequested(), _queryPlan(), _error() { | |
| | | } | |
| /** Used when handing off from one QueryOp to another. */ | | | |
| QueryOp( const QueryOp &other ) : | | | |
| _complete(), _stopRequested(), _qp(), _error(), _matcher( other | | | |
| ._matcher ), | | | |
| _orConstraint( other._orConstraint ) {} | | | |
| | | | |
| virtual ~QueryOp() {} | | virtual ~QueryOp() {} | |
| | | | |
| /** @return QueryPlan assigned to this QueryOp by the query optimiz
er. */ | | /** @return QueryPlan assigned to this QueryOp by the query optimiz
er. */ | |
|
| const QueryPlan &qp() const { return *_qp; } | | const QueryPlan &queryPlan() const { return *_queryPlan; } | |
| | | | |
| /** Advance to next potential matching document (eg using a cursor)
. */ | | /** Advance to next potential matching document (eg using a cursor)
. */ | |
| virtual void next() = 0; | | virtual void next() = 0; | |
| /** | | /** | |
| * @return current 'nscanned' metric for this QueryOp. Used to com
pare | | * @return current 'nscanned' metric for this QueryOp. Used to com
pare | |
| * cost to other QueryOps. | | * cost to other QueryOps. | |
| */ | | */ | |
| virtual long long nscanned() = 0; | | virtual long long nscanned() = 0; | |
| /** Take any steps necessary before the db mutex is yielded. */ | | /** Take any steps necessary before the db mutex is yielded. */ | |
| virtual void prepareToYield() = 0; | | virtual void prepareToYield() = 0; | |
| | | | |
| skipping to change at line 210 | | skipping to change at line 234 | |
| /** @return true iff the implementation called steStop(). */ | | /** @return true iff the implementation called steStop(). */ | |
| bool stopRequested() const { return _stopRequested; } | | bool stopRequested() const { return _stopRequested; } | |
| bool completeWithoutStop() const { return complete() && !stopReques
ted(); } | | bool completeWithoutStop() const { return complete() && !stopReques
ted(); } | |
| /** @return true iff the implementation threw an exception. */ | | /** @return true iff the implementation threw an exception. */ | |
| bool error() const { return _error; } | | bool error() const { return _error; } | |
| /** @return the exception thrown by implementation if one was throw
n. */ | | /** @return the exception thrown by implementation if one was throw
n. */ | |
| ExceptionInfo exception() const { return _exception; } | | ExceptionInfo exception() const { return _exception; } | |
| | | | |
| /** To be called by QueryPlanSet::Runner only. */ | | /** To be called by QueryPlanSet::Runner only. */ | |
| | | | |
|
| QueryOp *createChild(); | | /** | |
| void setQueryPlan( const QueryPlan *qp ) { _qp = qp; verify( _qp != | | * @return a copy of the inheriting class, which will be run with i | |
| NULL ); } | | ts own query plan. The | |
| void init(); | | * child QueryOp will assume its parent QueryOp has completed execu | |
| | | tion. | |
| | | */ | |
| | | virtual QueryOp *createChild() const = 0; | |
| | | void setQueryPlan( const QueryPlan *queryPlan ) { | |
| | | _queryPlan = queryPlan; | |
| | | verify( _queryPlan != NULL ); | |
| | | } | |
| | | /** Handle initialization after a QueryPlan has been set. */ | |
| | | virtual void init() = 0; | |
| void setException( const DBException &e ) { | | void setException( const DBException &e ) { | |
| _error = true; | | _error = true; | |
| _exception = e.getInfo(); | | _exception = e.getInfo(); | |
| } | | } | |
| | | | |
|
| shared_ptr<CoveredIndexMatcher> matcher( const shared_ptr<Cursor>& | | | |
| c ) const { | | | |
| return matcher( c.get() ); | | | |
| } | | | |
| shared_ptr<CoveredIndexMatcher> matcher( Cursor* c ) const { | | | |
| if( ! c ) return _matcher; | | | |
| return c->matcher() ? c->matcherPtr() : _matcher; | | | |
| } | | | |
| | | | |
| /** @return an ExplainPlanInfo object that will be updated as the q
uery runs. */ | | /** @return an ExplainPlanInfo object that will be updated as the q
uery runs. */ | |
| virtual shared_ptr<ExplainPlanInfo> generateExplainInfo() { | | virtual shared_ptr<ExplainPlanInfo> generateExplainInfo() { | |
| return shared_ptr<ExplainPlanInfo>( new ExplainPlanInfo() ); | | return shared_ptr<ExplainPlanInfo>( new ExplainPlanInfo() ); | |
| } | | } | |
| | | | |
| protected: | | protected: | |
| /** Call if all results have been found. */ | | /** Call if all results have been found. */ | |
|
| void setComplete() { | | void setComplete() { _complete = true; } | |
| _orConstraint = qp().originalFrv(); | | | |
| _complete = true; | | | |
| } | | | |
| /** Call if the scan is complete even if not all results have been
found. */ | | /** Call if the scan is complete even if not all results have been
found. */ | |
| void setStop() { setComplete(); _stopRequested = true; } | | void setStop() { setComplete(); _stopRequested = true; } | |
| | | | |
|
| /** Handle initialization after a QueryPlan has been set. */ | | | |
| virtual void _init() = 0; | | | |
| | | | |
| /** @return a copy of the inheriting class, which will be run with | | | |
| its own query plan. */ | | | |
| virtual QueryOp *_createChild() const = 0; | | | |
| | | | |
| private: | | private: | |
| bool _complete; | | bool _complete; | |
| bool _stopRequested; | | bool _stopRequested; | |
| ExceptionInfo _exception; | | ExceptionInfo _exception; | |
|
| const QueryPlan *_qp; | | const QueryPlan *_queryPlan; | |
| bool _error; | | bool _error; | |
|
| shared_ptr<CoveredIndexMatcher> _matcher; | | | |
| shared_ptr<CoveredIndexMatcher> _oldMatcher; | | | |
| shared_ptr<FieldRangeVector> _orConstraint; | | | |
| }; | | }; | |
| | | | |
| // temp. this class works if T::operator< is variant unlike a regular
stl priority queue. | | // temp. this class works if T::operator< is variant unlike a regular
stl priority queue. | |
| // but it's very slow. however if v.size() is always very small, it wo
uld be fine, | | // but it's very slow. however if v.size() is always very small, it wo
uld be fine, | |
| // maybe even faster than a smart impl that does more memory allocation
s. | | // maybe even faster than a smart impl that does more memory allocation
s. | |
| template<class T> | | template<class T> | |
| class our_priority_queue : boost::noncopyable { | | class our_priority_queue : boost::noncopyable { | |
| vector<T> v; | | vector<T> v; | |
| public: | | public: | |
| our_priority_queue() { | | our_priority_queue() { | |
| | | | |
| skipping to change at line 304 | | skipping to change at line 316 | |
| Use // Always use the recorded plan. | | Use // Always use the recorded plan. | |
| } RecordedPlanPolicy; | | } RecordedPlanPolicy; | |
| | | | |
| /** @param qps The QueryPlanSet to which plans will be provided. */ | | /** @param qps The QueryPlanSet to which plans will be provided. */ | |
| QueryPlanGenerator( QueryPlanSet &qps, | | QueryPlanGenerator( QueryPlanSet &qps, | |
| auto_ptr<FieldRangeSetPair> originalFrsp, | | auto_ptr<FieldRangeSetPair> originalFrsp, | |
| const shared_ptr<const ParsedQuery> &parsedQuery
, | | const shared_ptr<const ParsedQuery> &parsedQuery
, | |
| const BSONObj &hint, | | const BSONObj &hint, | |
| RecordedPlanPolicy recordedPlanPolicy, | | RecordedPlanPolicy recordedPlanPolicy, | |
| const BSONObj &min, | | const BSONObj &min, | |
|
| const BSONObj &max ); | | const BSONObj &max, | |
| | | bool allowSpecial ); | |
| /** Populate the provided QueryPlanSet with an initial set of plans
. */ | | /** Populate the provided QueryPlanSet with an initial set of plans
. */ | |
| void addInitialPlans(); | | void addInitialPlans(); | |
| /** Supplement a cached plan provided earlier by adding additional
query plans. */ | | /** Supplement a cached plan provided earlier by adding additional
query plans. */ | |
| void addFallbackPlans(); | | void addFallbackPlans(); | |
| | | | |
| private: | | private: | |
| | | | |
| bool addShortCircuitPlan( NamespaceDetails *d ); | | bool addShortCircuitPlan( NamespaceDetails *d ); | |
| bool addHintPlan( NamespaceDetails *d ); | | bool addHintPlan( NamespaceDetails *d ); | |
| bool addSpecialPlan( NamespaceDetails *d ); | | bool addSpecialPlan( NamespaceDetails *d ); | |
| void addStandardPlans( NamespaceDetails *d ); | | void addStandardPlans( NamespaceDetails *d ); | |
| bool addCachedPlan( NamespaceDetails *d ); | | bool addCachedPlan( NamespaceDetails *d ); | |
| shared_ptr<QueryPlan> newPlan( NamespaceDetails *d, | | shared_ptr<QueryPlan> newPlan( NamespaceDetails *d, | |
| int idxNo, | | int idxNo, | |
| const BSONObj &min = BSONObj(), | | const BSONObj &min = BSONObj(), | |
| const BSONObj &max = BSONObj(), | | const BSONObj &max = BSONObj(), | |
| const string &special = "" ) const; | | const string &special = "" ) const; | |
| bool setUnindexedPlanIf( bool set, NamespaceDetails *d ); | | bool setUnindexedPlanIf( bool set, NamespaceDetails *d ); | |
| void setSingleUnindexedPlan( NamespaceDetails *d ); | | void setSingleUnindexedPlan( NamespaceDetails *d ); | |
|
| void setHintedPlan( IndexDetails &id ); | | void setHintedPlanForIndex( IndexDetails& id ); | |
| | | void validateAndSetHintedPlan( const shared_ptr<QueryPlan>& plan ); | |
| void warnOnCappedIdTableScan() const; | | void warnOnCappedIdTableScan() const; | |
| QueryPlanSet &_qps; | | QueryPlanSet &_qps; | |
| auto_ptr<FieldRangeSetPair> _originalFrsp; | | auto_ptr<FieldRangeSetPair> _originalFrsp; | |
| shared_ptr<const ParsedQuery> _parsedQuery; | | shared_ptr<const ParsedQuery> _parsedQuery; | |
| BSONObj _hint; | | BSONObj _hint; | |
| RecordedPlanPolicy _recordedPlanPolicy; | | RecordedPlanPolicy _recordedPlanPolicy; | |
| BSONObj _min; | | BSONObj _min; | |
| BSONObj _max; | | BSONObj _max; | |
|
| | | bool _allowSpecial; | |
| }; | | }; | |
| | | | |
| /** | | /** | |
| * A set of candidate query plans for a query. This class can return a
best guess plan or run a | | * A set of candidate query plans for a query. This class can return a
best guess plan or run a | |
| * QueryOp on all the plans. | | * QueryOp on all the plans. | |
| */ | | */ | |
| class QueryPlanSet { | | class QueryPlanSet { | |
| public: | | public: | |
| typedef boost::shared_ptr<QueryPlan> QueryPlanPtr; | | typedef boost::shared_ptr<QueryPlan> QueryPlanPtr; | |
| typedef vector<QueryPlanPtr> PlanSet; | | typedef vector<QueryPlanPtr> PlanSet; | |
| | | | |
| /** | | /** | |
| * @param originalFrsp - original constraints for this query clause
; if null, frsp will be | | * @param originalFrsp - original constraints for this query clause
; if null, frsp will be | |
| * used. | | * used. | |
| */ | | */ | |
|
| QueryPlanSet( const char *ns, | | static QueryPlanSet* make( const char* ns, | |
| auto_ptr<FieldRangeSetPair> frsp, | | auto_ptr<FieldRangeSetPair> frsp, | |
| auto_ptr<FieldRangeSetPair> originalFrsp, | | auto_ptr<FieldRangeSetPair> originalFrsp | |
| const BSONObj &originalQuery, | | , | |
| const BSONObj &order, | | const BSONObj& originalQuery, | |
| const shared_ptr<const ParsedQuery> &parsedQuery = | | const BSONObj& order, | |
| shared_ptr<const ParsedQuery>(), | | const shared_ptr<const ParsedQuery>& par | |
| const BSONObj &hint = BSONObj(), | | sedQuery, | |
| QueryPlanGenerator::RecordedPlanPolicy recordedPlanPol | | const BSONObj& hint, | |
| icy = | | QueryPlanGenerator::RecordedPlanPolicy r | |
| QueryPlanGenerator::Use, | | ecordedPlanPolicy, | |
| const BSONObj &min = BSONObj(), | | const BSONObj& min, | |
| const BSONObj &max = BSONObj() ); | | const BSONObj& max, | |
| | | bool allowSpecial ); | |
| | | | |
| /** @return number of candidate plans. */ | | /** @return number of candidate plans. */ | |
| int nPlans() const { return _plans.size(); } | | int nPlans() const { return _plans.size(); } | |
| | | | |
| QueryPlanPtr firstPlan() const { return _plans[ 0 ]; } | | QueryPlanPtr firstPlan() const { return _plans[ 0 ]; } | |
| | | | |
| /** @return true if a plan is selected based on previous success of
this plan. */ | | /** @return true if a plan is selected based on previous success of
this plan. */ | |
| bool usingCachedPlan() const { return _usingCachedPlan; } | | bool usingCachedPlan() const { return _usingCachedPlan; } | |
| /** @return true if some candidate plans may have been excluded due
to plan caching. */ | | /** @return true if some candidate plans may have been excluded due
to plan caching. */ | |
| bool hasPossiblyExcludedPlans() const; | | bool hasPossiblyExcludedPlans() const; | |
| | | | |
| skipping to change at line 451 | | skipping to change at line 465 | |
| bool operator<( const OpHolder &other ) const { | | bool operator<( const OpHolder &other ) const { | |
| return _op->nscanned() + _offset > other._op->nscanned(
) + other._offset; | | return _op->nscanned() + _offset > other._op->nscanned(
) + other._offset; | |
| } | | } | |
| }; | | }; | |
| our_priority_queue<OpHolder> _queue; | | our_priority_queue<OpHolder> _queue; | |
| shared_ptr<ExplainClauseInfo> _explainClauseInfo; | | shared_ptr<ExplainClauseInfo> _explainClauseInfo; | |
| bool _done; | | bool _done; | |
| }; | | }; | |
| | | | |
| private: | | private: | |
|
| void addFallbackPlans(); | | | |
| | | QueryPlanSet( const char *ns, | |
| | | auto_ptr<FieldRangeSetPair> frsp, | |
| | | auto_ptr<FieldRangeSetPair> originalFrsp, | |
| | | const BSONObj &originalQuery, | |
| | | const BSONObj &order, | |
| | | const shared_ptr<const ParsedQuery> &parsedQuery, | |
| | | const BSONObj &hint, | |
| | | QueryPlanGenerator::RecordedPlanPolicy recordedPlanPol | |
| | | icy, | |
| | | const BSONObj &min, | |
| | | const BSONObj &max, | |
| | | bool allowSpecial ); | |
| void init(); | | void init(); | |
| | | | |
|
| | | void addFallbackPlans(); | |
| | | void pushPlan( const QueryPlanPtr& plan ); | |
| | | | |
| QueryPlanGenerator _generator; | | QueryPlanGenerator _generator; | |
| BSONObj _originalQuery; | | BSONObj _originalQuery; | |
| auto_ptr<FieldRangeSetPair> _frsp; | | auto_ptr<FieldRangeSetPair> _frsp; | |
| PlanSet _plans; | | PlanSet _plans; | |
| bool _mayRecordPlan; | | bool _mayRecordPlan; | |
| bool _usingCachedPlan; | | bool _usingCachedPlan; | |
| CandidatePlanCharacter _cachedPlanCharacter; | | CandidatePlanCharacter _cachedPlanCharacter; | |
| BSONObj _order; | | BSONObj _order; | |
| long long _oldNScanned; | | long long _oldNScanned; | |
| ElapsedTracker _yieldSometimesTracker; | | ElapsedTracker _yieldSometimesTracker; | |
|
| | | bool _allowSpecial; | |
| }; | | }; | |
| | | | |
| /** Handles $or type queries by generating a QueryPlanSet for each $or
clause. */ | | /** Handles $or type queries by generating a QueryPlanSet for each $or
clause. */ | |
| class MultiPlanScanner { | | class MultiPlanScanner { | |
| public: | | public: | |
|
| MultiPlanScanner( const char *ns, | | | |
| const BSONObj &query, | | static MultiPlanScanner *make( const char *ns, | |
| const BSONObj &order, | | const BSONObj &query, | |
| const shared_ptr<const ParsedQuery> &parsedQuery | | const BSONObj &order, | |
| = | | const shared_ptr<const ParsedQuery> & | |
| shared_ptr<const ParsedQuery>(), | | parsedQuery = | |
| const BSONObj &hint = BSONObj(), | | shared_ptr<const ParsedQuery>( | |
| QueryPlanGenerator::RecordedPlanPolicy recordedPl | | ), | |
| anPolicy = | | const BSONObj &hint = BSONObj(), | |
| QueryPlanGenerator::Use, | | QueryPlanGenerator::RecordedPlanPolic | |
| const BSONObj &min = BSONObj(), | | y recordedPlanPolicy = | |
| const BSONObj &max = BSONObj() ); | | QueryPlanGenerator::Use, | |
| | | const BSONObj &min = BSONObj(), | |
| | | const BSONObj &max = BSONObj() ); | |
| | | | |
| /** Set the initial QueryOp for QueryPlanSet iteration. */ | | /** Set the initial QueryOp for QueryPlanSet iteration. */ | |
| void initialOp( const shared_ptr<QueryOp> &originalOp ) { _baseOp =
originalOp; } | | void initialOp( const shared_ptr<QueryOp> &originalOp ) { _baseOp =
originalOp; } | |
| /** | | /** | |
| * Advance to the next QueryOp, if not doneOps(). | | * Advance to the next QueryOp, if not doneOps(). | |
| * @return the next non error op if there is one, otherwise an erro
r op. | | * @return the next non error op if there is one, otherwise an erro
r op. | |
| * If the returned op is complete() or error(), the MultiPlanScanne
r becomes doneOps() and | | * If the returned op is complete() or error(), the MultiPlanScanne
r becomes doneOps() and | |
| * no further QueryOp iteration is possible. | | * no further QueryOp iteration is possible. | |
| */ | | */ | |
| shared_ptr<QueryOp> nextOp(); | | shared_ptr<QueryOp> nextOp(); | |
| | | | |
| skipping to change at line 565 | | skipping to change at line 595 | |
| /** @return true if an active or fallback plan of _currentQps is in
order. */ | | /** @return true if an active or fallback plan of _currentQps is in
order. */ | |
| bool possibleInOrderPlan() const; | | bool possibleInOrderPlan() const; | |
| /** @return true if an active or fallback plan of _currentQps is ou
t of order. */ | | /** @return true if an active or fallback plan of _currentQps is ou
t of order. */ | |
| bool possibleOutOfOrderPlan() const; | | bool possibleOutOfOrderPlan() const; | |
| | | | |
| int i() const { return _i; } | | int i() const { return _i; } | |
| | | | |
| string toString() const; | | string toString() const; | |
| | | | |
| private: | | private: | |
|
| | | | |
| | | MultiPlanScanner( const char *ns, | |
| | | const BSONObj &query, | |
| | | const shared_ptr<const ParsedQuery> &parsedQuery, | |
| | | const BSONObj &hint, | |
| | | QueryPlanGenerator::RecordedPlanPolicy recordedPla | |
| | | nPolicy ); | |
| | | void init( const BSONObj &order, | |
| | | const BSONObj &min, | |
| | | const BSONObj &max ); | |
| | | | |
| /** Initialize or iterate a runner generated from @param originalOp
. */ | | /** Initialize or iterate a runner generated from @param originalOp
. */ | |
| shared_ptr<QueryOp> iterateRunner( QueryOp &originalOp, bool retrie
d = false ); | | shared_ptr<QueryOp> iterateRunner( QueryOp &originalOp, bool retrie
d = false ); | |
| | | | |
| shared_ptr<QueryOp> nextOpSimple(); | | shared_ptr<QueryOp> nextOpSimple(); | |
| shared_ptr<QueryOp> nextOpOr(); | | shared_ptr<QueryOp> nextOpOr(); | |
| | | | |
| void updateCurrentQps( QueryPlanSet *qps ); | | void updateCurrentQps( QueryPlanSet *qps ); | |
| | | | |
| void assertNotOr() const { | | void assertNotOr() const { | |
| massert( 13266, "not implemented for $or query", !_or ); | | massert( 13266, "not implemented for $or query", !_or ); | |
| | | | |
| skipping to change at line 607 | | skipping to change at line 647 | |
| scoped_ptr<QueryPlanSet::Runner> _runner; | | scoped_ptr<QueryPlanSet::Runner> _runner; | |
| shared_ptr<ExplainQueryInfo> _explainQueryInfo; | | shared_ptr<ExplainQueryInfo> _explainQueryInfo; | |
| bool _doneOps; | | bool _doneOps; | |
| }; | | }; | |
| | | | |
| /** | | /** | |
| * Provides a cursor interface for serial single Cursor iteration using
a MultiPlanScanner. | | * Provides a cursor interface for serial single Cursor iteration using
a MultiPlanScanner. | |
| * Currently used internally by a QueryOptimizerCursor. | | * Currently used internally by a QueryOptimizerCursor. | |
| * | | * | |
| * A MultiCursor is backed by one BasicCursor or BtreeCursor at a time
and forwards calls for | | * A MultiCursor is backed by one BasicCursor or BtreeCursor at a time
and forwards calls for | |
|
| * ensuring a consistent state after a write to its backing Cursor. Th | | * ensuring a consistent state after a write to its backing Cursor. | |
| ere is a known issue in | | | |
| * some cases when advance() causes a switch to a new BasicCursor backi | | | |
| ng (SERVER-5198). | | | |
| */ | | */ | |
| class MultiCursor : public Cursor { | | class MultiCursor : public Cursor { | |
| public: | | public: | |
| /** @param nscanned is the initial nscanned value. */ | | /** @param nscanned is the initial nscanned value. */ | |
| MultiCursor( auto_ptr<MultiPlanScanner> mps, const shared_ptr<Curso
r> &c, | | MultiCursor( auto_ptr<MultiPlanScanner> mps, const shared_ptr<Curso
r> &c, | |
| const shared_ptr<CoveredIndexMatcher> &matcher, | | const shared_ptr<CoveredIndexMatcher> &matcher, | |
| const shared_ptr<ExplainPlanInfo> &explainPlanInfo, | | const shared_ptr<ExplainPlanInfo> &explainPlanInfo, | |
| const QueryOp &op, long long nscanned ); | | const QueryOp &op, long long nscanned ); | |
| | | | |
| virtual bool ok() { return _c->ok(); } | | virtual bool ok() { return _c->ok(); } | |
| | | | |
| skipping to change at line 635 | | skipping to change at line 674 | |
| virtual void noteLocation() { _c->noteLocation(); } | | virtual void noteLocation() { _c->noteLocation(); } | |
| virtual void checkLocation() { _c->checkLocation(); } | | virtual void checkLocation() { _c->checkLocation(); } | |
| virtual void recoverFromYield(); | | virtual void recoverFromYield(); | |
| virtual bool supportGetMore() { return true; } | | virtual bool supportGetMore() { return true; } | |
| virtual bool supportYields() { return true; } | | virtual bool supportYields() { return true; } | |
| virtual BSONObj indexKeyPattern() { return _c->indexKeyPattern(); } | | virtual BSONObj indexKeyPattern() { return _c->indexKeyPattern(); } | |
| | | | |
| /** Deduping documents from a prior cursor is handled by the matche
r. */ | | /** Deduping documents from a prior cursor is handled by the matche
r. */ | |
| virtual bool getsetdup(DiskLoc loc) { return _c->getsetdup( loc );
} | | virtual bool getsetdup(DiskLoc loc) { return _c->getsetdup( loc );
} | |
| | | | |
|
| virtual bool autoDedup() const { return _c->autoDedup(); } | | | |
| | | | |
| virtual bool modifiedKeys() const { return true; } | | virtual bool modifiedKeys() const { return true; } | |
| | | | |
| virtual bool isMultiKey() const { return _mps->hasMultiKey(); } | | virtual bool isMultiKey() const { return _mps->hasMultiKey(); } | |
| | | | |
| virtual shared_ptr< CoveredIndexMatcher > matcherPtr() const { retu
rn _matcher; } | | virtual shared_ptr< CoveredIndexMatcher > matcherPtr() const { retu
rn _matcher; } | |
| virtual CoveredIndexMatcher* matcher() const { return _matcher.get(
); } | | virtual CoveredIndexMatcher* matcher() const { return _matcher.get(
); } | |
| | | | |
| virtual bool capped() const { return _c->capped(); } | | virtual bool capped() const { return _c->capped(); } | |
| | | | |
| virtual long long nscanned() { return _nscanned + _c->nscanned(); } | | virtual long long nscanned() { return _nscanned + _c->nscanned(); } | |
| | | | |
| void noteIterate( bool match, bool loadedRecord ); | | void noteIterate( bool match, bool loadedRecord ); | |
| | | | |
|
| void noteYield(); | | | |
| | | | |
| const QueryPlan &queryPlan() const { | | const QueryPlan &queryPlan() const { | |
| verify( _c->ok() && _queryPlan ); | | verify( _c->ok() && _queryPlan ); | |
| return *_queryPlan; | | return *_queryPlan; | |
| } | | } | |
| | | | |
| const Projection::KeyOnly *keyFieldsOnly() const { | | const Projection::KeyOnly *keyFieldsOnly() const { | |
| verify( _c->ok() && _queryPlan ); | | verify( _c->ok() && _queryPlan ); | |
| return _queryPlan->keyFieldsOnly().get(); | | return _queryPlan->keyFieldsOnly().get(); | |
| } | | } | |
| private: | | private: | |
|
| void nextClause(); | | void advanceClause(); | |
| | | void advanceExhaustedClauses(); | |
| auto_ptr<MultiPlanScanner> _mps; | | auto_ptr<MultiPlanScanner> _mps; | |
| shared_ptr<Cursor> _c; | | shared_ptr<Cursor> _c; | |
| shared_ptr<CoveredIndexMatcher> _matcher; | | shared_ptr<CoveredIndexMatcher> _matcher; | |
| const QueryPlan *_queryPlan; | | const QueryPlan *_queryPlan; | |
| long long _nscanned; | | long long _nscanned; | |
| shared_ptr<ExplainPlanInfo> _explainPlanInfo; | | shared_ptr<ExplainPlanInfo> _explainPlanInfo; | |
| }; | | }; | |
| | | | |
| /** NOTE min, max, and keyPattern will be updated to be consistent with
the selected index. */ | | /** NOTE min, max, and keyPattern will be updated to be consistent with
the selected index. */ | |
| IndexDetails *indexDetailsForRange( const char *ns, string &errmsg, BSO
NObj &min, BSONObj &max, BSONObj &keyPattern ); | | IndexDetails *indexDetailsForRange( const char *ns, string &errmsg, BSO
NObj &min, BSONObj &max, BSONObj &keyPattern ); | |
| | | | |
End of changes. 36 change blocks. |
| 107 lines changed or deleted | | 153 lines changed or added | |
|
| queryutil.h | | queryutil.h | |
| | | | |
| skipping to change at line 151 | | skipping to change at line 151 | |
| if ( ! e.isABSONObj() ) | | if ( ! e.isABSONObj() ) | |
| e = q["$query"]; | | e = q["$query"]; | |
| | | | |
| if ( e.isABSONObj() ) { | | if ( e.isABSONObj() ) { | |
| _filter = e.embeddedObject(); | | _filter = e.embeddedObject(); | |
| _initTop( q ); | | _initTop( q ); | |
| } | | } | |
| else { | | else { | |
| _filter = q; | | _filter = q; | |
| } | | } | |
|
| | | | |
| | | _filter = _filter.getOwned(); | |
| } | | } | |
| | | | |
| void _reset() { | | void _reset() { | |
| _wantMore = true; | | _wantMore = true; | |
| _explain = false; | | _explain = false; | |
| _snapshot = false; | | _snapshot = false; | |
| _returnKey = false; | | _returnKey = false; | |
| _showDiskLoc = false; | | _showDiskLoc = false; | |
| _maxScan = 0; | | _maxScan = 0; | |
| } | | } | |
| | | | |
| skipping to change at line 217 | | skipping to change at line 219 | |
| uassert( 12001 , "E12001 can't sort with $snapshot", _order
.isEmpty() ); | | uassert( 12001 , "E12001 can't sort with $snapshot", _order
.isEmpty() ); | |
| uassert( 12002 , "E12002 can't use hint with $snapshot", _h
int.isEmpty() ); | | uassert( 12002 , "E12002 can't use hint with $snapshot", _h
int.isEmpty() ); | |
| } | | } | |
| | | | |
| } | | } | |
| | | | |
| void initFields( const BSONObj& fields ) { | | void initFields( const BSONObj& fields ) { | |
| if ( fields.isEmpty() ) | | if ( fields.isEmpty() ) | |
| return; | | return; | |
| _fields.reset( new Projection() ); | | _fields.reset( new Projection() ); | |
|
| _fields->init( fields ); | | _fields->init( fields.getOwned() ); | |
| } | | } | |
| | | | |
| const char * const _ns; | | const char * const _ns; | |
| const int _ntoskip; | | const int _ntoskip; | |
| int _ntoreturn; | | int _ntoreturn; | |
| BSONObj _filter; | | BSONObj _filter; | |
| BSONObj _order; | | BSONObj _order; | |
| const int _options; | | const int _options; | |
| shared_ptr< Projection > _fields; | | shared_ptr< Projection > _fields; | |
| bool _wantMore; | | bool _wantMore; | |
| | | | |
| skipping to change at line 261 | | skipping to change at line 263 | |
| | | | |
| /** An interval defined between a lower and an upper FieldBound. */ | | /** An interval defined between a lower and an upper FieldBound. */ | |
| struct FieldInterval { | | struct FieldInterval { | |
| FieldInterval() : _cachedEquality( -1 ) {} | | FieldInterval() : _cachedEquality( -1 ) {} | |
| FieldInterval( const BSONElement& e ) : _cachedEquality( -1 ) { | | FieldInterval( const BSONElement& e ) : _cachedEquality( -1 ) { | |
| _lower._bound = _upper._bound = e; | | _lower._bound = _upper._bound = e; | |
| _lower._inclusive = _upper._inclusive = true; | | _lower._inclusive = _upper._inclusive = true; | |
| } | | } | |
| FieldBound _lower; | | FieldBound _lower; | |
| FieldBound _upper; | | FieldBound _upper; | |
|
| /** @return true iff no single element can be contained in the inte | | /** | |
| rval. */ | | * @return true when the interval can contain one or more values. | |
| bool strictValid() const { | | * NOTE May also return true in certain 'empty' discrete cases like | |
| | | x > false && x < true. | |
| | | */ | |
| | | bool isStrictValid() 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 ) ); | |
| } | | } | |
|
| /** @return true iff the interval is an equality constraint. */ | | /** @return true if the interval is an equality constraint. */ | |
| bool equality() const; | | bool equality() const; | |
| mutable int _cachedEquality; | | mutable int _cachedEquality; | |
| | | | |
| string toString() const; | | string toString() const; | |
| }; | | }; | |
| | | | |
| /** | | /** | |
| * An ordered list of FieldIntervals expressing constraints on valid | | * An ordered list of FieldIntervals expressing constraints on valid | |
| * BSONElement values for a field. | | * BSONElement values for a field. | |
| */ | | */ | |
| class FieldRange { | | class FieldRange { | |
| public: | | public: | |
|
| FieldRange( const BSONElement &e , bool singleKey , bool isNot=fals | | /** | |
| e , bool optimize=true ); | | * Creates a FieldRange representing a superset of the BSONElement | |
| | | values matching a query | |
| | | * expression element. | |
| | | * @param e - The query expression element. | |
| | | * @param isNot - Indicates that 'e' appears within a query $not cl | |
| | | ause and its matching | |
| | | * semantics are inverted. | |
| | | * @param optimize - If true, the range may be bracketed by 'e''s d | |
| | | ata type. | |
| | | * TODO It is unclear why 'optimize' is optional, see SERVER-51 | |
| | | 65. | |
| | | */ | |
| | | FieldRange( const BSONElement &e , bool isNot, bool optimize ); | |
| | | | |
|
| /** @return Range intersection with 'other'. */ | | /** | |
| const FieldRange &operator&=( const FieldRange &other ); | | * @return Range intersection with 'other'. | |
| | | * @param singleKey - Indicate whether intersection will be perform | |
| | | ed in a single value or | |
| | | * multi value context. | |
| | | */ | |
| | | const FieldRange &intersect( const FieldRange &other, bool singleKe | |
| | | y ); | |
| /** @return Range union with 'other'. */ | | /** @return Range union with 'other'. */ | |
| const FieldRange &operator|=( const FieldRange &other ); | | const FieldRange &operator|=( const FieldRange &other ); | |
| /** @return Range of elements elements included in 'this' but not '
other'. */ | | /** @return Range of elements elements included in 'this' but not '
other'. */ | |
| const FieldRange &operator-=( const FieldRange &other ); | | const FieldRange &operator-=( const FieldRange &other ); | |
| /** @return true iff this range is a subset of 'other'. */ | | /** @return true iff this range is a subset of 'other'. */ | |
| bool operator<=( const FieldRange &other ) const; | | bool operator<=( const FieldRange &other ) const; | |
| | | | |
| /** | | /** | |
| * If there are any valid values for this range, the extreme values
can | | * If there are any valid values for this range, the extreme values
can | |
| * be extracted. | | * be extracted. | |
| | | | |
| skipping to change at line 310 | | skipping to change at line 328 | |
| /** @return true iff this range expresses a single equality interva
l. */ | | /** @return true iff this range expresses a single equality interva
l. */ | |
| bool equality() const; | | bool equality() const; | |
| /** | | /** | |
| * @return true iff this range includes all BSONElements | | * @return true iff this range includes all BSONElements | |
| * (the range is the universal set of BSONElements). | | * (the range is the universal set of BSONElements). | |
| */ | | */ | |
| bool universal() const; | | bool universal() const; | |
| /** @return true iff this range includes no BSONElements. */ | | /** @return true iff this range includes no BSONElements. */ | |
| bool empty() const { return _intervals.empty(); } | | bool empty() const { return _intervals.empty(); } | |
| /** | | /** | |
|
| * @return true in many cases when this FieldRange describes a fini | | * @return true in many cases when this FieldRange represents the e | |
| te set of BSONElements, | | xact set of BSONElement | |
| * all of which will be matched by the query BSONElement that gener | | * values matching the query expression element used to construct t | |
| ated this FieldRange. | | he FieldRange. This | |
| * This attribute is used to implement higher level optimizations a | | * attribute is used to implement higher level optimizations and is | |
| nd is computed with a | | computed with a simple | |
| * simple implementation that identifies common (but not all) cases | | * implementation that identifies common (but not all) cases of thi | |
| satisfying the stated | | s property and may return | |
| * properties. | | * false negatives. | |
| */ | | */ | |
|
| bool simpleFiniteSet() const { return _simpleFiniteSet; } | | bool mustBeExactMatchRepresentation() const { return _exactMatchRep | |
| | | resentation; } | |
| | | /* Checks whether this FieldRange is a non-empty union of point-int | |
| | | ervals. | |
| | | * Examples: | |
| | | * FieldRange( { a:3 } ), isPointIntervalSet() -> true | |
| | | * FieldRange( { a:{ $in:[ 1, 2 ] } } ), isPointIntervalSet() -> t | |
| | | rue | |
| | | * FieldRange( { a:{ $gt:5 } } ), isPointIntervalSet() -> false | |
| | | * FieldRange( {} ), isPointIntervalSet() -> false | |
| | | */ | |
| | | bool isPointIntervalSet() const; | |
| | | | |
| /** Empty the range so it includes no BSONElements. */ | | /** Empty the range so it includes no BSONElements. */ | |
| void makeEmpty() { _intervals.clear(); } | | void makeEmpty() { _intervals.clear(); } | |
| const vector<FieldInterval> &intervals() const { return _intervals;
} | | const vector<FieldInterval> &intervals() const { return _intervals;
} | |
| string getSpecial() const { return _special; } | | string getSpecial() const { return _special; } | |
| /** Make component intervals noninclusive. */ | | /** Make component intervals noninclusive. */ | |
| void setExclusiveBounds(); | | void setExclusiveBounds(); | |
| /** | | /** | |
| * Constructs a range where all FieldIntervals and FieldBounds are
in | | * Constructs a range where all FieldIntervals and FieldBounds are
in | |
| * the opposite order of the current range. | | * the opposite order of the current range. | |
| * NOTE the resulting intervals might not be strictValid(). | | * NOTE the resulting intervals might not be strictValid(). | |
| */ | | */ | |
| void reverse( FieldRange &ret ) const; | | void reverse( FieldRange &ret ) const; | |
| | | | |
| string toString() const; | | string toString() const; | |
| private: | | private: | |
| BSONObj addObj( const BSONObj &o ); | | BSONObj addObj( const BSONObj &o ); | |
| void finishOperation( const vector<FieldInterval> &newIntervals, co
nst FieldRange &other, | | void finishOperation( const vector<FieldInterval> &newIntervals, co
nst FieldRange &other, | |
|
| bool simpleFiniteSet ); | | bool exactMatchRepresentation ); | |
| vector<FieldInterval> _intervals; | | vector<FieldInterval> _intervals; | |
| // Owns memory for our BSONElements. | | // Owns memory for our BSONElements. | |
| vector<BSONObj> _objData; | | vector<BSONObj> _objData; | |
| string _special; | | string _special; | |
|
| bool _singleKey; | | bool _exactMatchRepresentation; | |
| bool _simpleFiniteSet; | | | |
| }; | | }; | |
| | | | |
| /** | | /** | |
| * A BoundList contains intervals specified by inclusive start | | * A BoundList contains intervals specified by inclusive start | |
| * and end bounds. The intervals should be nonoverlapping and occur in | | * and end bounds. The intervals should be nonoverlapping and occur in | |
| * the specified direction of traversal. For example, given a simple i
ndex {i:1} | | * the specified direction of traversal. For example, given a simple i
ndex {i:1} | |
| * and direction +1, one valid BoundList is: (1, 2); (4, 6). The same
BoundList | | * and direction +1, one valid BoundList is: (1, 2); (4, 6). The same
BoundList | |
| * would be valid for index {i:-1} with direction -1. | | * would be valid for index {i:-1} with direction -1. | |
| */ | | */ | |
| typedef vector<pair<BSONObj,BSONObj> > BoundList; | | typedef vector<pair<BSONObj,BSONObj> > BoundList; | |
| | | | |
| skipping to change at line 363 | | skipping to change at line 388 | |
| class QueryPattern; | | class QueryPattern; | |
| | | | |
| /** | | /** | |
| * A set of FieldRanges determined from constraints on the fields of a
query, | | * A set of FieldRanges determined from constraints on the fields of a
query, | |
| * that may be used to determine index bounds. | | * that may be used to determine index bounds. | |
| */ | | */ | |
| class FieldRangeSet { | | class FieldRangeSet { | |
| public: | | public: | |
| friend class OrRangeGenerator; | | friend class OrRangeGenerator; | |
| friend class FieldRangeVector; | | friend class FieldRangeVector; | |
|
| FieldRangeSet( const char *ns, const BSONObj &query , bool singleKe | | /** | |
| y , bool optimize=true ); | | * Creates a FieldRangeSet representing a superset of the documents | |
| | | matching a query. | |
| | | * @param ns - The query's namespace. | |
| | | * @param query - The query. | |
| | | * @param singleKey - Indicates that document fields contain single | |
| | | values (there are no | |
| | | * multiply valued fields). | |
| | | * @param optimize - If true, each field's value range may be brack | |
| | | eted by data type. | |
| | | * TODO It is unclear why 'optimize' is optional, see SERVER-51 | |
| | | 65. | |
| | | */ | |
| | | FieldRangeSet( const char *ns, const BSONObj &query , bool singleKe | |
| | | y , bool optimize ); | |
| | | | |
| /** @return range for the given field. */ | | /** @return range for the given field. */ | |
| const FieldRange &range( const char *fieldName ) const; | | const FieldRange &range( const char *fieldName ) const; | |
|
| /** @return range for the given field. */ | | /** @return range for the given field. Public for testing. */ | |
| FieldRange &range( const char *fieldName ); | | FieldRange &range( const char *fieldName ); | |
| /** @return the number of non universal ranges. */ | | /** @return the number of non universal ranges. */ | |
| int numNonUniversalRanges() const; | | int numNonUniversalRanges() const; | |
| /** @return the field ranges comprising this set. */ | | /** @return the field ranges comprising this set. */ | |
| const map<string,FieldRange> &ranges() const { return _ranges; } | | const map<string,FieldRange> &ranges() const { return _ranges; } | |
| /** | | /** | |
| * @return true if a match could be possible on every field. Genera
lly this | | * @return true if a match could be possible on every field. Genera
lly this | |
| * is not useful information for a single key FieldRangeSet and | | * is not useful information for a single key FieldRangeSet and | |
| * matchPossibleForIndex() should be used instead. | | * matchPossibleForIndex() should be used instead. | |
| */ | | */ | |
| bool matchPossible() const; | | bool matchPossible() const; | |
| /** | | /** | |
| * @return true if a match could be possible given the value of _si
ngleKey | | * @return true if a match could be possible given the value of _si
ngleKey | |
| * and index key 'keyPattern'. | | * and index key 'keyPattern'. | |
| * @param keyPattern May be {} or {$natural:1} for a non index scan
. | | * @param keyPattern May be {} or {$natural:1} for a non index scan
. | |
| */ | | */ | |
| bool matchPossibleForIndex( const BSONObj &keyPattern ) const; | | bool matchPossibleForIndex( const BSONObj &keyPattern ) const; | |
| /** | | /** | |
|
| * @return true in many cases when this FieldRangeSet describes a f | | * @return true in many cases when this FieldRangeSet represents th | |
| inite set of BSONObjs, | | e exact set of BSONObjs | |
| * all of which will be matched by the query BSONObj that generated | | * matching the query expression used to construct the FieldRangeSe | |
| this FieldRangeSet. | | t. This attribute is | |
| * This attribute is used to implement higher level optimizations a | | * used to implement higher level optimizations and is computed wit | |
| nd is computed with a | | h a simple implementation | |
| * simple implementation that identifies common (but not all) cases | | * that identifies common (but not all) cases of this property and | |
| satisfying the stated | | may return false | |
| * properties. | | * negatives. | |
| */ | | */ | |
|
| bool simpleFiniteSet() const { return _simpleFiniteSet; } | | bool mustBeExactMatchRepresentation() const { return _exactMatchRep | |
| | | resentation; } | |
| | | | |
| | | /* Checks whether this FieldRangeSet is a non-empty union of point- | |
| | | intervals | |
| | | * on a given field. | |
| | | * Examples: | |
| | | * FieldRangeSet({a : 3}), isPointIntervalSet("a") -> true | |
| | | * FieldRangeSet({a : {$in : [1 , 2]}}), isPointIntervalSet("a") - | |
| | | > true | |
| | | * FieldRangeSet({}), isPointIntervalSet("a") -> false | |
| | | * FieldRangeSet({b : 1}), isPointIntervalSet("a") -> false | |
| | | * | |
| | | * Used in determining "suitability" for hashedindexes, and also in | |
| | | * sharding for determining the relevant shards for a query. | |
| | | * | |
| | | * TODO: move this into FieldRange instead of FieldRangeSet | |
| | | */ | |
| | | bool isPointIntervalSet( const string& fieldname ) const; | |
| | | | |
| const char *ns() const { return _ns.c_str(); } | | const char *ns() const { return _ns.c_str(); } | |
| | | | |
| /** | | /** | |
| * @return a simplified query from the extreme values of the non un
iversal | | * @return a simplified query from the extreme values of the non un
iversal | |
| * fields. | | * fields. | |
| * @param fields If specified, the fields of the returned object ar
e | | * @param fields If specified, the fields of the returned object ar
e | |
| * ordered to match those of 'fields'. | | * ordered to match those of 'fields'. | |
| */ | | */ | |
| BSONObj simplifiedQuery( const BSONObj &fields = BSONObj() ) const; | | BSONObj simplifiedQuery( const BSONObj &fields = BSONObj() ) const; | |
| | | | |
| skipping to change at line 436 | | skipping to change at line 485 | |
| BoundList indexBounds( const BSONObj &keyPattern, int direction ) c
onst; | | BoundList indexBounds( const BSONObj &keyPattern, int direction ) c
onst; | |
| | | | |
| /** | | /** | |
| * @return - A new FieldRangeSet based on this FieldRangeSet, but w
ith only | | * @return - A new FieldRangeSet based on this FieldRangeSet, but w
ith only | |
| * a subset of the fields. | | * a subset of the fields. | |
| * @param fields - Only fields which are represented as field names
in this object | | * @param fields - Only fields which are represented as field names
in this object | |
| * will be included in the returned FieldRangeSet. | | * will be included in the returned FieldRangeSet. | |
| */ | | */ | |
| FieldRangeSet *subset( const BSONObj &fields ) const; | | FieldRangeSet *subset( const BSONObj &fields ) const; | |
| | | | |
|
| | | /** | |
| | | * @return A new FieldRangeSet based on this FieldRangeSet, but wit | |
| | | h all field names | |
| | | * prefixed by the specified @param prefix field name. | |
| | | */ | |
| | | FieldRangeSet* prefixed( const string& prefix ) const; | |
| | | | |
| bool singleKey() const { return _singleKey; } | | bool singleKey() const { return _singleKey; } | |
| | | | |
| BSONObj originalQuery() const { return _queries[ 0 ]; } | | BSONObj originalQuery() const { return _queries[ 0 ]; } | |
| | | | |
| string toString() const; | | string toString() const; | |
|
| | | | |
| private: | | private: | |
|
| | | /** | |
| | | * Private constructor for implementation specific delegate objects | |
| | | . | |
| | | * @param boundElemMatch - If false, FieldRanges will not be comput | |
| | | ed for $elemMatch | |
| | | * expressions. | |
| | | */ | |
| | | FieldRangeSet( const char* ns, const BSONObj& query, bool singleKey | |
| | | , bool optimize, | |
| | | bool boundElemMatch ); | |
| | | /** Initializer shared by the constructors. */ | |
| | | void init( bool optimize ); | |
| | | | |
| void appendQueries( const FieldRangeSet &other ); | | void appendQueries( const FieldRangeSet &other ); | |
| void makeEmpty(); | | void makeEmpty(); | |
|
| void processQueryField( const BSONElement &e, bool optimize ); | | | |
| void processOpElement( const char *fieldName, const BSONElement &f, | | /** | |
| bool isNot, bool optimize ); | | * Query parsing routines. | |
| | | * TODO integrate these with an external query parser shared by the | |
| | | matcher. SERVER-1009 | |
| | | */ | |
| | | void handleMatchField( const BSONElement& matchElement, bool optimi | |
| | | ze ); | |
| | | void handleOp( const char* matchFieldName, const BSONElement& op, b | |
| | | ool isNot, | |
| | | bool optimize ); | |
| | | void handleNotOp( const char* matchFieldName, const BSONElement& no | |
| | | tOp, bool optimize ); | |
| | | void handleElemMatch( const char* matchFieldName, const BSONElement | |
| | | & elemMatch, bool isNot, | |
| | | bool optimize ); | |
| | | | |
| /** Must be called when a match element is skipped or modified to g
enerate a FieldRange. */ | | /** Must be called when a match element is skipped or modified to g
enerate a FieldRange. */ | |
| void adjustMatchField(); | | void adjustMatchField(); | |
| void intersectMatchField( const char *fieldName, const BSONElement
&matchElement, | | void intersectMatchField( const char *fieldName, const BSONElement
&matchElement, | |
| bool isNot, bool optimize ); | | bool isNot, bool optimize ); | |
|
| static FieldRange *__singleKeyUniversalRange; | | static FieldRange *__universalRange; | |
| static FieldRange *__multiKeyUniversalRange; | | | |
| const FieldRange &universalRange() const; | | const FieldRange &universalRange() const; | |
| map<string,FieldRange> _ranges; | | map<string,FieldRange> _ranges; | |
| string _ns; | | string _ns; | |
| // Owns memory for FieldRange BSONElements. | | // Owns memory for FieldRange BSONElements. | |
| vector<BSONObj> _queries; | | vector<BSONObj> _queries; | |
| bool _singleKey; | | bool _singleKey; | |
|
| bool _simpleFiniteSet; | | bool _exactMatchRepresentation; | |
| | | bool _boundElemMatch; | |
| }; | | }; | |
| | | | |
| class NamespaceDetails; | | class NamespaceDetails; | |
| | | | |
| /** | | /** | |
| * A pair of FieldRangeSets, one representing constraints for single ke
y | | * A pair of FieldRangeSets, one representing constraints for single ke
y | |
| * indexes and the other representing constraints for multi key indexes
and | | * indexes and the other representing constraints for multi key indexes
and | |
| * unindexed scans. In several member functions the caller is asked to | | * unindexed scans. In several member functions the caller is asked to | |
| * supply an index so that the implementation may utilize the proper | | * supply an index so that the implementation may utilize the proper | |
| * FieldRangeSet and return results that are appropriate with respect t
o that | | * FieldRangeSet and return results that are appropriate with respect t
o that | |
| | | | |
| skipping to change at line 595 | | skipping to change at line 671 | |
| | | | |
| /** | | /** | |
| * Helper class for iterating through an ordered representation of keys | | * Helper class for iterating through an ordered representation of keys | |
| * to find those keys that match a specified FieldRangeVector. | | * to find those keys that match a specified FieldRangeVector. | |
| */ | | */ | |
| class FieldRangeVectorIterator { | | class FieldRangeVectorIterator { | |
| public: | | public: | |
| /** | | /** | |
| * @param v - a FieldRangeVector representing matching keys. | | * @param v - a FieldRangeVector representing matching keys. | |
| * @param singleIntervalLimit - The maximum number of keys to match
a single (compound) | | * @param singleIntervalLimit - The maximum number of keys to match
a single (compound) | |
|
| * interval before advancing to the next interval. Limit check | | * interval before advancing to the next interval. Limit check | |
| ing is disabled if 0 and | | ing is disabled if 0. | |
| * must be disabled if v contains FieldIntervals that are not e | | | |
| quality(). | | | |
| */ | | */ | |
| FieldRangeVectorIterator( const FieldRangeVector &v, int singleInte
rvalLimit ); | | FieldRangeVectorIterator( const FieldRangeVector &v, int singleInte
rvalLimit ); | |
| | | | |
| /** | | /** | |
| * @return Suggested advance method through an ordered list of keys
with lookup support | | * @return Suggested advance method through an ordered list of keys
with lookup support | |
| * (generally a btree). | | * (generally a btree). | |
| * -2 Iteration is complete, no need to advance further. | | * -2 Iteration is complete, no need to advance further. | |
| * -1 Advance to the next ordered key, without skipping. | | * -1 Advance to the next ordered key, without skipping. | |
| * >=0 Skip parameter, let's call it 'r'. If after() is true, ski
p past the key prefix | | * >=0 Skip parameter, let's call it 'r'. If after() is true, ski
p past the key prefix | |
| * comprised of the first r elements of curr. For example, if
curr is {a:1,b:1}, the | | * comprised of the first r elements of curr. For example, if
curr is {a:1,b:1}, the | |
| | | | |
| skipping to change at line 642 | | skipping to change at line 717 | |
| void inc( int i ); | | void inc( int i ); | |
| void setZeroes( int i ); | | void setZeroes( int i ); | |
| void setUnknowns( int i ); | | void setUnknowns( int i ); | |
| void incSingleIntervalCount() { | | void incSingleIntervalCount() { | |
| if ( isTrackingIntervalCounts() ) ++_singleIntervalCount; | | if ( isTrackingIntervalCounts() ) ++_singleIntervalCount; | |
| } | | } | |
| bool hasSingleIntervalCountReachedLimit() const { | | bool hasSingleIntervalCountReachedLimit() const { | |
| return isTrackingIntervalCounts() && _singleIntervalCount >
= _singleIntervalLimit; | | return isTrackingIntervalCounts() && _singleIntervalCount >
= _singleIntervalLimit; | |
| } | | } | |
| void resetIntervalCount() { _singleIntervalCount = 0; } | | void resetIntervalCount() { _singleIntervalCount = 0; } | |
|
| | | string toString() const; | |
| private: | | private: | |
| bool isTrackingIntervalCounts() const { return _singleIntervalL
imit > 0; } | | bool isTrackingIntervalCounts() const { return _singleIntervalL
imit > 0; } | |
| vector<int> _i; | | vector<int> _i; | |
| int _singleIntervalCount; | | int _singleIntervalCount; | |
| int _singleIntervalLimit; | | int _singleIntervalLimit; | |
| }; | | }; | |
| | | | |
| /** | | /** | |
| * Helper class for matching a BSONElement with the bounds of a Fie
ldInterval. Some | | * Helper class for matching a BSONElement with the bounds of a Fie
ldInterval. Some | |
| * internal comparison results are cached. Public for testing. | | * internal comparison results are cached. Public for testing. | |
| | | | |
| skipping to change at line 706 | | skipping to change at line 782 | |
| bool reverse, bool first, bool &eqInclu
siveUpperBound ); | | bool reverse, bool first, bool &eqInclu
siveUpperBound ); | |
| | | | |
| /** Skip to curr / i / nextbounds. */ | | /** Skip to curr / i / nextbounds. */ | |
| int advanceToLowerBound( int i ); | | int advanceToLowerBound( int i ); | |
| /** Skip to curr / i / superlative. */ | | /** Skip to curr / i / superlative. */ | |
| int advancePast( int i ); | | int advancePast( int i ); | |
| /** Skip to curr / i / superlative and reset following interval pos
itions. */ | | /** Skip to curr / i / superlative and reset following interval pos
itions. */ | |
| int advancePastZeroed( int i ); | | int advancePastZeroed( int i ); | |
| | | | |
| bool hasReachedLimitForLastInterval( int intervalIdx ) const { | | bool hasReachedLimitForLastInterval( int intervalIdx ) const { | |
|
| return _i.hasSingleIntervalCountReachedLimit() && ( intervalIdx | | return | |
| + 1 == _i.size() ); | | _i.hasSingleIntervalCountReachedLimit() && | |
| | | ( intervalIdx + 1 == _endNonUniversalRanges ); | |
| } | | } | |
| | | | |
|
| | | /** @return the index of the last non universal range + 1. */ | |
| | | int endNonUniversalRanges() const; | |
| | | | |
| const FieldRangeVector &_v; | | const FieldRangeVector &_v; | |
| CompoundRangeCounter _i; | | CompoundRangeCounter _i; | |
| vector<const BSONElement*> _cmp; | | vector<const BSONElement*> _cmp; | |
| vector<bool> _inc; | | vector<bool> _inc; | |
| bool _after; | | bool _after; | |
|
| | | int _endNonUniversalRanges; | |
| }; | | }; | |
| | | | |
| /** | | /** | |
| * As we iterate through $or clauses this class generates a FieldRangeS
etPair | | * As we iterate through $or clauses this class generates a FieldRangeS
etPair | |
| * for the current $or clause, in some cases by excluding ranges that w
ere | | * for the current $or clause, in some cases by excluding ranges that w
ere | |
| * included in a previous clause. | | * included in a previous clause. | |
| */ | | */ | |
| class OrRangeGenerator { | | class OrRangeGenerator { | |
| public: | | public: | |
| OrRangeGenerator( const char *ns, const BSONObj &query , bool optim
ize=true ); | | OrRangeGenerator( const char *ns, const BSONObj &query , bool optim
ize=true ); | |
| | | | |
End of changes. 25 change blocks. |
| 47 lines changed or deleted | | 150 lines changed or added | |
|
| rs.h | | rs.h | |
| | | | |
| skipping to change at line 21 | | skipping to change at line 21 | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU Affero General Public License for more details. | | * GNU Affero General Public License for more details. | |
| * | | * | |
| * You should have received a copy of the GNU Affero General Public Licen
se | | * You should have received a copy of the GNU Affero General Public Licen
se | |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. | | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
|
| #include "../../util/concurrency/list.h" | | #include "mongo/db/commands.h" | |
| #include "../../util/concurrency/value.h" | | #include "mongo/db/oplog.h" | |
| #include "../../util/concurrency/msg.h" | | #include "mongo/db/oplogreader.h" | |
| #include "../../util/net/hostandport.h" | | #include "mongo/db/repl/rs_config.h" | |
| #include "../commands.h" | | #include "mongo/db/repl/rs_exception.h" | |
| #include "../oplog.h" | | #include "mongo/db/repl/rs_member.h" | |
| #include "../oplogreader.h" | | #include "mongo/db/repl/rs_optime.h" | |
| #include "rs_exception.h" | | #include "mongo/db/repl/rs_sync.h" | |
| #include "rs_optime.h" | | #include "mongo/util/concurrency/list.h" | |
| #include "rs_member.h" | | #include "mongo/util/concurrency/msg.h" | |
| #include "rs_config.h" | | #include "mongo/util/concurrency/thread_pool.h" | |
| | | #include "mongo/util/concurrency/value.h" | |
| | | #include "mongo/util/net/hostandport.h" | |
| | | | |
| /** | | /** | |
| * Order of Events | | * Order of Events | |
| * | | * | |
| * On startup, if the --replSet option is present, startReplSets is called. | | * On startup, if the --replSet option is present, startReplSets is called. | |
| * startReplSets forks off a new thread for replica set activities. It cre
ates | | * startReplSets forks off a new thread for replica set activities. It cre
ates | |
| * the global theReplSet variable and calls go() on it. | | * the global theReplSet variable and calls go() on it. | |
| * | | * | |
| * theReplSet's constructor changes the replica set's state to RS_STARTUP, | | * theReplSet's constructor changes the replica set's state to RS_STARTUP, | |
| * starts the replica set manager, and loads the config (if the replica set | | * starts the replica set manager, and loads the config (if the replica set | |
| | | | |
| skipping to change at line 84 | | skipping to change at line 86 | |
| bool potentiallyHot() const { return _config.potentiallyHot(); } //
not arbiter, not priority 0 | | bool potentiallyHot() const { return _config.potentiallyHot(); } //
not arbiter, not priority 0 | |
| void summarizeMember(stringstream& s) const; | | void summarizeMember(stringstream& s) const; | |
| | | | |
| private: | | private: | |
| friend class ReplSetImpl; | | friend class ReplSetImpl; | |
| ReplSetConfig::MemberCfg _config; | | ReplSetConfig::MemberCfg _config; | |
| const HostAndPort _h; | | const HostAndPort _h; | |
| HeartbeatInfo _hbinfo; | | HeartbeatInfo _hbinfo; | |
| }; | | }; | |
| | | | |
|
| namespace replset { | | | |
| /** | | | |
| * "Normal" replica set syncing | | | |
| */ | | | |
| class SyncTail : public Sync { | | | |
| public: | | | |
| virtual ~SyncTail() {} | | | |
| SyncTail(const string& host) : Sync(host) {} | | | |
| virtual bool syncApply(const BSONObj &o); | | | |
| }; | | | |
| | | | |
| /** | | | |
| * Initial clone and sync | | | |
| */ | | | |
| class InitialSync : public SyncTail { | | | |
| public: | | | |
| InitialSync(const string& host) : SyncTail(host) {} | | | |
| virtual ~InitialSync() {} | | | |
| bool oplogApplication(OplogReader& r, const Member* source, con | | | |
| st OpTime& applyGTE, const OpTime& minValid); | | | |
| virtual void applyOp(const BSONObj& o, const OpTime& minvalid); | | | |
| }; | | | |
| | | | |
| // TODO: move hbmsg into an error-keeping class (SERVER-4444) | | | |
| void sethbmsg(const string& s, const int logLevel=0); | | | |
| | | | |
| } // namespace replset | | | |
| | | | |
| class Manager : public task::Server { | | class Manager : public task::Server { | |
| ReplSetImpl *rs; | | ReplSetImpl *rs; | |
| bool busyWithElectSelf; | | bool busyWithElectSelf; | |
| int _primary; | | int _primary; | |
| | | | |
| /** @param two - if true two primaries were seen. this can happen
transiently, in addition to our | | /** @param two - if true two primaries were seen. this can happen
transiently, in addition to our | |
| polling being only occasional. in this case null
is returned, but the caller should | | polling being only occasional. in this case null
is returned, but the caller should | |
| not assume primary itself in that situation. | | not assume primary itself in that situation. | |
| */ | | */ | |
| const Member* findOtherPrimary(bool& two); | | const Member* findOtherPrimary(bool& two); | |
| | | | |
| skipping to change at line 313 | | skipping to change at line 288 | |
| sp.primary = p; | | sp.primary = p; | |
| } | | } | |
| void setSelfPrimary(const Member *self) { change(MemberState::RS_PR
IMARY, self); } | | void setSelfPrimary(const Member *self) { change(MemberState::RS_PR
IMARY, self); } | |
| void setOtherPrimary(const Member *mem) { | | void setOtherPrimary(const Member *mem) { | |
| rwlock lk(m, true); | | rwlock lk(m, true); | |
| verify( !sp.state.primary() ); | | verify( !sp.state.primary() ); | |
| sp.primary = mem; | | sp.primary = mem; | |
| } | | } | |
| void noteRemoteIsPrimary(const Member *remote) { | | void noteRemoteIsPrimary(const Member *remote) { | |
| rwlock lk(m, true); | | rwlock lk(m, true); | |
|
| if( !sp.state.secondary() && !sp.state.fatal() ) | | verify(!sp.state.primary()); | |
| sp.state = MemberState::RS_RECOVERING; | | | |
| sp.primary = remote; | | sp.primary = remote; | |
| } | | } | |
| StateBox() : m("StateBox") { } | | StateBox() : m("StateBox") { } | |
| private: | | private: | |
| RWLock m; | | RWLock m; | |
| SP sp; | | SP sp; | |
| }; | | }; | |
| | | | |
| void parseReplsetCmdLine(string cfgString, string& setname, vector<Host
AndPort>& seeds, set<HostAndPort>& seedSet ); | | void parseReplsetCmdLine(string cfgString, string& setname, vector<Host
AndPort>& seeds, set<HostAndPort>& seedSet ); | |
| | | | |
| | | | |
| skipping to change at line 359 | | skipping to change at line 333 | |
| static string stateAsHtml(MemberState state); | | static string stateAsHtml(MemberState state); | |
| | | | |
| /* todo thread */ | | /* todo thread */ | |
| void msgUpdateHBInfo(HeartbeatInfo); | | void msgUpdateHBInfo(HeartbeatInfo); | |
| | | | |
| StateBox box; | | StateBox box; | |
| | | | |
| OpTime lastOpTimeWritten; | | OpTime lastOpTimeWritten; | |
| long long lastH; // hash we use to make sure we are reading the rig
ht flow of ops and aren't on an out-of-date "fork" | | long long lastH; // hash we use to make sure we are reading the rig
ht flow of ops and aren't on an out-of-date "fork" | |
| bool forceSyncFrom(const string& host, string& errmsg, BSONObjBuild
er& result); | | bool forceSyncFrom(const string& host, string& errmsg, BSONObjBuild
er& result); | |
|
| | | | |
| | | /** | |
| | | * Find the closest member (using ping time) with a higher latest o | |
| | | ptime. | |
| | | */ | |
| | | Member* getMemberToSyncTo(); | |
| | | void veto(const string& host, unsigned secs=10); | |
| | | bool gotForceSync(); | |
| | | void goStale(const Member* m, const BSONObj& o); | |
| private: | | private: | |
| set<ReplSetHealthPollTask*> healthTasks; | | set<ReplSetHealthPollTask*> healthTasks; | |
| void endOldHealthTasks(); | | void endOldHealthTasks(); | |
| void startHealthTaskFor(Member *m); | | void startHealthTaskFor(Member *m); | |
| | | | |
| Consensus elect; | | Consensus elect; | |
| void relinquish(); | | void relinquish(); | |
| void forgetPrimary(); | | void forgetPrimary(); | |
| protected: | | protected: | |
| bool _stepDown(int secs); | | bool _stepDown(int secs); | |
| bool _freeze(int secs); | | bool _freeze(int secs); | |
| private: | | private: | |
| void assumePrimary(); | | void assumePrimary(); | |
| void loadLastOpTimeWritten(bool quiet=false); | | void loadLastOpTimeWritten(bool quiet=false); | |
| void changeState(MemberState s); | | void changeState(MemberState s); | |
| | | | |
|
| /** | | | |
| * Find the closest member (using ping time) with a higher latest o | | | |
| ptime. | | | |
| */ | | | |
| Member* getMemberToSyncTo(); | | | |
| void veto(const string& host, unsigned secs=10); | | | |
| Member* _currentSyncTarget; | | | |
| Member* _forceSyncTarget; | | Member* _forceSyncTarget; | |
| | | | |
| bool _blockSync; | | bool _blockSync; | |
| void blockSync(bool block); | | void blockSync(bool block); | |
| | | | |
| // set of electable members' _ids | | // set of electable members' _ids | |
| set<unsigned> _electableSet; | | set<unsigned> _electableSet; | |
| protected: | | protected: | |
| // "heartbeat message" | | // "heartbeat message" | |
| // sent in requestHeartbeat respond in field "hbm" | | // sent in requestHeartbeat respond in field "hbm" | |
| | | | |
| skipping to change at line 455 | | skipping to change at line 431 | |
| const ReplSetConfig& config() { return *_cfg; } | | const ReplSetConfig& config() { return *_cfg; } | |
| string name() const { return _name; } /* @return replica set's logi
cal name */ | | string name() const { return _name; } /* @return replica set's logi
cal name */ | |
| MemberState state() const { return box.getState(); } | | MemberState state() const { return box.getState(); } | |
| void _fatal(); | | void _fatal(); | |
| void _getOplogDiagsAsHtml(unsigned server_id, stringstream& ss) con
st; | | void _getOplogDiagsAsHtml(unsigned server_id, stringstream& ss) con
st; | |
| void _summarizeAsHtml(stringstream&) const; | | void _summarizeAsHtml(stringstream&) const; | |
| void _summarizeStatus(BSONObjBuilder&) const; // for replSetGetStat
us command | | void _summarizeStatus(BSONObjBuilder&) const; // for replSetGetStat
us command | |
| | | | |
| /* throws exception if a problem initializing. */ | | /* throws exception if a problem initializing. */ | |
| ReplSetImpl(ReplSetCmdline&); | | ReplSetImpl(ReplSetCmdline&); | |
|
| | | // used for testing | |
| | | ReplSetImpl(); | |
| | | | |
| /* call afer constructing to start - returns fairly quickly after l
aunching its threads */ | | /* call afer constructing to start - returns fairly quickly after l
aunching its threads */ | |
| void _go(); | | void _go(); | |
| | | | |
| private: | | private: | |
| string _name; | | string _name; | |
| const vector<HostAndPort> *_seeds; | | const vector<HostAndPort> *_seeds; | |
| ReplSetConfig *_cfg; | | ReplSetConfig *_cfg; | |
| | | | |
| /** | | /** | |
| | | | |
| skipping to change at line 477 | | skipping to change at line 455 | |
| */ | | */ | |
| bool _loadConfigFinish(vector<ReplSetConfig>& v); | | bool _loadConfigFinish(vector<ReplSetConfig>& v); | |
| /** | | /** | |
| * Gather all possible configs (from command line seeds, our own co
nfig | | * Gather all possible configs (from command line seeds, our own co
nfig | |
| * doc, and any hosts listed therein) and try to initiate from the
most | | * doc, and any hosts listed therein) and try to initiate from the
most | |
| * recent config we find. | | * recent config we find. | |
| */ | | */ | |
| void loadConfig(); | | void loadConfig(); | |
| | | | |
| list<HostAndPort> memberHostnames() const; | | list<HostAndPort> memberHostnames() const; | |
|
| const ReplSetConfig::MemberCfg& myConfig() const { return _config;
} | | | |
| bool iAmArbiterOnly() const { return myConfig().arbiterOnly; } | | bool iAmArbiterOnly() const { return myConfig().arbiterOnly; } | |
| bool iAmPotentiallyHot() const { | | bool iAmPotentiallyHot() const { | |
| return myConfig().potentiallyHot() && // not an arbiter | | return myConfig().potentiallyHot() && // not an arbiter | |
| elect.steppedDown <= time(0) && // not stepped down/frozen | | elect.steppedDown <= time(0) && // not stepped down/frozen | |
| state() == MemberState::RS_SECONDARY; // not stale | | state() == MemberState::RS_SECONDARY; // not stale | |
| } | | } | |
| protected: | | protected: | |
| Member *_self; | | Member *_self; | |
| bool _buildIndexes; // = _self->config().buildIndexes | | bool _buildIndexes; // = _self->config().buildIndexes | |
| void setSelfTo(Member *); // use this as it sets buildIndexes var | | void setSelfTo(Member *); // use this as it sets buildIndexes var | |
| | | | |
| skipping to change at line 522 | | skipping to change at line 499 | |
| void getTargets(list<Target>&, int &configVersion); | | void getTargets(list<Target>&, int &configVersion); | |
| void startThreads(); | | void startThreads(); | |
| friend class FeedbackThread; | | friend class FeedbackThread; | |
| friend class CmdReplSetElect; | | friend class CmdReplSetElect; | |
| friend class Member; | | friend class Member; | |
| friend class Manager; | | friend class Manager; | |
| friend class GhostSync; | | friend class GhostSync; | |
| friend class Consensus; | | friend class Consensus; | |
| | | | |
| private: | | private: | |
|
| bool initialSyncOplogApplication(const OpTime& applyGTE, const OpTi | | bool _syncDoInitialSync_clone( const char *master, const list<strin | |
| me& minValid); | | g>& dbs , bool dataPass ); | |
| | | bool _syncDoInitialSync_applyToHead( replset::InitialSync& init, Op | |
| | | logReader* r , | |
| | | const Member* source, const BS | |
| | | ONObj& lastOp, | |
| | | BSONObj& minValidOut); | |
| void _syncDoInitialSync(); | | void _syncDoInitialSync(); | |
| void syncDoInitialSync(); | | void syncDoInitialSync(); | |
| void _syncThread(); | | void _syncThread(); | |
|
| bool tryToGoLiveAsASecondary(OpTime&); // readlocks | | | |
| void syncTail(); | | void syncTail(); | |
| unsigned _syncRollback(OplogReader& r); | | unsigned _syncRollback(OplogReader& r); | |
|
| void syncRollback(OplogReader& r); | | | |
| void syncFixUp(HowToFixUp& h, OplogReader& r); | | void syncFixUp(HowToFixUp& h, OplogReader& r); | |
| | | | |
|
| // get an oplog reader for a server with an oplog entry timestamp g | | | |
| reater | | | |
| // than or equal to minTS, if set. | | | |
| Member* _getOplogReader(OplogReader& r, const OpTime& minTS); | | | |
| | | | |
| // check lastOpTimeWritten against the remote's earliest op, fillin | | | |
| g in | | | |
| // remoteOldestOp. | | | |
| bool _isStale(OplogReader& r, const OpTime& minTS, BSONObj& remoteO | | | |
| ldestOp); | | | |
| | | | |
| // keep a list of hosts that we've tried recently that didn't work | | // keep a list of hosts that we've tried recently that didn't work | |
| map<string,time_t> _veto; | | map<string,time_t> _veto; | |
|
| | | // persistent pool of worker threads for writing ops to the databas | |
| | | es | |
| | | threadpool::ThreadPool _writerPool; | |
| | | // persistent pool of worker threads for prefetching | |
| | | threadpool::ThreadPool _prefetcherPool; | |
| | | | |
| public: | | public: | |
|
| | | static const int replWriterThreadCount; | |
| | | static const int replPrefetcherThreadCount; | |
| | | threadpool::ThreadPool& getPrefetchPool() { return _prefetcherPool; | |
| | | } | |
| | | threadpool::ThreadPool& getWriterPool() { return _writerPool; } | |
| | | | |
| | | const ReplSetConfig::MemberCfg& myConfig() const { return _config; | |
| | | } | |
| | | bool tryToGoLiveAsASecondary(OpTime&); // readlocks | |
| | | void syncRollback(OplogReader& r); | |
| void syncThread(); | | void syncThread(); | |
| const OpTime lastOtherOpTime() const; | | const OpTime lastOtherOpTime() const; | |
| }; | | }; | |
| | | | |
| class ReplSet : public ReplSetImpl { | | class ReplSet : public ReplSetImpl { | |
| public: | | public: | |
|
| ReplSet(ReplSetCmdline& replSetCmdline) : ReplSetImpl(replSetCmdlin | | ReplSet(); | |
| e) { } | | ReplSet(ReplSetCmdline& replSetCmdline); | |
| | | virtual ~ReplSet() {} | |
| | | | |
| // for the replSetStepDown command | | // for the replSetStepDown command | |
| bool stepDown(int secs) { return _stepDown(secs); } | | bool stepDown(int secs) { return _stepDown(secs); } | |
| | | | |
| // for the replSetFreeze command | | // for the replSetFreeze command | |
| bool freeze(int secs) { return _freeze(secs); } | | bool freeze(int secs) { return _freeze(secs); } | |
| | | | |
| string selfFullName() { | | string selfFullName() { | |
| verify( _self ); | | verify( _self ); | |
| return _self->fullName(); | | return _self->fullName(); | |
| } | | } | |
| | | | |
|
| bool buildIndexes() const { return _buildIndexes; } | | virtual bool buildIndexes() const { return _buildIndexes; } | |
| | | | |
|
| /* call after constructing to start - returns fairly quickly after
la[unching its threads */ | | /* call after constructing to start - returns fairly quickly after
launching its threads */ | |
| void go() { _go(); } | | void go() { _go(); } | |
|
| | | void shutdown(); | |
| | | | |
| void fatal() { _fatal(); } | | void fatal() { _fatal(); } | |
|
| bool isPrimary() { return box.getState().primary(); } | | virtual bool isPrimary() { return box.getState().primary(); } | |
| bool isSecondary() { return box.getState().secondary(); } | | virtual bool isSecondary() { return box.getState().secondary(); } | |
| MemberState state() const { return ReplSetImpl::state(); } | | MemberState state() const { return ReplSetImpl::state(); } | |
| string name() const { return ReplSetImpl::name(); } | | string name() const { return ReplSetImpl::name(); } | |
|
| const ReplSetConfig& config() { return ReplSetImpl::config(); } | | virtual 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); } | |
|
| | | threadpool::ThreadPool& getPrefetchPool() { return ReplSetImpl::get | |
| | | PrefetchPool(); } | |
| | | threadpool::ThreadPool& getWriterPool() { return ReplSetImpl::getWr | |
| | | iterPool(); } | |
| | | | |
| /** | | /** | |
| * We have a new config (reconfig) - apply it. | | * We have a new config (reconfig) - apply it. | |
| * @param comment write a no-op comment to the oplog about it. onl
y | | * @param comment write a no-op comment to the oplog about it. onl
y | |
| * makes sense if one is primary and initiating the reconf. | | * makes sense if one is primary and initiating the reconf. | |
| * | | * | |
| * The slaves are updated when they get a heartbeat indicating the
new | | * The slaves are updated when they get a heartbeat indicating the
new | |
| * config. The comment is a no-op. | | * config. The comment is a no-op. | |
| */ | | */ | |
| void haveNewConfig(ReplSetConfig& c, bool comment); | | void haveNewConfig(ReplSetConfig& c, bool comment); | |
| | | | |
End of changes. 20 change blocks. |
| 71 lines changed or deleted | | 61 lines changed or added | |
|
| security.h | | security.h | |
| | | | |
| skipping to change at line 21 | | skipping to change at line 21 | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU Affero General Public License for more details. | | * GNU Affero General Public License for more details. | |
| * | | * | |
| * You should have received a copy of the GNU Affero General Public Licen
se | | * You should have received a copy of the GNU Affero General Public Licen
se | |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. | | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
|
| #include "mongo/db/authlevel.h" | | #include <string> | |
| | | | |
| #include "mongo/db/nonce.h" | | #include "mongo/db/nonce.h" | |
| #include "mongo/db/security_common.h" | | #include "mongo/db/security_common.h" | |
|
| | | #include "mongo/client/authentication_table.h" | |
| | | #include "mongo/client/authlevel.h" | |
| #include "mongo/util/concurrency/spin_lock.h" | | #include "mongo/util/concurrency/spin_lock.h" | |
| | | | |
| // this is used by both mongos and mongod | | // this is used by both mongos and mongod | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| /** An AuthenticationInfo object is present within every mongo::Client
object */ | | /** An AuthenticationInfo object is present within every mongo::Client
object */ | |
| class AuthenticationInfo : boost::noncopyable { | | class AuthenticationInfo : boost::noncopyable { | |
| bool _isLocalHost; | | bool _isLocalHost; | |
| bool _isLocalHostAndLocalHostIsAuthorizedForAll; | | bool _isLocalHostAndLocalHostIsAuthorizedForAll; | |
| public: | | public: | |
| void startRequest(); // need to call at the beginning of each reque
st | | void startRequest(); // need to call at the beginning of each reque
st | |
| void setIsALocalHostConnectionWithSpecialAuthPowers(); // called, i
f localhost, when conneciton established. | | void setIsALocalHostConnectionWithSpecialAuthPowers(); // called, i
f localhost, when conneciton established. | |
| AuthenticationInfo() { | | AuthenticationInfo() { | |
| _isLocalHost = false; | | _isLocalHost = false; | |
| _isLocalHostAndLocalHostIsAuthorizedForAll = false; | | _isLocalHostAndLocalHostIsAuthorizedForAll = false; | |
|
| | | _usingTempAuth = false; | |
| } | | } | |
| ~AuthenticationInfo() {} | | ~AuthenticationInfo() {} | |
| bool isLocalHost() const { return _isLocalHost; } // why are you ca
lling this? makes no sense to be externalized | | bool isLocalHost() const { return _isLocalHost; } // why are you ca
lling this? makes no sense to be externalized | |
| | | | |
| // -- modifiers ---- | | // -- modifiers ---- | |
| | | | |
|
| void logout(const string& dbname ) { | | void logout(const std::string& dbname ) { | |
| scoped_spinlock lk(_lock); | | scoped_spinlock lk(_lock); | |
|
| _dbs.erase(dbname); | | _authTable.removeAuth( dbname ); | |
| } | | } | |
|
| void authorize(const string& dbname , const string& user ) { | | void authorize(const std::string& dbname , const std::string& user
) { | |
| scoped_spinlock lk(_lock); | | scoped_spinlock lk(_lock); | |
|
| _dbs[dbname].level = Auth::WRITE; | | _authTable.addAuth( dbname, user, Auth::WRITE ); | |
| _dbs[dbname].user = user; | | | |
| } | | } | |
|
| void authorizeReadOnly(const string& dbname , const string& user )
{ | | void authorizeReadOnly(const std::string& dbname , const std::strin
g& user ) { | |
| scoped_spinlock lk(_lock); | | scoped_spinlock lk(_lock); | |
|
| _dbs[dbname].level = Auth::READ; | | _authTable.addAuth( dbname, user, Auth::READ ); | |
| _dbs[dbname].user = user; | | | |
| } | | } | |
| | | | |
| // -- accessors --- | | // -- accessors --- | |
| | | | |
|
| bool isAuthorized(const string& dbname) const { | | bool isAuthorized(const std::string& dbname) const { | |
| return _isAuthorized( dbname, Auth::WRITE ); | | return _isAuthorized( dbname, Auth::WRITE ); | |
| } | | } | |
| | | | |
|
| bool isAuthorizedReads(const string& dbname) const { | | bool isAuthorizedReads(const std::string& dbname) const { | |
| return _isAuthorized( dbname, Auth::READ ); | | return _isAuthorized( dbname, Auth::READ ); | |
| } | | } | |
| | | | |
| /** | | /** | |
| * @param lockType - this is from dbmutex 1 is write, 0 is read | | * @param lockType - this is from dbmutex 1 is write, 0 is read | |
| */ | | */ | |
|
| bool isAuthorizedForLock(const string& dbname, int lockType ) const
{ | | bool isAuthorizedForLock(const std::string& dbname, int lockType )
const { | |
| return _isAuthorized( dbname , lockType > 0 ? Auth::WRITE : Aut
h::READ ); | | return _isAuthorized( dbname , lockType > 0 ? Auth::WRITE : Aut
h::READ ); | |
| } | | } | |
| | | | |
|
| bool isAuthorizedForLevel( const string& dbname , Auth::Level level
) const { | | bool isAuthorizedForLevel( const std::string& dbname , Auth::Level
level ) const { | |
| return _isAuthorized( dbname , level ); | | return _isAuthorized( dbname , level ); | |
| } | | } | |
| | | | |
|
| string getUser( const string& dbname ) const; | | std::string getUser( const std::string& dbname ) const; | |
| | | | |
| void print() const; | | void print() const; | |
| | | | |
|
| | | BSONObj toBSON() const; | |
| | | | |
| | | void setTemporaryAuthorization( BSONObj& obj ); | |
| | | void clearTemporaryAuthorization(); | |
| | | bool hasTemporaryAuthorization(); | |
| | | | |
| | | // Returns true if this AuthenticationInfo has been auth'd to use t | |
| | | he internal user | |
| | | bool usingInternalUser(); | |
| | | | |
| | | const AuthenticationTable getAuthTable() const; | |
| | | | |
| | | // When TemporaryAuthReleaser goes out of scope it clears the tempo | |
| | | rary authentication set | |
| | | // in its AuthenticationInfo object, unless that AuthenticationInfo | |
| | | already had temporary | |
| | | // auth set at the time that the TemporaryAuthReleaser was initiali | |
| | | zed. | |
| | | class TemporaryAuthReleaser : boost::noncopyable { | |
| | | public: | |
| | | TemporaryAuthReleaser ( AuthenticationInfo* ai ); | |
| | | ~TemporaryAuthReleaser(); | |
| | | private: | |
| | | AuthenticationInfo* _ai; | |
| | | bool _hadTempAuthFromStart; | |
| | | }; | |
| | | | |
| private: | | private: | |
| void _checkLocalHostSpecialAdmin(); | | void _checkLocalHostSpecialAdmin(); | |
| | | | |
| /** takes a lock */ | | /** takes a lock */ | |
|
| bool _isAuthorized(const string& dbname, Auth::Level level) const; | | bool _isAuthorized(const std::string& dbname, Auth::Level level) co
nst; | |
| | | | |
|
| bool _isAuthorizedSingle_inlock(const string& dbname, Auth::Level l | | // Must be in _lock | |
| evel) const; | | bool _isAuthorizedSingle_inlock(const std::string& dbname, Auth::Le | |
| | | vel level) const; | |
| | | | |
| /** cannot call this locked */ | | /** cannot call this locked */ | |
|
| bool _isAuthorizedSpecialChecks( const string& dbname ) const ; | | bool _isAuthorizedSpecialChecks( const std::string& dbname ) const | |
| | | ; | |
| | | | |
| | | // Must be in _lock | |
| | | void _addTempAuth_inlock( const std::string& dbname, const std::str | |
| | | ing& user, | |
| | | Auth::Level level); | |
| | | | |
| private: | | private: | |
|
| // while most access to _dbs is from our thread (the TLS thread), c | | // while most access to _authTable is from our thread (the TLS thre | |
| urrentOp() inspects | | ad), currentOp() | |
| // it too thus we need this | | // inspects it too thus we need this. | |
| | | // This protects _authTable, _tempAuthTable, and _usingTempAuth. | |
| mutable SpinLock _lock; | | mutable SpinLock _lock; | |
| | | | |
| // todo: caching should not last forever | | // todo: caching should not last forever | |
|
| typedef map<string,Auth> MA; | | AuthenticationTable _authTable; | |
| MA _dbs; // dbname -> auth | | // when _usingTempAuth is true, this is used for all auth checks in | |
| | | stead of _authTable | |
| | | AuthenticationTable _tempAuthTable; | |
| | | | |
|
| | | bool _usingTempAuth; | |
| static bool _warned; | | static bool _warned; | |
| }; | | }; | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 21 change blocks. |
| 23 lines changed or deleted | | 63 lines changed or added | |
|
| syncclusterconnection.h | | syncclusterconnection.h | |
| | | | |
| skipping to change at line 45 | | skipping to change at line 45 | |
| * | | * | |
| * Read operations are sent to a single random node. | | * Read operations are sent to a single random node. | |
| * | | * | |
| * The class checks if a command is read or write style, and sends to a
single | | * The class checks if a command is read or write style, and sends to a
single | |
| * node if a read lock command and to all in two phases with a write st
yle command. | | * node if a read lock command and to all in two phases with a write st
yle command. | |
| */ | | */ | |
| class SyncClusterConnection : public DBClientBase { | | class SyncClusterConnection : public DBClientBase { | |
| public: | | public: | |
| | | | |
| using DBClientBase::query; | | using DBClientBase::query; | |
|
| | | using DBClientBase::update; | |
| | | using DBClientBase::remove; | |
| | | | |
| /** | | /** | |
| * @param commaSeparated should be 3 hosts comma separated | | * @param commaSeparated should be 3 hosts comma separated | |
| */ | | */ | |
| SyncClusterConnection( const list<HostAndPort> &, double socketTime
out = 0); | | SyncClusterConnection( const list<HostAndPort> &, double socketTime
out = 0); | |
| SyncClusterConnection( string commaSeparated, double socketTimeout
= 0); | | SyncClusterConnection( string commaSeparated, double socketTimeout
= 0); | |
| SyncClusterConnection( string a , string b , string c, double socke
tTimeout = 0 ); | | SyncClusterConnection( string a , string b , string c, double socke
tTimeout = 0 ); | |
| ~SyncClusterConnection(); | | ~SyncClusterConnection(); | |
| | | | |
| /** | | /** | |
| | | | |
| skipping to change at line 77 | | skipping to change at line 79 | |
| | | | |
| virtual auto_ptr<DBClientCursor> query(const string &ns, Query quer
y, int nToReturn, int nToSkip, | | virtual auto_ptr<DBClientCursor> query(const string &ns, Query quer
y, int nToReturn, int nToSkip, | |
| const BSONObj *fieldsToRetur
n, int queryOptions, int batchSize ); | | const BSONObj *fieldsToRetur
n, int queryOptions, int batchSize ); | |
| | | | |
| virtual auto_ptr<DBClientCursor> getMore( const string &ns, long lo
ng cursorId, int nToReturn, int options ); | | virtual auto_ptr<DBClientCursor> getMore( const string &ns, long lo
ng cursorId, int nToReturn, int options ); | |
| | | | |
| virtual void insert( const string &ns, BSONObj obj, int flags=0); | | virtual void insert( const string &ns, BSONObj obj, int flags=0); | |
| | | | |
| virtual void insert( const string &ns, const vector< BSONObj >& v,
int flags=0); | | virtual void insert( const string &ns, const vector< BSONObj >& v,
int flags=0); | |
| | | | |
|
| virtual void remove( const string &ns , Query query, bool justOne )
; | | virtual void remove( const string &ns , Query query, int flags ); | |
| | | | |
|
| virtual void update( const string &ns , Query query , BSONObj obj ,
bool upsert , bool multi ); | | virtual void update( const string &ns , Query query , BSONObj obj ,
int flags ); | |
| | | | |
| virtual bool call( Message &toSend, Message &response, bool assertO
k , string * actualServer ); | | virtual bool call( Message &toSend, Message &response, bool assertO
k , string * actualServer ); | |
| virtual void say( Message &toSend, bool isRetry = false , string *
actualServer = 0 ); | | virtual void say( Message &toSend, bool isRetry = false , string *
actualServer = 0 ); | |
| virtual void sayPiggyBack( Message &toSend ); | | virtual void sayPiggyBack( Message &toSend ); | |
| | | | |
| virtual void killCursor( long long cursorID ); | | virtual void killCursor( long long cursorID ); | |
| | | | |
| virtual string getServerAddress() const { return _address; } | | virtual string getServerAddress() const { return _address; } | |
| virtual bool isFailed() const { return false; } | | virtual bool isFailed() const { return false; } | |
| virtual string toString() { return _toString(); } | | virtual string toString() { return _toString(); } | |
| | | | |
| skipping to change at line 102 | | skipping to change at line 104 | |
| | | | |
| virtual bool callRead( Message& toSend , Message& response ); | | virtual bool callRead( Message& toSend , Message& response ); | |
| | | | |
| virtual ConnectionString::ConnectionType type() const { return Conn
ectionString::SYNC; } | | virtual ConnectionString::ConnectionType type() const { return Conn
ectionString::SYNC; } | |
| | | | |
| void setAllSoTimeouts( double socketTimeout ); | | void setAllSoTimeouts( double socketTimeout ); | |
| double getSoTimeout() const { return _socketTimeout; } | | double getSoTimeout() const { return _socketTimeout; } | |
| | | | |
| virtual bool auth(const string &dbname, const string &username, con
st string &password_text, string& errmsg, bool digestPassword, Auth::Level*
level=NULL); | | virtual bool auth(const string &dbname, const string &username, con
st string &password_text, string& errmsg, bool digestPassword, Auth::Level*
level=NULL); | |
| | | | |
|
| | | virtual void setAuthenticationTable( const AuthenticationTable& aut | |
| | | h ); | |
| | | virtual void clearAuthenticationTable(); | |
| | | | |
| virtual bool lazySupported() const { return false; } | | virtual bool lazySupported() const { return false; } | |
| private: | | private: | |
| SyncClusterConnection( SyncClusterConnection& prev, double socketTi
meout = 0 ); | | SyncClusterConnection( SyncClusterConnection& prev, double socketTi
meout = 0 ); | |
| string _toString() const; | | string _toString() const; | |
| bool _commandOnActive(const string &dbname, const BSONObj& cmd, BSO
NObj &info, int options=0); | | bool _commandOnActive(const string &dbname, const BSONObj& cmd, BSO
NObj &info, int options=0); | |
| auto_ptr<DBClientCursor> _queryOnActive(const string &ns, Query que
ry, int nToReturn, int nToSkip, | | auto_ptr<DBClientCursor> _queryOnActive(const string &ns, Query que
ry, int nToReturn, int nToSkip, | |
| const BSONObj *fieldsToRetu
rn, int queryOptions, int batchSize ); | | const BSONObj *fieldsToRetu
rn, int queryOptions, int batchSize ); | |
| int _lockType( const string& name ); | | int _lockType( const string& name ); | |
| void _checkLast(); | | void _checkLast(); | |
| void _connect( string host ); | | void _connect( string host ); | |
| | | | |
End of changes. 4 change blocks. |
| 2 lines changed or deleted | | 8 lines changed or added | |
|
| time_support.h | | time_support.h | |
| | | | |
| skipping to change at line 20 | | skipping to change at line 20 | |
| * | | * | |
| * Unless required by applicable law or agreed to in writing, software | | * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | | * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli
ed. | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli
ed. | |
| * See the License for the specific language governing permissions and | | * See the License for the specific language governing permissions and | |
| * limitations under the License. | | * limitations under the License. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
|
| #include "mongo/platform/basic.h" | | | |
| | | | |
| #include <cstdio> // sscanf | | | |
| #include <ctime> | | #include <ctime> | |
| #include <string> | | #include <string> | |
|
| #include <iostream> | | | |
| #include <boost/thread/thread.hpp> | | | |
| #include <boost/thread/tss.hpp> | | | |
| #include <boost/date_time/posix_time/posix_time.hpp> | | #include <boost/date_time/posix_time/posix_time.hpp> | |
|
| #include <boost/thread/xtime.hpp> | | | |
| #include "../bson/util/misc.h" // Date_t | | | |
| | | | |
|
| namespace mongo { | | #include "mongo/bson/util/misc.h" // Date_t | |
| | | | |
|
| using std::string; | | namespace mongo { | |
| | | | |
|
| inline void time_t_to_Struct(time_t t, struct tm * buf , bool local = f | | void time_t_to_Struct(time_t t, struct tm * buf , bool local = false ); | |
| alse ) { | | | |
| #if defined(_WIN32) | | | |
| if ( local ) | | | |
| localtime_s( buf , &t ); | | | |
| else | | | |
| gmtime_s(buf, &t); | | | |
| #else | | | |
| if ( local ) | | | |
| localtime_r(&t, buf); | | | |
| else | | | |
| gmtime_r(&t, buf); | | | |
| #endif | | | |
| } | | | |
| | | | |
| // uses ISO 8601 dates without trailing Z | | // uses ISO 8601 dates without trailing Z | |
| // colonsOk should be false when creating filenames | | // colonsOk should be false when creating filenames | |
|
| inline string terseCurrentTime(bool colonsOk=true) { | | std::string terseCurrentTime(bool colonsOk=true); | |
| struct tm t; | | | |
| time_t_to_Struct( time(0) , &t ); | | | |
| | | | |
| const char* fmt = (colonsOk ? "%Y-%m-%dT%H:%M:%S" : "%Y-%m-%dT%H-%M | | | |
| -%S"); | | | |
| char buf[32]; | | | |
| verify(strftime(buf, sizeof(buf), fmt, &t) == 19); | | | |
| return buf; | | | |
| } | | | |
| | | | |
| inline string timeToISOString(time_t time) { | | | |
| struct tm t; | | | |
| time_t_to_Struct( time, &t ); | | | |
| | | | |
|
| const char* fmt = "%Y-%m-%dT%H:%M:%SZ"; | | std::string timeToISOString(time_t time); | |
| char buf[32]; | | | |
| verify(strftime(buf, sizeof(buf), fmt, &t) == 20); | | | |
| return buf; | | | |
| } | | | |
| | | | |
|
| inline boost::gregorian::date currentDate() { | | boost::gregorian::date currentDate(); | |
| boost::posix_time::ptime now = boost::posix_time::second_clock::loc | | | |
| al_time(); | | | |
| return now.date(); | | | |
| } | | | |
| | | | |
| // parses time of day in "hh:mm" format assuming 'hh' is 00-23 | | // parses time of day in "hh:mm" format assuming 'hh' is 00-23 | |
|
| inline bool toPointInTime( const string& str , boost::posix_time::ptime | | bool toPointInTime( const std::string& str , boost::posix_time::ptime* | |
| * timeOfDay ) { | | timeOfDay ); | |
| int hh = 0; | | | |
| int mm = 0; | | | |
| if ( 2 != sscanf( str.c_str() , "%d:%d" , &hh , &mm ) ) { | | | |
| return false; | | | |
| } | | | |
| | | | |
| // verify that time is well formed | | | |
| if ( ( hh / 24 ) || ( mm / 60 ) ) { | | | |
| return false; | | | |
| } | | | |
| | | | |
| boost::posix_time::ptime res( currentDate() , boost::posix_time::ho | | | |
| urs( hh ) + boost::posix_time::minutes( mm ) ); | | | |
| *timeOfDay = res; | | | |
| return true; | | | |
| } | | | |
| | | | |
| #if defined(_WIN32) | | | |
| inline void sleepsecs(int s) { | | | |
| // todo : add an assert here that we are not locked in d.dbMutex. | | | |
| there may be debugging things where we | | | |
| // are but otherwise it's quite likely that would be a mista | | | |
| ke. | | | |
| Sleep(s*1000); | | | |
| } | | | |
| inline void sleepmillis(long long s) { | | | |
| verify( s <= 0xffffffff ); | | | |
| Sleep((DWORD) s); | | | |
| } | | | |
| inline void sleepmicros(long long s) { | | | |
| if ( s <= 0 ) | | | |
| return; | | | |
| boost::xtime xt; | | | |
| boost::xtime_get(&xt, boost::TIME_UTC); | | | |
| xt.sec += (int)( s / 1000000 ); | | | |
| xt.nsec += (int)(( s % 1000000 ) * 1000); | | | |
| if ( xt.nsec >= 1000000000 ) { | | | |
| xt.nsec -= 1000000000; | | | |
| xt.sec++; | | | |
| } | | | |
| boost::thread::sleep(xt); | | | |
| } | | | |
| #elif defined(__sunos__) | | | |
| inline void sleepsecs(int s) { | | | |
| boost::xtime xt; | | | |
| boost::xtime_get(&xt, boost::TIME_UTC); | | | |
| xt.sec += s; | | | |
| boost::thread::sleep(xt); | | | |
| } | | | |
| inline void sleepmillis(long long s) { | | | |
| boost::xtime xt; | | | |
| boost::xtime_get(&xt, boost::TIME_UTC); | | | |
| xt.sec += (int)( s / 1000 ); | | | |
| xt.nsec += (int)(( s % 1000 ) * 1000000); | | | |
| if ( xt.nsec >= 1000000000 ) { | | | |
| xt.nsec -= 1000000000; | | | |
| xt.sec++; | | | |
| } | | | |
| boost::thread::sleep(xt); | | | |
| } | | | |
| inline void sleepmicros(long long s) { | | | |
| if ( s <= 0 ) | | | |
| return; | | | |
| boost::xtime xt; | | | |
| boost::xtime_get(&xt, boost::TIME_UTC); | | | |
| xt.sec += (int)( s / 1000000 ); | | | |
| xt.nsec += (int)(( s % 1000000 ) * 1000); | | | |
| if ( xt.nsec >= 1000000000 ) { | | | |
| xt.nsec -= 1000000000; | | | |
| xt.sec++; | | | |
| } | | | |
| boost::thread::sleep(xt); | | | |
| } | | | |
| #else | | | |
| inline void sleepsecs(int s) { | | | |
| struct timespec t; | | | |
| t.tv_sec = s; | | | |
| t.tv_nsec = 0; | | | |
| if ( nanosleep( &t , 0 ) ) { | | | |
| std::cout << "nanosleep failed" << std::endl; | | | |
| } | | | |
| } | | | |
| inline void sleepmicros(long long s) { | | | |
| if ( s <= 0 ) | | | |
| return; | | | |
| struct timespec t; | | | |
| t.tv_sec = (int)(s / 1000000); | | | |
| t.tv_nsec = 1000 * ( s % 1000000 ); | | | |
| struct timespec out; | | | |
| if ( nanosleep( &t , &out ) ) { | | | |
| std::cout << "nanosleep failed" << std::endl; | | | |
| } | | | |
| } | | | |
| inline void sleepmillis(long long s) { | | | |
| sleepmicros( s * 1000 ); | | | |
| } | | | |
| #endif | | | |
| | | | |
|
| extern long long jsTime_virtual_skew; | | void sleepsecs(int s); | |
| extern boost::thread_specific_ptr<long long> jsTime_virtual_thread_skew | | void sleepmillis(long long ms); | |
| ; | | void sleepmicros(long long micros); | |
| | | | |
| // DO NOT TOUCH except for testing | | // DO NOT TOUCH except for testing | |
|
| inline void jsTimeVirtualSkew( long long skew ){ | | void jsTimeVirtualSkew( long long skew ); | |
| jsTime_virtual_skew = skew; | | | |
| } | | | |
| inline long long getJSTimeVirtualSkew(){ | | | |
| return jsTime_virtual_skew; | | | |
| } | | | |
| | | | |
|
| inline void jsTimeVirtualThreadSkew( long long skew ){ | | void jsTimeVirtualThreadSkew( long long skew ); | |
| jsTime_virtual_thread_skew.reset(new long long(skew)); | | long long getJSTimeVirtualThreadSkew(); | |
| } | | | |
| inline long long getJSTimeVirtualThreadSkew(){ | | | |
| if(jsTime_virtual_thread_skew.get()){ | | | |
| return *(jsTime_virtual_thread_skew.get()); | | | |
| } | | | |
| else return 0; | | | |
| } | | | |
| | | | |
| /** Date_t is milliseconds since epoch */ | | /** Date_t is milliseconds since epoch */ | |
|
| inline Date_t jsTime(); | | Date_t jsTime(); | |
| | | | |
| /** warning this will wrap */ | | /** warning this will wrap */ | |
|
| inline unsigned curTimeMicros(); | | unsigned curTimeMicros(); | |
| | | unsigned long long curTimeMicros64(); | |
| inline unsigned long long curTimeMicros64(); | | unsigned long long curTimeMillis64(); | |
| #ifdef _WIN32 // no gettimeofday on windows | | | |
| inline unsigned long long curTimeMillis64() { | | | |
| boost::xtime xt; | | | |
| boost::xtime_get(&xt, boost::TIME_UTC); | | | |
| return ((unsigned long long)xt.sec) * 1000 + xt.nsec / 1000000; | | | |
| } | | | |
| inline Date_t jsTime() { | | | |
| boost::xtime xt; | | | |
| boost::xtime_get(&xt, boost::TIME_UTC); | | | |
| unsigned long long t = xt.nsec / 1000000; | | | |
| return ((unsigned long long) xt.sec * 1000) + t + getJSTimeVirtualS | | | |
| kew() + getJSTimeVirtualThreadSkew(); | | | |
| } | | | |
| inline unsigned long long curTimeMicros64() { | | | |
| boost::xtime xt; | | | |
| boost::xtime_get(&xt, boost::TIME_UTC); | | | |
| unsigned long long t = xt.nsec / 1000; | | | |
| return (((unsigned long long) xt.sec) * 1000000) + t; | | | |
| } | | | |
| inline unsigned curTimeMicros() { | | | |
| boost::xtime xt; | | | |
| boost::xtime_get(&xt, boost::TIME_UTC); | | | |
| unsigned t = xt.nsec / 1000; | | | |
| unsigned secs = xt.sec % 1024; | | | |
| return secs*1000000 + t; | | | |
| } | | | |
| #else | | | |
| # include <sys/time.h> | | | |
| inline unsigned long long curTimeMillis64() { | | | |
| timeval tv; | | | |
| gettimeofday(&tv, NULL); | | | |
| return ((unsigned long long)tv.tv_sec) * 1000 + tv.tv_usec / 1000; | | | |
| } | | | |
| inline Date_t jsTime() { | | | |
| timeval tv; | | | |
| gettimeofday(&tv, NULL); | | | |
| unsigned long long t = tv.tv_usec / 1000; | | | |
| return ((unsigned long long) tv.tv_sec * 1000) + t + getJSTimeVirtu | | | |
| alSkew() + getJSTimeVirtualThreadSkew(); | | | |
| } | | | |
| inline unsigned long long curTimeMicros64() { | | | |
| timeval tv; | | | |
| gettimeofday(&tv, NULL); | | | |
| return (((unsigned long long) tv.tv_sec) * 1000*1000) + tv.tv_usec; | | | |
| } | | | |
| inline unsigned curTimeMicros() { | | | |
| timeval tv; | | | |
| gettimeofday(&tv, NULL); | | | |
| unsigned secs = tv.tv_sec % 1024; | | | |
| return secs*1000*1000 + tv.tv_usec; | | | |
| } | | | |
| #endif | | | |
| | | | |
| // these are so that if you use one of them compilation will fail | | // these are so that if you use one of them compilation will fail | |
| char *asctime(const struct tm *tm); | | char *asctime(const struct tm *tm); | |
| char *ctime(const time_t *timep); | | char *ctime(const time_t *timep); | |
| struct tm *gmtime(const time_t *timep); | | struct tm *gmtime(const time_t *timep); | |
| struct tm *localtime(const time_t *timep); | | struct tm *localtime(const time_t *timep); | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 15 change blocks. |
| 221 lines changed or deleted | | 18 lines changed or added | |
|
| unittest.h | | unittest.h | |
|
| // mongo/unittest/unittest.h | | | |
| | | | |
| /** | | /** | |
| * Copyright (C) 2008 10gen Inc. | | * Copyright (C) 2008 10gen Inc. | |
| * | | * | |
| * This program is free software: you can redistribute it and/or modify | | * This program is free software: you can redistribute it and/or modify | |
| * it under the terms of the GNU Affero General Public License, version 3
, | | * it under the terms of the GNU Affero General Public License, version 3
, | |
| * as published by the Free Software Foundation. | | * as published by the Free Software Foundation. | |
| * | | * | |
| * This program is distributed in the hope that it will be useful, | | * This program is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU Affero General Public License for more details. | | * GNU Affero General Public License for more details. | |
| * | | * | |
| * You should have received a copy of the GNU Affero General Public Licen
se | | * You should have received a copy of the GNU Affero General Public Licen
se | |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. | | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| */ | | */ | |
| | | | |
| /* | | /* | |
|
| | | * A C++ unit testing framework. | |
| simple portable regression system | | * | |
| | | * For examples of basic usage, see mongo/unittest/unittest_test.cpp. | |
| */ | | */ | |
| | | | |
| #include <sstream> | | #include <sstream> | |
| #include <string> | | #include <string> | |
| #include <vector> | | #include <vector> | |
| | | | |
|
| | | #include <boost/bind.hpp> | |
| | | #include <boost/function.hpp> | |
| #include <boost/noncopyable.hpp> | | #include <boost/noncopyable.hpp> | |
| #include <boost/scoped_ptr.hpp> | | #include <boost/scoped_ptr.hpp> | |
|
| | | #include <boost/shared_ptr.hpp> | |
| | | | |
| #include "mongo/util/assert_util.h" | | #include "mongo/util/assert_util.h" | |
|
| #include "mongo/util/log.h" | | #include "mongo/util/mongoutils/str.h" | |
| | | | |
|
| #define ASSERT_THROWS(a,b) \ | | /** | |
| try { \ | | * Fail unconditionally, reporting the given message. | |
| a; \ | | */ | |
| mongo::unittest::assert_fail( #a , __FILE__ , __LINE__ ); \ | | #define FAIL(MESSAGE) ::mongo::unittest::TestAssertion( __FILE__ , __LINE__ | |
| } catch ( b& ){ \ | | ).fail( (MESSAGE) ) | |
| mongo::unittest::assert_pass(); \ | | | |
| } | | | |
| | | | |
|
| #define ASSERT_EQUALS(a,b) mongo::unittest::MyAsserts( #a , #b , __FILE__ , | | /** | |
| __LINE__ ).ae( (a) , (b) ) | | * Fails unless "EXPRESSION" is true. | |
| #define ASSERT_NOT_EQUALS(a,b) mongo::unittest::MyAsserts( #a , #b , __FILE | | */ | |
| __ , __LINE__ ).nae( (a) , (b) ) | | #define ASSERT_TRUE(EXPRESSION) ::mongo::unittest::TestAssertion( __FILE__, | |
| | | __LINE__ ).failUnless( \ | |
| | | (EXPRESSION), "Expected: " #EXPRESSION ) | |
| | | #define ASSERT(EXPRESSION) ASSERT_TRUE(EXPRESSION) | |
| | | | |
|
| #define ASSERT(x) (void)( (!(!(x))) ? mongo::unittest::assert_pass() : FAIL | | /** | |
| (x) ) | | * Fails if "EXPRESSION" is true. | |
| #define FAIL(x) mongo::unittest::assert_fail( #x , __FILE__ , __LINE__ ) | | */ | |
| | | #define ASSERT_FALSE(EXPRESSION) ::mongo::unittest::TestAssertion( __FILE__ | |
| | | , __LINE__ ).failIf( \ | |
| | | (EXPRESSION), "Expected: !(" #EXPRESSION ")" ) | |
| | | | |
| | | /* | |
| | | * Binary comparison assertions. | |
| | | */ | |
| | | #define ASSERT_EQUALS(a,b) _ASSERT_COMPARISON(Equal, a, b) | |
| | | #define ASSERT_NOT_EQUALS(a,b) _ASSERT_COMPARISON(NotEqual, a, b) | |
| | | #define ASSERT_LESS_THAN(a, b) _ASSERT_COMPARISON(LessThan, a, b) | |
| | | #define ASSERT_NOT_LESS_THAN(a, b) _ASSERT_COMPARISON(NotLessThan, a, b) | |
| | | #define ASSERT_GREATER_THAN(a, b) _ASSERT_COMPARISON(GreaterThan, a, b) | |
| | | #define ASSERT_NOT_GREATER_THAN(a, b) _ASSERT_COMPARISON(NotGreaterThan, a, | |
| | | b) | |
| | | #define ASSERT_LESS_THAN_OR_EQUALS(a, b) ASSERT_NOT_GREATER_THAN(a, b) | |
| | | #define ASSERT_GREATER_THAN_OR_EQUALS(a, b) ASSERT_NOT_LESS_THAN(a, b) | |
| | | | |
| | | /** | |
| | | * Binary comparison utility macro. Do not use directly. | |
| | | */ | |
| | | #define _ASSERT_COMPARISON(COMPARISON, a, b) mongo::unittest::ComparisonAss | |
| | | ertion( \ | |
| | | #a, #b , __FILE__ , __LINE__ ).assert##COMPARISON( (a), (b) ) | |
| | | | |
| | | /** | |
| | | * Verify that the evaluation of "EXPRESSION" throws an exception of type E | |
| | | XCEPTION_TYPE. | |
| | | * | |
| | | * If "EXPRESSION" throws no exception, or one that is neither of type "EXC | |
| | | EPTION_TYPE" nor | |
| | | * of a subtype of "EXCEPTION_TYPE", the test is considered a failure and f | |
| | | urther evaluation | |
| | | * halts. | |
| | | */ | |
| | | #define ASSERT_THROWS(EXPRESSION, EXCEPTION_TYPE) \ | |
| | | do { \ | |
| | | bool threw = false; \ | |
| | | ::mongo::unittest::TestAssertion _testAssertion( __FILE__, __LINE__ | |
| | | ); \ | |
| | | try { \ | |
| | | EXPRESSION; \ | |
| | | } catch ( const EXCEPTION_TYPE& ) { threw = true; } \ | |
| | | if (!threw) \ | |
| | | _testAssertion.fail("Expected expression " #EXPRESSION \ | |
| | | " to throw " #EXCEPTION_TYPE " but it threw | |
| | | nothing."); \ | |
| | | } while( false ) | |
| | | | |
| | | /** | |
| | | * Construct a single test, named "TEST_NAME" within the test case "CASE_NA | |
| | | ME". | |
| | | * | |
| | | * Usage: | |
| | | * | |
| | | * TEST(MyModuleTests, TestThatFooFailsOnErrors) { | |
| | | * ASSERT_EQUALS(error_success, foo(invalidValue)); | |
| | | * } | |
| | | */ | |
| | | #define TEST(CASE_NAME, TEST_NAME) \ | |
| | | class _TEST_TYPE_NAME(CASE_NAME, TEST_NAME) : public ::mongo::unittest: | |
| | | :Test { \ | |
| | | private: \ | |
| | | virtual void _doTest(); \ | |
| | | \ | |
| | | static const RegistrationAgent<_TEST_TYPE_NAME(CASE_NAME, TEST_NAME | |
| | | ) > _agent; \ | |
| | | }; \ | |
| | | const ::mongo::unittest::Test::RegistrationAgent<_TEST_TYPE_NAME(CASE_N | |
| | | AME, TEST_NAME) > \ | |
| | | _TEST_TYPE_NAME(CASE_NAME, TEST_NAME)::_agent(#CASE_NAME, #TEST | |
| | | _NAME); \ | |
| | | void _TEST_TYPE_NAME(CASE_NAME, TEST_NAME)::_doTest() | |
| | | | |
| | | /** | |
| | | * Macro to construct a type name for a test, from its "CASE_NAME" and "TES | |
| | | T_NAME". | |
| | | * Do not use directly in test code. | |
| | | */ | |
| | | #define _TEST_TYPE_NAME(CASE_NAME, TEST_NAME) \ | |
| | | UnitTest__##CASE_NAME##__##TEST_NAME | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| namespace unittest { | | namespace unittest { | |
| | | | |
| class Result; | | class Result; | |
| | | | |
|
| class TestCase { | | /** | |
| | | * Type representing the function composing a test. | |
| | | */ | |
| | | typedef boost::function<void (void)> TestFunction; | |
| | | | |
| | | /** | |
| | | * Container holding a test function and its name. Suites | |
| | | * contain lists of these. | |
| | | */ | |
| | | class TestHolder : private boost::noncopyable { | |
| public: | | public: | |
|
| virtual ~TestCase(); | | TestHolder(const std::string& name, const TestFunction& fn) | |
| virtual void setUp(); | | : _name(name), _fn(fn) {} | |
| virtual void tearDown(); | | | |
| virtual void run() = 0; | | ~TestHolder() {} | |
| virtual std::string getName() = 0; | | void run() const { _fn(); } | |
| | | std::string getName() const { return _name; } | |
| | | | |
| | | private: | |
| | | std::string _name; | |
| | | TestFunction _fn; | |
| }; | | }; | |
| | | | |
|
| class Test : public TestCase { | | /** | |
| | | * Base type for unit test fixtures. Also, the default fixture typ | |
| | | e used | |
| | | * by the TEST() macro. | |
| | | * | |
| | | * TODO(schwerin): Implement a TEST_F macro that allows testers to | |
| | | specify | |
| | | * different subclasses of Test to be used as the test fixture. Th | |
| | | ese subclasses | |
| | | * could then provide per-test set-up and tear-down code by overrid | |
| | | ing the | |
| | | * setUp and tearDown methods. | |
| | | */ | |
| | | class Test : private boost::noncopyable { | |
| public: | | public: | |
| Test(); | | Test(); | |
| virtual ~Test(); | | virtual ~Test(); | |
|
| virtual std::string getName(); | | | |
| | | void run(); | |
| | | | |
| protected: | | protected: | |
|
| void setTestName( const std::string &name ); | | /** | |
| | | * Registration agent for adding tests to suites, used by TEST | |
| | | macro. | |
| | | */ | |
| | | template <typename T> | |
| | | class RegistrationAgent : private boost::noncopyable { | |
| | | public: | |
| | | RegistrationAgent(const std::string& suiteName, const std:: | |
| | | string& testName); | |
| | | }; | |
| | | | |
| private: | | private: | |
|
| std::string _name; | | /** | |
| }; | | * Called on the test object before running the test. | |
| | | */ | |
| template< class T > | | virtual void setUp(); | |
| class TestHolderBase : public TestCase { | | | |
| public: | | | |
| TestHolderBase() {} | | | |
| virtual ~TestHolderBase() {} | | | |
| virtual void run() { | | | |
| boost::scoped_ptr<T> t; | | | |
| t.reset( create() ); | | | |
| t->run(); | | | |
| } | | | |
| virtual T * create() = 0; | | | |
| virtual std::string getName() { | | | |
| return demangleName( typeid(T) ); | | | |
| } | | | |
| }; | | | |
| | | | |
|
| template< class T > | | /** | |
| class TestHolder0 : public TestHolderBase<T> { | | * Called on the test object after running the test. | |
| public: | | */ | |
| virtual T * create() { | | virtual void tearDown(); | |
| return new T(); | | | |
| } | | | |
| }; | | | |
| | | | |
|
| template< class T , typename A > | | /** | |
| class TestHolder1 : public TestHolderBase<T> { | | * The test itself. | |
| public: | | */ | |
| TestHolder1( const A& a ) : _a(a) {} | | virtual void _doTest() = 0; | |
| virtual T * create() { | | | |
| return new T( _a ); | | | |
| } | | | |
| const A _a; | | | |
| }; | | }; | |
| | | | |
|
| class Suite { | | /** | |
| | | * Representation of a collection of tests. | |
| | | * | |
| | | * One suite is constructed for each "CASE_NAME" when using the TES | |
| | | T macro. | |
| | | * Additionally, tests that are part of dbtests are manually assign | |
| | | ed to suites | |
| | | * by the programmer by overriding setupTests() in a subclass of Su | |
| | | ite. This | |
| | | * approach is deprecated. | |
| | | */ | |
| | | class Suite : private boost::noncopyable { | |
| public: | | public: | |
|
| Suite( const string &name ); | | Suite( const string& name ); | |
| virtual ~Suite(); | | virtual ~Suite(); | |
| | | | |
| template<class T> | | template<class T> | |
|
| void add() { | | void add() { add<T>(demangleName(typeid(T))); } | |
| _tests.push_back( new TestHolder0<T>() ); | | | |
| } | | | |
| | | | |
| template<class T , typename A > | | template<class T , typename A > | |
| void add( const A& a ) { | | void add( const A& a ) { | |
|
| _tests.push_back( new TestHolder1<T,A>(a) ); | | add(demangleName(typeid(T)), boost::bind(&Suite::runTestObj
ectWithArg<T, A>, a)); | |
| } | | } | |
| | | | |
|
| Result * run( const std::string& filter ); | | template<class T> | |
| | | void add(const std::string& name) { | |
| | | add(name, &Suite::runTestObject<T>); | |
| | | } | |
| | | | |
|
| static int run( const std::vector<std::string> &suites , const | | void add(const std::string& name, const TestFunction& testFn); | |
| std::string& filter ); | | | |
| | | Result * run( const std::string& filter , int runsPerTest ); | |
| | | | |
| | | static int run( const std::vector<std::string>& suites , const | |
| | | std::string& filter , int runsPerTest ); | |
| | | | |
| | | /** | |
| | | * Get a suite with the given name, creating it if necessary. | |
| | | * | |
| | | * The implementation of this function must be safe to call dur | |
| | | ing the global static | |
| | | * initialization block before main() executes. | |
| | | */ | |
| | | static Suite *getSuite(const string& name); | |
| | | | |
| protected: | | protected: | |
|
| virtual void setupTests() = 0; | | virtual void setupTests(); | |
| | | | |
| private: | | private: | |
|
| typedef std::vector<TestCase *> TestCaseList; | | typedef std::vector<TestHolder *> TestHolderList; | |
| | | | |
| | | template <typename T> | |
| | | static void runTestObject() { | |
| | | T testObj; | |
| | | testObj.run(); | |
| | | } | |
| | | | |
| | | template <typename T, typename A> | |
| | | static void runTestObjectWithArg(const A& a) { | |
| | | T testObj(a); | |
| | | testObj.run(); | |
| | | } | |
| | | | |
| std::string _name; | | std::string _name; | |
|
| TestCaseList _tests; | | TestHolderList _tests; | |
| bool _ran; | | bool _ran; | |
| | | | |
|
| void registerSuite( const std::string &name , Suite *s ); | | void registerSuite( const std::string& name , Suite* s ); | |
| }; | | }; | |
| | | | |
|
| void assert_pass(); | | /** | |
| void assert_fail( const char * exp , const char * file , unsigned l | | * Collection of information about failed tests. Used in reporting | |
| ine ); | | * failures. | |
| | | */ | |
| | | class TestAssertionFailureDetails : private boost::noncopyable { | |
| | | public: | |
| | | TestAssertionFailureDetails( const std::string& theFile, | |
| | | unsigned theLine, | |
| | | const std::string& theMessage ); | |
| | | | |
|
| class MyAssertionException : private boost::noncopyable { | | const std::string file; | |
| | | const unsigned line; | |
| | | const std::string message; | |
| | | }; | |
| | | | |
| | | /** | |
| | | * Exception thrown when a test assertion fails. | |
| | | * | |
| | | * Typically thrown by helpers in the TestAssertion class and its i | |
| | | lk, below. | |
| | | * | |
| | | * NOTE(schwerin): This intentionally does _not_ extend std::except | |
| | | ion, so that code under | |
| | | * test that (foolishly?) catches std::exception won't swallow test | |
| | | failures. Doesn't | |
| | | * protect you from code that foolishly catches ..., but you do wha | |
| | | t you can. | |
| | | */ | |
| | | class TestAssertionFailureException { | |
| public: | | public: | |
|
| MyAssertionException() { | | TestAssertionFailureException( const std::string& theFile, | |
| ss << "assertion: "; | | unsigned theLine, | |
| } | | const std::string& theMessage ); | |
| std::stringstream ss; | | | |
| | | const std::string& getFile() const { return _details->file; } | |
| | | unsigned getLine() const { return _details->line; } | |
| | | const std::string& getMessage() const { return _details->messag | |
| | | e; } | |
| | | | |
| | | std::string toString() const; | |
| | | | |
| | | private: | |
| | | boost::shared_ptr<TestAssertionFailureDetails> _details; | |
| }; | | }; | |
| | | | |
|
| class MyAsserts : private boost::noncopyable { | | /** | |
| | | * Object representing an assertion about some condition. | |
| | | */ | |
| | | class TestAssertion : private boost::noncopyable { | |
| | | | |
| public: | | public: | |
|
| MyAsserts( const char * aexp , const char * bexp , const char * | | TestAssertion( const std::string& file, unsigned line ); | |
| file , unsigned line ) | | ~TestAssertion(); | |
| : _aexp( aexp ) , _bexp( bexp ) , _file( file ) , _line( li | | | |
| ne ) { | | | |
| | | | |
|
| | | void fail( const std::string& message) const; | |
| | | void failIf( bool flag, const std::string &message ) const { | |
| | | if ( flag ) fail( message ); | |
| | | } | |
| | | void failUnless( bool flag, const std::string& message ) const | |
| | | { | |
| | | failIf( !flag, message ); | |
| } | | } | |
| | | | |
|
| template<typename A,typename B> | | private: | |
| void ae( const A &a , const B &b ) { | | const std::string _file; | |
| _gotAssert(); | | const unsigned _line; | |
| if ( a == b ) | | }; | |
| return; | | | |
| | | | |
|
| printLocation(); | | /** | |
| | | * Specialization of TestAssertion for binary comparisons. | |
| | | */ | |
| | | class ComparisonAssertion : private TestAssertion { | |
| | | public: | |
| | | ComparisonAssertion( const std::string& aexp , const std::strin | |
| | | g& bexp , | |
| | | const std::string& file , unsigned line ); | |
| | | | |
|
| MyAssertionException * e = getBase(); | | template<typename A,typename B> | |
| e->ss << a << " != " << b << std::endl; | | void assertEqual( const A& a , const B& b ) { | |
| log() << e->ss.str() << std::endl; | | failUnless(a == b, getComparisonFailureMessage("==", a, b)) | |
| throw e; | | ; | |
| } | | } | |
| | | | |
| template<typename A,typename B> | | template<typename A,typename B> | |
|
| void nae( const A &a , const B &b ) { | | void assertNotEqual( const A& a , const B& b ) { | |
| _gotAssert(); | | failUnless(a != b, getComparisonFailureMessage("!=", a, b)) | |
| if ( a != b ) | | ; | |
| return; | | } | |
| | | | |
| printLocation(); | | | |
| | | | |
|
| MyAssertionException * e = getBase(); | | template<typename A,typename B> | |
| e->ss << a << " == " << b << std::endl; | | void assertLessThan( const A& a , const B& b ) { | |
| log() << e->ss.str() << std::endl; | | failUnless(a < b, getComparisonFailureMessage("<", a, b)); | |
| throw e; | | | |
| } | | } | |
| | | | |
|
| void printLocation(); | | template<typename A,typename B> | |
| | | void assertNotLessThan( const A& a , const B& b ) { | |
| | | failUnless(a >= b, getComparisonFailureMessage(">=", a, b)) | |
| | | ; | |
| | | } | |
| | | | |
|
| private: | | template<typename A,typename B> | |
| | | void assertGreaterThan( const A& a , const B& b ) { | |
| | | failUnless(a > b, getComparisonFailureMessage(">", a, b)); | |
| | | } | |
| | | | |
|
| void _gotAssert(); | | template<typename A,typename B> | |
| | | void assertNotGreaterThan( const A& a , const B& b ) { | |
| | | failUnless(a <= b, getComparisonFailureMessage("<=", a, b)) | |
| | | ; | |
| | | } | |
| | | | |
|
| MyAssertionException * getBase(); | | private: | |
| | | template< typename A, typename B> | |
| | | std::string getComparisonFailureMessage(const std::string &theO | |
| | | perator, | |
| | | const A& a, const B& b) | |
| | | ; | |
| | | | |
| std::string _aexp; | | std::string _aexp; | |
| std::string _bexp; | | std::string _bexp; | |
|
| std::string _file; | | | |
| unsigned _line; | | | |
| }; | | }; | |
| | | | |
| /** | | /** | |
|
| * Returns the name of the currently executing unit test | | * Hack to support the runaway test observer in dbtests. This is a | |
| | | hook that | |
| | | * unit test running harnesses (unittest_main and dbtests) must imp | |
| | | lement. | |
| */ | | */ | |
|
| std::string getExecutingTestName(); | | void onCurrentTestNameChange( const std::string& testName ); | |
| | | | |
| /** | | /** | |
| * Return a list of suite names. | | * Return a list of suite names. | |
| */ | | */ | |
| std::vector<std::string> getAllSuiteNames(); | | std::vector<std::string> getAllSuiteNames(); | |
| | | | |
| } // namespace unittest | | } // namespace unittest | |
| } // namespace mongo | | } // namespace mongo | |
|
| | | | |
| | | #include "mongo/unittest/unittest-inl.h" | |
| | | | |
End of changes. 45 change blocks. |
| 115 lines changed or deleted | | 314 lines changed or added | |
|
| update.h | | update.h | |
|
| // update.h | | //@file update.h | |
| | | | |
| /** | | /** | |
| * Copyright (C) 2008 10gen Inc. | | * Copyright (C) 2008 10gen Inc. | |
| * | | * | |
| * This program is free software: you can redistribute it and/or modify | | * This program is free software: you can redistribute it and/or modify | |
| * it under the terms of the GNU Affero General Public License, version
3, | | * it under the terms of the GNU Affero General Public License, version
3, | |
| * as published by the Free Software Foundation. | | * as published by the Free Software Foundation. | |
| * | | * | |
| * This program is distributed in the hope that it will be useful, | | * This program is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU Affero General Public License for more details. | | * GNU Affero General Public License for more details. | |
| * | | * | |
| * You should have received a copy of the GNU Affero General Public Lice
nse | | * You should have received a copy of the GNU Affero General Public Lice
nse | |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. | | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| */ | | */ | |
| | | | |
|
| #include "../../pch.h" | | #include "pch.h" | |
| #include "../jsobj.h" | | | |
| #include "../../util/embedded_builder.h" | | #include "mongo/db/jsobj.h" | |
| #include "../../util/stringutils.h" | | #include "mongo/db/curop.h" | |
| #include "../matcher.h" | | #include "mongo/db/queryoptimizercursor.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| // ---------- public ------------- | | // ---------- public ------------- | |
| | | | |
| struct UpdateResult { | | struct UpdateResult { | |
| const bool existing; // if existing objects were modified | | const bool existing; // if existing objects were modified | |
| const bool mod; // was this a $ mod | | const bool mod; // was this a $ mod | |
| const long long num; // how many objects touched | | const long long num; // how many objects touched | |
| OID upserted; // if something was upserted, the new _id of the obj
ect | | OID upserted; // if something was upserted, the new _id of the obj
ect | |
| | | | |
|
| UpdateResult( bool e, bool m, unsigned long long n , const BSONObj&
upsertedObject = BSONObj() ) | | UpdateResult( bool e, bool m, unsigned long long n , const BSONObj&
upsertedObject ) | |
| : existing(e) , mod(m), num(n) { | | : existing(e) , mod(m), num(n) { | |
| upserted.clear(); | | upserted.clear(); | |
| BSONElement id = upsertedObject["_id"]; | | BSONElement id = upsertedObject["_id"]; | |
| if ( ! e && n == 1 && id.type() == jstOID ) { | | if ( ! e && n == 1 && id.type() == jstOID ) { | |
| upserted = id.OID(); | | upserted = id.OID(); | |
| } | | } | |
| } | | } | |
| }; | | }; | |
| | | | |
| class RemoveSaver; | | class RemoveSaver; | |
| | | | |
| /* returns true if an existing object was updated, false if no existing
object was found. | | /* returns true if an existing object was updated, false if no existing
object was found. | |
| multi - update multiple objects - mostly useful with things like $se
t | | multi - update multiple objects - mostly useful with things like $se
t | |
|
| god - allow access to system namespaces | | su - allow access to system namespaces (super user) | |
| */ | | */ | |
|
| UpdateResult updateObjects(const char *ns, | | UpdateResult updateObjects(const char* ns, | |
| const BSONObj& updateobj, | | const BSONObj& updateobj, | |
|
| BSONObj pattern, | | const BSONObj& pattern, | |
| bool upsert, | | bool upsert, | |
|
| bool multi , | | bool multi, | |
| bool logop , | | bool logop, | |
| OpDebug& debug, | | OpDebug& debug, | |
| bool fromMigrate = false, | | bool fromMigrate = false, | |
|
| const QueryPlanSelectionPolicy &planPolicy = | | const QueryPlanSelectionPolicy& planPolicy = | |
| QueryPlanSelectionPolicy::any()); | | QueryPlanSelectionPolicy::any()); | |
| UpdateResult _updateObjects(bool god, | | | |
| const char *ns, | | UpdateResult _updateObjects(bool su, | |
| | | const char* ns, | |
| const BSONObj& updateobj, | | const BSONObj& updateobj, | |
|
| BSONObj pattern, | | const BSONObj& pattern, | |
| bool upsert, | | bool upsert, | |
|
| bool multi , | | bool multi, | |
| bool logop , | | bool logop, | |
| OpDebug& debug , | | OpDebug& debug, | |
| RemoveSaver * rs = 0, | | RemoveSaver* rs = 0, | |
| bool fromMigrate = false, | | bool fromMigrate = false, | |
|
| const QueryPlanSelectionPolicy &planPolicy | | const QueryPlanSelectionPolicy& planPolicy | |
| = | | = QueryPlanSelectionPolicy::any()); | |
| QueryPlanSelectionPolicy::any()); | | | |
| | | | |
| // ---------- private ------------- | | | |
| | | | |
| class ModState; | | | |
| class ModSetState; | | | |
| | | | |
| /* Used for modifiers such as $inc, $set, $push, ... | | | |
| * stores the info about a single operation | | | |
| * once created should never be modified | | | |
| */ | | | |
| struct Mod { | | | |
| // See opFromStr below | | | |
| // 0 1 2 3 4 5 6 7 8 | | | |
| 9 10 11 12 13 | | | |
| enum Op { INC, SET, PUSH, PUSH_ALL, PULL, PULL_ALL , POP, UNSET, BI | | | |
| TAND, BITOR , BIT , ADDTOSET, RENAME_FROM, RENAME_TO } op; | | | |
| | | | |
| static const char* modNames[]; | | | |
| static unsigned modNamesNum; | | | |
| | | | |
| const char *fieldName; | | | |
| const char *shortFieldName; | | | |
| | | | |
| BSONElement elt; // x:5 note: this is the actual element from the u | | | |
| pdateobj | | | |
| boost::shared_ptr<Matcher> matcher; | | | |
| bool matcherOnPrimitive; | | | |
| | | | |
| void init( Op o , BSONElement& e ) { | | | |
| op = o; | | | |
| elt = e; | | | |
| if ( op == PULL && e.type() == Object ) { | | | |
| BSONObj t = e.embeddedObject(); | | | |
| if ( t.firstElement().getGtLtOp() == 0 ) { | | | |
| matcher.reset( new Matcher( t ) ); | | | |
| matcherOnPrimitive = false; | | | |
| } | | | |
| else { | | | |
| matcher.reset( new Matcher( BSON( "" << t ) ) ); | | | |
| matcherOnPrimitive = true; | | | |
| } | | | |
| } | | | |
| } | | | |
| | | | |
| void setFieldName( const char * s ) { | | | |
| fieldName = s; | | | |
| shortFieldName = strrchr( fieldName , '.' ); | | | |
| if ( shortFieldName ) | | | |
| shortFieldName++; | | | |
| else | | | |
| shortFieldName = fieldName; | | | |
| } | | | |
| | | | |
| /** | | | |
| * @param in incrememnts the actual value inside in | | | |
| */ | | | |
| void incrementMe( BSONElement& in ) const { | | | |
| BSONElementManipulator manip( in ); | | | |
| switch ( in.type() ) { | | | |
| case NumberDouble: | | | |
| manip.setNumber( elt.numberDouble() + in.numberDouble() ); | | | |
| break; | | | |
| case NumberLong: | | | |
| manip.setLong( elt.numberLong() + in.numberLong() ); | | | |
| break; | | | |
| case NumberInt: | | | |
| manip.setInt( elt.numberInt() + in.numberInt() ); | | | |
| break; | | | |
| default: | | | |
| verify(0); | | | |
| } | | | |
| } | | | |
| void IncrementMe( BSONElement& in ) const { | | | |
| BSONElementManipulator manip( in ); | | | |
| switch ( in.type() ) { | | | |
| case NumberDouble: | | | |
| manip.SetNumber( elt.numberDouble() + in.numberDouble() ); | | | |
| break; | | | |
| case NumberLong: | | | |
| manip.SetLong( elt.numberLong() + in.numberLong() ); | | | |
| break; | | | |
| case NumberInt: | | | |
| manip.SetInt( elt.numberInt() + in.numberInt() ); | | | |
| break; | | | |
| default: | | | |
| verify(0); | | | |
| } | | | |
| } | | | |
| | | | |
| template< class Builder > | | | |
| void appendIncremented( Builder& bb , const BSONElement& in, ModSta | | | |
| te& ms ) const; | | | |
| | | | |
| bool operator<( const Mod &other ) const { | | | |
| return strcmp( fieldName, other.fieldName ) < 0; | | | |
| } | | | |
| | | | |
| bool arrayDep() const { | | | |
| switch (op) { | | | |
| case PUSH: | | | |
| case PUSH_ALL: | | | |
| case POP: | | | |
| return true; | | | |
| default: | | | |
| return false; | | | |
| } | | | |
| } | | | |
| | | | |
| static bool isIndexed( const string& fullName , const set<string>& | | | |
| idxKeys ) { | | | |
| const char * fieldName = fullName.c_str(); | | | |
| // check if there is an index key that is a parent of mod | | | |
| for( const char *dot = strchr( fieldName, '.' ); dot; dot = str | | | |
| chr( dot + 1, '.' ) ) | | | |
| if ( idxKeys.count( string( fieldName, dot - fieldName ) ) | | | |
| ) | | | |
| return true; | | | |
| | | | |
| // check if there is an index key equal to mod | | | |
| if ( idxKeys.count(fullName) ) | | | |
| return true; | | | |
| // check if there is an index key that is a child of mod | | | |
| set< string >::const_iterator j = idxKeys.upper_bound( fullName | | | |
| ); | | | |
| if ( j != idxKeys.end() && j->find( fullName ) == 0 && (*j)[ful | | | |
| lName.size()] == '.' ) | | | |
| return true; | | | |
| | | | |
| return false; | | | |
| } | | | |
| | | | |
| bool isIndexed( const set<string>& idxKeys ) const { | | | |
| string fullName = fieldName; | | | |
| | | | |
| if ( isIndexed( fullName , idxKeys ) ) | | | |
| return true; | | | |
| | | | |
| if ( strstr( fieldName , "." ) ) { | | | |
| // check for a.0.1 | | | |
| StringBuilder buf; | | | |
| for ( size_t i=0; i<fullName.size(); i++ ) { | | | |
| char c = fullName[i]; | | | |
| | | | |
| if ( c == '$' && | | | |
| i > 0 && fullName[i-1] == '.' && | | | |
| i+1<fullName.size() && | | | |
| fullName[i+1] == '.' ) { | | | |
| i++; | | | |
| continue; | | | |
| } | | | |
| | | | |
| buf << c; | | | |
| | | | |
| if ( c != '.' ) | | | |
| continue; | | | |
| | | | |
| if ( ! isdigit( fullName[i+1] ) ) | | | |
| continue; | | | |
| | | | |
| bool possible = true; | | | |
| size_t j=i+2; | | | |
| for ( ; j<fullName.size(); j++ ) { | | | |
| char d = fullName[j]; | | | |
| if ( d == '.' ) | | | |
| break; | | | |
| if ( isdigit( d ) ) | | | |
| continue; | | | |
| possible = false; | | | |
| break; | | | |
| } | | | |
| | | | |
| if ( possible ) | | | |
| i = j; | | | |
| } | | | |
| string x = buf.str(); | | | |
| if ( isIndexed( x , idxKeys ) ) | | | |
| return true; | | | |
| } | | | |
| | | | |
| return false; | | | |
| } | | | |
| | | | |
| template< class Builder > | | | |
| void apply( Builder& b , BSONElement in , ModState& ms ) const; | | | |
| | | | |
| /** | | | |
| * @return true iff toMatch should be removed from the array | | | |
| */ | | | |
| bool _pullElementMatch( BSONElement& toMatch ) const; | | | |
| | | | |
| void _checkForAppending( const BSONElement& e ) const { | | | |
| if ( e.type() == Object ) { | | | |
| // this is a tiny bit slow, but rare and important | | | |
| // only when setting something TO an object, not setting so | | | |
| mething in an object | | | |
| // and it checks for { $set : { x : { 'a.b' : 1 } } } | | | |
| // which is feel has been common | | | |
| uassert( 12527 , "not okForStorage" , e.embeddedObject().ok | | | |
| ForStorage() ); | | | |
| } | | | |
| } | | | |
| | | | |
| bool isEach() const { | | | |
| if ( elt.type() != Object ) | | | |
| return false; | | | |
| BSONElement e = elt.embeddedObject().firstElement(); | | | |
| if ( e.type() != Array ) | | | |
| return false; | | | |
| return strcmp( e.fieldName() , "$each" ) == 0; | | | |
| } | | | |
| | | | |
| BSONObj getEach() const { | | | |
| return elt.embeddedObjectUserCheck().firstElement().embeddedObj | | | |
| ectUserCheck(); | | | |
| } | | | |
| | | | |
| void parseEach( BSONElementSet& s ) const { | | | |
| BSONObjIterator i(getEach()); | | | |
| while ( i.more() ) { | | | |
| s.insert( i.next() ); | | | |
| } | | | |
| } | | | |
| | | | |
| const char *renameFrom() const { | | | |
| massert( 13492, "mod must be RENAME_TO type", op == Mod::RENAME | | | |
| _TO ); | | | |
| return elt.fieldName(); | | | |
| } | | | |
| }; | | | |
| | | | |
| /** | | | |
| * stores a set of Mods | | | |
| * once created, should never be changed | | | |
| */ | | | |
| class ModSet : boost::noncopyable { | | | |
| typedef map<string,Mod> ModHolder; | | | |
| ModHolder _mods; | | | |
| int _isIndexed; | | | |
| bool _hasDynamicArray; | | | |
| | | | |
| static Mod::Op opFromStr( const char *fn ) { | | | |
| verify( fn[0] == '$' ); | | | |
| switch( fn[1] ) { | | | |
| case 'i': { | | | |
| if ( fn[2] == 'n' && fn[3] == 'c' && fn[4] == 0 ) | | | |
| return Mod::INC; | | | |
| break; | | | |
| } | | | |
| case 's': { | | | |
| if ( fn[2] == 'e' && fn[3] == 't' && fn[4] == 0 ) | | | |
| return Mod::SET; | | | |
| break; | | | |
| } | | | |
| case 'p': { | | | |
| if ( fn[2] == 'u' ) { | | | |
| if ( fn[3] == 's' && fn[4] == 'h' ) { | | | |
| if ( fn[5] == 0 ) | | | |
| return Mod::PUSH; | | | |
| if ( fn[5] == 'A' && fn[6] == 'l' && fn[7] == 'l' & | | | |
| & fn[8] == 0 ) | | | |
| return Mod::PUSH_ALL; | | | |
| } | | | |
| else if ( fn[3] == 'l' && fn[4] == 'l' ) { | | | |
| if ( fn[5] == 0 ) | | | |
| return Mod::PULL; | | | |
| if ( fn[5] == 'A' && fn[6] == 'l' && fn[7] == 'l' & | | | |
| & fn[8] == 0 ) | | | |
| return Mod::PULL_ALL; | | | |
| } | | | |
| } | | | |
| else if ( fn[2] == 'o' && fn[3] == 'p' && fn[4] == 0 ) | | | |
| return Mod::POP; | | | |
| break; | | | |
| } | | | |
| case 'u': { | | | |
| if ( fn[2] == 'n' && fn[3] == 's' && fn[4] == 'e' && fn[5] | | | |
| == 't' && fn[6] == 0 ) | | | |
| return Mod::UNSET; | | | |
| break; | | | |
| } | | | |
| case 'b': { | | | |
| if ( fn[2] == 'i' && fn[3] == 't' ) { | | | |
| if ( fn[4] == 0 ) | | | |
| return Mod::BIT; | | | |
| if ( fn[4] == 'a' && fn[5] == 'n' && fn[6] == 'd' && fn | | | |
| [7] == 0 ) | | | |
| return Mod::BITAND; | | | |
| if ( fn[4] == 'o' && fn[5] == 'r' && fn[6] == 0 ) | | | |
| return Mod::BITOR; | | | |
| } | | | |
| break; | | | |
| } | | | |
| case 'a': { | | | |
| if ( fn[2] == 'd' && fn[3] == 'd' ) { | | | |
| // add | | | |
| if ( fn[4] == 'T' && fn[5] == 'o' && fn[6] == 'S' && fn | | | |
| [7] == 'e' && fn[8] == 't' && fn[9] == 0 ) | | | |
| return Mod::ADDTOSET; | | | |
| | | | |
| } | | | |
| break; | | | |
| } | | | |
| case 'r': { | | | |
| if ( fn[2] == 'e' && fn[3] == 'n' && fn[4] == 'a' && fn[5] | | | |
| == 'm' && fn[6] =='e' ) { | | | |
| return Mod::RENAME_TO; // with this return code we hand | | | |
| le both RENAME_TO and RENAME_FROM | | | |
| } | | | |
| break; | | | |
| } | | | |
| default: break; | | | |
| } | | | |
| uassert( 10161 , "Invalid modifier specified " + string( fn ), | | | |
| false ); | | | |
| return Mod::INC; | | | |
| } | | | |
| | | | |
| ModSet() {} | | | |
| | | | |
| void updateIsIndexed( const Mod &m, const set<string> &idxKeys, con | | | |
| st set<string> *backgroundKeys ) { | | | |
| if ( m.isIndexed( idxKeys ) || | | | |
| (backgroundKeys && m.isIndexed(*backgroundKeys)) ) { | | | |
| _isIndexed++; | | | |
| } | | | |
| } | | | |
| | | | |
| public: | | | |
| | | | |
| ModSet( const BSONObj &from , | | | |
| const set<string>& idxKeys = set<string>(), | | | |
| const set<string>* backgroundKeys = 0 | | | |
| ); | | | |
| | | | |
| // TODO: this is inefficient - should probably just handle when ite | | | |
| rating | | | |
| ModSet * fixDynamicArray( const string &elemMatchKey ) const; | | | |
| | | | |
| bool hasDynamicArray() const { return _hasDynamicArray; } | | | |
| | | | |
| /** | | | |
| * creates a ModSetState suitable for operation on obj | | | |
| * doesn't change or modify this ModSet or any underying Mod | | | |
| */ | | | |
| auto_ptr<ModSetState> prepare( const BSONObj& obj ) const; | | | |
| | | | |
| /** | | | |
| * given a query pattern, builds an object suitable for an upsert | | | |
| * will take the query spec and combine all $ operators | | | |
| */ | | | |
| BSONObj createNewFromQuery( const BSONObj& query ); | | | |
| | | | |
| /** | | | |
| * | | | |
| */ | | | |
| int isIndexed() const { | | | |
| return _isIndexed; | | | |
| } | | | |
| | | | |
| unsigned size() const { return _mods.size(); } | | | |
| | | | |
| bool haveModForField( const char *fieldName ) const { | | | |
| return _mods.find( fieldName ) != _mods.end(); | | | |
| } | | | |
| | | | |
| bool haveConflictingMod( const string& fieldName ) { | | | |
| size_t idx = fieldName.find( '.' ); | | | |
| if ( idx == string::npos ) | | | |
| idx = fieldName.size(); | | | |
| | | | |
| ModHolder::const_iterator start = _mods.lower_bound(fieldName.s | | | |
| ubstr(0,idx)); | | | |
| for ( ; start != _mods.end(); start++ ) { | | | |
| FieldCompareResult r = compareDottedFieldNames( fieldName , | | | |
| start->first , | | | |
| LexNumCmp( t | | | |
| rue ) ); | | | |
| switch ( r ) { | | | |
| case LEFT_SUBFIELD: return true; | | | |
| case LEFT_BEFORE: return false; | | | |
| case SAME: return true; | | | |
| case RIGHT_BEFORE: return false; | | | |
| case RIGHT_SUBFIELD: return true; | | | |
| } | | | |
| } | | | |
| return false; | | | |
| | | | |
| } | | | |
| | | | |
| }; | | | |
| | | | |
| /** | | | |
| * stores any information about a single Mod operating on a single Obje | | | |
| ct | | | |
| */ | | | |
| class ModState : boost::noncopyable { | | | |
| public: | | | |
| const Mod * m; | | | |
| BSONElement old; | | | |
| BSONElement newVal; | | | |
| BSONObj _objData; | | | |
| | | | |
| const char * fixedOpName; | | | |
| BSONElement * fixed; | | | |
| int pushStartSize; | | | |
| | | | |
| BSONType incType; | | | |
| int incint; | | | |
| double incdouble; | | | |
| long long inclong; | | | |
| | | | |
| bool dontApply; | | | |
| | | | |
| ModState() { | | | |
| fixedOpName = 0; | | | |
| fixed = 0; | | | |
| pushStartSize = -1; | | | |
| incType = EOO; | | | |
| dontApply = false; | | | |
| } | | | |
| | | | |
| Mod::Op op() const { | | | |
| return m->op; | | | |
| } | | | |
| | | | |
| const char * fieldName() const { | | | |
| return m->fieldName; | | | |
| } | | | |
| | | | |
| bool needOpLogRewrite() const { | | | |
| if ( dontApply ) | | | |
| return false; | | | |
| | | | |
| if ( fixed || fixedOpName || incType ) | | | |
| return true; | | | |
| | | | |
| switch( op() ) { | | | |
| case Mod::RENAME_FROM: | | | |
| case Mod::RENAME_TO: | | | |
| return true; | | | |
| case Mod::BIT: | | | |
| case Mod::BITAND: | | | |
| case Mod::BITOR: | | | |
| // TODO: should we convert this to $set? | | | |
| return false; | | | |
| default: | | | |
| return false; | | | |
| } | | | |
| } | | | |
| | | | |
| void appendForOpLog( BSONObjBuilder& b ) const; | | | |
| | | | |
| template< class Builder > | | | |
| void apply( Builder& b , BSONElement in ) { | | | |
| m->apply( b , in , *this ); | | | |
| } | | | |
| | | | |
| template< class Builder > | | | |
| void appendIncValue( Builder& b , bool useFullName ) const { | | | |
| const char * n = useFullName ? m->fieldName : m->shortFieldName | | | |
| ; | | | |
| | | | |
| switch ( incType ) { | | | |
| case NumberDouble: | | | |
| b.append( n , incdouble ); break; | | | |
| case NumberLong: | | | |
| b.append( n , inclong ); break; | | | |
| case NumberInt: | | | |
| b.append( n , incint ); break; | | | |
| default: | | | |
| verify(0); | | | |
| } | | | |
| } | | | |
| | | | |
| string toString() const; | | | |
| | | | |
| template< class Builder > | | | |
| void handleRename( Builder &newObjBuilder, const char *shortFieldNa | | | |
| me ); | | | |
| }; | | | |
| | | | |
| /** | | /** | |
|
| * this is used to hold state, meta data while applying a ModSet to a B | | * takes the from document and returns a new document | |
| SONObj | | * after apply all the operators | |
| * the goal is to make ModSet const so its re-usable | | * e.g. | |
| | | * applyUpdateOperators( BSON( "x" << 1 ) , BSON( "$inc" << BSON( "x" | |
| | | << 1 ) ) ); | |
| | | * returns: { x : 2 } | |
| */ | | */ | |
|
| class ModSetState : boost::noncopyable { | | BSONObj applyUpdateOperators( const BSONObj& from, const BSONObj& opera | |
| typedef map<string,shared_ptr<ModState>,LexNumCmp> ModStateHolder; | | tors ); | |
| typedef pair<const ModStateHolder::iterator,const ModStateHolder::i | | | |
| terator> ModStateRange; | | | |
| const BSONObj& _obj; | | | |
| ModStateHolder _mods; | | | |
| bool _inPlacePossible; | | | |
| BSONObj _newFromMods; // keep this data alive, as oplog generation | | | |
| may depend on it | | | |
| | | | |
| ModSetState( const BSONObj& obj ) | | | |
| : _obj( obj ) , _mods( LexNumCmp( true ) ) , _inPlacePossible(t | | | |
| rue) { | | | |
| } | | | |
| | | | |
| /** | | | |
| * @return if in place is still possible | | | |
| */ | | | |
| bool amIInPlacePossible( bool inPlacePossible ) { | | | |
| if ( ! inPlacePossible ) | | | |
| _inPlacePossible = false; | | | |
| return _inPlacePossible; | | | |
| } | | | |
| | | | |
| ModStateRange modsForRoot( const string &root ); | | | |
| | | | |
| void createNewObjFromMods( const string &root, BSONObjBuilder &b, c | | | |
| onst BSONObj &obj ); | | | |
| void createNewArrayFromMods( const string &root, BSONArrayBuilder & | | | |
| b, | | | |
| const BSONArray &arr ); | | | |
| | | | |
| template< class Builder > | | | |
| void createNewFromMods( const string& root , Builder& b , BSONItera | | | |
| torSorted& es , | | | |
| const ModStateRange& modRange , const LexNum | | | |
| Cmp& lexNumCmp ); | | | |
| | | | |
| template< class Builder > | | | |
| void _appendNewFromMods( const string& root , ModState& m , Builder | | | |
| & b , set<string>& onedownseen ); | | | |
| | | | |
| template< class Builder > | | | |
| void appendNewFromMod( ModState& ms , Builder& b ) { | | | |
| if ( ms.dontApply ) { | | | |
| return; | | | |
| } | | | |
| | | | |
| //const Mod& m = *(ms.m); // HACK | | | |
| Mod& m = *((Mod*)(ms.m)); // HACK | | | |
| | | | |
| switch ( m.op ) { | | | |
| | | | |
| case Mod::PUSH: { | | | |
| if ( m.isEach() ) { | | | |
| b.appendArray( m.shortFieldName, m.getEach() ); | | | |
| } else { | | | |
| BSONObjBuilder arr( b.subarrayStart( m.shortFieldName ) | | | |
| ); | | | |
| arr.appendAs( m.elt, "0" ); | | | |
| arr.done(); | | | |
| } | | | |
| break; | | | |
| } | | | |
| case Mod::ADDTOSET: { | | | |
| if ( m.isEach() ) { | | | |
| // Remove any duplicates in given array | | | |
| BSONObjBuilder arr( b.subarrayStart( m.shortFieldName ) | | | |
| ); | | | |
| BSONElementSet toadd; | | | |
| m.parseEach( toadd ); | | | |
| BSONObjIterator i( m.getEach() ); | | | |
| int n = 0; | | | |
| while ( i.more() ) { | | | |
| BSONElement e = i.next(); | | | |
| if ( toadd.count(e) ) { | | | |
| arr.appendAs( e , BSONObjBuilder::numStr( n++ ) | | | |
| ); | | | |
| toadd.erase( e ); | | | |
| } | | | |
| } | | | |
| arr.done(); | | | |
| } | | | |
| else { | | | |
| BSONObjBuilder arr( b.subarrayStart( m.shortFieldName ) | | | |
| ); | | | |
| arr.appendAs( m.elt, "0" ); | | | |
| arr.done(); | | | |
| } | | | |
| break; | | | |
| } | | | |
| | | | |
| case Mod::PUSH_ALL: { | | | |
| b.appendAs( m.elt, m.shortFieldName ); | | | |
| break; | | | |
| } | | | |
| | | | |
| case Mod::UNSET: | | | |
| case Mod::PULL: | | | |
| case Mod::PULL_ALL: | | | |
| // no-op b/c unset/pull of nothing does nothing | | | |
| break; | | | |
| | | | |
| case Mod::INC: | | | |
| ms.fixedOpName = "$set"; | | | |
| case Mod::SET: { | | | |
| m._checkForAppending( m.elt ); | | | |
| b.appendAs( m.elt, m.shortFieldName ); | | | |
| break; | | | |
| } | | | |
| // shouldn't see RENAME_FROM here | | | |
| case Mod::RENAME_TO: | | | |
| ms.handleRename( b, m.shortFieldName ); | | | |
| break; | | | |
| default: | | | |
| stringstream ss; | | | |
| ss << "unknown mod in appendNewFromMod: " << m.op; | | | |
| throw UserException( 9015, ss.str() ); | | | |
| } | | | |
| | | | |
| } | | | |
| | | | |
| /** @return true iff the elements aren't eoo(), are distinct, and s | | | |
| hare a field name. */ | | | |
| static bool duplicateFieldName( const BSONElement &a, const BSONEle | | | |
| ment &b ); | | | |
| | | | |
| public: | | | |
| | | | |
| bool canApplyInPlace() const { | | | |
| return _inPlacePossible; | | | |
| } | | | |
| | | | |
| /** | | | |
| * modified underlying _obj | | | |
| * @param isOnDisk - true means this is an on disk object, and this | | | |
| update needs to be made durable | | | |
| */ | | | |
| void applyModsInPlace( bool isOnDisk ); | | | |
| | | | |
| BSONObj createNewFromMods(); | | | |
| | | | |
| // re-writing for oplog | | | |
| | | | |
| bool needOpLogRewrite() const { | | | |
| for ( ModStateHolder::const_iterator i = _mods.begin(); i != _m | | | |
| ods.end(); i++ ) | | | |
| if ( i->second->needOpLogRewrite() ) | | | |
| return true; | | | |
| return false; | | | |
| } | | | |
| | | | |
| BSONObj getOpLogRewrite() const { | | | |
| BSONObjBuilder b; | | | |
| for ( ModStateHolder::const_iterator i = _mods.begin(); i != _m | | | |
| ods.end(); i++ ) | | | |
| i->second->appendForOpLog( b ); | | | |
| return b.obj(); | | | |
| } | | | |
| | | | |
| bool haveArrayDepMod() const { | | | |
| for ( ModStateHolder::const_iterator i = _mods.begin(); i != _m | | | |
| ods.end(); i++ ) | | | |
| if ( i->second->m->arrayDep() ) | | | |
| return true; | | | |
| return false; | | | |
| } | | | |
| | | | |
| void appendSizeSpecForArrayDepMods( BSONObjBuilder &b ) const { | | | |
| for ( ModStateHolder::const_iterator i = _mods.begin(); i != _m | | | |
| ods.end(); i++ ) { | | | |
| const ModState& m = *i->second; | | | |
| if ( m.m->arrayDep() ) { | | | |
| if ( m.pushStartSize == -1 ) | | | |
| b.appendNull( m.fieldName() ); | | | |
| else | | | |
| b << m.fieldName() << BSON( "$size" << m.pushStartS | | | |
| ize ); | | | |
| } | | | |
| } | | | |
| } | | | |
| | | | |
| string toString() const; | | | |
| | | | |
| friend class ModSet; | | | |
| }; | | | |
| | | | |
|
| } | | } // namespace mongo | |
| | | | |
End of changes. 14 change blocks. |
| 693 lines changed or deleted | | 32 lines changed or added | |
|
| util.h | | util.h | |
| | | | |
| skipping to change at line 30 | | skipping to change at line 30 | |
| | | | |
| #include "mongo/pch.h" | | #include "mongo/pch.h" | |
| #include "mongo/db/jsobj.h" | | #include "mongo/db/jsobj.h" | |
| #include "mongo/util/mongoutils/str.h" | | #include "mongo/util/mongoutils/str.h" | |
| /** | | /** | |
| some generic sharding utils that can be used in mongod or mongos | | some generic sharding utils that can be used in mongod or mongos | |
| */ | | */ | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
|
| | | // | |
| | | // ShardChunkVersions consist of a major/minor version scoped to a vers | |
| | | ion epoch | |
| | | // | |
| struct ShardChunkVersion { | | struct ShardChunkVersion { | |
| union { | | union { | |
| struct { | | struct { | |
| int _minor; | | int _minor; | |
| int _major; | | int _major; | |
| }; | | }; | |
| unsigned long long _combined; | | unsigned long long _combined; | |
| }; | | }; | |
|
| | | OID _epoch; | |
| | | | |
|
| ShardChunkVersion( int major=0, int minor=0 ) | | ShardChunkVersion() : _minor(0), _major(0), _epoch(OID()) {} | |
| : _minor(minor),_major(major) { | | | |
| } | | | |
| | | | |
|
| ShardChunkVersion( unsigned long long ll ) | | // | |
| : _combined( ll ) { | | // Constructors shouldn't have default parameters here, since it's | |
| | | vital we track from | |
| | | // here on the epochs of versions, even if not used. | |
| | | // | |
| | | | |
| | | ShardChunkVersion( int major, int minor, const OID& epoch ) | |
| | | : _minor(minor),_major(major), _epoch(epoch) { | |
| } | | } | |
| | | | |
|
| ShardChunkVersion( const BSONElement& e ) { | | ShardChunkVersion( unsigned long long ll, const OID& epoch ) | |
| if ( e.type() == Date || e.type() == Timestamp ) { | | : _combined( ll ), _epoch(epoch) { | |
| _combined = e._numberLong(); | | | |
| } | | | |
| else if ( e.eoo() ) { | | | |
| _combined = 0; | | | |
| } | | | |
| else { | | | |
| _combined = 0; | | | |
| log() << "ShardChunkVersion can't handle type (" << (int)(e | | | |
| .type()) << ") " << e << endl; | | | |
| verify(0); | | | |
| } | | | |
| } | | } | |
| | | | |
| void inc( bool major ) { | | void inc( bool major ) { | |
| if ( major ) | | if ( major ) | |
| incMajor(); | | incMajor(); | |
| else | | else | |
| incMinor(); | | incMinor(); | |
| } | | } | |
| | | | |
| void incMajor() { | | void incMajor() { | |
| _major++; | | _major++; | |
| _minor = 0; | | _minor = 0; | |
| } | | } | |
| | | | |
| void incMinor() { | | void incMinor() { | |
| _minor++; | | _minor++; | |
| } | | } | |
| | | | |
|
| | | // Incrementing an epoch creates a new, randomly generated identifi | |
| | | er | |
| | | void incEpoch() { | |
| | | _epoch = OID::gen(); | |
| | | _major = 0; | |
| | | _minor = 0; | |
| | | } | |
| | | | |
| | | // Note: this shouldn't be used as a substitute for version except | |
| | | in specific cases - | |
| | | // epochs make versions more complex | |
| unsigned long long toLong() const { | | unsigned long long toLong() const { | |
| return _combined; | | return _combined; | |
| } | | } | |
| | | | |
| bool isSet() const { | | bool isSet() const { | |
| return _combined > 0; | | return _combined > 0; | |
| } | | } | |
| | | | |
|
| | | bool isEpochSet() const { | |
| | | return _epoch.isSet(); | |
| | | } | |
| | | | |
| string toString() const { | | string toString() const { | |
| stringstream ss; | | stringstream ss; | |
|
| ss << _major << "|" << _minor; | | // Similar to month/day/year. For the most part when debugging | |
| | | , we care about major | |
| | | // so it's first | |
| | | ss << _major << "|" << _minor << "||" << _epoch; | |
| return ss.str(); | | return ss.str(); | |
| } | | } | |
| | | | |
| int majorVersion() const { return _major; } | | int majorVersion() const { return _major; } | |
| int minorVersion() const { return _minor; } | | int minorVersion() const { return _minor; } | |
|
| | | OID epoch() const { return _epoch; } | |
| | | | |
|
| operator unsigned long long() const { return _combined; } | | // | |
| | | // Explicit comparison operators - versions with epochs have non-tr | |
| | | ivial comparisons. | |
| | | // > < operators do not check epoch cases. Generally if using == w | |
| | | e need to handle | |
| | | // more complex cases. | |
| | | // | |
| | | | |
|
| ShardChunkVersion& operator=( const BSONElement& elem ) { | | bool operator>( const ShardChunkVersion& otherVersion ) const { | |
| switch ( elem.type() ) { | | return this->_combined > otherVersion._combined; | |
| case Timestamp: | | } | |
| case NumberLong: | | | |
| case Date: | | bool operator>=( const ShardChunkVersion& otherVersion ) const { | |
| _combined = elem._numberLong(); | | return this->_combined >= otherVersion._combined; | |
| break; | | } | |
| case EOO: | | | |
| _combined = 0; | | bool operator<( const ShardChunkVersion& otherVersion ) const { | |
| break; | | return this->_combined < otherVersion._combined; | |
| default: | | } | |
| massert( 13657 , mongoutils::str::stream() << "unknown type | | | |
| for ShardChunkVersion: " << elem , 0 ); | | bool operator<=( const ShardChunkVersion& otherVersion ) const { | |
| | | return this->_combined < otherVersion._combined; | |
| | | } | |
| | | | |
| | | // | |
| | | // Equivalence comparison types. | |
| | | // | |
| | | | |
| | | // Can we write to this data and not have a problem? | |
| | | bool isWriteCompatibleWith( const ShardChunkVersion& otherVersion ) | |
| | | const { | |
| | | if( ! hasCompatibleEpoch( otherVersion ) ) return false; | |
| | | return otherVersion._major == _major; | |
| | | } | |
| | | | |
| | | // Is this the same version? | |
| | | bool isEquivalentTo( const ShardChunkVersion& otherVersion ) const | |
| | | { | |
| | | if( ! hasCompatibleEpoch( otherVersion ) ) return false; | |
| | | return otherVersion._combined == _combined; | |
| | | } | |
| | | | |
| | | // Is this in the same epoch? | |
| | | bool hasCompatibleEpoch( const ShardChunkVersion& otherVersion ) co | |
| | | nst { | |
| | | return hasCompatibleEpoch( otherVersion._epoch ); | |
| | | } | |
| | | | |
| | | bool hasCompatibleEpoch( const OID& otherEpoch ) const { | |
| | | // TODO : Change logic from eras are not-unequal to eras are eq | |
| | | ual | |
| | | if( otherEpoch.isSet() && _epoch.isSet() && otherEpoch != _epoc | |
| | | h ) return false; | |
| | | return true; | |
| | | } | |
| | | | |
| | | // | |
| | | // BSON input/output | |
| | | // | |
| | | // The idea here is to make the BSON input style very flexible righ | |
| | | t now, so we | |
| | | // can then tighten it up in the next version. We can accept eithe | |
| | | r a BSONObject field | |
| | | // with version and epoch, or version and epoch in different fields | |
| | | (either is optional). | |
| | | // In this case, epoch always is stored in a field name of the vers | |
| | | ion field name + "Epoch" | |
| | | // | |
| | | | |
| | | // | |
| | | // { version : <TS> } and { version : [<TS>,<OID>] } format | |
| | | // | |
| | | | |
| | | static bool canParseBSON( const BSONElement& el, const string& pref | |
| | | ix="" ){ | |
| | | bool canParse; | |
| | | fromBSON( el, prefix, &canParse ); | |
| | | return canParse; | |
| | | } | |
| | | | |
| | | static ShardChunkVersion fromBSON( const BSONElement& el, const str | |
| | | ing& prefix="" ){ | |
| | | bool canParse; | |
| | | return fromBSON( el, prefix, &canParse ); | |
| | | } | |
| | | | |
| | | static ShardChunkVersion fromBSON( const BSONElement& el, | |
| | | const string& prefix, | |
| | | bool* canParse ) | |
| | | { | |
| | | *canParse = true; | |
| | | | |
| | | int type = el.type(); | |
| | | | |
| | | if( type == Array ){ | |
| | | return fromBSON( BSONArray( el.Obj() ), canParse ); | |
| } | | } | |
|
| return *this; | | | |
| | | if( type == jstOID ){ | |
| | | return ShardChunkVersion( 0, 0, el.OID() ); | |
| | | } | |
| | | | |
| | | if( el.isNumber() ){ | |
| | | return ShardChunkVersion( static_cast<unsigned long long>( | |
| | | el.numberLong() ), OID() ); | |
| | | } | |
| | | | |
| | | if( type == Timestamp || type == Date ){ | |
| | | return ShardChunkVersion( el._numberLong(), OID() ); | |
| | | } | |
| | | | |
| | | // Note - we used to throw here, we can't anymore b/c debug bui | |
| | | lds will be unhappy | |
| | | warning() << "can't load version from element type (" << (int)( | |
| | | el.type()) << ") " | |
| | | << el << endl; | |
| | | | |
| | | *canParse = false; | |
| | | | |
| | | return ShardChunkVersion( 0, OID() ); | |
| | | } | |
| | | | |
| | | // | |
| | | // { version : <TS>, versionEpoch : <OID> } object format | |
| | | // | |
| | | | |
| | | static bool canParseBSON( const BSONObj& obj, const string& prefix= | |
| | | "" ){ | |
| | | bool canParse; | |
| | | fromBSON( obj, prefix, &canParse ); | |
| | | return canParse; | |
| | | } | |
| | | | |
| | | static ShardChunkVersion fromBSON( const BSONObj& obj, const string | |
| | | & prefix="" ){ | |
| | | bool canParse; | |
| | | return fromBSON( obj, prefix, &canParse ); | |
| | | } | |
| | | | |
| | | static ShardChunkVersion fromBSON( const BSONObj& obj, | |
| | | const string& prefixIn, | |
| | | bool* canParse ) | |
| | | { | |
| | | *canParse = true; | |
| | | | |
| | | string prefix = prefixIn; | |
| | | if( prefixIn == "" && ! obj[ "version" ].eoo() ){ | |
| | | prefix = (string)"version"; | |
| | | } | |
| | | else if( prefixIn == "" && ! obj[ "lastmod" ].eoo() ){ | |
| | | prefix = (string)"lastmod"; | |
| | | } | |
| | | | |
| | | ShardChunkVersion version = fromBSON( obj[ prefix ], prefixIn, | |
| | | canParse ); | |
| | | | |
| | | if( obj[ prefix + "Epoch" ].type() == jstOID ){ | |
| | | version._epoch = obj[ prefix + "Epoch" ].OID(); | |
| | | *canParse = true; | |
| | | } | |
| | | | |
| | | return version; | |
| | | } | |
| | | | |
| | | // | |
| | | // { version : [<TS>, <OID>] } format | |
| | | // | |
| | | | |
| | | static bool canParseBSON( const BSONArray& arr ){ | |
| | | bool canParse; | |
| | | fromBSON( arr, &canParse ); | |
| | | return canParse; | |
| | | } | |
| | | | |
| | | static ShardChunkVersion fromBSON( const BSONArray& arr ){ | |
| | | bool canParse; | |
| | | return fromBSON( arr, &canParse ); | |
| } | | } | |
|
| | | | |
| | | static ShardChunkVersion fromBSON( const BSONArray& arr, | |
| | | bool* canParse ) | |
| | | { | |
| | | *canParse = false; | |
| | | | |
| | | ShardChunkVersion version; | |
| | | | |
| | | BSONObjIterator it( arr ); | |
| | | if( ! it.more() ) return version; | |
| | | | |
| | | version = fromBSON( it.next(), "", canParse ); | |
| | | if( ! canParse ) return version; | |
| | | | |
| | | *canParse = true; | |
| | | | |
| | | if( ! it.more() ) return version; | |
| | | BSONElement next = it.next(); | |
| | | if( next.type() != jstOID ) return version; | |
| | | | |
| | | version._epoch = next.OID(); | |
| | | | |
| | | return version; | |
| | | } | |
| | | | |
| | | // | |
| | | // Currently our BSON output is to two different fields, to cleanly | |
| | | work with older | |
| | | // versions that know nothing about epochs. | |
| | | // | |
| | | | |
| | | BSONObj toBSON( const string& prefixIn="" ) const { | |
| | | BSONObjBuilder b; | |
| | | | |
| | | string prefix = prefixIn; | |
| | | if( prefix == "" ) prefix = "version"; | |
| | | | |
| | | b.appendTimestamp( prefix, _combined ); | |
| | | b.append( prefix + "Epoch", _epoch ); | |
| | | return b.obj(); | |
| | | } | |
| | | | |
| | | void addToBSON( BSONObjBuilder& b, const string& prefix="" ) const | |
| | | { | |
| | | b.appendElements( toBSON( prefix ) ); | |
| | | } | |
| | | | |
| | | void addEpochToBSON( BSONObjBuilder& b, const string& prefix="" ) c | |
| | | onst { | |
| | | b.append( prefix + "Epoch", _epoch ); | |
| | | } | |
| | | | |
| }; | | }; | |
| | | | |
| inline ostream& operator<<( ostream &s , const ShardChunkVersion& v) { | | inline ostream& operator<<( ostream &s , const ShardChunkVersion& v) { | |
|
| s << v._major << "|" << v._minor; | | s << v.toString(); | |
| return s; | | return s; | |
| } | | } | |
| | | | |
| /** | | /** | |
| * your config info for a given shard/chunk is out of date | | * your config info for a given shard/chunk is out of date | |
| */ | | */ | |
| class StaleConfigException : public AssertionException { | | class StaleConfigException : public AssertionException { | |
| public: | | public: | |
| StaleConfigException( const string& ns , const string& raw , int co
de, ShardChunkVersion received, ShardChunkVersion wanted, bool justConnecti
on = false ) | | StaleConfigException( const string& ns , const string& raw , int co
de, ShardChunkVersion received, ShardChunkVersion wanted, bool justConnecti
on = false ) | |
| : AssertionException( | | : AssertionException( | |
| | | | |
| skipping to change at line 138 | | skipping to change at line 342 | |
| ", " << ( code == SendStaleCon
figCode ? "send" : "recv" ) << " )", | | ", " << ( code == SendStaleCon
figCode ? "send" : "recv" ) << " )", | |
| code ), | | code ), | |
| _justConnection(justConnection) , | | _justConnection(justConnection) , | |
| _ns(ns), | | _ns(ns), | |
| _received( received ), | | _received( received ), | |
| _wanted( wanted ) | | _wanted( wanted ) | |
| {} | | {} | |
| | | | |
| // Preferred if we're rebuilding this from a thrown exception | | // Preferred if we're rebuilding this from a thrown exception | |
| StaleConfigException( const string& raw , int code, const BSONObj&
error, bool justConnection = false ) | | StaleConfigException( const string& raw , int code, const BSONObj&
error, bool justConnection = false ) | |
|
| : AssertionException( | | : AssertionException( mongoutils::str::stream() | |
| mongoutils::str::stream() << raw << " ( ns : " << error | | << raw << " ( ns : " << ( error["ns"].type() == String ? er | |
| ["ns"].String() << // Note, this will fail if we don't have a ns | | ror["ns"].String() : string("<unknown>") ) | |
| ", received : " << ShardChunkV | | << ", received : " << ShardChunkVersion::fromBSON( error, " | |
| ersion( error["vReceived"] ).toString() << | | vReceived" ).toString() | |
| ", wanted : " << ShardChunkVer | | << ", wanted : " << ShardChunkVersion::fromBSON( error, "vW | |
| sion( error["vWanted"] ).toString() << | | anted" ).toString() | |
| ", " << ( code == SendStaleCon | | << ", " << ( code == SendStaleConfigCode ? "send" : "recv" | |
| figCode ? "send" : "recv" ) << " )", | | ) << " )", | |
| code ), | | code ), | |
| | | | |
| _justConnection(justConnection) , | | _justConnection(justConnection) , | |
|
| _ns( error["ns"].String() ), | | // For legacy reasons, we may not always get a namespace here | |
| _received( ShardChunkVersion( error["vReceived"] ) ), | | _ns( error["ns"].type() == String ? error["ns"].String() : "" | |
| _wanted( ShardChunkVersion( error["vWanted"] ) ) | | ), | |
| | | _received( ShardChunkVersion::fromBSON( error, "vReceived" ) | |
| | | ), | |
| | | _wanted( ShardChunkVersion::fromBSON( error, "vWanted" ) ) | |
| {} | | {} | |
| | | | |
|
| StaleConfigException() : AssertionException( "", 0 ) {} | | // Needs message so when we trace all exceptions on construction we | |
| | | get a useful | |
| | | // message | |
| | | StaleConfigException() : | |
| | | AssertionException( "initializing empty stale config exception | |
| | | object", 0 ) {} | |
| | | | |
| virtual ~StaleConfigException() throw() {} | | virtual ~StaleConfigException() throw() {} | |
| | | | |
| virtual void appendPrefix( stringstream& ss ) const { ss << "stale
sharding config exception: "; } | | virtual void appendPrefix( stringstream& ss ) const { ss << "stale
sharding config exception: "; } | |
| | | | |
| bool justConnection() const { return _justConnection; } | | bool justConnection() const { return _justConnection; } | |
| | | | |
| string getns() const { return _ns; } | | string getns() const { return _ns; } | |
| | | | |
|
| | | /** | |
| | | * true if this exception would require a full reload of config dat | |
| | | a to resolve | |
| | | */ | |
| | | bool requiresFullReload() const { | |
| | | return ! _received.hasCompatibleEpoch( _wanted ) || | |
| | | _received.isSet() != _wanted.isSet(); | |
| | | } | |
| | | | |
| static bool parse( const string& big , string& ns , string& raw ) { | | static bool parse( const string& big , string& ns , string& raw ) { | |
| string::size_type start = big.find( '[' ); | | string::size_type start = big.find( '[' ); | |
| if ( start == string::npos ) | | if ( start == string::npos ) | |
| return false; | | return false; | |
| string::size_type end = big.find( ']' ,start ); | | string::size_type end = big.find( ']' ,start ); | |
| if ( end == string::npos ) | | if ( end == string::npos ) | |
| return false; | | return false; | |
| | | | |
| ns = big.substr( start + 1 , ( end - start ) - 1 ); | | ns = big.substr( start + 1 , ( end - start ) - 1 ); | |
| raw = big.substr( end + 1 ); | | raw = big.substr( end + 1 ); | |
| | | | |
End of changes. 18 change blocks. |
| 49 lines changed or deleted | | 296 lines changed or added | |
|