btree.h   btree.h 
skipping to change at line 260 skipping to change at line 260
return !bucket.isNull(); return !bucket.isNull();
} }
bool eof() { bool eof() {
return !ok(); return !ok();
} }
virtual bool advance(); virtual bool advance();
virtual void noteLocation(); // updates keyAtKeyOfs... virtual void noteLocation(); // updates keyAtKeyOfs...
virtual void checkLocation(); virtual void checkLocation();
/* used for multikey index traversal to avoid sending back dups. se e JSMatcher::matches(). /* used for multikey index traversal to avoid sending back dups. se e Matcher::matches().
if a multikey index traversal: if a multikey index traversal:
if loc has already been sent, returns true. if loc has already been sent, returns true.
otherwise, marks loc as sent. otherwise, marks loc as sent.
@return true if the loc has not been seen @return true if the loc has not been seen
*/ */
set<DiskLoc> dups; set<DiskLoc> dups;
virtual bool getsetdup(DiskLoc loc) { virtual bool getsetdup(DiskLoc loc) {
if( multikey ) { if( multikey ) {
pair<set<DiskLoc>::iterator, bool> p = dups.insert(loc); pair<set<DiskLoc>::iterator, bool> p = dups.insert(loc);
return !p.second; return !p.second;
 End of changes. 1 change blocks. 
1 lines changed or deleted 1 lines changed or added


 builder.h   builder.h 
skipping to change at line 174 skipping to change at line 174
} }
StringBuilder& operator<<( long long x ){ StringBuilder& operator<<( long long x ){
SBNUM( x , 22 , "%lld" ); SBNUM( x , 22 , "%lld" );
} }
StringBuilder& operator<<( unsigned long long x ){ StringBuilder& operator<<( unsigned long long x ){
SBNUM( x , 22 , "%llu" ); SBNUM( x , 22 , "%llu" );
} }
StringBuilder& operator<<( short x ){ StringBuilder& operator<<( short x ){
SBNUM( x , 8 , "%hd" ); SBNUM( x , 8 , "%hd" );
} }
StringBuilder& operator<<( char c ){
_buf.grow( 1 )[0] = c;
return *this;
}
void append( const char * str ){ void append( const char * str ){
int x = strlen( str ); int x = strlen( str );
memcpy( _buf.grow( x ) , str , x ); memcpy( _buf.grow( x ) , str , x );
} }
StringBuilder& operator<<( const char * str ){ StringBuilder& operator<<( const char * str ){
append( str ); append( str );
return *this; return *this;
} }
StringBuilder& operator<<( const string& s ){ StringBuilder& operator<<( const string& s ){
 End of changes. 1 change blocks. 
0 lines changed or deleted 4 lines changed or added


 client.h   client.h 
skipping to change at line 37 skipping to change at line 37
#include "../stdafx.h" #include "../stdafx.h"
#include "namespace.h" #include "namespace.h"
#include "lasterror.h" #include "lasterror.h"
#include "../util/top.h" #include "../util/top.h"
namespace mongo { namespace mongo {
class AuthenticationInfo; class AuthenticationInfo;
class Database; class Database;
class CurOp; class CurOp;
class Command;
class Client;
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)
Note this is also helpful if an exception happens as the state i
f fixed up.
*/
class Context {
Client * _client;
Database * _olddb;
string _oldns;
public:
Context(const char *ns)
: _client( currentClient.get() ) {
_olddb = _client->_database;
_oldns = _client->_ns;
setClient(ns);
}
Context(string ns)
: _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: */
Context()
: _client( currentClient.get() ) {
_olddb = _client->database();
_oldns = _client->ns();
}
/**
* 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
*/
Context( string ns , Database * db );
~Context() {
DEV assert( _client == currentClient.get() );
_client->setns( _oldns.c_str(), _olddb );
}
};
private: private:
CurOp * const _curOp; CurOp * const _curOp;
Database *_database; Database *_database;
Namespace _ns; Namespace _ns;
//NamespaceString _nsstr; //NamespaceString _nsstr;
bool _shutdown; bool _shutdown;
list<string> _tempCollections; list<string> _tempCollections;
const char *_desc; const char *_desc;
bool _god; bool _god;
public: public:
skipping to change at line 99 skipping to change at line 147
/* /*
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; }
}; };
/* defined in security.cpp - one day add client.cpp? */
extern boost::thread_specific_ptr<Client> currentClient;
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) {
assert( currentClient.get() == 0 ); assert( currentClient.get() == 0 );
currentClient.reset( new Client(desc) ); currentClient.reset( new Client(desc) );
 End of changes. 3 change blocks. 
3 lines changed or deleted 54 lines changed or added


 clientcursor.h   clientcursor.h 
skipping to change at line 53 skipping to change at line 53
typedef map<CursorId, ClientCursor*> CCById; typedef map<CursorId, ClientCursor*> CCById;
typedef multimap<DiskLoc, ClientCursor*> CCByLoc; typedef multimap<DiskLoc, ClientCursor*> CCByLoc;
extern BSONObj id_obj; extern BSONObj id_obj;
class ClientCursor { class ClientCursor {
friend class CmdCursorInfo; friend class CmdCursorInfo;
DiskLoc _lastLoc; // use getter and setter n ot this (important) DiskLoc _lastLoc; // use getter and setter n ot this (important)
unsigned _idleAgeMillis; // how long has the cursor been around, relative to server idle time unsigned _idleAgeMillis; // how long has the cursor been around, relative to server idle time
bool _noTimeout; // if true, never time out c
ursor /* 0 = normal
1 = no timeout allowed
100 = in use (pinned) -- see Pointer class
*/
unsigned _pinValue;
bool _doingDeletes; bool _doingDeletes;
static CCById clientCursorsById; static CCById clientCursorsById;
static CCByLoc byLoc; static CCByLoc byLoc;
static boost::recursive_mutex ccmutex; // must use this for all s tatics above! static boost::recursive_mutex ccmutex; // must use this for all s tatics above!
static CursorId allocCursorId_inlock(); static CursorId allocCursorId_inlock();
public: public:
/* use this to assure we don't in the background time out cursor wh
ile it is under use.
if you are using noTimeout() already, there is no risk anyway.
Further, this mechanism guards against two getMore requests on t
he same cursor executing
at the same time - which might be bad. That should never happen
, but if a client driver
had a bug, it could (or perhaps some sort of attack situation).
*/
class Pointer : boost::noncopyable {
public:
ClientCursor *_c;
void release() {
if( _c ) {
assert( _c->_pinValue >= 100 );
_c->_pinValue -= 100;
}
_c = 0;
}
Pointer(long long cursorid) {
recursive_boostlock lock(ccmutex);
_c = ClientCursor::find_inlock(cursorid, true);
if( _c ) {
if( _c->_pinValue >= 100 ) {
_c = 0;
uassert(12051, "clientcursor already in use? driver
problem?", false);
}
_c->_pinValue += 100;
}
}
~Pointer() {
release();
}
};
/*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), _noTimeout(false), _doingDelete s(false), pos(0) { ClientCursor() : _idleAgeMillis(0), _pinValue(0), _doingDeletes(fal se), pos(0) {
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;
} }
skipping to change at line 113 skipping to change at line 151
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);
return 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
// for the cursor or keep a ClientCursor::Pointer in
scope for it.
massert( 12521, "internal error: use of an unlocked ClientCurso
r", c->_pinValue );
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
delete cc; delete cc;
return true; return true;
} }
return false; return false;
} }
/* call when cursor's location changes so that we can update the /* call when cursor's location changes so that we can update the
cursorsbylocation map. if you are locked and internally iterati ng, only cursorsbylocation map. if you are locked and internally iterati ng, only
need to call when you are ready to "unlock". need to call when you are ready to "unlock".
*/ */
skipping to change at line 147 skipping to change at line 190
stringstream ss; stringstream ss;
ss << ns << "." << cursorid; ss << ns << "." << cursorid;
ids_->mayUpgradeStorage( ss.str() );*/ ids_->mayUpgradeStorage( ss.str() );*/
} }
/** /**
* @param millis amount of idle passed time since last call * @param millis amount of idle passed time since last call
*/ */
bool shouldTimeout( unsigned millis ){ bool shouldTimeout( unsigned millis ){
_idleAgeMillis += millis; _idleAgeMillis += millis;
return ! _noTimeout && _idleAgeMillis > 600000; return _idleAgeMillis > 600000 && _pinValue == 0;
} }
unsigned idleTime(){ unsigned idleTime(){
return _idleAgeMillis; return _idleAgeMillis;
} }
static void idleTimeReport(unsigned millis); static void idleTimeReport(unsigned millis);
// cursors normally timeout after an inactivy period to prevent exc
ess memory use
// setting this prevents timeout of the cursor in question.
void noTimeout() { void noTimeout() {
_noTimeout = true; _pinValue++;
} }
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 idleTimeReport(unsigned millis);
static void informAboutToDeleteBucket(const DiskLoc& b); static void informAboutToDeleteBucket(const DiskLoc& b);
static void aboutToDelete(const DiskLoc& dl); static void aboutToDelete(const DiskLoc& dl);
}; };
} // namespace mongo } // namespace mongo
 End of changes. 9 change blocks. 
7 lines changed or deleted 58 lines changed or added


 commands.h   commands.h 
skipping to change at line 103 skipping to change at line 103
virtual ~Command() {} virtual ~Command() {}
protected: protected:
BSONObj getQuery( const BSONObj& cmdObj ){ BSONObj getQuery( const BSONObj& cmdObj ){
if ( cmdObj["query"].type() == Object ) if ( cmdObj["query"].type() == Object )
return cmdObj["query"].embeddedObject(); return cmdObj["query"].embeddedObject();
if ( cmdObj["q"].type() == Object ) if ( cmdObj["q"].type() == Object )
return cmdObj["q"].embeddedObject(); return cmdObj["q"].embeddedObject();
return BSONObj(); return BSONObj();
} }
};
bool runCommandAgainstRegistered(const char *ns, BSONObj& jsobj, BSONOb static map<string,Command*> * _commands;
jBuilder& anObjBuilder);
public:
static bool runAgainstRegistered(const char *ns, BSONObj& jsobj, BS
ONObjBuilder& anObjBuilder);
static bool readOnly( const string& name );
static Command * findCommand( const string& name );
};
bool _runCommands(const char *ns, BSONObj& jsobj, BufBuilder &b, BSONOb jBuilder& anObjBuilder, bool fromRepl, int queryOptions); bool _runCommands(const char *ns, BSONObj& jsobj, BufBuilder &b, BSONOb jBuilder& anObjBuilder, bool fromRepl, int queryOptions);
} // namespace mongo } // namespace mongo
 End of changes. 2 change blocks. 
3 lines changed or deleted 8 lines changed or added


 concurrency.h   concurrency.h 
skipping to change at line 62 skipping to change at line 62
tl = timeLocked; tl = timeLocked;
} }
}; };
#if BOOST_VERSION >= 103500 #if BOOST_VERSION >= 103500
//#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
our normal/common code path, we never even touch it.
*/
ThreadLocalValue<bool> _releasedEarly;
public: public:
/** /**
* @return * @return
* > 0 write lock * > 0 write lock
* = 0 no lock * = 0 no lock
* < 0 read lock * < 0 read lock
*/ */
int getState(){ return _state.get(); } int getState(){ return _state.get(); }
void assertWriteLocked() { assert( _state.get() > 0 ); } void assertWriteLocked() {
assert( getState() > 0 );
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 , "internal error: locks are not upgradeable", s == 0 );
_state.set(1); _state.set(1);
_m.lock(); _m.lock();
skipping to change at line 92 skipping to change at line 101
_m.lock(); _m.lock();
_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;
} }
assert( s == 1 ); if( s != 1 ) {
if( _releasedEarly.get() ) {
_releasedEarly.set(false);
return;
}
assert(false); // attempt to unlock when wasn't in a write
lock
}
_state.set(0); _state.set(0);
_minfo.leaving(); _minfo.leaving();
_m.unlock(); _m.unlock();
} }
/* unlock (write lock), and when unlock() is called later,
be smart then and don't unlock it again.
*/
void releaseEarly() {
assert( getState() == 1 ); // must not be recursive
assert( !_releasedEarly.get() );
_releasedEarly.set(true);
unlock();
}
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 {
skipping to change at line 138 skipping to change at line 164
_state.set(0); _state.set(0);
_m.unlock_shared(); _m.unlock_shared();
} }
MutexInfo& info() { return _minfo; } MutexInfo& info() { return _minfo; }
}; };
#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;
public: public:
MongoMutex() { } MongoMutex() { }
void lock() { void lock() {
#if BOOST_VERSION >= 103500 #if BOOST_VERSION >= 103500
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 unlock() { void releaseEarly() {
assertWriteLocked(); // aso must not be recursive, although we
don't verify that in the old boost version
assert( !_releasedEarly.get() );
_releasedEarly.set(true);
_unlock();
}
void _unlock() {
_minfo.leaving(); _minfo.leaving();
#if BOOST_VERSION >= 103500 #if BOOST_VERSION >= 103500
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() {
if( _releasedEarly.get() ) {
_releasedEarly.set(false);
return;
}
_unlock();
}
void lock_shared() { lock(); } void lock_shared() { lock(); }
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() );
} }
 End of changes. 8 change blocks. 
3 lines changed or deleted 46 lines changed or added


 cursor.h   cursor.h 
skipping to change at line 84 skipping to change at line 84
*/ */
virtual void noteLocation() { } virtual void noteLocation() { }
/* called before query getmore block is iterated */ /* called before query getmore block is iterated */
virtual void checkLocation() { } virtual void checkLocation() { }
virtual string toString() { virtual string toString() {
return "abstract?"; return "abstract?";
} }
/* used for multikey index traversal to avoid sending back dups. se e JSMatcher::matches(). /* used for multikey index traversal to avoid sending back dups. se e Matcher::matches().
if a multikey index traversal: if a multikey index traversal:
if loc has already been sent, returns true. if loc has already been sent, returns true.
otherwise, marks loc as sent. otherwise, marks loc as sent.
@param deep - match was against an array, so we know it is multi key. this is legacy and kept @param deep - match was against an array, so we know it is multi key. this is legacy and kept
for backwards datafile compatibility. 'deep' can be eliminated next time we for backwards datafile compatibility. 'deep' can be eliminated next time we
force a data file conversion. 7Jul09 force a data file conversion. 7Jul09
*/ */
virtual bool getsetdup(DiskLoc loc) = 0; virtual bool getsetdup(DiskLoc loc) = 0;
virtual BSONObj prettyStartKey() const { return BSONObj(); } virtual BSONObj prettyStartKey() const { return BSONObj(); }
 End of changes. 1 change blocks. 
1 lines changed or deleted 1 lines changed or added


 database.h   database.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 "cmdline.h" #include "cmdline.h"
/* Database represents a database database
Each database database has its own set of files -- dbname.ns, dbname.0,
dbname.1, ...
*/
namespace mongo { namespace mongo {
/**
* Database represents a database database
* Each database database has its own set of files -- dbname.ns, dbname
.0, dbname.1, ...
* 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), { // check db name is valid
namespaceIndex( path, name )
{
{
int L = strlen(nm); int 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;
skipping to change at line 60 skipping to change at line 59
// If already exists, open. Otherwise behave as if empty until // If already exists, open. Otherwise behave as if empty until
// there's a write, then open. // there's a write, then open.
if ( ! newDb || cmdLine.defaultProfile ) { if ( ! newDb || cmdLine.defaultProfile ) {
namespaceIndex.init(); namespaceIndex.init();
if( _openAllFiles ) if( _openAllFiles )
openAllFiles(); openAllFiles();
} }
magic = 781231;
} }
~Database() { ~Database() {
magic = 0;
btreeStore->closeFiles(name, path); btreeStore->closeFiles(name, path);
int n = files.size(); int n = files.size();
for ( int i = 0; i < n; i++ ) for ( int i = 0; i < n; i++ )
delete files[i]; delete files[i];
} }
/**
* tries to make sure that this hasn't been deleted
*/
bool isOk(){
return magic == 781231;
}
bool isEmpty(){
return ! namespaceIndex.allocated();
}
bool exists(int n) { bool exists(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) / ss.str();
return boost::filesystem::exists(fullName); return boost::filesystem::exists(fullName);
} }
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
// until a write is requested
if ( n > 1 && getFile( n - 1 )->getHeader()->isEmpty() ) {
delete files[ n - 1 ];
files.pop_back();
}
} }
MongoDataFile* getFile( int n, int sizeNeeded = 0, bool preallocate Only = false ) { MongoDataFile* getFile( int n, int sizeNeeded = 0, bool preallocate Only = false ) {
assert(this); assert(this);
namespaceIndex.init(); namespaceIndex.init();
if ( n < 0 || n >= DiskLoc::MaxFiles ) { if ( n < 0 || n >= DiskLoc::MaxFiles ) {
out() << "getFile(): n=" << n << endl; out() << "getFile(): n=" << n << endl;
#if !defined(_RECSTORE) #if !defined(_RECSTORE)
if( n >= RecCache::Base && n <= RecCache::Base+1000 ) if( n >= RecCache::Base && n <= RecCache::Base+1000 )
skipping to change at line 185 skipping to change at line 203
bool setProfilingLevel( int newLevel , string& errmsg ); bool setProfilingLevel( int newLevel , string& errmsg );
void finishInit(); void finishInit();
vector<MongoDataFile*> files; vector<MongoDataFile*> files;
string name; // "alleyinsider" string name; // "alleyinsider"
string path; string path;
NamespaceIndex namespaceIndex; NamespaceIndex namespaceIndex;
int profile; // 0=off. int profile; // 0=off.
string profileName; // "alleyinsider.system.profile" string profileName; // "alleyinsider.system.profile"
int magic; // used for making sure the object is still loaded in me mory
}; };
} // namespace mongo } // namespace mongo
 End of changes. 8 change blocks. 
13 lines changed or deleted 31 lines changed or added


 db.h   db.h 
skipping to change at line 44 skipping to change at line 44
Also note that the server has some basic checks to enforce this limi t but those checks are not exhaustive Also note that the server has some basic checks to enforce this limi t but those checks are not exhaustive
for example need to check for size too big after for example need to check for size too big after
update $push (append) operation update $push (append) operation
various db.eval() type operations various db.eval() type operations
Note also we sometimes do work with objects slightly larger - an obj ect in the replication local.oplog Note also we sometimes do work with objects slightly larger - an obj ect in the replication local.oplog
could be slightly larger. could be slightly larger.
*/ */
const int MaxBSONObjectSize = 4 * 1024 * 1024; const int MaxBSONObjectSize = 4 * 1024 * 1024;
// tempish...move to TLS or pass all the way down as a parm /**
extern map<string,Database*> databases; * class to hold path + dbname -> Database
extern bool master; * might be able to optimizer further
*/
class DatabaseHolder {
public:
DatabaseHolder() : _size(0){
}
/* sometimes we deal with databases with the same name in different dir Database * get( const string& ns , const string& path ){
ectories - thus this */ dbMutex.assertAtLeastReadLocked();
inline string makeDbKeyStr( const char *ns, const string& path ) { map<string,Database*>& m = _paths[path];
char cl[256];
nsToClient(ns, cl);
return string( cl ) + ":" + path;
}
inline void resetClient(const char *ns, const string& path=dbpath) { string db = _todb( ns );
dbMutex.assertAtLeastReadLocked();
string key = makeDbKeyStr( ns, path ); map<string,Database*>::iterator it = m.find(db);
map<string,Database*>::iterator it = databases.find(key); if ( it != m.end() )
if ( it != databases.end() ) { return it->second;
cc().setns(ns, it->second); return 0;
return;
} }
assert(false);
} void put( const string& ns , const string& path , Database * db ){
dbMutex.assertWriteLocked();
map<string,Database*>& m = _paths[path];
Database*& d = m[_todb(ns)];
if ( ! d )
_size++;
d = db;
}
void erase( const string& ns , const string& path ){
dbMutex.assertWriteLocked();
map<string,Database*>& m = _paths[path];
_size -= m.erase( _todb( ns ) );
}
bool closeAll( const string& path , BSONObjBuilder& result );
int size(){
return _size;
}
/**
* gets all unique db names, ignoring paths
*/
void getAllShortNames( set<string>& all ) const{
dbMutex.assertAtLeastReadLocked();
for ( map<string, map<string,Database*> >::const_iterator i=_pa
ths.begin(); i!=_paths.end(); i++ ){
map<string,Database*> m = i->second;
for( map<string,Database*>::const_iterator j=m.begin(); j!=
m.end(); j++ ){
all.insert( j->first );
}
}
}
private:
string _todb( const string& ns ){
size_t i = ns.find( '.' );
if ( i == string::npos )
return ns;
return ns.substr( 0 , i );
}
map<string, map<string,Database*> > _paths;
int _size;
};
extern DatabaseHolder dbHolder;
/* returns true if the database ("database") did not exist, and it was created on this call /* 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 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). name in different places (for example temp ones on repair).
*/ */
inline bool setClient(const char *ns, const string& path=dbpath, mongol ock *lock = 0) { inline bool setClient(const char *ns, const string& path , mongolock *l ock ) {
if( logLevel > 5 ) if( logLevel > 5 )
log() << "setClient: " << ns << endl; log() << "setClient: " << ns << endl;
dbMutex.assertAtLeastReadLocked(); dbMutex.assertAtLeastReadLocked();
cc().top.clientStart( ns ); Client& c = cc();
c.top.clientStart( ns );
string key = makeDbKeyStr( ns, path ); Database * db = dbHolder.get( ns , path );
map<string,Database*>::iterator it = databases.find(key); if ( db ){
if ( it != databases.end() ) { c.setns(ns, db );
cc().setns(ns, it->second);
return false; return false;
} }
if( lock ) if( lock )
lock->releaseAndWriteLock(); lock->releaseAndWriteLock();
// when master for replication, we advertise all the db's, and that
// looks like a 'first operation'. so that breaks this log message'
s
// meaningfulness. instead of fixing (which would be better), we j
ust
// stop showing for now.
// 2008-12-22 We now open every database on startup, so this log is
// no longer helpful. Commenting.
// if( !master )
// log() << "first operation for database " << key << endl;
assertInWriteLock(); assertInWriteLock();
char cl[256]; char cl[256];
nsToClient(ns, cl); nsToDatabase(ns, cl);
bool justCreated; bool justCreated;
Database *newdb = new Database(cl, justCreated, path); Database *newdb = new Database(cl, justCreated, path);
databases[key] = newdb; dbHolder.put(ns,path,newdb);
cc().setns(ns, newdb); c.setns(ns, newdb);
newdb->finishInit(); newdb->finishInit();
return justCreated; return justCreated;
} }
// shared functionality for removing references to a database from this pro // shared functionality for removing references to a database from this
gram instance 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 );
/* remove database from the databases map */
inline void eraseDatabase( const char *ns, const string& path=dbpath )
{
string key = makeDbKeyStr( ns, path );
databases.erase( key );
}
inline bool clientIsEmpty() {
return !cc().database()->namespaceIndex.allocated();
}
struct dbtemprelease { struct dbtemprelease {
string clientname; string clientname;
string clientpath; string clientpath;
int locktype; int locktype;
dbtemprelease() { dbtemprelease() {
Client& client = cc(); Client& client = cc();
Database *database = client.database(); Database *database = client.database();
if ( database ) { if ( database ) {
clientname = database->name; clientname = database->name;
clientpath = database->path; clientpath = database->path;
 End of changes. 12 change blocks. 
53 lines changed or deleted 81 lines changed or added


 dbclient.h   dbclient.h 
skipping to change at line 37 skipping to change at line 37
/** the query field 'options' can have these bits set: */ /** the query field 'options' can have these bits set: */
enum QueryOptions { enum QueryOptions {
/** Tailable means cursor is not closed when the last data is retri eved. rather, the cursor marks /** Tailable means cursor is not closed when the last data is retri eved. rather, the cursor marks
the final object's position. you can resume using the cursor la ter, from where it was located, the final object's position. you can resume using the cursor la ter, from where it was located,
if more data were received. Set on dbQuery and dbGetMore. if more data were received. Set on dbQuery and dbGetMore.
like any "latent cursor", the cursor may become invalid at some point -- for example if that like any "latent cursor", the cursor may become invalid at some point -- for example if that
final object it references were deleted. Thus, you should be pr epared to requery if you get back final object it references were deleted. Thus, you should be pr epared to requery if you get back
ResultFlag_CursorNotFound. ResultFlag_CursorNotFound.
*/ */
Option_CursorTailable = 1 << 1, QueryOption_CursorTailable = 1 << 1,
/** allow query of replica slave. normally these return an error e xcept for namespace "local". /** allow query of replica slave. normally these return an error e xcept for namespace "local".
*/ */
Option_SlaveOk = 1 << 2, QueryOption_SlaveOk = 1 << 2,
Option_OplogReplay = 1 << 3, // findingStart mode is used to find the first operation of interes
t when
// we are scanning through a repl log. For efficiency in the commo
n case,
// where the first operation of interest is closer to the tail than
the head,
// we start from the tail of the log and work backwards until we fi
nd the
// first operation of interest. Then we scan forward from that fir
st operation,
// actually returning results to the client. During the findingSta
rt phase,
// we release the db mutex occasionally to avoid blocking the db pr
ocess for
// an extended period of time.
QueryOption_OplogReplay = 1 << 3,
/** The server normally times out idle cursors after an inactivy pe
riod to prevent excess memory use
Set this option to prevent that.
*/
QueryOption_NoCursorTimeout = 1 << 4,
/** Use with QueryOption_CursorTailable. If we are at the end of t
he data, block for a while rather
than returning no data. After a timeout period, we do return as
normal.
*/
QueryOption_AwaitData = 1 << 5
/** if there is a cursor, ignore the normal cursor timeout behavior
and never time it out
*/
Option_NoCursorTimeout = 1 << 4
}; };
enum UpdateOptions { enum UpdateOptions {
Option_Upsert = 1 << 0, /** Upsert - that is, insert the item if no matching item is found.
Option_Multi = 1 << 1 */
UpdateOption_Upsert = 1 << 0,
/** Update multiple documents (if multiple documents match query ex
pression).
(Default is update a single document and stop.) */
UpdateOption_Multi = 1 << 1
}; };
class BSONObj; class BSONObj;
/** Represents a Mongo query expression. Typically one uses the QUERY( ...) macro to construct a Query object. /** Represents a Mongo query expression. Typically one uses the QUERY( ...) macro to construct a Query object.
Examples: Examples:
QUERY( "age" << 33 << "school" << "UCLA" ).sort("name") QUERY( "age" << 33 << "school" << "UCLA" ).sort("name")
QUERY( "age" << GT << 30 << LT << 50 ) QUERY( "age" << GT << 30 << LT << 50 )
*/ */
class Query { class Query {
skipping to change at line 184 skipping to change at line 203
virtual ~DBConnector() {} virtual ~DBConnector() {}
virtual bool call( Message &toSend, Message &response, bool assertO k=true ) = 0; virtual bool call( Message &toSend, Message &response, bool assertO k=true ) = 0;
virtual void say( Message &toSend ) = 0; virtual void say( Message &toSend ) = 0;
virtual void sayPiggyBack( Message &toSend ) = 0; virtual void sayPiggyBack( Message &toSend ) = 0;
virtual void checkResponse( const string &data, int nReturned ) {} virtual void checkResponse( const string &data, int nReturned ) {}
}; };
/** 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();
public: public:
/** if true, safe to call next() */ /** If true, safe to call next(). Requests more from server if necessary. */
bool more(); bool more();
/** next /** next
@return next object in the result cursor. @return next object in the result cursor.
on an error at the remote server, you will get back: on an error at the remote server, you will get back:
{ $err: <string> } { $err: <string> }
if you do not want to handle that yourself, call nextSafe(). if you do not want to handle that yourself, call nextSafe().
*/ */
BSONObj next(); BSONObj next();
skipping to change at line 226 skipping to change at line 246
/** cursor no longer valid -- use with tailable cursors. /** cursor no longer valid -- use with tailable cursors.
note you should only rely on this once more() returns false; note you should only rely on this once more() returns false;
'dead' may be preset yet some data still queued and locally 'dead' may be preset yet some data still queued and locally
available from the dbclientcursor. available from the dbclientcursor.
*/ */
bool isDead() const { bool isDead() const {
return cursorId == 0; return cursorId == 0;
} }
bool tailable() const { bool tailable() const {
return (opts & Option_CursorTailable) != 0; return (opts & QueryOption_CursorTailable) != 0;
} }
bool hasResultFlag( int flag ){ bool hasResultFlag( int flag ){
return (resultFlags & flag) != 0; return (resultFlags & flag) != 0;
} }
private:
bool init();
public: public:
DBClientCursor( DBConnector *_connector, const string &_ns, BSONObj _query, int _nToReturn, DBClientCursor( DBConnector *_connector, const string &_ns, BSONObj _query, int _nToReturn,
int _nToSkip, const BSONObj *_fieldsToReturn, int q ueryOptions ) : int _nToSkip, const BSONObj *_fieldsToReturn, int q ueryOptions ) :
connector(_connector), connector(_connector),
ns(_ns), ns(_ns),
query(_query), query(_query),
nToReturn(_nToReturn), nToReturn(_nToReturn),
nToSkip(_nToSkip), nToSkip(_nToSkip),
fieldsToReturn(_fieldsToReturn), fieldsToReturn(_fieldsToReturn),
opts(queryOptions), opts(queryOptions),
m(new Message()), m(new Message()),
cursorId(), cursorId(),
nReturned(), nReturned(),
pos(), pos(),
data(), data(),
ownCursor_( true ) { _ownCursor( true ) {
} }
DBClientCursor( DBConnector *_connector, const string &_ns, long lo ng _cursorId, int _nToReturn, int options ) : DBClientCursor( DBConnector *_connector, const string &_ns, long lo ng _cursorId, int _nToReturn, int options ) :
connector(_connector), connector(_connector),
ns(_ns), ns(_ns),
nToReturn( _nToReturn ), nToReturn( _nToReturn ),
opts( options ), opts( options ),
m(new Message()), m(new Message()),
cursorId( _cursorId ), cursorId( _cursorId ),
nReturned(), nReturned(),
pos(), pos(),
data(), data(),
ownCursor_( true ) { _ownCursor( true ) {
} }
virtual ~DBClientCursor(); virtual ~DBClientCursor();
long long getCursorId() const { return cursorId; } long long getCursorId() const { return cursorId; }
void decouple() { ownCursor_ = false; }
/** by default we "own" the cursor and will send the server a KillC
ursor
message when ~DBClientCursor() is called. This function overrid
es that.
*/
void decouple() { _ownCursor = false; }
private: private:
DBConnector *connector; DBConnector *connector;
string ns; string ns;
BSONObj query; BSONObj query;
int nToReturn; int nToReturn;
int nToSkip; int nToSkip;
const BSONObj *fieldsToReturn; const BSONObj *fieldsToReturn;
int opts; int opts;
auto_ptr<Message> m; auto_ptr<Message> m;
int resultFlags; int resultFlags;
long long cursorId; long long cursorId;
int nReturned; int nReturned;
int pos; int pos;
const char *data; const char *data;
void dataReceived(); void dataReceived();
void requestMore(); void requestMore();
bool ownCursor_; bool _ownCursor; // see decouple()
}; };
/** /**
The interface that any db connection should implement The interface that any db connection should implement
*/ */
class DBClientInterface : boost::noncopyable { class DBClientInterface : boost::noncopyable {
public: public:
virtual auto_ptr<DBClientCursor> query(const string &ns, Query quer y, int nToReturn = 0, int nToSkip = 0, virtual auto_ptr<DBClientCursor> query(const string &ns, Query quer y, int nToReturn = 0, int nToSkip = 0,
const BSONObj *fieldsToRetur n = 0, int queryOptions = 0) = 0; const BSONObj *fieldsToRetur n = 0, int queryOptions = 0) = 0;
 End of changes. 13 change blocks. 
17 lines changed or deleted 52 lines changed or added


 dbhelpers.h   dbhelpers.h 
skipping to change at line 76 skipping to change at line 76
/* fetch a single object from collection ns that matches query. /* fetch a single object from collection ns that matches query.
set your db SavedContext first. set your db SavedContext first.
@param requireIndex if true, complain if no index for the query. a way to guard against @param requireIndex if true, complain if no index for the query. a way to guard against
writing a slow query. writing a slow query.
@return true if object found @return true if object found
*/ */
static bool findOne(const char *ns, BSONObj query, BSONObj& result, bool requireIndex = false); static bool findOne(const char *ns, BSONObj query, BSONObj& result, bool requireIndex = false);
static bool findById(const char *ns, BSONObj query, BSONObj& result /**
); * @param foundIndex if passed in will be set to 1 if ns and index
found
* @return true if object found
*/
static bool findById(Client&, const char *ns, BSONObj query, BSONOb
j& result ,
bool * nsFound = 0 , bool * indexFound = 0 );
static auto_ptr<CursorIterator> find( const char *ns , BSONObj quer y = BSONObj() , bool requireIndex = false ); static auto_ptr<CursorIterator> find( const char *ns , BSONObj quer y = BSONObj() , bool requireIndex = false );
/* Get/put the first object from a collection. Generally only usef ul if the collection /* Get/put the first object from a collection. Generally only usef ul if the collection
only ever has a single object -- which is a "singleton collectio n". only ever has a single object -- which is a "singleton collectio n".
You do not need to set the database before calling. You do not need to set the database before calling.
Returns: true if object exists. Returns: true if object exists.
*/ */
skipping to change at line 99 skipping to change at line 104
/* Remove all objects from a collection. /* Remove all objects from a collection.
You do not need to set the database before calling. You do not need to set the database before calling.
*/ */
static void emptyCollection(const char *ns); static void emptyCollection(const char *ns);
}; };
class Database; class Database;
/* 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 if fi
xed up.
*/
class DBContext {
Database *olddb;
string oldns;
public:
DBContext(const char *ns) {
olddb = cc().database();
oldns = cc().ns();
setClient(ns);
}
DBContext(string ns) {
olddb = cc().database();
oldns = cc().ns();
setClient(ns.c_str());
}
/* this version saves the context but doesn't yet set the new one:
*/
DBContext() {
olddb = cc().database();
oldns = cc().ns(); }
~DBContext() {
cc().setns(oldns.c_str(), olddb);
}
};
// manage a set using collection backed storage // manage a set using collection backed storage
class DbSet { class DbSet {
public: public:
DbSet( const string &name = "", const BSONObj &key = BSONObj() ) : DbSet( const string &name = "", const BSONObj &key = BSONObj() ) :
name_( name ), name_( name ),
key_( key.getOwned() ) { key_( key.getOwned() ) {
} }
~DbSet(); ~DbSet();
void reset( const string &name = "", const BSONObj &key = BSONObj() ); void reset( const string &name = "", const BSONObj &key = BSONObj() );
bool get( const BSONObj &obj ) const; bool get( const BSONObj &obj ) const;
 End of changes. 2 change blocks. 
33 lines changed or deleted 8 lines changed or added


 dbmessage.h   dbmessage.h 
skipping to change at line 41 skipping to change at line 41
int startingFrom; int startingFrom;
int nReturned; int nReturned;
list of marshalled JSObjects; list of marshalled JSObjects;
*/ */
extern bool objcheck; extern bool objcheck;
#pragma pack(1) #pragma pack(1)
struct QueryResult : public MsgData { struct QueryResult : public MsgData {
enum ResultFlagType { enum ResultFlagType {
ResultFlag_CursorNotFound = 1, /* returned, with zero results /* returned, with zero results, when getMore is called but the
, when getMore is called but the cursor id is not valid at the server. */ cursor id
ResultFlag_ErrSet = 2, /* { $err : ... } is being ret is not valid at the server. */
urned */ ResultFlag_CursorNotFound = 1,
ResultFlag_ShardConfigStale = 4 /* have to update config from
the server, usually $err is also set */ /* { $err : ... } is being returned */
ResultFlag_ErrSet = 2,
/* Have to update config from the server, usually $err is also
set */
ResultFlag_ShardConfigStale = 4,
/* for backward compatability: this let's us know the server su
pports
the QueryOption_AwaitData option. if it doesn't, a repl slav
e client should sleep
a little between getMore's.
*/
ResultFlag_AwaitCapable = 8
}; };
long long cursorId; long long cursorId;
int startingFrom; int startingFrom;
int nReturned; int nReturned;
const char *data() { const char *data() {
return (char *) (((int *)&nReturned)+1); return (char *) (((int *)&nReturned)+1);
} }
int& resultFlags() { int resultFlags() {
return dataAsInt();
}
int& _resultFlags() {
return dataAsInt(); return dataAsInt();
} }
void setResultFlagsToOk() {
_resultFlags() = 0; // ResultFlag_AwaitCapable
}
}; };
#pragma pack() #pragma pack()
/* For the database/server protocol, these objects and functions encaps ulate /* For the database/server protocol, these objects and functions encaps ulate
the various messages transmitted over the connection. the various messages transmitted over the connection.
*/ */
class DbMessage { class DbMessage {
public: public:
DbMessage(const Message& _m) : m(_m) { DbMessage(const Message& _m) : m(_m) {
skipping to change at line 198 skipping to change at line 216
inline void replyToQuery(int queryResultFlags, inline void replyToQuery(int queryResultFlags,
AbstractMessagingPort* p, Message& requestMsg, AbstractMessagingPort* p, Message& requestMsg,
void *data, int size, void *data, int size,
int nReturned, int startingFrom = 0, int nReturned, int startingFrom = 0,
long long cursorId = 0 long long cursorId = 0
) { ) {
BufBuilder b(32768); BufBuilder b(32768);
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 = new Message();
resp->setData(qr, true); // transport will free resp->setData(qr, true); // transport will free
p->reply(requestMsg, *resp, requestMsg.data->id); p->reply(requestMsg, *resp, requestMsg.data->id);
} }
skipping to change at line 235 skipping to change at line 253
} }
/* helper to do a reply using a DbResponse object */ /* helper to do a reply using a DbResponse object */
inline void replyToQuery(int queryResultFlags, Message &m, DbResponse & dbresponse, BSONObj obj) { inline void replyToQuery(int queryResultFlags, Message &m, DbResponse & dbresponse, BSONObj obj) {
BufBuilder b; BufBuilder b;
b.skip(sizeof(QueryResult)); b.skip(sizeof(QueryResult));
b.append((void*) obj.objdata(), obj.objsize()); b.append((void*) obj.objdata(), obj.objsize());
QueryResult* msgdata = (QueryResult *) b.buf(); QueryResult* msgdata = (QueryResult *) b.buf();
b.decouple(); b.decouple();
QueryResult *qr = msgdata; QueryResult *qr = msgdata;
qr->resultFlags() = queryResultFlags; qr->_resultFlags() = queryResultFlags;
qr->len = b.len(); qr->len = b.len();
qr->setOperation(opReply); qr->setOperation(opReply);
qr->cursorId = 0; qr->cursorId = 0;
qr->startingFrom = 0; qr->startingFrom = 0;
qr->nReturned = 1; qr->nReturned = 1;
Message *resp = new Message(); Message *resp = new Message();
resp->setData(msgdata, true); // transport will free resp->setData(msgdata, true); // transport will free
dbresponse.response = resp; dbresponse.response = resp;
dbresponse.responseTo = m.data->id; dbresponse.responseTo = m.data->id;
} }
 End of changes. 5 change blocks. 
9 lines changed or deleted 28 lines changed or added


 extsort.h   extsort.h 
skipping to change at line 22 skipping to change at line 22
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details. * GNU Affero General Public License for more details.
* *
* You should have received a copy of the GNU Affero General Public Licen se * You should have received a copy of the GNU Affero General Public Licen se
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "../stdafx.h" #include "../stdafx.h"
#include "jsobj.h" #include "jsobj.h"
#include "namespace.h" #include "namespace.h"
#include "curop.h"
#include <map>
namespace mongo { namespace mongo {
/** /**
for sorting by BSONObj and attaching a value for sorting by BSONObj and attaching a value
*/ */
class BSONObjExternalSorter : boost::noncopyable { class BSONObjExternalSorter : boost::noncopyable {
public: public:
typedef pair<BSONObj,DiskLoc> Data; typedef pair<BSONObj,DiskLoc> Data;
skipping to change at line 55 skipping to change at line 53
private: private:
MemoryMappedFile _file; MemoryMappedFile _file;
char * _buf; char * _buf;
char * _end; char * _end;
}; };
class MyCmp { class MyCmp {
public: public:
MyCmp( const BSONObj & order = BSONObj() ) : _order( order ){} MyCmp( const BSONObj & order = BSONObj() ) : _order( order ){}
bool operator()( const Data &l, const Data &r ) const { bool operator()( const Data &l, const Data &r ) const {
RARELY killCurrentOp.checkForInterrupt();
_compares++; _compares++;
int x = l.first.woCompare( r.first , _order ); int x = l.first.woCompare( r.first , _order );
if ( x ) if ( x )
return x < 0; return x < 0;
return l.second.compare( r.second ) < 0; return l.second.compare( r.second ) < 0;
}; };
private: private:
BSONObj _order; BSONObj _order;
}; };
 End of changes. 3 change blocks. 
3 lines changed or deleted 2 lines changed or added


 file_allocator.h   file_allocator.h 
skipping to change at line 58 skipping to change at line 58
} }
// May be called if file exists. If file exists, or its allocation has // May be called if file exists. If file exists, or its allocation has
// been requested, size is updated to match existing file size. // been requested, size is updated to match existing file size.
void requestAllocation( const string &name, long &size ) { void requestAllocation( const string &name, long &size ) {
/* Some of the system calls in the file allocator don't work in win, /* Some of the system calls in the file allocator don't work in win,
so no win support - 32 or 64 bit. Plus we don't seem to nee d preallocation so no win support - 32 or 64 bit. Plus we don't seem to nee d preallocation
on windows anyway as we don't have to pre-zero the file ther e. on windows anyway as we don't have to pre-zero the file ther e.
*/ */
#if !defined(_WIN32) #if !defined(_WIN32)
boostlock lk( pendingMutex_ ); boostlock lk( pendingMutex_ );
if ( failed_ )
return;
long oldSize = prevSize( name ); long oldSize = prevSize( name );
if ( oldSize != -1 ) { if ( oldSize != -1 ) {
size = oldSize; size = oldSize;
return; return;
} }
pending_.push_back( name ); pending_.push_back( name );
pendingSize_[ name ] = size; pendingSize_[ name ] = size;
pendingUpdated_.notify_all(); pendingUpdated_.notify_all();
#endif #endif
} }
skipping to change at line 79 skipping to change at line 81
// updated to match existing file size. // updated to match existing file size.
void allocateAsap( const string &name, long &size ) { void allocateAsap( const string &name, long &size ) {
#if !defined(_WIN32) #if !defined(_WIN32)
boostlock lk( pendingMutex_ ); boostlock lk( pendingMutex_ );
long oldSize = prevSize( name ); long oldSize = prevSize( name );
if ( oldSize != -1 ) { if ( oldSize != -1 ) {
size = oldSize; size = oldSize;
if ( !inProgress( name ) ) if ( !inProgress( name ) )
return; return;
} }
checkFailure();
pendingSize_[ name ] = size; pendingSize_[ name ] = size;
if ( pending_.size() == 0 ) if ( pending_.size() == 0 )
pending_.push_back( name ); pending_.push_back( name );
else if ( pending_.front() != name ) { else if ( pending_.front() != name ) {
pending_.remove( name ); pending_.remove( name );
list< string >::iterator i = pending_.begin(); list< string >::iterator i = pending_.begin();
++i; ++i;
pending_.insert( i, name ); pending_.insert( i, name );
} }
pendingUpdated_.notify_all(); pendingUpdated_.notify_all();
while( inProgress( name ) ) while( inProgress( name ) ) {
checkFailure();
pendingUpdated_.wait( lk ); pendingUpdated_.wait( lk );
}
#endif #endif
} }
void waitUntilFinished() const { void waitUntilFinished() const {
#if !defined(_WIN32) #if !defined(_WIN32)
if ( failed_ ) if ( failed_ )
return; return;
boostlock lk( pendingMutex_ ); boostlock lk( pendingMutex_ );
while( pending_.size() != 0 ) while( pending_.size() != 0 )
pendingUpdated_.wait( lk ); pendingUpdated_.wait( lk );
#endif #endif
} }
private: private:
#if !defined(_WIN32) #if !defined(_WIN32)
void checkFailure() {
massert( 12520, "file allocation failure", !failed_ );
}
// caller must hold pendingMutex_ lock. Returns size if allocated or // caller must hold pendingMutex_ lock. Returns size if allocated or
// allocation requested, -1 otherwise. // allocation requested, -1 otherwise.
long prevSize( const string &name ) const { long prevSize( const string &name ) const {
if ( pendingSize_.count( name ) > 0 ) if ( pendingSize_.count( name ) > 0 )
return pendingSize_[ name ]; return pendingSize_[ name ];
if ( boost::filesystem::exists( name ) ) if ( boost::filesystem::exists( name ) )
return boost::filesystem::file_size( name ); return boost::filesystem::file_size( name );
return -1; return -1;
} }
skipping to change at line 199 skipping to change at line 208
} }
close( fd ); close( fd );
} catch ( ... ) { } catch ( ... ) {
problem() << "Failed to allocate new file: " << name problem() << "Failed to allocate new file: " << name
<< ", size: " << size << ", aborting. " << endl; << ", size: " << size << ", aborting. " << endl;
try { try {
BOOST_CHECK_EXCEPTION( boost::filesystem::r emove( name ) ); BOOST_CHECK_EXCEPTION( boost::filesystem::r emove( name ) );
} catch ( ... ) { } catch ( ... ) {
} }
boostlock lk( a_.pendingMutex_ );
a_.failed_ = true; a_.failed_ = true;
dbexit( EXIT_FS ); // not erasing from pending
a_.pendingUpdated_.notify_all();
return; // no more allocation
} }
{ {
boostlock lk( a_.pendingMutex_ ); boostlock lk( a_.pendingMutex_ );
a_.pendingSize_.erase( name ); a_.pendingSize_.erase( name );
a_.pending_.pop_front(); a_.pending_.pop_front();
a_.pendingUpdated_.notify_all(); a_.pendingUpdated_.notify_all();
} }
} }
} }
 End of changes. 7 change blocks. 
2 lines changed or deleted 14 lines changed or added


 goodies.h   goodies.h 
skipping to change at line 187 skipping to change at line 187
ctime_r(&t, buf); ctime_r(&t, buf);
#endif #endif
buf[24] = 0; // don't want the \n buf[24] = 0; // don't want the \n
} }
#define asctime _asctime_not_threadsafe_ #define asctime _asctime_not_threadsafe_
#define gmtime _gmtime_not_threadsafe_ #define gmtime _gmtime_not_threadsafe_
#define localtime _localtime_not_threadsafe_ #define localtime _localtime_not_threadsafe_
#define ctime _ctime_is_not_threadsafe_ #define ctime _ctime_is_not_threadsafe_
#if defined(_WIN32) || defined(__sunos__)
inline void sleepsecs(int s) { inline void sleepsecs(int s) {
boost::xtime xt; boost::xtime xt;
boost::xtime_get(&xt, boost::TIME_UTC); boost::xtime_get(&xt, boost::TIME_UTC);
xt.sec += s; xt.sec += s;
boost::thread::sleep(xt); boost::thread::sleep(xt);
} }
inline void sleepmillis(int s) { inline void sleepmillis(int s) {
boost::xtime xt; boost::xtime xt;
boost::xtime_get(&xt, boost::TIME_UTC); boost::xtime_get(&xt, boost::TIME_UTC);
xt.sec += ( s / 1000 ); xt.sec += ( s / 1000 );
skipping to change at line 215 skipping to change at line 216
boost::xtime xt; boost::xtime xt;
boost::xtime_get(&xt, boost::TIME_UTC); boost::xtime_get(&xt, boost::TIME_UTC);
xt.sec += ( s / 1000000 ); xt.sec += ( s / 1000000 );
xt.nsec += ( s % 1000000 ) * 1000; xt.nsec += ( s % 1000000 ) * 1000;
if ( xt.nsec >= 1000000000 ) { if ( xt.nsec >= 1000000000 ) {
xt.nsec -= 1000000000; xt.nsec -= 1000000000;
xt.sec++; xt.sec++;
} }
boost::thread::sleep(xt); boost::thread::sleep(xt);
} }
#else
inline void sleepsecs(int s) {
struct timespec t;
t.tv_sec = s;
t.tv_nsec = 0;
if ( nanosleep( &t , 0 ) ){
cout << "nanosleep failed" << endl;
}
}
inline void sleepmicros(int s) {
struct timespec t;
t.tv_sec = (int)(s / 1000000);
t.tv_nsec = s % 1000000;
if ( nanosleep( &t , 0 ) ){
cout << "nanosleep failed" << endl;
}
}
inline void sleepmillis(int s) {
sleepmicros( s * 1000 );
}
#endif
// note this wraps // note this wraps
inline int tdiff(unsigned told, unsigned tnew) { inline int tdiff(unsigned told, unsigned tnew) {
return WrappingInt::diff(tnew, told); return WrappingInt::diff(tnew, told);
} }
inline unsigned curTimeMillis() { inline unsigned curTimeMillis() {
boost::xtime xt; boost::xtime xt;
boost::xtime_get(&xt, boost::TIME_UTC); boost::xtime_get(&xt, boost::TIME_UTC);
unsigned t = xt.nsec / 1000000; unsigned t = xt.nsec / 1000000;
return (xt.sec & 0xfffff) * 1000 + t; return (xt.sec & 0xfffff) * 1000 + t;
} }
 End of changes. 2 change blocks. 
0 lines changed or deleted 23 lines changed or added


 hashtab.h   hashtab.h 
skipping to change at line 41 skipping to change at line 41
/* you should define: /* you should define:
int Key::hash() return > 0 always. int Key::hash() return > 0 always.
*/ */
template < template <
class Key, class Key,
class Type class Type
> >
class HashTable { class HashTable : boost::noncopyable {
public: public:
const char *name; const char *name;
struct Node { struct Node {
int hash; int hash;
Key k; Key k;
Type value; Type value;
bool inUse() { bool inUse() {
return hash != 0; return hash != 0;
} }
void setUnused() { void setUnused() {
skipping to change at line 64 skipping to change at line 64
} *nodes; } *nodes;
int n; int n;
int maxChain; int maxChain;
int _find(const Key& k, bool& found) { int _find(const Key& k, bool& found) {
found = false; found = false;
int h = k.hash(); int h = k.hash();
int i = h % n; int i = h % n;
int start = i; int start = i;
int chain = 0; int chain = 0;
int firstNonUsed = -1;
while ( 1 ) { while ( 1 ) {
if ( !nodes[i].inUse() ) { if ( !nodes[i].inUse() ) {
return i; if ( firstNonUsed < 0 )
firstNonUsed = i;
} }
if ( nodes[i].hash == h && nodes[i].k == k ) { if ( nodes[i].hash == h && nodes[i].k == k ) {
if ( chain >= 200 )
out() << "warning: hashtable " << name << " long ch
ain " << endl;
found = true; found = true;
return i; return i;
} }
chain++; chain++;
i = (i+1) % n; i = (i+1) % n;
if ( i == start ) { if ( i == start ) {
// shouldn't get here / defensive for infinite loops // shouldn't get here / defensive for infinite loops
out() << "error: hashtable " << name << " is full n:" < < n << endl; out() << "error: hashtable " << name << " is full n:" < < n << endl;
return -1; return -1;
} }
if( chain >= maxChain ) { if( chain >= maxChain ) {
if ( firstNonUsed >= 0 )
return firstNonUsed;
out() << "error: hashtable " << name << " max chain n:" << n << endl; out() << "error: hashtable " << name << " max chain n:" << n << endl;
return -1; return -1;
} }
if ( chain == 200 )
out() << "warning: hashtable " << name << " long chain
" << endl;
} }
} }
public: public:
/* buf must be all zeroes on initialization. */ /* buf must be all zeroes on initialization. */
HashTable(void *buf, int buflen, const char *_name) : name(_name) { HashTable(void *buf, int buflen, const char *_name) : name(_name) {
int m = sizeof(Node); int m = sizeof(Node);
// out() << "hashtab init, buflen:" << buflen << " m:" << m << endl; // out() << "hashtab init, buflen:" << buflen << " m:" << m << endl;
n = buflen / m; n = buflen / m;
if ( (n & 1) == 0 ) if ( (n & 1) == 0 )
skipping to change at line 115 skipping to change at line 120
int i = _find(k, found); int i = _find(k, found);
if ( found ) if ( found )
return &nodes[i].value; return &nodes[i].value;
return 0; return 0;
} }
void kill(const Key& k) { void kill(const Key& k) {
bool found; bool found;
int i = _find(k, found); int i = _find(k, found);
if ( i >= 0 && found ) { if ( i >= 0 && found ) {
//TEMP nodes[i].k.kill(); nodes[i].k.kill();
nodes[i].setUnused(); nodes[i].setUnused();
} }
} }
/* /*
void drop(const Key& k) { void drop(const Key& k) {
bool found; bool found;
int i = _find(k, found); int i = _find(k, found);
if ( i >= 0 && found ) { if ( i >= 0 && found ) {
nodes[i].setUnused(); nodes[i].setUnused();
} }
skipping to change at line 145 skipping to change at line 150
nodes[i].k = k; nodes[i].k = k;
nodes[i].hash = k.hash(); nodes[i].hash = k.hash();
} }
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 );
void iterall( IteratorCallback callback ){
for ( int i=0; i<n; i++ ){
if ( ! nodes[i].inUse() )
continue;
callback( nodes[i].k , nodes[i].value );
}
}
}; };
#pragma pack() #pragma pack()
} // namespace mongo } // namespace mongo
 End of changes. 9 change blocks. 
6 lines changed or deleted 21 lines changed or added


 instance.h   instance.h 
skipping to change at line 104 skipping to change at line 104
delete response; delete response;
} }
}; };
static SockAddr unknownAddress( "0.0.0.0", 0 ); static SockAddr unknownAddress( "0.0.0.0", 0 );
bool assembleResponse( Message &m, DbResponse &dbresponse, const sockad dr_in &client = unknownAddress.sa ); bool assembleResponse( Message &m, DbResponse &dbresponse, const sockad dr_in &client = unknownAddress.sa );
void getDatabaseNames( vector< string > &names ); void getDatabaseNames( vector< string > &names );
// must call with db lock
void registerListenerSocket( int socket );
// --- local client --- // --- local client ---
class DBDirectClient : public DBClientBase { class DBDirectClient : public DBClientBase {
public: public:
virtual auto_ptr<DBClientCursor> query(const string &ns, Query quer y, int nToReturn = 0, int nToSkip = 0, virtual auto_ptr<DBClientCursor> query(const string &ns, Query quer y, int nToReturn = 0, int nToSkip = 0,
const BSONObj *fieldsToRetur n = 0, int queryOptions = 0); const BSONObj *fieldsToRetur n = 0, int queryOptions = 0);
virtual bool isFailed() const { virtual bool isFailed() const {
return false; return false;
 End of changes. 1 change blocks. 
3 lines changed or deleted 0 lines changed or added


 jsobj.h   jsobj.h 
skipping to change at line 522 skipping to change at line 522
char c = (value() + 4)[0]; 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();
} }
/** returns a string that when used as a matcher, would match a sup
er set of regex()
returns "" for complex regular expressions
used to optimize queries in some simple regex cases
that start with '^'
*/
string simpleRegex() const;
/** Retrieve the regex flags (options) for a Regex element */ /** Retrieve the regex flags (options) for a Regex element */
const char *regexFlags() const { const char *regexFlags() const {
const char *p = regex(); const char *p = regex();
return p + strlen(p) + 1; return p + strlen(p) + 1;
} }
/** like operator== but doesn't check the fieldname, /** like operator== but doesn't check the fieldname,
just the value. just the value.
*/ */
bool valuesEqual(const BSONElement& r) const { bool valuesEqual(const BSONElement& r) const {
skipping to change at line 860 skipping to change at line 854
const char *objdata() const { const char *objdata() const {
return _objdata; return _objdata;
} }
/** @return total size of the BSON object in bytes */ /** @return total size of the BSON object in bytes */
int objsize() const { int objsize() const {
return *(reinterpret_cast<const int*>(objdata())); return *(reinterpret_cast<const int*>(objdata()));
} }
bool isValid(); bool isValid();
/** @return if the user is a valid user doc
criter: isValid() no . or $ field names
*/
bool okForStorage() const;
/** @return true if object is empty -- i.e., {} */ /** @return true if object is empty -- i.e., {} */
bool isEmpty() const { bool isEmpty() const {
return objsize() <= 5; return objsize() <= 5;
} }
void dump() const { void dump() const {
out() << hex; out() << hex;
const char *p = objdata(); const char *p = objdata();
for ( int i = 0; i < objsize(); i++ ) { for ( int i = 0; i < objsize(); i++ ) {
out() << i << '\t' << ( 0xff & ( (unsigned) *p ) ); out() << i << '\t' << ( 0xff & ( (unsigned) *p ) );
skipping to change at line 978 skipping to change at line 977
GT = 0x4, GT = 0x4,
opIN = 0x8, // { x : { $in : [1,2,3] } } opIN = 0x8, // { x : { $in : [1,2,3] } }
NE = 0x9, NE = 0x9,
opSIZE = 0x0A, opSIZE = 0x0A,
opALL = 0x0B, opALL = 0x0B,
NIN = 0x0C, NIN = 0x0C,
opEXISTS = 0x0D, opEXISTS = 0x0D,
opMOD = 0x0E, opMOD = 0x0E,
opTYPE = 0x0F, opTYPE = 0x0F,
opREGEX = 0x10, opREGEX = 0x10,
opOPTIONS = 0x11 opOPTIONS = 0x11,
opELEM_MATCH = 0x12
}; };
}; };
ostream& operator<<( ostream &s, const BSONObj &o ); ostream& operator<<( ostream &s, const BSONObj &o );
ostream& operator<<( ostream &s, const BSONElement &e ); ostream& operator<<( ostream &s, const BSONElement &e );
struct BSONArray: BSONObj { struct BSONArray: BSONObj {
// Don't add anything other than forwarding constructors!!! // Don't add anything other than forwarding constructors!!!
BSONArray(): BSONObj() {} BSONArray(): BSONObj() {}
explicit BSONArray(const BSONObj& obj): BSONObj(obj) {} explicit BSONArray(const BSONObj& obj): BSONObj(obj) {}
}; };
skipping to change at line 1339 skipping to change at line 1339
b.append( fieldName ); b.append( fieldName );
b.append( (unsigned long long) 0 ); b.append( (unsigned long long) 0 );
} }
void appendTimestamp( const char *fieldName , unsigned long long va l ) { void appendTimestamp( const char *fieldName , unsigned long long va l ) {
b.append( (char) Timestamp ); b.append( (char) Timestamp );
b.append( fieldName ); b.append( fieldName );
b.append( val ); b.append( val );
} }
/**
* @param time - in millis (but stored in seconds)
*/
void appendTimestamp( const char *fieldName , unsigned long long ti me , unsigned int inc ){ void appendTimestamp( const char *fieldName , unsigned long long ti me , unsigned int inc ){
OpTime t( (unsigned) (time / 1000) , inc ); OpTime t( (unsigned) (time / 1000) , inc );
appendTimestamp( fieldName , t.asDate() ); appendTimestamp( fieldName , t.asDate() );
} }
/* Deprecated (but supported) */ /* Deprecated (but supported) */
void appendDBRef( const char *fieldName, const char *ns, const OID &oid ) { void appendDBRef( const char *fieldName, const char *ns, const OID &oid ) {
b.append( (char) DBRef ); b.append( (char) DBRef );
b.append( fieldName ); b.append( fieldName );
b.append( (int) strlen( ns ) + 1 ); b.append( (int) strlen( ns ) + 1 );
 End of changes. 4 change blocks. 
9 lines changed or deleted 10 lines changed or added


 lasterror.h   lasterror.h 
skipping to change at line 37 skipping to change at line 37
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; int nObjects;
int nPrev; int nPrev;
bool valid; bool valid;
bool overridenById; bool overridenById;
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, int nChanged ) {
reset( true ); reset( true );
nObjects = nChanged; nObjects = nChanged;
updatedExisting = _updatedExisting ? True : False; updatedExisting = _updatedExisting ? True : False;
} }
skipping to change at line 62 skipping to change at line 63
overridenById = false; overridenById = false;
reset(); reset();
} }
void reset( bool _valid = false ) { void reset( bool _valid = false ) {
code = 0; code = 0;
msg.clear(); msg.clear();
updatedExisting = NotUpdate; updatedExisting = NotUpdate;
nObjects = 0; nObjects = 0;
nPrev = 1; nPrev = 1;
valid = _valid; valid = _valid;
disabled = false;
} }
void appendSelf( BSONObjBuilder &b ); void appendSelf( BSONObjBuilder &b );
static LastError noError; static LastError noError;
}; };
extern class LastErrorHolder { extern class LastErrorHolder {
public: public:
LastErrorHolder() : _id( 0 ){} LastErrorHolder() : _id( 0 ) {}
LastError * get( bool create = false ); LastError * get( bool create = false );
LastError * _get( bool create = false ); // may return a disabled L
astError
void reset( LastError * le ); void reset( LastError * le );
/** /**
* id of 0 means should use thread local management * id of 0 means should use thread local management
*/ */
void setID( int id ); void setID( int id );
int getID(); int getID();
void remove( int id ); void remove( int id );
void release(); void release();
/** when db receives a message/request, call this */ /** when db receives a message/request, call this */
void startRequest( Message& m , LastError * connectionOwned ); void startRequest( Message& m , LastError * connectionOwned );
void startRequest( Message& m ); void startRequest( Message& m );
// used to disable lastError reporting while processing a killCurso
rs message
// disable causes get() to return 0.
LastError *disableForCommand(); // only call once per command invoc
ation!
private: private:
ThreadLocalValue<int> _id; ThreadLocalValue<int> _id;
boost::thread_specific_ptr<LastError> _tl; boost::thread_specific_ptr<LastError> _tl;
struct Status { struct Status {
time_t time; time_t time;
LastError *lerr; LastError *lerr;
}; };
static boost::mutex _idsmutex; static boost::mutex _idsmutex;
map<int,Status> _ids; map<int,Status> _ids;
} lastError; } lastError;
inline void raiseError(int code , const char *msg) { inline void raiseError(int code , const char *msg) {
LastError *le = lastError.get(); LastError *le = lastError.get();
if ( le == 0 ) { if ( le == 0 ) {
DEV log() << "warning: lastError==0 can't report:" << msg << '\ n'; DEV log() << "warning: lastError==0 can't report:" << msg << '\ n';
return; } else if ( le->disabled ) {
log() << "lastError disabled, can't report: " << msg << endl;
} else {
le->raiseError(code, msg);
} }
le->raiseError(code, msg);
} }
inline void recordUpdate( bool updatedExisting, int nChanged ) { inline void recordUpdate( bool updatedExisting, int nChanged ) {
LastError *le = lastError.get(); LastError *le = lastError.get();
if ( le ) if ( le )
le->recordUpdate( updatedExisting, nChanged ); le->recordUpdate( updatedExisting, nChanged );
} }
inline void recordDelete( int nDeleted ) { inline void recordDelete( int nDeleted ) {
LastError *le = lastError.get(); LastError *le = lastError.get();
 End of changes. 7 change blocks. 
3 lines changed or deleted 16 lines changed or added


 matcher.h   matcher.h 
// matcher.h // matcher.h
/* JSMatcher is our boolean expression evaluator for "where" clauses */ /* Matcher is our boolean expression evaluator for "where" clauses */
/** /**
* Copyright (C) 2008 10gen Inc. * Copyright (C) 2008 10gen Inc.
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3 , * it under the terms of the GNU Affero General Public License, version 3 ,
* as published by the Free Software Foundation. * as published by the Free Software Foundation.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
skipping to change at line 29 skipping to change at line 29
*/ */
#pragma once #pragma once
#include "jsobj.h" #include "jsobj.h"
#include <pcrecpp.h> #include <pcrecpp.h>
namespace mongo { namespace mongo {
class CoveredIndexMatcher; class CoveredIndexMatcher;
class Matcher;
class RegexMatcher { class RegexMatcher {
public: public:
const char *fieldName; const char *fieldName;
pcrecpp::RE *re; pcrecpp::RE *re;
RegexMatcher() { RegexMatcher() {
re = 0; re = 0;
} }
~RegexMatcher() { ~RegexMatcher() {
delete re; delete re;
skipping to change at line 53 skipping to change at line 54
{ {
bool operator()(const BSONElement& l, const BSONElement& r) const bool operator()(const BSONElement& l, const BSONElement& r) const
{ {
int x = (int) l.canonicalType() - (int) r.canonicalType(); int x = (int) l.canonicalType() - (int) r.canonicalType();
if ( x < 0 ) return true; if ( x < 0 ) return true;
else if ( x > 0 ) return false; else if ( x > 0 ) return false;
return compareElementValues(l,r) < 0; return compareElementValues(l,r) < 0;
} }
}; };
class BasicMatcher { class ElementMatcher {
public: public:
BasicMatcher(){ ElementMatcher() {
} }
BasicMatcher( BSONElement _e , int _op ) : toMatch( _e ) , compareO ElementMatcher( BSONElement _e , int _op );
p( _op ){
if ( _op == BSONObj::opMOD ){
BSONObj o = _e.embeddedObject().firstElement().embeddedObje
ct();
mod = o["0"].numberInt();
modm = o["1"].numberInt();
uassert( 10073 , "mod can't be 0" , mod );
}
else if ( _op == BSONObj::opTYPE ){
type = (BSONType)(_e.embeddedObject().firstElement().number
Int());
}
}
BasicMatcher( BSONElement _e , int _op , const BSONObj& array ) : t oMatch( _e ) , compareOp( _op ){ ElementMatcher( BSONElement _e , int _op , const BSONObj& array ) : toMatch( _e ) , compareOp( _op ) {
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();
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;
}; };
// SQL where clause equivalent // SQL where clause equivalent
class Where; 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:
{ a : { $gt : 3 } } { a : { $gt : 3 } }
Not equal: Not equal:
{ a : { $ne : 3 } } { a : { $ne : 3 } }
TODO: we should rewrite the matcher to be more an AST style. TODO: we should rewrite the matcher to be more an AST style.
*/ */
class JSMatcher : boost::noncopyable { class Matcher : boost::noncopyable {
int matchesDotted( int matchesDotted(
const char *fieldName, const char *fieldName,
const BSONElement& toMatch, const BSONObj& obj, const BSONElement& toMatch, const BSONObj& obj,
int compareOp, const BasicMatcher& bm, bool isArr = false); int compareOp, const ElementMatcher& bm, bool isArr = false);
int matchesNe( int matchesNe(
const char *fieldName, const char *fieldName,
const BSONElement &toMatch, const BSONObj &obj, const BSONElement &toMatch, const BSONObj &obj,
const BasicMatcher&bm); const ElementMatcher&bm);
public: public:
static int opDirection(int op) { static int opDirection(int op) {
return op <= BSONObj::LTE ? -1 : 1; return op <= BSONObj::LTE ? -1 : 1;
} }
// Only specify constrainIndexKey if matches() will be called with // Only specify constrainIndexKey if matches() will be called with
// index keys having empty string field names. // index keys having empty string field names.
JSMatcher(const BSONObj &pattern, const BSONObj &constrainIndexKey = BSONObj()); Matcher(const BSONObj &pattern, const BSONObj &constrainIndexKey = BSONObj());
~JSMatcher(); ~Matcher();
bool matches(const BSONObj& j); bool matches(const BSONObj& j);
bool keyMatch() const { return !all && !haveSize && !hasArray; } bool keyMatch() const { return !all && !haveSize && !hasArray; }
bool atomic() const { return _atomic; } bool atomic() const { return _atomic; }
private: private:
void addBasic(const BSONElement &e, int c) { void addBasic(const BSONElement &e, int c) {
// TODO May want to selectively ignore these element types base d on op type. // TODO May want to selectively ignore these element types base d on op type.
if ( e.type() == MinKey || e.type() == MaxKey ) if ( e.type() == MinKey || e.type() == MaxKey )
return; return;
basics.push_back( BasicMatcher( e , c ) ); basics.push_back( ElementMatcher( e , c ) );
} }
int valuesMatch(const BSONElement& l, const BSONElement& r, int op, const BasicMatcher& 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<BasicMatcher> basics; vector<ElementMatcher> basics;
// int n; // # of basicmatcher items // 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[
*/ */
bool _atomic; bool _atomic;
RegexMatcher regexs[4]; RegexMatcher regexs[4];
int nRegex; int nRegex;
// so we delete the mem when we're done: // so we delete the mem when we're done:
vector< shared_ptr< BSONObjBuilder > > _builders; vector< shared_ptr< BSONObjBuilder > > _builders;
friend class CoveredIndexMatcher; friend class CoveredIndexMatcher;
}; };
// If match succeeds on index key, then attempt to match full document. // If match succeeds on index key, then attempt to match full document.
class CoveredIndexMatcher : boost::noncopyable { class CoveredIndexMatcher : boost::noncopyable {
public: public:
CoveredIndexMatcher(const BSONObj &pattern, const BSONObj &indexKey Pattern); CoveredIndexMatcher(const BSONObj &pattern, const BSONObj &indexKey Pattern);
bool matches(const BSONObj &o){ return _docMatcher.matches( o ); } bool matches(const BSONObj &o){ return _docMatcher.matches( o ); }
bool matches(const BSONObj &key, const DiskLoc &recLoc); bool matches(const BSONObj &key, const DiskLoc &recLoc);
bool needRecord(){ return _needRecord; } bool needRecord(){ return _needRecord; }
JSMatcher& docMatcher() { return _docMatcher; } Matcher& docMatcher() { return _docMatcher; }
private: private:
JSMatcher _keyMatcher; Matcher _keyMatcher;
JSMatcher _docMatcher; Matcher _docMatcher;
bool _needRecord; bool _needRecord;
}; };
} // namespace mongo } // namespace mongo
 End of changes. 19 change blocks. 
30 lines changed or deleted 22 lines changed or added


 mockdbclient.h   mockdbclient.h 
skipping to change at line 69 skipping to change at line 69
virtual void afterCommand() {} virtual void afterCommand() {}
}; };
DirectDBClientConnection( ReplPair *rp, ConnectionCallback *cc = 0 ) : DirectDBClientConnection( ReplPair *rp, ConnectionCallback *cc = 0 ) :
rp_( rp ), rp_( rp ),
cc_( cc ) { cc_( cc ) {
} }
virtual BSONObj findOne(const string &ns, Query query, const BSONObj *f ieldsToReturn = 0, int queryOptions = 0) { virtual BSONObj findOne(const string &ns, Query query, const BSONObj *f ieldsToReturn = 0, int queryOptions = 0) {
if ( cc_ ) cc_->beforeCommand(); if ( cc_ ) cc_->beforeCommand();
SetGlobalReplPair s( rp_ ); SetGlobalReplPair s( rp_ );
BSONObjBuilder result; BSONObjBuilder result;
result.append( "ok", runCommandAgainstRegistered( "admin.$cmd", que ry.obj, result ) ? 1.0 : 0.0 ); result.append( "ok", Command::runAgainstRegistered( "admin.$cmd", q uery.obj, result ) ? 1.0 : 0.0 );
if ( cc_ ) cc_->afterCommand(); if ( cc_ ) cc_->afterCommand();
return result.obj(); return result.obj();
} }
virtual bool connect( const string &serverHostname, string& errmsg ) { virtual bool connect( const string &serverHostname, string& errmsg ) {
return true; return true;
} }
private: private:
ReplPair *rp_; ReplPair *rp_;
ConnectionCallback *cc_; ConnectionCallback *cc_;
class SetGlobalReplPair { class SetGlobalReplPair {
 End of changes. 1 change blocks. 
1 lines changed or deleted 1 lines changed or added


 mvar.h   mvar.h 
skipping to change at line 63 skipping to change at line 63
// unblock threads waiting to 'take' // unblock threads waiting to 'take'
_condition.notify_all(); _condition.notify_all();
return true; return true;
} }
// puts val into the MVar // puts val into the MVar
// will block if the MVar is already full // will block if the MVar is already full
void put(const T& val){ void put(const T& val){
Mutex::scoped_lock lock(_mutex);
while (!tryPut(val)){ while (!tryPut(val)){
// unlocks lock while waiting and relocks before returning // unlocks lock while waiting and relocks before returning
Mutex::scoped_lock lock(_mutex);
_condition.wait(lock); _condition.wait(lock);
} }
} }
// takes val out of the MVar and returns true or returns false if e mpty // takes val out of the MVar and returns true or returns false if e mpty
// never blocks // never blocks
bool tryTake(T& out){ bool tryTake(T& out){
// intentionally repeat test before and after lock // intentionally repeat test before and after lock
if (_state == EMPTY) return false; if (_state == EMPTY) return false;
Mutex::scoped_lock lock(_mutex); Mutex::scoped_lock lock(_mutex);
 End of changes. 2 change blocks. 
1 lines changed or deleted 1 lines changed or added


 namespace.h   namespace.h 
skipping to change at line 36 skipping to change at line 36
#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". */
const int MaxClientLen = 256; // max str len for the db name, including null char const int MaxDatabaseLen = 256; // max str len for the db name, includi ng null char
// "database.a.b.c" -> "database" // "database.a.b.c" -> "database"
inline void nsToClient(const char *ns, char *database) { inline void nsToDatabase(const char *ns, char *database) {
const char *p = ns; const char *p = ns;
char *q = database; char *q = database;
while ( *p != '.' ) { while ( *p != '.' ) {
if ( *p == 0 ) if ( *p == 0 )
break; break;
*q++ = *p++; *q++ = *p++;
} }
*q = 0; *q = 0;
if (q-database>=MaxClientLen) { if (q-database>=MaxDatabaseLen) {
log() << "nsToClient: ns too long. terminating, buf overrun con log() << "nsToDatabase: ns too long. terminating, buf overrun c
dition" << endl; ondition" << endl;
dbexit( EXIT_POSSIBLE_CORRUPTION ); dbexit( EXIT_POSSIBLE_CORRUPTION );
} }
} }
inline string nsToClient(const char *ns) { inline string nsToDatabase(const char *ns) {
char buf[MaxClientLen]; char buf[MaxDatabaseLen];
nsToClient(ns, buf); nsToDatabase(ns, buf);
return buf; return buf;
} }
/* e.g. /* e.g.
NamespaceString ns("acme.orders"); NamespaceString ns("acme.orders");
cout << ns.coll; // "orders" cout << ns.coll; // "orders"
*/ */
class NamespaceString { class NamespaceString {
public: public:
string db; string db;
skipping to change at line 136 skipping to change at line 136
perhaps this should move to the NamespaceString helper? perhaps this should move to the NamespaceString helper?
*/ */
string getSisterNS( const char * local ) { string getSisterNS( const char * local ) {
assert( local && local[0] != '.' ); assert( local && local[0] != '.' );
string old(buf); string old(buf);
if ( old.find( "." ) != string::npos ) if ( old.find( "." ) != string::npos )
old = old.substr( 0 , old.find( "." ) ); old = old.substr( 0 , old.find( "." ) );
return old + "." + local; return old + "." + local;
} }
operator string() const {
return (string)buf;
}
char buf[MaxNsLen]; char buf[MaxNsLen];
}; };
}
#include "index.h"
namespace mongo {
/** /**
@return true if a client can modify this namespace @return true if a client can modify this namespace
things like *.system.users things like *.system.users
*/ */
bool legalClientSystemNS( const string& ns , bool write ); bool legalClientSystemNS( const string& ns , bool write );
/* deleted lists -- linked lists of deleted records -- are placed in 'b uckets' of various sizes /* deleted lists -- linked lists of deleted records -- are placed in 'b uckets' of various sizes
so you can look for a deleterecord about the right size. so you can look for a deleterecord about the right size.
*/ */
const int Buckets = 19; const int Buckets = 19;
const int MaxBucket = 18; const int MaxBucket = 18;
/* Maximum # of indexes per collection. We need to raise this limit
at some point.
(Backward datafile compatibility is main issue with changing.)
*/
// const int MaxIndexes = 10;
/* 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
collection).
*/
class IndexDetails {
public:
DiskLoc head; /* btree head disk location */
/* Location of index info object. Format:
{ name:"nameofindex", ns:"parentnsname", key: {keypattobject}[
, unique: <bool>] }
This object is in the system.indexes collection. Note that sinc
e we
have a pointer to the object here, the object in system.indexes
MUST NEVER MOVE.
*/
DiskLoc info;
/* extract key value from the query object
e.g., if key() == { x : 1 },
{ x : 70, y : 3 } -> { x : 70 }
*/
BSONObj getKeyFromQuery(const BSONObj& query) const {
BSONObj k = keyPattern();
BSONObj res = query.extractFieldsUnDotted(k);
return res;
}
/* pull out the relevant key objects from obj, so we
can index them. Note that the set is multiple elements
only when it's a "multikey" array.
keys will be left empty if key not found in the object.
*/
void getKeysFromObject( const BSONObj& obj, BSONObjSetDefaultOrder&
keys) const;
/* get the key pattern for this object.
e.g., { lastname:1, firstname:1 }
*/
BSONObj keyPattern() const {
return info.obj().getObjectField("key");
}
/* true if the specified key is in the index */
bool hasKey(const BSONObj& key);
// returns name of this index's storage area
// database.table.$index
string indexNamespace() const {
BSONObj io = info.obj();
string s;
s.reserve(Namespace::MaxNsLen);
s = io.getStringField("ns");
assert( !s.empty() );
s += ".$";
s += io.getStringField("name");
return s;
}
string indexName() const { // e.g. "ts_1"
BSONObj io = info.obj();
return io.getStringField("name");
}
static bool isIdIndexPattern( const BSONObj &pattern ) {
BSONObjIterator i(pattern);
BSONElement e = i.next();
if( strcmp(e.fieldName(), "_id") != 0 ) return false;
return i.next().eoo();
}
/* returns true if this is the _id index. */
bool isIdIndex() const {
return isIdIndexPattern( keyPattern() );
}
/* gets not our namespace name (indexNamespace for that),
but the collection we index, its name.
*/
string parentNS() const {
BSONObj io = info.obj();
return io.getStringField("ns");
}
bool unique() const {
BSONObj io = info.obj();
return io["unique"].trueValue() ||
/* temp: can we juse make unique:true always be there for _
id and get rid of this? */
isIdIndex();
}
/* if set, when building index, if any duplicates, drop the duplica
ting object */
bool dropDups() const {
return info.obj().getBoolField( "dropDups" );
}
/* delete this index. does NOT clean up the system catalog
(system.indexes or system.namespaces) -- only NamespaceIndex.
*/
void kill_idx();
operator string() const {
return info.obj().toString();
}
};
extern int bucketSizes[]; extern int bucketSizes[];
/* this is the "header" for a collection that has all its details. in the .ns file. /* this is the "header" for a collection that has all its details. in the .ns file.
*/ */
class NamespaceDetails { class NamespaceDetails {
friend class NamespaceIndex; friend class NamespaceIndex;
enum { NIndexesExtra = 30, enum { NIndexesExtra = 30,
NIndexesBase = 10 NIndexesBase = 10
}; };
struct Extra { struct Extra {
skipping to change at line 309 skipping to change at line 210
capFirstNewRecord.setInvalid(); capFirstNewRecord.setInvalid();
// For capped case, signal that we are doing initial extent all ocation. // For capped case, signal that we are doing initial extent all ocation.
if ( capped ) if ( capped )
deletedList[ 1 ].setInvalid(); deletedList[ 1 ].setInvalid();
assert( sizeof(dataFileVersion) == 2 ); assert( sizeof(dataFileVersion) == 2 );
dataFileVersion = 0; dataFileVersion = 0;
indexFileVersion = 0; indexFileVersion = 0;
multiKeyIndexBits = 0; multiKeyIndexBits = 0;
reservedA = 0; reservedA = 0;
extraOffset = 0; extraOffset = 0;
backgroundIndexBuildInProgress = 0;
memset(reserved, 0, sizeof(reserved)); memset(reserved, 0, sizeof(reserved));
} }
DiskLoc firstExtent; DiskLoc firstExtent;
DiskLoc lastExtent; DiskLoc lastExtent;
/* NOTE: capped collections override the meaning of deleted list. /* NOTE: capped collections override the meaning of deleted list.
deletedList[0] points to a list of free records (DeletedRe cord's) for all extents in deletedList[0] points to a list of free records (DeletedRe cord's) for all extents in
the namespace. the namespace.
deletedList[1] points to the last record in the prev exten t. When the "current extent" deletedList[1] points to the last record in the prev exten t. When the "current extent"
changes, this value is updated. !deletedList[1].isValid() when this value is not changes, this value is updated. !deletedList[1].isValid() when this value is not
skipping to change at line 348 skipping to change at line 250
See filever.h See filever.h
*/ */
unsigned short dataFileVersion; unsigned short dataFileVersion;
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:
char reserved[80]; int backgroundIndexBuildInProgress; // 1 if in prog
char reserved[76];
/* NOTE: be careful with flags. are we manipulating them in read l
ocks? if so,
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];
} }
skipping to change at line 403 skipping to change at line 309
while( i.more() ) { while( i.more() ) {
if( &i.next() == &idx ) if( &i.next() == &idx )
return i.pos()-1; return i.pos()-1;
} }
massert( 10349 , "E12000 idxNo fails", false); massert( 10349 , "E12000 idxNo fails", false);
return -1; return -1;
} }
/* multikey indexes are indexes where there are more than one key i n the index /* multikey indexes are indexes where there are more than one key i n the index
for a single document. see multikey in wiki. for a single document. see multikey in wiki.
for these, we have to do some dedup object on queries. for these, we have to do some dedup work on queries.
*/ */
bool isMultikey(int i) { bool isMultikey(int i) {
return (multiKeyIndexBits & (((unsigned long long) 1) << i)) != 0; return (multiKeyIndexBits & (((unsigned long long) 1) << i)) != 0;
} }
void setIndexIsMultikey(int i) { void setIndexIsMultikey(int i) {
dassert( i < NIndexesMax ); dassert( i < NIndexesMax );
multiKeyIndexBits |= (((unsigned long long) 1) << i); multiKeyIndexBits |= (((unsigned long long) 1) << i);
} }
void clearIndexIsMultikey(int i) { void clearIndexIsMultikey(int i) {
dassert( i < NIndexesMax ); dassert( i < NIndexesMax );
skipping to change at line 454 skipping to change at line 360
//returns offset in indexes[] //returns offset in indexes[]
int findIndexByName(const char *name) { int findIndexByName(const char *name) {
IndexIterator i = ii(); IndexIterator i = ii();
while( i.more() ) { while( i.more() ) {
if ( strcmp(i.next().info.obj().getStringField("name"),name ) == 0 ) if ( strcmp(i.next().info.obj().getStringField("name"),name ) == 0 )
return i.pos()-1; return i.pos()-1;
} }
return -1; return -1;
} }
//returns offset in indexes[]
int findIndexByKeyPattern(const BSONObj& keyPattern) {
IndexIterator i = ii();
while( i.more() ) {
if( i.next().keyPattern() == keyPattern )
return i.pos()-1;
}
return -1;
}
/* @return -1 = not found /* @return -1 = not found
generally id is first index, so not that expensive an operation (assuming present). generally id is first index, so not that expensive an operation (assuming present).
*/ */
int findIdIndex() { int findIdIndex() {
IndexIterator i = ii(); IndexIterator i = ii();
while( i.more() ) { while( i.more() ) {
if( i.next().isIdIndex() ) if( i.next().isIdIndex() )
return i.pos()-1; return i.pos()-1;
} }
return -1; return -1;
skipping to change at line 510 skipping to change at line 426
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;
DiskLoc __stdAlloc(int len); DiskLoc __stdAlloc(int len);
DiskLoc __capAlloc(int len); DiskLoc __capAlloc(int len);
DiskLoc _alloc(const char *ns, int len); DiskLoc _alloc(const char *ns, int len);
void compact(); void compact(); // combine adjacent deleted records
DiskLoc &firstDeletedInCapExtent(); DiskLoc &firstDeletedInCapExtent();
bool nextIsInCapExtent( const DiskLoc &dl ) const; bool nextIsInCapExtent( const DiskLoc &dl ) const;
}; };
#pragma pack() #pragma pack()
/* these are things we know / compute about a namespace that are transi ent -- things /* these are things we know / compute about a namespace that are transi ent -- things
we don't actually store in the .ns file. so mainly caching of frequ ently used we don't actually store in the .ns file. so mainly caching of frequ ently used
information. information.
skipping to change at line 567 skipping to change at line 483
/* get set of index keys for this namespace. handy to quickly chec k if a given /* get set of index keys for this namespace. handy to quickly chec k if a given
field is indexed (Note it might be a secondary component of a co mpound index.) field is indexed (Note it might be a secondary component of a co mpound index.)
*/ */
set<string>& indexKeys() { set<string>& indexKeys() {
DEV assertInWriteLock(); DEV assertInWriteLock();
if ( !_keysComputed ) if ( !_keysComputed )
computeIndexKeys(); computeIndexKeys();
return _indexKeys; return _indexKeys;
} }
/* IndexSpec caching */
private:
map<const IndexDetails*,IndexSpec> _indexSpecs;
public:
const IndexSpec& getIndexSpec( const IndexDetails * details ){
DEV assertInWriteLock();
IndexSpec& spec = _indexSpecs[details];
if ( spec.meta.isEmpty() ){
spec.reset( details->info );
}
return spec;
}
/* query cache (for query optimizer) ------------------------------ ------- */ /* query cache (for query optimizer) ------------------------------ ------- */
private: private:
int _qcWriteCount; int _qcWriteCount;
map< QueryPattern, pair< BSONObj, long long > > _qcCache; map< QueryPattern, pair< BSONObj, long long > > _qcCache;
public: public:
static boost::mutex _qcMutex; static boost::mutex _qcMutex;
/* you must be in the qcMutex when calling this (and using the retu rned val): */ /* you must be in the qcMutex when calling this (and using the retu rned val): */
static NamespaceDetailsTransient& get_inlock(const char *ns) { static NamespaceDetailsTransient& get_inlock(const char *ns) {
return _get(ns); return _get(ns);
} }
 End of changes. 14 change blocks. 
129 lines changed or deleted 50 lines changed or added


 parallel.h   parallel.h 
skipping to change at line 43 skipping to change at line 43
public: public:
ClusteredCursor( QueryMessage& q ); ClusteredCursor( QueryMessage& q );
ClusteredCursor( const string& ns , const BSONObj& q , int options= 0 , const BSONObj& fields=BSONObj() ); ClusteredCursor( const string& ns , const BSONObj& q , int options= 0 , const BSONObj& fields=BSONObj() );
virtual ~ClusteredCursor(); virtual ~ClusteredCursor();
virtual bool more() = 0; virtual bool more() = 0;
virtual BSONObj next() = 0; virtual BSONObj next() = 0;
static BSONObj concatQuery( const BSONObj& query , const BSONObj& e xtraFilter ); static BSONObj concatQuery( const BSONObj& query , const BSONObj& e xtraFilter );
virtual string type() const = 0;
protected: protected:
auto_ptr<DBClientCursor> query( const string& server , int num = 0 , BSONObj extraFilter = BSONObj() ); auto_ptr<DBClientCursor> query( const string& server , int num = 0 , BSONObj extraFilter = BSONObj() );
static BSONObj _concatFilter( const BSONObj& filter , const BSONObj & extraFilter ); static BSONObj _concatFilter( const BSONObj& filter , const BSONObj & extraFilter );
string _ns; string _ns;
BSONObj _query; BSONObj _query;
int _options; int _options;
BSONObj _fields; BSONObj _fields;
skipping to change at line 76 skipping to change at line 78
if ( ! _orderObject.isEmpty() ) if ( ! _orderObject.isEmpty() )
return _orderObject.woCompare( other._orderObject ) < 0; return _orderObject.woCompare( other._orderObject ) < 0;
if ( _server < other._server ) if ( _server < other._server )
return true; return true;
if ( other._server > _server ) if ( other._server > _server )
return false; return false;
return _extra.woCompare( other._extra ) < 0; return _extra.woCompare( other._extra ) < 0;
} }
string toString() const {
StringBuilder ss;
ss << "server:" << _server << " _extra:" << _extra << " _orderO
bject:" << _orderObject;
return ss.str();
}
operator string() const {
return toString();
}
string _server; string _server;
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( set<ServerAndQuery> servers , QueryMes sage& q , int sortOrder=0);
virtual bool more(); virtual bool more();
virtual BSONObj next(); virtual BSONObj next();
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 sage& q , const BSONObj& sortKey ); ParallelSortClusteredCursor( set<ServerAndQuery> servers , QueryMes sage& q , const BSONObj& sortKey );
ParallelSortClusteredCursor( set<ServerAndQuery> servers , const st ring& ns , ParallelSortClusteredCursor( set<ServerAndQuery> servers , const st ring& 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"; }
private: private:
void _init(); void _init();
void advance(); void advance();
int _numServers; int _numServers;
set<ServerAndQuery> _servers; set<ServerAndQuery> _servers;
BSONObj _sortKey; BSONObj _sortKey;
auto_ptr<DBClientCursor> * _cursors; auto_ptr<DBClientCursor> * _cursors;
 End of changes. 4 change blocks. 
0 lines changed or deleted 15 lines changed or added


 pdfile.h   pdfile.h 
skipping to change at line 53 skipping to change at line 53
void dropDatabase(const char *ns); void dropDatabase(const char *ns);
bool repairDatabase(const char *ns, string &errmsg, bool preserveCloned FilesOnFailure = false, bool backupOriginalFiles = false); bool repairDatabase(const char *ns, string &errmsg, bool preserveCloned FilesOnFailure = false, bool backupOriginalFiles = false);
/* low level - only drops this ns */ /* low level - only drops this ns */
void dropNS(const string& dropNs); void dropNS(const string& dropNs);
/* deletes this ns, indexes and cursors */ /* deletes this ns, indexes and cursors */
void dropCollection( const string &name, string &errmsg, BSONObjBuilder &result ); void dropCollection( const string &name, string &errmsg, BSONObjBuilder &result );
bool userCreateNS(const char *ns, BSONObj j, string& err, bool logForRe plication); bool userCreateNS(const char *ns, BSONObj j, string& err, bool logForRe plication);
auto_ptr<Cursor> findTableScan(const char *ns, const BSONObj& order, co nst DiskLoc &startLoc=DiskLoc()); auto_ptr<Cursor> findTableScan(const char *ns, const BSONObj& order, co nst DiskLoc &startLoc=DiskLoc());
void getKeysFromObject( const BSONObj &keyPattern, const BSONObj &obj, BSONObjSetDefaultOrder &keys );
// -1 if library unavailable. // -1 if library unavailable.
boost::intmax_t freeSpace(); boost::intmax_t freeSpace();
/*--------------------------------------------------------------------- */ /*--------------------------------------------------------------------- */
class MDFHeader; class MDFHeader;
class MongoDataFile { class MongoDataFile {
friend class DataFileMgr; friend class DataFileMgr;
friend class BasicCursor; friend class BasicCursor;
skipping to change at line 276 skipping to change at line 275
char data[4]; char data[4];
static int headerSize() { static int headerSize() {
return sizeof(MDFHeader) - 4; return sizeof(MDFHeader) - 4;
} }
bool currentVersion() const { bool currentVersion() const {
return ( version == VERSION ) && ( versionMinor == VERSION_MINO R ); return ( version == VERSION ) && ( versionMinor == VERSION_MINO R );
} }
bool uninitialized() { bool uninitialized() const {
if ( version == 0 ) return true; if ( version == 0 ) return true;
return false; return false;
} }
Record* getRecord(DiskLoc dl) { Record* getRecord(DiskLoc dl) {
int ofs = dl.getOfs(); int ofs = dl.getOfs();
assert( ofs >= headerSize() ); assert( ofs >= headerSize() );
return (Record*) (((char *) this) + ofs); return (Record*) (((char *) this) + ofs);
} }
skipping to change at line 300 skipping to change at line 299
assert( headerSize() == 8192 ); assert( headerSize() == 8192 );
fileLength = filelength; fileLength = filelength;
version = VERSION; version = VERSION;
versionMinor = VERSION_MINOR; versionMinor = VERSION_MINOR;
unused.setOfs( fileno, headerSize() ); unused.setOfs( fileno, headerSize() );
assert( (data-(char*)this) == headerSize() ); assert( (data-(char*)this) == headerSize() );
unusedLength = fileLength - headerSize() - 16; unusedLength = fileLength - headerSize() - 16;
memcpy(data+unusedLength, " \nthe end\n", 16); memcpy(data+unusedLength, " \nthe end\n", 16);
} }
} }
bool isEmpty() const {
return uninitialized() || ( unusedLength == fileLength - header
Size() - 16 );
}
}; };
#pragma pack() #pragma pack()
inline Extent* MongoDataFile::_getExtent(DiskLoc loc) { inline Extent* MongoDataFile::_getExtent(DiskLoc loc) {
loc.assertOk(); loc.assertOk();
Extent *e = (Extent *) (((char *)header) + loc.getOfs()); Extent *e = (Extent *) (((char *)header) + loc.getOfs());
return e; return e;
} }
skipping to change at line 410 skipping to change at line 413
_applyOpToDataFiles( database, deleter, true ); _applyOpToDataFiles( database, deleter, true );
} }
boost::intmax_t dbSize( const char *database ); boost::intmax_t dbSize( const char *database );
inline NamespaceIndex* nsindex(const char *ns) { inline NamespaceIndex* nsindex(const char *ns) {
Database *database = cc().database(); Database *database = cc().database();
assert( database ); assert( database );
DEV { DEV {
char buf[256]; char buf[256];
nsToClient(ns, buf); nsToDatabase(ns, buf);
if ( database->name != buf ) { if ( database->name != buf ) {
out() << "ERROR: attempt to write to wrong database databas e\n"; out() << "ERROR: attempt to write to wrong database databas e\n";
out() << " ns:" << ns << '\n'; out() << " ns:" << ns << '\n';
out() << " database->name:" << database->name << endl; out() << " database->name:" << database->name << endl;
assert( database->name == buf ); assert( database->name == buf );
} }
} }
return &database->namespaceIndex; return &database->namespaceIndex;
} }
inline NamespaceDetails* nsdetails(const char *ns) { inline NamespaceDetails* nsdetails(const char *ns) {
// if this faults, did you set the current db first? (DBContext + dblock) // if this faults, did you set the current db first? (Client::Cont ext + dblock)
return nsindex(ns)->details(ns); return nsindex(ns)->details(ns);
} }
inline MongoDataFile& DiskLoc::pdf() const { inline MongoDataFile& DiskLoc::pdf() const {
assert( fileNo != -1 ); assert( fileNo != -1 );
return *cc().database()->getFile(fileNo); return *cc().database()->getFile(fileNo);
} }
inline Extent* DataFileMgr::getExtent(const DiskLoc& dl) { inline Extent* DataFileMgr::getExtent(const DiskLoc& dl) {
assert( dl.a() != -1 ); assert( dl.a() != -1 );
 End of changes. 5 change blocks. 
4 lines changed or deleted 8 lines changed or added


 processinfo.h   processinfo.h 
skipping to change at line 31 skipping to change at line 31
#ifndef _WIN32 #ifndef _WIN32
#include <unistd.h> #include <unistd.h>
#else #else
typedef int pid_t; typedef int pid_t;
int getpid(); int getpid();
#endif #endif
namespace mongo { namespace mongo {
class BSONObjBuilder;
class ProcessInfo { class ProcessInfo {
public: public:
ProcessInfo( pid_t pid = getpid() ); ProcessInfo( pid_t pid = getpid() );
~ProcessInfo(); ~ProcessInfo();
/** /**
* @return mbytes * @return mbytes
*/ */
int getVirtualMemorySize(); int getVirtualMemorySize();
/** /**
* @return mbytes * @return mbytes
*/ */
int getResidentSize(); int getResidentSize();
/**
* Append platform-specific data to obj
*/
void getExtraInfo(BSONObjBuilder& info);
bool supported(); bool supported();
private: private:
pid_t _pid; pid_t _pid;
}; };
} }
 End of changes. 2 change blocks. 
0 lines changed or deleted 7 lines changed or added


 repl.h   repl.h 
skipping to change at line 80 skipping to change at line 80
Can be a group of things to replicate for several databases. Can be a group of things to replicate for several databases.
{ host: ..., source: ..., only: ..., syncedTo: ..., localLogTs: . .., dbsNextPass: { ... }, incompleteCloneDbs: { ... } } { host: ..., source: ..., only: ..., syncedTo: ..., localLogTs: . .., dbsNextPass: { ... }, incompleteCloneDbs: { ... } }
'source' defaults to 'main'; support for multiple source names is 'source' defaults to 'main'; support for multiple source names is
not done (always use main for now). not done (always use main for now).
*/ */
class ReplSource { class ReplSource {
bool resync(string db); bool resync(string db);
/* pull some operations from the master's oplog, and apply them. */
bool sync_pullOpLog(int& nApplied); bool sync_pullOpLog(int& nApplied);
void sync_pullOpLog_applyOperation(BSONObj& op, OpTime *localLogTai l); void sync_pullOpLog_applyOperation(BSONObj& op, OpTime *localLogTai l);
auto_ptr<DBClientConnection> conn; auto_ptr<DBClientConnection> conn;
auto_ptr<DBClientCursor> cursor; auto_ptr<DBClientCursor> cursor;
/* we only clone one database per pass, even if a lot need done. T
his helps us
avoid overflowing the master's transaction log by doing too much
work before going
back to read more transactions. (Imagine a scenario of slave sta
rtup where we try to
clone 100 databases in one pass.)
*/
set<string> addDbNextPass; set<string> addDbNextPass;
set<string> incompleteCloneDbs; set<string> incompleteCloneDbs;
ReplSource(); ReplSource();
// returns the dummy ns used to do the drop // returns the dummy ns used to do the drop
string resyncDrop( const char *db, const char *requester ); string resyncDrop( const char *db, const char *requester );
// returns true if connected on return // returns true if connected on return
bool connect(); bool connect();
// returns possibly unowned id spec for the operation. // returns possibly unowned id spec for the operation.
static BSONObj idForOp( const BSONObj &op, bool &mod ); static BSONObj idForOp( const BSONObj &op, bool &mod );
skipping to change at line 121 skipping to change at line 130
static void applyOperation(const BSONObj& op); static void applyOperation(const BSONObj& op);
bool replacing; // in "replace mode" -- see CmdReplacePeer bool replacing; // in "replace mode" -- see CmdReplacePeer
bool paired; // --pair in use bool paired; // --pair in use
string hostName; // ip addr or hostname plus optionally, ":<port >" string hostName; // ip addr or hostname plus optionally, ":<port >"
string _sourceName; // a logical source name. string _sourceName; // a logical source name.
string sourceName() const { string sourceName() const {
return _sourceName.empty() ? "main" : _sourceName; return _sourceName.empty() ? "main" : _sourceName;
} }
string only; // only a certain db. note that in the sources collect ion, this may not be changed once you start replicating. string only; // only a certain db. note that in the sources collect ion, this may not be changed once you start replicating.
/* the last time point we have already synced up to. */ /* the last time point we have already synced up to (in the remote/ master's oplog). */
OpTime syncedTo; OpTime syncedTo;
OpTime lastSavedLocalTs_;
/* This is for repl pairs.
_lastSavedLocalTs is the most recent point in the local log that
we know is consistent
with the remote log ( ie say the local op log has entries ABCDE
and the remote op log
has ABCXY, then _lastSavedLocalTs won't be greater than C until
we have reconciled
the DE-XY difference.)
*/
OpTime _lastSavedLocalTs;
int nClonedThisPass; int nClonedThisPass;
typedef vector< shared_ptr< ReplSource > > SourceVector; typedef vector< shared_ptr< ReplSource > > SourceVector;
static void loadAll(SourceVector&); static void loadAll(SourceVector&);
explicit ReplSource(BSONObj); explicit ReplSource(BSONObj);
bool sync(int& nApplied); bool sync(int& nApplied);
void save(); // write ourself to local.sources void save(); // write ourself to local.sources
void resetConnection() { void resetConnection() {
cursor = auto_ptr<DBClientCursor>(0); cursor = auto_ptr<DBClientCursor>(0);
skipping to change at line 219 skipping to change at line 235
b.appendAs( id.firstElement(), "id" ); b.appendAs( id.firstElement(), "id" );
return b.obj(); return b.obj();
} }
DbSet impl_; DbSet impl_;
}; };
// class for tracking ids and mod ids, in memory or on disk // class for tracking ids and mod ids, in memory or on disk
// 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
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_( opIdMem ) {
} }
void reset( int maxMem = opIdMem ) { void reset( int maxMem = opIdMem ) {
memIds_.reset(); memIds_.reset();
 End of changes. 7 change blocks. 
2 lines changed or deleted 25 lines changed or added


 replset.h   replset.h 
skipping to change at line 145 skipping to change at line 145
} }
} }
if ( cc().isGod() ) if ( cc().isGod() )
return true; return true;
return strcmp( client, "local" ) == 0; return strcmp( client, "local" ) == 0;
} }
inline bool isMasterNs( const char *ns ) { inline bool isMasterNs( const char *ns ) {
char cl[ 256 ]; char cl[ 256 ];
nsToClient( ns, cl ); nsToDatabase( ns, cl );
return isMaster( cl ); return isMaster( cl );
} }
inline ReplPair::ReplPair(const char *remoteEnd, const char *arb) { inline ReplPair::ReplPair(const char *remoteEnd, const char *arb) {
state = -1; state = -1;
remote = remoteEnd; remote = remoteEnd;
remotePort = CmdLine::DefaultDBPort; remotePort = CmdLine::DefaultDBPort;
remoteHost = remoteEnd; remoteHost = remoteEnd;
const char *p = strchr(remoteEnd, ':'); const char *p = strchr(remoteEnd, ':');
if ( p ) { if ( p ) {
 End of changes. 1 change blocks. 
1 lines changed or deleted 1 lines changed or added


 security.h   security.h 
skipping to change at line 63 skipping to change at line 63
assertInWriteLock(); assertInWriteLock();
m[dbname].level = 2; m[dbname].level = 2;
} }
virtual bool isAuthorized(const char *dbname) { virtual bool isAuthorized(const char *dbname) {
if( m[dbname].level == 2 ) return true; if( m[dbname].level == 2 ) return true;
if( noauth ) return true; if( noauth ) return true;
if( m["admin"].level == 2 ) return true; if( m["admin"].level == 2 ) return true;
if( m["local"].level == 2 ) return true; if( m["local"].level == 2 ) return true;
if( isLocalHost ) { if( isLocalHost ) {
readlock l(""); readlock l("");
DBContext c("admin.system.users"); Client::Context c("admin.system.users");
BSONObj result; BSONObj result;
if( Helpers::getSingleton("admin.system.user s", result) ) if( Helpers::getSingleton("admin.system.user s", result) )
return false; return false;
if( warned == 0 ) { if( warned == 0 ) {
warned++; warned++;
log() << "warning: no users configur ed in admin.system.users, allowing localhost access" << endl; log() << "warning: no users configur ed in admin.system.users, allowing localhost access" << endl;
} }
return true; return true;
} }
return false; return false;
 End of changes. 1 change blocks. 
1 lines changed or deleted 1 lines changed or added


 sock.h   sock.h 
skipping to change at line 241 skipping to change at line 241
inline string getHostName() { inline string getHostName() {
char buf[256]; char buf[256];
int ec = gethostname(buf, 127); int ec = gethostname(buf, 127);
if ( ec || *buf == 0 ) { if ( ec || *buf == 0 ) {
log() << "can't get this server's hostname " << OUTPUT_ERRNO << endl; log() << "can't get this server's hostname " << OUTPUT_ERRNO << endl;
return ""; return "";
} }
return buf; return buf;
} }
class ListeningSockets {
public:
ListeningSockets() : _sockets( new set<int>() ){
}
void add( int sock ){
boostlock lk( _mutex );
_sockets->insert( sock );
}
void remove( int sock ){
boostlock lk( _mutex );
_sockets->erase( sock );
}
void closeAll(){
set<int>* s;
{
boostlock lk( _mutex );
s = _sockets;
_sockets = new set<int>();
}
for ( set<int>::iterator i=s->begin(); i!=s->end(); i++ ){
int sock = *i;
log() << "going to close listening socket: " << sock << end
l;
closesocket( sock );
}
}
static ListeningSockets* get();
private:
boost::mutex _mutex;
set<int>* _sockets;
static ListeningSockets* _instance;
};
} // namespace mongo } // namespace mongo
 End of changes. 1 change blocks. 
0 lines changed or deleted 39 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, ... */ /* Used for modifiers such as $inc, $set, $push, ... */
struct Mod { struct Mod {
// See opFromStr below // See opFromStr below
// 0 1 2 3 4 5 6 7 8 9 10 // 0 1 2 3 4 5 6 7 8 9 10
enum Op { INC, SET, PUSH, PUSH_ALL, PULL, PULL_ALL , POP, UNSET, BI TAND, BITOR , BIT } op; enum Op { INC, SET, PUSH, PUSH_ALL, PULL, PULL_ALL , POP, UNSET, BI TAND, BITOR , BIT } 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? // kind of lame; fix one day?
double *ndouble; double *ndouble;
int *nint; int *nint;
long long *nlong; 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; int pushStartSize;
boost::shared_ptr<JSMatcher> 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 JSMatcher( 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;
} }
skipping to change at line 149 skipping to change at line 149
} }
} }
void appendForOpLog( BSONObjBuilder& b ) const { void appendForOpLog( BSONObjBuilder& b ) const {
const char * name = modNames[op]; const char * name = modNames[op];
BSONObjBuilder bb( b.subobjStart( name ) ); BSONObjBuilder bb( b.subobjStart( name ) );
bb.append( elt ); bb.append( elt );
bb.done(); bb.done();
} }
void _checkForAppending( BSONElement& e ){
if ( e.type() == Object ){
// this is a tiny bit slow, but rare and important
// only when setting something TO an object, not setting so
mething in an object
// and it checks for { $set : { x : { 'a.b' : 1 } } }
// which is feel has been common
uassert( 12527 , "not okForStorage" , e.embeddedObject().ok
ForStorage() );
}
}
}; };
class ModSet { class ModSet {
typedef map<string,Mod> ModHolder; typedef map<string,Mod> ModHolder;
ModHolder _mods; ModHolder _mods;
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() );
skipping to change at line 201 skipping to change at line 211
} }
case Mod::UNSET: case Mod::UNSET:
case Mod::PULL: case Mod::PULL:
case Mod::PULL_ALL: case Mod::PULL_ALL:
// no-op b/c unset/pull of nothing does nothing // no-op b/c unset/pull of nothing does nothing
break; break;
case Mod::INC: case Mod::INC:
case Mod::SET: { case Mod::SET: {
m._checkForAppending( m.elt );
b.appendAs( m.elt, m.shortFieldName ); b.appendAs( m.elt, m.shortFieldName );
break; break;
} }
default: default:
stringstream ss; stringstream ss;
ss << "unknown mod in appendNewFromMod: " << m.op; ss << "unknown mod in appendNewFromMod: " << m.op;
throw UserException( 9015, ss.str() ); throw UserException( 9015, ss.str() );
} }
} }
 End of changes. 5 change blocks. 
3 lines changed or deleted 17 lines changed or added

This html diff was produced by rfcdiff 1.41. The latest version is available from http://tools.ietf.org/tools/rfcdiff/