assert_util.h | assert_util.h | |||
---|---|---|---|---|
skipping to change at line 93 | skipping to change at line 93 | |||
AssertionException() { code = 0; } | AssertionException() { code = 0; } | |||
virtual ~AssertionException() throw() { } | virtual ~AssertionException() throw() { } | |||
virtual bool severe() { | virtual bool severe() { | |||
return true; | return true; | |||
} | } | |||
virtual bool isUserAssertion() { | virtual bool isUserAssertion() { | |||
return false; | return false; | |||
} | } | |||
virtual int getCode(){ return code; } | virtual int getCode(){ return code; } | |||
virtual const char* what() const throw() { return msg.c_str(); } | virtual const char* what() const throw() { return msg.c_str(); } | |||
/* true if an interrupted exception - see KillCurrentOp */ | ||||
bool interrupted() { | ||||
return code == 11600 || code == 11601; | ||||
} | ||||
}; | }; | |||
/* UserExceptions are valid errors that a user can cause, like out of d isk space or duplicate key */ | /* UserExceptions are valid errors that a user can cause, like out of d isk space or duplicate key */ | |||
class UserException : public AssertionException { | class UserException : public AssertionException { | |||
public: | public: | |||
UserException(int c , const string& m) { | UserException(int c , const string& m) { | |||
code = c; | code = c; | |||
msg = m; | msg = m; | |||
} | } | |||
virtual bool severe() { | virtual bool severe() { | |||
skipping to change at line 175 | skipping to change at line 180 | |||
#define dassert(x) | #define dassert(x) | |||
#endif | #endif | |||
// some special ids that we want to duplicate | // some special ids that we want to duplicate | |||
// > 10000 asserts | // > 10000 asserts | |||
// < 10000 UserException | // < 10000 UserException | |||
#define ASSERT_ID_DUPKEY 11000 | #define ASSERT_ID_DUPKEY 11000 | |||
void streamNotGood( int code , string msg , std::ios& myios ); | ||||
#define ASSERT_STREAM_GOOD(msgid,msg,stream) (void)( (!!((stream).good())) | ||||
|| (mongo::streamNotGood(msgid, msg, stream), 0) ) | ||||
} // namespace mongo | } // namespace mongo | |||
#define BOOST_CHECK_EXCEPTION( expression ) \ | #define BOOST_CHECK_EXCEPTION( expression ) \ | |||
try { \ | try { \ | |||
expression; \ | expression; \ | |||
} catch ( const std::exception &e ) { \ | } catch ( const std::exception &e ) { \ | |||
problem() << "caught boost exception: " << e.what() << endl; \ | problem() << "caught boost exception: " << e.what() << endl; \ | |||
assert( false ); \ | assert( false ); \ | |||
} catch ( ... ) { \ | } catch ( ... ) { \ | |||
massert( 10437 , "unknown boost failed" , false ); \ | massert( 10437 , "unknown boost failed" , false ); \ | |||
} | } | |||
#define DESTRUCTOR_GUARD( expression ) \ | ||||
try { \ | ||||
expression; \ | ||||
} catch ( const std::exception &e ) { \ | ||||
problem() << "caught exception (" << e.what() << ") in destructor ( | ||||
" << __FUNCTION__ << ")" << endl; \ | ||||
} catch ( ... ) { \ | ||||
problem() << "caught unknown exception in destructor (" << __FUNCTI | ||||
ON__ << ")" << endl; \ | ||||
} | ||||
End of changes. 3 change blocks. | ||||
0 lines changed or deleted | 10 lines changed or added | |||
background.h | background.h | |||
---|---|---|---|---|
// background.h | /** | |||
* Copyright (C) 2010 10gen Inc. | ||||
* | ||||
* This program is free software: you can redistribute it and/or modify | ||||
* it under the terms of the GNU Affero General Public License, version 3 | ||||
, | ||||
* as published by the Free Software Foundation. | ||||
* | ||||
* This program is distributed in the hope that it will be useful, | ||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
* GNU Affero General Public License for more details. | ||||
* | ||||
* You should have received a copy of the GNU Affero General Public Licen | ||||
se | ||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
*/ | ||||
/* Copyright 2009 10gen Inc. | /* background.h | |||
* | ||||
* Licensed under the Apache License, Version 2.0 (the "License"); | Concurrency coordination for administrative operations. | |||
* you may not use this file except in compliance with the License. | */ | |||
* You may obtain a copy of the License at | ||||
* | ||||
* http://www.apache.org/licenses/LICENSE-2.0 | ||||
* | ||||
* Unless required by applicable law or agreed to in writing, software | ||||
* distributed under the License is distributed on an "AS IS" BASIS, | ||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli | ||||
ed. | ||||
* See the License for the specific language governing permissions and | ||||
* limitations under the License. | ||||
*/ | ||||
#pragma once | #pragma once | |||
namespace mongo { | namespace mongo { | |||
/* object-orienty background thread dispatching. | /* these are administrative operations / jobs | |||
for a namespace running in the background, and that only one | ||||
subclass and define run() | at a time per namespace is permitted, and that if in progress, | |||
you aren't allowed to do other NamespaceDetails major manipulations | ||||
(such as dropping ns or db) even in the foreground and must | ||||
instead uassert. | ||||
It is ok to call go() more than once -- if the previous invocation | It's assumed this is not for super-high RPS things, so we don't do | |||
has finished. Thus one pattern of use is to embed a backgroundjob | anything special in the implementation here to be fast. | |||
in your object and reuse it (or same thing with inheritance). | ||||
*/ | */ | |||
class BackgroundOperation : public boost::noncopyable { | ||||
class BackgroundJob { | ||||
protected: | ||||
/* define this to do your work! */ | ||||
virtual void run() = 0; | ||||
public: | public: | |||
enum State { | static bool inProgForDb(const char *db); | |||
NotStarted, | static bool inProgForNs(const char *ns); | |||
Running, | static void assertNoBgOpInProgForDb(const char *db); | |||
Done | static void assertNoBgOpInProgForNs(const char *ns); | |||
}; | static void dump(stringstream&); | |||
State getState() const { | ||||
return state; | ||||
} | ||||
bool running() const { | ||||
return state == Running; | ||||
} | ||||
bool deleteSelf; // delete self when Done? | ||||
BackgroundJob() { | ||||
deleteSelf = false; | ||||
state = NotStarted; | ||||
} | ||||
virtual ~BackgroundJob() { } | ||||
// start job. returns before it's finished. | /* check for in progress before instantiating */ | |||
BackgroundJob& go(); | BackgroundOperation(const char *ns); | |||
// wait for completion. this spins with sleep() so not terribly ef | virtual ~BackgroundOperation(); | |||
ficient. | ||||
// returns true if did not time out. | ||||
// | ||||
// note you can call wait() more than once if the first call times | ||||
out. | ||||
bool wait(int msMax = 0); | ||||
private: | private: | |||
static BackgroundJob *grab; | NamespaceString _ns; | |||
static boost::mutex &mutex; | static map<string, unsigned> dbsInProg; | |||
static void thr(); | static set<string> nsInProg; | |||
volatile State state; | ||||
}; | }; | |||
} // namespace mongo | } // namespace mongo | |||
End of changes. 9 change blocks. | ||||
60 lines changed or deleted | 41 lines changed or added | |||
btree.h | btree.h | |||
---|---|---|---|---|
skipping to change at line 23 | skipping to change at line 23 | |||
* GNU Affero General Public License for more details. | * GNU Affero General Public License for more details. | |||
* | * | |||
* You should have received a copy of the GNU Affero General Public Licen se | * You should have received a copy of the GNU Affero General Public Licen se | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../stdafx.h" | #include "../stdafx.h" | |||
#include "jsobj.h" | #include "jsobj.h" | |||
#include "storage.h" | #include "diskloc.h" | |||
#include "pdfile.h" | #include "pdfile.h" | |||
namespace mongo { | namespace mongo { | |||
#pragma pack(1) | #pragma pack(1) | |||
struct _KeyNode { | struct _KeyNode { | |||
DiskLoc prevChildBucket; | DiskLoc prevChildBucket; // the lchild | |||
DiskLoc recordLoc; | DiskLoc recordLoc; // location of the record associated with the ke | |||
y | ||||
short keyDataOfs() const { | short keyDataOfs() const { | |||
return (short) _kdo; | return (short) _kdo; | |||
} | } | |||
unsigned short _kdo; | unsigned short _kdo; | |||
void setKeyDataOfs(short s) { | void setKeyDataOfs(short s) { | |||
_kdo = s; | _kdo = s; | |||
assert(s>=0); | assert(s>=0); | |||
} | } | |||
void setKeyDataOfsSavingUse(short s) { | void setKeyDataOfsSavingUse(short s) { | |||
_kdo = s; | _kdo = s; | |||
skipping to change at line 91 | skipping to change at line 91 | |||
friend class BtreeBuilder; | friend class BtreeBuilder; | |||
friend class KeyNode; | friend class KeyNode; | |||
public: | public: | |||
void dumpTree(DiskLoc thisLoc, const BSONObj &order); | void dumpTree(DiskLoc thisLoc, const BSONObj &order); | |||
bool isHead() { return parent.isNull(); } | bool isHead() { return parent.isNull(); } | |||
void assertValid(const BSONObj &order, bool force = false); | void assertValid(const BSONObj &order, bool force = false); | |||
int fullValidate(const DiskLoc& thisLoc, const BSONObj &order); /* traverses everything */ | int fullValidate(const DiskLoc& thisLoc, const BSONObj &order); /* traverses everything */ | |||
protected: | protected: | |||
void modified(const DiskLoc& thisLoc); | void modified(const DiskLoc& thisLoc); | |||
KeyNode keyNode(int i) const { | KeyNode keyNode(int i) const { | |||
assert( i < n ); | if ( i >= n ){ | |||
massert( 13000 , (string)"invalid keyNode: " + BSON( "i" < | ||||
< i << "n" << n ).jsonString() , i < n ); | ||||
} | ||||
return KeyNode(*this, k(i)); | return KeyNode(*this, k(i)); | |||
} | } | |||
char * dataAt(short ofs) { | char * dataAt(short ofs) { | |||
return data + ofs; | return data + ofs; | |||
} | } | |||
void init(); // initialize a new node | void init(); // initialize a new node | |||
/* returns false if node is full and must be split | /* returns false if node is full and must be split | |||
skipping to change at line 187 | skipping to change at line 189 | |||
void dump(); | void dump(); | |||
/* @return true if key exists in index | /* @return true if key exists in index | |||
order - indicates order of keys in the index. this is basically the index's key pattern, e.g.: | order - indicates order of keys in the index. this is basically the index's key pattern, e.g.: | |||
BSONObj order = ((IndexDetails&)idx).keyPattern(); | BSONObj order = ((IndexDetails&)idx).keyPattern(); | |||
likewise below in bt_insert() etc. | likewise below in bt_insert() etc. | |||
*/ | */ | |||
bool exists(const IndexDetails& idx, DiskLoc thisLoc, const BSONObj & key, BSONObj order); | bool exists(const IndexDetails& idx, DiskLoc thisLoc, const BSONObj & key, BSONObj order); | |||
bool wouldCreateDup( | ||||
const IndexDetails& idx, DiskLoc thisLoc, | ||||
const BSONObj& key, BSONObj order, | ||||
DiskLoc self); | ||||
static DiskLoc addBucket(IndexDetails&); /* start a new index off, empty */ | static DiskLoc addBucket(IndexDetails&); /* start a new index off, empty */ | |||
void deallocBucket(const DiskLoc &thisLoc); // clear bucket memory, placeholder for deallocation | ||||
static void renameIndexNamespace(const char *oldNs, const char *new Ns); | static void renameIndexNamespace(const char *oldNs, const char *new Ns); | |||
int bt_insert(DiskLoc thisLoc, DiskLoc recordLoc, | int bt_insert(DiskLoc thisLoc, DiskLoc recordLoc, | |||
const BSONObj& key, const BSONObj &order, bool dupsAllow ed, | const BSONObj& key, const BSONObj &order, bool dupsAllow ed, | |||
IndexDetails& idx, bool toplevel = true); | IndexDetails& idx, bool toplevel = true); | |||
bool unindex(const DiskLoc& thisLoc, IndexDetails& id, BSONObj& key , const DiskLoc& recordLoc); | bool unindex(const DiskLoc& thisLoc, IndexDetails& id, BSONObj& key , const DiskLoc& recordLoc); | |||
/* locate may return an "unused" key that is just a marker. so be careful. | /* locate may return an "unused" key that is just a marker. so be careful. | |||
skipping to change at line 372 | skipping to change at line 380 | |||
DiskLoc locAtKeyOfs; | DiskLoc locAtKeyOfs; | |||
BoundList bounds_; | BoundList bounds_; | |||
unsigned boundIndex_; | unsigned boundIndex_; | |||
}; | }; | |||
#pragma pack() | #pragma pack() | |||
inline bool IndexDetails::hasKey(const BSONObj& key) { | inline bool IndexDetails::hasKey(const BSONObj& key) { | |||
return head.btree()->exists(*this, head, key, keyPattern()); | return head.btree()->exists(*this, head, key, keyPattern()); | |||
} | } | |||
inline bool IndexDetails::wouldCreateDup(const BSONObj& key, DiskLoc se | ||||
lf) { | ||||
return head.btree()->wouldCreateDup(*this, head, key, keyPattern(), | ||||
self); | ||||
} | ||||
/* build btree from the bottom up */ | /* build btree from the bottom up */ | |||
/* _ TODO dropDups */ | /* _ TODO dropDups */ | |||
class BtreeBuilder { | class BtreeBuilder { | |||
bool dupsAllowed; | bool dupsAllowed; | |||
IndexDetails& idx; | IndexDetails& idx; | |||
unsigned long long n; | unsigned long long n; | |||
BSONObj keyLast; | BSONObj keyLast; | |||
BSONObj order; | BSONObj order; | |||
bool committed; | bool committed; | |||
End of changes. 6 change blocks. | ||||
4 lines changed or deleted | 19 lines changed or added | |||
builder.h | builder.h | |||
---|---|---|---|---|
skipping to change at line 93 | skipping to change at line 93 | |||
void append(unsigned j) { | void append(unsigned j) { | |||
append<unsigned>(j); | append<unsigned>(j); | |||
} | } | |||
void append(bool j) { | void append(bool j) { | |||
append<bool>(j); | append<bool>(j); | |||
} | } | |||
void append(double j) { | void append(double j) { | |||
append<double>(j); | append<double>(j); | |||
} | } | |||
void append(const void *src, int len) { | void append(const void *src, size_t len) { | |||
memcpy(grow(len), src, len); | memcpy(grow(len), src, len); | |||
} | } | |||
void append(const char *str) { | void append(const char *str) { | |||
append((void*) str, strlen(str)+1); | append((void*) str, strlen(str)+1); | |||
} | } | |||
void append(const string &str) { | void append(const string &str) { | |||
append( (void *)str.c_str(), str.length() + 1 ); | append( (void *)str.c_str(), str.length() + 1 ); | |||
} | } | |||
skipping to change at line 199 | skipping to change at line 199 | |||
return *this; | return *this; | |||
} | } | |||
// access | // access | |||
void reset( int maxSize = 0 ){ | void reset( int maxSize = 0 ){ | |||
_buf.reset( maxSize ); | _buf.reset( maxSize ); | |||
} | } | |||
string str(){ | string str(){ | |||
return string(_buf.data,0,_buf.l); | return string(_buf.data, _buf.l); | |||
} | } | |||
private: | private: | |||
BufBuilder _buf; | BufBuilder _buf; | |||
}; | }; | |||
} // namespace mongo | } // namespace mongo | |||
End of changes. 2 change blocks. | ||||
2 lines changed or deleted | 2 lines changed or added | |||
chunk.h | chunk.h | |||
---|---|---|---|---|
skipping to change at line 64 | skipping to change at line 64 | |||
const BSONObj& getMin() const { return _min; } | const BSONObj& getMin() const { return _min; } | |||
const BSONObj& getMax() const { return _max; } | const BSONObj& getMax() const { return _max; } | |||
void setMin(const BSONObj& o){ | void setMin(const BSONObj& o){ | |||
_min = o; | _min = o; | |||
} | } | |||
void setMax(const BSONObj& o){ | void setMax(const BSONObj& o){ | |||
_max = o; | _max = o; | |||
} | } | |||
string getShard(){ | string getShard() const{ | |||
return _shard; | return _shard; | |||
} | } | |||
void setShard( string shard ); | void setShard( string shard ); | |||
bool contains( const BSONObj& obj ); | bool contains( const BSONObj& obj ) const; | |||
string toString() const; | string toString() const; | |||
operator string() const { return toString(); } | operator string() const { return toString(); } | |||
bool operator==(const Chunk& s); | bool operator==(const Chunk& s) const; | |||
bool operator!=(const Chunk& s){ | bool operator!=(const Chunk& s) const{ | |||
return ! ( *this == s ); | return ! ( *this == s ); | |||
} | } | |||
void getFilter( BSONObjBuilder& b ); | void getFilter( BSONObjBuilder& b ) const; | |||
BSONObj getFilter(){ BSONObjBuilder b; getFilter( b ); return b.obj | BSONObj getFilter() const{ BSONObjBuilder b; getFilter( b ); return | |||
(); } | b.obj(); } | |||
BSONObj pickSplitPoint(); | BSONObj pickSplitPoint() const; | |||
Chunk * split(); | Chunk * split(); | |||
Chunk * split( const BSONObj& middle ); | Chunk * split( const BSONObj& middle ); | |||
/** | /** | |||
* @return size of shard in bytes | * @return size of shard in bytes | |||
* talks to mongod to do this | * talks to mongod to do this | |||
*/ | */ | |||
long getPhysicalSize(); | long getPhysicalSize() const; | |||
long countObjects( const BSONObj& filter = BSONObj() ); | long countObjects( const BSONObj& filter = BSONObj() ) const; | |||
/** | /** | |||
* if the amount of data written nears the max size of a shard | * if the amount of data written nears the max size of a shard | |||
* then we check the real size, and if its too big, we split | * then we check the real size, and if its too big, we split | |||
*/ | */ | |||
bool splitIfShould( long dataWritten ); | bool splitIfShould( long dataWritten ); | |||
/* | /* | |||
* moves either this shard or newShard if it makes sense too | * moves either this shard or newShard if it makes sense too | |||
* @return whether or not a shard was moved | * @return whether or not a shard was moved | |||
skipping to change at line 127 | skipping to change at line 127 | |||
void _markModified(); | void _markModified(); | |||
static long MaxChunkSize; | static long MaxChunkSize; | |||
private: | private: | |||
// main shard info | // main shard info | |||
ChunkManager * _manager; | ChunkManager * _manager; | |||
ShardKeyPattern skey(); | ShardKeyPattern skey() const; | |||
string _ns; | string _ns; | |||
BSONObj _min; | BSONObj _min; | |||
BSONObj _max; | BSONObj _max; | |||
string _shard; | string _shard; | |||
ShardChunkVersion _lastmod; | ShardChunkVersion _lastmod; | |||
bool _modified; | bool _modified; | |||
// transient stuff | // transient stuff | |||
skipping to change at line 219 | skipping to change at line 219 | |||
vector<Chunk*> _chunks; | vector<Chunk*> _chunks; | |||
map<string,unsigned long long> _maxMarkers; | map<string,unsigned long long> _maxMarkers; | |||
unsigned long long _sequenceNumber; | unsigned long long _sequenceNumber; | |||
friend class Chunk; | friend class Chunk; | |||
static unsigned long long NextSequenceNumber; | static unsigned long long NextSequenceNumber; | |||
}; | }; | |||
// like BSONObjCmp. for use as an STL comparison functor | ||||
// key-order in "order" argument must match key-order in shardkey | ||||
class ChunkCmp { | ||||
public: | ||||
ChunkCmp( const BSONObj &order = BSONObj() ) : _cmp( order ) {} | ||||
bool operator()( const Chunk &l, const Chunk &r ) const { | ||||
return _cmp(l.getMin(), r.getMin()); | ||||
} | ||||
bool operator()( const Chunk *l, const Chunk *r ) const { | ||||
return operator()(*l, *r); | ||||
} | ||||
private: | ||||
BSONObjCmp _cmp; | ||||
}; | ||||
} // namespace mongo | } // namespace mongo | |||
End of changes. 10 change blocks. | ||||
11 lines changed or deleted | 27 lines changed or added | |||
client.h | client.h | |||
---|---|---|---|---|
skipping to change at line 28 | skipping to change at line 28 | |||
/* Client represents a connection to the database (the server-side) and cor responds | /* Client represents a connection to the database (the server-side) and cor responds | |||
to an open socket (or logical connection if pooling on sockets) from a c lient. | to an open socket (or logical connection if pooling on sockets) from a c lient. | |||
todo: switch to asio...this will fit nicely with that. | todo: switch to asio...this will fit nicely with that. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../stdafx.h" | #include "../stdafx.h" | |||
#include "security.h" | ||||
#include "namespace.h" | #include "namespace.h" | |||
#include "lasterror.h" | #include "lasterror.h" | |||
#include "../util/top.h" | #include "stats/top.h" | |||
namespace mongo { | namespace mongo { | |||
class AuthenticationInfo; | class AuthenticationInfo; | |||
class Database; | class Database; | |||
class CurOp; | class CurOp; | |||
class Command; | class Command; | |||
class Client; | class Client; | |||
extern boost::thread_specific_ptr<Client> currentClient; | extern boost::thread_specific_ptr<Client> currentClient; | |||
bool setClient(const char *ns, const string& path=dbpath, mongolock *lo | ||||
ck = 0); | ||||
class Client : boost::noncopyable { | class Client : boost::noncopyable { | |||
public: | public: | |||
static boost::mutex clientsMutex; | static boost::mutex clientsMutex; | |||
static set<Client*> clients; // always be in clientsMutex when mani pulating this | static set<Client*> clients; // always be in clientsMutex when mani pulating this | |||
class GodScope { | class GodScope { | |||
bool _prev; | bool _prev; | |||
public: | public: | |||
GodScope(); | GodScope(); | |||
~GodScope(); | ~GodScope(); | |||
}; | }; | |||
/* Set database we want to use, then, restores when we finish (are out of scope) | /* Set database we want to use, then, restores when we finish (are out of scope) | |||
Note this is also helpful if an exception happens as the state i f fixed up. | Note this is also helpful if an exception happens as the state i f fixed up. | |||
*/ | */ | |||
class Context { | class Context : boost::noncopyable{ | |||
Client * _client; | Client * _client; | |||
Database * _olddb; | Context * _oldContext; | |||
string _oldns; | ||||
string _path; | ||||
mongolock * _lock; | ||||
bool _justCreated; | ||||
string _ns; | ||||
Database * _db; | ||||
/** | ||||
* at this point _client, _oldContext and _ns have to be set | ||||
* _db should not have been touched | ||||
* this will set _db and create if needed | ||||
* will also set _client->_context to this | ||||
*/ | ||||
void _finishInit( bool doauth=true); | ||||
void _auth( int lockState = dbMutex.getState() ); | ||||
public: | public: | |||
Context(const char *ns) | Context(const string& ns, string path=dbpath, mongolock * lock | |||
: _client( currentClient.get() ) { | = 0 , bool doauth=true ) | |||
_olddb = _client->_database; | : _client( currentClient.get() ) , _oldContext( _client->_c | |||
_oldns = _client->_ns; | ontext ) , | |||
setClient(ns); | _path( path ) , _lock( lock ) , | |||
} | _ns( ns ){ | |||
Context(string ns) | _finishInit( doauth ); | |||
: _client( currentClient.get() ){ | ||||
_olddb = _client->_database; | ||||
_oldns = _client->_ns; | ||||
setClient(ns.c_str()); | ||||
} | } | |||
/* this version saves the context but doesn't yet set the new o ne: */ | /* this version saves the context but doesn't yet set the new o ne: */ | |||
Context() | ||||
: _client( currentClient.get() ) { | ||||
_olddb = _client->database(); | ||||
_oldns = _client->ns(); | ||||
Context() | ||||
: _client( currentClient.get() ) , _oldContext( _client->_c | ||||
ontext ), | ||||
_path( dbpath ) , _lock(0) , _justCreated(false){ | ||||
_client->_context = this; | ||||
clear(); | ||||
} | } | |||
/** | /** | |||
* if you are doing this after allowing a write there could be a race condition | * if you are doing this after allowing a write there could be a race condition | |||
* if someone closes that db. this checks that the DB is still valid | * if someone closes that db. this checks that the DB is still valid | |||
*/ | */ | |||
Context( string ns , Database * db ); | Context( string ns , Database * db ); | |||
~Context() { | ~Context(); | |||
DEV assert( _client == currentClient.get() ); | ||||
_client->setns( _oldns.c_str(), _olddb ); | Client* getClient() const { return _client; } | |||
Database* db() const { | ||||
return _db; | ||||
} | ||||
const char * ns() const { | ||||
return _ns.c_str(); | ||||
} | ||||
bool justCreated() const { | ||||
return _justCreated; | ||||
} | ||||
bool equals( const string& ns , const string& path=dbpath ) con | ||||
st { | ||||
return _ns == ns && _path == path; | ||||
} | } | |||
bool inDB( const string& db , const string& path=dbpath ) const | ||||
{ | ||||
if ( _path != path ) | ||||
return false; | ||||
if ( db == _ns ) | ||||
return true; | ||||
string::size_type idx = _ns.find( db ); | ||||
if ( idx != 0 ) | ||||
return false; | ||||
return _ns[db.size()] == '.'; | ||||
} | ||||
void clear(){ | ||||
_ns = ""; | ||||
_db = 0; | ||||
} | ||||
/** | ||||
* call before unlocking, so clear any non-thread safe state | ||||
*/ | ||||
void unlocked(){ | ||||
_db = 0; | ||||
} | ||||
/** | ||||
* call after going back into the lock, will re-establish non-t | ||||
hread safe stuff | ||||
*/ | ||||
void relocked(){ | ||||
_finishInit(); | ||||
} | ||||
friend class CurOp; | ||||
}; | }; | |||
private: | private: | |||
CurOp * const _curOp; | CurOp * _curOp; | |||
Database *_database; | Context * _context; | |||
Namespace _ns; | ||||
//NamespaceString _nsstr; | ||||
bool _shutdown; | bool _shutdown; | |||
list<string> _tempCollections; | list<string> _tempCollections; | |||
const char *_desc; | const char *_desc; | |||
bool _god; | bool _god; | |||
AuthenticationInfo _ai; | ||||
public: | public: | |||
AuthenticationInfo *ai; | ||||
Top top; | AuthenticationInfo * getAuthenticationInfo(){ return &_ai; } | |||
bool isAdmin() { return _ai.isAuthorized( "admin" ); } | ||||
CurOp* curop() { return _curOp; } | CurOp* curop() { return _curOp; } | |||
Database* database() { | ||||
return _database; | ||||
} | ||||
const char *ns() { return _ns.buf; } | ||||
void setns(const char *ns, Database *db) { | Context* getContext(){ return _context; } | |||
_database = db; | Database* database() { return _context ? _context->db() : 0; } | |||
_ns = ns; | const char *ns() { return _context->ns(); } | |||
//_nsstr = ns; | ||||
} | ||||
void clearns() { setns("", 0); } | ||||
Client(const char *desc); | Client(const char *desc); | |||
~Client(); | ~Client(); | |||
const char *desc() const { return _desc; } | const char *desc() const { return _desc; } | |||
void addTempCollection( const string& ns ){ | void addTempCollection( const string& ns ){ | |||
_tempCollections.push_back( ns ); | _tempCollections.push_back( ns ); | |||
} | } | |||
skipping to change at line 145 | skipping to change at line 198 | |||
*/ | */ | |||
static void initThread(const char *desc); | static void initThread(const char *desc); | |||
/* | /* | |||
this has to be called as the client goes away, but before thread termination | this has to be called as the client goes away, but before thread termination | |||
@return true if anything was done | @return true if anything was done | |||
*/ | */ | |||
bool shutdown(); | bool shutdown(); | |||
bool isGod() const { return _god; } | bool isGod() const { return _god; } | |||
friend class CurOp; | ||||
string toString() const; | ||||
}; | }; | |||
inline Client& cc() { | inline Client& cc() { | |||
return *currentClient.get(); | return *currentClient.get(); | |||
} | } | |||
/* each thread which does db operations has a Client object in TLS. | /* each thread which does db operations has a Client object in TLS. | |||
call this when your thread starts. | call this when your thread starts. | |||
*/ | */ | |||
inline void Client::initThread(const char *desc) { | inline void Client::initThread(const char *desc) { | |||
skipping to change at line 184 | skipping to change at line 241 | |||
if( s != -1 ) { | if( s != -1 ) { | |||
log() << "error: releaseAndWriteLock() s == " << s << endl; | log() << "error: releaseAndWriteLock() s == " << s << endl; | |||
msgasserted( 12600, "releaseAndWriteLock: unlock_shared fai led, probably recursive" ); | msgasserted( 12600, "releaseAndWriteLock: unlock_shared fai led, probably recursive" ); | |||
} | } | |||
#endif | #endif | |||
_writelock = true; | _writelock = true; | |||
dbMutex.unlock_shared(); | dbMutex.unlock_shared(); | |||
dbMutex.lock(); | dbMutex.lock(); | |||
/* this is defensive; as we were unlocked for a moment above, | if ( cc().getContext() ) | |||
the Database object we reference could have been deleted: | cc().getContext()->unlocked(); | |||
*/ | ||||
cc().clearns(); | ||||
} | } | |||
} | } | |||
string sayClientState(); | ||||
}; | }; | |||
End of changes. 18 change blocks. | ||||
45 lines changed or deleted | 107 lines changed or added | |||
clientcursor.h | clientcursor.h | |||
---|---|---|---|---|
skipping to change at line 31 | skipping to change at line 31 | |||
ClientCursor is a wrapper that represents a cursorid from our database | ClientCursor is a wrapper that represents a cursorid from our database | |||
application's perspective. | application's perspective. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../stdafx.h" | #include "../stdafx.h" | |||
#include "cursor.h" | #include "cursor.h" | |||
#include "jsobj.h" | #include "jsobj.h" | |||
#include "../util/message.h" | #include "../util/message.h" | |||
#include "storage.h" | #include "diskloc.h" | |||
#include "dbhelpers.h" | #include "dbhelpers.h" | |||
#include "matcher.h" | #include "matcher.h" | |||
namespace mongo { | namespace mongo { | |||
typedef long long CursorId; /* passed to the client so it can send back on getMore */ | typedef long long CursorId; /* passed to the client so it can send back on getMore */ | |||
class Cursor; /* internal server cursor base class */ | class Cursor; /* internal server cursor base class */ | |||
class ClientCursor; | class ClientCursor; | |||
/* todo: make this map be per connection. this will prevent cursor hij acking security attacks perhaps. | /* todo: make this map be per connection. this will prevent cursor hij acking security attacks perhaps. | |||
skipping to change at line 108 | skipping to change at line 108 | |||
} | } | |||
}; | }; | |||
/*const*/ CursorId cursorid; | /*const*/ CursorId cursorid; | |||
string ns; | string ns; | |||
auto_ptr<CoveredIndexMatcher> matcher; | auto_ptr<CoveredIndexMatcher> matcher; | |||
auto_ptr<Cursor> c; | auto_ptr<Cursor> c; | |||
int pos; // # objects into the curs or so far | int pos; // # objects into the curs or so far | |||
BSONObj query; | BSONObj query; | |||
ClientCursor() : _idleAgeMillis(0), _pinValue(0), _doingDeletes(fal | ClientCursor(auto_ptr<Cursor>& _c, const char *_ns, bool okToTimeou | |||
se), pos(0) { | t) : | |||
_idleAgeMillis(0), _pinValue(0), | ||||
_doingDeletes(false), | ||||
ns(_ns), c(_c), | ||||
pos(0) | ||||
{ | ||||
if( !okToTimeout ) | ||||
noTimeout(); | ||||
recursive_boostlock lock(ccmutex); | recursive_boostlock 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; | |||
} | } | |||
auto_ptr< FieldMatcher > filter; // which fields query wants return ed | auto_ptr< FieldMatcher > filter; // which fields query wants return ed | |||
Message originalMessage; // this is effectively an auto ptr for dat a the matcher points to | Message originalMessage; // this is effectively an auto ptr for dat a the matcher points to | |||
/* Get rid of cursors for namespaces that begin with nsprefix. | /* Get rid of cursors for namespaces that begin with nsprefix. | |||
Used by drop, deleteIndexes, dropDatabase. | Used by drop, dropIndexes, dropDatabase. | |||
*/ | */ | |||
static void invalidate(const char *nsPrefix); | static void invalidate(const char *nsPrefix); | |||
/** | /** | |||
* do a dbtemprelease | * do a dbtemprelease | |||
* note: caller should check matcher.docMatcher().atomic() first an d not yield if atomic - | * note: caller should check matcher.docMatcher().atomic() first an d not yield if atomic - | |||
* we don't do herein as this->matcher (above) is only initia lized for true queries/getmore. | * we don't do herein as this->matcher (above) is only initia lized for true queries/getmore. | |||
* (ie not set for remote/update) | * (ie not set for remote/update) | |||
* @return if the cursor is still valid. | * @return if the cursor is still valid. | |||
* if false is returned, then this ClientCursor should be c | * if false is returned, then this ClientCursor should be c | |||
onsidered deleted | onsidered deleted - | |||
* in fact, the whole database could be gone. | ||||
*/ | */ | |||
bool yield(); | bool yield(); | |||
private: | private: | |||
void setLastLoc_inlock(DiskLoc); | void setLastLoc_inlock(DiskLoc); | |||
static ClientCursor* find_inlock(CursorId id, bool warn = true) { | static ClientCursor* find_inlock(CursorId id, bool warn = true) { | |||
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_boostlock 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->_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_boostlock 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; | |||
skipping to change at line 198 | skipping to change at line 206 | |||
bool shouldTimeout( unsigned millis ){ | bool shouldTimeout( unsigned millis ){ | |||
_idleAgeMillis += millis; | _idleAgeMillis += millis; | |||
return _idleAgeMillis > 600000 && _pinValue == 0; | return _idleAgeMillis > 600000 && _pinValue == 0; | |||
} | } | |||
unsigned idleTime(){ | unsigned idleTime(){ | |||
return _idleAgeMillis; | return _idleAgeMillis; | |||
} | } | |||
static void idleTimeReport(unsigned millis); | static void idleTimeReport(unsigned millis); | |||
private: | ||||
// cursors normally timeout after an inactivy period to prevent exc ess memory use | // cursors normally timeout after an inactivy period to prevent exc ess memory use | |||
// setting this prevents timeout of the cursor in question. | // setting this prevents timeout of the cursor in question. | |||
void noTimeout() { | void noTimeout() { | |||
_pinValue++; | _pinValue++; | |||
} | } | |||
public: | ||||
void setDoingDeletes( bool doingDeletes ){ | void setDoingDeletes( bool doingDeletes ){ | |||
_doingDeletes = doingDeletes; | _doingDeletes = doingDeletes; | |||
} | } | |||
static unsigned byLocSize(); // just for diagnostics | static unsigned byLocSize(); // just for diagnostics | |||
static void informAboutToDeleteBucket(const DiskLoc& b); | static void informAboutToDeleteBucket(const DiskLoc& b); | |||
static void aboutToDelete(const DiskLoc& dl); | static void aboutToDelete(const DiskLoc& dl); | |||
}; | }; | |||
End of changes. 7 change blocks. | ||||
9 lines changed or deleted | 17 lines changed or added | |||
concurrency.h | concurrency.h | |||
---|---|---|---|---|
/* | ||||
* Copyright (C) 2010 10gen Inc. | ||||
* | ||||
* This program is free software: you can redistribute it and/or modify | ||||
* it under the terms of the GNU Affero General Public License, version | ||||
3, | ||||
* as published by the Free Software Foundation. | ||||
* | ||||
* This program is distributed in the hope that it will be useful, | ||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
* GNU Affero General Public License for more details. | ||||
* | ||||
* You should have received a copy of the GNU Affero General Public Lice | ||||
nse | ||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
*/ | ||||
/* concurrency.h | /* concurrency.h | |||
mongod concurrency rules & notes will be placed here. | mongod concurrency rules & notes will be placed here. | |||
Mutex heirarchy (1 = "leaf") | Mutex heirarchy (1 = "leaf") | |||
name level | name level | |||
Logstream::mutex 1 | Logstream::mutex 1 | |||
ClientCursor::ccmutex 2 | ClientCursor::ccmutex 2 | |||
dblock 3 | dblock 3 | |||
End func name with _inlock to indicate "caller must lock before callin g". | End func name with _inlock to indicate "caller must lock before callin g". | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#if BOOST_VERSION >= 103500 | #if BOOST_VERSION >= 103500 | |||
#include <boost/thread/shared_mutex.hpp> | #include <boost/thread/shared_mutex.hpp> | |||
#undef assert | #undef assert | |||
#define assert xassert | #define assert xassert | |||
#define HAVE_READLOCK | ||||
#else | #else | |||
#warning built with boost version 1.34 or older limited concurrency | #warning built with boost version 1.34 or older limited concurrency | |||
#endif | #endif | |||
namespace mongo { | namespace mongo { | |||
inline bool readLockSupported(){ | ||||
#ifdef HAVE_READLOCK | ||||
return true; | ||||
#else | ||||
return false; | ||||
#endif | ||||
} | ||||
string sayClientState(); | ||||
void curopWaitingForLock( int type ); | ||||
void curopGotLock(); | ||||
/* mutex time stats */ | /* mutex time stats */ | |||
class MutexInfo { | class MutexInfo { | |||
unsigned long long start, enter, timeLocked; // all in microseconds | unsigned long long start, enter, timeLocked; // all in microseconds | |||
int locked; | int locked; | |||
public: | public: | |||
MutexInfo() : locked(0) { | MutexInfo() : timeLocked(0) , locked(0) { | |||
start = curTimeMicros64(); | start = curTimeMicros64(); | |||
} | } | |||
void entered() { | void entered() { | |||
if ( locked == 0 ) | if ( locked == 0 ) | |||
enter = curTimeMicros64(); | enter = curTimeMicros64(); | |||
locked++; | locked++; | |||
assert( locked >= 1 ); | assert( locked >= 1 ); | |||
} | } | |||
void leaving() { | void leaving() { | |||
locked--; | locked--; | |||
skipping to change at line 54 | skipping to change at line 84 | |||
if ( locked == 0 ) | if ( locked == 0 ) | |||
timeLocked += curTimeMicros64() - enter; | timeLocked += curTimeMicros64() - enter; | |||
} | } | |||
int isLocked() const { | int isLocked() const { | |||
return locked; | return locked; | |||
} | } | |||
void getTimingInfo(unsigned long long &s, unsigned long long &tl) c onst { | void getTimingInfo(unsigned long long &s, unsigned long long &tl) c onst { | |||
s = start; | s = start; | |||
tl = timeLocked; | tl = timeLocked; | |||
} | } | |||
unsigned long long getTimeLocked() const { | ||||
return timeLocked; | ||||
} | ||||
}; | }; | |||
#if BOOST_VERSION >= 103500 | #ifdef HAVE_READLOCK | |||
//#if 0 | //#if 0 | |||
class MongoMutex { | class MongoMutex { | |||
MutexInfo _minfo; | MutexInfo _minfo; | |||
boost::shared_mutex _m; | boost::shared_mutex _m; | |||
ThreadLocalValue<int> _state; | ThreadLocalValue<int> _state; | |||
/* we use a separate TLS value for releasedEarly - that is ok as | /* we use a separate TLS value for releasedEarly - that is ok as | |||
our normal/common code path, we never even touch it. | our normal/common code path, we never even touch it. | |||
*/ | */ | |||
ThreadLocalValue<bool> _releasedEarly; | ThreadLocalValue<bool> _releasedEarly; | |||
skipping to change at line 83 | skipping to change at line 116 | |||
*/ | */ | |||
int getState(){ return _state.get(); } | int getState(){ return _state.get(); } | |||
void assertWriteLocked() { | void assertWriteLocked() { | |||
assert( getState() > 0 ); | assert( getState() > 0 ); | |||
DEV assert( !_releasedEarly.get() ); | DEV assert( !_releasedEarly.get() ); | |||
} | } | |||
bool atLeastReadLocked() { return _state.get() != 0; } | bool atLeastReadLocked() { return _state.get() != 0; } | |||
void assertAtLeastReadLocked() { assert(atLeastReadLocked()); } | void assertAtLeastReadLocked() { assert(atLeastReadLocked()); } | |||
void lock() { | void lock() { | |||
DEV cout << "LOCK" << endl; | //DEV cout << "LOCK" << endl; | |||
int s = _state.get(); | int s = _state.get(); | |||
if( s > 0 ) { | if( s > 0 ) { | |||
_state.set(s+1); | _state.set(s+1); | |||
return; | return; | |||
} | } | |||
massert( 10293 , "internal error: locks are not upgradeable", s == 0 ); | massert( 10293 , (string)"internal error: locks are not upgrade able: " + sayClientState() , s == 0 ); | |||
_state.set(1); | _state.set(1); | |||
curopWaitingForLock( 1 ); | ||||
_m.lock(); | _m.lock(); | |||
curopGotLock(); | ||||
_minfo.entered(); | _minfo.entered(); | |||
} | } | |||
void unlock() { | void unlock() { | |||
DEV cout << "UNLOCK" << endl; | //DEV cout << "UNLOCK" << endl; | |||
int s = _state.get(); | int s = _state.get(); | |||
if( s > 1 ) { | if( s > 1 ) { | |||
_state.set(s-1); | _state.set(s-1); | |||
return; | return; | |||
} | } | |||
if( s != 1 ) { | if( s != 1 ) { | |||
if( _releasedEarly.get() ) { | if( _releasedEarly.get() ) { | |||
_releasedEarly.set(false); | _releasedEarly.set(false); | |||
return; | return; | |||
} | } | |||
assert(false); // attempt to unlock when wasn't in a write lock | massert( 12599, "internal error: attempt to unlock when was n't in a write lock", false); | |||
} | } | |||
_state.set(0); | _state.set(0); | |||
_minfo.leaving(); | _minfo.leaving(); | |||
_m.unlock(); | _m.unlock(); | |||
} | } | |||
/* unlock (write lock), and when unlock() is called later, | /* unlock (write lock), and when unlock() is called later, | |||
be smart then and don't unlock it again. | be smart then and don't unlock it again. | |||
*/ | */ | |||
void releaseEarly() { | void releaseEarly() { | |||
assert( getState() == 1 ); // must not be recursive | assert( getState() == 1 ); // must not be recursive | |||
assert( !_releasedEarly.get() ); | assert( !_releasedEarly.get() ); | |||
_releasedEarly.set(true); | _releasedEarly.set(true); | |||
unlock(); | unlock(); | |||
} | } | |||
void lock_shared() { | void lock_shared() { | |||
DEV cout << " LOCKSHARED" << endl; | //DEV cout << " LOCKSHARED" << endl; | |||
int s = _state.get(); | int s = _state.get(); | |||
if( s ) { | if( s ) { | |||
if( s > 0 ) { | if( s > 0 ) { | |||
// already in write lock - just be recursive and stay w rite locked | // already in write lock - just be recursive and stay w rite locked | |||
_state.set(s+1); | _state.set(s+1); | |||
return; | return; | |||
} | } | |||
else { | else { | |||
// already in read lock - recurse | // already in read lock - recurse | |||
_state.set(s-1); | _state.set(s-1); | |||
return; | return; | |||
} | } | |||
} | } | |||
_state.set(-1); | _state.set(-1); | |||
curopWaitingForLock( -1 ); | ||||
_m.lock_shared(); | _m.lock_shared(); | |||
curopGotLock(); | ||||
} | ||||
bool lock_shared_try( int millis ) { | ||||
int s = _state.get(); | ||||
if ( s ){ | ||||
// we already have a lock, so no need to try | ||||
lock_shared(); | ||||
return true; | ||||
} | ||||
boost::system_time until = get_system_time(); | ||||
until += boost::posix_time::milliseconds(2); | ||||
bool got = _m.timed_lock_shared( until ); | ||||
if ( got ) | ||||
_state.set(-1); | ||||
return got; | ||||
} | } | |||
void unlock_shared() { | void unlock_shared() { | |||
DEV cout << " UNLOCKSHARED" << endl; | //DEV cout << " UNLOCKSHARED" << endl; | |||
int s = _state.get(); | int s = _state.get(); | |||
if( s > 0 ) { | if( s > 0 ) { | |||
assert( s > 1 ); /* we must have done a lock write first to have s > 1 */ | assert( s > 1 ); /* we must have done a lock write first to have s > 1 */ | |||
_state.set(s-1); | _state.set(s-1); | |||
return; | return; | |||
} | } | |||
if( s < -1 ) { | if( s < -1 ) { | |||
_state.set(s+1); | _state.set(s+1); | |||
return; | return; | |||
} | } | |||
skipping to change at line 168 | skipping to change at line 225 | |||
}; | }; | |||
#else | #else | |||
/* this will be for old versions of boost */ | /* this will be for old versions of boost */ | |||
class MongoMutex { | class MongoMutex { | |||
MutexInfo _minfo; | MutexInfo _minfo; | |||
boost::recursive_mutex m; | boost::recursive_mutex m; | |||
ThreadLocalValue<bool> _releasedEarly; | ThreadLocalValue<bool> _releasedEarly; | |||
public: | public: | |||
MongoMutex() { } | MongoMutex() { } | |||
void lock() { | void lock() { | |||
#if BOOST_VERSION >= 103500 | #ifdef HAVE_READLOCK | |||
m.lock(); | m.lock(); | |||
#else | #else | |||
boost::detail::thread::lock_ops<boost::recursive_mutex>::lock(m ); | boost::detail::thread::lock_ops<boost::recursive_mutex>::lock(m ); | |||
#endif | #endif | |||
_minfo.entered(); | _minfo.entered(); | |||
} | } | |||
void releaseEarly() { | void releaseEarly() { | |||
assertWriteLocked(); // aso must not be recursive, although we don't verify that in the old boost version | assertWriteLocked(); // aso must not be recursive, although we don't verify that in the old boost version | |||
assert( !_releasedEarly.get() ); | assert( !_releasedEarly.get() ); | |||
_releasedEarly.set(true); | _releasedEarly.set(true); | |||
_unlock(); | _unlock(); | |||
} | } | |||
void _unlock() { | void _unlock() { | |||
_minfo.leaving(); | _minfo.leaving(); | |||
#if BOOST_VERSION >= 103500 | #ifdef HAVE_READLOCK | |||
m.unlock(); | m.unlock(); | |||
#else | #else | |||
boost::detail::thread::lock_ops<boost::recursive_mutex>::unlock (m); | boost::detail::thread::lock_ops<boost::recursive_mutex>::unlock (m); | |||
#endif | #endif | |||
} | } | |||
void unlock() { | void unlock() { | |||
if( _releasedEarly.get() ) { | if( _releasedEarly.get() ) { | |||
_releasedEarly.set(false); | _releasedEarly.set(false); | |||
return; | return; | |||
} | } | |||
_unlock(); | _unlock(); | |||
} | } | |||
void lock_shared() { lock(); } | void lock_shared() { lock(); } | |||
bool lock_shared_try( int millis ) { | ||||
while ( millis-- ){ | ||||
if ( getState() ){ | ||||
sleepmillis(1); | ||||
continue; | ||||
} | ||||
lock_shared(); | ||||
return true; | ||||
} | ||||
return false; | ||||
} | ||||
void unlock_shared() { unlock(); } | void unlock_shared() { unlock(); } | |||
MutexInfo& info() { return _minfo; } | MutexInfo& info() { return _minfo; } | |||
void assertWriteLocked() { | void assertWriteLocked() { | |||
assert( info().isLocked() ); | assert( info().isLocked() ); | |||
} | } | |||
void assertAtLeastReadLocked() { | void assertAtLeastReadLocked() { | |||
assert( info().isLocked() ); | assert( info().isLocked() ); | |||
} | } | |||
bool atLeastReadLocked() { return info().isLocked(); } | bool atLeastReadLocked() { return info().isLocked(); } | |||
int getState(){ return info().isLocked() ? 1 : 0; } | int getState(){ return info().isLocked() ? 1 : 0; } | |||
skipping to change at line 223 | skipping to change at line 292 | |||
extern MongoMutex &dbMutex; | extern MongoMutex &dbMutex; | |||
void dbunlocking_write(); | void dbunlocking_write(); | |||
void dbunlocking_read(); | void dbunlocking_read(); | |||
struct writelock { | struct writelock { | |||
writelock(const string& ns) { | writelock(const string& ns) { | |||
dbMutex.lock(); | dbMutex.lock(); | |||
} | } | |||
~writelock() { | ~writelock() { | |||
dbunlocking_write(); | DESTRUCTOR_GUARD( | |||
dbMutex.unlock(); | dbunlocking_write(); | |||
dbMutex.unlock(); | ||||
); | ||||
} | } | |||
}; | }; | |||
struct readlock { | struct readlock { | |||
readlock(const string& ns) { | readlock(const string& ns) { | |||
dbMutex.lock_shared(); | dbMutex.lock_shared(); | |||
} | } | |||
~readlock() { | ~readlock() { | |||
dbunlocking_read(); | DESTRUCTOR_GUARD( | |||
dbMutex.unlock_shared(); | dbunlocking_read(); | |||
dbMutex.unlock_shared(); | ||||
); | ||||
} | } | |||
}; | }; | |||
struct readlocktry { | ||||
readlocktry( const string&ns , int tryms ){ | ||||
_got = dbMutex.lock_shared_try( tryms ); | ||||
} | ||||
~readlocktry() { | ||||
if ( _got ){ | ||||
dbunlocking_read(); | ||||
dbMutex.unlock_shared(); | ||||
} | ||||
} | ||||
bool got(){ | ||||
return _got; | ||||
} | ||||
bool _got; | ||||
}; | ||||
class mongolock { | class mongolock { | |||
bool _writelock; | bool _writelock; | |||
public: | public: | |||
mongolock(bool write) : _writelock(write) { | mongolock(bool write) : _writelock(write) { | |||
if( _writelock ) { | if( _writelock ) { | |||
dbMutex.lock(); | dbMutex.lock(); | |||
} | } | |||
else | else | |||
dbMutex.lock_shared(); | dbMutex.lock_shared(); | |||
} | } | |||
~mongolock() { | ~mongolock() { | |||
if( _writelock ) { | DESTRUCTOR_GUARD( | |||
dbunlocking_write(); | if( _writelock ) { | |||
dbMutex.unlock(); | dbunlocking_write(); | |||
} | dbMutex.unlock(); | |||
else { | } else { | |||
dbunlocking_read(); | dbunlocking_read(); | |||
dbMutex.unlock_shared(); | dbMutex.unlock_shared(); | |||
} | } | |||
); | ||||
} | } | |||
/* this unlocks, does NOT upgrade. that works for our current usage */ | /* this unlocks, does NOT upgrade. that works for our current usage */ | |||
void releaseAndWriteLock(); | void releaseAndWriteLock(); | |||
}; | }; | |||
/* use writelock and readlock instead */ | /* use writelock and readlock instead */ | |||
struct dblock : public writelock { | struct dblock : public writelock { | |||
dblock() : writelock("") { } | dblock() : writelock("") { } | |||
~dblock() { | ~dblock() { | |||
} | } | |||
End of changes. 24 change blocks. | ||||
22 lines changed or deleted | 113 lines changed or added | |||
curop.h | curop.h | |||
---|---|---|---|---|
// curop.h | // curop.h | |||
/* | ||||
* Copyright (C) 2010 10gen Inc. | ||||
* | ||||
* This program is free software: you can redistribute it and/or modify | ||||
* it under the terms of the GNU Affero General Public License, version | ||||
3, | ||||
* as published by the Free Software Foundation. | ||||
* | ||||
* This program is distributed in the hope that it will be useful, | ||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
* GNU Affero General Public License for more details. | ||||
* | ||||
* You should have received a copy of the GNU Affero General Public Lice | ||||
nse | ||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
*/ | ||||
#pragma once | #pragma once | |||
#include "namespace.h" | #include "namespace.h" | |||
#include "security.h" | ||||
#include "client.h" | #include "client.h" | |||
#include "../util/atomic_int.h" | ||||
#include "db.h" | ||||
namespace mongo { | namespace mongo { | |||
class OpDebug { | class OpDebug { | |||
public: | public: | |||
StringBuilder str; | StringBuilder str; | |||
void reset(){ | void reset(){ | |||
str.reset(); | str.reset(); | |||
} | } | |||
}; | }; | |||
/* Current operation (for the current Client). | /* Current operation (for the current Client). | |||
an embedded member of Client class, and typically used from within t he mutex there. */ | an embedded member of Client class, and typically used from within t he mutex there. */ | |||
class CurOp : boost::noncopyable { | class CurOp : boost::noncopyable { | |||
static WrappingInt _nextOpNum; | static AtomicUInt _nextOpNum; | |||
static BSONObj _tooBig; // { $msg : "query not recording (too large )" } | static BSONObj _tooBig; // { $msg : "query not recording (too large )" } | |||
Client * _client; | ||||
CurOp * _wrapped; | ||||
unsigned long long _start; | ||||
unsigned long long _checkpoint; | ||||
unsigned long long _end; | ||||
bool _active; | bool _active; | |||
Timer _timer; | ||||
int _op; | int _op; | |||
WrappingInt _opNum; | bool _command; | |||
int _lockType; // see concurrency.h for values | ||||
bool _waitingForLock; | ||||
int _dbprofile; // 0=off, 1=slow, 2=all | ||||
AtomicUInt _opNum; | ||||
char _ns[Namespace::MaxNsLen+2]; | char _ns[Namespace::MaxNsLen+2]; | |||
struct sockaddr_in client; | struct sockaddr_in _remote; | |||
char _queryBuf[256]; | char _queryBuf[256]; | |||
bool haveQuery() const { return *((int *) _queryBuf) != 0; } | ||||
void resetQuery(int x=0) { *((int *)_queryBuf) = x; } | void resetQuery(int x=0) { *((int *)_queryBuf) = x; } | |||
OpDebug _debug; | ||||
void _reset(){ | ||||
_command = false; | ||||
_lockType = 0; | ||||
_dbprofile = 0; | ||||
_end = 0; | ||||
_waitingForLock = false; | ||||
} | ||||
void setNS(const char *ns) { | ||||
strncpy(_ns, ns, Namespace::MaxNsLen); | ||||
} | ||||
public: | ||||
bool haveQuery() const { return *((int *) _queryBuf) != 0; } | ||||
BSONObj query() { | BSONObj query() { | |||
if( *((int *) _queryBuf) == 1 ) { | if( *((int *) _queryBuf) == 1 ) { | |||
return _tooBig; | return _tooBig; | |||
} | } | |||
BSONObj o(_queryBuf); | BSONObj o(_queryBuf); | |||
return o; | return o; | |||
} | } | |||
OpDebug _debug; | void ensureStarted(){ | |||
public: | if ( _start == 0 ) | |||
void reset( const sockaddr_in &_client) { | _start = _checkpoint = curTimeMicros64(); | |||
} | ||||
void enter( Client::Context * context ){ | ||||
ensureStarted(); | ||||
setNS( context->ns() ); | ||||
if ( context->_db && context->_db->profile > _dbprofile ) | ||||
_dbprofile = context->_db->profile; | ||||
} | ||||
void leave( Client::Context * context ){ | ||||
unsigned long long now = curTimeMicros64(); | ||||
Top::global.record( _ns , _op , _lockType , now - _checkpoint , | ||||
_command ); | ||||
_checkpoint = now; | ||||
} | ||||
void reset( const sockaddr_in & remote, int op ) { | ||||
_reset(); | ||||
_start = _checkpoint = 0; | ||||
_active = true; | _active = true; | |||
_opNum = _nextOpNum.atomicIncrement(); | _opNum = _nextOpNum++; | |||
_timer.reset(); | ||||
_ns[0] = '?'; // just in case not set later | _ns[0] = '?'; // just in case not set later | |||
_debug.reset(); | _debug.reset(); | |||
resetQuery(); | resetQuery(); | |||
client = _client; | _remote = remote; | |||
_op = op; | ||||
} | ||||
void markCommand(){ | ||||
_command = true; | ||||
} | ||||
void waitingForLock( int type ){ | ||||
_waitingForLock = true; | ||||
if ( type > 0 ) | ||||
_lockType = 1; | ||||
else | ||||
_lockType = -1; | ||||
} | ||||
void gotLock(){ | ||||
_waitingForLock = false; | ||||
} | } | |||
OpDebug& debug(){ | OpDebug& debug(){ | |||
return _debug; | return _debug; | |||
} | } | |||
WrappingInt opNum() const { return _opNum; } | int profileLevel() const { | |||
return _dbprofile; | ||||
} | ||||
const char * getNS() const { | ||||
return _ns; | ||||
} | ||||
bool shouldDBProfile( int ms ) const { | ||||
if ( _dbprofile <= 0 ) | ||||
return false; | ||||
return _dbprofile >= 2 || ms >= cmdLine.slowMS; | ||||
} | ||||
AtomicUInt opNum() const { return _opNum; } | ||||
/** if this op is running */ | ||||
bool active() const { return _active; } | bool active() const { return _active; } | |||
int elapsedMillis(){ return _timer.millis(); } | int getLockType() const { return _lockType; } | |||
bool isWaitingForLock() const { return _waitingForLock; } | ||||
int getOp() const { return _op; } | ||||
/** micros */ | /** micros */ | |||
unsigned long long startTime(){ | unsigned long long startTime() { | |||
return _timer.startTime(); | ensureStarted(); | |||
return _start; | ||||
} | } | |||
void setActive(bool active) { _active = active; } | void done() { | |||
void setNS(const char *ns) { | _active = false; | |||
strncpy(_ns, ns, Namespace::MaxNsLen); | _end = curTimeMicros64(); | |||
} | } | |||
void setOp(int op) { _op = op; } | ||||
unsigned long long totalTimeMicros() { | ||||
massert( 12601 , "CurOp not marked done yet" , ! _active ); | ||||
return _end - startTime(); | ||||
} | ||||
int totalTimeMillis() { | ||||
return (int) (totalTimeMicros() / 1000); | ||||
} | ||||
int elapsedMillis() { | ||||
unsigned long long total = curTimeMicros64() - startTime(); | ||||
return (int) (total / 1000); | ||||
} | ||||
int elapsedSeconds() { | ||||
return elapsedMillis() / 1000; | ||||
} | ||||
void setQuery(const BSONObj& query) { | void setQuery(const BSONObj& query) { | |||
if( query.objsize() > (int) sizeof(_queryBuf) ) { | if( query.objsize() > (int) sizeof(_queryBuf) ) { | |||
resetQuery(1); // flag as too big and return | resetQuery(1); // flag as too big and return | |||
return; | return; | |||
} | } | |||
memcpy(_queryBuf, query.objdata(), query.objsize()); | memcpy(_queryBuf, query.objdata(), query.objsize()); | |||
} | } | |||
CurOp() { | CurOp( Client * client , CurOp * wrapped = 0 ) { | |||
_client = client; | ||||
_wrapped = wrapped; | ||||
if ( _wrapped ){ | ||||
_client->_curOp = this; | ||||
} | ||||
_start = _checkpoint = 0; | ||||
_active = false; | _active = false; | |||
// opNum = 0; | _reset(); | |||
_op = 0; | _op = 0; | |||
// These addresses should never be written to again. The zeroe s are | // These addresses should never be written to again. The zeroe s are | |||
// placed here as a precaution because currentOp may be accesse d | // placed here as a precaution because currentOp may be accesse d | |||
// without the db mutex. | // without the db mutex. | |||
memset(_ns, 0, sizeof(_ns)); | memset(_ns, 0, sizeof(_ns)); | |||
memset(_queryBuf, 0, sizeof(_queryBuf)); | memset(_queryBuf, 0, sizeof(_queryBuf)); | |||
} | } | |||
~CurOp(){ | ||||
if ( _wrapped ) | ||||
_client->_curOp = _wrapped; | ||||
} | ||||
BSONObj info() { | BSONObj info() { | |||
AuthenticationInfo *ai = currentClient.get()->ai; | if( ! cc().getAuthenticationInfo()->isAuthorized("admin") ) { | |||
if( !ai->isAuthorized("admin") ) { | ||||
BSONObjBuilder b; | BSONObjBuilder b; | |||
b.append("err", "unauthorized"); | b.append("err", "unauthorized"); | |||
return b.obj(); | return b.obj(); | |||
} | } | |||
return infoNoauth(); | return infoNoauth(); | |||
} | } | |||
BSONObj infoNoauth() { | BSONObj infoNoauth(); | |||
BSONObjBuilder b; | ||||
b.append("opid", _opNum); | ||||
b.append("active", _active); | ||||
if( _active ) | ||||
b.append("secs_running", _timer.seconds() ); | ||||
if( _op == 2004 ) | ||||
b.append("op", "query"); | ||||
else if( _op == 2005 ) | ||||
b.append("op", "getMore"); | ||||
else if( _op == 2001 ) | ||||
b.append("op", "update"); | ||||
else if( _op == 2002 ) | ||||
b.append("op", "insert"); | ||||
else if( _op == 2006 ) | ||||
b.append("op", "delete"); | ||||
else | ||||
b.append("op", _op); | ||||
b.append("ns", _ns); | ||||
if( haveQuery() ) { | string getRemoteString(){ | |||
b.append("query", query()); | stringstream ss; | |||
} | ss << inet_ntoa( _remote.sin_addr ) << ":" << ntohs( _remote.si | |||
// b.append("inLock", ?? | n_port ); | |||
stringstream clientStr; | return ss.str(); | |||
clientStr << inet_ntoa( client.sin_addr ) << ":" << ntohs( clie | ||||
nt.sin_port ); | ||||
b.append("client", clientStr.str()); | ||||
return b.obj(); | ||||
} | } | |||
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; | |||
WrappingInt toKill; | AtomicUInt toKill; | |||
public: | public: | |||
void killAll() { state = All; } | void killAll() { state = All; } | |||
void kill(WrappingInt 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 ) { | |||
state = Off; | state = Off; | |||
uasserted(11601,"interrupted"); | uasserted(11601,"interrupted"); | |||
} | } | |||
} | } | |||
End of changes. 27 change blocks. | ||||
54 lines changed or deleted | 162 lines changed or added | |||
cursor.h | cursor.h | |||
---|---|---|---|---|
skipping to change at line 22 | skipping to change at line 22 | |||
* | * | |||
* You should have received a copy of the GNU Affero General Public Licen se | * You should have received a copy of the GNU Affero General Public Licen se | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../stdafx.h" | #include "../stdafx.h" | |||
#include "jsobj.h" | #include "jsobj.h" | |||
#include "storage.h" | #include "diskloc.h" | |||
namespace mongo { | namespace mongo { | |||
class Record; | class Record; | |||
/* Query cursors, base class. This is for our internal cursors. "Clie ntCursor" is a separate | /* Query cursors, base class. This is for our internal cursors. "Clie ntCursor" is a separate | |||
concept and is for the user's cursor. | concept and is for the user's cursor. | |||
WARNING concurrency: the vfunctions below are called back from withi n a | WARNING concurrency: the vfunctions below are called back from withi n a | |||
ClientCursor::ccmutex. Don't cause a deadlock, you've been warned. | ClientCursor::ccmutex. Don't cause a deadlock, you've been warned. | |||
End of changes. 1 change blocks. | ||||
1 lines changed or deleted | 1 lines changed or added | |||
cursors.h | cursors.h | |||
---|---|---|---|---|
// cursors.h | // cursors.h | |||
/* | ||||
* Copyright (C) 2010 10gen Inc. | ||||
* | ||||
* This program is free software: you can redistribute it and/or modify | ||||
* it under the terms of the GNU Affero General Public License, version | ||||
3, | ||||
* as published by the Free Software Foundation. | ||||
* | ||||
* This program is distributed in the hope that it will be useful, | ||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
* GNU Affero General Public License for more details. | ||||
* | ||||
* You should have received a copy of the GNU Affero General Public Lice | ||||
nse | ||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
*/ | ||||
#pragma once | #pragma once | |||
#include "../stdafx.h" | #include "../stdafx.h" | |||
#include "../db/jsobj.h" | #include "../db/jsobj.h" | |||
#include "../db/dbmessage.h" | #include "../db/dbmessage.h" | |||
#include "../client/dbclient.h" | #include "../client/dbclient.h" | |||
#include "../client/parallel.h" | #include "../client/parallel.h" | |||
End of changes. 1 change blocks. | ||||
0 lines changed or deleted | 17 lines changed or added | |||
d_logic.h | d_logic.h | |||
---|---|---|---|---|
// d_logic.h | // d_logic.h | |||
/* | ||||
* Copyright (C) 2010 10gen Inc. | ||||
* | ||||
* This program is free software: you can redistribute it and/or modify | ||||
* it under the terms of the GNU Affero General Public License, version | ||||
3, | ||||
* as published by the Free Software Foundation. | ||||
* | ||||
* This program is distributed in the hope that it will be useful, | ||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
* GNU Affero General Public License for more details. | ||||
* | ||||
* You should have received a copy of the GNU Affero General Public Lice | ||||
nse | ||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
*/ | ||||
#pragma once | #pragma once | |||
#include "../stdafx.h" | #include "../stdafx.h" | |||
namespace mongo { | namespace mongo { | |||
/** | /** | |||
* @return true if we have any shard info for the ns | * @return true if we have any shard info for the ns | |||
*/ | */ | |||
End of changes. 1 change blocks. | ||||
0 lines changed or deleted | 17 lines changed or added | |||
database.h | database.h | |||
---|---|---|---|---|
skipping to change at line 38 | skipping to change at line 38 | |||
* NOT memory mapped | * NOT memory mapped | |||
*/ | */ | |||
class Database { | class Database { | |||
public: | public: | |||
static bool _openAllFiles; | static bool _openAllFiles; | |||
Database(const char *nm, bool& newDb, const string& _path = dbpath) | Database(const char *nm, bool& newDb, const string& _path = dbpath) | |||
: name(nm), path(_path), namespaceIndex( path, name ) { | : name(nm), path(_path), namespaceIndex( path, name ) { | |||
{ // check db name is valid | { // check db name is valid | |||
int L = strlen(nm); | size_t L = strlen(nm); | |||
uassert( 10028 , "db name is empty", L > 0 ); | uassert( 10028 , "db name is empty", L > 0 ); | |||
uassert( 10029 , "bad db name [1]", *nm != '.' ); | uassert( 10029 , "bad db name [1]", *nm != '.' ); | |||
uassert( 10030 , "bad db name [2]", nm[L-1] != '.' ); | uassert( 10030 , "bad db name [2]", nm[L-1] != '.' ); | |||
uassert( 10031 , "bad char(s) in db name", strchr(nm, ' ') == 0 ); | uassert( 10031 , "bad char(s) in db name", strchr(nm, ' ') == 0 ); | |||
uassert( 10032 , "db name too long", L < 64 ); | uassert( 10032 , "db name too long", L < 64 ); | |||
} | } | |||
newDb = namespaceIndex.exists(); | newDb = namespaceIndex.exists(); | |||
profile = 0; | profile = 0; | |||
profileName = name + ".system.profile"; | profileName = name + ".system.profile"; | |||
skipping to change at line 65 | skipping to change at line 65 | |||
openAllFiles(); | openAllFiles(); | |||
} | } | |||
magic = 781231; | magic = 781231; | |||
} | } | |||
~Database() { | ~Database() { | |||
magic = 0; | magic = 0; | |||
btreeStore->closeFiles(name, path); | btreeStore->closeFiles(name, path); | |||
int n = files.size(); | size_t n = files.size(); | |||
for ( int i = 0; i < n; i++ ) | for ( size_t i = 0; i < n; i++ ) | |||
delete files[i]; | delete files[i]; | |||
} | } | |||
/** | /** | |||
* tries to make sure that this hasn't been deleted | * tries to make sure that this hasn't been deleted | |||
*/ | */ | |||
bool isOk(){ | bool isOk(){ | |||
return magic == 781231; | return magic == 781231; | |||
} | } | |||
bool isEmpty(){ | bool isEmpty(){ | |||
return ! namespaceIndex.allocated(); | return ! namespaceIndex.allocated(); | |||
} | } | |||
bool exists(int n) { | boost::filesystem::path fileName( int n ) { | |||
stringstream ss; | stringstream ss; | |||
ss << name << '.' << n; | ss << name << '.' << n; | |||
boost::filesystem::path fullName; | boost::filesystem::path fullName; | |||
fullName = boost::filesystem::path(path) / ss.str(); | fullName = boost::filesystem::path(path); | |||
return boost::filesystem::exists(fullName); | if ( directoryperdb ) | |||
fullName /= name; | ||||
fullName /= ss.str(); | ||||
return fullName; | ||||
} | ||||
bool exists(int n) { | ||||
return boost::filesystem::exists( fileName( n ) ); | ||||
} | } | |||
void openAllFiles() { | void openAllFiles() { | |||
int n = 0; | int n = 0; | |||
while( exists(n) ) { | while( exists(n) ) { | |||
getFile(n); | getFile(n); | |||
n++; | n++; | |||
} | } | |||
// If last file is empty, consider it preallocated and make sur e it's not mapped | // If last file is empty, consider it preallocated and make sur e it's not mapped | |||
// until a write is requested | // until a write is requested | |||
skipping to change at line 126 | skipping to change at line 133 | |||
if ( n > 100 ) | if ( n > 100 ) | |||
out() << "getFile(): n=" << n << "?" << endl; | out() << "getFile(): n=" << n << "?" << endl; | |||
} | } | |||
MongoDataFile* p = 0; | MongoDataFile* p = 0; | |||
if ( !preallocateOnly ) { | if ( !preallocateOnly ) { | |||
while ( n >= (int) files.size() ) | while ( n >= (int) files.size() ) | |||
files.push_back(0); | files.push_back(0); | |||
p = files[n]; | p = files[n]; | |||
} | } | |||
if ( p == 0 ) { | if ( p == 0 ) { | |||
stringstream ss; | boost::filesystem::path fullName = fileName( n ); | |||
ss << name << '.' << n; | ||||
boost::filesystem::path fullName; | ||||
fullName = boost::filesystem::path(path) / ss.str(); | ||||
string fullNameString = fullName.string(); | string fullNameString = fullName.string(); | |||
p = new MongoDataFile(n); | p = new MongoDataFile(n); | |||
int minSize = 0; | int minSize = 0; | |||
if ( n != 0 && files[ n - 1 ] ) | if ( n != 0 && files[ n - 1 ] ) | |||
minSize = files[ n - 1 ]->getHeader()->fileLength; | minSize = files[ n - 1 ]->getHeader()->fileLength; | |||
if ( sizeNeeded + MDFHeader::headerSize() > minSize ) | if ( sizeNeeded + MDFHeader::headerSize() > minSize ) | |||
minSize = sizeNeeded + MDFHeader::headerSize(); | minSize = sizeNeeded + MDFHeader::headerSize(); | |||
try { | try { | |||
p->open( fullNameString.c_str(), minSize, preallocateOn ly ); | p->open( fullNameString.c_str(), minSize, preallocateOn ly ); | |||
} | } | |||
End of changes. 5 change blocks. | ||||
10 lines changed or deleted | 14 lines changed or added | |||
db.h | db.h | |||
---|---|---|---|---|
skipping to change at line 21 | skipping to change at line 21 | |||
* GNU Affero General Public License for more details. | * GNU Affero General Public License for more details. | |||
* | * | |||
* You should have received a copy of the GNU Affero General Public Licen se | * You should have received a copy of the GNU Affero General Public Licen se | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../stdafx.h" | #include "../stdafx.h" | |||
#include "../util/message.h" | #include "../util/message.h" | |||
#include "../util/top.h" | ||||
#include "boost/version.hpp" | #include "boost/version.hpp" | |||
#include "concurrency.h" | #include "concurrency.h" | |||
#include "pdfile.h" | #include "pdfile.h" | |||
#include "client.h" | #include "client.h" | |||
namespace mongo { | namespace mongo { | |||
// void jniCallback(Message& m, Message& out); | // void jniCallback(Message& m, Message& out); | |||
/* Note the limit here is rather arbitrary and is simply a standard. ge nerally the code works | /* Note the limit here is rather arbitrary and is simply a standard. ge nerally the code works | |||
skipping to change at line 53 | skipping to change at line 52 | |||
/** | /** | |||
* class to hold path + dbname -> Database | * class to hold path + dbname -> Database | |||
* might be able to optimizer further | * might be able to optimizer further | |||
*/ | */ | |||
class DatabaseHolder { | class DatabaseHolder { | |||
public: | public: | |||
DatabaseHolder() : _size(0){ | DatabaseHolder() : _size(0){ | |||
} | } | |||
bool isLoaded( const string& ns , const string& path ){ | ||||
dbMutex.assertAtLeastReadLocked(); | ||||
map<string,Database*>& m = _paths[path]; | ||||
string db = _todb( ns ); | ||||
map<string,Database*>::iterator it = m.find(db); | ||||
return it != m.end(); | ||||
} | ||||
Database * get( const string& ns , const string& path ){ | Database * get( const string& ns , const string& path ){ | |||
dbMutex.assertAtLeastReadLocked(); | dbMutex.assertAtLeastReadLocked(); | |||
map<string,Database*>& m = _paths[path]; | map<string,Database*>& m = _paths[path]; | |||
string db = _todb( ns ); | string db = _todb( ns ); | |||
map<string,Database*>::iterator it = m.find(db); | map<string,Database*>::iterator it = m.find(db); | |||
if ( it != m.end() ) | if ( it != m.end() ) | |||
return it->second; | return it->second; | |||
return 0; | return 0; | |||
skipping to change at line 74 | skipping to change at line 83 | |||
void put( const string& ns , const string& path , Database * db ){ | void put( const string& ns , const string& path , Database * db ){ | |||
dbMutex.assertWriteLocked(); | dbMutex.assertWriteLocked(); | |||
map<string,Database*>& m = _paths[path]; | map<string,Database*>& m = _paths[path]; | |||
Database*& d = m[_todb(ns)]; | Database*& d = m[_todb(ns)]; | |||
if ( ! d ) | if ( ! d ) | |||
_size++; | _size++; | |||
d = db; | d = db; | |||
} | } | |||
Database* getOrCreate( const string& ns , const string& path , bool | ||||
& justCreated ){ | ||||
dbMutex.assertWriteLocked(); | ||||
map<string,Database*>& m = _paths[path]; | ||||
string dbname = _todb( ns ); | ||||
Database* & db = m[dbname]; | ||||
if ( db ){ | ||||
justCreated = false; | ||||
return db; | ||||
} | ||||
log(1) << "Accessing: " << dbname << " for the first time" << e | ||||
ndl; | ||||
db = new Database( dbname.c_str() , justCreated , path ); | ||||
_size++; | ||||
return db; | ||||
} | ||||
void erase( const string& ns , const string& path ){ | void erase( const string& ns , const string& path ){ | |||
dbMutex.assertWriteLocked(); | dbMutex.assertWriteLocked(); | |||
map<string,Database*>& m = _paths[path]; | map<string,Database*>& m = _paths[path]; | |||
_size -= m.erase( _todb( ns ) ); | _size -= (int)m.erase( _todb( ns ) ); | |||
} | } | |||
bool closeAll( const string& path , BSONObjBuilder& result ); | /* force - force close even if something underway - use at shutdown | |||
*/ | ||||
bool closeAll( const string& path , BSONObjBuilder& result, bool fo | ||||
rce ); | ||||
int size(){ | int size(){ | |||
return _size; | return _size; | |||
} | } | |||
/** | /** | |||
* gets all unique db names, ignoring paths | * gets all unique db names, ignoring paths | |||
*/ | */ | |||
void getAllShortNames( set<string>& all ) const{ | void getAllShortNames( set<string>& all ) const{ | |||
dbMutex.assertAtLeastReadLocked(); | dbMutex.assertAtLeastReadLocked(); | |||
skipping to change at line 115 | skipping to change at line 143 | |||
return ns.substr( 0 , i ); | return ns.substr( 0 , i ); | |||
} | } | |||
map<string, map<string,Database*> > _paths; | map<string, map<string,Database*> > _paths; | |||
int _size; | int _size; | |||
}; | }; | |||
extern DatabaseHolder dbHolder; | extern DatabaseHolder dbHolder; | |||
/* returns true if the database ("database") did not exist, and it was | ||||
created on this call | ||||
path - datafiles directory, if not the default, so we can differenti | ||||
ate between db's of the same | ||||
name in different places (for example temp ones on repair). | ||||
*/ | ||||
inline bool setClient(const char *ns, const string& path , mongolock *l | ||||
ock ) { | ||||
if( logLevel > 5 ) | ||||
log() << "setClient: " << ns << endl; | ||||
dbMutex.assertAtLeastReadLocked(); | ||||
Client& c = cc(); | ||||
c.top.clientStart( ns ); | ||||
Database * db = dbHolder.get( ns , path ); | ||||
if ( db ){ | ||||
c.setns(ns, db ); | ||||
return false; | ||||
} | ||||
if( lock ) | ||||
lock->releaseAndWriteLock(); | ||||
assertInWriteLock(); | ||||
char cl[256]; | ||||
nsToDatabase(ns, cl); | ||||
bool justCreated; | ||||
Database *newdb = new Database(cl, justCreated, path); | ||||
dbHolder.put(ns,path,newdb); | ||||
c.setns(ns, newdb); | ||||
newdb->finishInit(); | ||||
return justCreated; | ||||
} | ||||
// shared functionality for removing references to a database from this program instance | // shared functionality for removing references to a database from this program instance | |||
// does not delete the files on disk | // does not delete the files on disk | |||
void closeDatabase( const char *cl, const string& path = dbpath ); | void closeDatabase( const char *cl, const string& path = dbpath ); | |||
struct dbtemprelease { | struct dbtemprelease { | |||
string clientname; | Client::Context * _context; | |||
string clientpath; | int _locktype; | |||
int locktype; | ||||
dbtemprelease() { | dbtemprelease() { | |||
Client& client = cc(); | _context = cc().getContext(); | |||
Database *database = client.database(); | _locktype = dbMutex.getState(); | |||
if ( database ) { | assert( _locktype ); | |||
clientname = database->name; | ||||
clientpath = database->path; | if ( _locktype > 0 ) { | |||
} | massert( 10298 , "can't temprelease nested w | |||
client.top.clientStop(); | rite lock", _locktype == 1); | |||
locktype = dbMutex.getState(); | if ( _context ) _context->unlocked(); | |||
assert( locktype ); | ||||
if ( locktype > 0 ) { | ||||
massert( 10298 , "can't temprelease nested w | ||||
rite lock", locktype == 1); | ||||
dbMutex.unlock(); | dbMutex.unlock(); | |||
} | } | |||
else { | else { | |||
massert( 10299 , "can't temprelease nested r | massert( 10299 , "can't temprelease nested r | |||
ead lock", locktype == -1); | ead lock", _locktype == -1); | |||
if ( _context ) _context->unlocked(); | ||||
dbMutex.unlock_shared(); | dbMutex.unlock_shared(); | |||
} | } | |||
} | } | |||
~dbtemprelease() { | ~dbtemprelease() { | |||
if ( locktype > 0 ) | if ( _locktype > 0 ) | |||
dbMutex.lock(); | dbMutex.lock(); | |||
else | else | |||
dbMutex.lock_shared(); | dbMutex.lock_shared(); | |||
if ( clientname.empty() ) | ||||
cc().setns("", 0); | if ( _context ) _context->relocked(); | |||
else | ||||
setClient(clientname.c_str(), clientpath.c_str()); | ||||
} | } | |||
}; | }; | |||
/** | /** | |||
only does a temp release if we're not nested and have a lock | only does a temp release if we're not nested and have a lock | |||
*/ | */ | |||
struct dbtempreleasecond { | struct dbtempreleasecond { | |||
dbtemprelease * real; | dbtemprelease * real; | |||
int locktype; | int locktype; | |||
End of changes. 12 change blocks. | ||||
64 lines changed or deleted | 53 lines changed or added | |||
dbclient.h | dbclient.h | |||
---|---|---|---|---|
skipping to change at line 208 | skipping to change at line 208 | |||
}; | }; | |||
/** Queries return a cursor object */ | /** Queries return a cursor object */ | |||
class DBClientCursor : boost::noncopyable { | class DBClientCursor : boost::noncopyable { | |||
friend class DBClientBase; | friend class DBClientBase; | |||
bool init(); | bool init(); | |||
public: | public: | |||
/** If true, safe to call next(). Requests more from server if necessary. */ | /** If true, safe to call next(). Requests more from server if necessary. */ | |||
bool more(); | bool more(); | |||
/** If true, there is more in our local buffers to be fetched via n | ||||
ext(). Returns | ||||
false when a getMore request back to server would be required. | ||||
You can use this | ||||
if you want to exhaust whatever data has been fetched to the cl | ||||
ient already but | ||||
then perhaps stop. | ||||
*/ | ||||
bool moreInCurrentBatch() { return pos < nReturned; } | ||||
/** next | /** next | |||
@return next object in the result cursor. | @return next object in the result cursor. | |||
on an error at the remote server, you will get back: | on an error at the remote server, you will get back: | |||
{ $err: <string> } | { $err: <string> } | |||
if you do not want to handle that yourself, call nextSafe(). | if you do not want to handle that yourself, call nextSafe(). | |||
*/ | */ | |||
BSONObj next(); | BSONObj next(); | |||
/** throws AssertionException if get back { $err : ... } */ | /** throws AssertionException if get back { $err : ... } */ | |||
BSONObj nextSafe() { | BSONObj nextSafe() { | |||
End of changes. 1 change blocks. | ||||
0 lines changed or deleted | 10 lines changed or added | |||
dbmessage.h | dbmessage.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 "storage.h" | #include "diskloc.h" | |||
#include "jsobj.h" | #include "jsobj.h" | |||
#include "namespace.h" | #include "namespace.h" | |||
#include "../util/message.h" | #include "../util/message.h" | |||
namespace mongo { | namespace mongo { | |||
/* db response format | /* db response format | |||
Query or GetMore: // see struct QueryResult | Query or GetMore: // see struct QueryResult | |||
int resultFlags; | int resultFlags; | |||
skipping to change at line 223 | skipping to change at line 223 | |||
b.skip(sizeof(QueryResult)); | b.skip(sizeof(QueryResult)); | |||
b.append(data, size); | b.append(data, size); | |||
QueryResult *qr = (QueryResult *) b.buf(); | QueryResult *qr = (QueryResult *) b.buf(); | |||
qr->_resultFlags() = queryResultFlags; | qr->_resultFlags() = queryResultFlags; | |||
qr->len = b.len(); | qr->len = b.len(); | |||
qr->setOperation(opReply); | qr->setOperation(opReply); | |||
qr->cursorId = cursorId; | qr->cursorId = cursorId; | |||
qr->startingFrom = startingFrom; | qr->startingFrom = startingFrom; | |||
qr->nReturned = nReturned; | qr->nReturned = nReturned; | |||
b.decouple(); | b.decouple(); | |||
Message *resp = new Message(); | Message resp(qr, true); | |||
resp->setData(qr, true); // transport will free | p->reply(requestMsg, resp, requestMsg.data->id); | |||
p->reply(requestMsg, *resp, requestMsg.data->id); | ||||
} | } | |||
} // namespace mongo | } // namespace mongo | |||
//#include "bsonobj.h" | //#include "bsonobj.h" | |||
#include "instance.h" | #include "instance.h" | |||
namespace mongo { | namespace mongo { | |||
/* object reply helper. */ | /* object reply helper. */ | |||
End of changes. 2 change blocks. | ||||
4 lines changed or deleted | 3 lines changed or added | |||
engine.h | engine.h | |||
---|---|---|---|---|
skipping to change at line 114 | skipping to change at line 114 | |||
} | } | |||
static void validateObjectIdString( const string &str ); | static void validateObjectIdString( const string &str ); | |||
protected: | protected: | |||
virtual ScriptingFunction _createFunction( const char * code ) = 0; | virtual ScriptingFunction _createFunction( const char * code ) = 0; | |||
string _localDBName; | string _localDBName; | |||
long long _loadedVersion; | long long _loadedVersion; | |||
set<string> _storedNames; | ||||
static long long _lastVersion; | static long long _lastVersion; | |||
map<string,ScriptingFunction> _cachedFunctions; | map<string,ScriptingFunction> _cachedFunctions; | |||
static int _numScopes; | static int _numScopes; | |||
}; | }; | |||
void installGlobalUtils( Scope& scope ); | ||||
class ScriptEngine : boost::noncopyable { | class ScriptEngine : boost::noncopyable { | |||
public: | public: | |||
ScriptEngine(); | ScriptEngine(); | |||
virtual ~ScriptEngine(); | virtual ~ScriptEngine(); | |||
virtual Scope * newScope() { | virtual Scope * newScope() { | |||
Scope *s = createScope(); | Scope *s = createScope(); | |||
if ( s && _scopeInitCallback ) | if ( s && _scopeInitCallback ) | |||
_scopeInitCallback( *s ); | _scopeInitCallback( *s ); | |||
installGlobalUtils( *s ); | ||||
return s; | return s; | |||
} | } | |||
virtual void runTest() = 0; | virtual void runTest() = 0; | |||
virtual bool utf8Ok() const = 0; | virtual bool utf8Ok() const = 0; | |||
static void setup(); | static void setup(); | |||
auto_ptr<Scope> getPooledScope( const string& pool ); | auto_ptr<Scope> getPooledScope( const string& pool ); | |||
End of changes. 3 change blocks. | ||||
0 lines changed or deleted | 4 lines changed or added | |||
engine_spidermonkey.h | engine_spidermonkey.h | |||
---|---|---|---|---|
skipping to change at line 96 | skipping to change at line 96 | |||
class Convertor; | class Convertor; | |||
extern JSClass bson_class; | extern JSClass bson_class; | |||
extern JSClass bson_ro_class; | extern JSClass bson_ro_class; | |||
extern JSClass object_id_class; | extern JSClass object_id_class; | |||
extern JSClass dbpointer_class; | extern JSClass dbpointer_class; | |||
extern JSClass dbref_class; | extern JSClass dbref_class; | |||
extern JSClass bindata_class; | extern JSClass bindata_class; | |||
extern JSClass timestamp_class; | extern JSClass timestamp_class; | |||
extern JSClass numberlong_class; | ||||
extern JSClass minkey_class; | extern JSClass minkey_class; | |||
extern JSClass maxkey_class; | extern JSClass maxkey_class; | |||
// internal things | // internal things | |||
void dontDeleteScope( SMScope * s ){} | void dontDeleteScope( SMScope * s ){} | |||
void errorReporter( JSContext *cx, const char *message, JSErrorReport * report ); | void errorReporter( JSContext *cx, const char *message, JSErrorReport * report ); | |||
extern boost::thread_specific_ptr<SMScope> currentScope; | extern boost::thread_specific_ptr<SMScope> currentScope; | |||
// bson | // bson | |||
JSBool resolveBSONField( JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp ); | JSBool resolveBSONField( JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp ); | |||
// mongo | // mongo | |||
void initMongoJS( SMScope * scope , JSContext * cx , JSObject * global , bool local ); | void initMongoJS( SMScope * scope , JSContext * cx , JSObject * global , bool local ); | |||
bool appendSpecialDBObject( Convertor * c , BSONObjBuilder& b , const s tring& name , jsval val , JSObject * o ); | bool appendSpecialDBObject( Convertor * c , BSONObjBuilder& b , const s tring& name , jsval val , JSObject * o ); | |||
#define JSVAL_IS_OID(v) ( JSVAL_IS_OBJECT( v ) && JS_InstanceOf( cx , JSVAL _TO_OBJECT( v ) , &object_id_class , 0 ) ) | #define JSVAL_IS_OID(v) ( JSVAL_IS_OBJECT( v ) && JS_InstanceOf( cx , JSVAL _TO_OBJECT( v ) , &object_id_class , 0 ) ) | |||
bool isDate( JSContext * cx , JSObject * o ); | bool isDate( JSContext * cx , JSObject * o ); | |||
// JS private data must be 2byte aligned, so we use a holder to refer t | ||||
o an unaligned pointer. | ||||
struct BinDataHolder { | ||||
BinDataHolder( const char *c, int copyLen = -1 ) : | ||||
c_( const_cast< char * >( c ) ), | ||||
iFree_( copyLen != -1 ) { | ||||
if ( copyLen != -1 ) { | ||||
c_ = (char*)malloc( copyLen ); | ||||
memcpy( c_, c, copyLen ); | ||||
} | ||||
} | ||||
~BinDataHolder() { | ||||
if ( iFree_ ) | ||||
free( c_ ); | ||||
} | ||||
char *c_; | ||||
bool iFree_; | ||||
}; | ||||
} | } | |||
End of changes. 2 change blocks. | ||||
0 lines changed or deleted | 19 lines changed or added | |||
file_allocator.h | file_allocator.h | |||
---|---|---|---|---|
skipping to change at line 160 | skipping to change at line 160 | |||
string name; | string name; | |||
long size; | long size; | |||
{ | { | |||
boostlock lk( a_.pendingMutex_ ); | boostlock 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; | ||||
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; | |||
ss << "couldn't open " << name << ' ' << OU TPUT_ERRNO; | ss << "couldn't open " << name << ' ' << OU TPUT_ERRNO; | |||
massert( 10439 , ss.str(), fd <= 0 ); | massert( 10439 , ss.str(), fd <= 0 ); | |||
} | } | |||
#if defined(POSIX_FADV_DONTNEED) | #if defined(POSIX_FADV_DONTNEED) | |||
if( posix_fadvise(fd, 0, size, POSIX_FADV_DONTN EED) ) { | if( posix_fadvise(fd, 0, size, POSIX_FADV_DONTN EED) ) { | |||
log() << "warning: posix_fadvise fails " << name << ' ' << OUTPUT_ERRNO << endl; | log() << "warning: posix_fadvise fails " << name << ' ' << OUTPUT_ERRNO << endl; | |||
skipping to change at line 183 | skipping to change at line 184 | |||
/* make sure the file is the full desired lengt h */ | /* make sure the file is the full desired lengt h */ | |||
off_t filelen = lseek(fd, 0, SEEK_END); | off_t filelen = lseek(fd, 0, SEEK_END); | |||
if ( filelen < size ) { | if ( filelen < size ) { | |||
massert( 10440 , "failure creating new dat afile", filelen == 0 ); | massert( 10440 , "failure creating new dat afile", filelen == 0 ); | |||
// Check for end of disk. | // Check for end of disk. | |||
massert( 10441 , "Unable to allocate file of desired size", | massert( 10441 , "Unable to allocate file of desired size", | |||
size - 1 == lseek(fd, size - 1, SEE K_SET) ); | size - 1 == lseek(fd, size - 1, SEE K_SET) ); | |||
massert( 10442 , "Unable to allocate file of desired size", | massert( 10442 , "Unable to allocate file of desired size", | |||
1 == write(fd, "", 1) ); | 1 == write(fd, "", 1) ); | |||
lseek(fd, 0, SEEK_SET); | lseek(fd, 0, SEEK_SET); | |||
log() << "allocating new datafile " << name << ", filling with zeroes..." << endl; | ||||
Timer t; | Timer t; | |||
long z = 256 * 1024; | long z = 256 * 1024; | |||
char buf[z]; | char buf[z]; | |||
memset(buf, 0, z); | memset(buf, 0, z); | |||
long left = size; | long left = size; | |||
while ( 1 ) { | while ( left > 0 ) { | |||
if ( left <= z ) { | long towrite = left; | |||
massert( 10443 , "write failed", l | if ( towrite > z ) | |||
eft == write(fd, buf, left) ); | towrite = z; | |||
break; | ||||
} | int written = write( fd , buf , towrite | |||
massert( 10444 , "write failed", z == | ); | |||
write(fd, buf, z) ); | massert( 10443 , errnostring("write fai | |||
left -= z; | led" ), written > 0 ); | |||
left -= written; | ||||
} | } | |||
log() << "done allocating datafile " << nam e << ", size: " << size/1024/1024 << "MB, took " << ((double)t.millis())/10 00.0 << " secs" << endl; | log() << "done allocating datafile " << nam e << ", size: " << size/1024/1024 << "MB, took " << ((double)t.millis())/10 00.0 << " secs" << endl; | |||
} | } | |||
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 ) ); | |||
End of changes. 3 change blocks. | ||||
10 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) | #if !defined(_WIN32) && !defined(NOEXECINFO) && !defined(__freebsd__) | |||
} // 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 123 | skipping to change at line 123 | |||
#undef assert | #undef assert | |||
#define assert xassert | #define assert xassert | |||
#define yassert 1 | #define yassert 1 | |||
struct WrappingInt { | struct WrappingInt { | |||
WrappingInt() { | WrappingInt() { | |||
x = 0; | x = 0; | |||
} | } | |||
WrappingInt(unsigned z) : x(z) { } | WrappingInt(unsigned z) : x(z) { } | |||
volatile unsigned x; | unsigned x; | |||
operator unsigned() const { | operator unsigned() const { | |||
return x; | return x; | |||
} | } | |||
// returns original value (like x++) | ||||
WrappingInt atomicIncrement(){ | ||||
#if defined(_WIN32) | ||||
// InterlockedIncrement returns the new value | ||||
return InterlockedIncrement((volatile long*)&x)-1; //long is 32 | ||||
bits in Win64 | ||||
#elif defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) | ||||
// this is in GCC >= 4.1 | ||||
return __sync_fetch_and_add(&x, 1); | ||||
#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) | ||||
// from boost 1.39 interprocess/detail/atomic.hpp | ||||
int r; | ||||
int val = 1; | ||||
asm volatile | ||||
( | ||||
"lock\n\t" | ||||
"xadd %1, %0": | ||||
"+m"( x ), "=r"( r ): // outputs (%0, %1) | ||||
"1"( val ): // inputs (%2 == %1) | ||||
"memory", "cc" // clobbers | ||||
); | ||||
return r; | ||||
#else | ||||
# error "unsupported compiler or platform" | ||||
#endif | ||||
} | ||||
static int diff(unsigned a, unsigned b) { | static int diff(unsigned a, unsigned b) { | |||
return a-b; | return a-b; | |||
} | } | |||
bool operator<=(WrappingInt r) { | bool operator<=(WrappingInt r) { | |||
// platform dependent | // platform dependent | |||
int df = (r.x - x); | int df = (r.x - x); | |||
return df >= 0; | return df >= 0; | |||
} | } | |||
bool operator>(WrappingInt r) { | bool operator>(WrappingInt r) { | |||
return !(r<=*this); | return !(r<=*this); | |||
skipping to change at line 333 | skipping to change at line 307 | |||
public: | public: | |||
DebugMutex() : locked(0); { } | DebugMutex() : locked(0); { } | |||
bool isLocked() { return locked; } | bool isLocked() { return locked; } | |||
}; | }; | |||
*/ | */ | |||
//typedef boostlock lock; | //typedef boostlock lock; | |||
inline bool startsWith(const char *str, const char *prefix) { | inline bool startsWith(const char *str, const char *prefix) { | |||
unsigned 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) { | |||
int a = strlen(p); | size_t a = strlen(p); | |||
int b = strlen(suffix); | size_t b = strlen(suffix); | |||
if ( b > a ) return false; | if ( b > a ) return false; | |||
return strcmp(p + a - b, suffix) == 0; | return strcmp(p + a - b, suffix) == 0; | |||
} | } | |||
} // namespace mongo | } // namespace mongo | |||
#include "boost/detail/endian.hpp" | #include "boost/detail/endian.hpp" | |||
namespace mongo { | namespace mongo { | |||
End of changes. 5 change blocks. | ||||
32 lines changed or deleted | 5 lines changed or added | |||
hashtab.h | hashtab.h | |||
---|---|---|---|---|
skipping to change at line 152 | skipping to change at line 152 | |||
} | } | |||
else { | else { | |||
assert( nodes[i].hash == k.hash() ); | assert( nodes[i].hash == k.hash() ); | |||
} | } | |||
nodes[i].value = value; | nodes[i].value = value; | |||
return true; | return true; | |||
} | } | |||
typedef void (*IteratorCallback)( const Key& k , Type& v ); | typedef void (*IteratorCallback)( const Key& k , Type& v ); | |||
void iterall( IteratorCallback callback ){ | void iterAll( IteratorCallback callback ){ | |||
for ( int i=0; i<n; i++ ){ | for ( int i=0; i<n; i++ ){ | |||
if ( ! nodes[i].inUse() ) | if ( ! nodes[i].inUse() ) | |||
continue; | continue; | |||
callback( nodes[i].k , nodes[i].value ); | callback( nodes[i].k , nodes[i].value ); | |||
} | } | |||
} | } | |||
}; | }; | |||
#pragma pack() | #pragma pack() | |||
End of changes. 1 change blocks. | ||||
1 lines changed or deleted | 1 lines changed or added | |||
index.h | index.h | |||
---|---|---|---|---|
skipping to change at line 25 | skipping to change at line 25 | |||
* You should have received a copy of the GNU Affero General Public Licen se | * You should have received a copy of the GNU Affero General Public Licen se | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../stdafx.h" | #include "../stdafx.h" | |||
namespace mongo { | namespace mongo { | |||
/* precomputed details about an index, used for inserting keys on updat | ||||
es | ||||
stored/cached in NamespaceDetailsTransient, or can be used standalon | ||||
e | ||||
*/ | ||||
class IndexSpec { | class IndexSpec { | |||
public: | public: | |||
BSONObj keys; | BSONObj keyPattern; // e.g., { name : 1 } | |||
BSONObj meta; | BSONObj info; // this is the same as IndexDetails::info.obj() | |||
IndexSpec(){ | IndexSpec(){ | |||
} | } | |||
IndexSpec( const BSONObj& k , const BSONObj& m = BSONObj() ) | IndexSpec( const BSONObj& k , const BSONObj& m = BSONObj() ) | |||
: keys(k) , meta(m){ | : keyPattern(k) , info(m){ | |||
_init(); | _init(); | |||
} | } | |||
/** | /** | |||
this is a DickLock of an IndexDetails info | this is a DiscLoc of an IndexDetails info | |||
should have a key field | should have a key field | |||
*/ | */ | |||
IndexSpec( const DiskLoc& loc ){ | IndexSpec( const DiskLoc& loc ){ | |||
reset( loc ); | reset( loc ); | |||
} | } | |||
void reset( const DiskLoc& loc ){ | void reset( const DiskLoc& loc ){ | |||
meta = loc.obj(); | info = loc.obj(); | |||
keys = meta["key"].embeddedObjectUserCheck(); | keyPattern = info["key"].embeddedObjectUserCheck(); | |||
if ( keys.objsize() == 0 ) { | if ( keyPattern.objsize() == 0 ) { | |||
out() << meta.toString() << endl; | out() << info.toString() << endl; | |||
assert(false); | assert(false); | |||
} | } | |||
_init(); | _init(); | |||
} | } | |||
void getKeys( const BSONObj &obj, BSONObjSetDefaultOrder &keys ) co nst; | void getKeys( const BSONObj &obj, BSONObjSetDefaultOrder &keys ) co nst; | |||
private: | private: | |||
void _getKeys( vector<const char*> fieldNames , vector<BSONElement> fixed , const BSONObj &obj, BSONObjSetDefaultOrder &keys ) const; | void _getKeys( vector<const char*> fieldNames , vector<BSONElement> fixed , const BSONObj &obj, BSONObjSetDefaultOrder &keys ) const; | |||
vector<const char*> _fieldNames; | vector<const char*> _fieldNames; | |||
vector<BSONElement> _fixed; | vector<BSONElement> _fixed; | |||
BSONObj _nullKey; | BSONObj _nullKey; | |||
BSONObj _nullObj; | BSONObj _nullObj; | |||
BSONElement _nullElt; | BSONElement _nullElt; | |||
void _init(); | void _init(); | |||
skipping to change at line 77 | skipping to change at line 78 | |||
BSONObj _nullObj; | BSONObj _nullObj; | |||
BSONElement _nullElt; | BSONElement _nullElt; | |||
void _init(); | void _init(); | |||
}; | }; | |||
/* Details about a particular index. There is one of these effective ly for each object in | /* Details about a particular index. There is one of these effective ly for each object in | |||
system.namespaces (although this also includes the head pointer, which is not in that | system.namespaces (although this also includes the head pointer, which is not in that | |||
collection). | collection). | |||
** MemoryMapped Record ** | ** MemoryMapped Record ** (i.e., this is on disk data) | |||
*/ | */ | |||
class IndexDetails { | class IndexDetails { | |||
public: | public: | |||
DiskLoc head; /* btree head disk location */ | DiskLoc head; /* btree head disk location */ | |||
/* Location of index info object. Format: | /* Location of index info object. Format: | |||
{ name:"nameofindex", ns:"parentnsname", key: {keypattobject} | { name:"nameofindex", ns:"parentnsname", key: {keypattobject} | |||
[, unique: <bool>, background: <bool>] | [, unique: <bool>, background: <bool>] | |||
} | } | |||
skipping to change at line 120 | skipping to change at line 121 | |||
/* get the key pattern for this object. | /* get the key pattern for this object. | |||
e.g., { lastname:1, firstname:1 } | e.g., { lastname:1, firstname:1 } | |||
*/ | */ | |||
BSONObj keyPattern() const { | BSONObj keyPattern() const { | |||
return info.obj().getObjectField("key"); | return info.obj().getObjectField("key"); | |||
} | } | |||
/* true if the specified key is in the index */ | /* true if the specified key is in the index */ | |||
bool hasKey(const BSONObj& key); | bool hasKey(const BSONObj& key); | |||
bool wouldCreateDup(const BSONObj& key, DiskLoc self); | ||||
// returns name of this index's storage area | // returns name of this index's storage area | |||
// database.table.$index | // database.table.$index | |||
string indexNamespace() const { | string indexNamespace() const { | |||
BSONObj io = info.obj(); | BSONObj io = info.obj(); | |||
string s; | string s; | |||
s.reserve(Namespace::MaxNsLen); | s.reserve(Namespace::MaxNsLen); | |||
s = io.getStringField("ns"); | s = io.getStringField("ns"); | |||
assert( !s.empty() ); | assert( !s.empty() ); | |||
s += ".$"; | s += ".$"; | |||
skipping to change at line 187 | skipping to change at line 189 | |||
return info.obj().toString(); | return info.obj().toString(); | |||
} | } | |||
}; | }; | |||
struct IndexChanges/*on an update*/ { | struct IndexChanges/*on an update*/ { | |||
BSONObjSetDefaultOrder oldkeys; | BSONObjSetDefaultOrder oldkeys; | |||
BSONObjSetDefaultOrder newkeys; | BSONObjSetDefaultOrder newkeys; | |||
vector<BSONObj*> removed; // these keys were removed as part of the change | vector<BSONObj*> removed; // these keys were removed as part of the change | |||
vector<BSONObj*> added; // these keys were added as part of the c hange | vector<BSONObj*> added; // these keys were added as part of the c hange | |||
void dupCheck(IndexDetails& idx) { | /** @curObjLoc - the object we want to add's location. if it is al | |||
ready in the | ||||
index, that is allowed here (for bg indexing case) | ||||
. | ||||
*/ | ||||
void dupCheck(IndexDetails& idx, DiskLoc curObjLoc) { | ||||
if( added.empty() || !idx.unique() ) | if( added.empty() || !idx.unique() ) | |||
return; | return; | |||
for( vector<BSONObj*>::iterator i = added.begin(); i != added.e | for( vector<BSONObj*>::iterator i = added.begin(); i != added.e | |||
nd(); i++ ) | nd(); i++ ) { | |||
uassert( 11001 , "E11001 duplicate key on update", !idx.has | bool dup = idx.wouldCreateDup(**i, curObjLoc); | |||
Key(**i)); | uassert( 11001 , "E11001 duplicate key on update", !dup); | |||
} | ||||
} | } | |||
}; | }; | |||
class NamespaceDetails; | class NamespaceDetails; | |||
void getIndexChanges(vector<IndexChanges>& v, NamespaceDetails& d, BSON Obj newObj, BSONObj oldObj); | void getIndexChanges(vector<IndexChanges>& v, NamespaceDetails& d, BSON Obj newObj, BSONObj oldObj); | |||
void dupCheck(vector<IndexChanges>& v, NamespaceDetails& d); | void dupCheck(vector<IndexChanges>& v, NamespaceDetails& d, DiskLoc cur ObjLoc); | |||
} // namespace mongo | } // namespace mongo | |||
End of changes. 12 change blocks. | ||||
17 lines changed or deleted | 27 lines changed or added | |||
instance.h | instance.h | |||
---|---|---|---|---|
skipping to change at line 41 | skipping to change at line 41 | |||
#define OPWRITE if( _diaglog.level & 1 ) _diaglog.write((char *) m.data, m. data->len); | #define OPWRITE if( _diaglog.level & 1 ) _diaglog.write((char *) m.data, m. data->len); | |||
#define OPREAD if( _diaglog.level & 2 ) _diaglog.readop((char *) m.data, m. data->len); | #define OPREAD if( _diaglog.level & 2 ) _diaglog.readop((char *) m.data, m. data->len); | |||
struct DiagLog { | struct DiagLog { | |||
ofstream *f; | ofstream *f; | |||
/* 0 = off; 1 = writes, 2 = reads, 3 = both | /* 0 = off; 1 = writes, 2 = reads, 3 = both | |||
7 = log a few reads, and all writes. | 7 = log a few reads, and all writes. | |||
*/ | */ | |||
int level; | int level; | |||
boost::mutex mutex; | ||||
DiagLog() : f(0) , level(0) { } | DiagLog() : f(0) , level(0) { } | |||
void init() { | void init() { | |||
if ( ! f && level ){ | if ( ! f && level ){ | |||
log() << "diagLogging = " << level << endl; | log() << "diagLogging = " << level << endl; | |||
stringstream ss; | stringstream ss; | |||
ss << "diaglog." << hex << time(0); | ss << dbpath << "/diaglog." << hex << time(0); | |||
string name = ss.str(); | string name = ss.str(); | |||
f = new ofstream(name.c_str(), ios::out | ios::binary); | f = new ofstream(name.c_str(), ios::out | ios::binary); | |||
if ( ! f->good() ) { | if ( ! f->good() ) { | |||
problem() << "couldn't open log stream" << endl; | problem() << "couldn't open log stream" << endl; | |||
throw 1717; | throw 1717; | |||
} | } | |||
} | } | |||
} | } | |||
/** | /** | |||
* @return old | * @return old | |||
*/ | */ | |||
int setLevel( int newLevel ){ | int setLevel( int newLevel ){ | |||
int old = level; | int old = level; | |||
level = newLevel; | level = newLevel; | |||
init(); | init(); | |||
return old; | return old; | |||
} | } | |||
void flush() { | void flush() { | |||
if ( level ) f->flush(); | if ( level ){ | |||
boostlock lk(mutex); | ||||
f->flush(); | ||||
} | ||||
} | } | |||
void write(char *data,int len) { | void write(char *data,int len) { | |||
if ( level & 1 ) f->write(data,len); | if ( level & 1 ){ | |||
boostlock lk(mutex); | ||||
f->write(data,len); | ||||
} | ||||
} | } | |||
void readop(char *data, int len) { | void readop(char *data, int len) { | |||
if ( level & 2 ) { | if ( level & 2 ) { | |||
bool log = (level & 4) == 0; | bool log = (level & 4) == 0; | |||
OCCASIONALLY log = true; | OCCASIONALLY log = true; | |||
if ( log ) | if ( log ){ | |||
boostlock lk(mutex); | ||||
f->write(data,len); | f->write(data,len); | |||
} | ||||
} | } | |||
} | } | |||
}; | }; | |||
extern DiagLog _diaglog; | extern DiagLog _diaglog; | |||
/* we defer response until we unlock. don't want a blocked socket to | /* we defer response until we unlock. don't want a blocked socket to | |||
keep things locked. | keep things locked. | |||
*/ | */ | |||
struct DbResponse { | struct DbResponse { | |||
skipping to change at line 127 | skipping to change at line 137 | |||
} | } | |||
virtual string getServerAddress() const{ | virtual string getServerAddress() const{ | |||
return "localhost"; // TODO: should this have the port? | return "localhost"; // TODO: should this have the port? | |||
} | } | |||
virtual bool call( Message &toSend, Message &response, bool assertO k=true ); | virtual bool call( Message &toSend, Message &response, bool assertO k=true ); | |||
virtual void say( Message &toSend ); | virtual void say( Message &toSend ); | |||
virtual void sayPiggyBack( Message &toSend ) { | virtual void sayPiggyBack( Message &toSend ) { | |||
// don't need to piggy back when connected locally | // don't need to piggy back when connected locally | |||
return say( toSend ); | return say( toSend ); | |||
} | } | |||
class AlwaysAuthorized : public AuthenticationInfo { | ||||
virtual bool isAuthorized( const char *dbname ) { | ||||
return true; | ||||
} | ||||
}; | ||||
/* TODO: this looks bad that auth is set to always. is that really | ||||
always safe? */ | ||||
class SavedContext { | ||||
public: | ||||
SavedContext() { | ||||
_save = dbMutex.atLeastReadLocked(); | ||||
Client *c = currentClient.get(); | ||||
oldAuth = c->ai; | ||||
// careful, don't want to free this: | ||||
c->ai = &always; | ||||
/* it only makes sense to manipulate a pointer - c->databas | ||||
e() - if locked. | ||||
thus the _saved flag. | ||||
*/ | ||||
if( _save ) { | ||||
if ( c->database() ) { | ||||
dbMutex.assertAtLeastReadLocked(); | ||||
_oldName = c->database()->name; | ||||
} | ||||
} | ||||
} | ||||
~SavedContext() { | ||||
Client *c = currentClient.get(); | ||||
c->ai = oldAuth; | ||||
if( _save ) { | ||||
if ( !_oldName.empty() ) { | ||||
dbMutex.assertAtLeastReadLocked(); | ||||
setClient( _oldName.c_str() ); | ||||
} | ||||
} | ||||
else { | ||||
// defensive | ||||
cc().clearns(); | ||||
} | ||||
} | ||||
private: | ||||
bool _save; | ||||
static AlwaysAuthorized always; | ||||
AuthenticationInfo *oldAuth; | ||||
string _oldName; | ||||
}; | ||||
}; | }; | |||
extern int lockFile; | extern int lockFile; | |||
void acquirePathLock(); | void acquirePathLock(); | |||
} // namespace mongo | } // namespace mongo | |||
End of changes. 7 change blocks. | ||||
53 lines changed or deleted | 14 lines changed or added | |||
jsobj.h | jsobj.h | |||
---|---|---|---|---|
skipping to change at line 512 | skipping to change at line 512 | |||
const char *binData(int& len) const { | const char *binData(int& len) const { | |||
// BinData: <int len> <byte subtype> <byte[len] data> | // BinData: <int len> <byte subtype> <byte[len] data> | |||
assert( type() == BinData ); | assert( type() == BinData ); | |||
len = valuestrsize(); | len = valuestrsize(); | |||
return value() + 5; | return value() + 5; | |||
} | } | |||
BinDataType binDataType() const { | BinDataType binDataType() const { | |||
// BinData: <int len> <byte subtype> <byte[len] data> | // BinData: <int len> <byte subtype> <byte[len] data> | |||
assert( type() == BinData ); | assert( type() == BinData ); | |||
char c = (value() + 4)[0]; | unsigned char c = (value() + 4)[0]; | |||
return (BinDataType)c; | return (BinDataType)c; | |||
} | } | |||
/** Retrieve the regex string for a Regex element */ | /** Retrieve the regex string for a Regex element */ | |||
const char *regex() const { | const char *regex() const { | |||
assert(type() == RegEx); | assert(type() == RegEx); | |||
return value(); | return value(); | |||
} | } | |||
/** Retrieve the regex flags (options) for a Regex element */ | /** Retrieve the regex flags (options) for a Regex element */ | |||
skipping to change at line 576 | skipping to change at line 576 | |||
int getGtLtOp( int def = 0 ) const; | int getGtLtOp( int def = 0 ) const; | |||
/** Constructs an empty element */ | /** Constructs an empty element */ | |||
BSONElement(); | BSONElement(); | |||
/** Check that data is internally consistent. */ | /** Check that data is internally consistent. */ | |||
void validate() const; | void validate() const; | |||
/** True if this element may contain subobjects. */ | /** True if this element may contain subobjects. */ | |||
bool mayEncapsulate() const { | bool mayEncapsulate() const { | |||
return type() == Object || | switch ( type() ){ | |||
type() == Array || | case Object: | |||
type() == CodeWScope; | case Array: | |||
case CodeWScope: | ||||
return true; | ||||
default: | ||||
return false; | ||||
} | ||||
} | ||||
/** True if this element can be a BSONObj */ | ||||
bool isABSONObj() const { | ||||
switch( type() ){ | ||||
case Object: | ||||
case Array: | ||||
return true; | ||||
default: | ||||
return false; | ||||
} | ||||
} | } | |||
Date_t timestampTime() const{ | Date_t timestampTime() const{ | |||
unsigned long long t = ((unsigned int*)(value() + 4 ))[0]; | unsigned long long t = ((unsigned int*)(value() + 4 ))[0]; | |||
return t * 1000; | return t * 1000; | |||
} | } | |||
unsigned int timestampInc() const{ | unsigned int timestampInc() const{ | |||
return ((unsigned int*)(value() ))[0]; | return ((unsigned int*)(value() ))[0]; | |||
} | } | |||
skipping to change at line 627 | skipping to change at line 643 | |||
fieldNameSize_ = size + 1; | fieldNameSize_ = size + 1; | |||
} | } | |||
} | } | |||
totalSize = -1; | totalSize = -1; | |||
} | } | |||
private: | private: | |||
const char *data; | const char *data; | |||
mutable int fieldNameSize_; // cached value | mutable int fieldNameSize_; // cached value | |||
int fieldNameSize() const { | int fieldNameSize() const { | |||
if ( fieldNameSize_ == -1 ) | if ( fieldNameSize_ == -1 ) | |||
fieldNameSize_ = strlen( fieldName() ) + 1; | fieldNameSize_ = (int)strlen( fieldName() ) + 1; | |||
return fieldNameSize_; | return fieldNameSize_; | |||
} | } | |||
mutable int totalSize; /* caches the computed size */ | mutable int totalSize; /* caches the computed size */ | |||
}; | }; | |||
int getGtLtOp(const BSONElement& e); | int getGtLtOp(const BSONElement& e); | |||
struct BSONElementCmpWithoutField { | struct BSONElementCmpWithoutField { | |||
bool operator()( const BSONElement &l, const BSONElement &r ) const { | bool operator()( const BSONElement &l, const BSONElement &r ) const { | |||
return l.woCompare( r, false ); | return l.woCompare( r, false ); | |||
skipping to change at line 702 | skipping to change at line 718 | |||
}; | }; | |||
const char *_objdata; | const char *_objdata; | |||
boost::shared_ptr< Holder > _holder; | boost::shared_ptr< Holder > _holder; | |||
void init(const char *data, bool ifree) { | void init(const char *data, bool ifree) { | |||
if ( ifree ) | if ( ifree ) | |||
_holder.reset( new Holder( data ) ); | _holder.reset( new Holder( data ) ); | |||
_objdata = data; | _objdata = data; | |||
if ( ! isValid() ){ | if ( ! isValid() ){ | |||
stringstream ss; | stringstream ss; | |||
ss << "Invalid BSONObj spec size: " << objsize(); | ss << "Invalid BSONObj spec size: " << objsize(); | |||
try { | ||||
BSONElement e = firstElement(); | ||||
ss << " first element:" << e.toString() << " "; | ||||
} | ||||
catch ( ... ){} | ||||
string s = ss.str(); | string s = ss.str(); | |||
massert( 10334 , s , 0 ); | massert( 10334 , s , 0 ); | |||
} | } | |||
} | } | |||
#pragma pack(1) | #pragma pack(1) | |||
static struct EmptyObject { | static struct EmptyObject { | |||
EmptyObject() { | EmptyObject() { | |||
len = 5; | len = 5; | |||
jstype = EOO; | jstype = EOO; | |||
} | } | |||
skipping to change at line 770 | skipping to change at line 791 | |||
*/ | */ | |||
void getFieldsDotted(const char *name, BSONElementSet &ret, bool *d eep = 0) const; | void getFieldsDotted(const char *name, BSONElementSet &ret, bool *d eep = 0) const; | |||
/** Like getFieldDotted(), but returns first array encountered whil e traversing the | /** Like getFieldDotted(), but returns first array encountered whil e traversing the | |||
dotted fields of name. The name variable is updated to represe nt field | dotted fields of name. The name variable is updated to represe nt field | |||
names with respect to the returned element. */ | names with respect to the returned element. */ | |||
BSONElement getFieldDottedOrArray(const char *&name) const; | BSONElement getFieldDottedOrArray(const char *&name) const; | |||
/** Get the field of the specified name. eoo() is true on the retur ned | /** Get the field of the specified name. eoo() is true on the retur ned | |||
element if not found. | element if not found. | |||
*/ | */ | |||
BSONElement getField(const string name) const { | BSONElement getField(const char *name) const; | |||
return getField( name.c_str() ); | ||||
}; | ||||
/** Get the field of the specified name. eoo() is true on the retur ned | /** Get the field of the specified name. eoo() is true on the retur ned | |||
element if not found. | element if not found. | |||
*/ | */ | |||
BSONElement getField(const char *name) const; /* return has eoo() t | BSONElement getField(const string name) const { | |||
rue if no match */ | return getField( name.c_str() ); | |||
}; | ||||
/** Get the field of the specified name. eoo() is true on the retur ned | /** Get the field of the specified name. eoo() is true on the retur ned | |||
element if not found. | element if not found. | |||
*/ | */ | |||
BSONElement operator[] (const char *field) const { | BSONElement operator[] (const char *field) const { | |||
return getField(field); | return getField(field); | |||
} | } | |||
BSONElement operator[] (const string& field) const { | BSONElement operator[] (const string& field) const { | |||
return getField(field); | return getField(field); | |||
skipping to change at line 904 | skipping to change at line 925 | |||
return (os == 0 || memcmp(objdata(),r.objdata(),os)==0); | return (os == 0 || memcmp(objdata(),r.objdata(),os)==0); | |||
} | } | |||
return false; | return false; | |||
} | } | |||
/** @return first field of the object */ | /** @return first field of the object */ | |||
BSONElement firstElement() const { | BSONElement firstElement() const { | |||
return BSONElement(objdata() + 4); | return BSONElement(objdata() + 4); | |||
} | } | |||
/** @return element with fieldname "name". returnvalue.eoo( | /** use getField() instead. */ | |||
) is true if not found */ | //BSONElement getField(const char *name) const; | |||
BSONElement findElement(const char *name) const; | //BSONElement getField(string name) const { | |||
/** @return element with fieldname "name". returnvalue.eoo( | ||||
) is true if not found */ | ||||
BSONElement findElement(string name) const { | ||||
return findElement(name.c_str()); | ||||
} | ||||
/** @return true if field exists in the object */ | /** @return true if field exists in the object */ | |||
bool hasElement(const char *name) const; | bool hasElement(const char *name) const; | |||
/** Get the _id field from the object. For good performance drivers should | /** Get the _id field from the object. For good performance drivers should | |||
assure that _id is the first element of the object; however, co rrect operation | assure that _id is the first element of the object; however, co rrect operation | |||
is assured regardless. | is assured regardless. | |||
@return true if found | @return true if found | |||
*/ | */ | |||
bool getObjectID(BSONElement& e) const; | bool getObjectID(BSONElement& e) const; | |||
skipping to change at line 1188 | skipping to change at line 1205 | |||
return b; | return b; | |||
} | } | |||
/** Append a boolean element */ | /** Append a boolean element */ | |||
void appendBool(const char *fieldName, int val) { | void appendBool(const char *fieldName, int val) { | |||
b.append((char) Bool); | b.append((char) Bool); | |||
b.append(fieldName); | b.append(fieldName); | |||
b.append((char) (val?1:0)); | b.append((char) (val?1:0)); | |||
} | } | |||
/** Append a boolean element */ | ||||
void append(const char *fieldName, bool val) { | ||||
b.append((char) Bool); | ||||
b.append(fieldName); | ||||
b.append((char) (val?1:0)); | ||||
} | ||||
/** Append a 32 bit integer element */ | /** Append a 32 bit integer element */ | |||
void append(const char *fieldName, int n) { | void append(const char *fieldName, int n) { | |||
b.append((char) NumberInt); | b.append((char) NumberInt); | |||
b.append(fieldName); | b.append(fieldName); | |||
b.append(n); | b.append(n); | |||
} | } | |||
/** Append a 32 bit integer element */ | /** Append a 32 bit integer element */ | |||
void append(const string &fieldName, int n) { | void append(const string &fieldName, int n) { | |||
append( fieldName.c_str(), n ); | append( fieldName.c_str(), n ); | |||
} | } | |||
skipping to change at line 1214 | skipping to change at line 1238 | |||
b.append((char) NumberLong); | b.append((char) NumberLong); | |||
b.append(fieldName); | b.append(fieldName); | |||
b.append(n); | b.append(n); | |||
} | } | |||
/** Append a NumberLong */ | /** Append a NumberLong */ | |||
void append(const string& fieldName, long long n) { | void append(const string& fieldName, long long n) { | |||
append( fieldName.c_str() , n ); | append( fieldName.c_str() , n ); | |||
} | } | |||
/** appends a number. if n < max(int)/2 then uses int, otherwise l | ||||
ong long */ | ||||
void appendIntOrLL( const string& fieldName , long long n ){ | ||||
long long x = n; | ||||
if ( x < 0 ) | ||||
x = x * -1; | ||||
if ( x < ( numeric_limits<int>::max() / 2 ) ) | ||||
append( fieldName.c_str() , (int)n ); | ||||
else | ||||
append( fieldName.c_str() , n ); | ||||
} | ||||
/** 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; | |||
} | } | |||
/** tries to append the data as a number | /** tries to append the data as a number | |||
* @return true if the data was able to be converted to a number | * @return true if the data was able to be converted to a number | |||
skipping to change at line 1450 | skipping to change at line 1485 | |||
/** Fetch the object we have built. | /** Fetch the object we have built. | |||
BSONObjBuilder still frees the object when the build er goes out of | BSONObjBuilder still frees the object when the build er goes out of | |||
scope -- very important to keep in mind. Use obj() if you | scope -- very important to keep in mind. Use obj() if you | |||
would like the BSONObj to last longer than the build er. | would like the BSONObj to last longer than the build er. | |||
*/ | */ | |||
BSONObj done() { | BSONObj done() { | |||
return BSONObj(_done()); | return BSONObj(_done()); | |||
} | } | |||
/** Peek at what is in the builder, but leave the builder ready for | ||||
more appends. | ||||
The returned object is only valid until the next modification o | ||||
r destruction of the builder. | ||||
Intended use case: append a field if not already there. | ||||
*/ | ||||
BSONObj asTempObj() { | ||||
BSONObj temp(_done()); | ||||
b.setlen(b.len()-1); //next append should overwrite the EOO | ||||
return temp; | ||||
} | ||||
/* assume ownership of the buffer - you must then free it (with fre e()) */ | /* assume ownership of the buffer - you must then free it (with fre e()) */ | |||
char* decouple(int& l) { | char* decouple(int& l) { | |||
char *x = _done(); | char *x = _done(); | |||
assert( x ); | assert( x ); | |||
l = b.len(); | l = b.len(); | |||
b.decouple(); | b.decouple(); | |||
return x; | return x; | |||
} | } | |||
void decouple() { | void decouple() { | |||
b.decouple(); // post done() call version. be sure jsobj fr ees... | b.decouple(); // post done() call version. be sure jsobj fr ees... | |||
skipping to change at line 1581 | skipping to change at line 1626 | |||
/** @return true if more elements exist to be enumerated. */ | /** @return true if more elements exist to be enumerated. */ | |||
bool moreWithEOO() { | bool moreWithEOO() { | |||
return pos < theend; | return pos < theend; | |||
} | } | |||
bool more(){ | bool more(){ | |||
return pos < theend && pos[0]; | return pos < theend && pos[0]; | |||
} | } | |||
/** @return the next element in the object. For the final element, element.eoo() will be true. */ | /** @return the next element in the object. For the final element, element.eoo() will be true. */ | |||
BSONElement next( bool checkEnd = false ) { | BSONElement next( bool checkEnd = false ) { | |||
assert( pos < theend ); | assert( pos < theend ); | |||
BSONElement e( pos, checkEnd ? theend - pos : -1 ); | BSONElement e( pos, checkEnd ? (int)(theend - pos) : -1 ); | |||
pos += e.size( checkEnd ? theend - pos : -1 ); | pos += e.size( checkEnd ? (int)(theend - pos) : -1 ); | |||
return e; | return e; | |||
} | } | |||
private: | private: | |||
const char *pos; | const char *pos; | |||
const char *theend; | const char *theend; | |||
}; | }; | |||
/* iterator a BSONObj which is an array, in array order. | /* iterator a BSONObj which is an array, in array order. | |||
class JSArrayIter { | class JSArrayIter { | |||
public: | public: | |||
skipping to change at line 1651 | skipping to change at line 1696 | |||
#pragma pack() | #pragma pack() | |||
extern JSObj1 js1; | extern JSObj1 js1; | |||
#ifdef _DEBUG | #ifdef _DEBUG | |||
#define CHECK_OBJECT( o , msg ) massert( 10337 , (string)"object not valid " + (msg) , (o).isValid() ) | #define CHECK_OBJECT( o , msg ) massert( 10337 , (string)"object not valid " + (msg) , (o).isValid() ) | |||
#else | #else | |||
#define CHECK_OBJECT( o , msg ) | #define CHECK_OBJECT( o , msg ) | |||
#endif | #endif | |||
inline BSONObj BSONElement::embeddedObjectUserCheck() { | inline BSONObj BSONElement::embeddedObjectUserCheck() { | |||
uassert( 10065 , "invalid parameter: expected an object", type()== Object || type()==Array ); | uassert( 10065 , "invalid parameter: expected an object", isABSONO bj() ); | |||
return BSONObj(value()); | return BSONObj(value()); | |||
} | } | |||
inline BSONObj BSONElement::embeddedObject() const { | inline BSONObj BSONElement::embeddedObject() const { | |||
assert( type()==Object || type()==Array ); | assert( isABSONObj() ); | |||
return BSONObj(value()); | return BSONObj(value()); | |||
} | } | |||
inline BSONObj BSONElement::codeWScopeObject() const { | inline BSONObj BSONElement::codeWScopeObject() const { | |||
assert( type() == CodeWScope ); | assert( type() == CodeWScope ); | |||
int strSizeWNull = *(int *)( value() + 4 ); | int strSizeWNull = *(int *)( value() + 4 ); | |||
return BSONObj( value() + 4 + 4 + strSizeWNull ); | return BSONObj( value() + 4 + 4 + strSizeWNull ); | |||
} | } | |||
inline BSONObj BSONObj::copy() const { | inline BSONObj BSONObj::copy() const { | |||
skipping to change at line 1697 | skipping to change at line 1742 | |||
BSONObjIterator it(*this); | BSONObjIterator it(*this); | |||
while ( it.moreWithEOO() ) { | while ( it.moreWithEOO() ) { | |||
BSONElement e = it.next(); | BSONElement e = it.next(); | |||
if ( strcmp(name, e.fieldName()) == 0 ) | if ( strcmp(name, e.fieldName()) == 0 ) | |||
return true; | return true; | |||
} | } | |||
} | } | |||
return false; | return false; | |||
} | } | |||
inline BSONElement BSONObj::findElement(const char *name) const { | inline BSONElement BSONObj::getField(const char *name) const { | |||
if ( !isEmpty() ) { | BSONObjIterator i(*this); | |||
BSONObjIterator it(*this); | while ( i.more() ) { | |||
while ( it.moreWithEOO() ) { | BSONElement e = i.next(); | |||
BSONElement e = it.next(); | if ( strcmp(e.fieldName(), name) == 0 ) | |||
if ( strcmp(name, e.fieldName()) == 0 ) | return e; | |||
return e; | ||||
} | ||||
} | } | |||
return BSONElement(); | return BSONElement(); | |||
} | } | |||
/* add all the fields from the object specified to this object */ | /* add all the fields from the object specified to this object */ | |||
inline BSONObjBuilder& BSONObjBuilder::appendElements(BSONObj x) { | inline BSONObjBuilder& BSONObjBuilder::appendElements(BSONObj x) { | |||
BSONObjIterator it(x); | BSONObjIterator it(x); | |||
while ( it.moreWithEOO() ) { | while ( it.moreWithEOO() ) { | |||
BSONElement e = it.next(); | BSONElement e = it.next(); | |||
if ( e.eoo() ) break; | if ( e.eoo() ) break; | |||
append(e); | append(e); | |||
} | } | |||
return *this; | return *this; | |||
} | } | |||
inline bool BSONObj::isValid(){ | inline bool BSONObj::isValid(){ | |||
return objsize() > 0 && objsize() <= 1024 * 1024 * 8; | return objsize() > 0 && objsize() <= 1024 * 1024 * 8; | |||
} | } | |||
inline bool BSONObj::getObjectID(BSONElement& e) const { | inline bool BSONObj::getObjectID(BSONElement& e) const { | |||
BSONElement f = findElement("_id"); | BSONElement f = getField("_id"); | |||
if( !f.eoo() ) { | if( !f.eoo() ) { | |||
e = f; | e = f; | |||
return true; | return true; | |||
} | } | |||
return false; | return false; | |||
} | } | |||
inline BSONObjBuilderValueStream::BSONObjBuilderValueStream( BSONObjBui lder * builder ) { | inline BSONObjBuilderValueStream::BSONObjBuilderValueStream( BSONObjBui lder * builder ) { | |||
_fieldName = 0; | _fieldName = 0; | |||
_builder = builder; | _builder = builder; | |||
End of changes. 15 change blocks. | ||||
32 lines changed or deleted | 75 lines changed or added | |||
jsobjmanipulator.h | jsobjmanipulator.h | |||
---|---|---|---|---|
skipping to change at line 25 | skipping to change at line 25 | |||
* You should have received a copy of the GNU Affero General Public Lice nse | * You should have received a copy of the GNU Affero General Public Lice nse | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "jsobj.h" | #include "jsobj.h" | |||
namespace mongo { | namespace mongo { | |||
/** Manipulate the binary representation of a BSONElement in-place. | /** Manipulate the binary representation of a BSONElement in-place. | |||
Careful, this casts away const. | Careful, this casts away const. | |||
*/ | */ | |||
class BSONElementManipulator { | class BSONElementManipulator { | |||
public: | public: | |||
BSONElementManipulator( const BSONElement &element ) : | BSONElementManipulator( const BSONElement &element ) : | |||
element_( element ) { | _element( element ) { | |||
assert( !element_.eoo() ); | assert( !_element.eoo() ); | |||
} | } | |||
/** Replace a Timestamp type with a Date type initialized to | /** Replace a Timestamp type with a Date type initialized to | |||
OpTime::now().asDate() | OpTime::now().asDate() | |||
*/ | */ | |||
void initTimestamp(); | void initTimestamp(); | |||
/** Change the value, in place, of the number. */ | /** Change the value, in place, of the number. */ | |||
void setNumber(double d) { | void setNumber(double d) { | |||
if ( element_.type() == NumberDouble ) *reinterpret_cast< double * | if ( _element.type() == NumberDouble ) *reinterpret_cast< doubl | |||
>( value() ) = d; | e * >( value() ) = d; | |||
else if ( element_.type() == NumberInt ) *reinterpret_cast< int * > | else if ( _element.type() == NumberInt ) *reinterpret_cast< int | |||
( value() ) = (int) d; | * >( value() ) = (int) d; | |||
} | } | |||
void setLong(long long n) { | void setLong(long long n) { | |||
if( element_.type() == NumberLong ) *reinterpret_cast< long long * | if( _element.type() == NumberLong ) *reinterpret_cast< long lon | |||
>( value() ) = n; | g * >( value() ) = n; | |||
} | } | |||
void setInt(int n) { | ||||
assert( _element.type() == NumberInt ); | ||||
*reinterpret_cast< int * >( value() ) = n; | ||||
} | ||||
/** Replace the type and value of the element with the type and value o | /** Replace the type and value of the element with the type and val | |||
f e, | ue of e, | |||
preserving the original fieldName */ | preserving the original fieldName */ | |||
void replaceTypeAndValue( const BSONElement &e ) { | void replaceTypeAndValue( const BSONElement &e ) { | |||
*data() = e.type(); | *data() = e.type(); | |||
memcpy( value(), e.value(), e.valuesize() ); | memcpy( value(), e.value(), e.valuesize() ); | |||
} | } | |||
static void lookForTimestamps( const BSONObj& obj ){ | static void lookForTimestamps( const BSONObj& obj ){ | |||
// If have a Timestamp field as the first or second element, | // If have a Timestamp field as the first or second element, | |||
// update it to a Date field set to OpTime::now().asDate(). The | // update it to a Date field set to OpTime::now().asDate(). Th | |||
// replacement policy is a work in progress. | e | |||
// replacement policy is a work in progress. | ||||
BSONObjIterator i( obj ); | BSONObjIterator i( obj ); | |||
for( int j = 0; i.moreWithEOO() && j < 2; ++j ) { | for( int j = 0; i.moreWithEOO() && j < 2; ++j ) { | |||
BSONElement e = i.next(); | BSONElement e = i.next(); | |||
if ( e.eoo() ) | if ( e.eoo() ) | |||
break; | break; | |||
if ( e.type() == Timestamp ){ | if ( e.type() == Timestamp ){ | |||
BSONElementManipulator( e ).initTimestamp(); | BSONElementManipulator( e ).initTimestamp(); | |||
break; | break; | |||
} | ||||
} | } | |||
} | } | |||
} | private: | |||
private: | char *data() { return nonConst( _element.rawdata() ); } | |||
char *data() { return nonConst( element_.rawdata() ); } | char *value() { return nonConst( _element.value() ); } | |||
char *value() { return nonConst( element_.value() ); } | static char *nonConst( const char *s ) { return const_cast< char * | |||
static char *nonConst( const char *s ) { return const_cast< char * >( s | >( s ); } | |||
); } | ||||
const BSONElement element_; | const BSONElement _element; | |||
}; | }; | |||
} // namespace mongo | } // namespace mongo | |||
End of changes. 6 change blocks. | ||||
51 lines changed or deleted | 57 lines changed or added | |||
lasterror.h | lasterror.h | |||
---|---|---|---|---|
skipping to change at line 33 | skipping to change at line 33 | |||
namespace mongo { | namespace mongo { | |||
class BSONObjBuilder; | class BSONObjBuilder; | |||
class Message; | class Message; | |||
struct LastError { | struct LastError { | |||
int code; | int code; | |||
string msg; | string msg; | |||
enum UpdatedExistingType { NotUpdate, True, False } updatedExisting ; | enum UpdatedExistingType { NotUpdate, True, False } updatedExisting ; | |||
/* todo: nObjects should be 64 bit */ | /* todo: nObjects should be 64 bit */ | |||
int nObjects; | long long nObjects; | |||
int nPrev; | int nPrev; | |||
bool valid; | bool valid; | |||
bool overridenById; | bool overridenById; | |||
bool disabled; | bool disabled; | |||
void raiseError(int _code , const char *_msg) { | void raiseError(int _code , const char *_msg) { | |||
reset( true ); | reset( true ); | |||
code = _code; | code = _code; | |||
msg = _msg; | msg = _msg; | |||
} | } | |||
void recordUpdate( bool _updatedExisting, int nChanged ) { | void recordUpdate( bool _updatedExisting, long long nChanged ) { | |||
reset( true ); | reset( true ); | |||
nObjects = nChanged; | nObjects = nChanged; | |||
updatedExisting = _updatedExisting ? True : False; | updatedExisting = _updatedExisting ? True : False; | |||
} | } | |||
void recordDelete( int nDeleted ) { | void recordDelete( long long nDeleted ) { | |||
reset( true ); | reset( true ); | |||
nObjects = nDeleted; | nObjects = nDeleted; | |||
} | } | |||
LastError() { | LastError() { | |||
overridenById = false; | overridenById = false; | |||
reset(); | reset(); | |||
} | } | |||
void reset( bool _valid = false ) { | void reset( bool _valid = false ) { | |||
code = 0; | code = 0; | |||
msg.clear(); | msg.clear(); | |||
End of changes. 3 change blocks. | ||||
3 lines changed or deleted | 3 lines changed or added | |||
log.h | log.h | |||
---|---|---|---|---|
skipping to change at line 21 | skipping to change at line 21 | |||
* Unless required by applicable law or agreed to in writing, software | * Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | * distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. | |||
* See the License for the specific language governing permissions and | * See the License for the specific language governing permissions and | |||
* limitations under the License. | * limitations under the License. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include <string.h> | #include <string.h> | |||
#include <errno.h> | ||||
namespace mongo { | namespace mongo { | |||
using boost::shared_ptr; | using boost::shared_ptr; | |||
// Utility interface for stringifying object only when val() called. | // Utility interface for stringifying object only when val() called. | |||
class LazyString { | class LazyString { | |||
public: | public: | |||
virtual ~LazyString() {} | virtual ~LazyString() {} | |||
virtual string val() const = 0; | virtual string val() const = 0; | |||
skipping to change at line 247 | skipping to change at line 248 | |||
/** | /** | |||
log to a file rather than stdout | log to a file rather than stdout | |||
defined in assert_util.cpp | defined in assert_util.cpp | |||
*/ | */ | |||
void initLogging( const string& logpath , bool append ); | void initLogging( const string& logpath , bool append ); | |||
void rotateLogs( int signal = 0 ); | void rotateLogs( int signal = 0 ); | |||
#define OUTPUT_ERRNOX(x) "errno:" << x << " " << strerror(x) | #define OUTPUT_ERRNOX(x) "errno:" << x << " " << strerror(x) | |||
#define OUTPUT_ERRNO OUTPUT_ERRNOX(errno) | #define OUTPUT_ERRNO OUTPUT_ERRNOX(errno) | |||
string errnostring( const char * prefix = 0 ); | ||||
} // namespace mongo | } // namespace mongo | |||
End of changes. 2 change blocks. | ||||
0 lines changed or deleted | 3 lines changed or added | |||
matcher.h | matcher.h | |||
---|---|---|---|---|
skipping to change at line 73 | skipping to change at line 73 | |||
myset.reset( new set<BSONElement,element_lt>() ); | myset.reset( new set<BSONElement,element_lt>() ); | |||
BSONObjIterator i( array ); | BSONObjIterator i( array ); | |||
while ( i.more() ) { | while ( i.more() ) { | |||
BSONElement ie = i.next(); | BSONElement ie = i.next(); | |||
myset->insert(ie); | myset->insert(ie); | |||
} | } | |||
} | } | |||
~ElementMatcher(); | ~ElementMatcher() { } | |||
BSONElement toMatch; | BSONElement toMatch; | |||
int compareOp; | int compareOp; | |||
shared_ptr< set<BSONElement,element_lt> > myset; | shared_ptr< set<BSONElement,element_lt> > myset; | |||
// 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; | |||
}; | }; | |||
// SQL where clause equivalent | class Where; // used for $where javascript eval | |||
class Where; | ||||
class DiskLoc; | class DiskLoc; | |||
/* Match BSON objects against a query pattern. | /* Match BSON objects against a query pattern. | |||
e.g. | e.g. | |||
db.foo.find( { a : 3 } ); | db.foo.find( { a : 3 } ); | |||
{ a : 3 } is the pattern object. See wiki documentation for full in fo. | { a : 3 } is the pattern object. See wiki documentation for full in fo. | |||
GT/LT: | GT/LT: | |||
skipping to change at line 147 | skipping to change at line 146 | |||
return; | return; | |||
basics.push_back( ElementMatcher( e , c ) ); | basics.push_back( ElementMatcher( e , c ) ); | |||
} | } | |||
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; | |||
// int n; // # of basicmatcher items | ||||
bool haveSize; | bool haveSize; | |||
bool all; | bool all; | |||
bool hasArray; | bool hasArray; | |||
/* $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; | |||
End of changes. 3 change blocks. | ||||
4 lines changed or deleted | 2 lines changed or added | |||
message.h | message.h | |||
---|---|---|---|---|
skipping to change at line 21 | skipping to change at line 21 | |||
* Unless required by applicable law or agreed to in writing, software | * Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | * distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. | |||
* See the License for the specific language governing permissions and | * See the License for the specific language governing permissions and | |||
* limitations under the License. | * limitations under the License. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../util/sock.h" | #include "../util/sock.h" | |||
#include "../util/atomic_int.h" | ||||
namespace mongo { | namespace mongo { | |||
class Message; | class Message; | |||
class MessagingPort; | class MessagingPort; | |||
class PiggyBackData; | class PiggyBackData; | |||
typedef WrappingInt MSGID; | typedef AtomicUInt MSGID; | |||
class Listener { | class Listener { | |||
public: | public: | |||
Listener(const string &_ip, int p) : ip(_ip), port(p) { } | Listener(const string &_ip, int p) : ip(_ip), port(p) { } | |||
virtual ~Listener() {} | virtual ~Listener() {} | |||
bool init(); // set up socket | bool init(); // set up socket | |||
int socket() const { return sock; } | int socket() const { return sock; } | |||
void listen(); // never returns (start a thread) | void listen(); // never returns (start a thread) | |||
/* spawn a thread, etc., then return */ | /* spawn a thread, etc., then return */ | |||
skipping to change at line 102 | skipping to change at line 103 | |||
dbInsert = 2002, | dbInsert = 2002, | |||
//dbGetByOID = 2003, | //dbGetByOID = 2003, | |||
dbQuery = 2004, | dbQuery = 2004, | |||
dbGetMore = 2005, | dbGetMore = 2005, | |||
dbDelete = 2006, | dbDelete = 2006, | |||
dbKillCursors = 2007 | dbKillCursors = 2007 | |||
}; | }; | |||
bool doesOpGetAResponse( int op ); | bool doesOpGetAResponse( int op ); | |||
inline const char * opToString( int op ){ | ||||
switch ( op ){ | ||||
case 0: return "none"; | ||||
case opReply: return "reply"; | ||||
case dbMsg: return "msg"; | ||||
case dbUpdate: return "update"; | ||||
case dbInsert: return "insert"; | ||||
case dbQuery: return "query"; | ||||
case dbGetMore: return "getmore"; | ||||
case dbDelete: return "remove"; | ||||
case dbKillCursors: return "killcursors"; | ||||
default: | ||||
PRINT(op); | ||||
assert(0); | ||||
return ""; | ||||
} | ||||
} | ||||
struct MsgData { | struct MsgData { | |||
int len; /* len of the msg, including this field */ | int len; /* len of the msg, including this field */ | |||
MSGID id; /* request/reply id's match... */ | MSGID id; /* request/reply id's match... */ | |||
MSGID responseTo; /* id of the message we are responding to */ | MSGID responseTo; /* id of the message we are responding to */ | |||
int _operation; | int _operation; | |||
int operation() const { | int operation() const { | |||
return _operation; | return _operation; | |||
} | } | |||
void setOperation(int o) { | void setOperation(int o) { | |||
_operation = o; | _operation = o; | |||
skipping to change at line 178 | skipping to change at line 197 | |||
} | } | |||
void setData(MsgData *d, bool _freeIt) { | void setData(MsgData *d, bool _freeIt) { | |||
assert( data == 0 ); | assert( data == 0 ); | |||
freeIt = _freeIt; | freeIt = _freeIt; | |||
data = d; | data = d; | |||
} | } | |||
void setData(int operation, const char *msgtxt) { | void setData(int operation, const char *msgtxt) { | |||
setData(operation, msgtxt, strlen(msgtxt)+1); | setData(operation, msgtxt, strlen(msgtxt)+1); | |||
} | } | |||
void setData(int operation, const char *msgdata, int len) { | void setData(int operation, const char *msgdata, size_t len) { | |||
assert(data == 0); | assert(data == 0); | |||
int dataLen = len + sizeof(MsgData) - 4; | size_t dataLen = len + sizeof(MsgData) - 4; | |||
MsgData *d = (MsgData *) malloc(dataLen); | MsgData *d = (MsgData *) malloc(dataLen); | |||
memcpy(d->_data, msgdata, len); | memcpy(d->_data, msgdata, len); | |||
d->len = fixEndian(dataLen); | d->len = fixEndian(dataLen); | |||
d->setOperation(operation); | d->setOperation(operation); | |||
freeIt= true; | freeIt= true; | |||
data = d; | data = d; | |||
} | } | |||
bool doIFreeIt() { | bool doIFreeIt() { | |||
return freeIt; | return freeIt; | |||
End of changes. 5 change blocks. | ||||
3 lines changed or deleted | 22 lines changed or added | |||
mmap.h | mmap.h | |||
---|---|---|---|---|
skipping to change at line 25 | skipping to change at line 25 | |||
* limitations under the License. | * limitations under the License. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
namespace mongo { | namespace mongo { | |||
class MemoryMappedFile { | class MemoryMappedFile { | |||
public: | public: | |||
enum Options { | ||||
SEQUENTIAL = 1 | ||||
}; | ||||
MemoryMappedFile(); | MemoryMappedFile(); | |||
~MemoryMappedFile(); /* closes the file if open */ | ~MemoryMappedFile(); /* closes the file if open */ | |||
void close(); | void close(); | |||
// Throws exception if file doesn't exist. | // Throws exception if file doesn't exist. | |||
void* map( const char *filename ); | void* map( const char *filename ); | |||
/* Creates with length if DNE, otherwise uses existing file length, | /* Creates with length if DNE, otherwise uses existing file length, | |||
passed length. | passed length. | |||
*/ | */ | |||
void* map(const char *filename, long &length); | void* map(const char *filename, long &length, int options = 0 ); | |||
void flush(bool sync); | void flush(bool sync); | |||
void* viewOfs() { | void* viewOfs() { | |||
return view; | return view; | |||
} | } | |||
long length() { | long length() { | |||
return len; | return len; | |||
} | } | |||
End of changes. 2 change blocks. | ||||
1 lines changed or deleted | 5 lines changed or added | |||
namespace.h | namespace.h | |||
---|---|---|---|---|
skipping to change at line 24 | skipping to change at line 24 | |||
* | * | |||
* You should have received a copy of the GNU Affero General Public Licen se | * You should have received a copy of the GNU Affero General Public Licen se | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../stdafx.h" | #include "../stdafx.h" | |||
#include "jsobj.h" | #include "jsobj.h" | |||
#include "queryutil.h" | #include "queryutil.h" | |||
#include "storage.h" | #include "diskloc.h" | |||
#include "../util/hashtab.h" | #include "../util/hashtab.h" | |||
#include "../util/mmap.h" | #include "../util/mmap.h" | |||
namespace mongo { | namespace mongo { | |||
class Cursor; | class Cursor; | |||
#pragma pack(1) | #pragma pack(1) | |||
/* in the mongo source code, "client" means "database". */ | /* in the mongo source code, "client" means "database". */ | |||
skipping to change at line 78 | skipping to change at line 78 | |||
void init(const char *ns) { | void init(const char *ns) { | |||
const char *p = strchr(ns, '.'); | const char *p = strchr(ns, '.'); | |||
if( p == 0 ) return; | if( p == 0 ) return; | |||
db = string(ns, p - ns); | db = string(ns, p - ns); | |||
coll = p + 1; | coll = p + 1; | |||
} | } | |||
public: | public: | |||
NamespaceString( const char * ns ) { init(ns); } | NamespaceString( const char * ns ) { init(ns); } | |||
NamespaceString( const string& ns ) { init(ns.c_str()); } | NamespaceString( const string& ns ) { init(ns.c_str()); } | |||
string ns() const { | ||||
return db + '.' + coll; | ||||
} | ||||
bool isSystem() { | bool isSystem() { | |||
return strncmp(coll.c_str(), "system.", 7) == 0; | return strncmp(coll.c_str(), "system.", 7) == 0; | |||
} | } | |||
}; | }; | |||
/* This helper class is used to make the HashMap below in NamespaceD etails */ | /* This helper class is used to make the HashMap below in NamespaceD etails */ | |||
class Namespace { | class Namespace { | |||
public: | public: | |||
enum MaxNsLenValue { MaxNsLen = 128 }; | enum MaxNsLenValue { MaxNsLen = 128 }; | |||
Namespace(const char *ns) { | Namespace(const char *ns) { | |||
skipping to change at line 103 | skipping to change at line 107 | |||
strcpy_s(buf, MaxNsLen, ns); | strcpy_s(buf, MaxNsLen, ns); | |||
return *this; | return *this; | |||
} | } | |||
/* for more than 10 indexes -- see NamespaceDetails::Extra */ | /* for more than 10 indexes -- see NamespaceDetails::Extra */ | |||
string extraName() { | string extraName() { | |||
string s = string(buf) + "$extra"; | string s = string(buf) + "$extra"; | |||
massert( 10348 , "ns name too long", s.size() < MaxNsLen); | massert( 10348 , "ns name too long", s.size() < MaxNsLen); | |||
return s; | return s; | |||
} | } | |||
bool isExtra() const { | ||||
const char *p = strstr(buf, "$extra"); | ||||
return p && p[6] == 0; //==0 important in case an index uses na | ||||
me "$extra_1" for example | ||||
} | ||||
void kill() { | void kill() { | |||
buf[0] = 0x7f; | buf[0] = 0x7f; | |||
} | } | |||
bool operator==(const char *r) { | bool operator==(const char *r) { | |||
return strcmp(buf, r) == 0; | return strcmp(buf, r) == 0; | |||
} | } | |||
bool operator==(const Namespace& r) { | bool operator==(const Namespace& r) { | |||
return strcmp(buf, r.buf) == 0; | return strcmp(buf, r.buf) == 0; | |||
skipping to change at line 188 | skipping to change at line 196 | |||
assert( extraOffset ); | assert( extraOffset ); | |||
return (Extra *) (((char *) this) + extraOffset); | return (Extra *) (((char *) this) + extraOffset); | |||
} | } | |||
public: | public: | |||
void copyingFrom(const char *thisns, NamespaceDetails *src); // mus t be called when renaming a NS to fix up extra | void copyingFrom(const char *thisns, NamespaceDetails *src); // mus t be called when renaming a NS to fix up extra | |||
enum { NIndexesMax = 40 }; | enum { NIndexesMax = 40 }; | |||
BOOST_STATIC_ASSERT( NIndexesMax == NIndexesBase + NIndexesExtra ); | BOOST_STATIC_ASSERT( NIndexesMax == NIndexesBase + NIndexesExtra ); | |||
/* called when loaded from disk */ | ||||
void onLoad(const Namespace& k); | ||||
NamespaceDetails( const DiskLoc &loc, bool _capped ) { | NamespaceDetails( const DiskLoc &loc, bool _capped ) { | |||
/* be sure to initialize new fields here -- doesn't default to zeroes the way we use it */ | /* be sure to initialize new fields here -- doesn't default to zeroes the way we use it */ | |||
firstExtent = lastExtent = capExtent = loc; | firstExtent = lastExtent = capExtent = loc; | |||
datasize = nrecords = 0; | datasize = nrecords = 0; | |||
lastExtentSize = 0; | lastExtentSize = 0; | |||
nIndexes = 0; | nIndexes = 0; | |||
capped = _capped; | capped = _capped; | |||
max = 0x7fffffff; | max = 0x7fffffff; | |||
paddingFactor = 1.0; | paddingFactor = 1.0; | |||
flags = 0; | flags = 0; | |||
skipping to change at line 253 | skipping to change at line 264 | |||
unsigned short indexFileVersion; | unsigned short indexFileVersion; | |||
unsigned long long multiKeyIndexBits; | unsigned long long multiKeyIndexBits; | |||
private: | private: | |||
unsigned long long reservedA; | unsigned long long reservedA; | |||
long long extraOffset; // where the $extra info is located (bytes r elative to this) | long long extraOffset; // where the $extra info is located (bytes r elative to this) | |||
public: | public: | |||
int backgroundIndexBuildInProgress; // 1 if in prog | int backgroundIndexBuildInProgress; // 1 if in prog | |||
char reserved[76]; | char reserved[76]; | |||
/* when a background index build is in progress, we don't count the | ||||
index in nIndexes until | ||||
complete, yet need to still use it in _indexRecord() - thus we u | ||||
se this function for that. | ||||
*/ | ||||
int nIndexesBeingBuilt() const { | ||||
return nIndexes + backgroundIndexBuildInProgress; | ||||
} | ||||
/* NOTE: be careful with flags. are we manipulating them in read l ocks? if so, | /* NOTE: be careful with flags. are we manipulating them in read l ocks? if so, | |||
this isn't thread safe. TODO | this isn't thread safe. TODO | |||
*/ | */ | |||
enum NamespaceFlags { | enum NamespaceFlags { | |||
Flag_HaveIdIndex = 1 << 0, // set when we have _id index (ONLY if ensureIdIndex was called -- 0 if that has never been called) | Flag_HaveIdIndex = 1 << 0, // set when we have _id index (ONLY if ensureIdIndex was called -- 0 if that has never been called) | |||
Flag_CappedDisallowDelete = 1 << 1 // set when deletes not allo wed during capped table allocation. | Flag_CappedDisallowDelete = 1 << 1 // set when deletes not allo wed during capped table allocation. | |||
}; | }; | |||
IndexDetails& idx(int idxNo) { | IndexDetails& idx(int idxNo) { | |||
if( idxNo < NIndexesBase ) | if( idxNo < NIndexesBase ) | |||
return _indexes[idxNo]; | return _indexes[idxNo]; | |||
return extra()->details[idxNo-NIndexesBase]; | return extra()->details[idxNo-NIndexesBase]; | |||
} | } | |||
IndexDetails& backgroundIdx() { | ||||
DEV assert(backgroundIndexBuildInProgress); | ||||
return idx(nIndexes); | ||||
} | ||||
class IndexIterator { | class IndexIterator { | |||
friend class NamespaceDetails; | friend class NamespaceDetails; | |||
int i; | int i; | |||
int n; | int n; | |||
NamespaceDetails *d; | NamespaceDetails *d; | |||
Extra *e; | Extra *e; | |||
IndexIterator(NamespaceDetails *_d) { | IndexIterator(NamespaceDetails *_d) { | |||
d = _d; | d = _d; | |||
i = 0; | i = 0; | |||
skipping to change at line 412 | skipping to change at line 434 | |||
// Start from firstExtent by default. | // Start from firstExtent by default. | |||
DiskLoc firstRecord( const DiskLoc &startExtent = DiskLoc() ) const ; | DiskLoc firstRecord( const DiskLoc &startExtent = DiskLoc() ) const ; | |||
// Start from lastExtent by default. | // Start from lastExtent by default. | |||
DiskLoc lastRecord( const DiskLoc &startExtent = DiskLoc() ) const; | DiskLoc lastRecord( const DiskLoc &startExtent = DiskLoc() ) const; | |||
bool inCapExtent( const DiskLoc &dl ) const; | bool inCapExtent( const DiskLoc &dl ) const; | |||
void checkMigrate(); | void checkMigrate(); | |||
long long storageSize(); | long long storageSize( int * numExtents = 0 ); | |||
private: | private: | |||
bool cappedMayDelete() const { | bool cappedMayDelete() const { | |||
return !( flags & Flag_CappedDisallowDelete ); | return !( flags & Flag_CappedDisallowDelete ); | |||
} | } | |||
Extent *theCapExtent() const { | Extent *theCapExtent() const { | |||
return capExtent.ext(); | return capExtent.ext(); | |||
} | } | |||
void advanceCapExtent( const char *ns ); | void advanceCapExtent( const char *ns ); | |||
void maybeComplain( const char *ns, int len ) const; | void maybeComplain( const char *ns, int len ) const; | |||
skipping to change at line 490 | skipping to change at line 512 | |||
return _indexKeys; | return _indexKeys; | |||
} | } | |||
/* IndexSpec caching */ | /* IndexSpec caching */ | |||
private: | private: | |||
map<const IndexDetails*,IndexSpec> _indexSpecs; | map<const IndexDetails*,IndexSpec> _indexSpecs; | |||
public: | public: | |||
const IndexSpec& getIndexSpec( const IndexDetails * details ){ | const IndexSpec& getIndexSpec( const IndexDetails * details ){ | |||
DEV assertInWriteLock(); | DEV assertInWriteLock(); | |||
IndexSpec& spec = _indexSpecs[details]; | IndexSpec& spec = _indexSpecs[details]; | |||
if ( spec.meta.isEmpty() ){ | if ( spec.info.isEmpty() ){ | |||
spec.reset( details->info ); | spec.reset( details->info ); | |||
} | } | |||
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: | |||
skipping to change at line 557 | skipping to change at line 579 | |||
} | } | |||
/* NamespaceIndex is the ".ns" file you see in the data directory. It is the "system catalog" | /* NamespaceIndex is the ".ns" file you see in the data directory. It is the "system catalog" | |||
if you will: at least the core parts. (Additional info in system.* collections.) | if you will: at least the core parts. (Additional info in system.* collections.) | |||
*/ | */ | |||
class NamespaceIndex { | class NamespaceIndex { | |||
friend class NamespaceCursor; | friend class NamespaceCursor; | |||
BOOST_STATIC_ASSERT( sizeof(NamespaceDetails::Extra) <= sizeof(Name spaceDetails) ); | BOOST_STATIC_ASSERT( sizeof(NamespaceDetails::Extra) <= sizeof(Name spaceDetails) ); | |||
public: | public: | |||
NamespaceIndex(const string &dir, const string &database) : | NamespaceIndex(const string &dir, const string &database) : | |||
ht( 0 ), | ht( 0 ), | |||
dir_( dir ), | dir_( dir ), | |||
database_( database ) {} | database_( database ) {} | |||
/* returns true if new db will be created if we init lazily */ | /* returns true if new db will be created if we init lazily */ | |||
bool exists() const; | bool exists() const; | |||
void init(); | void init(); | |||
void add_ns(const char *ns, DiskLoc& loc, bool capped) { | void add_ns(const char *ns, DiskLoc& loc, bool capped) { | |||
NamespaceDetails details( loc, capped ); | NamespaceDetails details( loc, capped ); | |||
add_ns( ns, details ); | add_ns( ns, details ); | |||
} | } | |||
skipping to change at line 639 | skipping to change at line 661 | |||
} | } | |||
return false; | return false; | |||
} | } | |||
bool allocated() const { | bool allocated() const { | |||
return ht != 0; | return ht != 0; | |||
} | } | |||
private: | private: | |||
boost::filesystem::path path() const; | boost::filesystem::path path() const; | |||
void maybeMkdir() const; | ||||
MemoryMappedFile f; | MemoryMappedFile f; | |||
HashTable<Namespace,NamespaceDetails> *ht; | HashTable<Namespace,NamespaceDetails> *ht; | |||
string dir_; | string dir_; | |||
string database_; | string database_; | |||
}; | }; | |||
extern string dbpath; // --dbpath parm | extern string dbpath; // --dbpath parm | |||
extern bool directoryperdb; | ||||
// Rename a namespace within current 'client' db. | // Rename a namespace within current 'client' db. | |||
// (Arguments should include db name) | // (Arguments should include db name) | |||
void renameNamespace( const char *from, const char *to ); | void renameNamespace( const char *from, const char *to ); | |||
} // namespace mongo | } // namespace mongo | |||
End of changes. 11 change blocks. | ||||
6 lines changed or deleted | 33 lines changed or added | |||
parallel.h | parallel.h | |||
---|---|---|---|---|
skipping to change at line 99 | skipping to change at line 99 | |||
BSONObj _extra; | BSONObj _extra; | |||
BSONObj _orderObject; | BSONObj _orderObject; | |||
}; | }; | |||
/** | /** | |||
* runs a query in serial across any number of servers | * runs a query in serial across any number of servers | |||
* returns all results from 1 server, then the next, etc... | * returns all results from 1 server, then the next, etc... | |||
*/ | */ | |||
class SerialServerClusteredCursor : public ClusteredCursor { | class SerialServerClusteredCursor : public ClusteredCursor { | |||
public: | public: | |||
SerialServerClusteredCursor( set<ServerAndQuery> servers , QueryMes sage& q , int sortOrder=0); | SerialServerClusteredCursor( const set<ServerAndQuery>& servers , Q ueryMessage& q , int sortOrder=0); | |||
virtual bool more(); | virtual bool more(); | |||
virtual BSONObj next(); | virtual BSONObj next(); | |||
virtual string type() const { return "SerialServer"; } | virtual string type() const { return "SerialServer"; } | |||
private: | private: | |||
vector<ServerAndQuery> _servers; | vector<ServerAndQuery> _servers; | |||
unsigned _serverIndex; | unsigned _serverIndex; | |||
auto_ptr<DBClientCursor> _current; | auto_ptr<DBClientCursor> _current; | |||
}; | }; | |||
/** | /** | |||
* runs a query in parellel across N servers | * runs a query in parellel across N servers | |||
* sots | * sots | |||
*/ | */ | |||
class ParallelSortClusteredCursor : public ClusteredCursor { | class ParallelSortClusteredCursor : public ClusteredCursor { | |||
public: | public: | |||
ParallelSortClusteredCursor( set<ServerAndQuery> servers , QueryMes | ParallelSortClusteredCursor( const set<ServerAndQuery>& servers , Q | |||
sage& q , const BSONObj& sortKey ); | ueryMessage& q , const BSONObj& sortKey ); | |||
ParallelSortClusteredCursor( set<ServerAndQuery> servers , const st | ParallelSortClusteredCursor( const set<ServerAndQuery>& servers , c | |||
ring& ns , | onst string& ns , | |||
const Query& q , int options=0, const BSONObj& fields=BSONObj() ); | const Query& q , int options=0, const BSONObj& fields=BSONObj() ); | |||
virtual ~ParallelSortClusteredCursor(); | virtual ~ParallelSortClusteredCursor(); | |||
virtual bool more(); | virtual bool more(); | |||
virtual BSONObj next(); | virtual BSONObj next(); | |||
virtual string type() const { return "ParallelSort"; } | virtual string type() const { return "ParallelSort"; } | |||
private: | private: | |||
void _init(); | void _init(); | |||
void advance(); | void advance(); | |||
End of changes. 2 change blocks. | ||||
5 lines changed or deleted | 5 lines changed or added | |||
pdfile.h | pdfile.h | |||
---|---|---|---|---|
skipping to change at line 30 | skipping to change at line 30 | |||
database.ns - namespace index | database.ns - namespace index | |||
database.1 - data files | database.1 - data files | |||
database.2 | database.2 | |||
... | ... | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../stdafx.h" | #include "../stdafx.h" | |||
#include "../util/mmap.h" | #include "../util/mmap.h" | |||
#include "storage.h" | #include "diskloc.h" | |||
#include "jsobjmanipulator.h" | #include "jsobjmanipulator.h" | |||
#include "namespace.h" | #include "namespace.h" | |||
#include "client.h" | #include "client.h" | |||
namespace mongo { | namespace mongo { | |||
class MDFHeader; | class MDFHeader; | |||
class Extent; | class Extent; | |||
class Record; | class Record; | |||
class Cursor; | class Cursor; | |||
skipping to change at line 101 | skipping to change at line 101 | |||
class DataFileMgr { | class DataFileMgr { | |||
friend class BasicCursor; | friend class BasicCursor; | |||
public: | public: | |||
void init(const string& path ); | void init(const string& path ); | |||
/* see if we can find an extent of the right size in the freelist. */ | /* see if we can find an extent of the right size in the freelist. */ | |||
static Extent* allocFromFreeList(const char *ns, int approxSize, bo ol capped = false); | static Extent* allocFromFreeList(const char *ns, int approxSize, bo ol capped = false); | |||
/** @return DiskLoc where item ends up */ | /** @return DiskLoc where item ends up */ | |||
const DiskLoc update( | const DiskLoc updateRecord( | |||
const char *ns, | const char *ns, | |||
NamespaceDetails *d, | ||||
NamespaceDetailsTransient *nsdt, | ||||
Record *toupdate, const DiskLoc& dl, | Record *toupdate, const DiskLoc& dl, | |||
const char *buf, int len, OpDebug& debug); | const char *buf, int len, OpDebug& debug); | |||
// The object o may be updated if modified on insert. | // The object o may be updated if modified on insert. | |||
void insertAndLog( const char *ns, const BSONObj &o, bool god = fal se ); | void insertAndLog( const char *ns, const BSONObj &o, bool god = fal se ); | |||
DiskLoc insert(const char *ns, BSONObj &o, bool god = false); | DiskLoc insert(const char *ns, BSONObj &o, bool god = false); | |||
DiskLoc insert(const char *ns, const void *buf, int len, bool god = false, const BSONElement &writeId = BSONElement(), bool mayAddIndex = true ); | DiskLoc insert(const char *ns, const void *buf, int len, bool god = false, const BSONElement &writeId = BSONElement(), bool mayAddIndex = true ); | |||
void deleteRecord(const char *ns, Record *todelete, const DiskLoc& dl, bool cappedOK = false, bool noWarn = false); | void deleteRecord(const char *ns, Record *todelete, const DiskLoc& dl, bool cappedOK = false, bool noWarn = false); | |||
static auto_ptr<Cursor> findAll(const char *ns, const DiskLoc &star tLoc = DiskLoc()); | static auto_ptr<Cursor> findAll(const char *ns, const DiskLoc &star tLoc = DiskLoc()); | |||
/* special version of insert for transaction logging -- streamlined a bit. | /* special version of insert for transaction logging -- streamlined a bit. | |||
skipping to change at line 395 | skipping to change at line 397 | |||
public: | public: | |||
virtual ~FileOp() {} | virtual ~FileOp() {} | |||
// Return true if file exists and operation successful | // Return true if file exists and operation successful | |||
virtual bool apply( const boost::filesystem::path &p ) = 0; | virtual bool apply( const boost::filesystem::path &p ) = 0; | |||
virtual const char * op() const = 0; | virtual const char * op() const = 0; | |||
}; | }; | |||
void _applyOpToDataFiles( const char *database, FileOp &fo, bool afterA llocator = false, const string& path = dbpath ); | void _applyOpToDataFiles( const char *database, FileOp &fo, bool afterA llocator = false, const string& path = dbpath ); | |||
inline void _deleteDataFiles(const char *database) { | inline void _deleteDataFiles(const char *database) { | |||
if ( directoryperdb ) { | ||||
BOOST_CHECK_EXCEPTION( boost::filesystem::remove_all( boost::fi | ||||
lesystem::path( dbpath ) / database ) ); | ||||
return; | ||||
} | ||||
class : public FileOp { | class : public FileOp { | |||
virtual bool apply( const boost::filesystem::path &p ) { | virtual bool apply( const boost::filesystem::path &p ) { | |||
return boost::filesystem::remove( p ); | return boost::filesystem::remove( p ); | |||
} | } | |||
virtual const char * op() const { | virtual const char * op() const { | |||
return "remove"; | return "remove"; | |||
} | } | |||
} deleter; | } deleter; | |||
_applyOpToDataFiles( database, deleter, true ); | _applyOpToDataFiles( database, deleter, true ); | |||
} | } | |||
skipping to change at line 446 | skipping to change at line 452 | |||
return cc().database()->getFile(dl.a())->getExtent(dl); | return cc().database()->getFile(dl.a())->getExtent(dl); | |||
} | } | |||
inline Record* DataFileMgr::getRecord(const DiskLoc& dl) { | inline Record* DataFileMgr::getRecord(const DiskLoc& dl) { | |||
assert( dl.a() != -1 ); | assert( dl.a() != -1 ); | |||
return cc().database()->getFile(dl.a())->recordAt(dl); | return cc().database()->getFile(dl.a())->recordAt(dl); | |||
} | } | |||
void ensureHaveIdIndex(const char *ns); | void ensureHaveIdIndex(const char *ns); | |||
bool deleteIndexes( NamespaceDetails *d, const char *ns, const char *na me, string &errmsg, BSONObjBuilder &anObjBuilder, bool maydeleteIdIndex ); | bool dropIndexes( NamespaceDetails *d, const char *ns, const char *name , string &errmsg, BSONObjBuilder &anObjBuilder, bool maydeleteIdIndex ); | |||
} // namespace mongo | } // namespace mongo | |||
End of changes. 5 change blocks. | ||||
3 lines changed or deleted | 10 lines changed or added | |||
processinfo.h | processinfo.h | |||
---|---|---|---|---|
skipping to change at line 55 | skipping to change at line 55 | |||
*/ | */ | |||
int getResidentSize(); | int getResidentSize(); | |||
/** | /** | |||
* Append platform-specific data to obj | * Append platform-specific data to obj | |||
*/ | */ | |||
void getExtraInfo(BSONObjBuilder& info); | void getExtraInfo(BSONObjBuilder& info); | |||
bool supported(); | bool supported(); | |||
bool blockCheckSupported(); | ||||
bool blockInMemory( char * start ); | ||||
private: | private: | |||
pid_t _pid; | pid_t _pid; | |||
}; | }; | |||
} | } | |||
End of changes. 1 change blocks. | ||||
0 lines changed or deleted | 3 lines changed or added | |||
query.h | query.h | |||
---|---|---|---|---|
skipping to change at line 25 | skipping to change at line 25 | |||
* You should have received a copy of the GNU Affero General Public Licen se | * You should have received a copy of the GNU Affero General Public Licen se | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include "../stdafx.h" | #include "../stdafx.h" | |||
#include "../util/message.h" | #include "../util/message.h" | |||
#include "dbmessage.h" | #include "dbmessage.h" | |||
#include "jsobj.h" | #include "jsobj.h" | |||
#include "storage.h" | #include "diskloc.h" | |||
/* db request message format | /* db request message format | |||
unsigned opid; // arbitary; will be echoed back | unsigned opid; // arbitary; will be echoed back | |||
byte operation; | byte operation; | |||
int options; | int options; | |||
then for: | then for: | |||
dbInsert: | dbInsert: | |||
skipping to change at line 80 | skipping to change at line 80 | |||
#include "../client/dbclient.h" | #include "../client/dbclient.h" | |||
namespace mongo { | namespace mongo { | |||
// for an existing query (ie a ClientCursor), send back additional info rmation. | // for an existing query (ie a ClientCursor), send back additional info rmation. | |||
QueryResult* getMore(const char *ns, int ntoreturn, long long cursorid , CurOp& op); | QueryResult* getMore(const char *ns, int ntoreturn, long long cursorid , CurOp& op); | |||
struct UpdateResult { | struct UpdateResult { | |||
bool existing; | bool existing; | |||
bool mod; | bool mod; | |||
unsigned long long num; | long long num; | |||
UpdateResult( bool e, bool m, unsigned long long n ) | UpdateResult( bool e, bool m, unsigned long long n ) | |||
: existing(e) , mod(m), num(n ){} | : existing(e) , mod(m), num(n ){} | |||
int oldCode(){ | int oldCode(){ | |||
if ( ! num ) | if ( ! num ) | |||
return 0; | return 0; | |||
if ( existing ){ | if ( existing ){ | |||
if ( mod ) | if ( mod ) | |||
skipping to change at line 104 | skipping to change at line 104 | |||
if ( mod ) | if ( mod ) | |||
return 3; | return 3; | |||
return 4; | return 4; | |||
} | } | |||
}; | }; | |||
/* returns true if an existing object was updated, false if no existing object was found. | /* returns true if an existing object was updated, false if no existing object was found. | |||
multi - update multiple objects - mostly useful with things like $se t | multi - update multiple objects - mostly useful with things like $se t | |||
*/ | */ | |||
UpdateResult updateObjects(const char *ns, BSONObj updateobj, BSONObj p attern, bool upsert, bool multi , bool logop , OpDebug& debug ); | UpdateResult updateObjects(const char *ns, const BSONObj& updateobj, BS ONObj pattern, bool upsert, bool multi , bool logop , OpDebug& debug ); | |||
// If justOne is true, deletedId is set to the id of the deleted object . | // If justOne is true, deletedId is set to the id of the deleted object . | |||
int deleteObjects(const char *ns, BSONObj pattern, bool justOne, bool l ogop = false, bool god=false); | long long deleteObjects(const char *ns, BSONObj pattern, bool justOne, bool logop = false, bool god=false); | |||
long long runCount(const char *ns, const BSONObj& cmd, string& err); | long long runCount(const char *ns, const BSONObj& cmd, string& err); | |||
auto_ptr< QueryResult > runQuery(Message& m, QueryMessage& q, CurOp& cu rop ); | auto_ptr< QueryResult > runQuery(Message& m, QueryMessage& q, CurOp& cu rop ); | |||
} // namespace mongo | } // namespace mongo | |||
#include "clientcursor.h" | #include "clientcursor.h" | |||
End of changes. 4 change blocks. | ||||
4 lines changed or deleted | 4 lines changed or added | |||
queryoptimizer.h | queryoptimizer.h | |||
---|---|---|---|---|
skipping to change at line 53 | skipping to change at line 53 | |||
*/ | */ | |||
bool exactKeyMatch() const { return exactKeyMatch_; } | bool exactKeyMatch() const { return exactKeyMatch_; } | |||
/* If true, the startKey and endKey are unhelpful and the index ord er doesn't match the | /* If true, the startKey and endKey are unhelpful and the index ord er doesn't match the | |||
requested sort order */ | requested sort order */ | |||
bool unhelpful() const { return unhelpful_; } | bool unhelpful() const { return unhelpful_; } | |||
int direction() const { return direction_; } | int direction() const { return direction_; } | |||
auto_ptr< Cursor > newCursor( const DiskLoc &startLoc = DiskLoc() ) const; | auto_ptr< Cursor > newCursor( const DiskLoc &startLoc = DiskLoc() ) const; | |||
auto_ptr< Cursor > newReverseCursor() const; | auto_ptr< Cursor > newReverseCursor() const; | |||
BSONObj indexKey() const; | BSONObj indexKey() const; | |||
const char *ns() const { return fbs_.ns(); } | const char *ns() const { return fbs_.ns(); } | |||
NamespaceDetails *nsd() const { return d; } | ||||
BSONObj query() const { return fbs_.query(); } | BSONObj query() const { return fbs_.query(); } | |||
BSONObj simplifiedQuery( const BSONObj& fields = BSONObj() ) const { return fbs_.simplifiedQuery( fields ); } | BSONObj simplifiedQuery( const BSONObj& fields = BSONObj() ) const { return fbs_.simplifiedQuery( fields ); } | |||
const FieldRange &range( const char *fieldName ) const { return fbs _.range( fieldName ); } | const FieldRange &range( const char *fieldName ) const { return fbs _.range( fieldName ); } | |||
void registerSelf( long long nScanned ) const; | void registerSelf( long long nScanned ) const; | |||
// just for testing | // just for testing | |||
BoundList indexBounds() const { return indexBounds_; } | BoundList indexBounds() const { return indexBounds_; } | |||
private: | private: | |||
NamespaceDetails *d; | NamespaceDetails *d; | |||
int idxNo; | int idxNo; | |||
const FieldRangeSet &fbs_; | const FieldRangeSet &fbs_; | |||
skipping to change at line 146 | skipping to change at line 147 | |||
void addHint( IndexDetails &id ); | void addHint( IndexDetails &id ); | |||
struct Runner { | struct Runner { | |||
Runner( QueryPlanSet &plans, QueryOp &op ); | Runner( QueryPlanSet &plans, QueryOp &op ); | |||
shared_ptr< QueryOp > run(); | shared_ptr< QueryOp > run(); | |||
QueryOp &op_; | QueryOp &op_; | |||
QueryPlanSet &plans_; | QueryPlanSet &plans_; | |||
static void initOp( QueryOp &op ); | static void initOp( QueryOp &op ); | |||
static void nextOp( QueryOp &op ); | static void nextOp( QueryOp &op ); | |||
}; | }; | |||
const char *ns; | const char *ns; | |||
BSONObj query_; | ||||
FieldRangeSet fbs_; | FieldRangeSet fbs_; | |||
PlanSet plans_; | PlanSet plans_; | |||
bool mayRecordPlan_; | bool mayRecordPlan_; | |||
bool usingPrerecordedPlan_; | bool usingPrerecordedPlan_; | |||
BSONObj hint_; | BSONObj hint_; | |||
BSONObj order_; | BSONObj order_; | |||
long long oldNScanned_; | long long oldNScanned_; | |||
bool honorRecordedPlan_; | bool honorRecordedPlan_; | |||
BSONObj min_; | BSONObj min_; | |||
BSONObj max_; | BSONObj max_; | |||
}; | }; | |||
// NOTE min, max, and keyPattern will be updated to be consistent with the selected index. | // NOTE min, max, and keyPattern will be updated to be consistent with the selected index. | |||
IndexDetails *indexDetailsForRange( const char *ns, string &errmsg, BSO NObj &min, BSONObj &max, BSONObj &keyPattern ); | IndexDetails *indexDetailsForRange( const char *ns, string &errmsg, BSO NObj &min, BSONObj &max, BSONObj &keyPattern ); | |||
inline bool isSimpleIdQuery( const BSONObj& query ){ | ||||
return | ||||
strcmp( query.firstElement().fieldName() , "_id" ) == 0 && | ||||
query.nFields() == 1 && | ||||
query.firstElement().isSimpleType(); | ||||
} | ||||
} // namespace mongo | } // namespace mongo | |||
End of changes. 3 change blocks. | ||||
0 lines changed or deleted | 9 lines changed or added | |||
rec.h | rec.h | |||
---|---|---|---|---|
// rec.h | // rec.h | |||
/* | ||||
* Copyright (C) 2010 10gen Inc. | ||||
* | ||||
* This program is free software: you can redistribute it and/or modify | ||||
* it under the terms of the GNU Affero General Public License, version | ||||
3, | ||||
* as published by the Free Software Foundation. | ||||
* | ||||
* This program is distributed in the hope that it will be useful, | ||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
* GNU Affero General Public License for more details. | ||||
* | ||||
* You should have received a copy of the GNU Affero General Public Lice | ||||
nse | ||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
*/ | ||||
/* TODO for _RECSTORE | /* TODO for _RECSTORE | |||
_ support > 2GB data per file | _ support > 2GB data per file | |||
_ multiple files, not just indexes.dat | _ multiple files, not just indexes.dat | |||
_ lazier writes? (may be done?) | _ lazier writes? (may be done?) | |||
_ configurable cache size | _ configurable cache size | |||
_ fix on abnormal terminations to be able to restart some | _ fix on abnormal terminations to be able to restart some | |||
*/ | */ | |||
End of changes. 1 change blocks. | ||||
0 lines changed or deleted | 17 lines changed or added | |||
reccache.h | reccache.h | |||
---|---|---|---|---|
// reccache.h | // reccache.h | |||
/* | ||||
* Copyright (C) 2010 10gen Inc. | ||||
* | ||||
* This program is free software: you can redistribute it and/or modify | ||||
* it under the terms of the GNU Affero General Public License, version | ||||
3, | ||||
* as published by the Free Software Foundation. | ||||
* | ||||
* This program is distributed in the hope that it will be useful, | ||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
* GNU Affero General Public License for more details. | ||||
* | ||||
* You should have received a copy of the GNU Affero General Public Lice | ||||
nse | ||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
*/ | ||||
/* CachedBasicRecStore | /* CachedBasicRecStore | |||
This is our store which implements a traditional page-cache type of stor age | This is our store which implements a traditional page-cache type of stor age | |||
(not memory mapped files). | (not memory mapped files). | |||
*/ | */ | |||
/* LOCK HIERARCHY | /* LOCK HIERARCHY | |||
dblock | dblock | |||
RecCache::rcmutex | RecCache::rcmutex | |||
skipping to change at line 232 | skipping to change at line 247 | |||
theRecCache.closeFiles(dbname, dbpath); | theRecCache.closeFiles(dbname, dbpath); | |||
} | } | |||
}; | }; | |||
/* see concurrency.h - note on a lock reset from read->write we don't | /* see concurrency.h - note on a lock reset from read->write we don't | |||
call dbunlocking_read, we just wait for the final dbunlocking_write | call dbunlocking_read, we just wait for the final dbunlocking_write | |||
call | call | |||
*/ | */ | |||
inline void dbunlocking_read() { | inline void dbunlocking_read() { | |||
/* | ||||
Client *c = currentClient.get(); | Client *c = currentClient.get(); | |||
if ( c ) | if ( c ) | |||
c->top.clientStop(); | c->top.clientStop(); | |||
*/ | ||||
} | } | |||
inline void dbunlocking_write() { | inline void dbunlocking_write() { | |||
theRecCache.ejectOld(); | theRecCache.ejectOld(); | |||
dbunlocking_read(); | dbunlocking_read(); | |||
} | } | |||
} /*namespace*/ | } /*namespace*/ | |||
End of changes. 3 change blocks. | ||||
0 lines changed or deleted | 19 lines changed or added | |||
reci.h | reci.h | |||
---|---|---|---|---|
// reci.h | // reci.h | |||
/* | ||||
* Copyright (C) 2010 10gen Inc. | ||||
* | ||||
* This program is free software: you can redistribute it and/or modify | ||||
* it under the terms of the GNU Affero General Public License, version | ||||
3, | ||||
* as published by the Free Software Foundation. | ||||
* | ||||
* This program is distributed in the hope that it will be useful, | ||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
* GNU Affero General Public License for more details. | ||||
* | ||||
* You should have received a copy of the GNU Affero General Public Lice | ||||
nse | ||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
*/ | ||||
#pragma once | #pragma once | |||
#include "storage.h" | #include "diskloc.h" | |||
namespace mongo { | namespace mongo { | |||
/* Subclass this and implement your real storage interface. | /* Subclass this and implement your real storage interface. | |||
*/ | */ | |||
class RecStoreInterface { | class RecStoreInterface { | |||
public: | public: | |||
virtual ~RecStoreInterface() {} | virtual ~RecStoreInterface() {} | |||
/* Get a pointer to the data at diskloc d. Pointer guaranteed to stay in | /* Get a pointer to the data at diskloc d. Pointer guaranteed to stay in | |||
End of changes. 2 change blocks. | ||||
1 lines changed or deleted | 18 lines changed or added | |||
recstore.h | recstore.h | |||
---|---|---|---|---|
// recstore.h | // recstore.h | |||
/* | ||||
* Copyright (C) 2010 10gen Inc. | ||||
* | ||||
* This program is free software: you can redistribute it and/or modify | ||||
* it under the terms of the GNU Affero General Public License, version | ||||
3, | ||||
* as published by the Free Software Foundation. | ||||
* | ||||
* This program is distributed in the hope that it will be useful, | ||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
* GNU Affero General Public License for more details. | ||||
* | ||||
* You should have received a copy of the GNU Affero General Public Lice | ||||
nse | ||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
*/ | ||||
#pragma once | #pragma once | |||
#include "../util/file.h" | #include "../util/file.h" | |||
namespace mongo { | namespace mongo { | |||
using boost::uint32_t; | using boost::uint32_t; | |||
using boost::uint64_t; | using boost::uint64_t; | |||
End of changes. 1 change blocks. | ||||
0 lines changed or deleted | 17 lines changed or added | |||
repl.h | repl.h | |||
---|---|---|---|---|
skipping to change at line 49 | skipping to change at line 49 | |||
namespace mongo { | namespace mongo { | |||
class DBClientConnection; | class DBClientConnection; | |||
class DBClientCursor; | class DBClientCursor; | |||
/* replication slave? (possibly with slave or repl pair nonmaster) | /* replication slave? (possibly with slave or repl pair nonmaster) | |||
--slave cmd line setting -> SimpleSlave | --slave cmd line setting -> SimpleSlave | |||
*/ | */ | |||
typedef enum { NotSlave=0, SimpleSlave, ReplPairSlave } SlaveTypes; | typedef enum { NotSlave=0, SimpleSlave, ReplPairSlave } SlaveTypes; | |||
extern SlaveTypes slave; | ||||
/* true means we are master and doing replication. if we are not wr | class ReplSettings { | |||
iting to oplog (no --master or repl pairing), | public: | |||
this won't be true. | SlaveTypes slave; | |||
*/ | ||||
extern bool master; | ||||
extern int opIdMem; | /* true means we are master and doing replication. if we are not w | |||
riting to oplog (no --master or repl pairing), | ||||
this won't be true. | ||||
*/ | ||||
bool master; | ||||
int opIdMem; | ||||
bool autoresync; | ||||
ReplSettings() | ||||
: slave(NotSlave) , master(false) , opIdMem(100000000) , autore | ||||
sync(false) { | ||||
} | ||||
}; | ||||
extern ReplSettings replSettings; | ||||
bool cloneFrom(const char *masterHost, string& errmsg, const string& fr omdb, bool logForReplication, | bool cloneFrom(const char *masterHost, string& errmsg, const string& fr omdb, bool logForReplication, | |||
bool slaveOk, bool useReplAuth, bool snap shot); | bool slaveOk, bool useReplAuth, bool snap shot); | |||
/* A replication exception */ | /* A replication exception */ | |||
class SyncException : public DBException { | class SyncException : public DBException { | |||
public: | public: | |||
virtual const char* what() const throw() { return "sync exception"; } | virtual const char* what() const throw() { return "sync exception"; } | |||
virtual int getCode(){ return 10001; } | virtual int getCode(){ return 10001; } | |||
}; | }; | |||
skipping to change at line 242 | skipping to change at line 255 | |||
// All functions must be called with db mutex held | // All functions must be called with db mutex held | |||
// Kind of sloppy class structure, for now just want to keep the in mem | // Kind of sloppy class structure, for now just want to keep the in mem | |||
// version speedy. | // version speedy. | |||
// see http://www.mongodb.org/display/DOCS/Pairing+Internals | // see http://www.mongodb.org/display/DOCS/Pairing+Internals | |||
class IdTracker { | class IdTracker { | |||
public: | public: | |||
IdTracker() : | IdTracker() : | |||
dbIds_( "local.temp.replIds" ), | dbIds_( "local.temp.replIds" ), | |||
dbModIds_( "local.temp.replModIds" ), | dbModIds_( "local.temp.replModIds" ), | |||
inMem_( true ), | inMem_( true ), | |||
maxMem_( opIdMem ) { | maxMem_( replSettings.opIdMem ) { | |||
} | } | |||
void reset( int maxMem = opIdMem ) { | void reset( int maxMem = replSettings.opIdMem ) { | |||
memIds_.reset(); | memIds_.reset(); | |||
memModIds_.reset(); | memModIds_.reset(); | |||
dbIds_.reset(); | dbIds_.reset(); | |||
dbModIds_.reset(); | dbModIds_.reset(); | |||
maxMem_ = maxMem; | maxMem_ = maxMem; | |||
inMem_ = true; | inMem_ = true; | |||
} | } | |||
bool haveId( const char *ns, const BSONObj &id ) { | bool haveId( const char *ns, const BSONObj &id ) { | |||
if ( inMem_ ) | if ( inMem_ ) | |||
return get( memIds_, ns, id ); | return get( memIds_, ns, id ); | |||
skipping to change at line 315 | skipping to change at line 328 | |||
} | } | |||
} | } | |||
MemIds memIds_; | MemIds memIds_; | |||
MemIds memModIds_; | MemIds memModIds_; | |||
DbIds dbIds_; | DbIds dbIds_; | |||
DbIds dbModIds_; | DbIds dbModIds_; | |||
bool inMem_; | bool inMem_; | |||
int maxMem_; | int maxMem_; | |||
}; | }; | |||
bool anyReplEnabled(); | ||||
void appendReplicationInfo( BSONObjBuilder& result , bool authed , int | ||||
level = 0 ); | ||||
} // namespace mongo | } // namespace mongo | |||
End of changes. 6 change blocks. | ||||
9 lines changed or deleted | 27 lines changed or added | |||
replset.h | replset.h | |||
---|---|---|---|---|
skipping to change at line 114 | skipping to change at line 114 | |||
/* note we always return true for the "local" namespace. | /* note we always return true for the "local" namespace. | |||
we should not allow most operations when not the master | we should not allow most operations when not the master | |||
also we report not master if we are "dead". | also we report not master if we are "dead". | |||
See also CmdIsMaster. | See also CmdIsMaster. | |||
If 'client' is not specified, the current client is used. | If 'client' is not specified, the current client is used. | |||
*/ | */ | |||
inline bool isMaster( const char *client = 0 ) { | inline bool isMaster( const char *client = 0 ) { | |||
if( !slave ) | if( ! replSettings.slave ) | |||
return true; | return true; | |||
if ( !client ) { | if ( !client ) { | |||
Database *database = cc().database(); | Database *database = cc().database(); | |||
assert( database ); | assert( database ); | |||
client = database->name.c_str(); | client = database->name.c_str(); | |||
} | } | |||
if ( replAllDead ) | if ( replAllDead ) | |||
return strcmp( client, "local" ) == 0; | return strcmp( client, "local" ) == 0; | |||
if ( replPair ) { | if ( replPair ) { | |||
if( replPair->state == ReplPair::State_Master ) | if( replPair->state == ReplPair::State_Master ) | |||
return true; | return true; | |||
} | } | |||
else { | else { | |||
if( master ) { | if( replSettings.master ) { | |||
// if running with --master --slave, allow. note that mast er is also true | // if running with --master --slave, allow. note that mast er is also true | |||
// for repl pairs so the check for replPair above is import ant. | // for repl pairs so the check for replPair above is import ant. | |||
return true; | return true; | |||
} | } | |||
} | } | |||
if ( cc().isGod() ) | if ( cc().isGod() ) | |||
return true; | return true; | |||
return strcmp( client, "local" ) == 0; | return strcmp( client, "local" ) == 0; | |||
End of changes. 2 change blocks. | ||||
2 lines changed or deleted | 2 lines changed or added | |||
request.h | request.h | |||
---|---|---|---|---|
// request.h | // request.h | |||
/* | ||||
* Copyright (C) 2010 10gen Inc. | ||||
* | ||||
* This program is free software: you can redistribute it and/or modify | ||||
* it under the terms of the GNU Affero General Public License, version | ||||
3, | ||||
* as published by the Free Software Foundation. | ||||
* | ||||
* This program is distributed in the hope that it will be useful, | ||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
* GNU Affero General Public License for more details. | ||||
* | ||||
* You should have received a copy of the GNU Affero General Public Lice | ||||
nse | ||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
*/ | ||||
#pragma once | #pragma once | |||
#include "../stdafx.h" | #include "../stdafx.h" | |||
#include "../util/message.h" | #include "../util/message.h" | |||
#include "../db/dbmessage.h" | #include "../db/dbmessage.h" | |||
#include "config.h" | #include "config.h" | |||
#include "util.h" | #include "util.h" | |||
namespace mongo { | namespace mongo { | |||
End of changes. 1 change blocks. | ||||
0 lines changed or deleted | 17 lines changed or added | |||
security.h | security.h | |||
---|---|---|---|---|
skipping to change at line 25 | skipping to change at line 25 | |||
* You should have received a copy of the GNU Affero General Public Licen se | * You should have received a copy of the GNU Affero General Public Licen se | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include <boost/thread/tss.hpp> | #include <boost/thread/tss.hpp> | |||
#undef assert | #undef assert | |||
#define assert xassert | #define assert xassert | |||
#include "db.h" | ||||
#include "dbhelpers.h" | ||||
#include "nonce.h" | #include "nonce.h" | |||
#include "concurrency.h" | ||||
namespace mongo { | namespace mongo { | |||
// --noauth cmd line option | // --noauth cmd line option | |||
extern bool noauth; | extern bool noauth; | |||
/* for a particular db */ | /* for a particular db */ | |||
struct Auth { | struct Auth { | |||
Auth() { level = 0; } | Auth() { level = 0; } | |||
int level; | int level; | |||
}; | }; | |||
class AuthenticationInfo : boost::noncopyable { | class AuthenticationInfo : boost::noncopyable { | |||
map<string, Auth> m; // dbname -> auth | map<string, Auth> m; // dbname -> auth | |||
static int warned; | static int warned; | |||
public: | public: | |||
bool isLocalHost; | bool isLocalHost; | |||
AuthenticationInfo() { isLocalHost = false; } | AuthenticationInfo() { isLocalHost = false; } | |||
virtual ~AuthenticationInfo() { | ~AuthenticationInfo() { | |||
} | } | |||
void logout(const char *dbname) { | void logout(const string& dbname ) { | |||
assertInWriteLock(); | assertInWriteLock(); // TODO: can we get rid of this | |||
? only 1 thread should be looking at an AuthenticationInfo | ||||
m.erase(dbname); | m.erase(dbname); | |||
} | } | |||
void authorize(const char *dbname) { | void authorize(const string& dbname ) { | |||
assertInWriteLock(); | assertInWriteLock(); | |||
m[dbname].level = 2; | m[dbname].level = 2; | |||
} | } | |||
virtual bool isAuthorized(const char *dbname) { | void authorizeReadOnly(const string& dbname) { | |||
if( m[dbname].level == 2 ) return true; | assertInWriteLock(); | |||
m[dbname].level = 1; | ||||
} | ||||
bool isAuthorized(const string& dbname) { return _isAuthorized( dbn | ||||
ame, 2 ); } | ||||
bool isAuthorizedReads(const string& dbname) { return _isAuthorized | ||||
( dbname, 1 ); } | ||||
bool isAuthorizedForLock(const string& dbname, int lockType ) { ret | ||||
urn _isAuthorized( dbname , lockType > 0 ? 2 : 1 ); } | ||||
void print(); | ||||
protected: | ||||
bool _isAuthorized(const string& dbname, int level) { | ||||
if( m[dbname].level >= level ) return true; | ||||
if( noauth ) return true; | if( noauth ) return true; | |||
if( m["admin"].level == 2 ) return true; | if( m["admin"].level >= level ) return true; | |||
if( m["local"].level == 2 ) return true; | if( m["local"].level >= level ) return true; | |||
if( isLocalHost ) { | return _isAuthorizedSpecialChecks( dbname ); | |||
readlock l(""); | ||||
Client::Context c("admin.system.users"); | ||||
BSONObj result; | ||||
if( Helpers::getSingleton("admin.system.user | ||||
s", result) ) | ||||
return false; | ||||
if( warned == 0 ) { | ||||
warned++; | ||||
log() << "warning: no users configur | ||||
ed in admin.system.users, allowing localhost access" << endl; | ||||
} | ||||
return true; | ||||
} | ||||
return false; | ||||
} | } | |||
bool _isAuthorizedSpecialChecks( const string& dbname ); | ||||
}; | }; | |||
} // namespace mongo | } // namespace mongo | |||
End of changes. 8 change blocks. | ||||
25 lines changed or deleted | 27 lines changed or added | |||
strategy.h | strategy.h | |||
---|---|---|---|---|
// strategy.h | // strategy.h | |||
/* | ||||
* Copyright (C) 2010 10gen Inc. | ||||
* | ||||
* This program is free software: you can redistribute it and/or modify | ||||
* it under the terms of the GNU Affero General Public License, version | ||||
3, | ||||
* as published by the Free Software Foundation. | ||||
* | ||||
* This program is distributed in the hope that it will be useful, | ||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
* GNU Affero General Public License for more details. | ||||
* | ||||
* You should have received a copy of the GNU Affero General Public Lice | ||||
nse | ||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
*/ | ||||
#pragma once | #pragma once | |||
#include "../stdafx.h" | #include "../stdafx.h" | |||
#include "chunk.h" | #include "chunk.h" | |||
#include "request.h" | #include "request.h" | |||
namespace mongo { | namespace mongo { | |||
class Strategy { | class Strategy { | |||
End of changes. 1 change blocks. | ||||
0 lines changed or deleted | 17 lines changed or added | |||
syncclusterconnection.h | syncclusterconnection.h | |||
---|---|---|---|---|
// syncclusterconnection.h | // syncclusterconnection.h | |||
/* | ||||
* Copyright 2010 10gen Inc. | ||||
* | ||||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||||
* you may not use this file except in compliance with the License. | ||||
* You may obtain a copy of the License at | ||||
* | ||||
* http://www.apache.org/licenses/LICENSE-2.0 | ||||
* | ||||
* Unless required by applicable law or agreed to in writing, software | ||||
* distributed under the License is distributed on an "AS IS" BASIS, | ||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli | ||||
ed. | ||||
* See the License for the specific language governing permissions and | ||||
* limitations under the License. | ||||
*/ | ||||
#include "../stdafx.h" | #include "../stdafx.h" | |||
#include "dbclient.h" | #include "dbclient.h" | |||
namespace mongo { | namespace mongo { | |||
/** | /** | |||
* this is a connection to a cluster of servers that operate as one | * this is a connection to a cluster of servers that operate as one | |||
* for super high durability | * for super high durability | |||
*/ | */ | |||
class SyncCluterConnection : public DBClientWithCommands { | class SyncClusterConnection : public DBClientWithCommands { | |||
public: | public: | |||
/** | /** | |||
* @param commaSeperated should be 3 hosts comma seperated | * @param commaSeperated should be 3 hosts comma seperated | |||
*/ | */ | |||
SyncCluterConnection( string commaSeperated ); | SyncClusterConnection( string commaSeperated ); | |||
SyncCluterConnection( string a , string b , string c ); | SyncClusterConnection( string a , string b , string c ); | |||
~SyncCluterConnection(); | ~SyncClusterConnection(); | |||
/** | /** | |||
* @return true if all servers are up and ready for writes | * @return true if all servers are up and ready for writes | |||
*/ | */ | |||
bool prepare( string& errmsg ); | bool prepare( string& errmsg ); | |||
/** | /** | |||
* runs fsync on all servers | * runs fsync on all servers | |||
*/ | */ | |||
bool fsync( string& errmsg ); | bool fsync( string& errmsg ); | |||
End of changes. 3 change blocks. | ||||
4 lines changed or deleted | 20 lines changed or added | |||
tool.h | tool.h | |||
---|---|---|---|---|
/* | ||||
* Copyright (C) 2010 10gen Inc. | ||||
* | ||||
* This program is free software: you can redistribute it and/or modify | ||||
* it under the terms of the GNU Affero General Public License, version | ||||
3, | ||||
* as published by the Free Software Foundation. | ||||
* | ||||
* This program is distributed in the hope that it will be useful, | ||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
* GNU Affero General Public License for more details. | ||||
* | ||||
* You should have received a copy of the GNU Affero General Public Lice | ||||
nse | ||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
*/ | ||||
// Tool.h | // Tool.h | |||
#pragma once | #pragma once | |||
#include <string> | #include <string> | |||
#include <boost/program_options.hpp> | #include <boost/program_options.hpp> | |||
#if defined(_WIN32) | #if defined(_WIN32) | |||
#include <io.h> | #include <io.h> | |||
End of changes. 1 change blocks. | ||||
0 lines changed or deleted | 18 lines changed or added | |||
top.h | top.h | |||
---|---|---|---|---|
skipping to change at line 26 | skipping to change at line 26 | |||
*/ | */ | |||
#pragma once | #pragma once | |||
#include <boost/date_time/posix_time/posix_time.hpp> | #include <boost/date_time/posix_time/posix_time.hpp> | |||
#undef assert | #undef assert | |||
#define assert xassert | #define assert xassert | |||
namespace mongo { | namespace mongo { | |||
/** | ||||
* tracks usage by collection | ||||
*/ | ||||
class Top { | ||||
public: | ||||
class UsageData { | ||||
public: | ||||
UsageData() : time(0) , count(0){} | ||||
UsageData( UsageData& older , UsageData& newer ); | ||||
long long time; | ||||
long long count; | ||||
void inc( long long micros ){ | ||||
count++; | ||||
time += micros; | ||||
} | ||||
}; | ||||
class CollectionData { | ||||
public: | ||||
/** | ||||
* constructs a diff | ||||
*/ | ||||
CollectionData(){} | ||||
CollectionData( CollectionData& older , CollectionData& newer ) | ||||
; | ||||
UsageData total; | ||||
UsageData readLock; | ||||
UsageData writeLock; | ||||
UsageData queries; | ||||
UsageData getmore; | ||||
UsageData insert; | ||||
UsageData update; | ||||
UsageData remove; | ||||
UsageData commands; | ||||
}; | ||||
typedef map<string,CollectionData> UsageMap; | ||||
public: | ||||
void record( const string& ns , int op , int lockType , long long m | ||||
icros , bool command ); | ||||
void append( BSONObjBuilder& b ); | ||||
UsageMap cloneMap(); | ||||
CollectionData getGlobalData(){ return _global; } | ||||
public: // static stuff | ||||
static Top global; | ||||
void append( BSONObjBuilder& b , const char * name , const UsageDat | ||||
a& map ); | ||||
void append( BSONObjBuilder& b , const UsageMap& map ); | ||||
private: | ||||
void _record( CollectionData& c , int op , int lockType , long long | ||||
micros , bool command ); | ||||
boost::mutex _lock; | ||||
CollectionData _global; | ||||
UsageMap _usage; | ||||
}; | ||||
/* 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 Top { | 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: | |||
Top() : _read(false), _write(false) { } | TopOld() : _read(false), _write(false) { } | |||
/* these are used to record activity: */ | /* these are used to record activity: */ | |||
void clientStart( const char *client ) { | void clientStart( const char *client ) { | |||
clientStop(); | clientStop(); | |||
_currentStart = currentTime(); | _currentStart = currentTime(); | |||
_current = client; | _current = client; | |||
} | } | |||
/* indicate current request is a read operation. */ | /* indicate current request is a read operation. */ | |||
End of changes. 3 change blocks. | ||||
2 lines changed or deleted | 69 lines changed or added | |||
update.h | update.h | |||
---|---|---|---|---|
skipping to change at line 26 | skipping to change at line 26 | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | */ | |||
#include "../stdafx.h" | #include "../stdafx.h" | |||
#include "jsobj.h" | #include "jsobj.h" | |||
#include "../util/embedded_builder.h" | #include "../util/embedded_builder.h" | |||
#include "matcher.h" | #include "matcher.h" | |||
namespace mongo { | namespace mongo { | |||
/* Used for modifiers such as $inc, $set, $push, ... */ | class ModState; | |||
class ModSetState; | ||||
/* Used for modifiers such as $inc, $set, $push, ... | ||||
* stores the info about a single operation | ||||
* once created should never be modified | ||||
*/ | ||||
struct Mod { | struct Mod { | |||
// See opFromStr below | // See opFromStr below | |||
// 0 1 2 3 4 5 6 7 8 | // 0 1 2 3 4 5 6 7 8 | |||
9 10 | 9 10 11 | |||
enum Op { INC, SET, PUSH, PUSH_ALL, PULL, PULL_ALL , POP, UNSET, BI | enum Op { INC, SET, PUSH, PUSH_ALL, PULL, PULL_ALL , POP, UNSET, BI | |||
TAND, BITOR , BIT } op; | TAND, BITOR , BIT , ADDTOSET } op; | |||
static const char* modNames[]; | static const char* modNames[]; | |||
static unsigned modNamesNum; | static unsigned modNamesNum; | |||
const char *fieldName; | const char *fieldName; | |||
const char *shortFieldName; | const char *shortFieldName; | |||
// kind of lame; fix one day? | ||||
double *ndouble; | ||||
int *nint; | ||||
long long *nlong; | ||||
BSONElement elt; // x:5 note: this is the actual element from the u pdateobj | BSONElement elt; // x:5 note: this is the actual element from the u pdateobj | |||
int pushStartSize; | ||||
boost::shared_ptr<Matcher> matcher; | boost::shared_ptr<Matcher> matcher; | |||
void init( Op o , BSONElement& e ){ | void init( Op o , BSONElement& e ){ | |||
op = o; | op = o; | |||
elt = e; | elt = e; | |||
if ( op == PULL && e.type() == Object ) | if ( op == PULL && e.type() == Object ) | |||
matcher.reset( new Matcher( e.embeddedObject() ) ); | matcher.reset( new Matcher( e.embeddedObject() ) ); | |||
} | } | |||
void setFieldName( const char * s ){ | void setFieldName( const char * s ){ | |||
fieldName = s; | fieldName = s; | |||
shortFieldName = strrchr( fieldName , '.' ); | shortFieldName = strrchr( fieldName , '.' ); | |||
if ( shortFieldName ) | if ( shortFieldName ) | |||
shortFieldName++; | shortFieldName++; | |||
else | else | |||
shortFieldName = fieldName; | shortFieldName = fieldName; | |||
} | } | |||
/* [dm] why is this const? (or rather, why was setn const?) i see | /** | |||
why but think maybe clearer if were not. */ | * @param in incrememnts the actual value inside in | |||
void inc(BSONElement& n) const { | */ | |||
uassert( 10160 , "$inc value is not a number", n.isNumber() ); | void incrementMe( BSONElement& in ) const { | |||
if( ndouble ) | BSONElementManipulator manip( in ); | |||
*ndouble += n.numberDouble(); | ||||
else if( nint ) | ||||
*nint += n.numberInt(); | ||||
else | ||||
*nlong += n.numberLong(); | ||||
} | ||||
void setElementToOurNumericValue(BSONElement& e) const { | switch ( in.type() ){ | |||
BSONElementManipulator manip(e); | case NumberDouble: | |||
if( e.type() == NumberLong ) | manip.setNumber( elt.numberDouble() + in.numberDouble() ); | |||
manip.setLong(_getlong()); | break; | |||
else | case NumberLong: | |||
manip.setNumber(_getn()); | manip.setLong( elt.numberLong() + in.numberLong() ); | |||
} | break; | |||
case NumberInt: | ||||
manip.setInt( elt.numberInt() + in.numberInt() ); | ||||
break; | ||||
default: | ||||
assert(0); | ||||
} | ||||
double _getn() const { | ||||
if( ndouble ) return *ndouble; | ||||
if( nint ) return *nint; | ||||
return (double) *nlong; | ||||
} | ||||
long long _getlong() const { | ||||
if( nlong ) return *nlong; | ||||
if( ndouble ) return (long long) *ndouble; | ||||
return *nint; | ||||
} | } | |||
void appendIncremented( BSONObjBuilder& bb , const BSONElement& in, | ||||
ModState& 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: | |||
return true; | return true; | |||
skipping to change at line 123 | skipping to change at line 118 | |||
// 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 ); | void apply( BSONObjBuilder& b , BSONElement in , ModState& ms ) con st; | |||
/** | /** | |||
* @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; | |||
bool needOpLogRewrite() const { | void _checkForAppending( const BSONElement& e ) const { | |||
switch( op ){ | ||||
case BIT: | ||||
case BITAND: | ||||
case BITOR: | ||||
// TODO: should we convert this to $set? | ||||
return false; | ||||
default: | ||||
return false; | ||||
} | ||||
} | ||||
void appendForOpLog( BSONObjBuilder& b ) const { | ||||
const char * name = modNames[op]; | ||||
BSONObjBuilder bb( b.subobjStart( name ) ); | ||||
bb.append( elt ); | ||||
bb.done(); | ||||
} | ||||
void _checkForAppending( BSONElement& e ){ | ||||
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 | |||
// and it checks for { $set : { x : { 'a.b' : 1 } } } | // and it checks for { $set : { x : { 'a.b' : 1 } } } | |||
// which is feel has been common | // which is feel has been common | |||
uassert( 12527 , "not okForStorage" , e.embeddedObject().ok ForStorage() ); | uassert( 12527 , "not okForStorage" , e.embeddedObject().ok ForStorage() ); | |||
} | } | |||
} | } | |||
}; | }; | |||
class ModSet { | /** | |||
* stores a set of Mods | ||||
* once created, should never be changed | ||||
*/ | ||||
class ModSet : boost::noncopyable { | ||||
typedef map<string,Mod> ModHolder; | typedef map<string,Mod> ModHolder; | |||
ModHolder _mods; | ModHolder _mods; | |||
int _isIndexed; | ||||
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; | |||
// If one iterator is done we want to read from the other one, so say the other one is lower. | // If one iterator is done we want to read from the other one, so say the other one is lower. | |||
if ( mDone ) | if ( mDone ) | |||
return RIGHT_BEFORE; | return RIGHT_BEFORE; | |||
if ( pDone ) | if ( pDone ) | |||
return LEFT_BEFORE; | return LEFT_BEFORE; | |||
return compareDottedFieldNames( m->first, p->first.c_str() ); | return compareDottedFieldNames( m->first, p->first.c_str() ); | |||
} | } | |||
void _appendNewFromMods( const string& root , Mod& m , BSONObjBuild | ||||
er& b , set<string>& onedownseen ); | ||||
void appendNewFromMod( Mod& m , BSONObjBuilder& b ){ | ||||
switch ( m.op ){ | ||||
case Mod::PUSH: { | ||||
BSONObjBuilder arr( b.subarrayStart( m.shortFieldName ) ); | ||||
arr.appendAs( m.elt, "0" ); | ||||
arr.done(); | ||||
m.pushStartSize = -1; | ||||
break; | ||||
} | ||||
case Mod::PUSH_ALL: { | ||||
b.appendAs( m.elt, m.shortFieldName ); | ||||
m.pushStartSize = -1; | ||||
break; | ||||
} | ||||
case Mod::UNSET: | ||||
case Mod::PULL: | ||||
case Mod::PULL_ALL: | ||||
// no-op b/c unset/pull of nothing does nothing | ||||
break; | ||||
case Mod::INC: | ||||
case Mod::SET: { | ||||
m._checkForAppending( m.elt ); | ||||
b.appendAs( m.elt, m.shortFieldName ); | ||||
break; | ||||
} | ||||
default: | ||||
stringstream ss; | ||||
ss << "unknown mod in appendNewFromMod: " << m.op; | ||||
throw UserException( 9015, ss.str() ); | ||||
} | ||||
} | ||||
bool mayAddEmbedded( map< string, BSONElement > &existing, string r ight ) { | bool mayAddEmbedded( map< string, BSONElement > &existing, string r ight ) { | |||
for( string left = EmbeddedBuilder::splitDot( right ); | for( string left = EmbeddedBuilder::splitDot( right ); | |||
left.length() > 0 && left[ left.length() - 1 ] != '.'; | left.length() > 0 && left[ left.length() - 1 ] != '.'; | |||
left += "." + EmbeddedBuilder::splitDot( right ) ) { | left += "." + EmbeddedBuilder::splitDot( right ) ) { | |||
if ( existing.count( left ) > 0 && existing[ left ].type() != Object ) | if ( existing.count( left ) > 0 && existing[ left ].type() != Object ) | |||
return false; | return false; | |||
if ( haveModForField( left.c_str() ) ) | if ( haveModForField( left.c_str() ) ) | |||
return false; | return false; | |||
} | } | |||
return true; | return true; | |||
skipping to change at line 282 | skipping to change at line 223 | |||
if ( fn[2] == 'i' && fn[3] == 't' ){ | if ( fn[2] == 'i' && fn[3] == 't' ){ | |||
if ( fn[4] == 0 ) | if ( fn[4] == 0 ) | |||
return Mod::BIT; | return Mod::BIT; | |||
if ( fn[4] == 'a' && fn[5] == 'n' && fn[6] == 'd' && fn [7] == 0 ) | if ( fn[4] == 'a' && fn[5] == 'n' && fn[6] == 'd' && fn [7] == 0 ) | |||
return Mod::BITAND; | return Mod::BITAND; | |||
if ( fn[4] == 'o' && fn[5] == 'r' && fn[6] == 0 ) | if ( fn[4] == 'o' && fn[5] == 'r' && fn[6] == 0 ) | |||
return Mod::BITOR; | return Mod::BITOR; | |||
} | } | |||
break; | break; | |||
} | } | |||
case 'a': { | ||||
if ( fn[2] == 'd' && fn[3] == 'd' ){ | ||||
// add | ||||
if ( fn[4] == 'T' && fn[5] == 'o' && fn[6] == 'S' && fn | ||||
[7] == 'e' && fn[8] == 't' && fn[9] == 0 ) | ||||
return Mod::ADDTOSET; | ||||
} | ||||
} | ||||
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; | |||
} | } | |||
public: | public: | |||
void getMods( const BSONObj &from ); | ModSet( const BSONObj &from , | |||
const set<string>& idxKeys = set<string>(), | ||||
const set<string>* backgroundKeys = 0 | ||||
); | ||||
/** | /** | |||
will return if can be done in place, or uassert if there is an e | * creates a ModSetState suitable for operation on obj | |||
rror | * doesn't change or modify this ModSet or any underying Mod | |||
@return whether or not the mods can be done in place | ||||
*/ | */ | |||
bool canApplyInPlaceAndVerify( const BSONObj &obj ) const; | auto_ptr<ModSetState> prepare( const BSONObj& obj ) const; | |||
void applyModsInPlace( const BSONObj &obj ) const; | ||||
// new recursive version, will replace at some point | ||||
void createNewFromMods( const string& root , BSONObjBuilder& b , co | ||||
nst BSONObj &obj ); | ||||
BSONObj createNewFromMods( const BSONObj &obj ); | ||||
/** | ||||
* given a query pattern, builds an object suitable for an upsert | ||||
* will take the query spec and combine all $ operators | ||||
*/ | ||||
BSONObj createNewFromQuery( const BSONObj& query ); | BSONObj createNewFromQuery( const BSONObj& query ); | |||
/** | /** | |||
* | * | |||
*/ | */ | |||
int isIndexed( const set<string>& idxKeys ) const { | int isIndexed() const { | |||
int numIndexes = 0; | return _isIndexed; | |||
for ( ModHolder::const_iterator i = _mods.begin(); i != _mods.e | ||||
nd(); i++ ){ | ||||
if ( i->second.isIndexed( idxKeys ) ) | ||||
numIndexes++; | ||||
} | ||||
return numIndexes; | ||||
} | } | |||
unsigned size() const { return _mods.size(); } | unsigned size() const { return _mods.size(); } | |||
bool haveModForField( const char *fieldName ) const { | bool haveModForField( const char *fieldName ) const { | |||
return _mods.find( fieldName ) != _mods.end(); | return _mods.find( fieldName ) != _mods.end(); | |||
} | } | |||
bool haveConflictingMod( const string& fieldName ){ | bool haveConflictingMod( const string& fieldName ){ | |||
size_t idx = fieldName.find( '.' ); | size_t idx = fieldName.find( '.' ); | |||
skipping to change at line 343 | skipping to change at line 289 | |||
case LEFT_BEFORE: return false; | case LEFT_BEFORE: return false; | |||
case SAME: return true; | case SAME: return true; | |||
case RIGHT_BEFORE: return false; | case RIGHT_BEFORE: return false; | |||
case RIGHT_SUBFIELD: return true; | case RIGHT_SUBFIELD: return true; | |||
} | } | |||
} | } | |||
return false; | return false; | |||
} | } | |||
}; | ||||
/** | ||||
* stores any information about a single Mod operating on a single Obje | ||||
ct | ||||
*/ | ||||
class ModState { | ||||
public: | ||||
const Mod * m; | ||||
BSONElement old; | ||||
const char * fixedName; | ||||
BSONElement * fixed; | ||||
int pushStartSize; | ||||
BSONType incType; | ||||
int incint; | ||||
double incdouble; | ||||
long long inclong; | ||||
ModState(){ | ||||
fixedName = 0; | ||||
fixed = 0; | ||||
pushStartSize = -1; | ||||
incType = EOO; | ||||
} | ||||
Mod::Op op() const { | ||||
return m->op; | ||||
} | ||||
const char * fieldName() const { | ||||
return m->fieldName; | ||||
} | ||||
bool needOpLogRewrite() const { | ||||
if ( fixed || incType ) | ||||
return true; | ||||
switch( op() ){ | ||||
case Mod::BIT: | ||||
case Mod::BITAND: | ||||
case Mod::BITOR: | ||||
// TODO: should we convert this to $set? | ||||
return false; | ||||
default: | ||||
return false; | ||||
} | ||||
} | ||||
void appendForOpLog( BSONObjBuilder& b ) const { | ||||
if ( incType ){ | ||||
BSONObjBuilder bb( b.subobjStart( "$set" ) ); | ||||
appendIncValue( bb ); | ||||
bb.done(); | ||||
return; | ||||
} | ||||
const char * name = fixedName ? fixedName : Mod::modNames[op()] | ||||
; | ||||
BSONObjBuilder bb( b.subobjStart( name ) ); | ||||
if ( fixed ) | ||||
bb.append( *fixed ); | ||||
else | ||||
bb.append( m->elt ); | ||||
bb.done(); | ||||
} | ||||
void apply( BSONObjBuilder& b , BSONElement in ){ | ||||
m->apply( b , in , *this ); | ||||
} | ||||
void appendIncValue( BSONObjBuilder& b ) const { | ||||
switch ( incType ){ | ||||
case NumberDouble: | ||||
b.append( m->shortFieldName , incdouble ); break; | ||||
case NumberLong: | ||||
b.append( m->shortFieldName , inclong ); break; | ||||
case NumberInt: | ||||
b.append( m->shortFieldName , incint ); break; | ||||
default: | ||||
assert(0); | ||||
} | ||||
} | ||||
}; | ||||
/** | ||||
* 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 | ||||
*/ | ||||
class ModSetState : boost::noncopyable { | ||||
typedef map<string,ModState> ModStateHolder; | ||||
const BSONObj& _obj; | ||||
ModStateHolder _mods; | ||||
bool _inPlacePossible; | ||||
ModSetState( const BSONObj& obj ) | ||||
: _obj( obj ) , _inPlacePossible(true){ | ||||
} | ||||
/** | ||||
* @return if in place is still possible | ||||
*/ | ||||
bool amIInPlacePossible( bool inPlacePossible ){ | ||||
if ( ! inPlacePossible ) | ||||
_inPlacePossible = false; | ||||
return _inPlacePossible; | ||||
} | ||||
void createNewFromMods( const string& root , BSONObjBuilder& b , co | ||||
nst BSONObj &obj ); | ||||
void _appendNewFromMods( const string& root , ModState& m , BSONObj | ||||
Builder& b , set<string>& onedownseen ); | ||||
void appendNewFromMod( ModState& ms , BSONObjBuilder& b ){ | ||||
//const Mod& m = *(ms.m); // HACK | ||||
Mod& m = *((Mod*)(ms.m)); // HACK | ||||
switch ( m.op ){ | ||||
case Mod::PUSH: { | ||||
BSONObjBuilder arr( b.subarrayStart( m.shortFieldName ) ); | ||||
arr.appendAs( m.elt, "0" ); | ||||
arr.done(); | ||||
break; | ||||
} | ||||
case Mod::PUSH_ALL: { | ||||
b.appendAs( m.elt, m.shortFieldName ); | ||||
break; | ||||
} | ||||
case Mod::UNSET: | ||||
case Mod::PULL: | ||||
case Mod::PULL_ALL: | ||||
// no-op b/c unset/pull of nothing does nothing | ||||
break; | ||||
case Mod::INC: | ||||
case Mod::SET: { | ||||
m._checkForAppending( m.elt ); | ||||
b.appendAs( m.elt, m.shortFieldName ); | ||||
break; | ||||
} | ||||
default: | ||||
stringstream ss; | ||||
ss << "unknown mod in appendNewFromMod: " << m.op; | ||||
throw UserException( 9015, ss.str() ); | ||||
} | ||||
} | ||||
public: | ||||
bool canApplyInPlace() const { | ||||
return _inPlacePossible; | ||||
} | ||||
/** | ||||
* modified underlying _obj | ||||
*/ | ||||
void applyModsInPlace(); | ||||
BSONObj createNewFromMods(); | ||||
// re-writing for oplog | // re-writing for oplog | |||
bool needOpLogRewrite() const { | bool needOpLogRewrite() const { | |||
for ( ModHolder::const_iterator i = _mods.begin(); i != _mods.e nd(); i++ ) | for ( ModStateHolder::const_iterator i = _mods.begin(); i != _m ods.end(); i++ ) | |||
if ( i->second.needOpLogRewrite() ) | if ( i->second.needOpLogRewrite() ) | |||
return true; | return true; | |||
return false; | return false; | |||
} | } | |||
BSONObj getOpLogRewrite() const { | BSONObj getOpLogRewrite() const { | |||
BSONObjBuilder b; | BSONObjBuilder b; | |||
for ( ModHolder::const_iterator i = _mods.begin(); i != _mods.e nd(); i++ ) | for ( ModStateHolder::const_iterator i = _mods.begin(); i != _m ods.end(); i++ ) | |||
i->second.appendForOpLog( b ); | i->second.appendForOpLog( b ); | |||
return b.obj(); | return b.obj(); | |||
} | } | |||
bool haveArrayDepMod() const { | bool haveArrayDepMod() const { | |||
for ( ModHolder::const_iterator i = _mods.begin(); i != _mods.e | for ( ModStateHolder::const_iterator i = _mods.begin(); i != _m | |||
nd(); i++ ) | ods.end(); i++ ) | |||
if ( i->second.arrayDep() ) | if ( i->second.m->arrayDep() ) | |||
return true; | return true; | |||
return false; | return false; | |||
} | } | |||
void appendSizeSpecForArrayDepMods( BSONObjBuilder &b ) const { | void appendSizeSpecForArrayDepMods( BSONObjBuilder &b ) const { | |||
for ( ModHolder::const_iterator i = _mods.begin(); i != _mods.e | for ( ModStateHolder::const_iterator i = _mods.begin(); i != _m | |||
nd(); i++ ) { | ods.end(); i++ ) { | |||
const Mod& m = i->second; | const ModState& m = i->second; | |||
if ( m.arrayDep() ){ | if ( m.m->arrayDep() ){ | |||
if ( m.pushStartSize == -1 ) | if ( m.pushStartSize == -1 ) | |||
b.appendNull( m.fieldName ); | b.appendNull( m.fieldName() ); | |||
else | else | |||
b << m.fieldName << BSON( "$size" << m.pushStartSiz e ); | b << m.fieldName() << BSON( "$size" << m.pushStartS ize ); | |||
} | } | |||
} | } | |||
} | } | |||
friend class ModSet; | ||||
}; | }; | |||
} | } | |||
End of changes. 27 change blocks. | ||||
132 lines changed or deleted | 245 lines changed or added | |||
utils.h | utils.h | |||
---|---|---|---|---|
// utils.h | // utils.h | |||
/* | ||||
* Copyright 2010 10gen Inc. | ||||
* | ||||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||||
* you may not use this file except in compliance with the License. | ||||
* You may obtain a copy of the License at | ||||
* | ||||
* http://www.apache.org/licenses/LICENSE-2.0 | ||||
* | ||||
* Unless required by applicable law or agreed to in writing, software | ||||
* distributed under the License is distributed on an "AS IS" BASIS, | ||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli | ||||
ed. | ||||
* See the License for the specific language governing permissions and | ||||
* limitations under the License. | ||||
*/ | ||||
#pragma once | #pragma once | |||
#include "../scripting/engine.h" | #include "../scripting/engine.h" | |||
namespace mongo { | namespace mongo { | |||
namespace shellUtils { | namespace shellUtils { | |||
extern std::string _dbConnect; | extern std::string _dbConnect; | |||
End of changes. 1 change blocks. | ||||
0 lines changed or deleted | 16 lines changed or added | |||
v8_db.h | v8_db.h | |||
---|---|---|---|---|
skipping to change at line 62 | skipping to change at line 62 | |||
// DB members | // DB members | |||
v8::Handle<v8::Value> dbInit(const v8::Arguments& args); | v8::Handle<v8::Value> dbInit(const v8::Arguments& args); | |||
v8::Handle<v8::Value> collectionInit( const v8::Arguments& args ); | v8::Handle<v8::Value> collectionInit( const v8::Arguments& args ); | |||
v8::Handle<v8::Value> objectIdInit( const v8::Arguments& args ); | v8::Handle<v8::Value> objectIdInit( const v8::Arguments& args ); | |||
v8::Handle<v8::Value> dbRefInit( const v8::Arguments& args ); | v8::Handle<v8::Value> dbRefInit( const v8::Arguments& args ); | |||
v8::Handle<v8::Value> dbPointerInit( const v8::Arguments& args ); | v8::Handle<v8::Value> dbPointerInit( const v8::Arguments& args ); | |||
v8::Handle<v8::Value> binDataInit( const v8::Arguments& args ); | v8::Handle<v8::Value> binDataInit( const v8::Arguments& args ); | |||
v8::Handle<v8::Value> binDataToString( const v8::Arguments& args ); | ||||
v8::Handle<v8::Value> numberLongInit( const v8::Arguments& args ); | ||||
v8::Handle<v8::Value> numberLongToNumber(const v8::Arguments& args); | ||||
v8::Handle<v8::Value> numberLongValueOf(const v8::Arguments& args); | ||||
v8::Handle<v8::Value> numberLongToString(const v8::Arguments& args); | ||||
v8::Handle<v8::Value> dbQueryInit( const v8::Arguments& args ); | v8::Handle<v8::Value> dbQueryInit( const v8::Arguments& args ); | |||
v8::Handle<v8::Value> dbQueryIndexAccess( uint32_t index , const v8::Ac cessorInfo& info ); | v8::Handle<v8::Value> dbQueryIndexAccess( uint32_t index , const v8::Ac cessorInfo& info ); | |||
v8::Handle<v8::Value> collectionFallback( v8::Local<v8::String> name, c onst v8::AccessorInfo &info); | v8::Handle<v8::Value> collectionFallback( v8::Local<v8::String> name, c onst v8::AccessorInfo &info); | |||
v8::Handle<v8::Value> bsonsize( const v8::Arguments& args ); | v8::Handle<v8::Value> bsonsize( const v8::Arguments& args ); | |||
} | } | |||
End of changes. 1 change blocks. | ||||
0 lines changed or deleted | 6 lines changed or added | |||