diff options
Diffstat (limited to 'shell')
-rw-r--r-- | shell/collection.js | 569 | ||||
-rw-r--r-- | shell/db.js | 632 | ||||
-rw-r--r-- | shell/dbshell.cpp | 503 | ||||
-rw-r--r-- | shell/mongo.js | 84 | ||||
-rw-r--r-- | shell/mongo_vstudio.cpp | 2006 | ||||
-rw-r--r-- | shell/mr.js | 95 | ||||
-rw-r--r-- | shell/query.js | 248 | ||||
-rw-r--r-- | shell/servers.js | 608 | ||||
-rw-r--r-- | shell/utils.cpp | 541 | ||||
-rw-r--r-- | shell/utils.h | 27 | ||||
-rw-r--r-- | shell/utils.js | 907 |
11 files changed, 6220 insertions, 0 deletions
diff --git a/shell/collection.js b/shell/collection.js new file mode 100644 index 0000000..d228ba7 --- /dev/null +++ b/shell/collection.js @@ -0,0 +1,569 @@ +// collection.js + + +if ( ( typeof DBCollection ) == "undefined" ){ + DBCollection = function( mongo , db , shortName , fullName ){ + this._mongo = mongo; + this._db = db; + this._shortName = shortName; + this._fullName = fullName; + + this.verify(); + } +} + +DBCollection.prototype.verify = function(){ + assert( this._fullName , "no fullName" ); + assert( this._shortName , "no shortName" ); + assert( this._db , "no db" ); + + assert.eq( this._fullName , this._db._name + "." + this._shortName , "name mismatch" ); + + assert( this._mongo , "no mongo in DBCollection" ); +} + +DBCollection.prototype.getName = function(){ + return this._shortName; +} + +DBCollection.prototype.help = function(){ + print("DBCollection help"); + print("\tdb.foo.count()"); + print("\tdb.foo.dataSize()"); + print("\tdb.foo.distinct( key ) - eg. db.foo.distinct( 'x' )" ); + print("\tdb.foo.drop() drop the collection"); + print("\tdb.foo.dropIndex(name)"); + print("\tdb.foo.dropIndexes()"); + print("\tdb.foo.ensureIndex(keypattern,options) - options should be an object with these possible fields: name, unique, dropDups"); + print("\tdb.foo.find( [query] , [fields]) - first parameter is an optional query filter. second parameter is optional set of fields to return."); + print("\t e.g. db.foo.find( { x : 77 } , { name : 1 , x : 1 } )"); + print("\tdb.foo.find(...).count()"); + print("\tdb.foo.find(...).limit(n)"); + print("\tdb.foo.find(...).skip(n)"); + print("\tdb.foo.find(...).sort(...)"); + print("\tdb.foo.findOne([query])"); + print("\tdb.foo.findAndModify( { update : ... , remove : bool [, query: {}, sort: {}, 'new': false] } )"); + print("\tdb.foo.getDB() get DB object associated with collection"); + print("\tdb.foo.getIndexes()"); + print("\tdb.foo.group( { key : ..., initial: ..., reduce : ...[, cond: ...] } )"); + print("\tdb.foo.mapReduce( mapFunction , reduceFunction , <optional params> )" ); + print("\tdb.foo.remove(query)" ); + print("\tdb.foo.renameCollection( newName , <dropTarget> ) renames the collection."); + print("\tdb.foo.save(obj)"); + print("\tdb.foo.stats()"); + print("\tdb.foo.storageSize() - includes free space allocated to this collection"); + print("\tdb.foo.totalIndexSize() - size in bytes of all the indexes"); + print("\tdb.foo.totalSize() - storage allocated for all data and indexes"); + print("\tdb.foo.update(query, object[, upsert_bool, multi_bool])"); + print("\tdb.foo.validate() - SLOW"); + print("\tdb.foo.getShardVersion() - only for use with sharding" ); +} + +DBCollection.prototype.getFullName = function(){ + return this._fullName; +} +DBCollection.prototype.getDB = function(){ + return this._db; +} + +DBCollection.prototype._dbCommand = function( cmd ){ + return this._db._dbCommand( cmd ); +} + +DBCollection.prototype._massageObject = function( q ){ + if ( ! q ) + return {}; + + var type = typeof q; + + if ( type == "function" ) + return { $where : q }; + + if ( q.isObjectId ) + return { _id : q }; + + if ( type == "object" ) + return q; + + if ( type == "string" ){ + if ( q.length == 24 ) + return { _id : q }; + + return { $where : q }; + } + + throw "don't know how to massage : " + type; + +} + + +DBCollection.prototype._validateObject = function( o ){ + if ( o._ensureSpecial && o._checkModify ) + throw "can't save a DBQuery object"; +} + +DBCollection._allowedFields = { $id : 1 , $ref : 1 }; + +DBCollection.prototype._validateForStorage = function( o ){ + this._validateObject( o ); + for ( var k in o ){ + if ( k.indexOf( "." ) >= 0 ) { + throw "can't have . in field names [" + k + "]" ; + } + + if ( k.indexOf( "$" ) == 0 && ! DBCollection._allowedFields[k] ) { + throw "field names cannot start with $ [" + k + "]"; + } + + if ( o[k] !== null && typeof( o[k] ) === "object" ) { + this._validateForStorage( o[k] ); + } + } +}; + + +DBCollection.prototype.find = function( query , fields , limit , skip ){ + return new DBQuery( this._mongo , this._db , this , + this._fullName , this._massageObject( query ) , fields , limit , skip ); +} + +DBCollection.prototype.findOne = function( query , fields ){ + var cursor = this._mongo.find( this._fullName , this._massageObject( query ) || {} , fields , -1 , 0 ); + if ( ! cursor.hasNext() ) + return null; + var ret = cursor.next(); + if ( cursor.hasNext() ) throw "findOne has more than 1 result!"; + if ( ret.$err ) + throw "error " + tojson( ret ); + return ret; +} + +DBCollection.prototype.insert = function( obj , _allow_dot ){ + if ( ! obj ) + throw "no object passed to insert!"; + if ( ! _allow_dot ) { + this._validateForStorage( obj ); + } + return this._mongo.insert( this._fullName , obj ); +} + +DBCollection.prototype.remove = function( t ){ + this._mongo.remove( this._fullName , this._massageObject( t ) ); +} + +DBCollection.prototype.update = function( query , obj , upsert , multi ){ + assert( query , "need a query" ); + assert( obj , "need an object" ); + this._validateObject( obj ); + this._mongo.update( this._fullName , query , obj , upsert ? true : false , multi ? true : false ); +} + +DBCollection.prototype.save = function( obj ){ + if ( obj == null || typeof( obj ) == "undefined" ) + throw "can't save a null"; + + if ( typeof( obj._id ) == "undefined" ){ + obj._id = new ObjectId(); + return this.insert( obj ); + } + else { + return this.update( { _id : obj._id } , obj , true ); + } +} + +DBCollection.prototype._genIndexName = function( keys ){ + var name = ""; + for ( var k in keys ){ + var v = keys[k]; + if ( typeof v == "function" ) + continue; + + if ( name.length > 0 ) + name += "_"; + name += k + "_"; + + if ( typeof v == "number" ) + name += v; + } + return name; +} + +DBCollection.prototype._indexSpec = function( keys, options ) { + var ret = { ns : this._fullName , key : keys , name : this._genIndexName( keys ) }; + + if ( ! options ){ + } + else if ( typeof ( options ) == "string" ) + ret.name = options; + else if ( typeof ( options ) == "boolean" ) + ret.unique = true; + else if ( typeof ( options ) == "object" ){ + if ( options.length ){ + var nb = 0; + for ( var i=0; i<options.length; i++ ){ + if ( typeof ( options[i] ) == "string" ) + ret.name = options[i]; + else if ( typeof( options[i] ) == "boolean" ){ + if ( options[i] ){ + if ( nb == 0 ) + ret.unique = true; + if ( nb == 1 ) + ret.dropDups = true; + } + nb++; + } + } + } + else { + Object.extend( ret , options ); + } + } + else { + throw "can't handle: " + typeof( options ); + } + /* + return ret; + + var name; + var nTrue = 0; + + if ( ! isObject( options ) ) { + options = [ options ]; + } + + if ( options.length ){ + for( var i = 0; i < options.length; ++i ) { + var o = options[ i ]; + if ( isString( o ) ) { + ret.name = o; + } else if ( typeof( o ) == "boolean" ) { + if ( o ) { + ++nTrue; + } + } + } + if ( nTrue > 0 ) { + ret.unique = true; + } + if ( nTrue > 1 ) { + ret.dropDups = true; + } + } +*/ + return ret; +} + +DBCollection.prototype.createIndex = function( keys , options ){ + var o = this._indexSpec( keys, options ); + this._db.getCollection( "system.indexes" ).insert( o , true ); +} + +DBCollection.prototype.ensureIndex = function( keys , options ){ + var name = this._indexSpec( keys, options ).name; + this._indexCache = this._indexCache || {}; + if ( this._indexCache[ name ] ){ + return; + } + + this.createIndex( keys , options ); + if ( this.getDB().getLastError() == "" ) { + this._indexCache[name] = true; + } +} + +DBCollection.prototype.resetIndexCache = function(){ + this._indexCache = {}; +} + +DBCollection.prototype.reIndex = function(){ + var specs = this.getIndexSpecs(); + this.dropIndexes(); + for ( var i = 0; i < specs.length; ++i ){ + this.ensureIndex( specs[i].key, [ specs[i].unique, specs[i].name ] ); + } +} + +DBCollection.prototype.dropIndexes = function(){ + this.resetIndexCache(); + + var res = this._db.runCommand( { deleteIndexes: this.getName(), index: "*" } ); + assert( res , "no result from dropIndex result" ); + if ( res.ok ) + return res; + + if ( res.errmsg.match( /not found/ ) ) + return res; + + throw "error dropping indexes : " + tojson( res ); +} + + +DBCollection.prototype.drop = function(){ + this.resetIndexCache(); + var ret = this._db.runCommand( { drop: this.getName() } ); + if ( ! ret.ok ){ + if ( ret.errmsg == "ns not found" ) + return false; + throw "drop failed: " + tojson( ret ); + } + return true; +} + +DBCollection.prototype.findAndModify = function(args){ + var cmd = { findandmodify: this.getName() }; + for (key in args){ + cmd[key] = args[key]; + } + + var ret = this._db.runCommand( cmd ); + if ( ! ret.ok ){ + if (ret.errmsg == "No matching object found"){ + return {}; + } + throw "findAndModifyFailed failed: " + tojson( ret.errmsg ); + } + return ret.value; +} + +DBCollection.prototype.renameCollection = function( newName , dropTarget ){ + return this._db._adminCommand( { renameCollection : this._fullName , + to : this._db._name + "." + newName , + dropTarget : dropTarget } ) +} + +DBCollection.prototype.validate = function() { + var res = this._db.runCommand( { validate: this.getName() } ); + + res.valid = false; + + if ( res.result ){ + var str = "-" + tojson( res.result ); + res.valid = ! ( str.match( /exception/ ) || str.match( /corrupt/ ) ); + + var p = /lastExtentSize:(\d+)/; + var r = p.exec( str ); + if ( r ){ + res.lastExtentSize = Number( r[1] ); + } + } + + return res; +} + +DBCollection.prototype.getShardVersion = function(){ + return this._db._adminCommand( { getShardVersion : this._fullName } ); +} + +DBCollection.prototype.getIndexes = function(){ + return this.getDB().getCollection( "system.indexes" ).find( { ns : this.getFullName() } ).toArray(); +} + +DBCollection.prototype.getIndices = DBCollection.prototype.getIndexes; +DBCollection.prototype.getIndexSpecs = DBCollection.prototype.getIndexes; + +DBCollection.prototype.getIndexKeys = function(){ + return this.getIndexes().map( + function(i){ + return i.key; + } + ); +} + + +DBCollection.prototype.count = function( x ){ + return this.find( x ).count(); +} + +/** + * Drop free lists. Normally not used. + * Note this only does the collection itself, not the namespaces of its indexes (see cleanAll). + */ +DBCollection.prototype.clean = function() { + return this._dbCommand( { clean: this.getName() } ); +} + + + +/** + * <p>Drop a specified index.</p> + * + * <p> + * Name is the name of the index in the system.indexes name field. (Run db.system.indexes.find() to + * see example data.) + * </p> + * + * <p>Note : alpha: space is not reclaimed </p> + * @param {String} name of index to delete. + * @return A result object. result.ok will be true if successful. + */ +DBCollection.prototype.dropIndex = function(index) { + assert(index , "need to specify index to dropIndex" ); + + if ( ! isString( index ) && isObject( index ) ) + index = this._genIndexName( index ); + + var res = this._dbCommand( { deleteIndexes: this.getName(), index: index } ); + this.resetIndexCache(); + return res; +} + +DBCollection.prototype.copyTo = function( newName ){ + return this.getDB().eval( + function( collName , newName ){ + var from = db[collName]; + var to = db[newName]; + to.ensureIndex( { _id : 1 } ); + var count = 0; + + var cursor = from.find(); + while ( cursor.hasNext() ){ + var o = cursor.next(); + count++; + to.save( o ); + } + + return count; + } , this.getName() , newName + ); +} + +DBCollection.prototype.getCollection = function( subName ){ + return this._db.getCollection( this._shortName + "." + subName ); +} + +DBCollection.prototype.stats = function(){ + return this._db.runCommand( { collstats : this._shortName } ); +} + +DBCollection.prototype.dataSize = function(){ + return this.stats().size; +} + +DBCollection.prototype.storageSize = function(){ + return this.stats().storageSize; +} + +DBCollection.prototype.totalIndexSize = function( verbose ){ + var total = 0; + var mydb = this._db; + var shortName = this._shortName; + this.getIndexes().forEach( + function( spec ){ + var coll = mydb.getCollection( shortName + ".$" + spec.name ); + var mysize = coll.dataSize(); + total += coll.dataSize(); + if ( verbose ) { + print( coll + "\t" + mysize ); + } + } + ); + return total; +} + + +DBCollection.prototype.totalSize = function(){ + var total = this.storageSize(); + var mydb = this._db; + var shortName = this._shortName; + this.getIndexes().forEach( + function( spec ){ + var coll = mydb.getCollection( shortName + ".$" + spec.name ); + var mysize = coll.storageSize(); + //print( coll + "\t" + mysize + "\t" + tojson( coll.validate() ) ); + total += coll.dataSize(); + } + ); + return total; +} + + +DBCollection.prototype.convertToCapped = function( bytes ){ + if ( ! bytes ) + throw "have to specify # of bytes"; + return this._dbCommand( { convertToCapped : this._shortName , size : bytes } ) +} + +DBCollection.prototype.exists = function(){ + return this._db.system.namespaces.findOne( { name : this._fullName } ); +} + +DBCollection.prototype.isCapped = function(){ + var e = this.exists(); + return ( e && e.options && e.options.capped ) ? true : false; +} + +DBCollection.prototype.distinct = function( keyString , query ){ + var res = this._dbCommand( { distinct : this._shortName , key : keyString , query : query || {} } ); + if ( ! res.ok ) + throw "distinct failed: " + tojson( res ); + return res.values; +} + +DBCollection.prototype.group = function( params ){ + params.ns = this._shortName; + return this._db.group( params ); +} + +DBCollection.prototype.groupcmd = function( params ){ + params.ns = this._shortName; + return this._db.groupcmd( params ); +} + +MapReduceResult = function( db , o ){ + Object.extend( this , o ); + this._o = o; + this._keys = Object.keySet( o ); + this._db = db; + this._coll = this._db.getCollection( this.result ); +} + +MapReduceResult.prototype._simpleKeys = function(){ + return this._o; +} + +MapReduceResult.prototype.find = function(){ + return DBCollection.prototype.find.apply( this._coll , arguments ); +} + +MapReduceResult.prototype.drop = function(){ + return this._coll.drop(); +} + +/** +* just for debugging really +*/ +MapReduceResult.prototype.convertToSingleObject = function(){ + var z = {}; + this._coll.find().forEach( function(a){ z[a._id] = a.value; } ); + return z; +} + +/** +* @param optional object of optional fields; +*/ +DBCollection.prototype.mapReduce = function( map , reduce , optional ){ + var c = { mapreduce : this._shortName , map : map , reduce : reduce }; + if ( optional ) + Object.extend( c , optional ); + var raw = this._db.runCommand( c ); + if ( ! raw.ok ) + throw "map reduce failed: " + tojson( raw ); + return new MapReduceResult( this._db , raw ); + +} + +DBCollection.prototype.toString = function(){ + return this.getFullName(); +} + +DBCollection.prototype.toString = function(){ + return this.getFullName(); +} + + +DBCollection.prototype.tojson = DBCollection.prototype.toString; + +DBCollection.prototype.shellPrint = DBCollection.prototype.toString; + + + diff --git a/shell/db.js b/shell/db.js new file mode 100644 index 0000000..ab79e22 --- /dev/null +++ b/shell/db.js @@ -0,0 +1,632 @@ +// db.js + +if ( typeof DB == "undefined" ){ + DB = function( mongo , name ){ + this._mongo = mongo; + this._name = name; + } +} + +DB.prototype.getMongo = function(){ + assert( this._mongo , "why no mongo!" ); + return this._mongo; +} + +DB.prototype.getSisterDB = function( name ){ + return this.getMongo().getDB( name ); +} + +DB.prototype.getName = function(){ + return this._name; +} + +DB.prototype.getCollection = function( name ){ + return new DBCollection( this._mongo , this , name , this._name + "." + name ); +} + +DB.prototype.commandHelp = function( name ){ + var c = {}; + c[name] = 1; + c.help = true; + return this.runCommand( c ).help; +} + +DB.prototype.runCommand = function( obj ){ + if ( typeof( obj ) == "string" ){ + var n = {}; + n[obj] = 1; + obj = n; + } + return this.getCollection( "$cmd" ).findOne( obj ); +} + +DB.prototype._dbCommand = DB.prototype.runCommand; + +DB.prototype._adminCommand = function( obj ){ + if ( this._name == "admin" ) + return this.runCommand( obj ); + return this.getSisterDB( "admin" ).runCommand( obj ); +} + +DB.prototype.addUser = function( username , pass ){ + var c = this.getCollection( "system.users" ); + + var u = c.findOne( { user : username } ) || { user : username }; + u.pwd = hex_md5( username + ":mongo:" + pass ); + print( tojson( u ) ); + + c.save( u ); +} + +DB.prototype.removeUser = function( username ){ + this.getCollection( "system.users" ).remove( { user : username } ); +} + +DB.prototype.auth = function( username , pass ){ + var n = this.runCommand( { getnonce : 1 } ); + + var a = this.runCommand( + { + authenticate : 1 , + user : username , + nonce : n.nonce , + key : hex_md5( n.nonce + username + hex_md5( username + ":mongo:" + pass ) ) + } + ); + + return a.ok; +} + +/** + Create a new collection in the database. Normally, collection creation is automatic. You would + use this function if you wish to specify special options on creation. + + If the collection already exists, no action occurs. + + <p>Options:</p> + <ul> + <li> + size: desired initial extent size for the collection. Must be <= 1000000000. + for fixed size (capped) collections, this size is the total/max size of the + collection. + </li> + <li> + capped: if true, this is a capped collection (where old data rolls out). + </li> + <li> max: maximum number of objects if capped (optional).</li> + </ul> + + <p>Example: </p> + + <code>db.createCollection("movies", { size: 10 * 1024 * 1024, capped:true } );</code> + + * @param {String} name Name of new collection to create + * @param {Object} options Object with options for call. Options are listed above. + * @return SOMETHING_FIXME +*/ +DB.prototype.createCollection = function(name, opt) { + var options = opt || {}; + var cmd = { create: name, capped: options.capped, size: options.size, max: options.max }; + var res = this._dbCommand(cmd); + return res; +} + +/** + * Returns the current profiling level of this database + * @return SOMETHING_FIXME or null on error + */ + DB.prototype.getProfilingLevel = function() { + var res = this._dbCommand( { profile: -1 } ); + return res ? res.was : null; +} + + +/** + Erase the entire database. (!) + + * @return Object returned has member ok set to true if operation succeeds, false otherwise. +*/ +DB.prototype.dropDatabase = function() { + if ( arguments.length ) + throw "dropDatabase doesn't take arguments"; + return this._dbCommand( { dropDatabase: 1 } ); +} + + +DB.prototype.shutdownServer = function() { + if( "admin" != this._name ){ + return "shutdown command only works with the admin database; try 'use admin'"; + } + + try { + var res = this._dbCommand("shutdown"); + if( res ) + throw "shutdownServer failed: " + res.errmsg; + throw "shutdownServer failed"; + } + catch ( e ){ + assert( tojson( e ).indexOf( "error doing query: failed" ) >= 0 , "unexpected error: " + tojson( e ) ); + print( "server should be down..." ); + } +} + +/** + Clone database on another server to here. + <p> + Generally, you should dropDatabase() first as otherwise the cloned information will MERGE + into whatever data is already present in this database. (That is however a valid way to use + clone if you are trying to do something intentionally, such as union three non-overlapping + databases into one.) + <p> + This is a low level administrative function will is not typically used. + + * @param {String} from Where to clone from (dbhostname[:port]). May not be this database + (self) as you cannot clone to yourself. + * @return Object returned has member ok set to true if operation succeeds, false otherwise. + * See also: db.copyDatabase() +*/ +DB.prototype.cloneDatabase = function(from) { + assert( isString(from) && from.length ); + //this.resetIndexCache(); + return this._dbCommand( { clone: from } ); +} + + +/** + Clone collection on another server to here. + <p> + Generally, you should drop() first as otherwise the cloned information will MERGE + into whatever data is already present in this collection. (That is however a valid way to use + clone if you are trying to do something intentionally, such as union three non-overlapping + collections into one.) + <p> + This is a low level administrative function is not typically used. + + * @param {String} from mongod instance from which to clnoe (dbhostname:port). May + not be this mongod instance, as clone from self is not allowed. + * @param {String} collection name of collection to clone. + * @param {Object} query query specifying which elements of collection are to be cloned. + * @return Object returned has member ok set to true if operation succeeds, false otherwise. + * See also: db.cloneDatabase() + */ +DB.prototype.cloneCollection = function(from, collection, query) { + assert( isString(from) && from.length ); + assert( isString(collection) && collection.length ); + collection = this._name + "." + collection; + query = query || {}; + //this.resetIndexCache(); + return this._dbCommand( { cloneCollection:collection, from:from, query:query } ); +} + + +/** + Copy database from one server or name to another server or name. + + Generally, you should dropDatabase() first as otherwise the copied information will MERGE + into whatever data is already present in this database (and you will get duplicate objects + in collections potentially.) + + For security reasons this function only works when executed on the "admin" db. However, + if you have access to said db, you can copy any database from one place to another. + + This method provides a way to "rename" a database by copying it to a new db name and + location. Additionally, it effectively provides a repair facility. + + * @param {String} fromdb database name from which to copy. + * @param {String} todb database name to copy to. + * @param {String} fromhost hostname of the database (and optionally, ":port") from which to + copy the data. default if unspecified is to copy from self. + * @return Object returned has member ok set to true if operation succeeds, false otherwise. + * See also: db.clone() +*/ +DB.prototype.copyDatabase = function(fromdb, todb, fromhost) { + assert( isString(fromdb) && fromdb.length ); + assert( isString(todb) && todb.length ); + fromhost = fromhost || ""; + //this.resetIndexCache(); + return this._adminCommand( { copydb:1, fromhost:fromhost, fromdb:fromdb, todb:todb } ); +} + +/** + Repair database. + + * @return Object returned has member ok set to true if operation succeeds, false otherwise. +*/ +DB.prototype.repairDatabase = function() { + return this._dbCommand( { repairDatabase: 1 } ); +} + + +DB.prototype.help = function() { + print("DB methods:"); + print("\tdb.addUser(username, password)"); + print("\tdb.auth(username, password)"); + print("\tdb.cloneDatabase(fromhost)"); + print("\tdb.commandHelp(name) returns the help for the command"); + print("\tdb.copyDatabase(fromdb, todb, fromhost)"); + print("\tdb.createCollection(name, { size : ..., capped : ..., max : ... } )"); + print("\tdb.currentOp() displays the current operation in the db" ); + print("\tdb.dropDatabase()"); + print("\tdb.eval(func, args) run code server-side"); + print("\tdb.getCollection(cname) same as db['cname'] or db.cname"); + print("\tdb.getCollectionNames()"); + print("\tdb.getLastError() - just returns the err msg string"); + print("\tdb.getLastErrorObj() - return full status object"); + print("\tdb.getMongo() get the server connection object"); + print("\tdb.getMongo().setSlaveOk() allow this connection to read from the nonmaster member of a replica pair"); + print("\tdb.getName()"); + print("\tdb.getPrevError()"); + print("\tdb.getProfilingLevel()"); + print("\tdb.getReplicationInfo()"); + print("\tdb.getSisterDB(name) get the db at the same server as this onew"); + print("\tdb.killOp(opid) kills the current operation in the db" ); + print("\tdb.printCollectionStats()" ); + print("\tdb.printReplicationInfo()"); + print("\tdb.printSlaveReplicationInfo()"); + print("\tdb.printShardingStatus()"); + print("\tdb.removeUser(username)"); + print("\tdb.repairDatabase()"); + print("\tdb.resetError()"); + print("\tdb.runCommand(cmdObj) run a database command. if cmdObj is a string, turns it into { cmdObj : 1 }"); + print("\tdb.setProfilingLevel(level,<slowms>) 0=off 1=slow 2=all"); + print("\tdb.shutdownServer()"); + print("\tdb.version() current version of the server" ); +} + +DB.prototype.printCollectionStats = function(){ + var mydb = this; + this.getCollectionNames().forEach( + function(z){ + print( z ); + printjson( mydb.getCollection(z).stats() ); + print( "---" ); + } + ); +} + +/** + * <p> Set profiling level for your db. Profiling gathers stats on query performance. </p> + * + * <p>Default is off, and resets to off on a database restart -- so if you want it on, + * turn it on periodically. </p> + * + * <p>Levels :</p> + * <ul> + * <li>0=off</li> + * <li>1=log very slow (>100ms) operations</li> + * <li>2=log all</li> + * @param {String} level Desired level of profiling + * @return SOMETHING_FIXME or null on error + */ +DB.prototype.setProfilingLevel = function(level,slowms) { + + if (level < 0 || level > 2) { + throw { dbSetProfilingException : "input level " + level + " is out of range [0..2]" }; + } + + var cmd = { profile: level }; + if ( slowms ) + cmd["slowms"] = slowms; + return this._dbCommand( cmd ); +} + + +/** + * <p> Evaluate a js expression at the database server.</p> + * + * <p>Useful if you need to touch a lot of data lightly; in such a scenario + * the network transfer of the data could be a bottleneck. A good example + * is "select count(*)" -- can be done server side via this mechanism. + * </p> + * + * <p> + * If the eval fails, an exception is thrown of the form: + * </p> + * <code>{ dbEvalException: { retval: functionReturnValue, ok: num [, errno: num] [, errmsg: str] } }</code> + * + * <p>Example: </p> + * <code>print( "mycount: " + db.eval( function(){db.mycoll.find({},{_id:ObjId()}).length();} );</code> + * + * @param {Function} jsfunction Javascript function to run on server. Note this it not a closure, but rather just "code". + * @return result of your function, or null if error + * + */ +DB.prototype.eval = function(jsfunction) { + var cmd = { $eval : jsfunction }; + if ( arguments.length > 1 ) { + cmd.args = argumentsToArray( arguments ).slice(1); + } + + var res = this._dbCommand( cmd ); + + if (!res.ok) + throw tojson( res ); + + return res.retval; +} + +DB.prototype.dbEval = DB.prototype.eval; + + +/** + * + * <p> + * Similar to SQL group by. For example: </p> + * + * <code>select a,b,sum(c) csum from coll where active=1 group by a,b</code> + * + * <p> + * corresponds to the following in 10gen: + * </p> + * + * <code> + db.group( + { + ns: "coll", + key: { a:true, b:true }, + // keyf: ..., + cond: { active:1 }, + reduce: function(obj,prev) { prev.csum += obj.c; } , + initial: { csum: 0 } + }); + </code> + * + * + * <p> + * An array of grouped items is returned. The array must fit in RAM, thus this function is not + * suitable when the return set is extremely large. + * </p> + * <p> + * To order the grouped data, simply sort it client side upon return. + * <p> + Defaults + cond may be null if you want to run against all rows in the collection + keyf is a function which takes an object and returns the desired key. set either key or keyf (not both). + * </p> +*/ +DB.prototype.groupeval = function(parmsObj) { + + var groupFunction = function() { + var parms = args[0]; + var c = db[parms.ns].find(parms.cond||{}); + var map = new Map(); + var pks = parms.key ? Object.keySet( parms.key ) : null; + var pkl = pks ? pks.length : 0; + var key = {}; + + while( c.hasNext() ) { + var obj = c.next(); + if ( pks ) { + for( var i=0; i<pkl; i++ ){ + var k = pks[i]; + key[k] = obj[k]; + } + } + else { + key = parms.$keyf(obj); + } + + var aggObj = map.get(key); + if( aggObj == null ) { + var newObj = Object.extend({}, key); // clone + aggObj = Object.extend(newObj, parms.initial) + map.put( key , aggObj ); + } + parms.$reduce(obj, aggObj); + } + + return map.values(); + } + + return this.eval(groupFunction, this._groupFixParms( parmsObj )); +} + +DB.prototype.groupcmd = function( parmsObj ){ + var ret = this.runCommand( { "group" : this._groupFixParms( parmsObj ) } ); + if ( ! ret.ok ){ + throw "group command failed: " + tojson( ret ); + } + return ret.retval; +} + +DB.prototype.group = DB.prototype.groupcmd; + +DB.prototype._groupFixParms = function( parmsObj ){ + var parms = Object.extend({}, parmsObj); + + if( parms.reduce ) { + parms.$reduce = parms.reduce; // must have $ to pass to db + delete parms.reduce; + } + + if( parms.keyf ) { + parms.$keyf = parms.keyf; + delete parms.keyf; + } + + return parms; +} + +DB.prototype.resetError = function(){ + return this.runCommand( { reseterror : 1 } ); +} + +DB.prototype.forceError = function(){ + return this.runCommand( { forceerror : 1 } ); +} + +DB.prototype.getLastError = function(){ + var res = this.runCommand( { getlasterror : 1 } ); + if ( ! res.ok ) + throw "getlasterror failed: " + tojson( res ); + return res.err; +} +DB.prototype.getLastErrorObj = function(){ + var res = this.runCommand( { getlasterror : 1 } ); + if ( ! res.ok ) + throw "getlasterror failed: " + tojson( res ); + return res; +} +DB.prototype.getLastErrorCmd = DB.prototype.getLastErrorObj; + + +/* Return the last error which has occurred, even if not the very last error. + + Returns: + { err : <error message>, nPrev : <how_many_ops_back_occurred>, ok : 1 } + + result.err will be null if no error has occurred. + */ +DB.prototype.getPrevError = function(){ + return this.runCommand( { getpreverror : 1 } ); +} + +DB.prototype.getCollectionNames = function(){ + var all = []; + + var nsLength = this._name.length + 1; + + this.getCollection( "system.namespaces" ).find().sort({name:1}).forEach( + function(z){ + var name = z.name; + + if ( name.indexOf( "$" ) >= 0 ) + return; + + all.push( name.substring( nsLength ) ); + } + ); + return all; +} + +DB.prototype.tojson = function(){ + return this._name; +} + +DB.prototype.toString = function(){ + return this._name; +} + +DB.prototype.currentOp = function(){ + return db.$cmd.sys.inprog.findOne(); +} +DB.prototype.currentOP = DB.prototype.currentOp; + +DB.prototype.killOp = function(op) { + if( !op ) + throw "no opNum to kill specified"; + return db.$cmd.sys.killop.findOne({'op':op}); +} +DB.prototype.killOP = DB.prototype.killOp; + +DB.tsToSeconds = function(x){ + if ( x.t && x.i ) + return x.t / 1000; + return x / 4294967296; // low 32 bits are ordinal #s within a second +} + +/** + Get a replication log information summary. + <p> + This command is for the database/cloud administer and not applicable to most databases. + It is only used with the local database. One might invoke from the JS shell: + <pre> + use local + db.getReplicationInfo(); + </pre> + It is assumed that this database is a replication master -- the information returned is + about the operation log stored at local.oplog.$main on the replication master. (It also + works on a machine in a replica pair: for replica pairs, both machines are "masters" from + an internal database perspective. + <p> + * @return Object timeSpan: time span of the oplog from start to end if slave is more out + * of date than that, it can't recover without a complete resync +*/ +DB.prototype.getReplicationInfo = function() { + var db = this.getSisterDB("local"); + + var result = { }; + var ol = db.system.namespaces.findOne({name:"local.oplog.$main"}); + if( ol && ol.options ) { + result.logSizeMB = ol.options.size / 1000 / 1000; + } else { + result.errmsg = "local.oplog.$main, or its options, not found in system.namespaces collection (not --master?)"; + return result; + } + + var firstc = db.oplog.$main.find().sort({$natural:1}).limit(1); + var lastc = db.oplog.$main.find().sort({$natural:-1}).limit(1); + if( !firstc.hasNext() || !lastc.hasNext() ) { + result.errmsg = "objects not found in local.oplog.$main -- is this a new and empty db instance?"; + result.oplogMainRowCount = db.oplog.$main.count(); + return result; + } + + var first = firstc.next(); + var last = lastc.next(); + { + var tfirst = first.ts; + var tlast = last.ts; + + if( tfirst && tlast ) { + tfirst = DB.tsToSeconds( tfirst ); + tlast = DB.tsToSeconds( tlast ); + result.timeDiff = tlast - tfirst; + result.timeDiffHours = Math.round(result.timeDiff / 36)/100; + result.tFirst = (new Date(tfirst*1000)).toString(); + result.tLast = (new Date(tlast*1000)).toString(); + result.now = Date(); + } + else { + result.errmsg = "ts element not found in oplog objects"; + } + } + + return result; +} +DB.prototype.printReplicationInfo = function() { + var result = this.getReplicationInfo(); + if( result.errmsg ) { + print(tojson(result)); + return; + } + print("configured oplog size: " + result.logSizeMB + "MB"); + print("log length start to end: " + result.timeDiff + "secs (" + result.timeDiffHours + "hrs)"); + print("oplog first event time: " + result.tFirst); + print("oplog last event time: " + result.tLast); + print("now: " + result.now); +} + +DB.prototype.printSlaveReplicationInfo = function() { + function g(x) { + print("source: " + x.host); + var st = new Date( DB.tsToSeconds( x.syncedTo ) * 1000 ); + var now = new Date(); + print("syncedTo: " + st.toString() ); + var ago = (now-st)/1000; + var hrs = Math.round(ago/36)/100; + print(" = " + Math.round(ago) + "secs ago (" + hrs + "hrs)"); + } + var L = this.getSisterDB("local"); + if( L.sources.count() == 0 ) { + print("local.sources is empty; is this db a --slave?"); + return; + } + L.sources.find().forEach(g); +} + +DB.prototype.serverBuildInfo = function(){ + return this._adminCommand( "buildinfo" ); +} + +DB.prototype.serverStatus = function(){ + return this._adminCommand( "serverStatus" ); +} + +DB.prototype.version = function(){ + return this.serverBuildInfo().version; +} + +DB.prototype.printShardingStatus = function(){ + printShardingStatus( this.getSisterDB( "config" ) ); +} diff --git a/shell/dbshell.cpp b/shell/dbshell.cpp new file mode 100644 index 0000000..7984383 --- /dev/null +++ b/shell/dbshell.cpp @@ -0,0 +1,503 @@ +// dbshell.cpp + +#include <stdio.h> + +#ifdef USE_READLINE +#include <readline/readline.h> +#include <readline/history.h> +#include <setjmp.h> +jmp_buf jbuf; +#endif + +#include "../scripting/engine.h" +#include "../client/dbclient.h" +#include "../util/unittest.h" +#include "../db/cmdline.h" +#include "utils.h" + +using namespace std; +using namespace boost::filesystem; + +string historyFile; +bool gotInterrupted = 0; +bool inMultiLine = 0; + +#if defined(USE_READLINE) && !defined(__freebsd__) && !defined(_WIN32) +#define CTRLC_HANDLE +#endif + +void shellHistoryInit(){ +#ifdef USE_READLINE + stringstream ss; + char * h = getenv( "HOME" ); + if ( h ) + ss << h << "/"; + ss << ".dbshell"; + historyFile = ss.str(); + + using_history(); + read_history( historyFile.c_str() ); + +#else + cout << "type \"exit\" to exit" << endl; +#endif +} +void shellHistoryDone(){ +#ifdef USE_READLINE + write_history( historyFile.c_str() ); +#endif +} +void shellHistoryAdd( const char * line ){ + if ( strlen(line) == 0 ) + return; +#ifdef USE_READLINE + add_history( line ); +#endif +} + +void intr( int sig ){ +#ifdef CTRLC_HANDLE + longjmp( jbuf , 1 ); +#endif +} + +#if !defined(_WIN32) +void quitNicely( int sig ){ + if ( sig == SIGINT && inMultiLine ){ + gotInterrupted = 1; + return; + } + if ( sig == SIGPIPE ) + mongo::rawOut( "mongo got signal SIGPIPE\n" ); + shellHistoryDone(); + exit(0); +} +#endif + +char * shellReadline( const char * prompt , int handlesigint = 0 ){ +#ifdef USE_READLINE + +#ifdef CTRLC_HANDLE + if ( ! handlesigint ) + return readline( prompt ); + if ( setjmp( jbuf ) ){ + gotInterrupted = 1; + sigrelse(SIGINT); + signal( SIGINT , quitNicely ); + return 0; + } + signal( SIGINT , intr ); +#endif + + char * ret = readline( prompt ); + signal( SIGINT , quitNicely ); + return ret; +#else + printf( prompt ); + char * buf = new char[1024]; + char * l = fgets( buf , 1024 , stdin ); + int len = strlen( buf ); + buf[len-1] = 0; + return l; +#endif +} + +#if !defined(_WIN32) +#include <string.h> + +void quitAbruptly( int sig ) { + ostringstream ossSig; + ossSig << "mongo got signal " << sig << " (" << strsignal( sig ) << "), stack trace: " << endl; + mongo::rawOut( ossSig.str() ); + + ostringstream ossBt; + mongo::printStackTrace( ossBt ); + mongo::rawOut( ossBt.str() ); + + mongo::shellUtils::KillMongoProgramInstances(); + exit(14); +} + +void setupSignals() { + signal( SIGINT , quitNicely ); + signal( SIGTERM , quitNicely ); + signal( SIGPIPE , quitNicely ); // Maybe just log and continue? + signal( SIGABRT , quitAbruptly ); + signal( SIGSEGV , quitAbruptly ); + signal( SIGBUS , quitAbruptly ); + signal( SIGFPE , quitAbruptly ); +} +#else +inline void setupSignals() {} +#endif + +string fixHost( string url , string host , string port ){ + //cout << "fixHost url: " << url << " host: " << host << " port: " << port << endl; + + if ( host.size() == 0 && port.size() == 0 ){ + if ( url.find( "/" ) == string::npos ){ + // check for ips + if ( url.find( "." ) != string::npos ) + return url + "/test"; + + if ( url.find( ":" ) != string::npos && + isdigit( url[url.find(":")+1] ) ) + return url + "/test"; + } + return url; + } + + if ( url.find( "/" ) != string::npos ){ + cerr << "url can't have host or port if you specify them individually" << endl; + exit(-1); + } + + if ( host.size() == 0 ) + host = "127.0.0.1"; + + string newurl = host; + if ( port.size() > 0 ) + newurl += ":" + port; + + newurl += "/" + url; + + return newurl; +} + +bool isBalanced( string code ){ + int brackets = 0; + int parens = 0; + + for ( size_t i=0; i<code.size(); i++ ){ + switch( code[i] ){ + case '/': + if ( i+1 < code.size() && code[i+1] == '/' ){ + while ( i<code.size() && code[i] != '\n' ) + i++; + } + continue; + case '{': brackets++; break; + case '}': if ( brackets <= 0 ) return true; brackets--; break; + case '(': parens++; break; + case ')': if ( parens <= 0 ) return true; parens--; break; + case '"': + i++; + while ( i < code.size() && code[i] != '"' ) i++; + break; + case '\'': + i++; + while ( i < code.size() && code[i] != '\'' ) i++; + break; + } + } + + return brackets == 0 && parens == 0; +} + +using mongo::asserted; + +struct BalancedTest : public mongo::UnitTest { +public: + void run(){ + assert( isBalanced( "x = 5" ) ); + assert( isBalanced( "function(){}" ) ); + assert( isBalanced( "function(){\n}" ) ); + assert( ! isBalanced( "function(){" ) ); + assert( isBalanced( "x = \"{\";" ) ); + assert( isBalanced( "// {" ) ); + assert( ! isBalanced( "// \n {" ) ); + assert( ! isBalanced( "\"//\" {" ) ); + + } +} balnaced_test; + +string finishCode( string code ){ + while ( ! isBalanced( code ) ){ + inMultiLine = 1; + code += "\n"; + char * line = shellReadline("... " , 1 ); + if ( gotInterrupted ) + return ""; + if ( ! line ) + return ""; + code += line; + } + return code; +} + +#include <boost/program_options.hpp> +namespace po = boost::program_options; + +void show_help_text(const char* name, po::options_description options) { + cout << "MongoDB shell version: " << mongo::versionString << endl; + cout << "usage: " << name << " [options] [db address] [file names (ending in .js)]" << endl + << "db address can be:" << endl + << " foo foo database on local machine" << endl + << " 192.169.0.5/foo foo database on 192.168.0.5 machine" << endl + << " 192.169.0.5:9999/foo foo database on 192.168.0.5 machine on port 9999" << endl + << options << endl + << "file names: a list of files to run. files have to end in .js and will exit after " + << "unless --shell is specified" << endl; +}; + +bool fileExists( string file ){ + try { + path p(file); + return boost::filesystem::exists( file ); + } + catch (...){ + return false; + } +} + +int _main(int argc, char* argv[]) { + setupSignals(); + + mongo::shellUtils::RecordMyLocation( argv[ 0 ] ); + + string url = "test"; + string dbhost; + string port; + vector<string> files; + + string username; + string password; + + bool runShell = false; + bool nodb = false; + + string script; + + po::options_description shell_options("options"); + po::options_description hidden_options("Hidden options"); + po::options_description cmdline_options("Command line options"); + po::positional_options_description positional_options; + + shell_options.add_options() + ("shell", "run the shell after executing files") + ("nodb", "don't connect to mongod on startup - no 'db address' arg expected") + ("quiet", "be less chatty" ) + ("port", po::value<string>(&port), "port to connect to") + ("host", po::value<string>(&dbhost), "server to connect to") + ("eval", po::value<string>(&script), "evaluate javascript") + ("username,u", po::value<string>(&username), "username for authentication") + ("password,p", po::value<string>(&password), "password for authentication") + ("help,h", "show this usage information") + ("version", "show version information") + ; + + hidden_options.add_options() + ("dbaddress", po::value<string>(), "dbaddress") + ("files", po::value< vector<string> >(), "files") + ; + + positional_options.add("dbaddress", 1); + positional_options.add("files", -1); + + cmdline_options.add(shell_options).add(hidden_options); + + po::variables_map params; + + /* using the same style as db.cpp uses because eventually we're going + * to merge some of this stuff. */ + int command_line_style = (((po::command_line_style::unix_style ^ + po::command_line_style::allow_guessing) | + po::command_line_style::allow_long_disguise) ^ + po::command_line_style::allow_sticky); + + try { + po::store(po::command_line_parser(argc, argv).options(cmdline_options). + positional(positional_options). + style(command_line_style).run(), params); + po::notify(params); + } catch (po::error &e) { + cout << "ERROR: " << e.what() << endl << endl; + show_help_text(argv[0], shell_options); + return mongo::EXIT_BADOPTIONS; + } + + if (params.count("shell")) { + runShell = true; + } + if (params.count("nodb")) { + nodb = true; + } + if (params.count("help")) { + show_help_text(argv[0], shell_options); + return mongo::EXIT_CLEAN; + } + if (params.count("files")) { + files = params["files"].as< vector<string> >(); + } + if (params.count("version")) { + cout << "MongoDB shell version: " << mongo::versionString << endl; + return mongo::EXIT_CLEAN; + } + if (params.count("quiet")) { + mongo::cmdLine.quiet = true; + } + + /* This is a bit confusing, here are the rules: + * + * if nodb is set then all positional parameters are files + * otherwise the first positional parameter might be a dbaddress, but + * only if one of these conditions is met: + * - it contains no '.' after the last appearance of '\' or '/' + * - it doesn't end in '.js' and it doesn't specify a path to an existing file */ + if (params.count("dbaddress")) { + string dbaddress = params["dbaddress"].as<string>(); + if (nodb) { + files.insert(files.begin(), dbaddress); + } else { + string basename = dbaddress.substr(dbaddress.find_last_of("/\\") + 1); + if (basename.find_first_of('.') == string::npos || + (basename.find(".js", basename.size() - 3) == string::npos && !fileExists(dbaddress))) { + url = dbaddress; + } else { + files.insert(files.begin(), dbaddress); + } + } + } + + if ( ! mongo::cmdLine.quiet ) + cout << "MongoDB shell version: " << mongo::versionString << endl; + + mongo::UnitTest::runTests(); + + if ( !nodb ) { // connect to db + if ( ! mongo::cmdLine.quiet ) cout << "url: " << url << endl; + + stringstream ss; + if ( mongo::cmdLine.quiet ) + ss << "__quiet = true;"; + ss << "db = connect( \"" << fixHost( url , dbhost , port ) << "\")"; + + mongo::shellUtils::_dbConnect = ss.str(); + + if ( username.size() && password.size() ){ + stringstream ss; + ss << "if ( ! db.auth( \"" << username << "\" , \"" << password << "\" ) ){ throw 'login failed'; }"; + mongo::shellUtils::_dbAuth = ss.str(); + } + + } + + mongo::ScriptEngine::setup(); + mongo::globalScriptEngine->setScopeInitCallback( mongo::shellUtils::initScope ); + auto_ptr< mongo::Scope > scope( mongo::globalScriptEngine->newScope() ); + + if ( !script.empty() ) { + mongo::shellUtils::MongoProgramScope s; + if ( ! scope->exec( script , "(shell eval)" , true , true , false ) ) + return -4; + } + + for (size_t i = 0; i < files.size(); i++) { + mongo::shellUtils::MongoProgramScope s; + + if ( files.size() > 1 ) + cout << "loading file: " << files[i] << endl; + + if ( ! scope->execFile( files[i] , false , true , false ) ){ + cout << "failed to load: " << files[i] << endl; + return -3; + } + } + + if ( files.size() == 0 && script.empty() ) { + runShell = true; + } + + if ( runShell ){ + + mongo::shellUtils::MongoProgramScope s; + + shellHistoryInit(); + + cout << "type \"help\" for help" << endl; + + //v8::Handle<v8::Object> shellHelper = baseContext_->Global()->Get( v8::String::New( "shellHelper" ) )->ToObject(); + + while ( 1 ){ + inMultiLine = 0; + gotInterrupted = 0; + char * line = shellReadline( "> " ); + + if ( line ) + while ( line[0] == ' ' ) + line++; + + if ( ! line || ( strlen(line) == 4 && strstr( line , "exit" ) ) ){ + cout << "bye" << endl; + break; + } + + string code = line; + if ( code == "exit" ){ + break; + } + if ( code.size() == 0 ) + continue; + + code = finishCode( code ); + if ( gotInterrupted ){ + cout << endl; + continue; + } + + if ( code.size() == 0 ) + break; + + bool wascmd = false; + { + string cmd = line; + if ( cmd.find( " " ) > 0 ) + cmd = cmd.substr( 0 , cmd.find( " " ) ); + + if ( cmd.find( "\"" ) == string::npos ){ + scope->exec( (string)"__iscmd__ = shellHelper[\"" + cmd + "\"];" , "(shellhelp1)" , false , true , true ); + if ( scope->getBoolean( "__iscmd__" ) ){ + scope->exec( (string)"shellHelper( \"" + cmd + "\" , \"" + code.substr( cmd.size() ) + "\");" , "(shellhelp2)" , false , true , false ); + wascmd = true; + } + } + + } + + if ( ! wascmd ){ + try { + scope->exec( code.c_str() , "(shell)" , false , true , false ); + scope->exec( "shellPrintHelper( __lastres__ );" , "(shell2)" , true , true , false ); + } + catch ( std::exception& e ){ + cout << "error:" << e.what() << endl; + } + } + + + shellHistoryAdd( line ); + } + + shellHistoryDone(); + } + + return 0; +} + +int main(int argc, char* argv[]) { + try { + return _main( argc , argv ); + } + catch ( mongo::DBException& e ){ + cerr << "exception: " << e.what() << endl; + return -1; + } +} + +namespace mongo { + DBClientBase * createDirectClient(){ + uassert( 10256 , "no createDirectClient in shell" , 0 ); + return 0; + } +} + diff --git a/shell/mongo.js b/shell/mongo.js new file mode 100644 index 0000000..dc7fcd9 --- /dev/null +++ b/shell/mongo.js @@ -0,0 +1,84 @@ +// mongo.js + +// NOTE 'Mongo' may be defined here or in MongoJS.cpp. Add code to init, not to this constructor. +if ( typeof Mongo == "undefined" ){ + Mongo = function( host ){ + this.init( host ); + } +} + +if ( ! Mongo.prototype ){ + throw "Mongo.prototype not defined"; +} + +if ( ! Mongo.prototype.find ) + Mongo.prototype.find = function( ns , query , fields , limit , skip ){ throw "find not implemented"; } +if ( ! Mongo.prototype.insert ) + Mongo.prototype.insert = function( ns , obj ){ throw "insert not implemented"; } +if ( ! Mongo.prototype.remove ) + Mongo.prototype.remove = function( ns , pattern ){ throw "remove not implemented;" } +if ( ! Mongo.prototype.update ) + Mongo.prototype.update = function( ns , query , obj , upsert ){ throw "update not implemented;" } + +if ( typeof mongoInject == "function" ){ + mongoInject( Mongo.prototype ); +} + +Mongo.prototype.setSlaveOk = function() { + this.slaveOk = true; +} + +Mongo.prototype.getDB = function( name ){ + return new DB( this , name ); +} + +Mongo.prototype.getDBs = function(){ + var res = this.getDB( "admin" ).runCommand( { "listDatabases" : 1 } ); + assert( res.ok == 1 , "listDatabases failed" ); + return res; +} + +Mongo.prototype.getDBNames = function(){ + return this.getDBs().databases.map( + function(z){ + return z.name; + } + ); +} + +Mongo.prototype.getCollection = function(ns){ + var idx = ns.indexOf( "." ); + if ( idx < 0 ) + throw "need . in ns"; + var db = ns.substring( 0 , idx ); + var c = ns.substring( idx + 1 ); + return this.getDB( db ).getCollection( c ); +} + +Mongo.prototype.toString = function(){ + return "mongo connection to " + this.host; +} + +connect = function( url , user , pass ){ + chatty( "connecting to: " + url ) + + if ( user && ! pass ) + throw "you specified a user and not a password. either you need a password, or you're using the old connect api"; + + var idx = url.indexOf( "/" ); + + var db; + + if ( idx < 0 ) + db = new Mongo().getDB( url ); + else + db = new Mongo( url.substring( 0 , idx ) ).getDB( url.substring( idx + 1 ) ); + + if ( user && pass ){ + if ( ! db.auth( user , pass ) ){ + throw "couldn't login"; + } + } + + return db; +} diff --git a/shell/mongo_vstudio.cpp b/shell/mongo_vstudio.cpp new file mode 100644 index 0000000..d0f1b48 --- /dev/null +++ b/shell/mongo_vstudio.cpp @@ -0,0 +1,2006 @@ +const char * jsconcatcode = +"if ( ( typeof DBCollection ) == \"undefined\" ){\n" + "DBCollection = function( mongo , db , shortName , fullName ){\n" + "this._mongo = mongo;\n" + "this._db = db;\n" + "this._shortName = shortName;\n" + "this._fullName = fullName;\n" + "this.verify();\n" + "}\n" + "}\n" + "DBCollection.prototype.verify = function(){\n" + "assert( this._fullName , \"no fullName\" );\n" + "assert( this._shortName , \"no shortName\" );\n" + "assert( this._db , \"no db\" );\n" + "assert.eq( this._fullName , this._db._name + \".\" + this._shortName , \"name mismatch\" );\n" + "assert( this._mongo , \"no mongo in DBCollection\" );\n" + "}\n" + "DBCollection.prototype.getName = function(){\n" + "return this._shortName;\n" + "}\n" + "DBCollection.prototype.help = function(){\n" + "print(\"DBCollection help\");\n" + "print(\"\\tdb.foo.getDB() get DB object associated with collection\");\n" + "print(\"\\tdb.foo.findOne([query])\");\n" + "print(\"\\tdb.foo.find( [query] , [fields]) - first parameter is an optional query filter. second parameter is optional set of fields to return.\");\n" + "print(\"\\t e.g. db.foo.find( { x : 77 } , { name : 1 , x : 1 } )\");\n" + "print(\"\\tdb.foo.find(...).sort(...)\");\n" + "print(\"\\tdb.foo.find(...).limit(n)\");\n" + "print(\"\\tdb.foo.find(...).skip(n)\");\n" + "print(\"\\tdb.foo.find(...).count()\");\n" + "print(\"\\tdb.foo.count()\");\n" + "print(\"\\tdb.foo.group( { key : ..., initial: ..., reduce : ...[, cond: ...] } )\");\n" + "print(\"\\tdb.foo.save(obj)\");\n" + "print(\"\\tdb.foo.update(query, object[, upsert_bool])\");\n" + "print(\"\\tdb.foo.remove(query)\" );\n" + "print(\"\\tdb.foo.ensureIndex(keypattern,options) - options should be an object with these possible fields: name, unique, dropDups\");\n" + "print(\"\\tdb.foo.dropIndexes()\");\n" + "print(\"\\tdb.foo.dropIndex(name)\");\n" + "print(\"\\tdb.foo.getIndexes()\");\n" + "print(\"\\tdb.foo.drop() drop the collection\");\n" + "print(\"\\tdb.foo.renameCollection( newName ) renames the collection\");\n" + "print(\"\\tdb.foo.validate() - SLOW\");\n" + "print(\"\\tdb.foo.stats()\");\n" + "print(\"\\tdb.foo.dataSize()\");\n" + "print(\"\\tdb.foo.storageSize() - includes free space allocated to this collection\");\n" + "print(\"\\tdb.foo.totalIndexSize() - size in bytes of all the indexes\");\n" + "print(\"\\tdb.foo.totalSize() - storage allocated for all data and indexes\");\n" + "}\n" + "DBCollection.prototype.getFullName = function(){\n" + "return this._fullName;\n" + "}\n" + "DBCollection.prototype.getDB = function(){\n" + "return this._db;\n" + "}\n" + "DBCollection.prototype._dbCommand = function( cmd ){\n" + "return this._db._dbCommand( cmd );\n" + "}\n" + "DBCollection.prototype._massageObject = function( q ){\n" + "if ( ! q )\n" + "return {};\n" + "var type = typeof q;\n" + "if ( type == \"function\" )\n" + "return { $where : q };\n" + "if ( q.isObjectId )\n" + "return { _id : q };\n" + "if ( type == \"object\" )\n" + "return q;\n" + "if ( type == \"string\" ){\n" + "if ( q.length == 24 )\n" + "return { _id : q };\n" + "return { $where : q };\n" + "}\n" + "throw \"don't know how to massage : \" + type;\n" + "}\n" + "DBCollection.prototype._validateObject = function( o ){\n" + "if ( o._ensureSpecial && o._checkModify )\n" + "throw \"can't save a DBQuery object\";\n" + "}\n" + "DBCollection._allowedFields = { $id : 1 , $ref : 1 };\n" + "DBCollection.prototype._validateForStorage = function( o ){\n" + "this._validateObject( o );\n" + "for ( var k in o ){\n" + "if ( k.indexOf( \".\" ) >= 0 ) {\n" + "throw \"can't have . in field names [\" + k + \"]\" ;\n" + "}\n" + "if ( k.indexOf( \"$\" ) == 0 && ! DBCollection._allowedFields[k] ) {\n" + "throw \"field names cannot start with $ [\" + k + \"]\";\n" + "}\n" + "if ( o[k] !== null && typeof( o[k] ) === \"object\" ) {\n" + "this._validateForStorage( o[k] );\n" + "}\n" + "}\n" + "};\n" + "DBCollection.prototype.find = function( query , fields , limit , skip ){\n" + "return new DBQuery( this._mongo , this._db , this ,\n" + "this._fullName , this._massageObject( query ) , fields , limit , skip );\n" + "}\n" + "DBCollection.prototype.findOne = function( query , fields ){\n" + "var cursor = this._mongo.find( this._fullName , this._massageObject( query ) || {} , fields , -1 , 0 );\n" + "if ( ! cursor.hasNext() )\n" + "return null;\n" + "var ret = cursor.next();\n" + "if ( cursor.hasNext() ) throw \"findOne has more than 1 result!\";\n" + "if ( ret.$err )\n" + "throw \"error \" + tojson( ret );\n" + "return ret;\n" + "}\n" + "DBCollection.prototype.insert = function( obj , _allow_dot ){\n" + "if ( ! obj )\n" + "throw \"no object!\";\n" + "if ( ! _allow_dot ) {\n" + "this._validateForStorage( obj );\n" + "}\n" + "return this._mongo.insert( this._fullName , obj );\n" + "}\n" + "DBCollection.prototype.remove = function( t ){\n" + "this._mongo.remove( this._fullName , this._massageObject( t ) );\n" + "}\n" + "DBCollection.prototype.update = function( query , obj , upsert ){\n" + "assert( query , \"need a query\" );\n" + "assert( obj , \"need an object\" );\n" + "this._validateObject( obj );\n" + "return this._mongo.update( this._fullName , query , obj , upsert ? true : false );\n" + "}\n" + "DBCollection.prototype.save = function( obj ){\n" + "if ( obj == null || typeof( obj ) == \"undefined\" )\n" + "throw \"can't save a null\";\n" + "if ( typeof( obj._id ) == \"undefined\" ){\n" + "obj._id = new ObjectId();\n" + "return this.insert( obj );\n" + "}\n" + "else {\n" + "return this.update( { _id : obj._id } , obj , true );\n" + "}\n" + "}\n" + "DBCollection.prototype._genIndexName = function( keys ){\n" + "var name = \"\";\n" + "for ( var k in keys ){\n" + "if ( name.length > 0 )\n" + "name += \"_\";\n" + "name += k + \"_\";\n" + "var v = keys[k];\n" + "if ( typeof v == \"number\" )\n" + "name += v;\n" + "}\n" + "return name;\n" + "}\n" + "DBCollection.prototype._indexSpec = function( keys, options ) {\n" + "var ret = { ns : this._fullName , key : keys , name : this._genIndexName( keys ) };\n" + "if ( ! options ){\n" + "}\n" + "else if ( typeof ( options ) == \"string\" )\n" + "ret.name = options;\n" + "else if ( typeof ( options ) == \"boolean\" )\n" + "ret.unique = true;\n" + "else if ( typeof ( options ) == \"object\" ){\n" + "if ( options.length ){\n" + "var nb = 0;\n" + "for ( var i=0; i<options.length; i++ ){\n" + "if ( typeof ( options[i] ) == \"string\" )\n" + "ret.name = options[i];\n" + "else if ( typeof( options[i] ) == \"boolean\" ){\n" + "if ( options[i] ){\n" + "if ( nb == 0 )\n" + "ret.unique = true;\n" + "if ( nb == 1 )\n" + "ret.dropDups = true;\n" + "}\n" + "nb++;\n" + "}\n" + "}\n" + "}\n" + "else {\n" + "Object.extend( ret , options );\n" + "}\n" + "}\n" + "else {\n" + "throw \"can't handle: \" + typeof( options );\n" + "}\n" + "/*\n" + "return ret;\n" + "var name;\n" + "var nTrue = 0;\n" + "if ( ! isObject( options ) ) {\n" + "options = [ options ];\n" + "}\n" + "if ( options.length ){\n" + "for( var i = 0; i < options.length; ++i ) {\n" + "var o = options[ i ];\n" + "if ( isString( o ) ) {\n" + "ret.name = o;\n" + "} else if ( typeof( o ) == \"boolean\" ) {\n" + "if ( o ) {\n" + "++nTrue;\n" + "}\n" + "}\n" + "}\n" + "if ( nTrue > 0 ) {\n" + "ret.unique = true;\n" + "}\n" + "if ( nTrue > 1 ) {\n" + "ret.dropDups = true;\n" + "}\n" + "}\n" + "*/\n" + "return ret;\n" + "}\n" + "DBCollection.prototype.createIndex = function( keys , options ){\n" + "var o = this._indexSpec( keys, options );\n" + "this._db.getCollection( \"system.indexes\" ).insert( o , true );\n" + "}\n" + "DBCollection.prototype.ensureIndex = function( keys , options ){\n" + "var name = this._indexSpec( keys, options ).name;\n" + "this._indexCache = this._indexCache || {};\n" + "if ( this._indexCache[ name ] ){\n" + "return false;\n" + "}\n" + "this.createIndex( keys , options );\n" + "if ( this.getDB().getLastError() == \"\" ) {\n" + "this._indexCache[name] = true;\n" + "}\n" + "return true;\n" + "}\n" + "DBCollection.prototype.resetIndexCache = function(){\n" + "this._indexCache = {};\n" + "}\n" + "DBCollection.prototype.reIndex = function(){\n" + "var specs = this.getIndexSpecs();\n" + "this.dropIndexes();\n" + "for ( var i = 0; i < specs.length; ++i ){\n" + "this.ensureIndex( specs[i].key, [ specs[i].unique, specs[i].name ] );\n" + "}\n" + "}\n" + "DBCollection.prototype.dropIndexes = function(){\n" + "this.resetIndexCache();\n" + "var res = this._db.runCommand( { deleteIndexes: this.getName(), index: \"*\" } );\n" + "assert( res , \"no result from dropIndex result\" );\n" + "if ( res.ok )\n" + "return res;\n" + "if ( res.errmsg.match( /not found/ ) )\n" + "return res;\n" + "throw \"error dropping indexes : \" + tojson( res );\n" + "}\n" + "DBCollection.prototype.drop = function(){\n" + "this.resetIndexCache();\n" + "return this._db.runCommand( { drop: this.getName() } );\n" + "}\n" + "DBCollection.prototype.renameCollection = function( newName ){\n" + "return this._db._adminCommand( { renameCollection : this._fullName , to : this._db._name + \".\" + newName } ).ok;\n" + "}\n" + "DBCollection.prototype.validate = function() {\n" + "var res = this._db.runCommand( { validate: this.getName() } );\n" + "res.valid = false;\n" + "if ( res.result ){\n" + "var str = \"-\" + tojson( res.result );\n" + "res.valid = ! ( str.match( /exception/ ) || str.match( /corrupt/ ) );\n" + "var p = /lastExtentSize:(\\d+)/;\n" + "var r = p.exec( str );\n" + "if ( r ){\n" + "res.lastExtentSize = Number( r[1] );\n" + "}\n" + "}\n" + "return res;\n" + "}\n" + "DBCollection.prototype.getIndexes = function(){\n" + "return this.getDB().getCollection( \"system.indexes\" ).find( { ns : this.getFullName() } ).toArray();\n" + "}\n" + "DBCollection.prototype.getIndices = DBCollection.prototype.getIndexes;\n" + "DBCollection.prototype.getIndexSpecs = DBCollection.prototype.getIndexes;\n" + "DBCollection.prototype.getIndexKeys = function(){\n" + "return this.getIndexes().map(\n" + "function(i){\n" + "return i.key;\n" + "}\n" + ");\n" + "}\n" + "DBCollection.prototype.count = function( x ){\n" + "return this.find( x ).count();\n" + "}\n" + "/**\n" + "* Drop free lists. Normally not used.\n" + "* Note this only does the collection itself, not the namespaces of its indexes (see cleanAll).\n" + "*/\n" + "DBCollection.prototype.clean = function() {\n" + "return this._dbCommand( { clean: this.getName() } );\n" + "}\n" + "/**\n" + "* <p>Drop a specified index.</p>\n" + "*\n" + "* <p>\n" + "* Name is the name of the index in the system.indexes name field. (Run db.system.indexes.find() to\n" + "* see example data.)\n" + "* </p>\n" + "*\n" + "* <p>Note : alpha: space is not reclaimed </p>\n" + "* @param {String} name of index to delete.\n" + "* @return A result object. result.ok will be true if successful.\n" + "*/\n" + "DBCollection.prototype.dropIndex = function(index) {\n" + "assert(index , \"need to specify index to dropIndex\" );\n" + "if ( ! isString( index ) && isObject( index ) )\n" + "index = this._genIndexName( index );\n" + "var res = this._dbCommand( { deleteIndexes: this.getName(), index: index } );\n" + "this.resetIndexCache();\n" + "return res;\n" + "}\n" + "DBCollection.prototype.copyTo = function( newName ){\n" + "return this.getDB().eval(\n" + "function( collName , newName ){\n" + "var from = db[collName];\n" + "var to = db[newName];\n" + "to.ensureIndex( { _id : 1 } );\n" + "var count = 0;\n" + "var cursor = from.find();\n" + "while ( cursor.hasNext() ){\n" + "var o = cursor.next();\n" + "count++;\n" + "to.save( o );\n" + "}\n" + "return count;\n" + "} , this.getName() , newName\n" + ");\n" + "}\n" + "DBCollection.prototype.getCollection = function( subName ){\n" + "return this._db.getCollection( this._shortName + \".\" + subName );\n" + "}\n" + "DBCollection.prototype.stats = function(){\n" + "return this._db.runCommand( { collstats : this._shortName } );\n" + "}\n" + "DBCollection.prototype.dataSize = function(){\n" + "return this.stats().size;\n" + "}\n" + "DBCollection.prototype.storageSize = function(){\n" + "return this.stats().storageSize;\n" + "}\n" + "DBCollection.prototype.totalIndexSize = function( verbose ){\n" + "var total = 0;\n" + "var mydb = this._db;\n" + "var shortName = this._shortName;\n" + "this.getIndexes().forEach(\n" + "function( spec ){\n" + "var coll = mydb.getCollection( shortName + \".$\" + spec.name );\n" + "var mysize = coll.dataSize();\n" + "total += coll.dataSize();\n" + "if ( verbose ) {\n" + "print( coll + \"\\t\" + mysize );\n" + "}\n" + "}\n" + ");\n" + "return total;\n" + "}\n" + "DBCollection.prototype.totalSize = function(){\n" + "var total = this.storageSize();\n" + "var mydb = this._db;\n" + "var shortName = this._shortName;\n" + "this.getIndexes().forEach(\n" + "function( spec ){\n" + "var coll = mydb.getCollection( shortName + \".$\" + spec.name );\n" + "var mysize = coll.storageSize();\n" + "total += coll.dataSize();\n" + "}\n" + ");\n" + "return total;\n" + "}\n" + "DBCollection.prototype.convertToCapped = function( bytes ){\n" + "if ( ! bytes )\n" + "throw \"have to specify # of bytes\";\n" + "return this._dbCommand( { convertToCapped : this._shortName , size : bytes } )\n" + "}\n" + "DBCollection.prototype.exists = function(){\n" + "return this._db.system.namespaces.findOne( { name : this._fullName } );\n" + "}\n" + "DBCollection.prototype.isCapped = function(){\n" + "var e = this.exists();\n" + "return ( e && e.options && e.options.capped ) ? true : false;\n" + "}\n" + "DBCollection.prototype.distinct = function( keyString ){\n" + "var res = this._dbCommand( { distinct : this._shortName , key : keyString } );\n" + "if ( ! res.ok )\n" + "throw \"distinct failed: \" + tojson( res );\n" + "return res.values;\n" + "}\n" + "DBCollection.prototype.group = function( params ){\n" + "params.ns = this._shortName;\n" + "return this._db.group( params );\n" + "}\n" + "DBCollection.prototype.groupcmd = function( params ){\n" + "params.ns = this._shortName;\n" + "return this._db.groupcmd( params );\n" + "}\n" + "DBCollection.prototype.toString = function(){\n" + "return this.getFullName();\n" + "}\n" + "DBCollection.prototype.shellPrint = DBCollection.prototype.toString;\n" + "if ( typeof DB == \"undefined\" ){\n" + "DB = function( mongo , name ){\n" + "this._mongo = mongo;\n" + "this._name = name;\n" + "}\n" + "}\n" + "DB.prototype.getMongo = function(){\n" + "assert( this._mongo , \"why no mongo!\" );\n" + "return this._mongo;\n" + "}\n" + "DB.prototype.getSisterDB = function( name ){\n" + "return this.getMongo().getDB( name );\n" + "}\n" + "DB.prototype.getName = function(){\n" + "return this._name;\n" + "}\n" + "DB.prototype.getCollection = function( name ){\n" + "return new DBCollection( this._mongo , this , name , this._name + \".\" + name );\n" + "}\n" + "DB.prototype.commandHelp = function( name ){\n" + "var c = {};\n" + "c[name] = 1;\n" + "c.help = true;\n" + "return this.runCommand( c ).help;\n" + "}\n" + "DB.prototype.runCommand = function( obj ){\n" + "if ( typeof( obj ) == \"string\" ){\n" + "var n = {};\n" + "n[obj] = 1;\n" + "obj = n;\n" + "}\n" + "return this.getCollection( \"$cmd\" ).findOne( obj );\n" + "}\n" + "DB.prototype._dbCommand = DB.prototype.runCommand;\n" + "DB.prototype._adminCommand = function( obj ){\n" + "if ( this._name == \"admin\" )\n" + "return this.runCommand( obj );\n" + "return this.getSisterDB( \"admin\" ).runCommand( obj );\n" + "}\n" + "DB.prototype.addUser = function( username , pass ){\n" + "var c = this.getCollection( \"system.users\" );\n" + "var u = c.findOne( { user : username } ) || { user : username };\n" + "u.pwd = hex_md5( username + \":mongo:\" + pass );\n" + "print( tojson( u ) );\n" + "c.save( u );\n" + "}\n" + "DB.prototype.removeUser = function( username ){\n" + "this.getCollection( \"system.users\" ).remove( { user : username } );\n" + "}\n" + "DB.prototype.auth = function( username , pass ){\n" + "var n = this.runCommand( { getnonce : 1 } );\n" + "var a = this.runCommand(\n" + "{\n" + "authenticate : 1 ,\n" + "user : username ,\n" + "nonce : n.nonce ,\n" + "key : hex_md5( n.nonce + username + hex_md5( username + \":mongo:\" + pass ) )\n" + "}\n" + ");\n" + "return a.ok;\n" + "}\n" + "/**\n" + "Create a new collection in the database. Normally, collection creation is automatic. You would\n" + "use this function if you wish to specify special options on creation.\n" + "If the collection already exists, no action occurs.\n" + "<p>Options:</p>\n" + "<ul>\n" + "<li>\n" + "size: desired initial extent size for the collection. Must be <= 1000000000.\n" + "for fixed size (capped) collections, this size is the total/max size of the\n" + "collection.\n" + "</li>\n" + "<li>\n" + "capped: if true, this is a capped collection (where old data rolls out).\n" + "</li>\n" + "<li> max: maximum number of objects if capped (optional).</li>\n" + "</ul>\n" + "<p>Example: </p>\n" + "<code>db.createCollection(\"movies\", { size: 10 * 1024 * 1024, capped:true } );</code>\n" + "* @param {String} name Name of new collection to create\n" + "* @param {Object} options Object with options for call. Options are listed above.\n" + "* @return SOMETHING_FIXME\n" + "*/\n" + "DB.prototype.createCollection = function(name, opt) {\n" + "var options = opt || {};\n" + "var cmd = { create: name, capped: options.capped, size: options.size, max: options.max };\n" + "var res = this._dbCommand(cmd);\n" + "return res;\n" + "}\n" + "/**\n" + "* Returns the current profiling level of this database\n" + "* @return SOMETHING_FIXME or null on error\n" + "*/\n" + "DB.prototype.getProfilingLevel = function() {\n" + "var res = this._dbCommand( { profile: -1 } );\n" + "return res ? res.was : null;\n" + "}\n" + "/**\n" + "Erase the entire database. (!)\n" + "* @return Object returned has member ok set to true if operation succeeds, false otherwise.\n" + "*/\n" + "DB.prototype.dropDatabase = function() {\n" + "if ( arguments.length )\n" + "throw \"dropDatabase doesn't take arguments\";\n" + "return this._dbCommand( { dropDatabase: 1 } );\n" + "}\n" + "DB.prototype.shutdownServer = function() {\n" + "if( \"admin\" != this._name ){\n" + "return \"shutdown command only works with the admin database; try 'use admin'\";\n" + "}\n" + "try {\n" + "this._dbCommand(\"shutdown\");\n" + "throw \"shutdownServer failed\";\n" + "}\n" + "catch ( e ){\n" + "assert( tojson( e ).indexOf( \"error doing query: failed\" ) >= 0 , \"unexpected error: \" + tojson( e ) );\n" + "print( \"server should be down...\" );\n" + "}\n" + "}\n" + "/**\n" + "Clone database on another server to here.\n" + "<p>\n" + "Generally, you should dropDatabase() first as otherwise the cloned information will MERGE\n" + "into whatever data is already present in this database. (That is however a valid way to use\n" + "clone if you are trying to do something intentionally, such as union three non-overlapping\n" + "databases into one.)\n" + "<p>\n" + "This is a low level administrative function will is not typically used.\n" + "* @param {String} from Where to clone from (dbhostname[:port]). May not be this database\n" + "(self) as you cannot clone to yourself.\n" + "* @return Object returned has member ok set to true if operation succeeds, false otherwise.\n" + "* See also: db.copyDatabase()\n" + "*/\n" + "DB.prototype.cloneDatabase = function(from) {\n" + "assert( isString(from) && from.length );\n" + "return this._dbCommand( { clone: from } );\n" + "}\n" + "/**\n" + "Clone collection on another server to here.\n" + "<p>\n" + "Generally, you should drop() first as otherwise the cloned information will MERGE\n" + "into whatever data is already present in this collection. (That is however a valid way to use\n" + "clone if you are trying to do something intentionally, such as union three non-overlapping\n" + "collections into one.)\n" + "<p>\n" + "This is a low level administrative function is not typically used.\n" + "* @param {String} from mongod instance from which to clnoe (dbhostname:port). May\n" + "not be this mongod instance, as clone from self is not allowed.\n" + "* @param {String} collection name of collection to clone.\n" + "* @param {Object} query query specifying which elements of collection are to be cloned.\n" + "* @return Object returned has member ok set to true if operation succeeds, false otherwise.\n" + "* See also: db.cloneDatabase()\n" + "*/\n" + "DB.prototype.cloneCollection = function(from, collection, query) {\n" + "assert( isString(from) && from.length );\n" + "assert( isString(collection) && collection.length );\n" + "collection = this._name + \".\" + collection;\n" + "query = query || {};\n" + "return this._dbCommand( { cloneCollection:collection, from:from, query:query } );\n" + "}\n" + "/**\n" + "Copy database from one server or name to another server or name.\n" + "Generally, you should dropDatabase() first as otherwise the copied information will MERGE\n" + "into whatever data is already present in this database (and you will get duplicate objects\n" + "in collections potentially.)\n" + "For security reasons this function only works when executed on the \"admin\" db. However,\n" + "if you have access to said db, you can copy any database from one place to another.\n" + "This method provides a way to \"rename\" a database by copying it to a new db name and\n" + "location. Additionally, it effectively provides a repair facility.\n" + "* @param {String} fromdb database name from which to copy.\n" + "* @param {String} todb database name to copy to.\n" + "* @param {String} fromhost hostname of the database (and optionally, \":port\") from which to\n" + "copy the data. default if unspecified is to copy from self.\n" + "* @return Object returned has member ok set to true if operation succeeds, false otherwise.\n" + "* See also: db.clone()\n" + "*/\n" + "DB.prototype.copyDatabase = function(fromdb, todb, fromhost) {\n" + "assert( isString(fromdb) && fromdb.length );\n" + "assert( isString(todb) && todb.length );\n" + "fromhost = fromhost || \"\";\n" + "return this._adminCommand( { copydb:1, fromhost:fromhost, fromdb:fromdb, todb:todb } );\n" + "}\n" + "/**\n" + "Repair database.\n" + "* @return Object returned has member ok set to true if operation succeeds, false otherwise.\n" + "*/\n" + "DB.prototype.repairDatabase = function() {\n" + "return this._dbCommand( { repairDatabase: 1 } );\n" + "}\n" + "DB.prototype.help = function() {\n" + "print(\"DB methods:\");\n" + "print(\"\\tdb.auth(username, password)\");\n" + "print(\"\\tdb.getMongo() get the server connection object\");\n" + "print(\"\\tdb.getMongo().setSlaveOk() allow this connection to read from the nonmaster member of a replica pair\");\n" + "print(\"\\tdb.getSisterDB(name) get the db at the same server as this onew\");\n" + "print(\"\\tdb.getName()\");\n" + "print(\"\\tdb.getCollection(cname) same as db['cname'] or db.cname\");\n" + "print(\"\\tdb.runCommand(cmdObj) run a database command. if cmdObj is a string, turns it into { cmdObj : 1 }\");\n" + "print(\"\\tdb.commandHelp(name) returns the help for the command\");\n" + "print(\"\\tdb.addUser(username, password)\");\n" + "print(\"\\tdb.removeUser(username)\");\n" + "print(\"\\tdb.createCollection(name, { size : ..., capped : ..., max : ... } )\");\n" + "print(\"\\tdb.getReplicationInfo()\");\n" + "print(\"\\tdb.printReplicationInfo()\");\n" + "print(\"\\tdb.printSlaveReplicationInfo()\");\n" + "print(\"\\tdb.getProfilingLevel()\");\n" + "print(\"\\tdb.setProfilingLevel(level) 0=off 1=slow 2=all\");\n" + "print(\"\\tdb.cloneDatabase(fromhost)\");\n" + "print(\"\\tdb.copyDatabase(fromdb, todb, fromhost)\");\n" + "print(\"\\tdb.shutdownServer()\");\n" + "print(\"\\tdb.dropDatabase()\");\n" + "print(\"\\tdb.repairDatabase()\");\n" + "print(\"\\tdb.eval(func, args) run code server-side\");\n" + "print(\"\\tdb.getLastError() - just returns the err msg string\");\n" + "print(\"\\tdb.getLastErrorObj() - return full status object\");\n" + "print(\"\\tdb.getPrevError()\");\n" + "print(\"\\tdb.resetError()\");\n" + "print(\"\\tdb.getCollectionNames()\");\n" + "print(\"\\tdb.currentOp() displays the current operation in the db\" );\n" + "print(\"\\tdb.killOp() kills the current operation in the db\" );\n" + "print(\"\\tdb.printCollectionStats()\" );\n" + "print(\"\\tdb.version() current version of the server\" );\n" + "}\n" + "DB.prototype.printCollectionStats = function(){\n" + "this.getCollectionNames().forEach(\n" + "function(z){\n" + "print( z );\n" + "printjson( db[z].stats() );\n" + "print( \"---\" );\n" + "}\n" + ");\n" + "}\n" + "/**\n" + "* <p> Set profiling level for your db. Profiling gathers stats on query performance. </p>\n" + "*\n" + "* <p>Default is off, and resets to off on a database restart -- so if you want it on,\n" + "* turn it on periodically. </p>\n" + "*\n" + "* <p>Levels :</p>\n" + "* <ul>\n" + "* <li>0=off</li>\n" + "* <li>1=log very slow (>100ms) operations</li>\n" + "* <li>2=log all</li>\n" + "* @param {String} level Desired level of profiling\n" + "* @return SOMETHING_FIXME or null on error\n" + "*/\n" + "DB.prototype.setProfilingLevel = function(level) {\n" + "if (level < 0 || level > 2) {\n" + "throw { dbSetProfilingException : \"input level \" + level + \" is out of range [0..2]\" };\n" + "}\n" + "if (level) {\n" + "this.createCollection(\"system.profile\", { capped: true, size: 128 * 1024 } );\n" + "}\n" + "return this._dbCommand( { profile: level } );\n" + "}\n" + "/**\n" + "* <p> Evaluate a js expression at the database server.</p>\n" + "*\n" + "* <p>Useful if you need to touch a lot of data lightly; in such a scenario\n" + "* the network transfer of the data could be a bottleneck. A good example\n" + "* is \"select count(*)\" -- can be done server side via this mechanism.\n" + "* </p>\n" + "*\n" + "* <p>\n" + "* If the eval fails, an exception is thrown of the form:\n" + "* </p>\n" + "* <code>{ dbEvalException: { retval: functionReturnValue, ok: num [, errno: num] [, errmsg: str] } }</code>\n" + "*\n" + "* <p>Example: </p>\n" + "* <code>print( \"mycount: \" + db.eval( function(){db.mycoll.find({},{_id:ObjId()}).length();} );</code>\n" + "*\n" + "* @param {Function} jsfunction Javascript function to run on server. Note this it not a closure, but rather just \"code\".\n" + "* @return result of your function, or null if error\n" + "*\n" + "*/\n" + "DB.prototype.eval = function(jsfunction) {\n" + "var cmd = { $eval : jsfunction };\n" + "if ( arguments.length > 1 ) {\n" + "cmd.args = argumentsToArray( arguments ).slice(1);\n" + "}\n" + "var res = this._dbCommand( cmd );\n" + "if (!res.ok)\n" + "throw tojson( res );\n" + "return res.retval;\n" + "}\n" + "DB.prototype.dbEval = DB.prototype.eval;\n" + "/**\n" + "*\n" + "* <p>\n" + "* Similar to SQL group by. For example: </p>\n" + "*\n" + "* <code>select a,b,sum(c) csum from coll where active=1 group by a,b</code>\n" + "*\n" + "* <p>\n" + "* corresponds to the following in 10gen:\n" + "* </p>\n" + "*\n" + "* <code>\n" + "db.group(\n" + "{\n" + "ns: \"coll\",\n" + "key: { a:true, b:true },\n" + "cond: { active:1 },\n" + "reduce: function(obj,prev) { prev.csum += obj.c; } ,\n" + "initial: { csum: 0 }\n" + "});\n" + "</code>\n" + "*\n" + "*\n" + "* <p>\n" + "* An array of grouped items is returned. The array must fit in RAM, thus this function is not\n" + "* suitable when the return set is extremely large.\n" + "* </p>\n" + "* <p>\n" + "* To order the grouped data, simply sort it client side upon return.\n" + "* <p>\n" + "Defaults\n" + "cond may be null if you want to run against all rows in the collection\n" + "keyf is a function which takes an object and returns the desired key. set either key or keyf (not both).\n" + "* </p>\n" + "*/\n" + "DB.prototype.groupeval = function(parmsObj) {\n" + "var groupFunction = function() {\n" + "var parms = args[0];\n" + "var c = db[parms.ns].find(parms.cond||{});\n" + "var map = new Map();\n" + "var pks = parms.key ? parms.key.keySet() : null;\n" + "var pkl = pks ? pks.length : 0;\n" + "var key = {};\n" + "while( c.hasNext() ) {\n" + "var obj = c.next();\n" + "if ( pks ) {\n" + "for( var i=0; i<pkl; i++ ){\n" + "var k = pks[i];\n" + "key[k] = obj[k];\n" + "}\n" + "}\n" + "else {\n" + "key = parms.$keyf(obj);\n" + "}\n" + "var aggObj = map.get(key);\n" + "if( aggObj == null ) {\n" + "var newObj = Object.extend({}, key);\n" + "aggObj = Object.extend(newObj, parms.initial)\n" + "map.put( key , aggObj );\n" + "}\n" + "parms.$reduce(obj, aggObj);\n" + "}\n" + "return map.values();\n" + "}\n" + "return this.eval(groupFunction, this._groupFixParms( parmsObj ));\n" + "}\n" + "DB.prototype.groupcmd = function( parmsObj ){\n" + "var ret = this.runCommand( { \"group\" : this._groupFixParms( parmsObj ) } );\n" + "if ( ! ret.ok ){\n" + "throw \"group command failed: \" + tojson( ret );\n" + "}\n" + "return ret.retval;\n" + "}\n" + "DB.prototype.group = DB.prototype.groupcmd;\n" + "DB.prototype._groupFixParms = function( parmsObj ){\n" + "var parms = Object.extend({}, parmsObj);\n" + "if( parms.reduce ) {\n" + "parms.$reduce = parms.reduce;\n" + "delete parms.reduce;\n" + "}\n" + "if( parms.keyf ) {\n" + "parms.$keyf = parms.keyf;\n" + "delete parms.keyf;\n" + "}\n" + "return parms;\n" + "}\n" + "DB.prototype.resetError = function(){\n" + "return this.runCommand( { reseterror : 1 } );\n" + "}\n" + "DB.prototype.forceError = function(){\n" + "return this.runCommand( { forceerror : 1 } );\n" + "}\n" + "DB.prototype.getLastError = function(){\n" + "var res = this.runCommand( { getlasterror : 1 } );\n" + "if ( ! res.ok )\n" + "throw \"getlasterror failed: \" + tojson( res );\n" + "return res.err;\n" + "}\n" + "DB.prototype.getLastErrorObj = function(){\n" + "var res = this.runCommand( { getlasterror : 1 } );\n" + "if ( ! res.ok )\n" + "throw \"getlasterror failed: \" + tojson( res );\n" + "return res;\n" + "}\n" + "/* Return the last error which has occurred, even if not the very last error.\n" + "Returns:\n" + "{ err : <error message>, nPrev : <how_many_ops_back_occurred>, ok : 1 }\n" + "result.err will be null if no error has occurred.\n" + "*/\n" + "DB.prototype.getPrevError = function(){\n" + "return this.runCommand( { getpreverror : 1 } );\n" + "}\n" + "DB.prototype.getCollectionNames = function(){\n" + "var all = [];\n" + "var nsLength = this._name.length + 1;\n" + "this.getCollection( \"system.namespaces\" ).find().sort({name:1}).forEach(\n" + "function(z){\n" + "var name = z.name;\n" + "if ( name.indexOf( \"$\" ) >= 0 )\n" + "return;\n" + "all.push( name.substring( nsLength ) );\n" + "}\n" + ");\n" + "return all;\n" + "}\n" + "DB.prototype.tojson = function(){\n" + "return this._name;\n" + "}\n" + "DB.prototype.toString = function(){\n" + "return this._name;\n" + "}\n" + "DB.prototype.currentOp = function(){\n" + "return db.$cmd.sys.inprog.findOne();\n" + "}\n" + "DB.prototype.currentOP = DB.prototype.currentOp;\n" + "DB.prototype.killOp = function(){\n" + "return db.$cmd.sys.killop.findOne();\n" + "}\n" + "DB.prototype.killOP = DB.prototype.killOp;\n" + "/**\n" + "Get a replication log information summary.\n" + "<p>\n" + "This command is for the database/cloud administer and not applicable to most databases.\n" + "It is only used with the local database. One might invoke from the JS shell:\n" + "<pre>\n" + "use local\n" + "db.getReplicationInfo();\n" + "</pre>\n" + "It is assumed that this database is a replication master -- the information returned is\n" + "about the operation log stored at local.oplog.$main on the replication master. (It also\n" + "works on a machine in a replica pair: for replica pairs, both machines are \"masters\" from\n" + "an internal database perspective.\n" + "<p>\n" + "* @return Object timeSpan: time span of the oplog from start to end if slave is more out\n" + "* of date than that, it can't recover without a complete resync\n" + "*/\n" + "DB.prototype.getReplicationInfo = function() {\n" + "var db = this.getSisterDB(\"local\");\n" + "var result = { };\n" + "var ol = db.system.namespaces.findOne({name:\"local.oplog.$main\"});\n" + "if( ol && ol.options ) {\n" + "result.logSizeMB = ol.options.size / 1000 / 1000;\n" + "} else {\n" + "result.errmsg = \"local.oplog.$main, or its options, not found in system.namespaces collection (not --master?)\";\n" + "return result;\n" + "}\n" + "var firstc = db.oplog.$main.find().sort({$natural:1}).limit(1);\n" + "var lastc = db.oplog.$main.find().sort({$natural:-1}).limit(1);\n" + "if( !firstc.hasNext() || !lastc.hasNext() ) {\n" + "result.errmsg = \"objects not found in local.oplog.$main -- is this a new and empty db instance?\";\n" + "result.oplogMainRowCount = db.oplog.$main.count();\n" + "return result;\n" + "}\n" + "var first = firstc.next();\n" + "var last = lastc.next();\n" + "{\n" + "var tfirst = first.ts;\n" + "var tlast = last.ts;\n" + "if( tfirst && tlast ) {\n" + "tfirst = tfirst / 4294967296;\n" + "tlast = tlast / 4294967296;\n" + "result.timeDiff = tlast - tfirst;\n" + "result.timeDiffHours = Math.round(result.timeDiff / 36)/100;\n" + "result.tFirst = (new Date(tfirst*1000)).toString();\n" + "result.tLast = (new Date(tlast*1000)).toString();\n" + "result.now = Date();\n" + "}\n" + "else {\n" + "result.errmsg = \"ts element not found in oplog objects\";\n" + "}\n" + "}\n" + "return result;\n" + "}\n" + "DB.prototype.printReplicationInfo = function() {\n" + "var result = this.getReplicationInfo();\n" + "if( result.errmsg ) {\n" + "print(tojson(result));\n" + "return;\n" + "}\n" + "print(\"configured oplog size: \" + result.logSizeMB + \"MB\");\n" + "print(\"log length start to end: \" + result.timeDiff + \"secs (\" + result.timeDiffHours + \"hrs)\");\n" + "print(\"oplog first event time: \" + result.tFirst);\n" + "print(\"oplog last event time: \" + result.tLast);\n" + "print(\"now: \" + result.now);\n" + "}\n" + "DB.prototype.printSlaveReplicationInfo = function() {\n" + "function g(x) {\n" + "print(\"source: \" + x.host);\n" + "var st = new Date(x.syncedTo/4294967296*1000);\n" + "var now = new Date();\n" + "print(\"syncedTo: \" + st.toString() );\n" + "var ago = (now-st)/1000;\n" + "var hrs = Math.round(ago/36)/100;\n" + "print(\" = \" + Math.round(ago) + \"secs ago (\" + hrs + \"hrs)\");\n" + "}\n" + "var L = this.getSisterDB(\"local\");\n" + "if( L.sources.count() == 0 ) {\n" + "print(\"local.sources is empty; is this db a --slave?\");\n" + "return;\n" + "}\n" + "L.sources.find().forEach(g);\n" + "}\n" + "DB.prototype.serverBuildInfo = function(){\n" + "return this._adminCommand( \"buildinfo\" );\n" + "}\n" + "DB.prototype.serverStatus = function(){\n" + "return this._adminCommand( \"serverStatus\" );\n" + "}\n" + "DB.prototype.version = function(){\n" + "return this.serverBuildInfo().version;\n" + "}\n" + "if ( typeof Mongo == \"undefined\" ){\n" + "Mongo = function( host ){\n" + "this.init( host );\n" + "}\n" + "}\n" + "if ( ! Mongo.prototype ){\n" + "throw \"Mongo.prototype not defined\";\n" + "}\n" + "if ( ! Mongo.prototype.find )\n" + "Mongo.prototype.find = function( ns , query , fields , limit , skip ){ throw \"find not implemented\"; }\n" + "if ( ! Mongo.prototype.insert )\n" + "Mongo.prototype.insert = function( ns , obj ){ throw \"insert not implemented\"; }\n" + "if ( ! Mongo.prototype.remove )\n" + "Mongo.prototype.remove = function( ns , pattern ){ throw \"remove not implemented;\" }\n" + "if ( ! Mongo.prototype.update )\n" + "Mongo.prototype.update = function( ns , query , obj , upsert ){ throw \"update not implemented;\" }\n" + "if ( typeof mongoInject == \"function\" ){\n" + "mongoInject( Mongo.prototype );\n" + "}\n" + "Mongo.prototype.setSlaveOk = function() {\n" + "this.slaveOk = true;\n" + "}\n" + "Mongo.prototype.getDB = function( name ){\n" + "return new DB( this , name );\n" + "}\n" + "Mongo.prototype.getDBs = function(){\n" + "var res = this.getDB( \"admin\" ).runCommand( { \"listDatabases\" : 1 } );\n" + "assert( res.ok == 1 , \"listDatabases failed\" );\n" + "return res;\n" + "}\n" + "Mongo.prototype.getDBNames = function(){\n" + "return this.getDBs().databases.map(\n" + "function(z){\n" + "return z.name;\n" + "}\n" + ");\n" + "}\n" + "Mongo.prototype.toString = function(){\n" + "return \"mongo connection to \" + this.host;\n" + "}\n" + "connect = function( url , user , pass ){\n" + "print( \"connecting to: \" + url )\n" + "if ( user && ! pass )\n" + "throw \"you specified a user and not a password. either you need a password, or you're using the old connect api\";\n" + "var idx = url.indexOf( \"/\" );\n" + "var db;\n" + "if ( idx < 0 )\n" + "db = new Mongo().getDB( url );\n" + "else\n" + "db = new Mongo( url.substring( 0 , idx ) ).getDB( url.substring( idx + 1 ) );\n" + "if ( user && pass ){\n" + "if ( ! db.auth( user , pass ) ){\n" + "throw \"couldn't login\";\n" + "}\n" + "}\n" + "return db;\n" + "}\n" + "MR = {};\n" + "MR.init = function(){\n" + "$max = 0;\n" + "$arr = [];\n" + "emit = MR.emit;\n" + "gc();\n" + "}\n" + "MR.cleanup = function(){\n" + "MR.init();\n" + "gc();\n" + "}\n" + "MR.emit = function(k,v){\n" + "var num = get_num( k );\n" + "var data = $arr[num];\n" + "if ( ! data ){\n" + "data = { key : k , values : [] };\n" + "$arr[num] = data;\n" + "}\n" + "data.values.push( v );\n" + "$max = Math.max( $max , data.values.length );\n" + "}\n" + "MR.doReduce = function( useDB ){\n" + "$max = 0;\n" + "for ( var i=0; i<$arr.length; i++){\n" + "var data = $arr[i];\n" + "if ( ! data )\n" + "continue;\n" + "if ( useDB ){\n" + "var x = tempcoll.findOne( { _id : data.key } );\n" + "if ( x ){\n" + "data.values.push( x.value );\n" + "}\n" + "}\n" + "var r = $reduce( data.key , data.values );\n" + "if ( r.length && r[0] ){\n" + "data.values = r;\n" + "}\n" + "else{\n" + "data.values = [ r ];\n" + "}\n" + "$max = Math.max( $max , data.values.length );\n" + "if ( useDB ){\n" + "if ( data.values.length == 1 ){\n" + "tempcoll.save( { _id : data.key , value : data.values[0] } );\n" + "}\n" + "else {\n" + "tempcoll.save( { _id : data.key , value : data.values } );\n" + "}\n" + "}\n" + "}\n" + "}\n" + "MR.check = function(){\n" + "if ( $max < 2000 && $arr.length < 1000 ){\n" + "return 0;\n" + "}\n" + "MR.doReduce();\n" + "if ( $max < 2000 && $arr.length < 1000 ){\n" + "return 1;\n" + "}\n" + "MR.doReduce( true );\n" + "$arr = [];\n" + "$max = 0;\n" + "reset_num();\n" + "gc();\n" + "return 2;\n" + "}\n" + "if ( typeof DBQuery == \"undefined\" ){\n" + "DBQuery = function( mongo , db , collection , ns , query , fields , limit , skip ){\n" + "this._mongo = mongo;\n" + "this._db = db;\n" + "this._collection = collection;\n" + "this._ns = ns;\n" + "this._query = query || {};\n" + "this._fields = fields;\n" + "this._limit = limit || 0;\n" + "this._skip = skip || 0;\n" + "this._cursor = null;\n" + "this._numReturned = 0;\n" + "this._special = false;\n" + "}\n" + "print( \"DBQuery probably won't have array access \" );\n" + "}\n" + "DBQuery.prototype.help = function(){\n" + "print( \"DBQuery help\" );\n" + "print( \"\\t.sort( {...} )\" )\n" + "print( \"\\t.limit( n )\" )\n" + "print( \"\\t.skip( n )\" )\n" + "print( \"\\t.count()\" )\n" + "print( \"\\t.explain()\" )\n" + "print( \"\\t.forEach( func )\" )\n" + "print( \"\\t.map( func )\" )\n" + "}\n" + "DBQuery.prototype.clone = function(){\n" + "var q = new DBQuery( this._mongo , this._db , this._collection , this._ns ,\n" + "this._query , this._fields ,\n" + "this._limit , this._skip );\n" + "q._special = this._special;\n" + "return q;\n" + "}\n" + "DBQuery.prototype._ensureSpecial = function(){\n" + "if ( this._special )\n" + "return;\n" + "var n = { query : this._query };\n" + "this._query = n;\n" + "this._special = true;\n" + "}\n" + "DBQuery.prototype._checkModify = function(){\n" + "if ( this._cursor )\n" + "throw \"query already executed\";\n" + "}\n" + "DBQuery.prototype._exec = function(){\n" + "if ( ! this._cursor ){\n" + "assert.eq( 0 , this._numReturned );\n" + "this._cursor = this._mongo.find( this._ns , this._query , this._fields , this._limit , this._skip );\n" + "this._cursorSeen = 0;\n" + "}\n" + "return this._cursor;\n" + "}\n" + "DBQuery.prototype.limit = function( limit ){\n" + "this._checkModify();\n" + "this._limit = limit;\n" + "return this;\n" + "}\n" + "DBQuery.prototype.skip = function( skip ){\n" + "this._checkModify();\n" + "this._skip = skip;\n" + "return this;\n" + "}\n" + "DBQuery.prototype.hasNext = function(){\n" + "this._exec();\n" + "if ( this._limit > 0 && this._cursorSeen >= this._limit )\n" + "return false;\n" + "var o = this._cursor.hasNext();\n" + "return o;\n" + "}\n" + "DBQuery.prototype.next = function(){\n" + "this._exec();\n" + "var o = this._cursor.hasNext();\n" + "if ( o )\n" + "this._cursorSeen++;\n" + "else\n" + "throw \"error hasNext: \" + o;\n" + "var ret = this._cursor.next();\n" + "if ( ret.$err && this._numReturned == 0 && ! this.hasNext() )\n" + "throw \"error: \" + tojson( ret );\n" + "this._numReturned++;\n" + "return ret;\n" + "}\n" + "DBQuery.prototype.toArray = function(){\n" + "if ( this._arr )\n" + "return this._arr;\n" + "var a = [];\n" + "while ( this.hasNext() )\n" + "a.push( this.next() );\n" + "this._arr = a;\n" + "return a;\n" + "}\n" + "DBQuery.prototype.count = function(){\n" + "var cmd = { count: this._collection.getName() };\n" + "if ( this._query ){\n" + "if ( this._special )\n" + "cmd.query = this._query.query;\n" + "else\n" + "cmd.query = this._query;\n" + "}\n" + "cmd.fields = this._fields || {};\n" + "var res = this._db.runCommand( cmd );\n" + "if( res && res.n != null ) return res.n;\n" + "throw \"count failed: \" + tojson( res );\n" + "}\n" + "DBQuery.prototype.countReturn = function(){\n" + "var c = this.count();\n" + "if ( this._skip )\n" + "c = c - this._skip;\n" + "if ( this._limit > 0 && this._limit < c )\n" + "return this._limit;\n" + "return c;\n" + "}\n" + "/**\n" + "* iterative count - only for testing\n" + "*/\n" + "DBQuery.prototype.itcount = function(){\n" + "var num = 0;\n" + "while ( this.hasNext() ){\n" + "num++;\n" + "this.next();\n" + "}\n" + "return num;\n" + "}\n" + "DBQuery.prototype.length = function(){\n" + "return this.toArray().length;\n" + "}\n" + "DBQuery.prototype.sort = function( sortBy ){\n" + "this._ensureSpecial();\n" + "this._query.orderby = sortBy;\n" + "return this;\n" + "}\n" + "DBQuery.prototype.hint = function( hint ){\n" + "this._ensureSpecial();\n" + "this._query[\"$hint\"] = hint;\n" + "return this;\n" + "}\n" + "DBQuery.prototype.min = function( min ) {\n" + "this._ensureSpecial();\n" + "this._query[\"$min\"] = min;\n" + "return this;\n" + "}\n" + "DBQuery.prototype.max = function( max ) {\n" + "this._ensureSpecial();\n" + "this._query[\"$max\"] = max;\n" + "return this;\n" + "}\n" + "DBQuery.prototype.forEach = function( func ){\n" + "while ( this.hasNext() )\n" + "func( this.next() );\n" + "}\n" + "DBQuery.prototype.map = function( func ){\n" + "var a = [];\n" + "while ( this.hasNext() )\n" + "a.push( func( this.next() ) );\n" + "return a;\n" + "}\n" + "DBQuery.prototype.arrayAccess = function( idx ){\n" + "return this.toArray()[idx];\n" + "}\n" + "DBQuery.prototype.explain = function(){\n" + "var n = this.clone();\n" + "n._ensureSpecial();\n" + "n._query.$explain = true;\n" + "n._limit = n._limit * -1;\n" + "return n.next();\n" + "}\n" + "DBQuery.prototype.snapshot = function(){\n" + "this._ensureSpecial();\n" + "this._query.$snapshot = true;\n" + "return this;\n" + "}\n" + "DBQuery.prototype.shellPrint = function(){\n" + "try {\n" + "var n = 0;\n" + "while ( this.hasNext() && n < 20 ){\n" + "var s = tojson( this.next() );\n" + "print( s );\n" + "n++;\n" + "}\n" + "if ( this.hasNext() ){\n" + "print( \"has more\" );\n" + "___it___ = this;\n" + "}\n" + "else {\n" + "___it___ = null;\n" + "}\n" + "}\n" + "catch ( e ){\n" + "print( e );\n" + "}\n" + "}\n" + "DBQuery.prototype.toString = function(){\n" + "return \"DBQuery: \" + this._ns + \" -> \" + tojson( this.query );\n" + "}\n" + "_parsePath = function() {\n" + "var dbpath = \"\";\n" + "for( var i = 0; i < arguments.length; ++i )\n" + "if ( arguments[ i ] == \"--dbpath\" )\n" + "dbpath = arguments[ i + 1 ];\n" + "if ( dbpath == \"\" )\n" + "throw \"No dbpath specified\";\n" + "return dbpath;\n" + "}\n" + "_parsePort = function() {\n" + "var port = \"\";\n" + "for( var i = 0; i < arguments.length; ++i )\n" + "if ( arguments[ i ] == \"--port\" )\n" + "port = arguments[ i + 1 ];\n" + "if ( port == \"\" )\n" + "throw \"No port specified\";\n" + "return port;\n" + "}\n" + "createMongoArgs = function( binaryName , args ){\n" + "var fullArgs = [ binaryName ];\n" + "if ( args.length == 1 && isObject( args[0] ) ){\n" + "var o = args[0];\n" + "for ( var k in o ){\n" + "if ( k == \"v\" && isNumber( o[k] ) ){\n" + "var n = o[k];\n" + "if ( n > 0 ){\n" + "var temp = \"-\";\n" + "while ( n-- > 0 ) temp += \"v\";\n" + "fullArgs.push( temp );\n" + "}\n" + "}\n" + "else {\n" + "fullArgs.push( \"--\" + k );\n" + "if ( o[k] != \"\" )\n" + "fullArgs.push( \"\" + o[k] );\n" + "}\n" + "}\n" + "}\n" + "else {\n" + "for ( var i=0; i<args.length; i++ )\n" + "fullArgs.push( args[i] )\n" + "}\n" + "return fullArgs;\n" + "}\n" + "startMongod = function(){\n" + "var args = createMongoArgs( \"mongod\" , arguments );\n" + "var dbpath = _parsePath.apply( null, args );\n" + "resetDbpath( dbpath );\n" + "return startMongoProgram.apply( null, args );\n" + "}\n" + "startMongos = function(){\n" + "return startMongoProgram.apply( null, createMongoArgs( \"mongos\" , arguments ) );\n" + "}\n" + "startMongoProgram = function(){\n" + "var port = _parsePort.apply( null, arguments );\n" + "_startMongoProgram.apply( null, arguments );\n" + "var m;\n" + "assert.soon\n" + "( function() {\n" + "try {\n" + "m = new Mongo( \"127.0.0.1:\" + port );\n" + "return true;\n" + "} catch( e ) {\n" + "}\n" + "return false;\n" + "}, \"unable to connect to mongo program on port \" + port, 30000 );\n" + "return m;\n" + "}\n" + "startMongoProgramNoConnect = function() {\n" + "return _startMongoProgram.apply( null, arguments );\n" + "}\n" + "myPort = function() {\n" + "var m = db.getMongo();\n" + "if ( m.host.match( /:/ ) )\n" + "return m.host.match( /:(.*)/ )[ 1 ];\n" + "else\n" + "return 27017;\n" + "}\n" + "ShardingTest = function( testName , numServers , verboseLevel , numMongos ){\n" + "this._connections = [];\n" + "this._serverNames = [];\n" + "for ( var i=0; i<numServers; i++){\n" + "var conn = startMongod( { port : 30000 + i , dbpath : \"/data/db/\" + testName + i , noprealloc : \"\" } );\n" + "conn.name = \"localhost:\" + ( 30000 + i );\n" + "this._connections.push( conn );\n" + "this._serverNames.push( conn.name );\n" + "}\n" + "this._configDB = \"localhost:30000\";\n" + "this._mongos = [];\n" + "var startMongosPort = 39999;\n" + "for ( var i=0; i<(numMongos||1); i++ ){\n" + "var myPort = startMongosPort - i;\n" + "var conn = startMongos( { port : startMongosPort - i , v : verboseLevel || 0 , configdb : this._configDB } );\n" + "conn.name = \"localhost:\" + myPort;\n" + "this._mongos.push( conn );\n" + "if ( i == 0 )\n" + "this.s = conn;\n" + "}\n" + "var admin = this.admin = this.s.getDB( \"admin\" );\n" + "this.config = this.s.getDB( \"config\" );\n" + "this._serverNames.forEach(\n" + "function(z){\n" + "admin.runCommand( { addshard : z } );\n" + "}\n" + ");\n" + "}\n" + "ShardingTest.prototype.getDB = function( name ){\n" + "return this.s.getDB( name );\n" + "}\n" + "ShardingTest.prototype.getServerName = function( dbname ){\n" + "return this.config.databases.findOne( { name : dbname } ).primary;\n" + "}\n" + "ShardingTest.prototype.getServer = function( dbname ){\n" + "var name = this.getServerName( dbname );\n" + "for ( var i=0; i<this._serverNames.length; i++ ){\n" + "if ( name == this._serverNames[i] )\n" + "return this._connections[i];\n" + "}\n" + "throw \"can't find server for: \" + dbname + \" name:\" + name;\n" + "}\n" + "ShardingTest.prototype.getOther = function( one ){\n" + "if ( this._connections.length != 2 )\n" + "throw \"getOther only works with 2 servers\";\n" + "if ( this._connections[0] == one )\n" + "return this._connections[1];\n" + "return this._connections[0];\n" + "}\n" + "ShardingTest.prototype.stop = function(){\n" + "for ( var i=0; i<this._mongos.length; i++ ){\n" + "stopMongoProgram( 39999 - i );\n" + "}\n" + "for ( var i=0; i<this._connections.length; i++){\n" + "stopMongod( 30000 + i );\n" + "}\n" + "}\n" + "ShardingTest.prototype.adminCommand = function(cmd){\n" + "var res = this.admin.runCommand( cmd );\n" + "if ( res && res.ok == 1 )\n" + "return true;\n" + "throw \"command \" + tojson( cmd ) + \" failed: \" + tojson( res );\n" + "}\n" + "ShardingTest.prototype.getChunksString = function( ns ){\n" + "var q = {}\n" + "if ( ns )\n" + "q.ns = ns;\n" + "return Array.tojson( this.config.chunks.find( q ).toArray() , \"\\n\" );\n" + "}\n" + "ShardingTest.prototype.printChunks = function( ns ){\n" + "print( this.getChunksString( ns ) );\n" + "}\n" + "ShardingTest.prototype.sync = function(){\n" + "this.adminCommand( \"connpoolsync\" );\n" + "}\n" + "MongodRunner = function( port, dbpath, peer, arbiter, extraArgs ) {\n" + "this.port_ = port;\n" + "this.dbpath_ = dbpath;\n" + "this.peer_ = peer;\n" + "this.arbiter_ = arbiter;\n" + "this.extraArgs_ = extraArgs;\n" + "}\n" + "MongodRunner.prototype.start = function( reuseData ) {\n" + "var args = [];\n" + "if ( reuseData ) {\n" + "args.push( \"mongod\" );\n" + "}\n" + "args.push( \"--port\" );\n" + "args.push( this.port_ );\n" + "args.push( \"--dbpath\" );\n" + "args.push( this.dbpath_ );\n" + "if ( this.peer_ && this.arbiter_ ) {\n" + "args.push( \"--pairwith\" );\n" + "args.push( this.peer_ );\n" + "args.push( \"--arbiter\" );\n" + "args.push( this.arbiter_ );\n" + "args.push( \"--oplogSize\" );\n" + "args.push( \"1\" );\n" + "}\n" + "args.push( \"--nohttpinterface\" );\n" + "args.push( \"--noprealloc\" );\n" + "args.push( \"--bind_ip\" );\n" + "args.push( \"127.0.0.1\" );\n" + "if ( this.extraArgs_ ) {\n" + "args = args.concat( this.extraArgs_ );\n" + "}\n" + "if ( reuseData ) {\n" + "return startMongoProgram.apply( null, args );\n" + "} else {\n" + "return startMongod.apply( null, args );\n" + "}\n" + "}\n" + "MongodRunner.prototype.port = function() { return this.port_; }\n" + "MongodRunner.prototype.toString = function() { return [ this.port_, this.dbpath_, this.peer_, this.arbiter_ ].toString(); }\n" + "ReplPair = function( left, right, arbiter ) {\n" + "this.left_ = left;\n" + "this.leftC_ = null;\n" + "this.right_ = right;\n" + "this.rightC_ = null;\n" + "this.arbiter_ = arbiter;\n" + "this.arbiterC_ = null;\n" + "this.master_ = null;\n" + "this.slave_ = null;\n" + "}\n" + "ReplPair.prototype.start = function( reuseData ) {\n" + "if ( this.arbiterC_ == null ) {\n" + "this.arbiterC_ = this.arbiter_.start();\n" + "}\n" + "if ( this.leftC_ == null ) {\n" + "this.leftC_ = this.left_.start( reuseData );\n" + "}\n" + "if ( this.rightC_ == null ) {\n" + "this.rightC_ = this.right_.start( reuseData );\n" + "}\n" + "}\n" + "ReplPair.prototype.isMaster = function( mongo, debug ) {\n" + "var im = mongo.getDB( \"admin\" ).runCommand( { ismaster : 1 } );\n" + "assert( im && im.ok, \"command ismaster failed\" );\n" + "if ( debug ) {\n" + "printjson( im );\n" + "}\n" + "return im.ismaster;\n" + "}\n" + "ReplPair.prototype.isInitialSyncComplete = function( mongo, debug ) {\n" + "var isc = mongo.getDB( \"admin\" ).runCommand( { isinitialsynccomplete : 1 } );\n" + "assert( isc && isc.ok, \"command isinitialsynccomplete failed\" );\n" + "if ( debug ) {\n" + "printjson( isc );\n" + "}\n" + "return isc.initialsynccomplete;\n" + "}\n" + "ReplPair.prototype.checkSteadyState = function( state, expectedMasterHost, twoMasterOk, leftValues, rightValues, debug ) {\n" + "leftValues = leftValues || {};\n" + "rightValues = rightValues || {};\n" + "var lm = null;\n" + "var lisc = null;\n" + "if ( this.leftC_ != null ) {\n" + "lm = this.isMaster( this.leftC_, debug );\n" + "leftValues[ lm ] = true;\n" + "lisc = this.isInitialSyncComplete( this.leftC_, debug );\n" + "}\n" + "var rm = null;\n" + "var risc = null;\n" + "if ( this.rightC_ != null ) {\n" + "rm = this.isMaster( this.rightC_, debug );\n" + "rightValues[ rm ] = true;\n" + "risc = this.isInitialSyncComplete( this.rightC_, debug );\n" + "}\n" + "var stateSet = {}\n" + "state.forEach( function( i ) { stateSet[ i ] = true; } );\n" + "if ( !( 1 in stateSet ) || ( ( risc || risc == null ) && ( lisc || lisc == null ) ) ) {\n" + "if ( rm == 1 && lm != 1 ) {\n" + "assert( twoMasterOk || !( 1 in leftValues ) );\n" + "this.master_ = this.rightC_;\n" + "this.slave_ = this.leftC_;\n" + "} else if ( lm == 1 && rm != 1 ) {\n" + "assert( twoMasterOk || !( 1 in rightValues ) );\n" + "this.master_ = this.leftC_;\n" + "this.slave_ = this.rightC_;\n" + "}\n" + "if ( !twoMasterOk ) {\n" + "assert( lm != 1 || rm != 1, \"two masters\" );\n" + "}\n" + "if ( state.sort().toString() == [ lm, rm ].sort().toString() ) {\n" + "if ( expectedMasterHost != null ) {\n" + "if( expectedMasterHost == this.master_.host ) {\n" + "return true;\n" + "}\n" + "} else {\n" + "return true;\n" + "}\n" + "}\n" + "}\n" + "this.master_ = null;\n" + "this.slave_ = null;\n" + "return false;\n" + "}\n" + "ReplPair.prototype.waitForSteadyState = function( state, expectedMasterHost, twoMasterOk, debug ) {\n" + "state = state || [ 1, 0 ];\n" + "twoMasterOk = twoMasterOk || false;\n" + "var rp = this;\n" + "var leftValues = {};\n" + "var rightValues = {};\n" + "assert.soon( function() { return rp.checkSteadyState( state, expectedMasterHost, twoMasterOk, leftValues, rightValues, debug ); },\n" + "\"rp (\" + rp + \") failed to reach expected steady state (\" + state + \")\" );\n" + "}\n" + "ReplPair.prototype.master = function() { return this.master_; }\n" + "ReplPair.prototype.slave = function() { return this.slave_; }\n" + "ReplPair.prototype.right = function() { return this.rightC_; }\n" + "ReplPair.prototype.left = function() { return this.leftC_; }\n" + "ReplPair.prototype.killNode = function( mongo, signal ) {\n" + "signal = signal || 15;\n" + "if ( this.leftC_ != null && this.leftC_.host == mongo.host ) {\n" + "stopMongod( this.left_.port_ );\n" + "this.leftC_ = null;\n" + "}\n" + "if ( this.rightC_ != null && this.rightC_.host == mongo.host ) {\n" + "stopMongod( this.right_.port_ );\n" + "this.rightC_ = null;\n" + "}\n" + "}\n" + "ReplPair.prototype._annotatedNode = function( mongo ) {\n" + "var ret = \"\";\n" + "if ( mongo != null ) {\n" + "ret += \" (connected)\";\n" + "if ( this.master_ != null && mongo.host == this.master_.host ) {\n" + "ret += \"(master)\";\n" + "}\n" + "if ( this.slave_ != null && mongo.host == this.slave_.host ) {\n" + "ret += \"(slave)\";\n" + "}\n" + "}\n" + "return ret;\n" + "}\n" + "ReplPair.prototype.toString = function() {\n" + "var ret = \"\";\n" + "ret += \"left: \" + this.left_;\n" + "ret += \" \" + this._annotatedNode( this.leftC_ );\n" + "ret += \" right: \" + this.right_;\n" + "ret += \" \" + this._annotatedNode( this.rightC_ );\n" + "return ret;\n" + "}\n" + "friendlyEqual = function( a , b ){\n" + "if ( a == b )\n" + "return true;\n" + "if ( tojson( a ) == tojson( b ) )\n" + "return true;\n" + "return false;\n" + "}\n" + "doassert = function( msg ){\n" + "print( \"assert: \" + msg );\n" + "throw msg;\n" + "}\n" + "assert = function( b , msg ){\n" + "if ( assert._debug && msg ) print( \"in assert for: \" + msg );\n" + "if ( b )\n" + "return;\n" + "doassert( \"assert failed : \" + msg );\n" + "}\n" + "assert._debug = false;\n" + "assert.eq = function( a , b , msg ){\n" + "if ( assert._debug && msg ) print( \"in assert for: \" + msg );\n" + "if ( a == b )\n" + "return;\n" + "if ( ( a != null && b != null ) && friendlyEqual( a , b ) )\n" + "return;\n" + "doassert( \"[\" + tojson( a ) + \"] != [\" + tojson( b ) + \"] are not equal : \" + msg );\n" + "}\n" + "assert.neq = function( a , b , msg ){\n" + "if ( assert._debug && msg ) print( \"in assert for: \" + msg );\n" + "if ( a != b )\n" + "return;\n" + "doassert( \"[\" + a + \"] != [\" + b + \"] are equal : \" + msg );\n" + "}\n" + "assert.soon = function( f, msg, timeout, interval ) {\n" + "if ( assert._debug && msg ) print( \"in assert for: \" + msg );\n" + "var start = new Date();\n" + "timeout = timeout || 30000;\n" + "interval = interval || 200;\n" + "var last;\n" + "while( 1 ) {\n" + "if ( typeof( f ) == \"string\" ){\n" + "if ( eval( f ) )\n" + "return;\n" + "}\n" + "else {\n" + "if ( f() )\n" + "return;\n" + "}\n" + "if ( ( new Date() ).getTime() - start.getTime() > timeout )\n" + "doassert( \"assert.soon failed: \" + f + \", msg:\" + msg );\n" + "sleep( interval );\n" + "}\n" + "}\n" + "assert.throws = function( func , params , msg ){\n" + "if ( assert._debug && msg ) print( \"in assert for: \" + msg );\n" + "try {\n" + "func.apply( null , params );\n" + "}\n" + "catch ( e ){\n" + "return e;\n" + "}\n" + "doassert( \"did not throw exception: \" + msg );\n" + "}\n" + "assert.commandWorked = function( res , msg ){\n" + "if ( assert._debug && msg ) print( \"in assert for: \" + msg );\n" + "if ( res.ok == 1 )\n" + "return;\n" + "doassert( \"command failed: \" + tojson( res ) + \" : \" + msg );\n" + "}\n" + "assert.commandFailed = function( res , msg ){\n" + "if ( assert._debug && msg ) print( \"in assert for: \" + msg );\n" + "if ( res.ok == 0 )\n" + "return;\n" + "doassert( \"command worked when it should have failed: \" + tojson( res ) + \" : \" + msg );\n" + "}\n" + "assert.isnull = function( what , msg ){\n" + "if ( assert._debug && msg ) print( \"in assert for: \" + msg );\n" + "if ( what == null )\n" + "return;\n" + "doassert( \"supposed to null (\" + ( msg || \"\" ) + \") was: \" + tojson( what ) );\n" + "}\n" + "assert.lt = function( a , b , msg ){\n" + "if ( assert._debug && msg ) print( \"in assert for: \" + msg );\n" + "if ( a < b )\n" + "return;\n" + "doassert( a + \" is not less than \" + b + \" : \" + msg );\n" + "}\n" + "assert.gt = function( a , b , msg ){\n" + "if ( assert._debug && msg ) print( \"in assert for: \" + msg );\n" + "if ( a > b )\n" + "return;\n" + "doassert( a + \" is not greater than \" + b + \" : \" + msg );\n" + "}\n" + "Object.extend = function( dst , src ){\n" + "for ( var k in src ){\n" + "dst[k] = src[k];\n" + "}\n" + "return dst;\n" + "}\n" + "argumentsToArray = function( a ){\n" + "var arr = [];\n" + "for ( var i=0; i<a.length; i++ )\n" + "arr[i] = a[i];\n" + "return arr;\n" + "}\n" + "isString = function( x ){\n" + "return typeof( x ) == \"string\";\n" + "}\n" + "isNumber = function(x){\n" + "return typeof( x ) == \"number\";\n" + "}\n" + "isObject = function( x ){\n" + "return typeof( x ) == \"object\";\n" + "}\n" + "String.prototype.trim = function() {\n" + "return this.replace(/^\\s+|\\s+$/g,\"\");\n" + "}\n" + "String.prototype.ltrim = function() {\n" + "return this.replace(/^\\s+/,\"\");\n" + "}\n" + "String.prototype.rtrim = function() {\n" + "return this.replace(/\\s+$/,\"\");\n" + "}\n" + "Date.timeFunc = function( theFunc , numTimes ){\n" + "var start = new Date();\n" + "numTimes = numTimes || 1;\n" + "for ( var i=0; i<numTimes; i++ ){\n" + "theFunc.apply( null , argumentsToArray( arguments ).slice( 2 ) );\n" + "}\n" + "return (new Date()).getTime() - start.getTime();\n" + "}\n" + "Date.prototype.tojson = function(){\n" + "return \"\\\"\" + this.toString() + \"\\\"\";\n" + "}\n" + "RegExp.prototype.tojson = RegExp.prototype.toString;\n" + "Array.contains = function( a , x ){\n" + "for ( var i=0; i<a.length; i++ ){\n" + "if ( a[i] == x )\n" + "return true;\n" + "}\n" + "return false;\n" + "}\n" + "Array.unique = function( a ){\n" + "var u = [];\n" + "for ( var i=0; i<a.length; i++){\n" + "var o = a[i];\n" + "if ( ! Array.contains( u , o ) ){\n" + "u.push( o );\n" + "}\n" + "}\n" + "return u;\n" + "}\n" + "Array.shuffle = function( arr ){\n" + "for ( var i=0; i<arr.length-1; i++ ){\n" + "var pos = i+Math.floor(Math.random()*(arr.length-i));\n" + "var save = arr[i];\n" + "arr[i] = arr[pos];\n" + "arr[pos] = save;\n" + "}\n" + "return arr;\n" + "}\n" + "Array.tojson = function( a , sepLines ){\n" + "var s = \"[\";\n" + "if ( sepLines ) s += \"\\n\";\n" + "for ( var i=0; i<a.length; i++){\n" + "if ( i > 0 ){\n" + "s += \",\";\n" + "if ( sepLines ) s += \"\\n\";\n" + "}\n" + "s += tojson( a[i] );\n" + "}\n" + "s += \"]\";\n" + "if ( sepLines ) s += \"\\n\";\n" + "return s;\n" + "}\n" + "Array.fetchRefs = function( arr , coll ){\n" + "var n = [];\n" + "for ( var i=0; i<arr.length; i ++){\n" + "var z = arr[i];\n" + "if ( coll && coll != z.getCollection() )\n" + "continue;\n" + "n.push( z.fetch() );\n" + "}\n" + "return n;\n" + "}\n" + "if ( ! ObjectId.prototype )\n" + "ObjectId.prototype = {}\n" + "ObjectId.prototype.toString = function(){\n" + "return this.str;\n" + "}\n" + "ObjectId.prototype.tojson = function(){\n" + "return \" ObjectId( \\\"\" + this.str + \"\\\") \";\n" + "}\n" + "ObjectId.prototype.isObjectId = true;\n" + "if ( typeof( DBPointer ) != \"undefined\" ){\n" + "DBPointer.prototype.fetch = function(){\n" + "assert( this.ns , \"need a ns\" );\n" + "assert( this.id , \"need an id\" );\n" + "return db[ this.ns ].findOne( { _id : this.id } );\n" + "}\n" + "DBPointer.prototype.tojson = function(){\n" + "return \"{ 'ns' : \\\"\" + this.ns + \"\\\" , 'id' : \\\"\" + this.id + \"\\\" } \";\n" + "}\n" + "DBPointer.prototype.getCollection = function(){\n" + "return this.ns;\n" + "}\n" + "DBPointer.prototype.toString = function(){\n" + "return \"DBPointer \" + this.ns + \":\" + this.id;\n" + "}\n" + "}\n" + "else {\n" + "print( \"warning: no DBPointer\" );\n" + "}\n" + "if ( typeof( DBRef ) != \"undefined\" ){\n" + "DBRef.prototype.fetch = function(){\n" + "assert( this.$ref , \"need a ns\" );\n" + "assert( this.$id , \"need an id\" );\n" + "return db[ this.$ref ].findOne( { _id : this.$id } );\n" + "}\n" + "DBRef.prototype.tojson = function(){\n" + "return \"{ '$ref' : \\\"\" + this.$ref + \"\\\" , '$id' : \\\"\" + this.$id + \"\\\" } \";\n" + "}\n" + "DBRef.prototype.getCollection = function(){\n" + "return this.$ref;\n" + "}\n" + "DBRef.prototype.toString = function(){\n" + "return this.tojson();\n" + "}\n" + "}\n" + "else {\n" + "print( \"warning: no DBRef\" );\n" + "}\n" + "if ( typeof( BinData ) != \"undefined\" ){\n" + "BinData.prototype.tojson = function(){\n" + "return \"BinData type: \" + this.type + \" len: \" + this.len;\n" + "}\n" + "}\n" + "else {\n" + "print( \"warning: no BinData\" );\n" + "}\n" + "tojson = function( x ){\n" + "if ( x == null )\n" + "return \"null\";\n" + "if ( x == undefined )\n" + "return \"\";\n" + "switch ( typeof x ){\n" + "case \"string\": {\n" + "var s = \"\\\"\";\n" + "for ( var i=0; i<x.length; i++ ){\n" + "if ( x[i] == '\"' ){\n" + "s += \"\\\\\\\"\";\n" + "}\n" + "else\n" + "s += x[i];\n" + "}\n" + "return s + \"\\\"\";\n" + "}\n" + "case \"number\":\n" + "case \"boolean\":\n" + "return \"\" + x;\n" + "case \"object\":\n" + "return tojsonObject( x );\n" + "case \"function\":\n" + "return x.toString();\n" + "default:\n" + "throw \"tojson can't handle type \" + ( typeof x );\n" + "}\n" + "}\n" + "tojsonObject = function( x ){\n" + "assert.eq( ( typeof x ) , \"object\" , \"tojsonObject needs object, not [\" + ( typeof x ) + \"]\" );\n" + "if ( typeof( x.tojson ) == \"function\" && x.tojson != tojson )\n" + "return x.tojson();\n" + "if ( typeof( x.constructor.tojson ) == \"function\" && x.constructor.tojson != tojson )\n" + "return x.constructor.tojson( x );\n" + "if ( x.toString() == \"[object MaxKey]\" )\n" + "return \"{ $maxKey : 1 }\";\n" + "if ( x.toString() == \"[object MinKey]\" )\n" + "return \"{ $minKey : 1 }\";\n" + "var s = \"{\";\n" + "var first = true;\n" + "for ( var k in x ){\n" + "var val = x[k];\n" + "if ( val == DB.prototype || val == DBCollection.prototype )\n" + "continue;\n" + "if ( first ) first = false;\n" + "else s += \" , \";\n" + "s += \"\\\"\" + k + \"\\\" : \" + tojson( val );\n" + "}\n" + "return s + \"}\";\n" + "}\n" + "shellPrint = function( x ){\n" + "it = x;\n" + "if ( x != undefined )\n" + "shellPrintHelper( x );\n" + "if ( db ){\n" + "var e = db.getPrevError();\n" + "if ( e.err ) {\n" + "if( e.nPrev <= 1 )\n" + "print( \"error on last call: \" + tojson( e.err ) );\n" + "else\n" + "print( \"an error \" + tojson(e.err) + \" occurred \" + e.nPrev + \" operations back in the command invocation\" );\n" + "}\n" + "db.resetError();\n" + "}\n" + "}\n" + "printjson = function(x){\n" + "print( tojson( x ) );\n" + "}\n" + "shellPrintHelper = function( x ){\n" + "if ( typeof( x ) == \"undefined\" ){\n" + "if ( typeof( db ) != \"undefined\" && db.getLastError ){\n" + "var e = db.getLastError();\n" + "if ( e != null )\n" + "print( e );\n" + "}\n" + "return;\n" + "}\n" + "if ( x == null ){\n" + "print( \"null\" );\n" + "return;\n" + "}\n" + "if ( typeof x != \"object\" )\n" + "return print( x );\n" + "var p = x.shellPrint;\n" + "if ( typeof p == \"function\" )\n" + "return x.shellPrint();\n" + "var p = x.tojson;\n" + "if ( typeof p == \"function\" )\n" + "print( x.tojson() );\n" + "else\n" + "print( tojson( x ) );\n" + "}\n" + "shellHelper = function( command , rest , shouldPrint ){\n" + "command = command.trim();\n" + "var args = rest.trim().replace(/;$/,\"\").split( \"\\s+\" );\n" + "if ( ! shellHelper[command] )\n" + "throw \"no command [\" + command + \"]\";\n" + "var res = shellHelper[command].apply( null , args );\n" + "if ( shouldPrint ){\n" + "shellPrintHelper( res );\n" + "}\n" + "return res;\n" + "}\n" + "help = shellHelper.help = function(){\n" + "print( \"HELP\" );\n" + "print( \"\\t\" + \"show dbs show database names\");\n" + "print( \"\\t\" + \"show collections show collections in current database\");\n" + "print( \"\\t\" + \"show users show users in current database\");\n" + "print( \"\\t\" + \"show profile show most recent system.profile entries with time >= 1ms\");\n" + "print( \"\\t\" + \"use <db name> set curent database to <db name>\" );\n" + "print( \"\\t\" + \"db.help() help on DB methods\");\n" + "print( \"\\t\" + \"db.foo.help() help on collection methods\");\n" + "print( \"\\t\" + \"db.foo.find() list objects in collection foo\" );\n" + "print( \"\\t\" + \"db.foo.find( { a : 1 } ) list objects in foo where a == 1\" );\n" + "print( \"\\t\" + \"it result of the last line evaluated; use to further iterate\");\n" + "}\n" + "shellHelper.use = function( dbname ){\n" + "db = db.getMongo().getDB( dbname );\n" + "print( \"switched to db \" + db.getName() );\n" + "}\n" + "shellHelper.it = function(){\n" + "if ( typeof( ___it___ ) == \"undefined\" || ___it___ == null ){\n" + "print( \"no cursor\" );\n" + "return;\n" + "}\n" + "shellPrintHelper( ___it___ );\n" + "}\n" + "shellHelper.show = function( what ){\n" + "assert( typeof what == \"string\" );\n" + "if( what == \"profile\" ) {\n" + "if( db.system.profile.count() == 0 ) {\n" + "print(\"db.system.profile is empty\");\n" + "print(\"Use db.setProfilingLevel(2) will enable profiling\");\n" + "print(\"Use db.system.profile.find() to show raw profile entries\");\n" + "}\n" + "else {\n" + "print();\n" + "db.system.profile.find({ millis : { $gt : 0 } }).sort({$natural:-1}).limit(5).forEach( function(x){print(\"\"+x.millis+\"ms \" + String(x.ts).substring(0,24)); print(x.info); print(\"\\n\");} )\n" + "}\n" + "return \"\";\n" + "}\n" + "if ( what == \"users\" ){\n" + "db.system.users.find().forEach( printjson );\n" + "return \"\";\n" + "}\n" + "if ( what == \"collections\" || what == \"tables\" ) {\n" + "db.getCollectionNames().forEach( function(x){print(x)} );\n" + "return \"\";\n" + "}\n" + "if ( what == \"dbs\" ) {\n" + "db.getMongo().getDBNames().sort().forEach( function(x){print(x)} );\n" + "return \"\";\n" + "}\n" + "throw \"don't know how to show [\" + what + \"]\";\n" + "}\n" + "if ( typeof( Map ) == \"undefined\" ){\n" + "Map = function(){\n" + "this._data = {};\n" + "}\n" + "}\n" + "Map.hash = function( val ){\n" + "if ( ! val )\n" + "return val;\n" + "switch ( typeof( val ) ){\n" + "case 'string':\n" + "case 'number':\n" + "case 'date':\n" + "return val.toString();\n" + "case 'object':\n" + "case 'array':\n" + "var s = \"\";\n" + "for ( var k in val ){\n" + "s += k + val[k];\n" + "}\n" + "return s;\n" + "}\n" + "throw \"can't hash : \" + typeof( val );\n" + "}\n" + "Map.prototype.put = function( key , value ){\n" + "var o = this._get( key );\n" + "var old = o.value;\n" + "o.value = value;\n" + "return old;\n" + "}\n" + "Map.prototype.get = function( key ){\n" + "return this._get( key ).value;\n" + "}\n" + "Map.prototype._get = function( key ){\n" + "var h = Map.hash( key );\n" + "var a = this._data[h];\n" + "if ( ! a ){\n" + "a = [];\n" + "this._data[h] = a;\n" + "}\n" + "for ( var i=0; i<a.length; i++ ){\n" + "if ( friendlyEqual( key , a[i].key ) ){\n" + "return a[i];\n" + "}\n" + "}\n" + "var o = { key : key , value : null };\n" + "a.push( o );\n" + "return o;\n" + "}\n" + "Map.prototype.values = function(){\n" + "var all = [];\n" + "for ( var k in this._data ){\n" + "this._data[k].forEach( function(z){ all.push( z.value ); } );\n" + "}\n" + "return all;\n" + "}\n" + "Math.sigFig = function( x , N ){\n" + "if ( ! N ){\n" + "N = 3;\n" + "}\n" + "var p = Math.pow( 10, N - Math.ceil( Math.log( Math.abs(x) ) / Math.log( 10 )) );\n" + "return Math.round(x*p)/p;\n" + "}\n" + ; + diff --git a/shell/mr.js b/shell/mr.js new file mode 100644 index 0000000..7b0814d --- /dev/null +++ b/shell/mr.js @@ -0,0 +1,95 @@ +// mr.js + +MR = {}; + +MR.init = function(){ + $max = 0; + $arr = []; + emit = MR.emit; + $numEmits = 0; + $numReduces = 0; + $numReducesToDB = 0; + gc(); // this is just so that keep memory size sane +} + +MR.cleanup = function(){ + MR.init(); + gc(); +} + +MR.emit = function(k,v){ + $numEmits++; + var num = nativeHelper.apply( get_num_ , [ k ] ); + var data = $arr[num]; + if ( ! data ){ + data = { key : k , values : new Array(1000) , count : 0 }; + $arr[num] = data; + } + data.values[data.count++] = v; + $max = Math.max( $max , data.count ); +} + +MR.doReduce = function( useDB ){ + $numReduces++; + if ( useDB ) + $numReducesToDB++; + $max = 0; + for ( var i=0; i<$arr.length; i++){ + var data = $arr[i]; + if ( ! data ) + continue; + + if ( useDB ){ + var x = tempcoll.findOne( { _id : data.key } ); + if ( x ){ + data.values[data.count++] = x.value; + } + } + + var r = $reduce( data.key , data.values.slice( 0 , data.count ) ); + if ( r && r.length && r[0] ){ + data.values = r; + data.count = r.length; + } + else{ + data.values[0] = r; + data.count = 1; + } + + $max = Math.max( $max , data.count ); + + if ( useDB ){ + if ( data.count == 1 ){ + tempcoll.save( { _id : data.key , value : data.values[0] } ); + } + else { + tempcoll.save( { _id : data.key , value : data.values.slice( 0 , data.count ) } ); + } + } + } +} + +MR.check = function(){ + if ( $max < 2000 && $arr.length < 1000 ){ + return 0; + } + MR.doReduce(); + if ( $max < 2000 && $arr.length < 1000 ){ + return 1; + } + MR.doReduce( true ); + $arr = []; + $max = 0; + reset_num(); + gc(); + return 2; +} + +MR.finalize = function(){ + tempcoll.find().forEach( + function(z){ + z.value = $finalize( z._id , z.value ); + tempcoll.save( z ); + } + ); +} diff --git a/shell/query.js b/shell/query.js new file mode 100644 index 0000000..aabd12e --- /dev/null +++ b/shell/query.js @@ -0,0 +1,248 @@ +// query.js + +if ( typeof DBQuery == "undefined" ){ + DBQuery = function( mongo , db , collection , ns , query , fields , limit , skip ){ + + this._mongo = mongo; // 0 + this._db = db; // 1 + this._collection = collection; // 2 + this._ns = ns; // 3 + + this._query = query || {}; // 4 + this._fields = fields; // 5 + this._limit = limit || 0; // 6 + this._skip = skip || 0; // 7 + + this._cursor = null; + this._numReturned = 0; + this._special = false; + } + print( "DBQuery probably won't have array access " ); +} + +DBQuery.prototype.help = function(){ + print( "DBQuery help" ); + print( "\t.sort( {...} )" ) + print( "\t.limit( n )" ) + print( "\t.skip( n )" ) + print( "\t.count() - total # of objects matching query, ignores skip,limit" ) + print( "\t.size() - total # of objects cursor would return skip,limit effect this" ) + print( "\t.explain()" ) + print( "\t.forEach( func )" ) + print( "\t.map( func )" ) + +} + +DBQuery.prototype.clone = function(){ + var q = new DBQuery( this._mongo , this._db , this._collection , this._ns , + this._query , this._fields , + this._limit , this._skip ); + q._special = this._special; + return q; +} + +DBQuery.prototype._ensureSpecial = function(){ + if ( this._special ) + return; + + var n = { query : this._query }; + this._query = n; + this._special = true; +} + +DBQuery.prototype._checkModify = function(){ + if ( this._cursor ) + throw "query already executed"; +} + +DBQuery.prototype._exec = function(){ + if ( ! this._cursor ){ + assert.eq( 0 , this._numReturned ); + this._cursor = this._mongo.find( this._ns , this._query , this._fields , this._limit , this._skip ); + this._cursorSeen = 0; + } + return this._cursor; +} + +DBQuery.prototype.limit = function( limit ){ + this._checkModify(); + this._limit = limit; + return this; +} + +DBQuery.prototype.skip = function( skip ){ + this._checkModify(); + this._skip = skip; + return this; +} + +DBQuery.prototype.hasNext = function(){ + this._exec(); + + if ( this._limit > 0 && this._cursorSeen >= this._limit ) + return false; + var o = this._cursor.hasNext(); + return o; +} + +DBQuery.prototype.next = function(){ + this._exec(); + + var o = this._cursor.hasNext(); + if ( o ) + this._cursorSeen++; + else + throw "error hasNext: " + o; + + var ret = this._cursor.next(); + if ( ret.$err && this._numReturned == 0 && ! this.hasNext() ) + throw "error: " + tojson( ret ); + + this._numReturned++; + return ret; +} + +DBQuery.prototype.toArray = function(){ + if ( this._arr ) + return this._arr; + + var a = []; + while ( this.hasNext() ) + a.push( this.next() ); + this._arr = a; + return a; +} + +DBQuery.prototype.count = function( applySkipLimit ){ + var cmd = { count: this._collection.getName() }; + if ( this._query ){ + if ( this._special ) + cmd.query = this._query.query; + else + cmd.query = this._query; + } + cmd.fields = this._fields || {}; + + if ( applySkipLimit ){ + if ( this._limit ) + cmd.limit = this._limit; + if ( this._skip ) + cmd.skip = this._skip; + } + + var res = this._db.runCommand( cmd ); + if( res && res.n != null ) return res.n; + throw "count failed: " + tojson( res ); +} + +DBQuery.prototype.size = function(){ + return this.count( true ); +} + +DBQuery.prototype.countReturn = function(){ + var c = this.count(); + + if ( this._skip ) + c = c - this._skip; + + if ( this._limit > 0 && this._limit < c ) + return this._limit; + + return c; +} + +/** +* iterative count - only for testing +*/ +DBQuery.prototype.itcount = function(){ + var num = 0; + while ( this.hasNext() ){ + num++; + this.next(); + } + return num; +} + +DBQuery.prototype.length = function(){ + return this.toArray().length; +} + +DBQuery.prototype.sort = function( sortBy ){ + this._ensureSpecial(); + this._query.orderby = sortBy; + return this; +} + +DBQuery.prototype.hint = function( hint ){ + this._ensureSpecial(); + this._query["$hint"] = hint; + return this; +} + +DBQuery.prototype.min = function( min ) { + this._ensureSpecial(); + this._query["$min"] = min; + return this; +} + +DBQuery.prototype.max = function( max ) { + this._ensureSpecial(); + this._query["$max"] = max; + return this; +} + +DBQuery.prototype.forEach = function( func ){ + while ( this.hasNext() ) + func( this.next() ); +} + +DBQuery.prototype.map = function( func ){ + var a = []; + while ( this.hasNext() ) + a.push( func( this.next() ) ); + return a; +} + +DBQuery.prototype.arrayAccess = function( idx ){ + return this.toArray()[idx]; +} + +DBQuery.prototype.explain = function(){ + var n = this.clone(); + n._ensureSpecial(); + n._query.$explain = true; + n._limit = n._limit * -1; + return n.next(); +} + +DBQuery.prototype.snapshot = function(){ + this._ensureSpecial(); + this._query.$snapshot = true; + return this; + } + +DBQuery.prototype.shellPrint = function(){ + try { + var n = 0; + while ( this.hasNext() && n < 20 ){ + var s = tojson( this.next() , "" , true ); + print( s ); + n++; + } + if ( this.hasNext() ){ + print( "has more" ); + ___it___ = this; + } + else { + ___it___ = null; + } + } + catch ( e ){ + print( e ); + } + +} + +DBQuery.prototype.toString = function(){ + return "DBQuery: " + this._ns + " -> " + tojson( this.query ); +} diff --git a/shell/servers.js b/shell/servers.js new file mode 100644 index 0000000..109f871 --- /dev/null +++ b/shell/servers.js @@ -0,0 +1,608 @@ + + +_parsePath = function() { + var dbpath = ""; + for( var i = 0; i < arguments.length; ++i ) + if ( arguments[ i ] == "--dbpath" ) + dbpath = arguments[ i + 1 ]; + + if ( dbpath == "" ) + throw "No dbpath specified"; + + return dbpath; +} + +_parsePort = function() { + var port = ""; + for( var i = 0; i < arguments.length; ++i ) + if ( arguments[ i ] == "--port" ) + port = arguments[ i + 1 ]; + + if ( port == "" ) + throw "No port specified"; + return port; +} + +createMongoArgs = function( binaryName , args ){ + var fullArgs = [ binaryName ]; + + if ( args.length == 1 && isObject( args[0] ) ){ + var o = args[0]; + for ( var k in o ){ + if ( k == "v" && isNumber( o[k] ) ){ + var n = o[k]; + if ( n > 0 ){ + var temp = "-"; + while ( n-- > 0 ) temp += "v"; + fullArgs.push( temp ); + } + } + else { + fullArgs.push( "--" + k ); + if ( o[k] != "" ) + fullArgs.push( "" + o[k] ); + } + } + } + else { + for ( var i=0; i<args.length; i++ ) + fullArgs.push( args[i] ) + } + + return fullArgs; +} + +// Start a mongod instance and return a 'Mongo' object connected to it. +// This function's arguments are passed as command line arguments to mongod. +// The specified 'dbpath' is cleared if it exists, created if not. +startMongod = function(){ + + var args = createMongoArgs( "mongod" , arguments ); + + var dbpath = _parsePath.apply( null, args ); + resetDbpath( dbpath ); + + return startMongoProgram.apply( null, args ); +} + +startMongos = function(){ + return startMongoProgram.apply( null, createMongoArgs( "mongos" , arguments ) ); +} + +// Start a mongo program instance (generally mongod or mongos) and return a +// 'Mongo' object connected to it. This function's first argument is the +// program name, and subsequent arguments to this function are passed as +// command line arguments to the program. +startMongoProgram = function(){ + var port = _parsePort.apply( null, arguments ); + + _startMongoProgram.apply( null, arguments ); + + var m; + assert.soon + ( function() { + try { + m = new Mongo( "127.0.0.1:" + port ); + return true; + } catch( e ) { + } + return false; + }, "unable to connect to mongo program on port " + port, 30000 ); + + return m; +} + +// Start a mongo program instance. This function's first argument is the +// program name, and subsequent arguments to this function are passed as +// command line arguments to the program. Returns pid of the spawned program. +startMongoProgramNoConnect = function() { + return _startMongoProgram.apply( null, arguments ); +} + +myPort = function() { + var m = db.getMongo(); + if ( m.host.match( /:/ ) ) + return m.host.match( /:(.*)/ )[ 1 ]; + else + return 27017; +} + +ShardingTest = function( testName , numServers , verboseLevel , numMongos ){ + this._connections = []; + this._serverNames = []; + + for ( var i=0; i<numServers; i++){ + var conn = startMongod( { port : 30000 + i , dbpath : "/data/db/" + testName + i , + noprealloc : "" , smallfiles : "" , oplogSize : "2" } ); + conn.name = "localhost:" + ( 30000 + i ); + + this._connections.push( conn ); + this._serverNames.push( conn.name ); + } + + this._configDB = "localhost:30000"; + + + this._mongos = []; + var startMongosPort = 39999; + for ( var i=0; i<(numMongos||1); i++ ){ + var myPort = startMongosPort - i; + var conn = startMongos( { port : startMongosPort - i , v : verboseLevel || 0 , configdb : this._configDB } ); + conn.name = "localhost:" + myPort; + this._mongos.push( conn ); + if ( i == 0 ) + this.s = conn; + } + + var admin = this.admin = this.s.getDB( "admin" ); + this.config = this.s.getDB( "config" ); + + this._serverNames.forEach( + function(z){ + admin.runCommand( { addshard : z , allowLocal : true } ); + } + ); +} + +ShardingTest.prototype.getDB = function( name ){ + return this.s.getDB( name ); +} + +ShardingTest.prototype.getServerName = function( dbname ){ + return this.config.databases.findOne( { name : dbname } ).primary; +} + +ShardingTest.prototype.getServer = function( dbname ){ + var name = this.getServerName( dbname ); + for ( var i=0; i<this._serverNames.length; i++ ){ + if ( name == this._serverNames[i] ) + return this._connections[i]; + } + throw "can't find server for: " + dbname + " name:" + name; + +} + +ShardingTest.prototype.getOther = function( one ){ + if ( this._connections.length != 2 ) + throw "getOther only works with 2 servers"; + + if ( this._connections[0] == one ) + return this._connections[1]; + return this._connections[0]; +} + +ShardingTest.prototype.stop = function(){ + for ( var i=0; i<this._mongos.length; i++ ){ + stopMongoProgram( 39999 - i ); + } + for ( var i=0; i<this._connections.length; i++){ + stopMongod( 30000 + i ); + } +} + +ShardingTest.prototype.adminCommand = function(cmd){ + var res = this.admin.runCommand( cmd ); + if ( res && res.ok == 1 ) + return true; + + throw "command " + tojson( cmd ) + " failed: " + tojson( res ); +} + +ShardingTest.prototype.getChunksString = function( ns ){ + var q = {} + if ( ns ) + q.ns = ns; + return Array.tojson( this.config.chunks.find( q ).toArray() , "\n" ); +} + +ShardingTest.prototype.printChunks = function( ns ){ + print( this.getChunksString( ns ) ); +} + +ShardingTest.prototype.printShardingStatus = function(){ + printShardingStatus( this.config ); +} + +ShardingTest.prototype.printCollectionInfo = function( ns , msg ){ + var out = ""; + if ( msg ) + out += msg + "\n"; + out += "sharding collection info: " + ns + "\n"; + for ( var i=0; i<this._connections.length; i++ ){ + var c = this._connections[i]; + out += " mongod " + c + " " + tojson( c.getCollection( ns ).getShardVersion() , " " , true ) + "\n"; + } + for ( var i=0; i<this._mongos.length; i++ ){ + var c = this._mongos[i]; + out += " mongos " + c + " " + tojson( c.getCollection( ns ).getShardVersion() , " " , true ) + "\n"; + } + + print( out ); +} + +printShardingStatus = function( configDB ){ + + var version = configDB.getCollection( "version" ).findOne(); + if ( version == null ){ + print( "not a shard db!" ); + return; + } + + var raw = ""; + var output = function(s){ + raw += s + "\n"; + } + output( "--- Sharding Status --- " ); + output( " sharding version: " + tojson( configDB.getCollection( "version" ).findOne() ) ); + + output( " shards:" ); + configDB.shards.find().forEach( + function(z){ + output( " " + tojson(z) ); + } + ); + + output( " databases:" ); + configDB.databases.find().sort( { name : 1 } ).forEach( + function(z){ + output( "\t" + tojson(z,"",true) ); + + output( "\t\tmy chunks" ); + + configDB.chunks.find( { "ns" : new RegExp( "^" + z.name ) } ).sort( { ns : 1 } ).forEach( + function(z){ + output( "\t\t\t" + z.ns + " " + tojson( z.min ) + " -->> " + tojson( z.max ) + + " on : " + z.shard + " " + tojson( z.lastmod ) ); + } + ); + } + ); + + print( raw ); +} + +ShardingTest.prototype.sync = function(){ + this.adminCommand( "connpoolsync" ); +} + +ShardingTest.prototype.onNumShards = function( collName , dbName ){ + this.sync(); // we should sync since we're going directly to mongod here + dbName = dbName || "test"; + var num=0; + for ( var i=0; i<this._connections.length; i++ ) + if ( this._connections[i].getDB( dbName ).getCollection( collName ).count() > 0 ) + num++; + return num; +} + +ShardingTest.prototype.shardGo = function( collName , key , split , move , dbName ){ + split = split || key; + move = move || split; + dbName = dbName || "test"; + + var c = dbName + "." + collName; + + s.adminCommand( { shardcollection : c , key : key } ); + s.adminCommand( { split : c , middle : split } ); + s.adminCommand( { movechunk : c , find : move , to : this.getOther( s.getServer( dbName ) ).name } ); + +} + +MongodRunner = function( port, dbpath, peer, arbiter, extraArgs ) { + this.port_ = port; + this.dbpath_ = dbpath; + this.peer_ = peer; + this.arbiter_ = arbiter; + this.extraArgs_ = extraArgs; +} + +MongodRunner.prototype.start = function( reuseData ) { + var args = []; + if ( reuseData ) { + args.push( "mongod" ); + } + args.push( "--port" ); + args.push( this.port_ ); + args.push( "--dbpath" ); + args.push( this.dbpath_ ); + if ( this.peer_ && this.arbiter_ ) { + args.push( "--pairwith" ); + args.push( this.peer_ ); + args.push( "--arbiter" ); + args.push( this.arbiter_ ); + args.push( "--oplogSize" ); + // small oplog by default so startup fast + args.push( "1" ); + } + args.push( "--nohttpinterface" ); + args.push( "--noprealloc" ); + args.push( "--smallfiles" ); + args.push( "--bind_ip" ); + args.push( "127.0.0.1" ); + if ( this.extraArgs_ ) { + args = args.concat( this.extraArgs_ ); + } + if ( reuseData ) { + return startMongoProgram.apply( null, args ); + } else { + return startMongod.apply( null, args ); + } +} + +MongodRunner.prototype.port = function() { return this.port_; } + +MongodRunner.prototype.toString = function() { return [ this.port_, this.dbpath_, this.peer_, this.arbiter_ ].toString(); } + +ReplPair = function( left, right, arbiter ) { + this.left_ = left; + this.leftC_ = null; + this.right_ = right; + this.rightC_ = null; + this.arbiter_ = arbiter; + this.arbiterC_ = null; + this.master_ = null; + this.slave_ = null; +} + +ReplPair.prototype.start = function( reuseData ) { + if ( this.arbiterC_ == null ) { + this.arbiterC_ = this.arbiter_.start(); + } + if ( this.leftC_ == null ) { + this.leftC_ = this.left_.start( reuseData ); + } + if ( this.rightC_ == null ) { + this.rightC_ = this.right_.start( reuseData ); + } +} + +ReplPair.prototype.isMaster = function( mongo, debug ) { + var im = mongo.getDB( "admin" ).runCommand( { ismaster : 1 } ); + assert( im && im.ok, "command ismaster failed" ); + if ( debug ) { + printjson( im ); + } + return im.ismaster; +} + +ReplPair.prototype.isInitialSyncComplete = function( mongo, debug ) { + var isc = mongo.getDB( "admin" ).runCommand( { isinitialsynccomplete : 1 } ); + assert( isc && isc.ok, "command isinitialsynccomplete failed" ); + if ( debug ) { + printjson( isc ); + } + return isc.initialsynccomplete; +} + +ReplPair.prototype.checkSteadyState = function( state, expectedMasterHost, twoMasterOk, leftValues, rightValues, debug ) { + leftValues = leftValues || {}; + rightValues = rightValues || {}; + + var lm = null; + var lisc = null; + if ( this.leftC_ != null ) { + lm = this.isMaster( this.leftC_, debug ); + leftValues[ lm ] = true; + lisc = this.isInitialSyncComplete( this.leftC_, debug ); + } + var rm = null; + var risc = null; + if ( this.rightC_ != null ) { + rm = this.isMaster( this.rightC_, debug ); + rightValues[ rm ] = true; + risc = this.isInitialSyncComplete( this.rightC_, debug ); + } + + var stateSet = {} + state.forEach( function( i ) { stateSet[ i ] = true; } ); + if ( !( 1 in stateSet ) || ( ( risc || risc == null ) && ( lisc || lisc == null ) ) ) { + if ( rm == 1 && lm != 1 ) { + assert( twoMasterOk || !( 1 in leftValues ) ); + this.master_ = this.rightC_; + this.slave_ = this.leftC_; + } else if ( lm == 1 && rm != 1 ) { + assert( twoMasterOk || !( 1 in rightValues ) ); + this.master_ = this.leftC_; + this.slave_ = this.rightC_; + } + if ( !twoMasterOk ) { + assert( lm != 1 || rm != 1, "two masters" ); + } + // check for expected state + if ( state.sort().toString() == [ lm, rm ].sort().toString() ) { + if ( expectedMasterHost != null ) { + if( expectedMasterHost == this.master_.host ) { + return true; + } + } else { + return true; + } + } + } + + this.master_ = null; + this.slave_ = null; + return false; +} + +ReplPair.prototype.waitForSteadyState = function( state, expectedMasterHost, twoMasterOk, debug ) { + state = state || [ 1, 0 ]; + twoMasterOk = twoMasterOk || false; + var rp = this; + var leftValues = {}; + var rightValues = {}; + assert.soon( function() { return rp.checkSteadyState( state, expectedMasterHost, twoMasterOk, leftValues, rightValues, debug ); }, + "rp (" + rp + ") failed to reach expected steady state (" + state + ")" ); +} + +ReplPair.prototype.master = function() { return this.master_; } +ReplPair.prototype.slave = function() { return this.slave_; } +ReplPair.prototype.right = function() { return this.rightC_; } +ReplPair.prototype.left = function() { return this.leftC_; } +ReplPair.prototype.arbiter = function() { return this.arbiterC_; } + +ReplPair.prototype.killNode = function( mongo, signal ) { + signal = signal || 15; + if ( this.leftC_ != null && this.leftC_.host == mongo.host ) { + stopMongod( this.left_.port_ ); + this.leftC_ = null; + } + if ( this.rightC_ != null && this.rightC_.host == mongo.host ) { + stopMongod( this.right_.port_ ); + this.rightC_ = null; + } + if ( this.arbiterC_ != null && this.arbiterC_.host == mongo.host ) { + stopMongod( this.arbiter_.port_ ); + this.arbiterC_ = null; + } +} + +ReplPair.prototype._annotatedNode = function( mongo ) { + var ret = ""; + if ( mongo != null ) { + ret += " (connected)"; + if ( this.master_ != null && mongo.host == this.master_.host ) { + ret += "(master)"; + } + if ( this.slave_ != null && mongo.host == this.slave_.host ) { + ret += "(slave)"; + } + } + return ret; +} + +ReplPair.prototype.toString = function() { + var ret = ""; + ret += "left: " + this.left_; + ret += " " + this._annotatedNode( this.leftC_ ); + ret += " right: " + this.right_; + ret += " " + this._annotatedNode( this.rightC_ ); + return ret; +} + + +ToolTest = function( name ){ + this.name = name; + this.port = allocatePorts(1)[0]; + this.baseName = "jstests_tool_" + name; + this.root = "/data/db/" + this.baseName; + this.dbpath = this.root + "/"; + this.ext = this.root + "_external/"; + this.extFile = this.root + "_external/a"; + resetDbpath( this.dbpath ); +} + +ToolTest.prototype.startDB = function( coll ){ + assert( ! this.m , "db already running" ); + + this.m = startMongoProgram( "mongod" , "--port", this.port , "--dbpath" , this.dbpath , "--nohttpinterface", "--noprealloc" , "--smallfiles" , "--bind_ip", "127.0.0.1" ); + this.db = this.m.getDB( this.baseName ); + if ( coll ) + return this.db.getCollection( coll ); + return this.db; +} + +ToolTest.prototype.stop = function(){ + if ( ! this.m ) + return; + stopMongod( this.port ); + this.m = null; + this.db = null; +} + +ToolTest.prototype.runTool = function(){ + var a = [ "mongo" + arguments[0] ]; + + var hasdbpath = false; + + for ( var i=1; i<arguments.length; i++ ){ + a.push( arguments[i] ); + if ( arguments[i] == "--dbpath" ) + hasdbpath = true; + } + + if ( ! hasdbpath ){ + a.push( "--host" ); + a.push( "127.0.0.1:" + this.port ); + } + + runMongoProgram.apply( null , a ); +} + + +ReplTest = function( name, ports ){ + this.name = name; + this.ports = ports || allocatePorts( 2 ); +} + +ReplTest.prototype.getPort = function( master ){ + if ( master ) + return this.ports[ 0 ]; + return this.ports[ 1 ] +} + +ReplTest.prototype.getPath = function( master ){ + var p = "/data/db/" + this.name + "-"; + if ( master ) + p += "master"; + else + p += "slave" + return p; +} + + +ReplTest.prototype.getOptions = function( master , extra , putBinaryFirst ){ + + if ( ! extra ) + extra = {}; + + if ( ! extra.oplogSize ) + extra.oplogSize = "1"; + + var a = [] + if ( putBinaryFirst ) + a.push( "mongod" ) + a.push( "--nohttpinterface", "--noprealloc", "--bind_ip" , "127.0.0.1" , "--smallfiles" ); + + a.push( "--port" ); + a.push( this.getPort( master ) ); + + a.push( "--dbpath" ); + a.push( this.getPath( master ) ); + + + if ( master ){ + a.push( "--master" ); + } + else { + a.push( "--slave" ); + a.push( "--source" ); + a.push( "127.0.0.1:" + this.ports[0] ); + } + + for ( var k in extra ){ + var v = extra[k]; + a.push( "--" + k ); + if ( v != null ) + a.push( v ); + } + + return a; +} + +ReplTest.prototype.start = function( master , options , restart ){ + var o = this.getOptions( master , options , restart ); + if ( restart ) + return startMongoProgram.apply( null , o ); + else + return startMongod.apply( null , o ); +} + +ReplTest.prototype.stop = function( master , signal ){ + if ( arguments.length == 0 ){ + this.stop( true ); + this.stop( false ); + return; + } + stopMongod( this.getPort( master ) , signal || 15 ); +} diff --git a/shell/utils.cpp b/shell/utils.cpp new file mode 100644 index 0000000..c4735ee --- /dev/null +++ b/shell/utils.cpp @@ -0,0 +1,541 @@ +// utils.cpp + +#include <boost/thread/xtime.hpp> + +#include <cstring> +#include <cstdio> +#include <cstdlib> +#include <assert.h> +#include <iostream> +#include <map> +#include <sstream> +#include <vector> + +#ifndef _WIN32 +#include <sys/socket.h> +#include <netinet/in.h> +#endif + +#include "../client/dbclient.h" +#include "../util/processinfo.h" +#include "../util/md5.hpp" +#include "utils.h" + +extern const char * jsconcatcode_server; + +namespace mongo { + + namespace shellUtils { + + std::string _dbConnect; + std::string _dbAuth; + + const char *argv0 = 0; + void RecordMyLocation( const char *_argv0 ) { argv0 = _argv0; } + + // helpers + + BSONObj makeUndefined() { + BSONObjBuilder b; + b.appendUndefined( "" ); + return b.obj(); + } + const BSONObj undefined_ = makeUndefined(); + + BSONObj encapsulate( const BSONObj &obj ) { + return BSON( "" << obj ); + } + + void sleepms( int ms ) { + boost::xtime xt; + boost::xtime_get(&xt, boost::TIME_UTC); + xt.sec += ( ms / 1000 ); + xt.nsec += ( ms % 1000 ) * 1000000; + if ( xt.nsec >= 1000000000 ) { + xt.nsec -= 1000000000; + xt.sec++; + } + boost::thread::sleep(xt); + } + + // real methods + + + + mongo::BSONObj JSSleep(const mongo::BSONObj &args){ + assert( args.nFields() == 1 ); + assert( args.firstElement().isNumber() ); + int ms = int( args.firstElement().number() ); + { + auto_ptr< ScriptEngine::Unlocker > u = globalScriptEngine->newThreadUnlocker(); + sleepms( ms ); + } + return undefined_; + } + + BSONObj listFiles(const BSONObj& args){ + uassert( 10257 , "need to specify 1 argument to listFiles" , args.nFields() == 1 ); + + BSONObjBuilder lst; + + string rootname = args.firstElement().valuestrsafe(); + path root( rootname ); + uassert( 12581, "listFiles: no such directory", boost::filesystem::exists( root ) ); + + directory_iterator end; + directory_iterator i( root); + + int num =0; + while ( i != end ){ + path p = *i; + BSONObjBuilder b; + b << "name" << p.string(); + b.appendBool( "isDirectory", is_directory( p ) ); + if ( ! is_directory( p ) ){ + try { + b.append( "size" , (double)file_size( p ) ); + } + catch ( ... ){ + i++; + continue; + } + } + + stringstream ss; + ss << num; + string name = ss.str(); + lst.append( name.c_str(), b.done() ); + num++; + i++; + } + + BSONObjBuilder ret; + ret.appendArray( "", lst.done() ); + return ret.obj(); + } + + BSONObj Quit(const BSONObj& args) { + // If not arguments are given first element will be EOO, which + // converts to the integer value 0. + int exit_code = int( args.firstElement().number() ); + ::exit(exit_code); + return undefined_; + } + + BSONObj JSGetMemInfo( const BSONObj& args ){ + ProcessInfo pi; + uassert( 10258 , "processinfo not supported" , pi.supported() ); + + BSONObjBuilder e; + e.append( "virtual" , pi.getVirtualMemorySize() ); + e.append( "resident" , pi.getResidentSize() ); + + BSONObjBuilder b; + b.append( "ret" , e.obj() ); + + return b.obj(); + } + + BSONObj JSVersion( const BSONObj& args ){ + cout << "version: " << versionString << endl; + if ( strstr( versionString , "+" ) ) + printGitVersion(); + return BSONObj(); + } + +#ifndef _WIN32 +#include <signal.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/wait.h> + + BSONObj AllocatePorts( const BSONObj &args ) { + uassert( 10259 , "allocatePorts takes exactly 1 argument", args.nFields() == 1 ); + uassert( 10260 , "allocatePorts needs to be passed an integer", args.firstElement().isNumber() ); + + int n = int( args.firstElement().number() ); + + vector< int > ports; + vector< int > sockets; + for( int i = 0; i < n; ++i ) { + int s = socket( AF_INET, SOCK_STREAM, 0 ); + assert( s ); + + sockaddr_in address; + memset(address.sin_zero, 0, sizeof(address.sin_zero)); + address.sin_family = AF_INET; + address.sin_port = 0; + address.sin_addr.s_addr = inet_addr( "127.0.0.1" ); + assert( 0 == ::bind( s, (sockaddr*)&address, sizeof( address ) ) ); + + sockaddr_in newAddress; + socklen_t len = sizeof( newAddress ); + assert( 0 == getsockname( s, (sockaddr*)&newAddress, &len ) ); + ports.push_back( ntohs( newAddress.sin_port ) ); + sockets.push_back( s ); + } + for( vector< int >::const_iterator i = sockets.begin(); i != sockets.end(); ++i ) + assert( 0 == close( *i ) ); + + sort( ports.begin(), ports.end() ); + for( unsigned i = 1; i < ports.size(); ++i ) + massert( 10434 , "duplicate ports allocated", ports[ i - 1 ] != ports[ i ] ); + BSONObjBuilder b; + b.append( "", ports ); + return b.obj(); + } + + map< int, pair< pid_t, int > > dbs; + map< pid_t, int > shells; + + char *copyString( const char *original ) { + char *ret = reinterpret_cast< char * >( malloc( strlen( original ) + 1 ) ); + strcpy( ret, original ); + return ret; + } + + boost::mutex &mongoProgramOutputMutex( *( new boost::mutex ) ); + stringstream mongoProgramOutput_; + + void writeMongoProgramOutputLine( int port, int pid, const char *line ) { + boost::mutex::scoped_lock lk( mongoProgramOutputMutex ); + stringstream buf; + if ( port > 0 ) + buf << "m" << port << "| " << line; + else + buf << "sh" << pid << "| " << line; + cout << buf.str() << endl; + mongoProgramOutput_ << buf.str() << endl; + } + + BSONObj RawMongoProgramOutput( const BSONObj &args ) { + boost::mutex::scoped_lock lk( mongoProgramOutputMutex ); + return BSON( "" << mongoProgramOutput_.str() ); + } + + class MongoProgramRunner { + char **argv_; + int port_; + int pipe_; + pid_t pid_; + public: + pid_t pid() const { return pid_; } + MongoProgramRunner( const BSONObj &args ) { + assert( args.nFields() > 0 ); + string program( args.firstElement().valuestrsafe() ); + + assert( !program.empty() ); + boost::filesystem::path programPath = ( boost::filesystem::path( argv0 ) ).branch_path() / program; + massert( 10435 , "couldn't find " + programPath.native_file_string(), boost::filesystem::exists( programPath ) ); + + port_ = -1; + argv_ = new char *[ args.nFields() + 1 ]; + { + string s = programPath.native_file_string(); + if ( s == program ) + s = "./" + s; + argv_[ 0 ] = copyString( s.c_str() ); + } + + BSONObjIterator j( args ); + j.next(); + for( int i = 1; i < args.nFields(); ++i ) { + BSONElement e = j.next(); + string str; + if ( e.isNumber() ) { + stringstream ss; + ss << e.number(); + str = ss.str(); + } else { + assert( e.type() == mongo::String ); + str = e.valuestr(); + } + char *s = copyString( str.c_str() ); + if ( string( "--port" ) == s ) + port_ = -2; + else if ( port_ == -2 ) + port_ = strtol( s, 0, 10 ); + argv_[ i ] = s; + } + argv_[ args.nFields() ] = 0; + + if ( program != "mongod" && program != "mongos" && program != "mongobridge" ) + port_ = 0; + else + assert( port_ > 0 ); + if ( port_ > 0 && dbs.count( port_ ) != 0 ){ + cerr << "count for port: " << port_ << " is not 0 is: " << dbs.count( port_ ) << endl; + assert( dbs.count( port_ ) == 0 ); + } + } + + void start() { + int pipeEnds[ 2 ]; + assert( pipe( pipeEnds ) != -1 ); + + fflush( 0 ); + pid_ = fork(); + assert( pid_ != -1 ); + + if ( pid_ == 0 ) { + + assert( dup2( pipeEnds[ 1 ], STDOUT_FILENO ) != -1 ); + assert( dup2( pipeEnds[ 1 ], STDERR_FILENO ) != -1 ); + execvp( argv_[ 0 ], argv_ ); + massert( 10436 , "Unable to start program" , 0 ); + } + + cout << "shell: started mongo program"; + int i = 0; + while( argv_[ i ] ) + cout << " " << argv_[ i++ ]; + cout << endl; + + i = 0; + while( argv_[ i ] ) + free( argv_[ i++ ] ); + free( argv_ ); + + if ( port_ > 0 ) + dbs.insert( make_pair( port_, make_pair( pid_, pipeEnds[ 1 ] ) ) ); + else + shells.insert( make_pair( pid_, pipeEnds[ 1 ] ) ); + pipe_ = pipeEnds[ 0 ]; + } + + // Continue reading output + void operator()() { + // This assumes there aren't any 0's in the mongo program output. + // Hope that's ok. + char buf[ 1024 ]; + char temp[ 1024 ]; + char *start = buf; + while( 1 ) { + int lenToRead = 1023 - ( start - buf ); + int ret = read( pipe_, (void *)start, lenToRead ); + assert( ret != -1 ); + start[ ret ] = '\0'; + if ( strlen( start ) != unsigned( ret ) ) + writeMongoProgramOutputLine( port_, pid_, "WARNING: mongod wrote null bytes to output" ); + char *last = buf; + for( char *i = strchr( buf, '\n' ); i; last = i + 1, i = strchr( last, '\n' ) ) { + *i = '\0'; + writeMongoProgramOutputLine( port_, pid_, last ); + } + if ( ret == 0 ) { + if ( *last ) + writeMongoProgramOutputLine( port_, pid_, last ); + close( pipe_ ); + break; + } + if ( last != buf ) { + strcpy( temp, last ); + strcpy( buf, temp ); + } else { + assert( strlen( buf ) < 1023 ); + } + start = buf + strlen( buf ); + } + } + }; + + BSONObj StartMongoProgram( const BSONObj &a ) { + MongoProgramRunner r( a ); + r.start(); + boost::thread t( r ); + return BSON( string( "" ) << int( r.pid() ) ); + } + + BSONObj RunMongoProgram( const BSONObj &a ) { + MongoProgramRunner r( a ); + r.start(); + boost::thread t( r ); + int temp; + waitpid( r.pid() , &temp , 0 ); + shells.erase( r.pid() ); + return BSON( string( "" ) << int( r.pid() ) ); + } + + BSONObj ResetDbpath( const BSONObj &a ) { + assert( a.nFields() == 1 ); + string path = a.firstElement().valuestrsafe(); + assert( !path.empty() ); + if ( boost::filesystem::exists( path ) ) + boost::filesystem::remove_all( path ); + boost::filesystem::create_directory( path ); + return undefined_; + } + + void killDb( int port, pid_t _pid, int signal ) { + pid_t pid; + if ( port > 0 ) { + if( dbs.count( port ) != 1 ) { + cout << "No db started on port: " << port << endl; + return; + } + pid = dbs[ port ].first; + } else { + pid = _pid; + } + + assert( 0 == kill( pid, signal ) ); + + int i = 0; + for( ; i < 65; ++i ) { + if ( i == 5 ) { + char now[64]; + time_t_to_String(time(0), now); + now[ 20 ] = 0; + cout << now << " process on port " << port << ", with pid " << pid << " not terminated, sending sigkill" << endl; + assert( 0 == kill( pid, SIGKILL ) ); + } + int temp; + int ret = waitpid( pid, &temp, WNOHANG ); + if ( ret == pid ) + break; + sleepms( 1000 ); + } + if ( i == 65 ) { + char now[64]; + time_t_to_String(time(0), now); + now[ 20 ] = 0; + cout << now << " failed to terminate process on port " << port << ", with pid " << pid << endl; + assert( "Failed to terminate process" == 0 ); + } + + if ( port > 0 ) { + close( dbs[ port ].second ); + dbs.erase( port ); + } else { + close( shells[ pid ] ); + shells.erase( pid ); + } + if ( i > 4 || signal == SIGKILL ) { + sleepms( 4000 ); // allow operating system to reclaim resources + } + } + + int getSignal( const BSONObj &a ) { + int ret = SIGTERM; + if ( a.nFields() == 2 ) { + BSONObjIterator i( a ); + i.next(); + BSONElement e = i.next(); + assert( e.isNumber() ); + ret = int( e.number() ); + } + return ret; + } + + BSONObj StopMongoProgram( const BSONObj &a ) { + assert( a.nFields() == 1 || a.nFields() == 2 ); + assert( a.firstElement().isNumber() ); + int port = int( a.firstElement().number() ); + killDb( port, 0, getSignal( a ) ); + cout << "shell: stopped mongo program on port " << port << endl; + return undefined_; + } + + BSONObj StopMongoProgramByPid( const BSONObj &a ) { + assert( a.nFields() == 1 || a.nFields() == 2 ); + assert( a.firstElement().isNumber() ); + int pid = int( a.firstElement().number() ); + killDb( 0, pid, getSignal( a ) ); + cout << "shell: stopped mongo program on pid " << pid << endl; + return undefined_; + } + + void KillMongoProgramInstances() { + vector< int > ports; + for( map< int, pair< pid_t, int > >::iterator i = dbs.begin(); i != dbs.end(); ++i ) + ports.push_back( i->first ); + for( vector< int >::iterator i = ports.begin(); i != ports.end(); ++i ) + killDb( *i, 0, SIGTERM ); + vector< pid_t > pids; + for( map< pid_t, int >::iterator i = shells.begin(); i != shells.end(); ++i ) + pids.push_back( i->first ); + for( vector< pid_t >::iterator i = pids.begin(); i != pids.end(); ++i ) + killDb( 0, *i, SIGTERM ); + } + + MongoProgramScope::~MongoProgramScope() { + try { + KillMongoProgramInstances(); + } catch ( ... ) { + assert( false ); + } + } + +#else + MongoProgramScope::~MongoProgramScope() {} + void KillMongoProgramInstances() {} +#endif + + BSONObj jsmd5( const BSONObj &a ){ + uassert( 10261 , "js md5 needs a string" , a.firstElement().type() == String ); + const char * s = a.firstElement().valuestrsafe(); + + md5digest d; + md5_state_t st; + md5_init(&st); + md5_append( &st , (const md5_byte_t*)s , strlen( s ) ); + md5_finish(&st, d); + + return BSON( "" << digestToString( d ) ); + } + + unsigned _randomSeed; + + BSONObj JSSrand( const BSONObj &a ) { + uassert( 12518, "srand requires a single numeric argument", + a.nFields() == 1 && a.firstElement().isNumber() ); + _randomSeed = a.firstElement().numberLong(); // grab least significant digits + return undefined_; + } + + BSONObj JSRand( const BSONObj &a ) { + uassert( 12519, "rand accepts no arguments", a.nFields() == 0 ); + unsigned r; +#if !defined(_WIN32) + r = rand_r( &_randomSeed ); +#else + r = rand(); // seed not used in this case +#endif + return BSON( "" << double( r ) / ( double( RAND_MAX ) + 1 ) ); + } + + void installShellUtils( Scope& scope ){ + scope.injectNative( "listFiles" , listFiles ); + scope.injectNative( "sleep" , JSSleep ); + scope.injectNative( "quit", Quit ); + scope.injectNative( "getMemInfo" , JSGetMemInfo ); + scope.injectNative( "version" , JSVersion ); + scope.injectNative( "hex_md5" , jsmd5 ); + scope.injectNative( "_srand" , JSSrand ); + scope.injectNative( "_rand" , JSRand ); +#if !defined(_WIN32) + scope.injectNative( "allocatePorts", AllocatePorts ); + scope.injectNative( "_startMongoProgram", StartMongoProgram ); + scope.injectNative( "runMongoProgram", RunMongoProgram ); + scope.injectNative( "stopMongod", StopMongoProgram ); + scope.injectNative( "stopMongoProgram", StopMongoProgram ); + scope.injectNative( "stopMongoProgramByPid", StopMongoProgramByPid ); + scope.injectNative( "resetDbpath", ResetDbpath ); + scope.injectNative( "rawMongoProgramOutput", RawMongoProgramOutput ); +#endif + } + + void initScope( Scope &scope ) { + scope.externalSetup(); + mongo::shellUtils::installShellUtils( scope ); + scope.execSetup( jsconcatcode_server , "setupServerCode" ); + + if ( !_dbConnect.empty() ) { + uassert( 12513, "connect failed", scope.exec( _dbConnect , "(connect)" , false , true , false ) ); + if ( !_dbAuth.empty() ) { + uassert( 12514, "login failed", scope.exec( _dbAuth , "(auth)" , true , true , false ) ); + } + } + } + } +} diff --git a/shell/utils.h b/shell/utils.h new file mode 100644 index 0000000..7c98e2c --- /dev/null +++ b/shell/utils.h @@ -0,0 +1,27 @@ +// utils.h + +#pragma once + +#include "../scripting/engine.h" + +namespace mongo { + + namespace shellUtils { + + extern std::string _dbConnect; + extern std::string _dbAuth; + + void RecordMyLocation( const char *_argv0 ); + void installShellUtils( Scope& scope ); + + // Scoped management of mongo program instances. Simple implementation: + // destructor kills all mongod instances created by the shell. + struct MongoProgramScope { + MongoProgramScope() {} // Avoid 'unused variable' warning. + ~MongoProgramScope(); + }; + void KillMongoProgramInstances(); + + void initScope( Scope &scope ); + } +} diff --git a/shell/utils.js b/shell/utils.js new file mode 100644 index 0000000..2d59b80 --- /dev/null +++ b/shell/utils.js @@ -0,0 +1,907 @@ + +__quiet = false; + +chatty = function(s){ + if ( ! __quiet ) + print( s ); +} + +friendlyEqual = function( a , b ){ + if ( a == b ) + return true; + + if ( tojson( a ) == tojson( b ) ) + return true; + + return false; +} + + +doassert = function( msg ){ + print( "assert: " + msg ); + throw msg; +} + +assert = function( b , msg ){ + if ( assert._debug && msg ) print( "in assert for: " + msg ); + + if ( b ) + return; + + doassert( "assert failed : " + msg ); +} + +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.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.soon = 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 ) + doassert( "assert.soon failed: " + f + ", msg:" + msg ); + sleep( interval ); + } +} + +assert.throws = function( func , params , msg ){ + if ( assert._debug && msg ) print( "in assert for: " + msg ); + try { + func.apply( null , params ); + } + catch ( e ){ + return e; + } + + doassert( "did not throw exception: " + msg ); +} + +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 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 ); +} + +Object.extend = function( dst , src , deep ){ + for ( var k in src ){ + var v = src[k]; + if ( deep && typeof(v) == "object" ){ + v = Object.extend( typeof ( v.length ) == "number" ? [] : {} , v , true ); + } + dst[k] = v; + } + return dst; +} + +argumentsToArray = function( a ){ + var arr = []; + for ( var i=0; i<a.length; i++ ) + arr[i] = a[i]; + 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+$/,""); +} + +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(){ + return "\"" + this.toString() + "\""; +} + +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 ){ + if (!indent) + indent = ""; + + if (a.length == 0) { + return "[ ]"; + } + + var s = "[\n"; + indent += "\t"; + for ( var i=0; i<a.length; i++){ + s += indent + tojson( a[i], indent ); + if ( i < a.length - 1 ){ + s += ",\n"; + } + } + if ( a.length == 0 ) { + s += indent; + } + + indent = indent.substring(1); + s += "\n"+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 ); +} + +Object.keySet = function( o ) { + var ret = new Array(); + for( i in o ) { + if ( !( i in o.__proto__ && o[ i ] === o.__proto__[ i ] ) ) { + ret.push( i ); + } + } + return ret; +} + +if ( ! ObjectId.prototype ) + ObjectId.prototype = {} + +ObjectId.prototype.toString = function(){ + return this.str; +} + +ObjectId.prototype.tojson = function(){ + return "ObjectId(\"" + this.str + "\")"; +} + +ObjectId.prototype.isObjectId = true; + +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 tojson({"ns" : this.ns, "id" : this.id}, indent); + } + + DBPointer.prototype.getCollection = function(){ + return this.ns; + } + + DBPointer.prototype.toString = function(){ + return "DBPointer " + this.ns + ":" + 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 tojson({"$ref" : this.$ref, "$id" : this.$id}, indent); + } + + DBRef.prototype.getCollection = function(){ + return this.$ref; + } + + DBRef.prototype.toString = function(){ + return this.tojson(); + } +} +else { + print( "warning: no DBRef" ); +} + +if ( typeof( BinData ) != "undefined" ){ + BinData.prototype.tojson = function(){ + return "BinData type: " + this.type + " len: " + this.len; + } +} +else { + print( "warning: no BinData" ); +} + +if ( typeof _threadInject != "undefined" ){ + print( "fork() available!" ); + + Thread = function(){ + this.init.apply( this, arguments ); + } + _threadInject( Thread.prototype ); + + ScopedThread = function() { + this.init.apply( this, arguments ); + } + ScopedThread.prototype = new Thread( function() {} ); + _scopedThreadInject( ScopedThread.prototype ); + + fork = function() { + var t = new Thread( function() {} ); + Thread.apply( t, arguments ); + return t; + } + + // Helper class to generate a list of events which may be executed by a ParallelTester + EventGenerator = function( me, collectionName, mean ) { + this.mean = mean; + this.events = new Array( me, collectionName ); + } + + EventGenerator.prototype._add = function( action ) { + this.events.push( [ Random.genExp( this.mean ), action ] ); + } + + EventGenerator.prototype.addInsert = function( obj ) { + this._add( "t.insert( " + tojson( obj ) + " )" ); + } + + EventGenerator.prototype.addRemove = function( obj ) { + this._add( "t.remove( " + tojson( obj ) + " )" ); + } + + EventGenerator.prototype.addUpdate = function( objOld, objNew ) { + this._add( "t.update( " + tojson( objOld ) + ", " + tojson( objNew ) + " )" ); + } + + EventGenerator.prototype.addCheckCount = function( count, query, shouldPrint, checkQuery ) { + query = query || {}; + shouldPrint = shouldPrint || false; + checkQuery = checkQuery || false; + var action = "assert.eq( " + count + ", t.count( " + tojson( query ) + " ) );" + if ( checkQuery ) { + action += " assert.eq( " + count + ", t.find( " + tojson( query ) + " ).toArray().length );" + } + if ( shouldPrint ) { + action += " print( me + ' ' + " + count + " );"; + } + this._add( action ); + } + + EventGenerator.prototype.getEvents = function() { + return this.events; + } + + EventGenerator.dispatch = function() { + var args = argumentsToArray( arguments ); + var me = args.shift(); + var collectionName = args.shift(); + var m = new Mongo( db.getMongo().host ); + var t = m.getDB( "test" )[ collectionName ]; + for( var i in args ) { + sleep( args[ i ][ 0 ] ); + eval( args[ i ][ 1 ] ); + } + } + + // Helper class for running tests in parallel. It assembles a set of tests + // and then calls assert.parallelests to run them. + ParallelTester = function() { + this.params = new Array(); + } + + ParallelTester.prototype.add = function( fun, args ) { + args = args || []; + args.unshift( fun ); + this.params.push( args ); + } + + ParallelTester.prototype.run = function( msg, newScopes ) { + newScopes = newScopes || false; + assert.parallelTests( this.params, msg, newScopes ); + } + + // creates lists of tests from jstests dir in a format suitable for use by + // ParallelTester.fileTester. The lists will be in random order. + // n: number of lists to split these tests into + ParallelTester.createJstestsLists = function( n ) { + var params = new Array(); + for( var i = 0; i < n; ++i ) { + params.push( [] ); + } + + var makeKeys = function( a ) { + var ret = {}; + for( var i in a ) { + ret[ a[ i ] ] = 1; + } + return ret; + } + + // some tests can't run in parallel with most others + var skipTests = makeKeys( [ "jstests/dbadmin.js", + "jstests/repair.js", + "jstests/cursor8.js", + "jstests/recstore.js", + "jstests/extent.js", + "jstests/indexb.js", + "jstests/profile1.js", + "jstests/mr3.js"] ); + + // some tests can't be run in parallel with each other + var serialTestsArr = [ "jstests/fsync.js", + "jstests/fsync2.js" ]; + var serialTests = makeKeys( serialTestsArr ); + + params[ 0 ] = serialTestsArr; + + var files = listFiles("jstests"); + files = Array.shuffle( files ); + + var i = 0; + files.forEach( + function(x) { + + if ( /_runner/.test(x.name) || + /_lodeRunner/.test(x.name) || + ( x.name in skipTests ) || + ( x.name in serialTests ) || + ! /\.js$/.test(x.name ) ){ + print(" >>>>>>>>>>>>>>> skipping " + x.name); + return; + } + + params[ i % n ].push( x.name ); + ++i; + } + ); + + // randomize ordering of the serialTests + params[ 0 ] = Array.shuffle( params[ 0 ] ); + + for( var i in params ) { + params[ i ].unshift( i ); + } + + return params; + } + + // runs a set of test files + // first argument is an identifier for this tester, remaining arguments are file names + ParallelTester.fileTester = function() { + var args = argumentsToArray( arguments ); + var suite = args.shift(); + args.forEach( + function( x ) { + print(" S" + suite + " Test : " + x + " ..."); + var time = Date.timeFunc( function() { load(x); }, 1); + print(" S" + suite + " Test : " + x + " " + time + "ms" ); + } + ); + } + + // params: array of arrays, each element of which consists of a function followed + // by zero or more arguments to that function. Each function and its arguments will + // be called in a separate thread. + // msg: failure message + // newScopes: if true, each thread starts in a fresh scope + assert.parallelTests = function( params, msg, newScopes ) { + newScopes = newScopes || false; + var wrapper = function( fun, argv ) { + eval ( + "var z = function() {" + + "var __parallelTests__fun = " + fun.toString() + ";" + + "var __parallelTests__argv = " + tojson( argv ) + ";" + + "var __parallelTests__passed = false;" + + "try {" + + "__parallelTests__fun.apply( 0, __parallelTests__argv );" + + "__parallelTests__passed = true;" + + "} catch ( e ) {" + + "print( e );" + + "}" + + "return __parallelTests__passed;" + + "}" + ); + return z; + } + var runners = new Array(); + for( var i in params ) { + var param = params[ i ]; + var test = param.shift(); + var t; + if ( newScopes ) + t = new ScopedThread( wrapper( test, param ) ); + else + t = new Thread( wrapper( test, param ) ); + runners.push( t ); + } + + runners.forEach( function( x ) { x.start(); } ); + var nFailed = 0; + // v8 doesn't like it if we exit before all threads are joined (SERVER-529) + runners.forEach( function( x ) { if( !x.returnData() ) { ++nFailed; } } ); + assert.eq( 0, nFailed, msg ); + } +} + +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++ ){ + if ( x[i] == '"' ){ + s += "\\\""; + } + 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 ( typeof( x.constructor.tojson ) == "function" && x.constructor.tojson != tojson ) { + return x.constructor.tojson( x, indent , nolint ); + } + + if ( x.toString() == "[object MaxKey]" ) + return "{ $maxKey : 1 }"; + if ( x.toString() == "[object MinKey]" ) + return "{ $minKey : 1 }"; + + 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 ) + shellPrintHelper( x ); + + if ( db ){ + var e = db.getPrevError(); + if ( e.err ) { + if( e.nPrev <= 1 ) + print( "error on last call: " + tojson( e.err ) ); + else + print( "an error " + tojson(e.err) + " occurred " + e.nPrev + " operations back in the command invocation" ); + } + db.resetError(); + } +} + +printjson = function(x){ + print( tojson( x ) ); +} + +shellPrintHelper = function( x ){ + + if ( typeof( x ) == "undefined" ){ + + if ( typeof( db ) != "undefined" && db.getLastError ){ + var e = db.getLastError(); + if ( e != null ) + print( e ); + } + + return; + } + + if ( x == null ){ + print( "null" ); + return; + } + + if ( typeof x != "object" ) + return print( x ); + + var p = x.shellPrint; + if ( typeof p == "function" ) + return x.shellPrint(); + + var p = x.tojson; + if ( typeof p == "function" ) + print( x.tojson() ); + else + print( tojson( x ) ); +} + +shellHelper = function( command , rest , shouldPrint ){ + command = command.trim(); + var args = rest.trim().replace(/;$/,"").split( "\s+" ); + + if ( ! shellHelper[command] ) + throw "no command [" + command + "]"; + + var res = shellHelper[command].apply( null , args ); + if ( shouldPrint ){ + shellPrintHelper( res ); + } + return res; +} + +help = shellHelper.help = function(){ + print( "HELP" ); + print( "\t" + "show dbs show database names"); + print( "\t" + "show collections show collections in current database"); + print( "\t" + "show users show users in current database"); + print( "\t" + "show profile show most recent system.profile entries with time >= 1ms"); + print( "\t" + "use <db name> set curent database to <db name>" ); + print( "\t" + "db.help() help on DB methods"); + print( "\t" + "db.foo.help() help on collection methods"); + print( "\t" + "db.foo.find() list objects in collection foo" ); + print( "\t" + "db.foo.find( { a : 1 } ) list objects in foo where a == 1" ); + print( "\t" + "it result of the last line evaluated; use to further iterate"); +} + +shellHelper.use = function( dbname ){ + db = db.getMongo().getDB( dbname ); + print( "switched to db " + db.getName() ); +} + +shellHelper.it = function(){ + if ( typeof( ___it___ ) == "undefined" || ___it___ == null ){ + print( "no cursor" ); + return; + } + shellPrintHelper( ___it___ ); +} + +shellHelper.show = function( what ){ + assert( typeof what == "string" ); + + if( what == "profile" ) { + if( db.system.profile.count() == 0 ) { + print("db.system.profile is empty"); + print("Use db.setProfilingLevel(2) will enable profiling"); + print("Use db.system.profile.find() to show raw profile entries"); + } + else { + print(); + db.system.profile.find({ millis : { $gt : 0 } }).sort({$natural:-1}).limit(5).forEach( function(x){print(""+x.millis+"ms " + String(x.ts).substring(0,24)); print(x.info); print("\n");} ) + } + return ""; + } + + if ( what == "users" ){ + db.system.users.find().forEach( printjson ); + return ""; + } + + if ( what == "collections" || what == "tables" ) { + db.getCollectionNames().forEach( function(x){print(x)} ); + return ""; + } + + if ( what == "dbs" ) { + db.getMongo().getDBNames().sort().forEach( function(x){print(x)} ); + return ""; + } + + throw "don't know how to show [" + 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(){ + } +} + + +Math.sigFig = function( x , N ){ + if ( ! N ){ + N = 3; + } + var p = Math.pow( 10, N - Math.ceil( Math.log( Math.abs(x) ) / Math.log( 10 )) ); + return Math.round(x*p)/p; +} + +Random = function() {} + +// set random seed +Random.srand = function( s ) { _srand( s ); } + +// random number 0 <= r < 1 +Random.rand = function() { return _rand(); } + +// random integer 0 <= r < n +Random.randInt = function( n ) { return Math.floor( Random.rand() * n ); } + +Random.setRandomSeed = function( s ) { + s = s || new Date().getTime(); + print( "setting random seed: " + s ); + Random.srand( s ); +} + +// generate a random value from the exponential distribution with the specified mean +Random.genExp = function( mean ) { + return -Math.log( Random.rand() ) * mean; +} |