| base64.h | | base64.h | |
| | | | |
| skipping to change at line 18 | | skipping to change at line 18 | |
| * | | * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | | * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | | * | |
| * Unless required by applicable law or agreed to in writing, software | | * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | | * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli
ed. | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli
ed. | |
| * See the License for the specific language governing permissions and | | * See the License for the specific language governing permissions and | |
| * limitations under the License. | | * limitations under the License. | |
| */ | | */ | |
| | | | |
|
| | | #pragma once | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| namespace base64 { | | namespace base64 { | |
| | | | |
|
| | | class Alphabet { | |
| | | public: | |
| | | Alphabet() | |
| | | : encode((unsigned char*) | |
| | | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |
| | | "abcdefghijklmnopqrstuvwxyz" | |
| | | "0123456789" | |
| | | "+/") | |
| | | , decode(new unsigned char[257]) | |
| | | { | |
| | | memset( decode.get() , 0 , 256 ); | |
| | | for ( int i=0; i<64; i++ ){ | |
| | | decode[ encode[i] ] = i; | |
| | | } | |
| | | | |
| | | test(); | |
| | | } | |
| | | void test(){ | |
| | | assert( strlen( (char*)encode ) == 64 ); | |
| | | for ( int i=0; i<26; i++ ) | |
| | | assert( encode[i] == toupper( encode[i+26] ) ); | |
| | | } | |
| | | | |
| | | char e( int x ){ | |
| | | return encode[x&0x3f]; | |
| | | } | |
| | | | |
| | | private: | |
| | | const unsigned char * encode; | |
| | | public: | |
| | | boost::scoped_array<unsigned char> decode; | |
| | | }; | |
| | | | |
| | | extern Alphabet alphabet; | |
| | | | |
| void encode( stringstream& ss , const char * data , int size ); | | void encode( stringstream& ss , const char * data , int size ); | |
| string encode( const char * data , int size ); | | string encode( const char * data , int size ); | |
| string encode( const string& s ); | | string encode( const string& s ); | |
| | | | |
| void decode( stringstream& ss , const string& s ); | | void decode( stringstream& ss , const string& s ); | |
| string decode( const string& s ); | | string decode( const string& s ); | |
| | | | |
| void testAlphabet(); | | void testAlphabet(); | |
| } | | } | |
| } | | } | |
| | | | |
End of changes. 2 change blocks. |
| 0 lines changed or deleted | | 37 lines changed or added | |
|
| btree.h | | btree.h | |
| | | | |
| skipping to change at line 274 | | skipping to change at line 274 | |
| virtual bool ok() { | | virtual bool ok() { | |
| return !bucket.isNull(); | | return !bucket.isNull(); | |
| } | | } | |
| bool eof() { | | bool eof() { | |
| return !ok(); | | return !ok(); | |
| } | | } | |
| virtual bool advance(); | | virtual bool advance(); | |
| | | | |
| virtual void noteLocation(); // updates keyAtKeyOfs... | | virtual void noteLocation(); // updates keyAtKeyOfs... | |
| virtual void checkLocation(); | | virtual void checkLocation(); | |
|
| | | virtual bool supportGetMore() { return true; } | |
| | | | |
| /* used for multikey index traversal to avoid sending back dups. se
e Matcher::matches(). | | /* used for multikey index traversal to avoid sending back dups. se
e Matcher::matches(). | |
| if a multikey index traversal: | | if a multikey index traversal: | |
| if loc has already been sent, returns true. | | if loc has already been sent, returns true. | |
| otherwise, marks loc as sent. | | otherwise, marks loc as sent. | |
| @return true if the loc has not been seen | | @return true if the loc has not been seen | |
| */ | | */ | |
| set<DiskLoc> dups; | | set<DiskLoc> dups; | |
| virtual bool getsetdup(DiskLoc loc) { | | virtual bool getsetdup(DiskLoc loc) { | |
| if( multikey ) { | | if( multikey ) { | |
| | | | |
| skipping to change at line 336 | | skipping to change at line 337 | |
| string s = string("BtreeCursor ") + indexDetails.indexName(); | | string s = string("BtreeCursor ") + indexDetails.indexName(); | |
| if ( direction < 0 ) s += " reverse"; | | if ( direction < 0 ) s += " reverse"; | |
| if ( bounds_.size() > 1 ) s += " multi"; | | if ( bounds_.size() > 1 ) s += " multi"; | |
| return s; | | return s; | |
| } | | } | |
| | | | |
| BSONObj prettyKey( const BSONObj &key ) const { | | BSONObj prettyKey( const BSONObj &key ) const { | |
| return key.replaceFieldNames( indexDetails.keyPattern() ).clien
tReadable(); | | return key.replaceFieldNames( indexDetails.keyPattern() ).clien
tReadable(); | |
| } | | } | |
| | | | |
|
| virtual BSONObj prettyStartKey() const { | | virtual BSONObj prettyIndexBounds() const { | |
| return prettyKey( startKey ); | | BSONArrayBuilder ba; | |
| } | | if ( bounds_.size() == 0 ) { | |
| virtual BSONObj prettyEndKey() const { | | ba << BSON_ARRAY( prettyKey( startKey ) << prettyKey( endKe | |
| return prettyKey( endKey ); | | y ) ); | |
| | | } else { | |
| | | for( BoundList::const_iterator i = bounds_.begin(); i != bo | |
| | | unds_.end(); ++i ) { | |
| | | ba << BSON_ARRAY( prettyKey( i->first ) << prettyKey( i | |
| | | ->second ) ); | |
| | | } | |
| | | } | |
| | | return ba.arr(); | |
| } | | } | |
| | | | |
| void forgetEndKey() { endKey = BSONObj(); } | | void forgetEndKey() { endKey = BSONObj(); } | |
| | | | |
| private: | | private: | |
| /* Our btrees may (rarely) have "unused" keys when items are delete
d. | | /* Our btrees may (rarely) have "unused" keys when items are delete
d. | |
| Skip past them. | | Skip past them. | |
| */ | | */ | |
| void skipUnusedKeys(); | | void skipUnusedKeys(); | |
| | | | |
| | | | |
End of changes. 2 change blocks. |
| 5 lines changed or deleted | | 14 lines changed or added | |
|
| clientcursor.h | | clientcursor.h | |
| | | | |
| skipping to change at line 86 | | skipping to change at line 86 | |
| public: | | public: | |
| ClientCursor *_c; | | ClientCursor *_c; | |
| void release() { | | void release() { | |
| if( _c ) { | | if( _c ) { | |
| assert( _c->_pinValue >= 100 ); | | assert( _c->_pinValue >= 100 ); | |
| _c->_pinValue -= 100; | | _c->_pinValue -= 100; | |
| } | | } | |
| _c = 0; | | _c = 0; | |
| } | | } | |
| Pointer(long long cursorid) { | | Pointer(long long cursorid) { | |
|
| recursive_boostlock lock(ccmutex); | | recursive_scoped_lock lock(ccmutex); | |
| _c = ClientCursor::find_inlock(cursorid, true); | | _c = ClientCursor::find_inlock(cursorid, true); | |
| if( _c ) { | | if( _c ) { | |
| if( _c->_pinValue >= 100 ) { | | if( _c->_pinValue >= 100 ) { | |
| _c = 0; | | _c = 0; | |
| uassert(12051, "clientcursor already in use? driver
problem?", false); | | uassert(12051, "clientcursor already in use? driver
problem?", false); | |
| } | | } | |
| _c->_pinValue += 100; | | _c->_pinValue += 100; | |
| } | | } | |
| } | | } | |
| ~Pointer() { | | ~Pointer() { | |
| | | | |
| skipping to change at line 116 | | skipping to change at line 116 | |
| BSONObj query; | | BSONObj query; | |
| | | | |
| ClientCursor(auto_ptr<Cursor>& _c, const char *_ns, bool okToTimeou
t) : | | ClientCursor(auto_ptr<Cursor>& _c, const char *_ns, bool okToTimeou
t) : | |
| _idleAgeMillis(0), _pinValue(0), | | _idleAgeMillis(0), _pinValue(0), | |
| _doingDeletes(false), | | _doingDeletes(false), | |
| ns(_ns), c(_c), | | ns(_ns), c(_c), | |
| pos(0) | | pos(0) | |
| { | | { | |
| if( !okToTimeout ) | | if( !okToTimeout ) | |
| noTimeout(); | | noTimeout(); | |
|
| recursive_boostlock lock(ccmutex); | | recursive_scoped_lock lock(ccmutex); | |
| cursorid = allocCursorId_inlock(); | | cursorid = allocCursorId_inlock(); | |
| clientCursorsById.insert( make_pair(cursorid, this) ); | | clientCursorsById.insert( make_pair(cursorid, this) ); | |
| } | | } | |
| ~ClientCursor(); | | ~ClientCursor(); | |
| | | | |
| DiskLoc lastLoc() const { | | DiskLoc lastLoc() const { | |
| return _lastLoc; | | return _lastLoc; | |
| } | | } | |
| | | | |
| shared_ptr< FieldMatcher > fields; // which fields query wants retu
rned | | shared_ptr< FieldMatcher > fields; // which fields query wants retu
rned | |
| | | | |
| skipping to change at line 158 | | skipping to change at line 158 | |
| CCById::iterator it = clientCursorsById.find(id); | | CCById::iterator it = clientCursorsById.find(id); | |
| if ( it == clientCursorsById.end() ) { | | if ( it == clientCursorsById.end() ) { | |
| if ( warn ) | | if ( warn ) | |
| OCCASIONALLY out() << "ClientCursor::find(): cursor not
found in map " << id << " (ok after a drop)\n"; | | OCCASIONALLY out() << "ClientCursor::find(): cursor not
found in map " << id << " (ok after a drop)\n"; | |
| return 0; | | return 0; | |
| } | | } | |
| return it->second; | | return it->second; | |
| } | | } | |
| public: | | public: | |
| static ClientCursor* find(CursorId id, bool warn = true) { | | static ClientCursor* find(CursorId id, bool warn = true) { | |
|
| recursive_boostlock lock(ccmutex); | | recursive_scoped_lock lock(ccmutex); | |
| ClientCursor *c = find_inlock(id, warn); | | ClientCursor *c = find_inlock(id, warn); | |
| // if this asserts, your code was not thread safe -
you either need to set no timeout | | // if this asserts, your code was not thread safe -
you either need to set no timeout | |
| // for the cursor or keep a ClientCursor::Pointer in
scope for it. | | // for the cursor or keep a ClientCursor::Pointer in
scope for it. | |
| massert( 12521, "internal error: use of an unlocked ClientCurso
r", c == 0 || c->_pinValue ); | | massert( 12521, "internal error: use of an unlocked ClientCurso
r", c == 0 || c->_pinValue ); | |
| return c; | | return c; | |
| } | | } | |
| | | | |
| static bool erase(CursorId id) { | | static bool erase(CursorId id) { | |
|
| recursive_boostlock lock(ccmutex); | | recursive_scoped_lock lock(ccmutex); | |
| ClientCursor *cc = find_inlock(id); | | ClientCursor *cc = find_inlock(id); | |
| if ( cc ) { | | if ( cc ) { | |
| assert( cc->_pinValue < 100 ); // you can't still have an a
ctive ClientCursor::Pointer | | assert( cc->_pinValue < 100 ); // you can't still have an a
ctive ClientCursor::Pointer | |
| delete cc; | | delete cc; | |
| return true; | | return true; | |
| } | | } | |
| return false; | | return false; | |
| } | | } | |
| | | | |
| /* call when cursor's location changes so that we can update the | | /* call when cursor's location changes so that we can update the | |
| | | | |
End of changes. 4 change blocks. |
| 4 lines changed or deleted | | 4 lines changed or added | |
|
| cmdline.h | | cmdline.h | |
| | | | |
| skipping to change at line 19 | | skipping to change at line 19 | |
| * 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 "../stdafx.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| /* command line options | | /* command line options | |
| */ | | */ | |
| /* concurrency: OK/READ */ | | /* concurrency: OK/READ */ | |
| struct CmdLine { | | struct CmdLine { | |
| int port; // --port | | int port; // --port | |
|
| | | bool rest; // --rest | |
| | | | |
| string source; // --source | | string source; // --source | |
| string only; // --only | | string only; // --only | |
| | | | |
| bool quiet; // --quiet | | bool quiet; // --quiet | |
| bool notablescan; // --notablescan | | bool notablescan; // --notablescan | |
| bool prealloc; // --noprealloc | | bool prealloc; // --noprealloc | |
| bool smallfiles; // --smallfiles | | bool smallfiles; // --smallfiles | |
| | | | |
| bool quota; // --quota | | bool quota; // --quota | |
| | | | |
| skipping to change at line 50 | | skipping to change at line 53 | |
| int defaultProfile; // --profile | | int defaultProfile; // --profile | |
| int slowMS; // --time in ms that is "slow" | | int slowMS; // --time in ms that is "slow" | |
| | | | |
| enum { | | enum { | |
| DefaultDBPort = 27017, | | DefaultDBPort = 27017, | |
| ConfigServerPort = 27019, | | ConfigServerPort = 27019, | |
| ShardServerPort = 27018 | | ShardServerPort = 27018 | |
| }; | | }; | |
| | | | |
| CmdLine() : | | CmdLine() : | |
|
| port(DefaultDBPort), quiet(false), notablescan(false), prealloc
(true), smallfiles(false), | | port(DefaultDBPort), rest(false), quiet(false), notablescan(fal
se), prealloc(true), smallfiles(false), | |
| quota(false), quotaFiles(8), cpu(false), oplogSize(0), defaultP
rofile(0), slowMS(100) | | quota(false), quotaFiles(8), cpu(false), oplogSize(0), defaultP
rofile(0), slowMS(100) | |
| { } | | { } | |
| | | | |
|
| | | void addGlobalOptions( boost::program_options::options_description& | |
| | | general , | |
| | | boost::program_options::options_description& | |
| | | hidden ); | |
| }; | | }; | |
| | | | |
| extern CmdLine cmdLine; | | extern CmdLine cmdLine; | |
|
| | | | |
| } | | } | |
| | | | |
End of changes. 5 change blocks. |
| 1 lines changed or deleted | | 9 lines changed or added | |
|
| curop.h | | curop.h | |
| | | | |
| skipping to change at line 65 | | skipping to change at line 65 | |
| AtomicUInt _opNum; | | AtomicUInt _opNum; | |
| char _ns[Namespace::MaxNsLen+2]; | | char _ns[Namespace::MaxNsLen+2]; | |
| struct sockaddr_in _remote; | | struct sockaddr_in _remote; | |
| | | | |
| char _queryBuf[256]; | | char _queryBuf[256]; | |
| | | | |
| void resetQuery(int x=0) { *((int *)_queryBuf) = x; } | | void resetQuery(int x=0) { *((int *)_queryBuf) = x; } | |
| | | | |
| OpDebug _debug; | | OpDebug _debug; | |
| | | | |
|
| | | ThreadSafeString _message; | |
| | | ProgressMeter _progressMeter; | |
| | | | |
| void _reset(){ | | void _reset(){ | |
| _command = false; | | _command = false; | |
| _lockType = 0; | | _lockType = 0; | |
| _dbprofile = 0; | | _dbprofile = 0; | |
| _end = 0; | | _end = 0; | |
| _waitingForLock = false; | | _waitingForLock = false; | |
|
| | | _message = ""; | |
| | | _progressMeter.finished(); | |
| } | | } | |
| | | | |
| void setNS(const char *ns) { | | void setNS(const char *ns) { | |
| strncpy(_ns, ns, Namespace::MaxNsLen); | | strncpy(_ns, ns, Namespace::MaxNsLen); | |
| } | | } | |
| | | | |
| public: | | public: | |
| | | | |
| bool haveQuery() const { return *((int *) _queryBuf) != 0; } | | bool haveQuery() const { return *((int *) _queryBuf) != 0; } | |
| | | | |
| | | | |
| skipping to change at line 237 | | skipping to change at line 242 | |
| } | | } | |
| | | | |
| BSONObj infoNoauth(); | | BSONObj infoNoauth(); | |
| | | | |
| string getRemoteString(){ | | string getRemoteString(){ | |
| stringstream ss; | | stringstream ss; | |
| ss << inet_ntoa( _remote.sin_addr ) << ":" << ntohs( _remote.si
n_port ); | | ss << inet_ntoa( _remote.sin_addr ) << ":" << ntohs( _remote.si
n_port ); | |
| return ss.str(); | | return ss.str(); | |
| } | | } | |
| | | | |
|
| | | ProgressMeter& setMessage( const char * msg , long long progressMet | |
| | | erTotal = 0 , int secondsBetween = 3 ){ | |
| | | _message = msg; | |
| | | if ( progressMeterTotal ){ | |
| | | assert( ! _progressMeter.isActive() ); | |
| | | _progressMeter.reset( progressMeterTotal , secondsBetween ) | |
| | | ; | |
| | | } | |
| | | else { | |
| | | _progressMeter.finished(); | |
| | | } | |
| | | return _progressMeter; | |
| | | } | |
| | | | |
| | | string getMessage() const { return _message; } | |
| | | ProgressMeter getProgressMeter() { return _progressMeter; } | |
| | | | |
| friend class Client; | | friend class Client; | |
| }; | | }; | |
| | | | |
| /* 0 = ok | | /* 0 = ok | |
| 1 = kill current operation and reset this to 0 | | 1 = kill current operation and reset this to 0 | |
| future: maybe use this as a "going away" thing on process terminatio
n with a higher flag value | | future: maybe use this as a "going away" thing on process terminatio
n with a higher flag value | |
| */ | | */ | |
| extern class KillCurrentOp { | | extern class KillCurrentOp { | |
|
| enum { Off, On, All } state; | | enum { Off, On, All } state; | |
| AtomicUInt toKill; | | AtomicUInt toKill; | |
| public: | | public: | |
| void killAll() { state = All; } | | void killAll() { state = All; } | |
| void kill(AtomicUInt i) { toKill = i; state = On; } | | void kill(AtomicUInt i) { toKill = i; state = On; } | |
| | | | |
| void checkForInterrupt() { | | void checkForInterrupt() { | |
| if( state != Off ) { | | if( state != Off ) { | |
| if( state == All ) | | if( state == All ) | |
| uasserted(11600,"interrupted at shutdown"); | | uasserted(11600,"interrupted at shutdown"); | |
| if( cc().curop()->opNum() == toKill ) { | | if( cc().curop()->opNum() == toKill ) { | |
| | | | |
End of changes. 4 change blocks. |
| 1 lines changed or deleted | | 23 lines changed or added | |
|
| cursor.h | | cursor.h | |
| | | | |
| skipping to change at line 80 | | skipping to change at line 80 | |
| } | | } | |
| | | | |
| /* called after every query block is iterated -- i.e. between getMo
re() blocks | | /* called after every query block is iterated -- i.e. between getMo
re() blocks | |
| so you can note where we are, if necessary. | | so you can note where we are, if necessary. | |
| */ | | */ | |
| virtual void noteLocation() { } | | virtual void noteLocation() { } | |
| | | | |
| /* called before query getmore block is iterated */ | | /* called before query getmore block is iterated */ | |
| virtual void checkLocation() { } | | virtual void checkLocation() { } | |
| | | | |
|
| | | virtual bool supportGetMore() = 0; | |
| | | | |
| virtual string toString() { | | virtual string toString() { | |
| return "abstract?"; | | return "abstract?"; | |
| } | | } | |
| | | | |
| /* used for multikey index traversal to avoid sending back dups. se
e Matcher::matches(). | | /* used for multikey index traversal to avoid sending back dups. se
e Matcher::matches(). | |
| if a multikey index traversal: | | if a multikey index traversal: | |
| if loc has already been sent, returns true. | | if loc has already been sent, returns true. | |
| otherwise, marks loc as sent. | | otherwise, marks loc as sent. | |
| @param deep - match was against an array, so we know it is multi
key. this is legacy and kept | | @param deep - match was against an array, so we know it is multi
key. this is legacy and kept | |
| for backwards datafile compatibility. 'deep' can
be eliminated next time we | | for backwards datafile compatibility. 'deep' can
be eliminated next time we | |
| force a data file conversion. 7Jul09 | | force a data file conversion. 7Jul09 | |
| */ | | */ | |
| virtual bool getsetdup(DiskLoc loc) = 0; | | virtual bool getsetdup(DiskLoc loc) = 0; | |
| | | | |
|
| virtual BSONObj prettyStartKey() const { return BSONObj(); } | | virtual BSONObj prettyIndexBounds() const { return BSONObj(); } | |
| virtual BSONObj prettyEndKey() const { return BSONObj(); } | | | |
| | | | |
| virtual bool capped() const { return false; } | | virtual bool capped() const { return false; } | |
| | | | |
| }; | | }; | |
| | | | |
| // strategy object implementing direction of traversal. | | // strategy object implementing direction of traversal. | |
| class AdvanceStrategy { | | class AdvanceStrategy { | |
| public: | | public: | |
| virtual ~AdvanceStrategy() { } | | virtual ~AdvanceStrategy() { } | |
| virtual DiskLoc next( const DiskLoc &prev ) const = 0; | | virtual DiskLoc next( const DiskLoc &prev ) const = 0; | |
| | | | |
| skipping to change at line 161 | | skipping to change at line 162 | |
| return "BasicCursor"; | | return "BasicCursor"; | |
| } | | } | |
| virtual void setTailable() { | | virtual void setTailable() { | |
| if ( !curr.isNull() || !last.isNull() ) | | if ( !curr.isNull() || !last.isNull() ) | |
| tailable_ = true; | | tailable_ = true; | |
| } | | } | |
| virtual bool tailable() { | | virtual bool tailable() { | |
| return tailable_; | | return tailable_; | |
| } | | } | |
| virtual bool getsetdup(DiskLoc loc) { return false; } | | virtual bool getsetdup(DiskLoc loc) { return false; } | |
|
| | | | |
| | | virtual bool supportGetMore() { return true; } | |
| }; | | }; | |
| | | | |
| /* used for order { $natural: -1 } */ | | /* used for order { $natural: -1 } */ | |
| class ReverseCursor : public BasicCursor { | | class ReverseCursor : public BasicCursor { | |
| public: | | public: | |
| ReverseCursor(DiskLoc dl) : BasicCursor( dl, reverse() ) { } | | ReverseCursor(DiskLoc dl) : BasicCursor( dl, reverse() ) { } | |
| ReverseCursor() : BasicCursor( reverse() ) { } | | ReverseCursor() : BasicCursor( reverse() ) { } | |
| virtual string toString() { | | virtual string toString() { | |
| return "ReverseCursor"; | | return "ReverseCursor"; | |
| } | | } | |
| | | | |
End of changes. 3 change blocks. |
| 2 lines changed or deleted | | 5 lines changed or added | |
|
| dbclient.h | | dbclient.h | |
| | | | |
| skipping to change at line 371 | | skipping to change at line 371 | |
| */ | | */ | |
| virtual BSONObj findOne(const string &ns, Query query, const BSONOb
j *fieldsToReturn = 0, int queryOptions = 0); | | virtual BSONObj findOne(const string &ns, Query query, const BSONOb
j *fieldsToReturn = 0, int queryOptions = 0); | |
| | | | |
| }; | | }; | |
| | | | |
| /** | | /** | |
| DB "commands" | | DB "commands" | |
| Basically just invocations of connection.$cmd.findOne({...}); | | Basically just invocations of connection.$cmd.findOne({...}); | |
| */ | | */ | |
| class DBClientWithCommands : public DBClientInterface { | | class DBClientWithCommands : public DBClientInterface { | |
|
| bool isOk(const BSONObj&); | | | |
| set<string> _seenIndexes; | | set<string> _seenIndexes; | |
| public: | | public: | |
| | | | |
| /** helper function. run a simple command where the command
expression is simply | | /** helper function. run a simple command where the command
expression 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); | |
| | | | |
| skipping to change at line 393 | | skipping to change at line 392 | |
| /** Run a database command. Database commands are represented as B
SON objects. Common database | | /** Run a database command. Database commands are represented as B
SON objects. Common database | |
| commands have prebuilt helper functions -- see below. If a hel
per is not available you can | | commands have prebuilt helper functions -- see below. If a hel
per is not available you can | |
| directly call runCommand. | | directly call runCommand. | |
| | | | |
| @param dbname database name. Use "admin" for global administra
tive commands. | | @param dbname database name. Use "admin" for global administra
tive commands. | |
| @param cmd the command object to execute. For exam
ple, { ismaster : 1 } | | @param cmd the command object to execute. For exam
ple, { ismaster : 1 } | |
| @param info the result object the database returns.
Typically has { ok : ..., errmsg : ... } fields | | @param info the result object the database returns.
Typically has { ok : ..., errmsg : ... } fields | |
| set. | | set. | |
| @return true if the command returned "ok". | | @return true if the command returned "ok". | |
| */ | | */ | |
|
| bool runCommand(const string &dbname, const BSONObj& cmd, BSONObj &
info, int options=0); | | virtual bool runCommand(const string &dbname, const BSONObj& cmd, B
SONObj &info, int options=0); | |
| | | | |
| /** Authorize access to a particular database. | | /** Authorize access to a particular database. | |
| Authentication is separate for each database on the
server -- you may authenticate for any | | Authentication is separate for each database on the
server -- you may authenticate for any | |
| number of databases on a single connection. | | number of databases on a single connection. | |
| The "admin" database is special and once authenticat
ed provides access to all databases on the | | The "admin" database is special and once authenticat
ed 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 | |
| @return true if successful | | @return true if successful | |
| */ | | */ | |
| virtual bool auth(const string &dbname, const string &username, con
st string &pwd, string& errmsg, bool digestPassword = true); | | virtual bool auth(const string &dbname, const string &username, con
st string &pwd, string& errmsg, bool digestPassword = true); | |
| | | | |
| skipping to change at line 665 | | skipping to change at line 665 | |
| | | | |
| /** @return the collection name portion of an ns string */ | | /** @return the collection name portion of an ns string */ | |
| string nsGetCollection( const string &ns ){ | | string nsGetCollection( const string &ns ){ | |
| string::size_type pos = ns.find( "." ); | | string::size_type pos = ns.find( "." ); | |
| if ( pos == string::npos ) | | if ( pos == string::npos ) | |
| return ""; | | return ""; | |
| | | | |
| return ns.substr( pos + 1 ); | | return ns.substr( pos + 1 ); | |
| } | | } | |
| | | | |
|
| | | protected: | |
| | | bool isOk(const BSONObj&); | |
| | | | |
| }; | | }; | |
| | | | |
| /** | | /** | |
| 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 { | |
| public: | | public: | |
| /** send a query to the database. | | /** send a query to the database. | |
| ns: namespace to query, format is <dbname>.<collectname
>[.<collectname>]* | | ns: namespace to query, format is <dbname>.<collectname
>[.<collectname>]* | |
| query: query to perform on the collection. this is a BSON
Obj (binary JSON) | | query: query to perform on the collection. this is a BSON
Obj (binary JSON) | |
| | | | |
End of changes. 3 change blocks. |
| 2 lines changed or deleted | | 4 lines changed or added | |
|
| file_allocator.h | | file_allocator.h | |
| | | | |
| skipping to change at line 57 | | skipping to change at line 57 | |
| #endif | | #endif | |
| } | | } | |
| // May be called if file exists. If file exists, or its allocation
has | | // May be called if file exists. If file exists, or its allocation
has | |
| // been requested, size is updated to match existing file size. | | // been requested, size is updated to match existing file size. | |
| void requestAllocation( const string &name, long &size ) { | | void requestAllocation( const string &name, long &size ) { | |
| /* Some of the system calls in the file allocator don't work in
win, | | /* Some of the system calls in the file allocator don't work in
win, | |
| so no win support - 32 or 64 bit. Plus we don't seem to nee
d preallocation | | so no win support - 32 or 64 bit. Plus we don't seem to nee
d preallocation | |
| on windows anyway as we don't have to pre-zero the file ther
e. | | on windows anyway as we don't have to pre-zero the file ther
e. | |
| */ | | */ | |
| #if !defined(_WIN32) | | #if !defined(_WIN32) | |
|
| boostlock lk( pendingMutex_ ); | | scoped_lock lk( pendingMutex_ ); | |
| if ( failed_ ) | | if ( failed_ ) | |
| return; | | return; | |
| long oldSize = prevSize( name ); | | long oldSize = prevSize( name ); | |
| if ( oldSize != -1 ) { | | if ( oldSize != -1 ) { | |
| size = oldSize; | | size = oldSize; | |
| return; | | return; | |
| } | | } | |
| pending_.push_back( name ); | | pending_.push_back( name ); | |
| pendingSize_[ name ] = size; | | pendingSize_[ name ] = size; | |
| pendingUpdated_.notify_all(); | | pendingUpdated_.notify_all(); | |
| #endif | | #endif | |
| } | | } | |
| // Returns when file has been allocated. If file exists, size is | | // Returns when file has been allocated. If file exists, size is | |
| // updated to match existing file size. | | // updated to match existing file size. | |
| void allocateAsap( const string &name, long &size ) { | | void allocateAsap( const string &name, long &size ) { | |
| #if !defined(_WIN32) | | #if !defined(_WIN32) | |
|
| boostlock lk( pendingMutex_ ); | | scoped_lock lk( pendingMutex_ ); | |
| long oldSize = prevSize( name ); | | long oldSize = prevSize( name ); | |
| if ( oldSize != -1 ) { | | if ( oldSize != -1 ) { | |
| size = oldSize; | | size = oldSize; | |
| if ( !inProgress( name ) ) | | if ( !inProgress( name ) ) | |
| return; | | return; | |
| } | | } | |
| checkFailure(); | | checkFailure(); | |
| pendingSize_[ name ] = size; | | pendingSize_[ name ] = size; | |
| if ( pending_.size() == 0 ) | | if ( pending_.size() == 0 ) | |
| pending_.push_back( name ); | | pending_.push_back( name ); | |
| else if ( pending_.front() != name ) { | | else if ( pending_.front() != name ) { | |
| pending_.remove( name ); | | pending_.remove( name ); | |
| list< string >::iterator i = pending_.begin(); | | list< string >::iterator i = pending_.begin(); | |
| ++i; | | ++i; | |
| pending_.insert( i, name ); | | pending_.insert( i, name ); | |
| } | | } | |
| pendingUpdated_.notify_all(); | | pendingUpdated_.notify_all(); | |
| while( inProgress( name ) ) { | | while( inProgress( name ) ) { | |
| checkFailure(); | | checkFailure(); | |
|
| pendingUpdated_.wait( lk ); | | pendingUpdated_.wait( lk.boost() ); | |
| } | | } | |
| #endif | | #endif | |
| } | | } | |
| | | | |
| void waitUntilFinished() const { | | void waitUntilFinished() const { | |
| #if !defined(_WIN32) | | #if !defined(_WIN32) | |
| if ( failed_ ) | | if ( failed_ ) | |
| return; | | return; | |
|
| boostlock lk( pendingMutex_ ); | | scoped_lock lk( pendingMutex_ ); | |
| while( pending_.size() != 0 ) | | while( pending_.size() != 0 ) | |
|
| pendingUpdated_.wait( lk ); | | pendingUpdated_.wait( lk.boost() ); | |
| #endif | | #endif | |
| } | | } | |
| | | | |
| private: | | private: | |
| #if !defined(_WIN32) | | #if !defined(_WIN32) | |
| void checkFailure() { | | void checkFailure() { | |
| massert( 12520, "file allocation failure", !failed_ ); | | massert( 12520, "file allocation failure", !failed_ ); | |
| } | | } | |
| | | | |
| // caller must hold pendingMutex_ lock. Returns size if allocated
or | | // caller must hold pendingMutex_ lock. Returns size if allocated
or | |
| | | | |
| skipping to change at line 133 | | skipping to change at line 133 | |
| } | | } | |
| | | | |
| // caller must hold pendingMutex_ lock. | | // caller must hold pendingMutex_ lock. | |
| bool inProgress( const string &name ) const { | | bool inProgress( const string &name ) const { | |
| for( list< string >::const_iterator i = pending_.begin(); i !=
pending_.end(); ++i ) | | for( list< string >::const_iterator i = pending_.begin(); i !=
pending_.end(); ++i ) | |
| if ( *i == name ) | | if ( *i == name ) | |
| return true; | | return true; | |
| return false; | | return false; | |
| } | | } | |
| | | | |
|
| mutable boost::mutex pendingMutex_; | | mutable mongo::mutex pendingMutex_; | |
| mutable boost::condition pendingUpdated_; | | mutable boost::condition pendingUpdated_; | |
| list< string > pending_; | | list< string > pending_; | |
| mutable map< string, long > pendingSize_; | | mutable map< string, long > pendingSize_; | |
| bool failed_; | | bool failed_; | |
| | | | |
| struct Runner { | | struct Runner { | |
| Runner( FileAllocator &allocator ) : a_( allocator ) {} | | Runner( FileAllocator &allocator ) : a_( allocator ) {} | |
| FileAllocator &a_; | | FileAllocator &a_; | |
| void operator()() { | | void operator()() { | |
| while( 1 ) { | | while( 1 ) { | |
| { | | { | |
|
| boostlock lk( a_.pendingMutex_ ); | | scoped_lock lk( a_.pendingMutex_ ); | |
| if ( a_.pending_.size() == 0 ) | | if ( a_.pending_.size() == 0 ) | |
|
| a_.pendingUpdated_.wait( lk ); | | a_.pendingUpdated_.wait( lk.boost() ); | |
| } | | } | |
| while( 1 ) { | | while( 1 ) { | |
| string name; | | string name; | |
| long size; | | long size; | |
| { | | { | |
|
| boostlock lk( a_.pendingMutex_ ); | | scoped_lock lk( a_.pendingMutex_ ); | |
| if ( a_.pending_.size() == 0 ) | | if ( a_.pending_.size() == 0 ) | |
| break; | | break; | |
| name = a_.pending_.front(); | | name = a_.pending_.front(); | |
| size = a_.pendingSize_[ name ]; | | size = a_.pendingSize_[ name ]; | |
| } | | } | |
| try { | | try { | |
| log() << "allocating new datafile " << name <<
", filling with zeroes..." << endl; | | log() << "allocating new datafile " << name <<
", filling with zeroes..." << endl; | |
| long fd = open(name.c_str(), O_CREAT | O_RDWR |
O_NOATIME, S_IRUSR | S_IWUSR); | | long fd = open(name.c_str(), O_CREAT | O_RDWR |
O_NOATIME, S_IRUSR | S_IWUSR); | |
| if ( fd <= 0 ) { | | if ( fd <= 0 ) { | |
| stringstream ss; | | stringstream ss; | |
| | | | |
| skipping to change at line 209 | | skipping to change at line 209 | |
| } | | } | |
| close( fd ); | | close( fd ); | |
| | | | |
| } catch ( ... ) { | | } catch ( ... ) { | |
| problem() << "Failed to allocate new file: " <<
name | | problem() << "Failed to allocate new file: " <<
name | |
| << ", size: " << size << ", aborting.
" << endl; | | << ", size: " << size << ", aborting.
" << endl; | |
| try { | | try { | |
| BOOST_CHECK_EXCEPTION( boost::filesystem::r
emove( name ) ); | | BOOST_CHECK_EXCEPTION( boost::filesystem::r
emove( name ) ); | |
| } catch ( ... ) { | | } catch ( ... ) { | |
| } | | } | |
|
| boostlock lk( a_.pendingMutex_ ); | | scoped_lock lk( a_.pendingMutex_ ); | |
| a_.failed_ = true; | | a_.failed_ = true; | |
| // not erasing from pending | | // not erasing from pending | |
| a_.pendingUpdated_.notify_all(); | | a_.pendingUpdated_.notify_all(); | |
| return; // no more allocation | | return; // no more allocation | |
| } | | } | |
| | | | |
| { | | { | |
|
| boostlock lk( a_.pendingMutex_ ); | | scoped_lock lk( a_.pendingMutex_ ); | |
| a_.pendingSize_.erase( name ); | | a_.pendingSize_.erase( name ); | |
| a_.pending_.pop_front(); | | a_.pending_.pop_front(); | |
| a_.pendingUpdated_.notify_all(); | | a_.pendingUpdated_.notify_all(); | |
| } | | } | |
| } | | } | |
| } | | } | |
| } | | } | |
| }; | | }; | |
| #endif | | #endif | |
| }; | | }; | |
| | | | |
End of changes. 11 change blocks. |
| 11 lines changed or deleted | | 11 lines changed or added | |
|
| goodies.h | | goodies.h | |
| | | | |
| skipping to change at line 27 | | skipping to change at line 27 | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #if defined(_WIN32) | | #if defined(_WIN32) | |
| # include <windows.h> | | # include <windows.h> | |
| #endif | | #endif | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
|
| #if !defined(_WIN32) && !defined(NOEXECINFO) && !defined(__freebsd__) | | #if !defined(_WIN32) && !defined(NOEXECINFO) && !defined(__freebsd__) && !d
efined(__sun__) | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
| #include <pthread.h> | | #include <pthread.h> | |
| #include <execinfo.h> | | #include <execinfo.h> | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| inline pthread_t GetCurrentThreadId() { | | inline pthread_t GetCurrentThreadId() { | |
| return pthread_self(); | | return pthread_self(); | |
| | | | |
| skipping to change at line 156 | | skipping to change at line 156 | |
| | | | |
| inline void time_t_to_String(time_t t, char *buf) { | | inline void time_t_to_String(time_t t, char *buf) { | |
| #if defined(_WIN32) | | #if defined(_WIN32) | |
| ctime_s(buf, 64, &t); | | ctime_s(buf, 64, &t); | |
| #else | | #else | |
| ctime_r(&t, buf); | | ctime_r(&t, buf); | |
| #endif | | #endif | |
| buf[24] = 0; // don't want the \n | | buf[24] = 0; // don't want the \n | |
| } | | } | |
| | | | |
|
| | | inline void time_t_to_Struct(time_t t, struct tm * buf , bool local = f | |
| | | alse ) { | |
| | | #if defined(_WIN32) | |
| | | if ( local ) | |
| | | localtime_s( buf , &t ); | |
| | | else | |
| | | gmtime_s(buf, &t); | |
| | | #else | |
| | | if ( local ) | |
| | | localtime_r(&t, buf); | |
| | | else | |
| | | gmtime_r(&t, buf); | |
| | | #endif | |
| | | } | |
| | | | |
| #define asctime _asctime_not_threadsafe_ | | #define asctime _asctime_not_threadsafe_ | |
| #define gmtime _gmtime_not_threadsafe_ | | #define gmtime _gmtime_not_threadsafe_ | |
| #define localtime _localtime_not_threadsafe_ | | #define localtime _localtime_not_threadsafe_ | |
| #define ctime _ctime_is_not_threadsafe_ | | #define ctime _ctime_is_not_threadsafe_ | |
| | | | |
| #if defined(_WIN32) || defined(__sunos__) | | #if defined(_WIN32) || defined(__sunos__) | |
| inline void sleepsecs(int s) { | | inline void sleepsecs(int s) { | |
| boost::xtime xt; | | boost::xtime xt; | |
| boost::xtime_get(&xt, boost::TIME_UTC); | | boost::xtime_get(&xt, boost::TIME_UTC); | |
| xt.sec += s; | | xt.sec += s; | |
| | | | |
| skipping to change at line 255 | | skipping to change at line 269 | |
| | | | |
| // measures up to 1024 seconds. or, 512 seconds with tdiff that is... | | // measures up to 1024 seconds. or, 512 seconds with tdiff that is... | |
| inline unsigned curTimeMicros() { | | inline unsigned curTimeMicros() { | |
| boost::xtime xt; | | boost::xtime xt; | |
| boost::xtime_get(&xt, boost::TIME_UTC); | | boost::xtime_get(&xt, boost::TIME_UTC); | |
| unsigned t = xt.nsec / 1000; | | unsigned t = xt.nsec / 1000; | |
| unsigned secs = xt.sec % 1024; | | unsigned secs = xt.sec % 1024; | |
| return secs*1000000 + t; | | return secs*1000000 + t; | |
| } | | } | |
| using namespace boost; | | using namespace boost; | |
|
| typedef boost::mutex::scoped_lock boostlock; | | | |
| typedef boost::recursive_mutex::scoped_lock recursive_boostlock; | | extern bool __destroyingStatics; | |
| | | | |
| | | // If you create a local static instance of this class, that instance w | |
| | | ill be destroyed | |
| | | // before all global static objects are destroyed, so __destroyingStati | |
| | | cs will be set | |
| | | // to true before the global static variables are destroyed. | |
| | | class StaticObserver : boost::noncopyable { | |
| | | public: | |
| | | ~StaticObserver() { __destroyingStatics = true; } | |
| | | }; | |
| | | | |
| | | // On pthread systems, it is an error to destroy a mutex while held. S | |
| | | tatic global | |
| | | // mutexes may be held upon shutdown in our implementation, and this wa | |
| | | y we avoid | |
| | | // destroying them. | |
| | | class mutex : boost::noncopyable { | |
| | | public: | |
| | | mutex() { new (_buf) boost::mutex(); } | |
| | | ~mutex() { | |
| | | if( !__destroyingStatics ) { | |
| | | boost().boost::mutex::~mutex(); | |
| | | } | |
| | | } | |
| | | class scoped_lock : boost::noncopyable { | |
| | | public: | |
| | | scoped_lock( mongo::mutex &m ) : _l( m.boost() ) {} | |
| | | boost::mutex::scoped_lock &boost() { return _l; } | |
| | | private: | |
| | | boost::mutex::scoped_lock _l; | |
| | | }; | |
| | | private: | |
| | | boost::mutex &boost() { return *( boost::mutex * )( _buf ); } | |
| | | char _buf[ sizeof( boost::mutex ) ]; | |
| | | }; | |
| | | | |
| | | typedef mongo::mutex::scoped_lock scoped_lock; | |
| | | typedef boost::recursive_mutex::scoped_lock recursive_scoped_lock; | |
| | | | |
| // simple scoped timer | | // simple scoped timer | |
| class Timer { | | class Timer { | |
| public: | | public: | |
| Timer() { | | Timer() { | |
| reset(); | | reset(); | |
| } | | } | |
| Timer( unsigned long long start ) { | | Timer( unsigned long long start ) { | |
| old = start; | | old = start; | |
| } | | } | |
| | | | |
| skipping to change at line 295 | | skipping to change at line 343 | |
| old = curTimeMicros64(); | | old = curTimeMicros64(); | |
| } | | } | |
| private: | | private: | |
| unsigned long long old; | | unsigned long long old; | |
| }; | | }; | |
| | | | |
| /* | | /* | |
| | | | |
| class DebugMutex : boost::noncopyable { | | class DebugMutex : boost::noncopyable { | |
| friend class lock; | | friend class lock; | |
|
| boost::mutex m; | | mongo::mutex m; | |
| int locked; | | int locked; | |
| public: | | public: | |
| DebugMutex() : locked(0); { } | | DebugMutex() : locked(0); { } | |
| bool isLocked() { return locked; } | | bool isLocked() { return locked; } | |
| }; | | }; | |
| | | | |
| */ | | */ | |
| | | | |
|
| //typedef boostlock lock; | | //typedef scoped_lock lock; | |
| | | | |
| inline bool startsWith(const char *str, const char *prefix) { | | inline bool startsWith(const char *str, const char *prefix) { | |
| size_t l = strlen(prefix); | | size_t l = strlen(prefix); | |
| if ( strlen(str) < l ) return false; | | if ( strlen(str) < l ) return false; | |
| return strncmp(str, prefix, l) == 0; | | return strncmp(str, prefix, l) == 0; | |
| } | | } | |
| | | | |
| inline bool endsWith(const char *p, const char *suffix) { | | inline bool endsWith(const char *p, const char *suffix) { | |
| size_t a = strlen(p); | | size_t a = strlen(p); | |
| size_t b = strlen(suffix); | | size_t b = strlen(suffix); | |
| | | | |
| skipping to change at line 395 | | skipping to change at line 443 | |
| _val.reset( v ); | | _val.reset( v ); | |
| } | | } | |
| | | | |
| private: | | private: | |
| T _default; | | T _default; | |
| boost::thread_specific_ptr<T> _val; | | boost::thread_specific_ptr<T> _val; | |
| }; | | }; | |
| | | | |
| class ProgressMeter { | | class ProgressMeter { | |
| public: | | public: | |
|
| ProgressMeter( long long total , int secondsBetween = 3 , int check | | ProgressMeter( long long total , int secondsBetween = 3 , int check | |
| Interval = 100 ) | | Interval = 100 ){ | |
| : _total( total ) , _secondsBetween( secondsBetween ) , _checkI | | reset( total , secondsBetween , checkInterval ); | |
| nterval( checkInterval ) , | | } | |
| _done(0) , _hits(0) , _lastTime( (int) time(0) ){ | | | |
| | | ProgressMeter(){ | |
| | | _active = 0; | |
| | | } | |
| | | | |
| | | void reset( long long total , int secondsBetween = 3 , int checkInt | |
| | | erval = 100 ){ | |
| | | _total = total; | |
| | | _secondsBetween = secondsBetween; | |
| | | _checkInterval = checkInterval; | |
| | | | |
| | | _done = 0; | |
| | | _hits = 0; | |
| | | _lastTime = (int)time(0); | |
| | | | |
| | | _active = 1; | |
| | | } | |
| | | | |
| | | void finished(){ | |
| | | _active = 0; | |
| | | } | |
| | | | |
| | | bool isActive(){ | |
| | | return _active; | |
| } | | } | |
| | | | |
| bool hit( int n = 1 ){ | | bool hit( int n = 1 ){ | |
|
| | | if ( ! _active ){ | |
| | | cout << "warning: hit on in-active ProgressMeter" << endl; | |
| | | } | |
| | | | |
| _done += n; | | _done += n; | |
| _hits++; | | _hits++; | |
| if ( _hits % _checkInterval ) | | if ( _hits % _checkInterval ) | |
| return false; | | return false; | |
| | | | |
| int t = (int) time(0); | | int t = (int) time(0); | |
| if ( t - _lastTime < _secondsBetween ) | | if ( t - _lastTime < _secondsBetween ) | |
| return false; | | return false; | |
| | | | |
| if ( _total > 0 ){ | | if ( _total > 0 ){ | |
| | | | |
| skipping to change at line 426 | | skipping to change at line 501 | |
| } | | } | |
| | | | |
| long long done(){ | | long long done(){ | |
| return _done; | | return _done; | |
| } | | } | |
| | | | |
| long long hits(){ | | long long hits(){ | |
| return _hits; | | return _hits; | |
| } | | } | |
| | | | |
|
| | | string toString() const { | |
| | | if ( ! _active ) | |
| | | return ""; | |
| | | stringstream buf; | |
| | | buf << _done << "/" << _total << " " << (_done*100)/_total << " | |
| | | %"; | |
| | | return buf.str(); | |
| | | } | |
| private: | | private: | |
| | | | |
|
| | | bool _active; | |
| | | | |
| long long _total; | | long long _total; | |
| int _secondsBetween; | | int _secondsBetween; | |
| int _checkInterval; | | int _checkInterval; | |
| | | | |
| long long _done; | | long long _done; | |
| long long _hits; | | long long _hits; | |
| int _lastTime; | | int _lastTime; | |
| }; | | }; | |
| | | | |
| class TicketHolder { | | class TicketHolder { | |
| public: | | public: | |
| TicketHolder( int num ){ | | TicketHolder( int num ){ | |
| _outof = num; | | _outof = num; | |
| _num = num; | | _num = num; | |
| } | | } | |
| | | | |
| bool tryAcquire(){ | | bool tryAcquire(){ | |
|
| boostlock lk( _mutex ); | | scoped_lock lk( _mutex ); | |
| if ( _num <= 0 ){ | | if ( _num <= 0 ){ | |
| if ( _num < 0 ){ | | if ( _num < 0 ){ | |
| cerr << "DISASTER! in TicketHolder" << endl; | | cerr << "DISASTER! in TicketHolder" << endl; | |
| } | | } | |
| return false; | | return false; | |
| } | | } | |
| _num--; | | _num--; | |
| return true; | | return true; | |
| } | | } | |
| | | | |
| void release(){ | | void release(){ | |
|
| boostlock lk( _mutex ); | | scoped_lock lk( _mutex ); | |
| _num++; | | _num++; | |
| } | | } | |
| | | | |
| void resize( int newSize ){ | | void resize( int newSize ){ | |
|
| boostlock lk( _mutex ); | | scoped_lock lk( _mutex ); | |
| int used = _outof - _num; | | int used = _outof - _num; | |
| if ( used > newSize ){ | | if ( used > newSize ){ | |
| cout << "ERROR: can't resize since we're using (" << used <
< ") more than newSize(" << newSize << ")" << endl; | | cout << "ERROR: can't resize since we're using (" << used <
< ") more than newSize(" << newSize << ")" << endl; | |
| return; | | return; | |
| } | | } | |
| | | | |
| _outof = newSize; | | _outof = newSize; | |
| _num = _outof - used; | | _num = _outof - used; | |
| } | | } | |
| | | | |
| | | | |
| skipping to change at line 484 | | skipping to change at line 568 | |
| return _num; | | return _num; | |
| } | | } | |
| | | | |
| int used(){ | | int used(){ | |
| return _outof - _num; | | return _outof - _num; | |
| } | | } | |
| | | | |
| private: | | private: | |
| int _outof; | | int _outof; | |
| int _num; | | int _num; | |
|
| boost::mutex _mutex; | | mongo::mutex _mutex; | |
| }; | | }; | |
| | | | |
| class TicketHolderReleaser { | | class TicketHolderReleaser { | |
| public: | | public: | |
| TicketHolderReleaser( TicketHolder * holder ){ | | TicketHolderReleaser( TicketHolder * holder ){ | |
| _holder = holder; | | _holder = holder; | |
| } | | } | |
| | | | |
| ~TicketHolderReleaser(){ | | ~TicketHolderReleaser(){ | |
| _holder->release(); | | _holder->release(); | |
| | | | |
| skipping to change at line 558 | | skipping to change at line 642 | |
| return _buf[0] == 0; | | return _buf[0] == 0; | |
| } | | } | |
| | | | |
| private: | | private: | |
| size_t _size; | | size_t _size; | |
| char * _buf; | | char * _buf; | |
| }; | | }; | |
| | | | |
| ostream& operator<<( ostream &s, const ThreadSafeString &o ); | | ostream& operator<<( ostream &s, const ThreadSafeString &o ); | |
| | | | |
|
| | | inline bool isNumber( char c ) { | |
| | | return c >= '0' && c <= '9'; | |
| | | } | |
| | | | |
| | | // for convenience, '{' is greater than anything and stops number parsi | |
| | | ng | |
| | | inline int lexNumCmp( const char *s1, const char *s2 ) { | |
| | | int nret = 0; | |
| | | while( *s1 && *s2 ) { | |
| | | bool p1 = ( *s1 == '{' ); | |
| | | bool p2 = ( *s2 == '{' ); | |
| | | if ( p1 && !p2 ) | |
| | | return 1; | |
| | | if ( p2 && !p1 ) | |
| | | return -1; | |
| | | bool n1 = isNumber( *s1 ); | |
| | | bool n2 = isNumber( *s2 ); | |
| | | if ( n1 && n2 ) { | |
| | | if ( nret == 0 ) { | |
| | | nret = *s1 > *s2 ? 1 : ( *s1 == *s2 ? 0 : -1 ); | |
| | | } | |
| | | } else if ( n1 ) { | |
| | | return 1; | |
| | | } else if ( n2 ) { | |
| | | return -1; | |
| | | } else { | |
| | | if ( nret ) { | |
| | | return nret; | |
| | | } | |
| | | if ( *s1 > *s2 ) { | |
| | | return 1; | |
| | | } else if ( *s2 > *s1 ) { | |
| | | return -1; | |
| | | } | |
| | | nret = 0; | |
| | | } | |
| | | ++s1; ++s2; | |
| | | } | |
| | | if ( *s1 ) { | |
| | | return 1; | |
| | | } else if ( *s2 ) { | |
| | | return -1; | |
| | | } | |
| | | return nret; | |
| | | } | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 14 change blocks. |
| 14 lines changed or deleted | | 150 lines changed or added | |
|
| jsobj.h | | jsobj.h | |
| | | | |
| skipping to change at line 996 | | skipping to change at line 996 | |
| NE = 0x9, | | NE = 0x9, | |
| opSIZE = 0x0A, | | opSIZE = 0x0A, | |
| opALL = 0x0B, | | opALL = 0x0B, | |
| NIN = 0x0C, | | NIN = 0x0C, | |
| opEXISTS = 0x0D, | | opEXISTS = 0x0D, | |
| opMOD = 0x0E, | | opMOD = 0x0E, | |
| opTYPE = 0x0F, | | opTYPE = 0x0F, | |
| opREGEX = 0x10, | | opREGEX = 0x10, | |
| opOPTIONS = 0x11, | | opOPTIONS = 0x11, | |
| opELEM_MATCH = 0x12, | | opELEM_MATCH = 0x12, | |
|
| opNEAR = 0x13 | | opNEAR = 0x13, | |
| | | opWITHIN = 0x14, | |
| }; | | }; | |
| }; | | }; | |
| ostream& operator<<( ostream &s, const BSONObj &o ); | | ostream& operator<<( ostream &s, const BSONObj &o ); | |
| ostream& operator<<( ostream &s, const BSONElement &e ); | | ostream& operator<<( ostream &s, const BSONElement &e ); | |
| | | | |
| struct BSONArray: BSONObj { | | struct BSONArray: BSONObj { | |
| // Don't add anything other than forwarding constructors!!! | | // Don't add anything other than forwarding constructors!!! | |
| BSONArray(): BSONObj() {} | | BSONArray(): BSONObj() {} | |
| explicit BSONArray(const BSONObj& obj): BSONObj(obj) {} | | explicit BSONArray(const BSONObj& obj): BSONObj(obj) {} | |
| }; | | }; | |
| | | | |
| skipping to change at line 1312 | | skipping to change at line 1313 | |
| } | | } | |
| | | | |
| void appendNumber( const string& fieldName , long long l ){ | | void appendNumber( const string& fieldName , long long l ){ | |
| static long long maxInt = (int)pow( 2.0 , 30.0 ); | | static long long maxInt = (int)pow( 2.0 , 30.0 ); | |
| static long long maxDouble = (long long)pow( 2.0 , 40.0 ); | | static long long maxDouble = (long long)pow( 2.0 , 40.0 ); | |
| | | | |
| if ( l < maxInt ) | | if ( l < maxInt ) | |
| append( fieldName.c_str() , (int)l ); | | append( fieldName.c_str() , (int)l ); | |
| else if ( l < maxDouble ) | | else if ( l < maxDouble ) | |
| append( fieldName.c_str() , (double)l ); | | append( fieldName.c_str() , (double)l ); | |
|
| append( fieldName.c_str() , l ); | | else | |
| | | append( fieldName.c_str() , l ); | |
| } | | } | |
| | | | |
| /** Append a double element */ | | /** Append a double element */ | |
| BSONObjBuilder& append(const char *fieldName, double n) { | | BSONObjBuilder& append(const char *fieldName, double n) { | |
| b.append((char) NumberDouble); | | b.append((char) NumberDouble); | |
| b.append(fieldName); | | b.append(fieldName); | |
| b.append(n); | | b.append(n); | |
| return *this; | | return *this; | |
| } | | } | |
| | | | |
| | | | |
| skipping to change at line 1649 | | skipping to change at line 1651 | |
| | | | |
| BufBuilder &b; | | BufBuilder &b; | |
| BufBuilder buf_; | | BufBuilder buf_; | |
| int offset_; | | int offset_; | |
| BSONObjBuilderValueStream s_; | | BSONObjBuilderValueStream s_; | |
| BSONSizeTracker * _tracker; | | BSONSizeTracker * _tracker; | |
| }; | | }; | |
| | | | |
| class BSONArrayBuilder : boost::noncopyable{ | | class BSONArrayBuilder : boost::noncopyable{ | |
| public: | | public: | |
|
| BSONArrayBuilder() :i(0), b() {} | | BSONArrayBuilder() : _i(0), _b() {} | |
| | | BSONArrayBuilder( BufBuilder &b ) : _i(0), _b(b) {} | |
| | | | |
| template <typename T> | | template <typename T> | |
| BSONArrayBuilder& append(const T& x){ | | BSONArrayBuilder& append(const T& x){ | |
|
| b.append(num().c_str(), x); | | _b.append(num().c_str(), x); | |
| return *this; | | return *this; | |
| } | | } | |
| | | | |
| BSONArrayBuilder& append(const BSONElement& e){ | | BSONArrayBuilder& append(const BSONElement& e){ | |
|
| b.appendAs(e, num().c_str()); | | _b.appendAs(e, num().c_str()); | |
| return *this; | | return *this; | |
| } | | } | |
| | | | |
| template <typename T> | | template <typename T> | |
| BSONArrayBuilder& operator<<(const T& x){ | | BSONArrayBuilder& operator<<(const T& x){ | |
| return append(x); | | return append(x); | |
| } | | } | |
| | | | |
|
| BSONArray arr(){ return BSONArray(b.obj()); } | | void appendNull() { | |
| | | _b.appendNull(num().c_str()); | |
| | | } | |
| | | | |
| | | BSONArray arr(){ return BSONArray(_b.obj()); } | |
| | | | |
| | | BSONObj done() { return _b.done(); } | |
| | | | |
| | | template <typename T> | |
| | | BSONArrayBuilder& append(const char *name, const T& x){ | |
| | | fill( name ); | |
| | | append( x ); | |
| | | return *this; | |
| | | } | |
| | | | |
| | | BufBuilder &subobjStart( const char *name ) { | |
| | | fill( name ); | |
| | | return _b.subobjStart( num().c_str() ); | |
| | | } | |
| | | | |
| | | BufBuilder &subarrayStart( const char *name ) { | |
| | | fill( name ); | |
| | | return _b.subarrayStart( num().c_str() ); | |
| | | } | |
| | | | |
| | | void appendArray( const char *name, BSONObj subObj ) { | |
| | | fill( name ); | |
| | | _b.appendArray( num().c_str(), subObj ); | |
| | | } | |
| | | | |
| | | void appendAs( const BSONElement &e, const char *name ) { | |
| | | fill( name ); | |
| | | append( e ); | |
| | | } | |
| | | | |
| private: | | private: | |
|
| string num(){ return b.numStr(i++); } | | void fill( const char *name ) { | |
| int i; | | char *r; | |
| BSONObjBuilder b; | | int n = strtol( name, &r, 10 ); | |
| | | uassert( 13048, "can't append to array using string field name" | |
| | | , !*r ); | |
| | | while( _i < n ) | |
| | | append( nullElt() ); | |
| | | } | |
| | | | |
| | | static BSONElement nullElt() { | |
| | | static BSONObj n = nullObj(); | |
| | | return n.firstElement(); | |
| | | } | |
| | | | |
| | | static BSONObj nullObj() { | |
| | | BSONObjBuilder b; | |
| | | b.appendNull( "" ); | |
| | | return b.obj(); | |
| | | } | |
| | | | |
| | | string num(){ return _b.numStr(_i++); } | |
| | | int _i; | |
| | | BSONObjBuilder _b; | |
| }; | | }; | |
| | | | |
| /** iterator for a BSONObj | | /** iterator for a BSONObj | |
| | | | |
| Note each BSONObj ends with an EOO element: so you will get more() o
n an empty | | Note each BSONObj ends with an EOO element: so you will get more() o
n an empty | |
| object, although next().eoo() will be true. | | object, although next().eoo() will be true. | |
| | | | |
| todo: we may want to make a more stl-like iterator interface for thi
s | | todo: we may want to make a more stl-like iterator interface for thi
s | |
| with things like begin() and end() | | with things like begin() and end() | |
| */ | | */ | |
| | | | |
| skipping to change at line 1963 | | skipping to change at line 2018 | |
| s.insert( it.next() ); | | s.insert( it.next() ); | |
| return s; | | return s; | |
| } | | } | |
| | | | |
| class BSONObjIteratorSorted { | | class BSONObjIteratorSorted { | |
| public: | | public: | |
| BSONObjIteratorSorted( const BSONObj& o ); | | BSONObjIteratorSorted( const BSONObj& o ); | |
| | | | |
| ~BSONObjIteratorSorted(){ | | ~BSONObjIteratorSorted(){ | |
| assert( _fields ); | | assert( _fields ); | |
|
| delete _fields; | | delete[] _fields; | |
| _fields = 0; | | _fields = 0; | |
| } | | } | |
| | | | |
| bool more(){ | | bool more(){ | |
| return _cur < _nfields; | | return _cur < _nfields; | |
| } | | } | |
| | | | |
| BSONElement next(){ | | BSONElement next(){ | |
| assert( _fields ); | | assert( _fields ); | |
| if ( _cur < _nfields ) | | if ( _cur < _nfields ) | |
| | | | |
End of changes. 8 change blocks. |
| 10 lines changed or deleted | | 66 lines changed or added | |
|
| matcher.h | | matcher.h | |
| | | | |
| skipping to change at line 37 | | skipping to change at line 37 | |
| | | | |
| class CoveredIndexMatcher; | | class CoveredIndexMatcher; | |
| class Matcher; | | class Matcher; | |
| | | | |
| 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; | |
|
| pcrecpp::RE *re; | | shared_ptr< pcrecpp::RE > re; | |
| bool isNot; | | bool isNot; | |
|
| RegexMatcher() : re( 0 ), isNot() {} | | RegexMatcher() : isNot() {} | |
| ~RegexMatcher() { | | | |
| delete re; | | | |
| } | | | |
| }; | | }; | |
| | | | |
| 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; | |
| | | | |
| skipping to change at line 72 | | skipping to change at line 69 | |
| ElementMatcher( BSONElement _e , int _op, bool _isNot ); | | ElementMatcher( BSONElement _e , int _op, bool _isNot ); | |
| | | | |
| ElementMatcher( BSONElement _e , int _op , const BSONObj& array, bo
ol _isNot ); | | ElementMatcher( BSONElement _e , int _op , const BSONObj& array, bo
ol _isNot ); | |
| | | | |
| ~ElementMatcher() { } | | ~ElementMatcher() { } | |
| | | | |
| BSONElement toMatch; | | BSONElement toMatch; | |
| int compareOp; | | int compareOp; | |
| bool isNot; | | bool isNot; | |
| shared_ptr< set<BSONElement,element_lt> > myset; | | shared_ptr< set<BSONElement,element_lt> > myset; | |
|
| | | shared_ptr< vector<RegexMatcher> > myregex; | |
| | | | |
| // these are for specific operators | | // these are for specific operators | |
| int mod; | | int mod; | |
| int modm; | | int modm; | |
| BSONType type; | | BSONType type; | |
| | | | |
| shared_ptr<Matcher> subMatcher; | | shared_ptr<Matcher> subMatcher; | |
| | | | |
| vector< shared_ptr<Matcher> > allMatchers; | | vector< shared_ptr<Matcher> > allMatchers; | |
| }; | | }; | |
| | | | |
| class Where; // used for $where javascript eval | | class Where; // used for $where javascript eval | |
| class DiskLoc; | | class DiskLoc; | |
| | | | |
|
| | | struct MatchDetails { | |
| | | MatchDetails(){ | |
| | | reset(); | |
| | | } | |
| | | | |
| | | void reset(){ | |
| | | loadedObject = false; | |
| | | elemMatchKey = 0; | |
| | | } | |
| | | | |
| | | string toString() const { | |
| | | stringstream ss; | |
| | | ss << "loadedObject: " << loadedObject << " "; | |
| | | ss << "elemMatchKey: " << ( elemMatchKey ? elemMatchKey : "NULL | |
| | | " ) << " "; | |
| | | return ss.str(); | |
| | | } | |
| | | | |
| | | bool loadedObject; | |
| | | const char * elemMatchKey; // warning, this may go out of scope if | |
| | | matched object does | |
| | | }; | |
| | | | |
| /* Match BSON objects against a query pattern. | | /* Match BSON objects against a query pattern. | |
| | | | |
| e.g. | | e.g. | |
| db.foo.find( { a : 3 } ); | | db.foo.find( { a : 3 } ); | |
| | | | |
| { a : 3 } is the pattern object. See wiki documentation for full in
fo. | | { a : 3 } is the pattern object. See wiki documentation for full in
fo. | |
| | | | |
| GT/LT: | | GT/LT: | |
| { a : { $gt : 3 } } | | { a : { $gt : 3 } } | |
| Not equal: | | Not equal: | |
| { a : { $ne : 3 } } | | { a : { $ne : 3 } } | |
| | | | |
| TODO: we should rewrite the matcher to be more an AST style. | | TODO: we should rewrite the matcher to be more an AST style. | |
| */ | | */ | |
| class Matcher : boost::noncopyable { | | class Matcher : boost::noncopyable { | |
| int matchesDotted( | | int matchesDotted( | |
| const char *fieldName, | | const char *fieldName, | |
| const BSONElement& toMatch, const BSONObj& obj, | | const BSONElement& toMatch, const BSONObj& obj, | |
|
| int compareOp, const ElementMatcher& bm, bool isArr = false); | | int compareOp, const ElementMatcher& bm, bool isArr , MatchDeta
ils * details ); | |
| | | | |
| int matchesNe( | | int matchesNe( | |
| const char *fieldName, | | const char *fieldName, | |
| const BSONElement &toMatch, const BSONObj &obj, | | const BSONElement &toMatch, const BSONObj &obj, | |
|
| const ElementMatcher&bm); | | const ElementMatcher&bm, MatchDetails * details ); | |
| | | | |
| public: | | public: | |
| static int opDirection(int op) { | | static int opDirection(int op) { | |
| return op <= BSONObj::LTE ? -1 : 1; | | return op <= BSONObj::LTE ? -1 : 1; | |
| } | | } | |
| | | | |
| // Only specify constrainIndexKey if matches() will be called with | | // Only specify constrainIndexKey if matches() will be called with | |
| // index keys having empty string field names. | | // index keys having empty string field names. | |
| Matcher(const BSONObj &pattern, const BSONObj &constrainIndexKey =
BSONObj()); | | Matcher(const BSONObj &pattern, const BSONObj &constrainIndexKey =
BSONObj()); | |
| | | | |
| ~Matcher(); | | ~Matcher(); | |
| | | | |
|
| bool matches(const BSONObj& j); | | bool matches(const BSONObj& j, MatchDetails * details = 0 ); | |
| | | | |
|
| bool keyMatch() const { return !all && !haveSize && !hasArray && !h
aveNot; } | | bool keyMatch() const { return !all && !haveSize && !hasArray && !h
aveNeg; } | |
| | | | |
| bool atomic() const { return _atomic; } | | bool atomic() const { return _atomic; } | |
| | | | |
|
| | | bool hasType( BSONObj::MatchType type ) const; | |
| private: | | private: | |
| void addBasic(const BSONElement &e, int c, bool isNot) { | | void addBasic(const BSONElement &e, int c, bool isNot) { | |
| // TODO May want to selectively ignore these element types base
d on op type. | | // TODO May want to selectively ignore these element types base
d on op type. | |
| if ( e.type() == MinKey || e.type() == MaxKey ) | | if ( e.type() == MinKey || e.type() == MaxKey ) | |
| return; | | return; | |
| basics.push_back( ElementMatcher( e , c, isNot ) ); | | basics.push_back( ElementMatcher( e , c, isNot ) ); | |
| } | | } | |
| | | | |
| void addRegex(const char *fieldName, const char *regex, const char
*flags, bool isNot = false); | | void addRegex(const char *fieldName, const char *regex, const char
*flags, bool isNot = false); | |
| bool addOp( const BSONElement &e, const BSONElement &fe, bool isNot
, const char *& regex, const char *&flags ); | | bool addOp( const BSONElement &e, const BSONElement &fe, bool isNot
, const char *& regex, const char *&flags ); | |
| | | | |
| int valuesMatch(const BSONElement& l, const BSONElement& r, int op,
const ElementMatcher& bm); | | int valuesMatch(const BSONElement& l, const BSONElement& r, int op,
const ElementMatcher& bm); | |
| | | | |
| Where *where; // set if query uses $where | | Where *where; // set if query uses $where | |
| BSONObj jsobj; // the query pattern. e.g., { name
: "joe" } | | BSONObj jsobj; // the query pattern. e.g., { name
: "joe" } | |
| BSONObj constrainIndexKey_; | | BSONObj constrainIndexKey_; | |
| vector<ElementMatcher> basics; | | vector<ElementMatcher> basics; | |
| bool haveSize; | | bool haveSize; | |
| bool all; | | bool all; | |
| bool hasArray; | | bool hasArray; | |
|
| bool haveNot; | | bool haveNeg; | |
| | | | |
| /* $atomic - if true, a multi document operation (some removes, upd
ates) | | /* $atomic - if true, a multi document operation (some removes, upd
ates) | |
| should be done atomically. in that case, we do not yi
eld - | | should be done atomically. in that case, we do not yi
eld - | |
| i.e. we stay locked the whole time. | | i.e. we stay locked the whole time. | |
| http://www.mongodb.org/display/DOCS/Removing[ | | http://www.mongodb.org/display/DOCS/Removing[ | |
| */ | | */ | |
| bool _atomic; | | bool _atomic; | |
| | | | |
| RegexMatcher regexs[4]; | | RegexMatcher regexs[4]; | |
| int nRegex; | | int nRegex; | |
| | | | |
| skipping to change at line 171 | | skipping to change at line 191 | |
| vector< shared_ptr< BSONObjBuilder > > _builders; | | vector< shared_ptr< BSONObjBuilder > > _builders; | |
| | | | |
| 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 matches(const BSONObj &o){ return _docMatcher.matches( o ); } | | bool matches(const BSONObj &o){ return _docMatcher.matches( o ); } | |
|
| bool matches(const BSONObj &key, const DiskLoc &recLoc , bool * loa
ded = 0 ); | | bool matches(const BSONObj &key, const DiskLoc &recLoc , MatchDetai
ls * details = 0 ); | |
| bool needRecord(){ return _needRecord; } | | bool needRecord(){ return _needRecord; } | |
| | | | |
| Matcher& docMatcher() { return _docMatcher; } | | Matcher& docMatcher() { return _docMatcher; } | |
| private: | | private: | |
| Matcher _keyMatcher; | | Matcher _keyMatcher; | |
| Matcher _docMatcher; | | Matcher _docMatcher; | |
| bool _needRecord; | | bool _needRecord; | |
| }; | | }; | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 11 change blocks. |
| 11 lines changed or deleted | | 33 lines changed or added | |
|
| miniwebserver.h | | miniwebserver.h | |
| | | | |
| skipping to change at line 20 | | skipping to change at line 20 | |
| * | | * | |
| * Unless required by applicable law or agreed to in writing, software | | * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | | * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli
ed. | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli
ed. | |
| * See the License for the specific language governing permissions and | | * See the License for the specific language governing permissions and | |
| * limitations under the License. | | * limitations under the License. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
|
| | | #include "../stdafx.h" | |
| #include "message.h" | | #include "message.h" | |
|
| | | #include "../db/jsobj.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| class MiniWebServer { | | class MiniWebServer { | |
| public: | | public: | |
| MiniWebServer(); | | MiniWebServer(); | |
| virtual ~MiniWebServer() {} | | virtual ~MiniWebServer() {} | |
| | | | |
| bool init(const string &ip, int _port); | | bool init(const string &ip, int _port); | |
| void run(); | | void run(); | |
| | | | |
| skipping to change at line 48 | | skipping to change at line 50 | |
| vector<string>& headers, // if completely empty, content-type:
text/html will be added | | vector<string>& headers, // if completely empty, content-type:
text/html will be added | |
| const SockAddr &from | | const SockAddr &from | |
| ) = 0; | | ) = 0; | |
| | | | |
| int socket() const { return sock; } | | int socket() const { return sock; } | |
| | | | |
| protected: | | protected: | |
| string parseURL( const char * buf ); | | string parseURL( const char * buf ); | |
| string parseMethod( const char * headers ); | | string parseMethod( const char * headers ); | |
| string getHeader( const char * headers , string name ); | | string getHeader( const char * headers , string name ); | |
|
| void parseParams( map<string,string> & params , string query ); | | void parseParams( BSONObj & params , string query ); | |
| static const char *body( const char *buf ); | | static const char *body( const char *buf ); | |
| | | | |
|
| | | static string urlDecode(const char* s); | |
| | | static string urlDecode(string s) {return urlDecode(s.c_str());} | |
| | | | |
| private: | | private: | |
| void accepted(int s, const SockAddr &from); | | void accepted(int s, const SockAddr &from); | |
| static bool fullReceive( const char *buf ); | | static bool fullReceive( const char *buf ); | |
| | | | |
| int port; | | int port; | |
| int sock; | | int sock; | |
| }; | | }; | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 4 change blocks. |
| 1 lines changed or deleted | | 6 lines changed or added | |
|
| namespace.h | | namespace.h | |
| | | | |
| skipping to change at line 348 | | skipping to change at line 348 | |
| multiKeyIndexBits |= (((unsigned long long) 1) << i); | | multiKeyIndexBits |= (((unsigned long long) 1) << i); | |
| } | | } | |
| void clearIndexIsMultikey(int i) { | | void clearIndexIsMultikey(int i) { | |
| dassert( i < NIndexesMax ); | | dassert( i < NIndexesMax ); | |
| multiKeyIndexBits &= ~(((unsigned long long) 1) << i); | | multiKeyIndexBits &= ~(((unsigned long long) 1) << i); | |
| } | | } | |
| | | | |
| /* add a new index. does not add to system.indexes etc. - just to
NamespaceDetails. | | /* add a new index. does not add to system.indexes etc. - just to
NamespaceDetails. | |
| caller must populate returned object. | | caller must populate returned object. | |
| */ | | */ | |
|
| IndexDetails& addIndex(const char *thisns); | | IndexDetails& addIndex(const char *thisns, bool resetTransient=true
); | |
| | | | |
| void aboutToDeleteAnIndex() { | | void aboutToDeleteAnIndex() { | |
| flags &= ~Flag_HaveIdIndex; | | flags &= ~Flag_HaveIdIndex; | |
| } | | } | |
| | | | |
| void cappedDisallowDelete() { | | void cappedDisallowDelete() { | |
| flags |= Flag_CappedDisallowDelete; | | flags |= Flag_CappedDisallowDelete; | |
| } | | } | |
| | | | |
| /* returns index of the first index in which the field is present.
-1 if not present. */ | | /* returns index of the first index in which the field is present.
-1 if not present. */ | |
| | | | |
| skipping to change at line 508 | | skipping to change at line 508 | |
| set<string>& indexKeys() { | | set<string>& indexKeys() { | |
| DEV assertInWriteLock(); | | DEV assertInWriteLock(); | |
| if ( !_keysComputed ) | | if ( !_keysComputed ) | |
| computeIndexKeys(); | | computeIndexKeys(); | |
| return _indexKeys; | | return _indexKeys; | |
| } | | } | |
| | | | |
| /* IndexSpec caching */ | | /* IndexSpec caching */ | |
| private: | | private: | |
| map<const IndexDetails*,IndexSpec> _indexSpecs; | | map<const IndexDetails*,IndexSpec> _indexSpecs; | |
|
| static boost::mutex _isMutex; | | static mongo::mutex _isMutex; | |
| public: | | public: | |
| const IndexSpec& getIndexSpec( const IndexDetails * details ){ | | const IndexSpec& getIndexSpec( const IndexDetails * details ){ | |
| IndexSpec& spec = _indexSpecs[details]; | | IndexSpec& spec = _indexSpecs[details]; | |
| if ( ! spec._finishedInit ){ | | if ( ! spec._finishedInit ){ | |
|
| boostlock lk(_isMutex); | | scoped_lock lk(_isMutex); | |
| if ( ! spec._finishedInit ){ | | if ( ! spec._finishedInit ){ | |
| spec.reset( details ); | | spec.reset( details ); | |
| assert( spec._finishedInit ); | | assert( spec._finishedInit ); | |
| } | | } | |
| } | | } | |
| return spec; | | return spec; | |
| } | | } | |
| | | | |
| /* query cache (for query optimizer) ------------------------------
------- */ | | /* query cache (for query optimizer) ------------------------------
------- */ | |
| private: | | private: | |
| int _qcWriteCount; | | int _qcWriteCount; | |
| map< QueryPattern, pair< BSONObj, long long > > _qcCache; | | map< QueryPattern, pair< BSONObj, long long > > _qcCache; | |
| public: | | public: | |
|
| static boost::mutex _qcMutex; | | static mongo::mutex _qcMutex; | |
| /* you must be in the qcMutex when calling this (and using the retu
rned val): */ | | /* you must be in the qcMutex when calling this (and using the retu
rned val): */ | |
| static NamespaceDetailsTransient& get_inlock(const char *ns) { | | static NamespaceDetailsTransient& get_inlock(const char *ns) { | |
| return _get(ns); | | return _get(ns); | |
| } | | } | |
| void clearQueryCache() { // public for unit tests | | void clearQueryCache() { // public for unit tests | |
| _qcCache.clear(); | | _qcCache.clear(); | |
| _qcWriteCount = 0; | | _qcWriteCount = 0; | |
| } | | } | |
| /* you must notify the cache if you are doing writes, as query plan
optimality will change */ | | /* you must notify the cache if you are doing writes, as query plan
optimality will change */ | |
| void notifyOfWriteOp() { | | void notifyOfWriteOp() { | |
| | | | |
End of changes. 4 change blocks. |
| 4 lines changed or deleted | | 4 lines changed or added | |
|
| optime.h | | optime.h | |
| | | | |
| skipping to change at line 23 | | skipping to change at line 23 | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli
ed. | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli
ed. | |
| * See the License for the specific language governing permissions and | | * See the License for the specific language governing permissions and | |
| * limitations under the License. | | * limitations under the License. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include "../db/concurrency.h" | | #include "../db/concurrency.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
|
| | | void exitCleanly( int code ); | |
| | | | |
| /* Operation sequence #. A combination of current second plus an ordin
al value. | | /* Operation sequence #. A combination of current second plus an ordin
al value. | |
| */ | | */ | |
|
| | | struct ClockSkewException : public DBException { | |
| | | virtual const char* what() const throw() { return "clock skew excep | |
| | | tion"; } | |
| | | virtual int getCode(){ return 20001; } | |
| | | }; | |
| | | | |
| #pragma pack(4) | | #pragma pack(4) | |
| class OpTime { | | class OpTime { | |
| unsigned i; | | unsigned i; | |
| unsigned secs; | | unsigned secs; | |
| static OpTime last; | | static OpTime last; | |
| public: | | public: | |
|
| | | static void setLast(const Date_t &date) { | |
| | | last = OpTime(date); | |
| | | } | |
| unsigned getSecs() const { | | unsigned getSecs() const { | |
| return secs; | | return secs; | |
| } | | } | |
| OpTime(Date_t date) { | | OpTime(Date_t date) { | |
| reinterpret_cast<unsigned long long&>(*this) = date.millis; | | reinterpret_cast<unsigned long long&>(*this) = date.millis; | |
| } | | } | |
| OpTime(unsigned long long date) { | | OpTime(unsigned long long date) { | |
| reinterpret_cast<unsigned long long&>(*this) = date; | | reinterpret_cast<unsigned long long&>(*this) = date; | |
| } | | } | |
| OpTime(unsigned a, unsigned b) { | | OpTime(unsigned a, unsigned b) { | |
| secs = a; | | secs = a; | |
| i = b; | | i = b; | |
| } | | } | |
| OpTime() { | | OpTime() { | |
| secs = 0; | | secs = 0; | |
| i = 0; | | i = 0; | |
| } | | } | |
| static OpTime now() { | | static OpTime now() { | |
| unsigned t = (unsigned) time(0); | | unsigned t = (unsigned) time(0); | |
| // DEV assertInWriteLock(); | | // DEV assertInWriteLock(); | |
|
| | | if ( t < last.secs ){ | |
| | | bool toLog = false; | |
| | | ONCE toLog = true; | |
| | | RARELY toLog = true; | |
| | | if ( last.i & 0x80000000 ) | |
| | | toLog = true; | |
| | | if ( toLog ) | |
| | | log() << "clock skew detected prev: " << last.secs << | |
| | | " now: " << t << " trying to handle..." << endl; | |
| | | if ( last.i & 0x80000000 ) { | |
| | | log() << "ERROR Large clock skew detected, shutting dow | |
| | | n" << endl; | |
| | | throw ClockSkewException(); | |
| | | } | |
| | | t = last.secs; | |
| | | } | |
| if ( last.secs == t ) { | | if ( last.secs == t ) { | |
| last.i++; | | last.i++; | |
| return last; | | return last; | |
| } | | } | |
| last = OpTime(t, 1); | | last = OpTime(t, 1); | |
| return last; | | return last; | |
| } | | } | |
| | | | |
| /* We store OpTime's in the database as BSON Date datatype -- we ne
eded some sort of | | /* We store OpTime's in the database as BSON Date datatype -- we ne
eded some sort of | |
| 64 bit "container" for these values. While these are not really "
Dates", that seems a | | 64 bit "container" for these values. While these are not really "
Dates", that seems a | |
| | | | |
End of changes. 4 change blocks. |
| 0 lines changed or deleted | | 26 lines changed or added | |
|
| reccache.h | | reccache.h | |
| | | | |
| skipping to change at line 51 | | skipping to change at line 51 | |
| Node(void* _data) : data((char *) _data) { dirty = false; newer = 0
; } | | Node(void* _data) : data((char *) _data) { dirty = false; newer = 0
; } | |
| ~Node() { | | ~Node() { | |
| free(data); | | free(data); | |
| data = 0; | | data = 0; | |
| } | | } | |
| char *data; | | char *data; | |
| DiskLoc loc; | | DiskLoc loc; | |
| bool dirty; | | bool dirty; | |
| Node *older, *newer; // lru | | Node *older, *newer; // lru | |
| }; | | }; | |
|
| boost::mutex &rcmutex; // mainly to coordinate with the lazy writer thr
ead | | mongo::mutex rcmutex; // mainly to coordinate with the lazy writer thre
ad | |
| unsigned recsize; | | unsigned recsize; | |
| map<DiskLoc, Node*> m; // the cache | | map<DiskLoc, Node*> m; // the cache | |
| Node *newest, *oldest; | | Node *newest, *oldest; | |
| unsigned nnodes; | | unsigned nnodes; | |
| set<DiskLoc> dirtyl; | | set<DiskLoc> dirtyl; | |
| vector<BasicRecStore*> stores; // DiskLoc::a() indicates the index into
this vector | | vector<BasicRecStore*> stores; // DiskLoc::a() indicates the index into
this vector | |
| map<string, BasicRecStore*> storesByNsKey; // nskey -> BasicRecStore* | | map<string, BasicRecStore*> storesByNsKey; // nskey -> BasicRecStore* | |
| public: | | public: | |
| static unsigned MAXNODES; | | static unsigned MAXNODES; | |
| enum BaseValue { Base = 10000 }; | | enum BaseValue { Base = 10000 }; | |
| | | | |
| skipping to change at line 136 | | skipping to change at line 136 | |
| fileofs fileOfs(DiskLoc d) { | | fileofs fileOfs(DiskLoc d) { | |
| return ((fileofs) d.getOfs()) * recsize; | | return ((fileofs) d.getOfs()) * recsize; | |
| } | | } | |
| | | | |
| void dump(); | | void dump(); | |
| void _ejectOld(); | | void _ejectOld(); | |
| | | | |
| public: | | public: | |
| /* all public functions (except constructor) should use the mutex */ | | /* all public functions (except constructor) should use the mutex */ | |
| | | | |
|
| RecCache(unsigned recsz) : rcmutex( *( new boost::mutex() ) ), recsize(
recsz) { | | RecCache(unsigned recsz) : recsize(recsz) { | |
| nnodes = 0; | | nnodes = 0; | |
| newest = oldest = 0; | | newest = oldest = 0; | |
| } | | } | |
| | | | |
| /* call this after doing some work, after you are sure you are done wit
h modifications. | | /* call this after doing some work, after you are sure you are done wit
h modifications. | |
| we call it from dbunlocking(). | | we call it from dbunlocking(). | |
| */ | | */ | |
| void ejectOld() { | | void ejectOld() { | |
| if( nnodes > MAXNODES ) // just enough here to be inlineable for sp
eed reasons. _ejectOld does the real work | | if( nnodes > MAXNODES ) // just enough here to be inlineable for sp
eed reasons. _ejectOld does the real work | |
| _ejectOld(); | | _ejectOld(); | |
| | | | |
| skipping to change at line 158 | | skipping to change at line 158 | |
| | | | |
| /* bg writer thread invokes this */ | | /* bg writer thread invokes this */ | |
| void writeLazily(); | | void writeLazily(); | |
| | | | |
| /* Note that this may be called BEFORE the actual writing to the node | | /* Note that this may be called BEFORE the actual writing to the node | |
| takes place. We do flushing later on a dbunlocking() call, which ha
ppens | | takes place. We do flushing later on a dbunlocking() call, which ha
ppens | |
| after the writing. | | after the writing. | |
| */ | | */ | |
| void dirty(DiskLoc d) { | | void dirty(DiskLoc d) { | |
| assert( d.a() >= Base ); | | assert( d.a() >= Base ); | |
|
| boostlock lk(rcmutex); | | scoped_lock lk(rcmutex); | |
| map<DiskLoc, Node*>::iterator i = m.find(d); | | map<DiskLoc, Node*>::iterator i = m.find(d); | |
| if( i != m.end() ) { | | if( i != m.end() ) { | |
| Node *n = i->second; | | Node *n = i->second; | |
| if( !n->dirty ) { | | if( !n->dirty ) { | |
| n->dirty = true; | | n->dirty = true; | |
| dirtyl.insert(n->loc); | | dirtyl.insert(n->loc); | |
| } | | } | |
| } | | } | |
| } | | } | |
| | | | |
| char* get(DiskLoc d, unsigned len) { | | char* get(DiskLoc d, unsigned len) { | |
| assert( d.a() >= Base ); | | assert( d.a() >= Base ); | |
| assert( len == recsize ); | | assert( len == recsize ); | |
| | | | |
|
| boostlock lk(rcmutex); | | scoped_lock lk(rcmutex); | |
| map<DiskLoc, Node*>::iterator i = m.find(d); | | map<DiskLoc, Node*>::iterator i = m.find(d); | |
| if( i != m.end() ) { | | if( i != m.end() ) { | |
| touch(i->second); | | touch(i->second); | |
| return i->second->data; | | return i->second->data; | |
| } | | } | |
| | | | |
| Node *n = mkNode(); | | Node *n = mkNode(); | |
| n->loc = d; | | n->loc = d; | |
| store(d).get(fileOfs(d), n->data, recsize); // could throw exceptio
n | | store(d).get(fileOfs(d), n->data, recsize); // could throw exceptio
n | |
| m.insert( pair<DiskLoc, Node*>(d, n) ); | | m.insert( pair<DiskLoc, Node*>(d, n) ); | |
| return n->data; | | return n->data; | |
| } | | } | |
| | | | |
| void drop(const char *ns); | | void drop(const char *ns); | |
| | | | |
| DiskLoc insert(const char *ns, const void *obuf, int len, bool god) { | | DiskLoc insert(const char *ns, const void *obuf, int len, bool god) { | |
|
| boostlock lk(rcmutex); | | scoped_lock lk(rcmutex); | |
| BasicRecStore& rs = store(ns); | | BasicRecStore& rs = store(ns); | |
| fileofs o = rs.insert((const char *) obuf, len); | | fileofs o = rs.insert((const char *) obuf, len); | |
| assert( o % recsize == 0 ); | | assert( o % recsize == 0 ); | |
| fileofs recnum = o / recsize; | | fileofs recnum = o / recsize; | |
| massert( 10377 , "RecCache file too large?", recnum <= 0x7fffffff
); | | massert( 10377 , "RecCache file too large?", recnum <= 0x7fffffff
); | |
| Node *n = mkNode(); | | Node *n = mkNode(); | |
| memcpy(n->data, obuf, len); | | memcpy(n->data, obuf, len); | |
| DiskLoc d(rs.fileNumber + Base, (int) recnum); | | DiskLoc d(rs.fileNumber + Base, (int) recnum); | |
| n->loc = d; | | n->loc = d; | |
| m[d] = n; | | m[d] = n; | |
| | | | |
End of changes. 5 change blocks. |
| 5 lines changed or deleted | | 5 lines changed or added | |
|
| repl.h | | repl.h | |
| | | | |
| skipping to change at line 35 | | skipping to change at line 35 | |
| local.oplog.$<source> | | local.oplog.$<source> | |
| local.oplog.$main is the default | | local.oplog.$main is the default | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include "pdfile.h" | | #include "pdfile.h" | |
| #include "db.h" | | #include "db.h" | |
| #include "dbhelpers.h" | | #include "dbhelpers.h" | |
| #include "query.h" | | #include "query.h" | |
|
| | | #include "queryoptimizer.h" | |
| | | | |
| #include "../client/dbclient.h" | | #include "../client/dbclient.h" | |
| | | | |
| #include "../util/optime.h" | | #include "../util/optime.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| class DBClientConnection; | | class DBClientConnection; | |
| class DBClientCursor; | | class DBClientCursor; | |
| | | | |
| | | | |
| skipping to change at line 200 | | skipping to change at line 201 | |
| void forceResync( const char *requester ); | | void forceResync( const char *requester ); | |
| }; | | }; | |
| | | | |
| /* Write operation to the log (local.oplog.$main) | | /* Write operation to the log (local.oplog.$main) | |
| "i" insert | | "i" insert | |
| "u" update | | "u" update | |
| "d" delete | | "d" delete | |
| "c" db cmd | | "c" db cmd | |
| "db" declares presence of a database (ns is set to the db name + '.'
) | | "db" declares presence of a database (ns is set to the db name + '.'
) | |
| */ | | */ | |
|
| void _logOp(const char *opstr, const char *ns, const char *logNs, const
BSONObj& obj, BSONObj *patt, bool *b, const OpTime &ts); | | | |
| void logOp(const char *opstr, const char *ns, const BSONObj& obj, BSONO
bj *patt = 0, bool *b = 0); | | void logOp(const char *opstr, const char *ns, const BSONObj& obj, BSONO
bj *patt = 0, bool *b = 0); | |
| | | | |
| // class for managing a set of ids in memory | | // class for managing a set of ids in memory | |
| class MemIds { | | class MemIds { | |
| public: | | public: | |
| MemIds() : size_() {} | | MemIds() : size_() {} | |
| friend class IdTracker; | | friend class IdTracker; | |
| void reset() { imp_.clear(); } | | void reset() { imp_.clear(); } | |
| bool get( const char *ns, const BSONObj &id ) { return imp_[ ns ].c
ount( id ); } | | bool get( const char *ns, const BSONObj &id ) { return imp_[ ns ].c
ount( id ); } | |
| void set( const char *ns, const BSONObj &id, bool val ) { | | void set( const char *ns, const BSONObj &id, bool val ) { | |
| | | | |
| skipping to change at line 342 | | skipping to change at line 342 | |
| MemIds memModIds_; | | MemIds memModIds_; | |
| DbIds dbIds_; | | DbIds dbIds_; | |
| DbIds dbModIds_; | | DbIds dbModIds_; | |
| bool inMem_; | | bool inMem_; | |
| int maxMem_; | | int maxMem_; | |
| }; | | }; | |
| | | | |
| bool anyReplEnabled(); | | bool anyReplEnabled(); | |
| void appendReplicationInfo( BSONObjBuilder& result , bool authed , int
level = 0 ); | | void appendReplicationInfo( BSONObjBuilder& result , bool authed , int
level = 0 ); | |
| | | | |
|
| | | void replCheckCloseDatabase( Database * db ); | |
| | | | |
| | | extern int __findingStartInitialTimeout; // configurable for testing | |
| | | | |
| | | class FindingStartCursor { | |
| | | public: | |
| | | FindingStartCursor( const QueryPlan & qp ) : | |
| | | _qp( qp ), | |
| | | _findingStart( true ), | |
| | | _findingStartMode(), | |
| | | _findingStartTimer( 0 ), | |
| | | _findingStartCursor( 0 ) | |
| | | { init(); } | |
| | | bool done() const { return !_findingStart; } | |
| | | auto_ptr< Cursor > cRelease() { return _c; } | |
| | | void next() { | |
| | | if ( !_findingStartCursor || !_findingStartCursor->c->ok() ) { | |
| | | _findingStart = false; | |
| | | _c = _qp.newCursor(); // on error, start from beginning | |
| | | destroyClientCursor(); | |
| | | return; | |
| | | } | |
| | | switch( _findingStartMode ) { | |
| | | case Initial: { | |
| | | if ( !_matcher->matches( _findingStartCursor->c->currKe | |
| | | y(), _findingStartCursor->c->currLoc() ) ) { | |
| | | _findingStart = false; // found first record out of | |
| | | query range, so scan normally | |
| | | _c = _qp.newCursor( _findingStartCursor->c->currLoc | |
| | | () ); | |
| | | destroyClientCursor(); | |
| | | return; | |
| | | } | |
| | | _findingStartCursor->c->advance(); | |
| | | RARELY { | |
| | | if ( _findingStartTimer.seconds() >= __findingStart | |
| | | InitialTimeout ) { | |
| | | createClientCursor( startLoc( _findingStartCurs | |
| | | or->c->currLoc() ) ); | |
| | | _findingStartMode = FindExtent; | |
| | | return; | |
| | | } | |
| | | } | |
| | | maybeRelease(); | |
| | | return; | |
| | | } | |
| | | case FindExtent: { | |
| | | if ( !_matcher->matches( _findingStartCursor->c->currKe | |
| | | y(), _findingStartCursor->c->currLoc() ) ) { | |
| | | _findingStartMode = InExtent; | |
| | | return; | |
| | | } | |
| | | DiskLoc prev = prevLoc( _findingStartCursor->c->currLoc | |
| | | () ); | |
| | | if ( prev.isNull() ) { // hit beginning, so start scann | |
| | | ing from here | |
| | | createClientCursor(); | |
| | | _findingStartMode = InExtent; | |
| | | return; | |
| | | } | |
| | | // There might be a more efficient implementation than | |
| | | creating new cursor & client cursor each time, | |
| | | // not worrying about that for now | |
| | | createClientCursor( prev ); | |
| | | maybeRelease(); | |
| | | return; | |
| | | } | |
| | | case InExtent: { | |
| | | if ( _matcher->matches( _findingStartCursor->c->currKey | |
| | | (), _findingStartCursor->c->currLoc() ) ) { | |
| | | _findingStart = false; // found first record in que | |
| | | ry range, so scan normally | |
| | | _c = _qp.newCursor( _findingStartCursor->c->currLoc | |
| | | () ); | |
| | | destroyClientCursor(); | |
| | | return; | |
| | | } | |
| | | _findingStartCursor->c->advance(); | |
| | | maybeRelease(); | |
| | | return; | |
| | | } | |
| | | default: { | |
| | | massert( 12600, "invalid _findingStartMode", false ); | |
| | | } | |
| | | } | |
| | | } | |
| | | private: | |
| | | enum FindingStartMode { Initial, FindExtent, InExtent }; | |
| | | const QueryPlan &_qp; | |
| | | bool _findingStart; | |
| | | FindingStartMode _findingStartMode; | |
| | | auto_ptr< CoveredIndexMatcher > _matcher; | |
| | | Timer _findingStartTimer; | |
| | | ClientCursor * _findingStartCursor; | |
| | | auto_ptr< Cursor > _c; | |
| | | DiskLoc startLoc( const DiskLoc &rec ) { | |
| | | Extent *e = rec.rec()->myExtent( rec ); | |
| | | if ( e->myLoc != _qp.nsd()->capExtent ) | |
| | | return e->firstRecord; | |
| | | // Likely we are on the fresh side of capExtent, so return firs | |
| | | t fresh record. | |
| | | // If we are on the stale side of capExtent, then the collectio | |
| | | n is small and it | |
| | | // doesn't matter if we start the extent scan with capFirstNewR | |
| | | ecord. | |
| | | return _qp.nsd()->capFirstNewRecord; | |
| | | } | |
| | | | |
| | | DiskLoc prevLoc( const DiskLoc &rec ) { | |
| | | Extent *e = rec.rec()->myExtent( rec ); | |
| | | if ( e->xprev.isNull() ) | |
| | | e = _qp.nsd()->lastExtent.ext(); | |
| | | else | |
| | | e = e->xprev.ext(); | |
| | | if ( e->myLoc != _qp.nsd()->capExtent ) | |
| | | return e->firstRecord; | |
| | | return DiskLoc(); // reached beginning of collection | |
| | | } | |
| | | void createClientCursor( const DiskLoc &startLoc = DiskLoc() ) { | |
| | | auto_ptr<Cursor> c = _qp.newCursor( startLoc ); | |
| | | _findingStartCursor = new ClientCursor(c, _qp.ns(), false); | |
| | | } | |
| | | void destroyClientCursor() { | |
| | | if ( _findingStartCursor ) { | |
| | | ClientCursor::erase( _findingStartCursor->cursorid ); | |
| | | _findingStartCursor = 0; | |
| | | } | |
| | | } | |
| | | void maybeRelease() { | |
| | | RARELY { | |
| | | CursorId id = _findingStartCursor->cursorid; | |
| | | _findingStartCursor->updateLocation(); | |
| | | { | |
| | | dbtemprelease t; | |
| | | } | |
| | | _findingStartCursor = ClientCursor::find( id, false ); | |
| | | } | |
| | | } | |
| | | void init() { | |
| | | // Use a ClientCursor here so we can release db mutex while sca | |
| | | nning | |
| | | // oplog (can take quite a while with large oplogs). | |
| | | auto_ptr<Cursor> c = _qp.newReverseCursor(); | |
| | | _findingStartCursor = new ClientCursor(c, _qp.ns(), false); | |
| | | _findingStartTimer.reset(); | |
| | | _findingStartMode = Initial; | |
| | | BSONElement tsElt = _qp.query()[ "ts" ]; | |
| | | massert( 13044, "no ts field in query", !tsElt.eoo() ); | |
| | | BSONObjBuilder b; | |
| | | b.append( tsElt ); | |
| | | BSONObj tsQuery = b.obj(); | |
| | | _matcher.reset(new CoveredIndexMatcher(tsQuery, _qp.indexKey()) | |
| | | ); | |
| | | } | |
| | | }; | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 3 change blocks. |
| 1 lines changed or deleted | | 157 lines changed or added | |
|
| snapshots.h | | snapshots.h | |
| | | | |
| skipping to change at line 37 | | skipping to change at line 37 | |
| */ | | */ | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| class SnapshotThread; | | class SnapshotThread; | |
| | | | |
| /** | | /** | |
| * stores a point in time snapshot | | * stores a point in time snapshot | |
| * i.e. all counters at a given time | | * i.e. all counters at a given time | |
| */ | | */ | |
| class SnapshotData { | | class SnapshotData { | |
|
| SnapshotData(); | | void takeSnapshot(); | |
| | | | |
| unsigned long long _created; | | unsigned long long _created; | |
| Top::CollectionData _globalUsage; | | Top::CollectionData _globalUsage; | |
| unsigned long long _totalWriteLockedTime; // micros of total time l
ocked | | unsigned long long _totalWriteLockedTime; // micros of total time l
ocked | |
| Top::UsageMap _usage; | | Top::UsageMap _usage; | |
| | | | |
| friend class SnapshotThread; | | friend class SnapshotThread; | |
| friend class SnapshotDelta; | | friend class SnapshotDelta; | |
|
| | | friend class Snapshots; | |
| }; | | }; | |
| | | | |
| /** | | /** | |
| * contains performance information for a time period | | * contains performance information for a time period | |
| */ | | */ | |
| class SnapshotDelta { | | class SnapshotDelta { | |
| public: | | public: | |
|
| SnapshotDelta( SnapshotData * older , SnapshotData * newer ); | | SnapshotDelta( const SnapshotData& older , const SnapshotData& newe
r ); | |
| | | | |
| unsigned long long start() const { | | unsigned long long start() const { | |
|
| return _older->_created; | | return _older._created; | |
| } | | } | |
| | | | |
| unsigned long long elapsed() const { | | unsigned long long elapsed() const { | |
| return _elapsed; | | return _elapsed; | |
| } | | } | |
| | | | |
| unsigned long long timeInWriteLock() const { | | unsigned long long timeInWriteLock() const { | |
|
| return _newer->_totalWriteLockedTime - _older->_totalWriteLocke
dTime; | | return _newer._totalWriteLockedTime - _older._totalWriteLockedT
ime; | |
| } | | } | |
| double percentWriteLocked() const { | | double percentWriteLocked() const { | |
| double e = (double) elapsed(); | | double e = (double) elapsed(); | |
| double w = (double) timeInWriteLock(); | | double w = (double) timeInWriteLock(); | |
| return w/e; | | return w/e; | |
| } | | } | |
| | | | |
| Top::CollectionData globalUsageDiff(); | | Top::CollectionData globalUsageDiff(); | |
| Top::UsageMap collectionUsageDiff(); | | Top::UsageMap collectionUsageDiff(); | |
| | | | |
| private: | | private: | |
|
| SnapshotData * _older; | | const SnapshotData& _older; | |
| SnapshotData * _newer; | | const SnapshotData& _newer; | |
| | | | |
| unsigned long long _elapsed; | | unsigned long long _elapsed; | |
| }; | | }; | |
| | | | |
| class Snapshots { | | class Snapshots { | |
| public: | | public: | |
|
| Snapshots(); | | Snapshots(int n=100); | |
| | | | |
|
| void add( SnapshotData * s ); | | const SnapshotData* takeSnapshot(); | |
| | | | |
| int numDeltas() const { return _stored-1; } | | int numDeltas() const { return _stored-1; } | |
| | | | |
|
| SnapshotData* getPrev( int numBack = 0 ); | | const SnapshotData& getPrev( int numBack = 0 ); | |
| auto_ptr<SnapshotDelta> computeDelta( int numBack = 0 ); | | auto_ptr<SnapshotDelta> computeDelta( int numBack = 0 ); | |
| | | | |
| void outputLockInfoHTML( stringstream& ss ); | | void outputLockInfoHTML( stringstream& ss ); | |
| private: | | private: | |
|
| boost::mutex _lock; | | mongo::mutex _lock; | |
| int _n; | | int _n; | |
|
| SnapshotData** _snapshots; | | boost::scoped_array<SnapshotData> _snapshots; | |
| int _loc; | | int _loc; | |
| int _stored; | | int _stored; | |
| }; | | }; | |
| | | | |
| class SnapshotThread : public BackgroundJob { | | class SnapshotThread : public BackgroundJob { | |
| public: | | public: | |
| void run(); | | void run(); | |
| }; | | }; | |
| | | | |
| extern Snapshots statsSnapshots; | | extern Snapshots statsSnapshots; | |
| | | | |
End of changes. 11 change blocks. |
| 11 lines changed or deleted | | 12 lines changed or added | |
|
| thread_pool.h | | thread_pool.h | |
| | | | |
| skipping to change at line 63 | | skipping to change at line 63 | |
| template<typename F, typename A, typename B, typename C> | | template<typename F, typename A, typename B, typename C> | |
| void schedule(F f, A a, B b, C c){ schedule(boost::bind(f,a,b,c));
} | | void schedule(F f, A a, B b, C c){ schedule(boost::bind(f,a,b,c));
} | |
| template<typename F, typename A, typename B, typename C, typename D
> | | template<typename F, typename A, typename B, typename C, typename D
> | |
| void schedule(F f, A a, B b, C c, D d){ schedule(boost::bind(f,a,b,
c,d)); } | | void schedule(F f, A a, B b, C c, D d){ schedule(boost::bind(f,a,b,
c,d)); } | |
| template<typename F, typename A, typename B, typename C, typename D
, typename E> | | template<typename F, typename A, typename B, typename C, typename D
, typename E> | |
| void schedule(F f, A a, B b, C c, D d, E e){ schedule(boost::bind(f
,a,b,c,d,e)); } | | void schedule(F f, A a, B b, C c, D d, E e){ schedule(boost::bind(f
,a,b,c,d,e)); } | |
| | | | |
| int tasks_remaining() { return _tasksRemaining; } | | int tasks_remaining() { return _tasksRemaining; } | |
| | | | |
| private: | | private: | |
|
| boost::mutex _mutex; | | mongo::mutex _mutex; | |
| boost::condition _condition; | | boost::condition _condition; | |
| | | | |
| list<Worker*> _freeWorkers; //used as LIFO stack (always front) | | list<Worker*> _freeWorkers; //used as LIFO stack (always front) | |
| list<Task> _tasks; //used as FIFO queue (push_back, pop_front) | | list<Task> _tasks; //used as FIFO queue (push_back, pop_front) | |
| int _tasksRemaining; // in queue + currently processing | | int _tasksRemaining; // in queue + currently processing | |
| int _nThreads; // only used for sanity checking. could be removed i
n the future. | | int _nThreads; // only used for sanity checking. could be removed i
n the future. | |
| | | | |
| // should only be called by a worker from the worker's thread | | // should only be called by a worker from the worker's thread | |
| void task_done(Worker* worker); | | void task_done(Worker* worker); | |
| friend class Worker; | | friend class Worker; | |
| | | | |
End of changes. 1 change blocks. |
| 1 lines changed or deleted | | 1 lines changed or added | |
|
| top.h | | top.h | |
| | | | |
| skipping to change at line 35 | | skipping to change at line 35 | |
| | | | |
| /** | | /** | |
| * tracks usage by collection | | * tracks usage by collection | |
| */ | | */ | |
| class Top { | | class Top { | |
| | | | |
| public: | | public: | |
| class UsageData { | | class UsageData { | |
| public: | | public: | |
| UsageData() : time(0) , count(0){} | | UsageData() : time(0) , count(0){} | |
|
| UsageData( UsageData& older , UsageData& newer ); | | UsageData( const UsageData& older , const UsageData& newer ); | |
| long long time; | | long long time; | |
| long long count; | | long long count; | |
| | | | |
| void inc( long long micros ){ | | void inc( long long micros ){ | |
| count++; | | count++; | |
| time += micros; | | time += micros; | |
| } | | } | |
| }; | | }; | |
| | | | |
| class CollectionData { | | class CollectionData { | |
| public: | | public: | |
| /** | | /** | |
| * constructs a diff | | * constructs a diff | |
| */ | | */ | |
| CollectionData(){} | | CollectionData(){} | |
|
| CollectionData( CollectionData& older , CollectionData& newer )
; | | CollectionData( const CollectionData& older , const CollectionD
ata& newer ); | |
| | | | |
| UsageData total; | | UsageData total; | |
| | | | |
| UsageData readLock; | | UsageData readLock; | |
| UsageData writeLock; | | UsageData writeLock; | |
| | | | |
| UsageData queries; | | UsageData queries; | |
| UsageData getmore; | | UsageData getmore; | |
| UsageData insert; | | UsageData insert; | |
| UsageData update; | | UsageData update; | |
| UsageData remove; | | UsageData remove; | |
| UsageData commands; | | UsageData commands; | |
| }; | | }; | |
| | | | |
| typedef map<string,CollectionData> UsageMap; | | typedef map<string,CollectionData> UsageMap; | |
| | | | |
| public: | | public: | |
| void record( const string& ns , int op , int lockType , long long m
icros , bool command ); | | void record( const string& ns , int op , int lockType , long long m
icros , bool command ); | |
| void append( BSONObjBuilder& b ); | | void append( BSONObjBuilder& b ); | |
|
| UsageMap cloneMap(); | | void cloneMap(UsageMap& out); | |
| CollectionData getGlobalData(){ return _global; } | | CollectionData getGlobalData(){ return _global; } | |
|
| | | void collectionDropped( const string& ns ); | |
| | | | |
| public: // static stuff | | public: // static stuff | |
| static Top global; | | static Top global; | |
| | | | |
| void append( BSONObjBuilder& b , const char * name , const UsageDat
a& map ); | | void append( BSONObjBuilder& b , const char * name , const UsageDat
a& map ); | |
| void append( BSONObjBuilder& b , const UsageMap& map ); | | void append( BSONObjBuilder& b , const UsageMap& map ); | |
| | | | |
| private: | | private: | |
| | | | |
| void _record( CollectionData& c , int op , int lockType , long long
micros , bool command ); | | void _record( CollectionData& c , int op , int lockType , long long
micros , bool command ); | |
| | | | |
|
| boost::mutex _lock; | | mongo::mutex _lock; | |
| CollectionData _global; | | CollectionData _global; | |
| UsageMap _usage; | | UsageMap _usage; | |
|
| | | string _lastDropped; | |
| }; | | }; | |
| | | | |
| /* Records per namespace utilization of the mongod process. | | /* Records per namespace utilization of the mongod process. | |
| No two functions of this class may be called concurrently. | | No two functions of this class may be called concurrently. | |
| */ | | */ | |
| class TopOld { | | class TopOld { | |
| typedef boost::posix_time::ptime T; | | typedef boost::posix_time::ptime T; | |
| typedef boost::posix_time::time_duration D; | | typedef boost::posix_time::time_duration D; | |
| typedef boost::tuple< D, int, int, int > UsageData; | | typedef boost::tuple< D, int, int, int > UsageData; | |
| public: | | public: | |
| | | | |
| skipping to change at line 118 | | skipping to change at line 120 | |
| void setRead() { _read = true; } | | void setRead() { _read = true; } | |
| | | | |
| void setWrite() { _write = true; } | | void setWrite() { _write = true; } | |
| | | | |
| void clientStop() { | | void clientStop() { | |
| if ( _currentStart == T() ) | | if ( _currentStart == T() ) | |
| return; | | return; | |
| D d = currentTime() - _currentStart; | | D d = currentTime() - _currentStart; | |
| | | | |
| { | | { | |
|
| boostlock L(topMutex); | | scoped_lock L(topMutex); | |
| recordUsage( _current, d ); | | recordUsage( _current, d ); | |
| } | | } | |
| | | | |
| _currentStart = T(); | | _currentStart = T(); | |
| _read = false; | | _read = false; | |
| _write = false; | | _write = false; | |
| } | | } | |
| | | | |
| /* these are used to fetch the stats: */ | | /* these are used to fetch the stats: */ | |
| | | | |
| struct Usage { | | struct Usage { | |
| string ns; | | string ns; | |
| D time; | | D time; | |
| double pct; | | double pct; | |
| int reads, writes, calls; | | int reads, writes, calls; | |
| }; | | }; | |
| | | | |
| static void usage( vector< Usage > &res ) { | | static void usage( vector< Usage > &res ) { | |
|
| boostlock L(topMutex); | | scoped_lock L(topMutex); | |
| | | | |
| // Populate parent namespaces | | // Populate parent namespaces | |
| UsageMap snapshot; | | UsageMap snapshot; | |
| UsageMap totalUsage; | | UsageMap totalUsage; | |
| fillParentNamespaces( snapshot, _snapshot ); | | fillParentNamespaces( snapshot, _snapshot ); | |
| fillParentNamespaces( totalUsage, _totalUsage ); | | fillParentNamespaces( totalUsage, _totalUsage ); | |
| | | | |
| multimap< D, string, more > sorted; | | multimap< D, string, more > sorted; | |
| for( UsageMap::iterator i = snapshot.begin(); i != snapshot.end
(); ++i ) | | for( UsageMap::iterator i = snapshot.begin(); i != snapshot.end
(); ++i ) | |
| sorted.insert( make_pair( i->second.get<0>(), i->first ) ); | | sorted.insert( make_pair( i->second.get<0>(), i->first ) ); | |
| | | | |
| skipping to change at line 175 | | skipping to change at line 177 | |
| u.time = i->second.get<0>(); | | u.time = i->second.get<0>(); | |
| u.pct = 0; | | u.pct = 0; | |
| u.reads = 0; | | u.reads = 0; | |
| u.writes = 0; | | u.writes = 0; | |
| u.calls = 0; | | u.calls = 0; | |
| res.push_back( u ); | | res.push_back( u ); | |
| } | | } | |
| } | | } | |
| | | | |
| static void completeSnapshot() { | | static void completeSnapshot() { | |
|
| boostlock L(topMutex); | | scoped_lock L(topMutex); | |
| | | | |
| if ( &_snapshot == &_snapshotA ) { | | if ( &_snapshot == &_snapshotA ) { | |
| _snapshot = _snapshotB; | | _snapshot = _snapshotB; | |
| _nextSnapshot = _snapshotA; | | _nextSnapshot = _snapshotA; | |
| } else { | | } else { | |
| _snapshot = _snapshotA; | | _snapshot = _snapshotA; | |
| _nextSnapshot = _snapshotB; | | _nextSnapshot = _snapshotB; | |
| } | | } | |
| _snapshotDuration = currentTime() - _snapshotStart; | | _snapshotDuration = currentTime() - _snapshotStart; | |
| _snapshotStart = currentTime(); | | _snapshotStart = currentTime(); | |
| _nextSnapshot.clear(); | | _nextSnapshot.clear(); | |
| } | | } | |
| | | | |
| private: | | private: | |
|
| static boost::mutex topMutex; | | static mongo::mutex topMutex; | |
| static bool trivialNs( const char *ns ) { | | static bool trivialNs( const char *ns ) { | |
| const char *ret = strrchr( ns, '.' ); | | const char *ret = strrchr( ns, '.' ); | |
| return ret && ret[ 1 ] == '\0'; | | return ret && ret[ 1 ] == '\0'; | |
| } | | } | |
| typedef map<string,UsageData> UsageMap; // duration, # reads, # wri
tes, # total calls | | typedef map<string,UsageData> UsageMap; // duration, # reads, # wri
tes, # total calls | |
| static T currentTime() { | | static T currentTime() { | |
| return boost::posix_time::microsec_clock::universal_time(); | | return boost::posix_time::microsec_clock::universal_time(); | |
| } | | } | |
| void recordUsage( const string &client, D duration ) { | | void recordUsage( const string &client, D duration ) { | |
| recordUsageForMap( _totalUsage, client, duration ); | | recordUsageForMap( _totalUsage, client, duration ); | |
| | | | |
End of changes. 10 change blocks. |
| 8 lines changed or deleted | | 10 lines changed or added | |
|
| update.h | | update.h | |
| | | | |
| skipping to change at line 85 | | skipping to change at line 85 | |
| break; | | break; | |
| case NumberInt: | | case NumberInt: | |
| manip.setInt( elt.numberInt() + in.numberInt() ); | | manip.setInt( elt.numberInt() + in.numberInt() ); | |
| break; | | break; | |
| default: | | default: | |
| assert(0); | | assert(0); | |
| } | | } | |
| | | | |
| } | | } | |
| | | | |
|
| void appendIncremented( BSONObjBuilder& bb , const BSONElement& in, | | template< class Builder > | |
| ModState& ms ) const; | | void appendIncremented( Builder& bb , const BSONElement& in, ModSta | |
| | | te& ms ) const; | |
| | | | |
| bool operator<( const Mod &other ) const { | | bool operator<( const Mod &other ) const { | |
| return strcmp( fieldName, other.fieldName ) < 0; | | return strcmp( fieldName, other.fieldName ) < 0; | |
| } | | } | |
| | | | |
| bool arrayDep() const { | | bool arrayDep() const { | |
| switch (op){ | | switch (op){ | |
| case PUSH: | | case PUSH: | |
| case PUSH_ALL: | | case PUSH_ALL: | |
| case POP: | | case POP: | |
| | | | |
| skipping to change at line 118 | | skipping to change at line 119 | |
| // check if there is an index key equal to mod | | // check if there is an index key equal to mod | |
| if ( idxKeys.count(fullName) ) | | if ( idxKeys.count(fullName) ) | |
| return true; | | return true; | |
| // check if there is an index key that is a child of mod | | // check if there is an index key that is a child of mod | |
| set< string >::const_iterator j = idxKeys.upper_bound( fullName
); | | set< string >::const_iterator j = idxKeys.upper_bound( fullName
); | |
| if ( j != idxKeys.end() && j->find( fullName ) == 0 && (*j)[ful
lName.size()] == '.' ) | | if ( j != idxKeys.end() && j->find( fullName ) == 0 && (*j)[ful
lName.size()] == '.' ) | |
| return true; | | return true; | |
| return false; | | return false; | |
| } | | } | |
| | | | |
|
| void apply( BSONObjBuilder& b , BSONElement in , ModState& ms ) con | | template< class Builder > | |
| st; | | void apply( Builder& b , BSONElement in , ModState& ms ) const; | |
| | | | |
| /** | | /** | |
| * @return true iff toMatch should be removed from the array | | * @return true iff toMatch should be removed from the array | |
| */ | | */ | |
| bool _pullElementMatch( BSONElement& toMatch ) const; | | bool _pullElementMatch( BSONElement& toMatch ) const; | |
| | | | |
| void _checkForAppending( const BSONElement& e ) const { | | void _checkForAppending( const BSONElement& e ) const { | |
| if ( e.type() == Object ){ | | if ( e.type() == Object ){ | |
| // this is a tiny bit slow, but rare and important | | // this is a tiny bit slow, but rare and important | |
| // only when setting something TO an object, not setting so
mething in an object | | // only when setting something TO an object, not setting so
mething in an object | |
| | | | |
| skipping to change at line 164 | | skipping to change at line 167 | |
| }; | | }; | |
| | | | |
| /** | | /** | |
| * stores a set of Mods | | * stores a set of Mods | |
| * once created, should never be changed | | * once created, should never be changed | |
| */ | | */ | |
| class ModSet : boost::noncopyable { | | class ModSet : boost::noncopyable { | |
| typedef map<string,Mod> ModHolder; | | typedef map<string,Mod> ModHolder; | |
| ModHolder _mods; | | ModHolder _mods; | |
| int _isIndexed; | | int _isIndexed; | |
|
| | | bool _hasDynamicArray; | |
| | | | |
| static void extractFields( map< string, BSONElement > &fields, cons
t BSONElement &top, const string &base ); | | static void extractFields( map< string, BSONElement > &fields, cons
t BSONElement &top, const string &base ); | |
| | | | |
| FieldCompareResult compare( const ModHolder::iterator &m, map< stri
ng, BSONElement >::iterator &p, const map< string, BSONElement >::iterator
&pEnd ) const { | | FieldCompareResult compare( const ModHolder::iterator &m, map< stri
ng, BSONElement >::iterator &p, const map< string, BSONElement >::iterator
&pEnd ) const { | |
| bool mDone = ( m == _mods.end() ); | | bool mDone = ( m == _mods.end() ); | |
| bool pDone = ( p == pEnd ); | | bool pDone = ( p == pEnd ); | |
| assert( ! mDone ); | | assert( ! mDone ); | |
| assert( ! pDone ); | | assert( ! pDone ); | |
| if ( mDone && pDone ) | | if ( mDone && pDone ) | |
| return SAME; | | return SAME; | |
| | | | |
| skipping to change at line 256 | | skipping to change at line 260 | |
| return Mod::ADDTOSET; | | return Mod::ADDTOSET; | |
| | | | |
| } | | } | |
| } | | } | |
| default: break; | | default: break; | |
| } | | } | |
| uassert( 10161 , "Invalid modifier specified " + string( fn ),
false ); | | uassert( 10161 , "Invalid modifier specified " + string( fn ),
false ); | |
| return Mod::INC; | | return Mod::INC; | |
| } | | } | |
| | | | |
|
| | | ModSet(){} | |
| | | | |
| public: | | public: | |
| | | | |
| ModSet( const BSONObj &from , | | ModSet( const BSONObj &from , | |
| const set<string>& idxKeys = set<string>(), | | const set<string>& idxKeys = set<string>(), | |
| const set<string>* backgroundKeys = 0 | | const set<string>* backgroundKeys = 0 | |
| ); | | ); | |
| | | | |
|
| | | // TODO: this is inefficient - should probably just handle when ite | |
| | | rating | |
| | | ModSet * fixDynamicArray( const char * elemMatchKey ) const; | |
| | | | |
| | | bool hasDynamicArray() const { return _hasDynamicArray; } | |
| | | | |
| /** | | /** | |
| * creates a ModSetState suitable for operation on obj | | * creates a ModSetState suitable for operation on obj | |
| * doesn't change or modify this ModSet or any underying Mod | | * doesn't change or modify this ModSet or any underying Mod | |
| */ | | */ | |
| auto_ptr<ModSetState> prepare( const BSONObj& obj ) const; | | auto_ptr<ModSetState> prepare( const BSONObj& obj ) const; | |
| | | | |
| /** | | /** | |
| * given a query pattern, builds an object suitable for an upsert | | * given a query pattern, builds an object suitable for an upsert | |
| * will take the query spec and combine all $ operators | | * will take the query spec and combine all $ operators | |
| */ | | */ | |
| | | | |
| skipping to change at line 343 | | skipping to change at line 354 | |
| | | | |
| Mod::Op op() const { | | Mod::Op op() const { | |
| return m->op; | | return m->op; | |
| } | | } | |
| | | | |
| const char * fieldName() const { | | const char * fieldName() const { | |
| return m->fieldName; | | return m->fieldName; | |
| } | | } | |
| | | | |
| bool needOpLogRewrite() const { | | bool needOpLogRewrite() const { | |
|
| if ( fixed || incType ) | | if ( fixed || fixedName || incType ) | |
| return true; | | return true; | |
| | | | |
| switch( op() ){ | | switch( op() ){ | |
| case Mod::BIT: | | case Mod::BIT: | |
| case Mod::BITAND: | | case Mod::BITAND: | |
| case Mod::BITOR: | | case Mod::BITOR: | |
| // TODO: should we convert this to $set? | | // TODO: should we convert this to $set? | |
| return false; | | return false; | |
| default: | | default: | |
| return false; | | return false; | |
| | | | |
| skipping to change at line 369 | | skipping to change at line 380 | |
| BSONObjBuilder bb( b.subobjStart( "$set" ) ); | | BSONObjBuilder bb( b.subobjStart( "$set" ) ); | |
| appendIncValue( bb ); | | appendIncValue( bb ); | |
| bb.done(); | | bb.done(); | |
| return; | | return; | |
| } | | } | |
| | | | |
| const char * name = fixedName ? fixedName : Mod::modNames[op()]
; | | const char * name = fixedName ? fixedName : Mod::modNames[op()]
; | |
| | | | |
| BSONObjBuilder bb( b.subobjStart( name ) ); | | BSONObjBuilder bb( b.subobjStart( name ) ); | |
| if ( fixed ) | | if ( fixed ) | |
|
| bb.append( *fixed ); | | bb.appendAs( *fixed , m->fieldName ); | |
| else | | else | |
| bb.append( m->elt ); | | bb.append( m->elt ); | |
| bb.done(); | | bb.done(); | |
| } | | } | |
| | | | |
|
| void apply( BSONObjBuilder& b , BSONElement in ){ | | template< class Builder > | |
| | | void apply( Builder& b , BSONElement in ){ | |
| m->apply( b , in , *this ); | | m->apply( b , in , *this ); | |
| } | | } | |
| | | | |
|
| void appendIncValue( BSONObjBuilder& b ) const { | | template< class Builder > | |
| | | void appendIncValue( Builder& b ) const { | |
| switch ( incType ){ | | switch ( incType ){ | |
| case NumberDouble: | | case NumberDouble: | |
| b.append( m->shortFieldName , incdouble ); break; | | b.append( m->shortFieldName , incdouble ); break; | |
| case NumberLong: | | case NumberLong: | |
| b.append( m->shortFieldName , inclong ); break; | | b.append( m->shortFieldName , inclong ); break; | |
| case NumberInt: | | case NumberInt: | |
| b.append( m->shortFieldName , incint ); break; | | b.append( m->shortFieldName , incint ); break; | |
| default: | | default: | |
| assert(0); | | assert(0); | |
| } | | } | |
| } | | } | |
| }; | | }; | |
| | | | |
| /** | | /** | |
| * this is used to hold state, meta data while applying a ModSet to a B
SONObj | | * this is used to hold state, meta data while applying a ModSet to a B
SONObj | |
| * the goal is to make ModSet const so its re-usable | | * the goal is to make ModSet const so its re-usable | |
| */ | | */ | |
| class ModSetState : boost::noncopyable { | | class ModSetState : boost::noncopyable { | |
|
| typedef map<string,ModState> ModStateHolder; | | struct FieldCmp { | |
| | | bool operator()( const string &l, const string &r ) const { | |
| | | return lexNumCmp( l.c_str(), r.c_str() ) < 0; | |
| | | } | |
| | | }; | |
| | | typedef map<string,ModState,FieldCmp> ModStateHolder; | |
| const BSONObj& _obj; | | const BSONObj& _obj; | |
| ModStateHolder _mods; | | ModStateHolder _mods; | |
| bool _inPlacePossible; | | bool _inPlacePossible; | |
| | | | |
| ModSetState( const BSONObj& obj ) | | ModSetState( const BSONObj& obj ) | |
| : _obj( obj ) , _inPlacePossible(true){ | | : _obj( obj ) , _inPlacePossible(true){ | |
| } | | } | |
| | | | |
| /** | | /** | |
| * @return if in place is still possible | | * @return if in place is still possible | |
| */ | | */ | |
| bool amIInPlacePossible( bool inPlacePossible ){ | | bool amIInPlacePossible( bool inPlacePossible ){ | |
| if ( ! inPlacePossible ) | | if ( ! inPlacePossible ) | |
| _inPlacePossible = false; | | _inPlacePossible = false; | |
| return _inPlacePossible; | | return _inPlacePossible; | |
| } | | } | |
| | | | |
|
| void createNewFromMods( const string& root , BSONObjBuilder& b , co | | template< class Builder > | |
| nst BSONObj &obj ); | | void createNewFromMods( const string& root , Builder& b , const BSO | |
| | | NObj &obj ); | |
| | | | |
|
| void _appendNewFromMods( const string& root , ModState& m , BSONObj | | template< class Builder > | |
| Builder& b , set<string>& onedownseen ); | | void _appendNewFromMods( const string& root , ModState& m , Builder | |
| | | & b , set<string>& onedownseen ); | |
| | | | |
|
| void appendNewFromMod( ModState& ms , BSONObjBuilder& b ){ | | template< class Builder > | |
| | | void appendNewFromMod( ModState& ms , Builder& b ){ | |
| //const Mod& m = *(ms.m); // HACK | | //const Mod& m = *(ms.m); // HACK | |
| Mod& m = *((Mod*)(ms.m)); // HACK | | Mod& m = *((Mod*)(ms.m)); // HACK | |
| | | | |
| switch ( m.op ){ | | switch ( m.op ){ | |
| | | | |
| case Mod::PUSH: | | case Mod::PUSH: | |
| case Mod::ADDTOSET: { | | case Mod::ADDTOSET: { | |
| if ( m.isEach() ){ | | if ( m.isEach() ){ | |
| b.appendArray( m.shortFieldName , m.getEach() ); | | b.appendArray( m.shortFieldName , m.getEach() ); | |
| } | | } | |
| | | | |
| skipping to change at line 451 | | skipping to change at line 472 | |
| break; | | break; | |
| } | | } | |
| | | | |
| case Mod::UNSET: | | case Mod::UNSET: | |
| case Mod::PULL: | | case Mod::PULL: | |
| case Mod::PULL_ALL: | | case Mod::PULL_ALL: | |
| // no-op b/c unset/pull of nothing does nothing | | // no-op b/c unset/pull of nothing does nothing | |
| break; | | break; | |
| | | | |
| case Mod::INC: | | case Mod::INC: | |
|
| | | ms.fixedName = "$set"; | |
| case Mod::SET: { | | case Mod::SET: { | |
| m._checkForAppending( m.elt ); | | m._checkForAppending( m.elt ); | |
| b.appendAs( m.elt, m.shortFieldName ); | | b.appendAs( m.elt, m.shortFieldName ); | |
| break; | | break; | |
| } | | } | |
| default: | | default: | |
| stringstream ss; | | stringstream ss; | |
| ss << "unknown mod in appendNewFromMod: " << m.op; | | ss << "unknown mod in appendNewFromMod: " << m.op; | |
| throw UserException( 9015, ss.str() ); | | throw UserException( 9015, ss.str() ); | |
| } | | } | |
| | | | |
End of changes. 14 change blocks. |
| 14 lines changed or deleted | | 35 lines changed or added | |
|