summaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
Diffstat (limited to 'shell')
-rw-r--r--shell/collection.js569
-rw-r--r--shell/db.js632
-rw-r--r--shell/dbshell.cpp503
-rw-r--r--shell/mongo.js84
-rw-r--r--shell/mongo_vstudio.cpp2006
-rw-r--r--shell/mr.js95
-rw-r--r--shell/query.js248
-rw-r--r--shell/servers.js608
-rw-r--r--shell/utils.cpp541
-rw-r--r--shell/utils.h27
-rw-r--r--shell/utils.js907
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;
+}