diff options
Diffstat (limited to 'src/mongo')
-rw-r--r-- | src/mongo/SConscript | 11 | ||||
-rw-r--r-- | src/mongo/db/commands/group.cpp | 3 | ||||
-rw-r--r-- | src/mongo/db/commands/mr.cpp | 3 | ||||
-rw-r--r-- | src/mongo/db/dbeval.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/matcher.cpp | 17 | ||||
-rw-r--r-- | src/mongo/scripting/bench.cpp | 17 | ||||
-rw-r--r-- | src/mongo/scripting/bench.h | 5 | ||||
-rw-r--r-- | src/mongo/scripting/engine.cpp | 11 | ||||
-rw-r--r-- | src/mongo/scripting/engine.h | 2 | ||||
-rw-r--r-- | src/mongo/scripting/engine_v8.cpp | 135 | ||||
-rw-r--r-- | src/mongo/scripting/engine_v8.h | 8 | ||||
-rw-r--r-- | src/mongo/scripting/utils.cpp | 4 | ||||
-rw-r--r-- | src/mongo/scripting/v8_db.cpp | 88 | ||||
-rw-r--r-- | src/mongo/scripting/v8_db.h | 9 | ||||
-rw-r--r-- | src/mongo/scripting/v8_utils.cpp | 8 | ||||
-rw-r--r-- | src/mongo/scripting/v8_utils.h | 1 | ||||
-rw-r--r-- | src/mongo/shell/assert.js | 227 | ||||
-rw-r--r-- | src/mongo/shell/createCPPfromJavaScriptFiles.js | 8 | ||||
-rw-r--r-- | src/mongo/shell/shell_utils.cpp | 1 | ||||
-rw-r--r-- | src/mongo/shell/types.js | 598 | ||||
-rw-r--r-- | src/mongo/shell/utils.js | 844 |
21 files changed, 1010 insertions, 992 deletions
diff --git a/src/mongo/SConscript b/src/mongo/SConscript index 41f57bbe999..8fe048ffe28 100644 --- a/src/mongo/SConscript +++ b/src/mongo/SConscript @@ -684,12 +684,15 @@ if darwin or env["_HAVEPCAP"]: # --- shell --- # if you add a file here, you need to add it in scripting/engine.cpp and shell/createCPPfromJavaScriptFiles.js as well -env.JSHeader( "shell/mongo.cpp" , - [ "shell/utils.js","shell/utils_sh.js","shell/db.js","shell/mongo.js","shell/mr.js","shell/query.js","shell/collection.js"] ) +env.JSHeader("shell/mongo.cpp", + ["shell/assert.js", "shell/types.js", "shell/utils.js", "shell/utils_sh.js", + "shell/db.js", "shell/mongo.js", "shell/mr.js", "shell/query.js", + "shell/collection.js"]) # if you add a file here, you need to add it in shell/shell_utils.cpp and shell/createCPPfromJavaScriptFiles.js as well -env.JSHeader( "shell/mongo-server.cpp" , - ["shell/servers.js", "shell/shardingtest.js", "shell/servers_misc.js", "shell/replsettest.js", "shell/replsetbridge.js"] ) +env.JSHeader("shell/mongo-server.cpp", + ["shell/servers.js", "shell/shardingtest.js", + "shell/servers_misc.js", "shell/replsettest.js", "shell/replsetbridge.js"]) coreShellFiles = [ "shell/dbshell.cpp", "shell/shell_utils.cpp", diff --git a/src/mongo/db/commands/group.cpp b/src/mongo/db/commands/group.cpp index 5d5f82cae9a..bd7b439620a 100644 --- a/src/mongo/db/commands/group.cpp +++ b/src/mongo/db/commands/group.cpp @@ -72,8 +72,7 @@ namespace mongo { string& errmsg, BSONObjBuilder& result ) { - auto_ptr<Scope> s = globalScriptEngine->getPooledScope( realdbname ); - s->localConnect( realdbname.c_str() ); + auto_ptr<Scope> s = globalScriptEngine->getPooledScope( realdbname + "group"); if ( reduceScope ) s->init( reduceScope ); diff --git a/src/mongo/db/commands/mr.cpp b/src/mongo/db/commands/mr.cpp index 7325c1f8596..a616fef7304 100644 --- a/src/mongo/db/commands/mr.cpp +++ b/src/mongo/db/commands/mr.cpp @@ -598,8 +598,7 @@ namespace mongo { */ void State::init() { // setup js - _scope.reset(globalScriptEngine->getPooledScope( _config.dbname ).release() ); - _scope->localConnect( _config.dbname.c_str() ); + _scope.reset(globalScriptEngine->getPooledScope( _config.dbname + "mapreduce" ).release() ); if ( ! _config.scopeSetup.isEmpty() ) _scope->init( &_config.scopeSetup ); diff --git a/src/mongo/db/dbeval.cpp b/src/mongo/db/dbeval.cpp index 4848279ebe3..056cafb9bdc 100644 --- a/src/mongo/db/dbeval.cpp +++ b/src/mongo/db/dbeval.cpp @@ -57,7 +57,7 @@ namespace mongo { return false; } - auto_ptr<Scope> s = globalScriptEngine->getPooledScope( dbName ); + auto_ptr<Scope> s = globalScriptEngine->getPooledScope( dbName + "dbeval" ); ScriptingFunction f = s->createFunction(code); if ( f == 0 ) { errmsg = (string)"compile failed: " + s->getError(); diff --git a/src/mongo/db/matcher.cpp b/src/mongo/db/matcher.cpp index 5d814f4190d..2f2cb8b35e0 100644 --- a/src/mongo/db/matcher.cpp +++ b/src/mongo/db/matcher.cpp @@ -64,17 +64,8 @@ namespace mongo { _func = 0; _initCalled = false; } - - ~Where() { - if ( _scope.get() ){ - try { - _scope->execSetup( "_mongo.readOnly = false;" , "make not read only" ); - } - catch( DBException& e ){ - warning() << "javascript scope cleanup interrupted" << causedBy( e ) << endl; - } - } + ~Where() { _func = 0; } @@ -83,14 +74,12 @@ namespace mongo { return; _initCalled = true; - _scope = globalScriptEngine->getPooledScope( _ns ); + _scope = globalScriptEngine->getPooledScope( _ns + "where" ); NamespaceString ns( _ns ); - _scope->localConnect( ns.db.c_str() ); - + massert( 10341 , "code has to be set first!" , ! _jsCode.empty() ); _func = _scope->createFunction( _jsCode.c_str() ); - _scope->execSetup( "_mongo.readOnly = true;" , "make read only" ); } void setScope( const BSONObj& scope ) { diff --git a/src/mongo/scripting/bench.cpp b/src/mongo/scripting/bench.cpp index 234550586e6..f0256574afa 100644 --- a/src/mongo/scripting/bench.cpp +++ b/src/mongo/scripting/bench.cpp @@ -347,7 +347,7 @@ namespace mongo { bool check = ! e["check"].eoo(); if( check ){ if ( e["check"].type() == CodeWScope || e["check"].type() == Code || e["check"].type() == String ) { - scope = globalScriptEngine->getPooledScope( ns ); + scope = globalScriptEngine->getPooledScope( ns + "benchrun" ); verify( scope.get() ); if ( e.type() == CodeWScope ) { @@ -790,7 +790,7 @@ namespace mongo { /** * benchRun( { ops : [] , host : XXX , db : XXXX , parallel : 5 , seconds : 5 } */ - BSONObj benchRunSync( const BSONObj& argsFake, void* data ) { + BSONObj BenchRunner::benchRunSync( const BSONObj& argsFake, void* data ) { BSONObj start = benchStart( argsFake, data ); @@ -804,7 +804,7 @@ namespace mongo { /** * benchRun( { ops : [] , host : XXX , db : XXXX , parallel : 5 , seconds : 5 } */ - BSONObj benchStart( const BSONObj& argsFake, void* data ) { + BSONObj BenchRunner::benchStart( const BSONObj& argsFake, void* data ) { verify( argsFake.firstElement().isABSONObj() ); BSONObj args = argsFake.firstElement().Obj(); @@ -819,7 +819,7 @@ namespace mongo { /** * benchRun( { ops : [] , host : XXX , db : XXXX , parallel : 5 , seconds : 5 } */ - BSONObj benchFinish( const BSONObj& argsFake, void* data ) { + BSONObj BenchRunner::benchFinish( const BSONObj& argsFake, void* data ) { OID oid = OID( argsFake.firstElement().String() ); @@ -831,11 +831,4 @@ namespace mongo { return BSON( "" << finalObj ); } - void installBenchmarkSystem( Scope& scope ) { - scope.injectNative( "benchRun" , benchRunSync ); - scope.injectNative( "benchRunSync" , benchRunSync ); - scope.injectNative( "benchStart" , benchStart ); - scope.injectNative( "benchFinish" , benchFinish ); - } - -} +} // namespace mongo diff --git a/src/mongo/scripting/bench.h b/src/mongo/scripting/bench.h index 7ae7d642f53..b3a644c7765 100644 --- a/src/mongo/scripting/bench.h +++ b/src/mongo/scripting/bench.h @@ -395,6 +395,11 @@ namespace mongo { const BenchRunConfig &config() const { return *_config; } // TODO: Remove this function. + // JS bindings + static BSONObj benchFinish(const BSONObj& argsFake, void* data); + static BSONObj benchStart(const BSONObj& argsFake, void* data); + static BSONObj benchRunSync(const BSONObj& argsFake, void* data); + private: // TODO: Same as for createWithConfig. static boost::mutex _staticMutex; diff --git a/src/mongo/scripting/engine.cpp b/src/mongo/scripting/engine.cpp index 0124b45b45c..5017f40a4ed 100644 --- a/src/mongo/scripting/engine.cpp +++ b/src/mongo/scripting/engine.cpp @@ -24,6 +24,7 @@ #include "mongo/client/dbclientcursor.h" #include "mongo/client/dbclientinterface.h" +#include "mongo/scripting/bench.h" #include "mongo/util/file.h" namespace mongo { @@ -230,7 +231,6 @@ namespace mongo { } void Scope::execCoreFiles() { - // keeping same order as in SConstruct execSetup(JSFiles::utils); execSetup(JSFiles::utils_sh); execSetup(JSFiles::db); @@ -240,6 +240,14 @@ namespace mongo { execSetup(JSFiles::collection); } + /** install BenchRunner suite */ + void Scope::installBenchRun() { + injectNative("benchRun", BenchRunner::benchRunSync); + injectNative("benchRunSync", BenchRunner::benchRunSync); + injectNative("benchStart", BenchRunner::benchStart); + injectNative("benchFinish", BenchRunner::benchFinish); + } + typedef map<string, list<Scope*> > PoolToScopes; class ScopeCache { @@ -378,6 +386,7 @@ namespace mongo { Scope* _real; }; + /** Get a scope from the pool of scopes matching the supplied pool name */ auto_ptr<Scope> ScriptEngine::getPooledScope(const string& pool) { if (!scopeCache.get()) scopeCache.reset(new ScopeCache()); diff --git a/src/mongo/scripting/engine.h b/src/mongo/scripting/engine.h index e9d388145c0..160ba077962 100644 --- a/src/mongo/scripting/engine.h +++ b/src/mongo/scripting/engine.h @@ -71,6 +71,8 @@ namespace mongo { virtual bool hasOutOfMemoryException() = 0; + virtual void installBenchRun(); + virtual bool isKillPending() const = 0; virtual void gc() = 0; diff --git a/src/mongo/scripting/engine_v8.cpp b/src/mongo/scripting/engine_v8.cpp index 88be725567c..534a74197f6 100644 --- a/src/mongo/scripting/engine_v8.cpp +++ b/src/mongo/scripting/engine_v8.cpp @@ -25,6 +25,12 @@ using namespace mongoutils; namespace mongo { + // Generated symbols for JS files + namespace JSFiles { + extern const JSFile types; + extern const JSFile assert; + } + /** * Unwraps a BSONObj from the JS wrapper */ @@ -501,17 +507,20 @@ namespace mongo { internalFieldObjects->SetInternalFieldCount(1); injectV8Function("print", Print); - injectV8Function("version", Version); - injectV8Function("load", load); + injectV8Function("version", Version); // TODO: remove injectV8Function("gc", GCV8); - injectV8Function("startCpuProfiler", startCpuProfiler); - injectV8Function("stopCpuProfiler", stopCpuProfiler); - injectV8Function("getCpuProfile", getCpuProfile); + // injectV8Function("startCpuProfiler", startCpuProfiler); + // injectV8Function("stopCpuProfiler", stopCpuProfiler); + // injectV8Function("getCpuProfile", getCpuProfile); - // install db and bson types in the global scope - installDBTypes(this, _global); + // install BSON functions in the global object + installBSONTypes(); - // install db/shell-specific utilities in the global scope + // load JS helpers (dependancy: installBSONTypes) + execSetup(JSFiles::assert); + execSetup(JSFiles::types); + + // install process-specific utilities in the global scope (dependancy: types.js, assert.js) if (_engine->_scopeInitCallback) _engine->_scopeInitCallback(*this); @@ -730,6 +739,40 @@ namespace mongo { return v8ToMongo(v->ToObject()); } + v8::Handle<v8::FunctionTemplate> getNumberLongFunctionTemplate(V8Scope* scope) { + v8::Handle<v8::FunctionTemplate> numberLong = scope->createV8Function(numberLongInit); + v8::Local<v8::Template> proto = numberLong->PrototypeTemplate(); + scope->injectV8Function("valueOf", numberLongValueOf, proto); + scope->injectV8Function("toNumber", numberLongToNumber, proto); + scope->injectV8Function("toString", numberLongToString, proto); + return numberLong; + } + + v8::Handle<v8::FunctionTemplate> getNumberIntFunctionTemplate(V8Scope* scope) { + v8::Handle<v8::FunctionTemplate> numberInt = scope->createV8Function(numberIntInit); + v8::Local<v8::Template> proto = numberInt->PrototypeTemplate(); + scope->injectV8Function("valueOf", numberIntValueOf, proto); + scope->injectV8Function("toNumber", numberIntToNumber, proto); + scope->injectV8Function("toString", numberIntToString, proto); + return numberInt; + } + + v8::Handle<v8::FunctionTemplate> getBinDataFunctionTemplate(V8Scope* scope) { + v8::Handle<v8::FunctionTemplate> binData = scope->createV8Function(binDataInit); + binData->InstanceTemplate()->SetInternalFieldCount(1); + v8::Local<v8::Template> proto = binData->PrototypeTemplate(); + scope->injectV8Function("toString", binDataToString, proto); + scope->injectV8Function("base64", binDataToBase64, proto); + scope->injectV8Function("hex", binDataToHex, proto); + return binData; + } + + v8::Handle<v8::FunctionTemplate> getTimestampFunctionTemplate(V8Scope* scope) { + v8::Handle<v8::FunctionTemplate> ts = scope->createV8Function(dbTimestampInit); + ts->InstanceTemplate()->SetInternalFieldCount(1); + return ts; + } + // --- functions ----- bool hasFunctionIdentifier(const string& code) { @@ -1012,14 +1055,17 @@ namespace mongo { return; uassert(12511, "localConnect previously called with a different name", false); } - _global->ForceSet(v8StringData("Mongo"), - getMongoFunctionTemplate(this, true)->GetFunction()); - execCoreFiles(); - exec("_mongo = new Mongo();", "local connect 2", false, true, true, 0); - exec((string)"db = _mongo.getDB(\"" + dbName + "\");", "local connect 3", - false, true, true, 0); - _connectState = LOCAL; - _localDBName = dbName; + + // NOTE: order is important here. the following methods must be called after + // the above conditional statements. + + // install db access functions in the global object + installDBAccess(); + + // add global load() helper + injectV8Function("load", load); + + // install the Mongo function object and instantiate the 'db' global _global->ForceSet(v8StringData("Mongo"), getMongoFunctionTemplate(this, true)->GetFunction()); execCoreFiles(); @@ -1038,13 +1084,70 @@ namespace mongo { return; if (_connectState == LOCAL) uassert(12512, "localConnect already called, can't call externalSetup", false); + + // install db access functions in the global object + installDBAccess(); + + // install thread-related functions (e.g. _threadInject) installFork(this, _global, _context); + + // install 'load' helper function + injectV8Function("load", load); + + // install the Mongo function object _global->ForceSet(v8StringData("Mongo"), getMongoFunctionTemplate(this, false)->GetFunction()); execCoreFiles(); _connectState = EXTERNAL; } + void V8Scope::installDBAccess() { + v8::Handle<v8::FunctionTemplate> db = createV8Function(dbInit); + db->InstanceTemplate()->SetNamedPropertyHandler(collectionGetter, collectionSetter); + _global->ForceSet(v8StringData("DB"), db->GetFunction()); + v8::Handle<v8::FunctionTemplate> dbCollection = createV8Function(collectionInit); + dbCollection->InstanceTemplate()->SetNamedPropertyHandler(collectionGetter, + collectionSetter); + _global->ForceSet(v8StringData("DBCollection"), dbCollection->GetFunction()); + + v8::Handle<v8::FunctionTemplate> dbQuery = createV8Function(dbQueryInit); + dbQuery->InstanceTemplate()->SetIndexedPropertyHandler(dbQueryIndexAccess); + _global->ForceSet(v8StringData("DBQuery"), dbQuery->GetFunction()); + } + + void V8Scope::installBSONTypes() { + injectV8Function("ObjectId", objectIdInit, _global); + injectV8Function("DBRef", dbRefInit, _global); + injectV8Function("DBPointer", dbPointerInit, _global); + + _global->ForceSet(v8StringData("BinData"), + getBinDataFunctionTemplate(this)->GetFunction()); + _global->ForceSet(v8StringData("UUID"), + createV8Function(uuidInit)->GetFunction()); + _global->ForceSet(v8StringData("MD5"), + createV8Function(md5Init)->GetFunction()); + _global->ForceSet(v8StringData("HexData"), + createV8Function(hexDataInit)->GetFunction()); + _global->ForceSet(v8StringData("NumberLong"), + getNumberLongFunctionTemplate(this)->GetFunction()); + _global->ForceSet(v8StringData("NumberInt"), + getNumberIntFunctionTemplate(this)->GetFunction()); + _global->ForceSet(v8StringData("Timestamp"), + getTimestampFunctionTemplate(this)->GetFunction()); + + BSONObjBuilder b; + b.appendMaxKey(""); + b.appendMinKey(""); + BSONObj o = b.obj(); + BSONObjIterator i(o); + _global->ForceSet(v8StringData("MaxKey"), mongoToV8Element(i.next())); + _global->ForceSet(v8StringData("MinKey"), mongoToV8Element(i.next())); + _global->Get(v8StringData("Object"))->ToObject()->ForceSet( + v8StringData("bsonsize"), + createV8Function(bsonsize)->GetFunction()); + } + + // ----- internal ----- void V8Scope::reset() { diff --git a/src/mongo/scripting/engine_v8.h b/src/mongo/scripting/engine_v8.h index 2ece1eab79d..991e2f63fcd 100644 --- a/src/mongo/scripting/engine_v8.h +++ b/src/mongo/scripting/engine_v8.h @@ -91,10 +91,18 @@ namespace mongo { /** check if there is a pending killOp request */ bool isKillPending() const; + /** + * Connect to a local database, create a Mongo object instance, and load any + * server-side js into the global object + */ virtual void localConnect(const char* dbName); virtual void externalSetup(); + virtual void installDBAccess(); + + virtual void installBSONTypes(); + virtual string getError() { return _error; } virtual bool hasOutOfMemoryException(); diff --git a/src/mongo/scripting/utils.cpp b/src/mongo/scripting/utils.cpp index f3ae976d019..0ba7e900bcb 100644 --- a/src/mongo/scripting/utils.cpp +++ b/src/mongo/scripting/utils.cpp @@ -21,8 +21,6 @@ namespace mongo { - void installBenchmarkSystem( Scope& scope ); - static BSONObj native_hex_md5( const BSONObj& args, void* data ) { uassert( 10261, "hex_md5 takes a single string argument -- hex_md5(string)", @@ -61,8 +59,6 @@ namespace mongo { scope.injectNative( "hex_md5" , native_hex_md5 ); scope.injectNative( "version" , native_version ); scope.injectNative( "sleep" , native_sleep ); - - installBenchmarkSystem( scope ); } } diff --git a/src/mongo/scripting/v8_db.cpp b/src/mongo/scripting/v8_db.cpp index a0a8c8e58c6..e056dfbe206 100644 --- a/src/mongo/scripting/v8_db.cpp +++ b/src/mongo/scripting/v8_db.cpp @@ -102,94 +102,6 @@ namespace mongo { return mongo; } - v8::Handle<v8::FunctionTemplate> getNumberLongFunctionTemplate(V8Scope* scope) { - v8::Handle<v8::FunctionTemplate> numberLong = scope->createV8Function(numberLongInit); - v8::Local<v8::Template> proto = numberLong->PrototypeTemplate(); - scope->injectV8Function("valueOf", numberLongValueOf, proto); - scope->injectV8Function("toNumber", numberLongToNumber, proto); - scope->injectV8Function("toString", numberLongToString, proto); - return numberLong; - } - - v8::Handle<v8::FunctionTemplate> getNumberIntFunctionTemplate(V8Scope* scope) { - v8::Handle<v8::FunctionTemplate> numberInt = scope->createV8Function(numberIntInit); - v8::Local<v8::Template> proto = numberInt->PrototypeTemplate(); - scope->injectV8Function("valueOf", numberIntValueOf, proto); - scope->injectV8Function("toNumber", numberIntToNumber, proto); - scope->injectV8Function("toString", numberIntToString, proto); - return numberInt; - } - - v8::Handle<v8::FunctionTemplate> getBinDataFunctionTemplate(V8Scope* scope) { - v8::Handle<v8::FunctionTemplate> binData = scope->createV8Function(binDataInit); - binData->InstanceTemplate()->SetInternalFieldCount(1); - v8::Local<v8::Template> proto = binData->PrototypeTemplate(); - scope->injectV8Function("toString", binDataToString, proto); - scope->injectV8Function("base64", binDataToBase64, proto); - scope->injectV8Function("hex", binDataToHex, proto); - return binData; - } - - v8::Handle<v8::FunctionTemplate> getUUIDFunctionTemplate(V8Scope* scope) { - v8::Handle<v8::FunctionTemplate> templ = scope->createV8Function(uuidInit); - return templ; - } - - v8::Handle<v8::FunctionTemplate> getMD5FunctionTemplate(V8Scope* scope) { - v8::Handle<v8::FunctionTemplate> templ = scope->createV8Function(md5Init); - return templ; - } - - v8::Handle<v8::FunctionTemplate> getHexDataFunctionTemplate(V8Scope* scope) { - v8::Handle<v8::FunctionTemplate> templ = scope->createV8Function(hexDataInit); - return templ; - } - - v8::Handle<v8::FunctionTemplate> getTimestampFunctionTemplate(V8Scope* scope) { - v8::Handle<v8::FunctionTemplate> ts = scope->createV8Function(dbTimestampInit); - ts->InstanceTemplate()->SetInternalFieldCount(1); - return ts; - } - - void installDBTypes(V8Scope* scope, v8::Handle<v8::Object>& global) { - v8::Handle<v8::FunctionTemplate> db = scope->createV8Function(dbInit); - db->InstanceTemplate()->SetNamedPropertyHandler(collectionGetter, collectionSetter); - global->ForceSet(scope->v8StringData("DB"), db->GetFunction()); - v8::Handle<v8::FunctionTemplate> dbCollection = scope->createV8Function(collectionInit); - dbCollection->InstanceTemplate()->SetNamedPropertyHandler(collectionGetter, - collectionSetter); - global->ForceSet(scope->v8StringData("DBCollection"), dbCollection->GetFunction()); - - v8::Handle<v8::FunctionTemplate> dbQuery = scope->createV8Function(dbQueryInit); - dbQuery->InstanceTemplate()->SetIndexedPropertyHandler(dbQueryIndexAccess); - global->ForceSet(scope->v8StringData("DBQuery"), dbQuery->GetFunction()); - - scope->injectV8Function("ObjectId", objectIdInit, global); - scope->injectV8Function("DBRef", dbRefInit, global); - scope->injectV8Function("DBPointer", dbPointerInit, global); - - global->ForceSet(scope->v8StringData("BinData"), getBinDataFunctionTemplate(scope)->GetFunction()); - global->ForceSet(scope->v8StringData("UUID"), getUUIDFunctionTemplate(scope)->GetFunction()); - global->ForceSet(scope->v8StringData("MD5"), getMD5FunctionTemplate(scope)->GetFunction()); - global->ForceSet(scope->v8StringData("HexData"), getHexDataFunctionTemplate(scope)->GetFunction()); - global->ForceSet(scope->v8StringData("NumberLong"), - getNumberLongFunctionTemplate(scope)->GetFunction()); - global->ForceSet(scope->v8StringData("NumberInt"), - getNumberIntFunctionTemplate(scope)->GetFunction()); - global->ForceSet(scope->v8StringData("Timestamp"), - getTimestampFunctionTemplate(scope)->GetFunction()); - - BSONObjBuilder b; - b.appendMaxKey(""); - b.appendMinKey(""); - BSONObj o = b.obj(); - BSONObjIterator i(o); - global->ForceSet(scope->v8StringData("MaxKey"), scope->mongoToV8Element(i.next())); - global->ForceSet(scope->v8StringData("MinKey"), scope->mongoToV8Element(i.next())); - global->Get(scope->v8StringData("Object"))->ToObject()->ForceSet(scope->v8StringData("bsonsize"), - scope->createV8Function(bsonsize)->GetFunction()); - } - void destroyConnection(v8::Persistent<v8::Value> self, void* parameter) { delete static_cast<DBClientBase*>(parameter); self.Dispose(); diff --git a/src/mongo/scripting/v8_db.h b/src/mongo/scripting/v8_db.h index 6e8924465e4..2f747fd9714 100644 --- a/src/mongo/scripting/v8_db.h +++ b/src/mongo/scripting/v8_db.h @@ -27,9 +27,14 @@ namespace mongo { class DBClientBase; /** - * install the db related functions and objects in the given scope + * install database access functions */ - void installDBTypes(V8Scope* scope, v8::Handle<v8::Object>& global); + void installDBAccess(V8Scope* scope); + + /** + * install BSON types and helpers + */ + void installBSONTypes(V8Scope* scope); /** * get the DBClientBase connection from JS args diff --git a/src/mongo/scripting/v8_utils.cpp b/src/mongo/scripting/v8_utils.cpp index 23f8f8e8980..de20f0713e1 100644 --- a/src/mongo/scripting/v8_utils.cpp +++ b/src/mongo/scripting/v8_utils.cpp @@ -41,6 +41,14 @@ namespace mongo { return s; } + /** Get the properties of an object (and it's prototype) as a comma-delimited string */ + std::string v8objectToString(const v8::Handle<v8::Object>& o) { + v8::Local<v8::Array> properties = o->GetPropertyNames(); + v8::String::Utf8Value str(properties); + massert(16696 , "error converting js type to Utf8Value", *str); + return std::string(*str, str.length()); + } + std::string toSTLString(const v8::TryCatch* try_catch) { stringstream ss; v8::String::Utf8Value exception(try_catch->Exception()); diff --git a/src/mongo/scripting/v8_utils.h b/src/mongo/scripting/v8_utils.h index ba271ffe787..1bdec4ae213 100644 --- a/src/mongo/scripting/v8_utils.h +++ b/src/mongo/scripting/v8_utils.h @@ -34,6 +34,7 @@ namespace mongo { std::string toSTLString(const v8::Handle<v8::Value>& o); std::string toSTLString(const v8::TryCatch* try_catch); + std::string v8ObjectToString(const v8::Handle<v8::Object>& o); class V8Scope; void installFork(V8Scope* scope, diff --git a/src/mongo/shell/assert.js b/src/mongo/shell/assert.js new file mode 100644 index 00000000000..b1279028307 --- /dev/null +++ b/src/mongo/shell/assert.js @@ -0,0 +1,227 @@ +doassert = function(msg) { + if (msg.indexOf("assert") == 0) + print(msg); + else + print("assert: " + msg); + printStackTrace(); + throw msg; +} + +assert = function(b, msg){ + if (assert._debug && msg) print("in assert for: " + msg); + if (b) + return; + doassert(msg == undefined ? "assert failed" : "assert failed : " + msg); +} + +assert.automsg = function(b) { + assert(eval(b), b); +} + +assert._debug = false; + +assert.eq = function(a, b, msg){ + if (assert._debug && msg) print("in assert for: " + msg); + + if (a == b) + return; + + if ((a != null && b != null) && friendlyEqual(a, b)) + return; + + doassert("[" + tojson(a) + "] != [" + tojson(b) + "] are not equal : " + msg); +} + +assert.eq.automsg = function(a, b) { + assert.eq(eval(a), eval(b), "[" + a + "] != [" + b + "]"); +} + +assert.neq = function(a, b, msg){ + if (assert._debug && msg) print("in assert for: " + msg); + if (a != b) + return; + + doassert("[" + a + "] != [" + b + "] are equal : " + msg); +} + +assert.contains = function(o, arr, msg){ + var wasIn = false + + if(! arr.length){ + for(var i in arr){ + wasIn = arr[i] == o || ((arr[i] != null && o != null) && friendlyEqual(arr[i], o)) + return; + if(wasIn) break + } + } + else { + for(var i = 0; i < arr.length; i++){ + wasIn = arr[i] == o || ((arr[i] != null && o != null) && friendlyEqual(arr[i], o)) + if(wasIn) break + } + } + + if(! wasIn) doassert(tojson(o) + " was not in " + tojson(arr) + " : " + msg) +} + +assert.repeat = function(f, msg, timeout, interval) { + if (assert._debug && msg) print("in assert for: " + msg); + + var start = new Date(); + timeout = timeout || 30000; + interval = interval || 200; + var last; + while(1) { + + if (typeof(f) == "string"){ + if (eval(f)) + return; + } + else { + if (f()) + return; + } + + if ((new Date()).getTime() - start.getTime() > timeout) + break; + sleep(interval); + } +} + +assert.soon = function(f, msg, timeout /*ms*/, interval) { + if (assert._debug && msg) print("in assert for: " + msg); + + var start = new Date(); + timeout = timeout || 30000; + interval = interval || 200; + var last; + while(1) { + if (typeof(f) == "string"){ + if (eval(f)) + return; + } + else { + if (f()) + return; + } + + diff = (new Date()).getTime() - start.getTime(); + if (diff > timeout) + doassert("assert.soon failed: " + f + ", msg:" + msg); + sleep(interval); + } +} + +assert.time = function(f, msg, timeout /*ms*/) { + if (assert._debug && msg) print("in assert for: " + msg); + + var start = new Date(); + timeout = timeout || 30000; + if (typeof(f) == "string"){ + res = eval(f); + } + else { + res = f(); + } + + diff = (new Date()).getTime() - start.getTime(); + if (diff > timeout) + doassert("assert.time failed timeout " + timeout + "ms took " + diff + "ms : " + f + ", msg:" + msg); + return res; +} + +assert.throws = function(func, params, msg){ + if (assert._debug && msg) print("in assert for: " + msg); + if (params && typeof(params) == "string") + throw "2nd argument to assert.throws has to be an array" + try { + func.apply(null, params); + } + catch (e){ + return e; + } + doassert("did not throw exception: " + msg); +} + +assert.throws.automsg = function(func, params) { + assert.throws(func, params, func.toString()); +} + +assert.commandWorked = function(res, msg){ + if (assert._debug && msg) print("in assert for: " + msg); + + if (res.ok == 1) + return; + doassert("command failed: " + tojson(res) + " : " + msg); +} + +assert.commandFailed = function(res, msg){ + if (assert._debug && msg) print("in assert for: " + msg); + + if (res.ok == 0) + return; + doassert("command worked when it should have failed: " + tojson(res) + " : " + msg); +} + +assert.isnull = function(what, msg){ + if (assert._debug && msg) print("in assert for: " + msg); + + if (what == null) + return; + doassert("supposed to be null (" + (msg || "") + ") was: " + tojson(what)); +} + +assert.lt = function(a, b, msg){ + if (assert._debug && msg) print("in assert for: " + msg); + + if (a < b) + return; + doassert(a + " is not less than " + b + " : " + msg); +} + +assert.gt = function(a, b, msg){ + if (assert._debug && msg) print("in assert for: " + msg); + + if (a > b) + return; + doassert(a + " is not greater than " + b + " : " + msg); +} + +assert.lte = function(a, b, msg){ + if (assert._debug && msg) print("in assert for: " + msg); + + if (a <= b) + return; + doassert(a + " is not less than or eq " + b + " : " + msg); +} + +assert.gte = function(a, b, msg){ + if (assert._debug && msg) print("in assert for: " + msg); + + if (a >= b) + return; + doassert(a + " is not greater than or eq " + b + " : " + msg); +} + +assert.between = function(a, b, c, msg, inclusive){ + if (assert._debug && msg) print("in assert for: " + msg); + + if((inclusive == undefined || inclusive == true) && + a <= b && b <= c) return; + else if(a < b && b < c) return; + doassert(b + " is not between " + a + " and " + c + " : " + msg); +} + +assert.betweenIn = function(a, b, c, msg){ assert.between(a, b, c, msg, true) } +assert.betweenEx = function(a, b, c, msg){ assert.between(a, b, c, msg, false) } + +assert.close = function(a, b, msg, places){ + if (places === undefined) { + places = 4; + } + if (Math.round((a - b) * Math.pow(10, places)) === 0) { + return; + } + doassert(a + " is not equal to " + b + " within " + places + + " places, diff: " + (a-b) + " : " + msg); +}; diff --git a/src/mongo/shell/createCPPfromJavaScriptFiles.js b/src/mongo/shell/createCPPfromJavaScriptFiles.js index ecb6be2fb68..32d2fbbd044 100644 --- a/src/mongo/shell/createCPPfromJavaScriptFiles.js +++ b/src/mongo/shell/createCPPfromJavaScriptFiles.js @@ -101,5 +101,9 @@ var shell = new ActiveXObject( "WScript.Shell" ); shell.CurrentDirectory = WScript.Arguments.Unnamed.Item( 0 ); var fso = new ActiveXObject( "Scripting.FileSystemObject" ); -rebuildIfNeeded( fso, "shell/mongo.cpp", ["shell/utils.js", "shell/utils_sh.js", "shell/db.js", "shell/mongo.js", "shell/mr.js", "shell/query.js", "shell/collection.js"] ); -rebuildIfNeeded( fso, "shell/mongo-server.cpp", ["shell/servers.js", "shell/shardingtest.js", "shell/servers_misc.js", "shell/replsettest.js", "shell/replsetbridge.js"] ); +rebuildIfNeeded(fso, "shell/mongo.cpp", ["shell/assert.js", "shell/types.js", "shell/utils.js", "shell/utils_sh.js", + "shell/db.js", "shell/mongo.js", "shell/mr.js", + "shell/query.js", "shell/collection.js"]); +rebuildIfNeeded(fso, "shell/mongo-server.cpp", ["shell/servers.js", "shell/shardingtest.js", + "shell/servers_misc.js", "shell/replsettest.js", + "shell/replsetbridge.js"]); diff --git a/src/mongo/shell/shell_utils.cpp b/src/mongo/shell/shell_utils.cpp index c106cbf6d6a..dce193c1a44 100644 --- a/src/mongo/shell/shell_utils.cpp +++ b/src/mongo/shell/shell_utils.cpp @@ -152,6 +152,7 @@ namespace mongo { scope.execSetup(JSFiles::servers_misc); scope.execSetup(JSFiles::replsettest); scope.execSetup(JSFiles::replsetbridge); + scope.installBenchRun(); if ( !_dbConnect.empty() ) { uassert( 12513, "connect failed", scope.exec( _dbConnect , "(connect)" , false , true , false ) ); diff --git a/src/mongo/shell/types.js b/src/mongo/shell/types.js new file mode 100644 index 00000000000..f0da6cf0d0c --- /dev/null +++ b/src/mongo/shell/types.js @@ -0,0 +1,598 @@ +// Date and time types +if (typeof(Timestamp) != "undefined"){ + Timestamp.prototype.tojson = function() { + return this.toString(); + } + + Timestamp.prototype.getTime = function() { + return this.t; + } + + Timestamp.prototype.getInc = function() { + return this.i; + } + + Timestamp.prototype.toString = function() { + return "Timestamp(" + this.t + ", " + this.i + ")"; + } +} +else { + print("warning: no Timestamp class"); +} + +Date.timeFunc = function(theFunc, numTimes){ + var start = new Date(); + numTimes = numTimes || 1; + for (var i=0; i<numTimes; i++){ + theFunc.apply(null, argumentsToArray(arguments).slice(2)); + } + + return (new Date()).getTime() - start.getTime(); +} + +Date.prototype.tojson = function(){ + var UTC = 'UTC'; + var year = this['get'+UTC+'FullYear']().zeroPad(4); + var month = (this['get'+UTC+'Month']() + 1).zeroPad(2); + var date = this['get'+UTC+'Date']().zeroPad(2); + var hour = this['get'+UTC+'Hours']().zeroPad(2); + var minute = this['get'+UTC+'Minutes']().zeroPad(2); + var sec = this['get'+UTC+'Seconds']().zeroPad(2) + + if (this['get'+UTC+'Milliseconds']()) + sec += '.' + this['get'+UTC+'Milliseconds']().zeroPad(3) + + var ofs = 'Z'; + // // print a non-UTC time + // var ofsmin = this.getTimezoneOffset(); + // if (ofsmin != 0){ + // ofs = ofsmin > 0 ? '-' : '+'; // This is correct + // ofs += (ofsmin/60).zeroPad(2) + // ofs += (ofsmin%60).zeroPad(2) + // } + return 'ISODate("'+year+'-'+month+'-'+date+'T'+hour+':'+minute+':'+sec+ofs+'")'; +} + +ISODate = function(isoDateStr){ + if (!isoDateStr) + return new Date(); + + var isoDateRegex = /(\d{4})-?(\d{2})-?(\d{2})([T ](\d{2})(:?(\d{2})(:?(\d{2}(\.\d+)?))?)?(Z|([+-])(\d{2}):?(\d{2})?)?)?/; + var res = isoDateRegex.exec(isoDateStr); + + if (!res) + throw "invalid ISO date"; + + var year = parseInt(res[1],10) || 1970; // this should always be present + var month = (parseInt(res[2],10) || 1) - 1; + var date = parseInt(res[3],10) || 0; + var hour = parseInt(res[5],10) || 0; + var min = parseInt(res[7],10) || 0; + var sec = parseFloat(res[9]) || 0; + var ms = Math.round((sec%1) * 1000) + sec -= ms/1000 + + var time = Date.UTC(year, month, date, hour, min, sec, ms); + + if (res[11] && res[11] != 'Z'){ + var ofs = 0; + ofs += (parseInt(res[13],10) || 0) * 60*60*1000; // hours + ofs += (parseInt(res[14],10) || 0) * 60*1000; // mins + if (res[12] == '+') // if ahead subtract + ofs *= -1; + + time += ofs + } + + return new Date(time); +} + +// Regular Expression +RegExp.escape = function(text){ + return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); +} + +RegExp.prototype.tojson = RegExp.prototype.toString; + +// Array +Array.contains = function(a, x){ + for (var i=0; i<a.length; i++){ + if (a[i] == x) + return true; + } + return false; +} + +Array.unique = function(a){ + var u = []; + for (var i=0; i<a.length; i++){ + var o = a[i]; + if (! Array.contains(u, o)){ + u.push(o); + } + } + return u; +} + +Array.shuffle = function(arr){ + for (var i=0; i<arr.length-1; i++){ + var pos = i+Random.randInt(arr.length-i); + var save = arr[i]; + arr[i] = arr[pos]; + arr[pos] = save; + } + return arr; +} + +Array.tojson = function(a, indent, nolint){ + var lineEnding = nolint ? " " : "\n"; + + if (!indent) + indent = ""; + if (nolint) + indent = ""; + + if (a.length == 0) { + return "[ ]"; + } + + var s = "[" + lineEnding; + indent += "\t"; + for (var i=0; i<a.length; i++){ + s += indent + tojson(a[i], indent, nolint); + if (i < a.length - 1){ + s += "," + lineEnding; + } + } + if (a.length == 0) { + s += indent; + } + + indent = indent.substring(1); + s += lineEnding+indent+"]"; + return s; +} + +Array.fetchRefs = function(arr, coll){ + var n = []; + for (var i=0; i<arr.length; i ++){ + var z = arr[i]; + if (coll && coll != z.getCollection()) + continue; + n.push(z.fetch()); + } + return n; +} + +Array.sum = function(arr){ + if (arr.length == 0) + return null; + var s = arr[0]; + for (var i=1; i<arr.length; i++) + s += arr[i]; + return s; +} + +Array.avg = function(arr){ + if (arr.length == 0) + return null; + return Array.sum(arr) / arr.length; +} + +Array.stdDev = function(arr){ + var avg = Array.avg(arr); + var sum = 0; + + for (var i=0; i<arr.length; i++){ + sum += Math.pow(arr[i] - avg, 2); + } + + return Math.sqrt(sum / arr.length); +} + +if (typeof Array.isArray != "function"){ + Array.isArray = function(arr){ + return arr != undefined && arr.constructor == Array + } +} + +// Object +Object.extend = function(dst, src, deep){ + for (var k in src){ + var v = src[k]; + if (deep && typeof(v) == "object"){ + if ("floatApprox" in v) { // convert NumberLong properly + eval("v = " + tojson(v)); + } else { + v = Object.extend(typeof (v.length) == "number" ? [] : {}, v, true); + } + } + dst[k] = v; + } + return dst; +} + +Object.merge = function(dst, src, deep){ + var clone = Object.extend({}, dst, deep) + return Object.extend(clone, src, deep) +} + +Object.keySet = function(o) { + var ret = new Array(); + for(var i in o) { + if (!(i in o.__proto__ && o[ i ] === o.__proto__[ i ])) { + ret.push(i); + } + } + return ret; +} + +// String +String.prototype.trim = function() { + return this.replace(/^\s+|\s+$/g,""); +} +String.prototype.ltrim = function() { + return this.replace(/^\s+/,""); +} +String.prototype.rtrim = function() { + return this.replace(/\s+$/,""); +} + +String.prototype.startsWith = function(str){ + return this.indexOf(str) == 0 +} + +String.prototype.endsWith = function(str){ + return new RegExp(RegExp.escape(str) + "$").test(this) +} + +// Returns a copy padded with the provided character _chr_ so it becomes (at least) _length_ +// characters long. +// No truncation is performed if the string is already longer than _length_. +// @param length minimum length of the returned string +// @param right if falsy add leading whitespace, otherwise add trailing whitespace +// @param chr character to be used for padding, defaults to whitespace +// @return the padded string +String.prototype.pad = function(length, right, chr) { + if (typeof chr == 'undefined') chr = ' '; + var str = this; + for (var i = length - str.length; i > 0; i--) { + if (right) { + str = str + chr; + } else { + str = chr + str; + } + } + return str; +} + +// Number +Number.prototype.toPercentStr = function() { + return (this * 100).toFixed(2) + "%"; +} + +Number.prototype.zeroPad = function(width) { + return ('' + this).pad(width, false, '0'); +} + +// NumberLong +if (! NumberLong.prototype) { + NumberLong.prototype = {} +} + +NumberLong.prototype.tojson = function() { + return this.toString(); +} + +// NumberInt +if (! NumberInt.prototype) { + NumberInt.prototype = {} +} + +NumberInt.prototype.tojson = function() { + return this.toString(); +} + +// ObjectId +if (! ObjectId.prototype) + ObjectId.prototype = {} + +ObjectId.prototype.toString = function(){ + return "ObjectId(" + tojson(this.str) + ")"; +} + +ObjectId.prototype.tojson = function(){ + return this.toString(); +} + +ObjectId.prototype.valueOf = function(){ + return this.str; +} + +ObjectId.prototype.isObjectId = true; + +ObjectId.prototype.getTimestamp = function(){ + return new Date(parseInt(this.valueOf().slice(0,8), 16)*1000); +} + +ObjectId.prototype.equals = function(other){ + return this.str == other.str; +} + +// DBPointer +if (typeof(DBPointer) != "undefined"){ + DBPointer.prototype.fetch = function(){ + assert(this.ns, "need a ns"); + assert(this.id, "need an id"); + return db[ this.ns ].findOne({ _id : this.id }); + } + + DBPointer.prototype.tojson = function(indent){ + return this.toString(); + } + + DBPointer.prototype.getCollection = function(){ + return this.ns; + } + + DBPointer.prototype.getId = function(){ + return this.id; + } + + DBPointer.prototype.toString = function(){ + return "DBPointer(" + tojson(this.ns) + ", " + tojson(this.id) + ")"; + } +} +else { + print("warning: no DBPointer"); +} + +// DBRef +if (typeof(DBRef) != "undefined"){ + DBRef.prototype.fetch = function(){ + assert(this.$ref, "need a ns"); + assert(this.$id, "need an id"); + return db[ this.$ref ].findOne({ _id : this.$id }); + } + + DBRef.prototype.tojson = function(indent){ + return this.toString(); + } + + DBRef.prototype.getCollection = function(){ + return this.$ref; + } + + DBRef.prototype.getRef = function(){ + return this.$ref; + } + + DBRef.prototype.getId = function(){ + return this.$id; + } + + DBRef.prototype.toString = function(){ + return "DBRef(" + tojson(this.$ref) + ", " + tojson(this.$id) + ")"; + } +} +else { + print("warning: no DBRef"); +} + +// BinData +if (typeof(BinData) != "undefined"){ + BinData.prototype.tojson = function() { + return this.toString(); + } + + BinData.prototype.subtype = function() { + return this.type; + } + BinData.prototype.length = function() { + return this.len; + } +} +else { + print("warning: no BinData class"); +} + +// Map +if (typeof(Map) == "undefined"){ + Map = function(){ + this._data = {}; + } +} + +Map.hash = function(val){ + if (! val) + return val; + + switch (typeof(val)){ + case 'string': + case 'number': + case 'date': + return val.toString(); + case 'object': + case 'array': + var s = ""; + for (var k in val){ + s += k + val[k]; + } + return s; + } + + throw "can't hash : " + typeof(val); +} + +Map.prototype.put = function(key, value){ + var o = this._get(key); + var old = o.value; + o.value = value; + return old; +} + +Map.prototype.get = function(key){ + return this._get(key).value; +} + +Map.prototype._get = function(key){ + var h = Map.hash(key); + var a = this._data[h]; + if (! a){ + a = []; + this._data[h] = a; + } + for (var i=0; i<a.length; i++){ + if (friendlyEqual(key, a[i].key)){ + return a[i]; + } + } + var o = { key : key, value : null }; + a.push(o); + return o; +} + +Map.prototype.values = function(){ + var all = []; + for (var k in this._data){ + this._data[k].forEach(function(z){ all.push(z.value); }); + } + return all; +} + +if (typeof(gc) == "undefined"){ + gc = function(){ + print("warning: using noop gc()"); + } +} + +// Free Functions +tojsononeline = function(x){ + return tojson(x, " ", true); +} + +tojson = function(x, indent, nolint){ + if (x === null) + return "null"; + + if (x === undefined) + return "undefined"; + + if (!indent) + indent = ""; + + switch (typeof x) { + case "string": { + var s = "\""; + for (var i=0; i<x.length; i++){ + switch (x[i]){ + case '"': s += '\\"'; break; + case '\\': s += '\\\\'; break; + case '\b': s += '\\b'; break; + case '\f': s += '\\f'; break; + case '\n': s += '\\n'; break; + case '\r': s += '\\r'; break; + case '\t': s += '\\t'; break; + + default: { + var code = x.charCodeAt(i); + if (code < 0x20){ + s += (code < 0x10 ? '\\u000' : '\\u00') + code.toString(16); + } else { + s += x[i]; + } + } + } + } + return s + "\""; + } + case "number": + case "boolean": + return "" + x; + case "object":{ + var s = tojsonObject(x, indent, nolint); + if ((nolint == null || nolint == true) && s.length < 80 && (indent == null || indent.length == 0)){ + s = s.replace(/[\s\r\n ]+/gm, " "); + } + return s; + } + case "function": + return x.toString(); + default: + throw "tojson can't handle type " + (typeof x); + } + +} + +tojsonObject = function(x, indent, nolint){ + var lineEnding = nolint ? " " : "\n"; + var tabSpace = nolint ? "" : "\t"; + assert.eq((typeof x), "object", "tojsonObject needs object, not [" + (typeof x) + "]"); + + if (!indent) + indent = ""; + + if (typeof(x.tojson) == "function" && x.tojson != tojson) { + return x.tojson(indent,nolint); + } + + if (x.constructor && typeof(x.constructor.tojson) == "function" && x.constructor.tojson != tojson) { + return x.constructor.tojson(x, indent, nolint); + } + + try { + // modify display of min/max key for spidermonkey + if (x.toString() == "[object MaxKey]") + return "{ $maxKey : 1 }"; + if (x.toString() == "[object MinKey]") + return "{ $minKey : 1 }"; + } + catch(e) { + // toString not callable + return "[object]"; + } + + var s = "{" + lineEnding; + + // push one level of indent + indent += tabSpace; + + var total = 0; + for (var k in x) total++; + if (total == 0) { + s += indent + lineEnding; + } + + var keys = x; + if (typeof(x._simpleKeys) == "function") + keys = x._simpleKeys(); + var num = 1; + for (var k in keys){ + var val = x[k]; + if (val == DB.prototype || val == DBCollection.prototype) + continue; + + s += indent + "\"" + k + "\" : " + tojson(val, indent, nolint); + if (num != total) { + s += ","; + num++; + } + s += lineEnding; + } + + // pop one level of indent + indent = indent.substring(1); + return s + indent + "}"; +} + +isString = function(x){ + return typeof(x) == "string"; +} + +isNumber = function(x){ + return typeof(x) == "number"; +} + +isObject = function(x){ + return typeof(x) == "object"; +} diff --git a/src/mongo/shell/utils.js b/src/mongo/shell/utils.js index b787838a704..1d95dffff8f 100644 --- a/src/mongo/shell/utils.js +++ b/src/mongo/shell/utils.js @@ -50,267 +50,6 @@ setVerboseShell = function( value ) { _verboseShell = value; } -doassert = function (msg) { - if (msg.indexOf("assert") == 0) - print(msg); - else - print("assert: " + msg); - printStackTrace(); - throw msg; -} - -assert = function( b , msg ){ - if ( assert._debug && msg ) print( "in assert for: " + msg ); - if ( b ) - return; - doassert( msg == undefined ? "assert failed" : "assert failed : " + msg ); -} - -// the mongo code uses verify -// so this is to be nice to mongo devs -verify = assert; - -assert.automsg = function( b ) { - assert( eval( b ), b ); -} - -assert._debug = false; - -assert.eq = function( a , b , msg ){ - if ( assert._debug && msg ) print( "in assert for: " + msg ); - - if ( a == b ) - return; - - if ( ( a != null && b != null ) && friendlyEqual( a , b ) ) - return; - - doassert( "[" + tojson( a ) + "] != [" + tojson( b ) + "] are not equal : " + msg ); -} - -assert.eq.automsg = function( a, b ) { - assert.eq( eval( a ), eval( b ), "[" + a + "] != [" + b + "]" ); -} - -assert.neq = function( a , b , msg ){ - if ( assert._debug && msg ) print( "in assert for: " + msg ); - if ( a != b ) - return; - - doassert( "[" + a + "] != [" + b + "] are equal : " + msg ); -} - -assert.contains = function( o, arr, msg ){ - var wasIn = false - - if( ! arr.length ){ - for( var i in arr ){ - wasIn = arr[i] == o || ( ( arr[i] != null && o != null ) && friendlyEqual( arr[i] , o ) ) - return; - if( wasIn ) break - } - } - else { - for( var i = 0; i < arr.length; i++ ){ - wasIn = arr[i] == o || ( ( arr[i] != null && o != null ) && friendlyEqual( arr[i] , o ) ) - if( wasIn ) break - } - } - - if( ! wasIn ) doassert( tojson( o ) + " was not in " + tojson( arr ) + " : " + msg ) -} - -assert.repeat = function( f, msg, timeout, interval ) { - if ( assert._debug && msg ) print( "in assert for: " + msg ); - - var start = new Date(); - timeout = timeout || 30000; - interval = interval || 200; - var last; - while( 1 ) { - - if ( typeof( f ) == "string" ){ - if ( eval( f ) ) - return; - } - else { - if ( f() ) - return; - } - - if ( ( new Date() ).getTime() - start.getTime() > timeout ) - break; - sleep( interval ); - } -} - -assert.soon = function( f, msg, timeout /*ms*/, interval ) { - if ( assert._debug && msg ) print( "in assert for: " + msg ); - - var start = new Date(); - timeout = timeout || 30000; - interval = interval || 200; - var last; - while( 1 ) { - - if ( typeof( f ) == "string" ){ - if ( eval( f ) ) - return; - } - else { - if ( f() ) - return; - } - - diff = ( new Date() ).getTime() - start.getTime(); - if ( diff > timeout ) - doassert( "assert.soon failed: " + f + ", msg:" + msg ); - sleep( interval ); - } -} - -assert.time = function( f, msg, timeout /*ms*/ ) { - if ( assert._debug && msg ) print( "in assert for: " + msg ); - - var start = new Date(); - timeout = timeout || 30000; - - if ( typeof( f ) == "string" ){ - res = eval( f ); - } - else { - res = f(); - } - - diff = ( new Date() ).getTime() - start.getTime(); - if ( diff > timeout ) - doassert( "assert.time failed timeout " + timeout + "ms took " + diff + "ms : " + f + ", msg:" + msg ); - return res; -} - -assert.throws = function( func , params , msg ){ - if ( assert._debug && msg ) print( "in assert for: " + msg ); - - if ( params && typeof( params ) == "string" ) - throw "2nd argument to assert.throws has to be an array" - - try { - func.apply( null , params ); - } - catch ( e ){ - return e; - } - - doassert( "did not throw exception: " + msg ); -} - -assert.throws.automsg = function( func, params ) { - assert.throws( func, params, func.toString() ); -} - -assert.commandWorked = function( res , msg ){ - if ( assert._debug && msg ) print( "in assert for: " + msg ); - - if ( res.ok == 1 ) - return; - - doassert( "command failed: " + tojson( res ) + " : " + msg ); -} - -assert.commandFailed = function( res , msg ){ - if ( assert._debug && msg ) print( "in assert for: " + msg ); - - if ( res.ok == 0 ) - return; - - doassert( "command worked when it should have failed: " + tojson( res ) + " : " + msg ); -} - -assert.isnull = function( what , msg ){ - if ( assert._debug && msg ) print( "in assert for: " + msg ); - - if ( what == null ) - return; - - doassert( "supposed to be null (" + ( msg || "" ) + ") was: " + tojson( what ) ); -} - -assert.lt = function( a , b , msg ){ - if ( assert._debug && msg ) print( "in assert for: " + msg ); - - if ( a < b ) - return; - doassert( a + " is not less than " + b + " : " + msg ); -} - -assert.gt = function( a , b , msg ){ - if ( assert._debug && msg ) print( "in assert for: " + msg ); - - if ( a > b ) - return; - doassert( a + " is not greater than " + b + " : " + msg ); -} - -assert.lte = function( a , b , msg ){ - if ( assert._debug && msg ) print( "in assert for: " + msg ); - - if ( a <= b ) - return; - doassert( a + " is not less than or eq " + b + " : " + msg ); -} - -assert.gte = function( a , b , msg ){ - if ( assert._debug && msg ) print( "in assert for: " + msg ); - - if ( a >= b ) - return; - doassert( a + " is not greater than or eq " + b + " : " + msg ); -} - -assert.between = function( a, b, c, msg, inclusive ){ - if ( assert._debug && msg ) print( "in assert for: " + msg ); - - if( ( inclusive == undefined || inclusive == true ) && - a <= b && b <= c ) return; - else if( a < b && b < c ) return; - - doassert( b + " is not between " + a + " and " + c + " : " + msg ); -} - -assert.betweenIn = function( a, b, c, msg ){ assert.between( a, b, c, msg, true ) } -assert.betweenEx = function( a, b, c, msg ){ assert.between( a, b, c, msg, false ) } - -assert.close = function( a , b , msg , places ){ - if (places === undefined) { - places = 4; - } - if (Math.round((a - b) * Math.pow(10, places)) === 0) { - return; - } - doassert( a + " is not equal to " + b + " within " + places + - " places, diff: " + (a-b) + " : " + msg ); -}; - -Object.extend = function( dst , src , deep ){ - for ( var k in src ){ - var v = src[k]; - if ( deep && typeof(v) == "object" ){ - if ( "floatApprox" in v ) { // convert NumberLong properly - eval( "v = " + tojson( v ) ); - } else { - v = Object.extend( typeof ( v.length ) == "number" ? [] : {} , v , true ); - } - } - dst[k] = v; - } - return dst; -} - -Object.merge = function( dst, src, deep ){ - var clone = Object.extend( {}, dst, deep ) - return Object.extend( clone, src, deep ) -} - argumentsToArray = function( a ){ var arr = []; for ( var i=0; i<a.length; i++ ) @@ -318,64 +57,6 @@ argumentsToArray = function( a ){ return arr; } -isString = function( x ){ - return typeof( x ) == "string"; -} - -isNumber = function(x){ - return typeof( x ) == "number"; -} - -isObject = function( x ){ - return typeof( x ) == "object"; -} - -String.prototype.trim = function() { - return this.replace(/^\s+|\s+$/g,""); -} -String.prototype.ltrim = function() { - return this.replace(/^\s+/,""); -} -String.prototype.rtrim = function() { - return this.replace(/\s+$/,""); -} - -String.prototype.startsWith = function (str){ - return this.indexOf(str) == 0 -} - -String.prototype.endsWith = function (str){ - return new RegExp( RegExp.escape(str) + "$" ).test( this ) -} - -// Returns a copy padded with the provided character _chr_ so it becomes (at least) _length_ -// characters long. -// No truncation is performed if the string is already longer than _length_. -// @param length minimum length of the returned string -// @param right if falsy add leading whitespace, otherwise add trailing whitespace -// @param chr character to be used for padding, defaults to whitespace -// @return the padded string -String.prototype.pad = function(length, right, chr) { - if (typeof chr == 'undefined') chr = ' '; - var str = this; - for (var i = length - str.length; i > 0; i--) { - if (right) { - str = str + chr; - } else { - str = chr + str; - } - } - return str; -} - -Number.prototype.toPercentStr = function() { - return (this * 100).toFixed(2) + "%"; -} - -Number.prototype.zeroPad = function(width) { - return ('' + this).pad(width, false, '0'); -} - // Formats a simple stacked horizontal histogram bar in the shell. // @param data array of the form [[ratio, symbol], ...] where ratio is between 0 and 1 and // symbol is a string of length 1 @@ -399,191 +80,6 @@ _barFormat = function(data, width) { return res; } -Date.timeFunc = function( theFunc , numTimes ){ - - var start = new Date(); - - numTimes = numTimes || 1; - for ( var i=0; i<numTimes; i++ ){ - theFunc.apply( null , argumentsToArray( arguments ).slice( 2 ) ); - } - - return (new Date()).getTime() - start.getTime(); -} - -Date.prototype.tojson = function(){ - - var UTC = Date.printAsUTC ? 'UTC' : ''; - - var year = this['get'+UTC+'FullYear']().zeroPad(4); - var month = (this['get'+UTC+'Month']() + 1).zeroPad(2); - var date = this['get'+UTC+'Date']().zeroPad(2); - var hour = this['get'+UTC+'Hours']().zeroPad(2); - var minute = this['get'+UTC+'Minutes']().zeroPad(2); - var sec = this['get'+UTC+'Seconds']().zeroPad(2) - - if (this['get'+UTC+'Milliseconds']()) - sec += '.' + this['get'+UTC+'Milliseconds']().zeroPad(3) - - var ofs = 'Z'; - if (!Date.printAsUTC){ - var ofsmin = this.getTimezoneOffset(); - if (ofsmin != 0){ - ofs = ofsmin > 0 ? '-' : '+'; // This is correct - ofs += (ofsmin/60).zeroPad(2) - ofs += (ofsmin%60).zeroPad(2) - } - } - - return 'ISODate("'+year+'-'+month+'-'+date+'T'+hour+':'+minute+':'+sec+ofs+'")'; -} - -Date.printAsUTC = true; - - -ISODate = function(isoDateStr){ - if (!isoDateStr) - return new Date(); - - var isoDateRegex = /(\d{4})-?(\d{2})-?(\d{2})([T ](\d{2})(:?(\d{2})(:?(\d{2}(\.\d+)?))?)?(Z|([+-])(\d{2}):?(\d{2})?)?)?/; - var res = isoDateRegex.exec(isoDateStr); - - if (!res) - throw "invalid ISO date"; - - var year = parseInt(res[1],10) || 1970; // this should always be present - var month = (parseInt(res[2],10) || 1) - 1; - var date = parseInt(res[3],10) || 0; - var hour = parseInt(res[5],10) || 0; - var min = parseInt(res[7],10) || 0; - var sec = parseFloat(res[9]) || 0; - var ms = Math.round((sec%1) * 1000) - sec -= ms/1000 - - var time = Date.UTC(year, month, date, hour, min, sec, ms); - - if (res[11] && res[11] != 'Z'){ - var ofs = 0; - ofs += (parseInt(res[13],10) || 0) * 60*60*1000; // hours - ofs += (parseInt(res[14],10) || 0) * 60*1000; // mins - if (res[12] == '+') // if ahead subtract - ofs *= -1; - - time += ofs - } - - return new Date(time); -} - -RegExp.escape = function( text ){ - return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); -} - -RegExp.prototype.tojson = RegExp.prototype.toString; - -Array.contains = function( a , x ){ - for ( var i=0; i<a.length; i++ ){ - if ( a[i] == x ) - return true; - } - return false; -} - -Array.unique = function( a ){ - var u = []; - for ( var i=0; i<a.length; i++){ - var o = a[i]; - if ( ! Array.contains( u , o ) ){ - u.push( o ); - } - } - return u; -} - -Array.shuffle = function( arr ){ - for ( var i=0; i<arr.length-1; i++ ){ - var pos = i+Random.randInt(arr.length-i); - var save = arr[i]; - arr[i] = arr[pos]; - arr[pos] = save; - } - return arr; -} - - -Array.tojson = function( a , indent , nolint ){ - var lineEnding = nolint ? " " : "\n"; - - if (!indent) - indent = ""; - - if ( nolint ) - indent = ""; - - if (a.length == 0) { - return "[ ]"; - } - - var s = "[" + lineEnding; - indent += "\t"; - for ( var i=0; i<a.length; i++){ - s += indent + tojson( a[i], indent , nolint ); - if ( i < a.length - 1 ){ - s += "," + lineEnding; - } - } - if ( a.length == 0 ) { - s += indent; - } - - indent = indent.substring(1); - s += lineEnding+indent+"]"; - return s; -} - -Array.fetchRefs = function( arr , coll ){ - var n = []; - for ( var i=0; i<arr.length; i ++){ - var z = arr[i]; - if ( coll && coll != z.getCollection() ) - continue; - n.push( z.fetch() ); - } - - return n; -} - -Array.sum = function( arr ){ - if ( arr.length == 0 ) - return null; - var s = arr[0]; - for ( var i=1; i<arr.length; i++ ) - s += arr[i]; - return s; -} - -Array.avg = function( arr ){ - if ( arr.length == 0 ) - return null; - return Array.sum( arr ) / arr.length; -} - -Array.stdDev = function( arr ){ - var avg = Array.avg( arr ); - var sum = 0; - - for ( var i=0; i<arr.length; i++ ){ - sum += Math.pow( arr[i] - avg , 2 ); - } - - return Math.sqrt( sum / arr.length ); -} - -if( typeof Array.isArray != "function" ){ - Array.isArray = function( arr ){ - return arr != undefined && arr.constructor == Array - } -} //these two are helpers for Array.sort(func) compare = function(l, r){ return (l == r ? 0 : (l < r ? -1 : 1)); } @@ -593,155 +89,6 @@ compareOn = function(field){ return function(l, r) { return compare(l[field], r[field]); } } -Object.keySet = function( o ) { - var ret = new Array(); - for( var i in o ) { - if ( !( i in o.__proto__ && o[ i ] === o.__proto__[ i ] ) ) { - ret.push( i ); - } - } - return ret; -} - -if ( ! NumberLong.prototype ) { - NumberLong.prototype = {} -} - -NumberLong.prototype.tojson = function() { - return this.toString(); -} - -if ( ! NumberInt.prototype ) { - NumberInt.prototype = {} -} - -NumberInt.prototype.tojson = function() { - return this.toString(); -} - -if ( ! ObjectId.prototype ) - ObjectId.prototype = {} - -ObjectId.prototype.toString = function(){ - return "ObjectId(" + tojson(this.str) + ")"; -} - -ObjectId.prototype.tojson = function(){ - return this.toString(); -} - -ObjectId.prototype.valueOf = function(){ - return this.str; -} - -ObjectId.prototype.isObjectId = true; - -ObjectId.prototype.getTimestamp = function(){ - return new Date(parseInt(this.valueOf().slice(0,8), 16)*1000); -} - -ObjectId.prototype.equals = function( other){ - return this.str == other.str; -} - -if ( typeof( DBPointer ) != "undefined" ){ - DBPointer.prototype.fetch = function(){ - assert( this.ns , "need a ns" ); - assert( this.id , "need an id" ); - - return db[ this.ns ].findOne( { _id : this.id } ); - } - - DBPointer.prototype.tojson = function(indent){ - return this.toString(); - } - - DBPointer.prototype.getCollection = function(){ - return this.ns; - } - - DBPointer.prototype.getId = function(){ - return this.id; - } - - DBPointer.prototype.toString = function(){ - return "DBPointer(" + tojson(this.ns) + ", " + tojson(this.id) + ")"; - } -} -else { - print( "warning: no DBPointer" ); -} - -if ( typeof( DBRef ) != "undefined" ){ - DBRef.prototype.fetch = function(){ - assert( this.$ref , "need a ns" ); - assert( this.$id , "need an id" ); - - return db[ this.$ref ].findOne( { _id : this.$id } ); - } - - DBRef.prototype.tojson = function(indent){ - return this.toString(); - } - - DBRef.prototype.getCollection = function(){ - return this.$ref; - } - - DBRef.prototype.getRef = function(){ - return this.$ref; - } - - DBRef.prototype.getId = function(){ - return this.$id; - } - - DBRef.prototype.toString = function(){ - return "DBRef(" + tojson(this.$ref) + ", " + tojson(this.$id) + ")"; - } -} -else { - print( "warning: no DBRef" ); -} - -if ( typeof( Timestamp ) != "undefined" ){ - Timestamp.prototype.tojson = function () { - return this.toString(); - } - - Timestamp.prototype.getTime = function () { - return this.t; - } - - Timestamp.prototype.getInc = function () { - return this.i; - } - - Timestamp.prototype.toString = function () { - return "Timestamp(" + this.t + ", " + this.i + ")"; - } -} -else { - print( "warning: no Timestamp class" ); -} - -if ( typeof( BinData ) != "undefined" ){ - BinData.prototype.tojson = function () { - return this.toString(); - } - - BinData.prototype.subtype = function () { - return this.type; - } - - BinData.prototype.length = function () { - return this.len; - } -} -else { - print( "warning: no BinData class" ); -} - if ( typeof _threadInject != "undefined" ){ //print( "fork() available!" ); @@ -974,126 +321,6 @@ if ( typeof _threadInject != "undefined" ){ } } -tojsononeline = function( x ){ - return tojson( x , " " , true ); -} - -tojson = function( x, indent , nolint ){ - if ( x === null ) - return "null"; - - if ( x === undefined ) - return "undefined"; - - if (!indent) - indent = ""; - - switch ( typeof x ) { - case "string": { - var s = "\""; - for ( var i=0; i<x.length; i++ ){ - switch (x[i]){ - case '"': s += '\\"'; break; - case '\\': s += '\\\\'; break; - case '\b': s += '\\b'; break; - case '\f': s += '\\f'; break; - case '\n': s += '\\n'; break; - case '\r': s += '\\r'; break; - case '\t': s += '\\t'; break; - - default: { - var code = x.charCodeAt(i); - if (code < 0x20){ - s += (code < 0x10 ? '\\u000' : '\\u00') + code.toString(16); - } else { - s += x[i]; - } - } - } - } - return s + "\""; - } - case "number": - case "boolean": - return "" + x; - case "object":{ - var s = tojsonObject( x, indent , nolint ); - if ( ( nolint == null || nolint == true ) && s.length < 80 && ( indent == null || indent.length == 0 ) ){ - s = s.replace( /[\s\r\n ]+/gm , " " ); - } - return s; - } - case "function": - return x.toString(); - default: - throw "tojson can't handle type " + ( typeof x ); - } - -} - -tojsonObject = function( x, indent , nolint ){ - var lineEnding = nolint ? " " : "\n"; - var tabSpace = nolint ? "" : "\t"; - - assert.eq( ( typeof x ) , "object" , "tojsonObject needs object, not [" + ( typeof x ) + "]" ); - - if (!indent) - indent = ""; - - if ( typeof( x.tojson ) == "function" && x.tojson != tojson ) { - return x.tojson(indent,nolint); - } - - if ( x.constructor && typeof( x.constructor.tojson ) == "function" && x.constructor.tojson != tojson ) { - return x.constructor.tojson( x, indent , nolint ); - } - - try { - // modify display of min/max key for spidermonkey - if ( x.toString() == "[object MaxKey]" ) - return "{ $maxKey : 1 }"; - if ( x.toString() == "[object MinKey]" ) - return "{ $minKey : 1 }"; - } - catch(e) { - // toString not callable - return "[object]"; - } - - var s = "{" + lineEnding; - - // push one level of indent - indent += tabSpace; - - var total = 0; - for ( var k in x ) total++; - if ( total == 0 ) { - s += indent + lineEnding; - } - - var keys = x; - if ( typeof( x._simpleKeys ) == "function" ) - keys = x._simpleKeys(); - var num = 1; - for ( var k in keys ){ - - var val = x[k]; - if ( val == DB.prototype || val == DBCollection.prototype ) - continue; - - s += indent + "\"" + k + "\" : " + tojson( val, indent , nolint ); - if (num != total) { - s += ","; - num++; - } - s += lineEnding; - } - - // pop one level of indent - indent = indent.substring(1); - return s + indent + "}"; -} - shellPrint = function( x ){ it = x; if ( x != undefined ) @@ -1620,77 +847,6 @@ shellHelper.show = function (what) { } -if ( typeof( Map ) == "undefined" ){ - Map = function(){ - this._data = {}; - } -} - -Map.hash = function( val ){ - if ( ! val ) - return val; - - switch ( typeof( val ) ){ - case 'string': - case 'number': - case 'date': - return val.toString(); - case 'object': - case 'array': - var s = ""; - for ( var k in val ){ - s += k + val[k]; - } - return s; - } - - throw "can't hash : " + typeof( val ); -} - -Map.prototype.put = function( key , value ){ - var o = this._get( key ); - var old = o.value; - o.value = value; - return old; -} - -Map.prototype.get = function( key ){ - return this._get( key ).value; -} - -Map.prototype._get = function( key ){ - var h = Map.hash( key ); - var a = this._data[h]; - if ( ! a ){ - a = []; - this._data[h] = a; - } - - for ( var i=0; i<a.length; i++ ){ - if ( friendlyEqual( key , a[i].key ) ){ - return a[i]; - } - } - var o = { key : key , value : null }; - a.push( o ); - return o; -} - -Map.prototype.values = function(){ - var all = []; - for ( var k in this._data ){ - this._data[k].forEach( function(z){ all.push( z.value ); } ); - } - return all; -} - -if ( typeof( gc ) == "undefined" ){ - gc = function(){ - print( "warning: using noop gc()" ); - } -} - - Math.sigFig = function( x , N ){ if ( ! N ){ N = 3; |