1 // db.js
  2 
  3 var DB;
  4 
  5 (function() {
  6 
  7     if (DB === undefined) {
  8         DB = function(mongo, name) {
  9             this._mongo = mongo;
 10             this._name = name;
 11         };
 12     }
 13 
 14     DB.prototype.getMongo = function() {
 15         assert(this._mongo, "why no mongo!");
 16         return this._mongo;
 17     };
 18 
 19     DB.prototype.getSiblingDB = function(name) {
 20         return this.getMongo().getDB(name);
 21     };
 22 
 23     DB.prototype.getSisterDB = DB.prototype.getSiblingDB;
 24 
 25     DB.prototype.getName = function() {
 26         return this._name;
 27     };
 28 
 29     DB.prototype.stats = function(scale) {
 30         return this.runCommand({dbstats: 1, scale: scale});
 31     };
 32 
 33     DB.prototype.getCollection = function(name) {
 34         return new DBCollection(this._mongo, this, name, this._name + "." + name);
 35     };
 36 
 37     DB.prototype.commandHelp = function(name) {
 38         var c = {};
 39         c[name] = 1;
 40         c.help = true;
 41         var res = this.runCommand(c);
 42         if (!res.ok)
 43             throw _getErrorWithCode(res, res.errmsg);
 44         return res.help;
 45     };
 46 
 47     // utility to attach readPreference if needed.
 48     DB.prototype._attachReadPreferenceToCommand = function(cmdObj, readPref) {
 49         "use strict";
 50         // if the user has not set a readpref, return the original cmdObj
 51         if ((readPref === null) || typeof(readPref) !== "object") {
 52             return cmdObj;
 53         }
 54 
 55         // if user specifies $readPreference manually, then don't change it
 56         if (cmdObj.hasOwnProperty("$readPreference")) {
 57             return cmdObj;
 58         }
 59 
 60         // copy object so we don't mutate the original
 61         var clonedCmdObj = Object.extend({}, cmdObj);
 62         // The server selection spec mandates that the key is '$query', but
 63         // the shell has historically used 'query'. The server accepts both,
 64         // so we maintain the existing behavior
 65         var cmdObjWithReadPref = {query: clonedCmdObj, $readPreference: readPref};
 66         return cmdObjWithReadPref;
 67     };
 68 
 69     // if someone passes i.e. runCommand("foo", {bar: "baz"}
 70     // we merge it in to runCommand({foo: 1, bar: "baz"}
 71     // this helper abstracts that logic.
 72     DB.prototype._mergeCommandOptions = function(commandName, extraKeys) {
 73         "use strict";
 74         var mergedCmdObj = {};
 75         mergedCmdObj[commandName] = 1;
 76 
 77         if (typeof(extraKeys) === "object") {
 78             // this will traverse the prototype chain of extra, but keeping
 79             // to maintain legacy behavior
 80             for (var key in extraKeys) {
 81                 mergedCmdObj[key] = extraKeys[key];
 82             }
 83         }
 84         return mergedCmdObj;
 85     };
 86 
 87     // Like runCommand but applies readPreference if one has been set
 88     // on the connection. Also sets slaveOk if a (non-primary) readPref has been set.
 89     DB.prototype.runReadCommand = function(obj, extra, queryOptions) {
 90         "use strict";
 91 
 92         // Support users who call this function with a string commandName, e.g.
 93         // db.runReadCommand("commandName", {arg1: "value", arg2: "value"}).
 94         var mergedObj = (typeof(obj) === "string") ? this._mergeCommandOptions(obj, extra) : obj;
 95         var cmdObjWithReadPref =
 96             this._attachReadPreferenceToCommand(mergedObj, this.getMongo().getReadPref());
 97 
 98         var options =
 99             (typeof(queryOptions) !== "undefined") ? queryOptions : this.getQueryOptions();
100         var readPrefMode = this.getMongo().getReadPrefMode();
101 
102         // Set slaveOk if readPrefMode has been explicitly set with a readPreference other than
103         // primary.
104         if (!!readPrefMode && readPrefMode !== "primary") {
105             options |= 4;
106         }
107 
108         // The 'extra' parameter is not used as we have already created a merged command object.
109         return this.runCommand(cmdObjWithReadPref, null, options);
110     };
111 
112     // runCommand uses this impl to actually execute the command
113     DB.prototype._runCommandImpl = function(name, obj, options) {
114         return this.getMongo().runCommand(name, obj, options);
115     };
116 
117     DB.prototype.runCommand = function(obj, extra, queryOptions) {
118         var mergedObj = (typeof(obj) === "string") ? this._mergeCommandOptions(obj, extra) : obj;
119         // if options were passed (i.e. because they were overridden on a collection), use them.
120         // Otherwise use getQueryOptions.
121         var options =
122             (typeof(queryOptions) !== "undefined") ? queryOptions : this.getQueryOptions();
123         var res;
124         try {
125             res = this._runCommandImpl(this._name, mergedObj, options);
126         } catch (ex) {
127             // When runCommand flowed through query, a connection error resulted in the message
128             // "error doing query: failed". Even though this message is arguably incorrect
129             // for a command failing due to a connection failure, we preserve it for backwards
130             // compatibility. See SERVER-18334 for details.
131             if (ex.message.indexOf("network error") >= 0) {
132                 throw new Error("error doing query: failed: " + ex.message);
133             }
134             throw ex;
135         }
136         return res;
137     };
138 
139     DB.prototype.runCommandWithMetadata = function(commandName, commandArgs, metadata) {
140         return this.getMongo().runCommandWithMetadata(
141             this._name, commandName, metadata, commandArgs);
142     };
143 
144     DB.prototype._dbCommand = DB.prototype.runCommand;
145     DB.prototype._dbReadCommand = DB.prototype.runReadCommand;
146 
147     DB.prototype.adminCommand = function(obj, extra) {
148         if (this._name == "admin")
149             return this.runCommand(obj, extra);
150         return this.getSiblingDB("admin").runCommand(obj, extra);
151     };
152 
153     DB.prototype._adminCommand = DB.prototype.adminCommand;  // alias old name
154 
155     /**
156       Create a new collection in the database.  Normally, collection creation is automatic.  You
157      would
158        use this function if you wish to specify special options on creation.
159 
160        If the collection already exists, no action occurs.
161 
162         <p>Options:</p>
163         <ul>
164         <li>
165             size: desired initial extent size for the collection.  Must be <= 1000000000.
166                   for fixed size (capped) collections, this size is the total/max size of the
167                   collection.
168         </li>
169         <li>
170             capped: if true, this is a capped collection (where old data rolls out).
171         </li>
172         <li> max: maximum number of objects if capped (optional).</li>
173         <li> usePowerOf2Sizes: if true, set usePowerOf2Sizes allocation for the collection.</li>
174         <li>
175             storageEngine: BSON document containing storage engine specific options. Format:
176                            {
177                                storageEngine: {
178                                    storageEngine1: {
179                                        ...
180                                    },
181                                    storageEngine2: {
182                                        ...
183                                    },
184                                    ...
185                                }
186                            }
187         </li>
188         </ul>
189 
190         <p>Example:</p>
191         <code>db.createCollection("movies", { size: 10 * 1024 * 1024, capped:true } );</code>
192 
193      * @param {String} name Name of new collection to create
194      * @param {Object} options Object with options for call.  Options are listed above.
195      * @return SOMETHING_FIXME
196     */
197     DB.prototype.createCollection = function(name, opt) {
198         var options = opt || {};
199 
200         // We have special handling for the 'flags' field, and provide sugar for specific flags. If
201         // the
202         // user specifies any flags we send the field in the command. Otherwise, we leave it blank
203         // and
204         // use the server's defaults.
205         var sendFlags = false;
206         var flags = 0;
207         if (options.usePowerOf2Sizes != undefined) {
208             print(
209                 "WARNING: The 'usePowerOf2Sizes' flag is ignored in 3.0 and higher as all MMAPv1 " +
210                 "collections use fixed allocation sizes unless the 'noPadding' flag is specified");
211 
212             sendFlags = true;
213             if (options.usePowerOf2Sizes) {
214                 flags |= 1;  // Flag_UsePowerOf2Sizes
215             }
216             delete options.usePowerOf2Sizes;
217         }
218         if (options.noPadding != undefined) {
219             sendFlags = true;
220             if (options.noPadding) {
221                 flags |= 2;  // Flag_NoPadding
222             }
223             delete options.noPadding;
224         }
225 
226         // New flags must be added above here.
227         if (sendFlags) {
228             if (options.flags != undefined)
229                 throw Error("Can't set 'flags' with either 'usePowerOf2Sizes' or 'noPadding'");
230             options.flags = flags;
231         }
232 
233         var cmd = {create: name};
234         Object.extend(cmd, options);
235 
236         return this._dbCommand(cmd);
237     };
238 
239     /**
240      * Command to create a view based on the specified aggregation pipeline.
241      * Usage: db.createView(name, viewOn, pipeline: [{ $operator: {...}}, ... ])
242      *
243      *  @param name String - name of the new view to create
244      *  @param viewOn String - name of the backing view or collection
245      *  @param pipeline [{ $operator: {...}}, ... ] - the aggregation pipeline that defines the view
246      */
247     DB.prototype.createView = function(name, viewOn, pipeline, opt) {
248         var options = opt || {};
249 
250         var cmd = {create: name};
251 
252         if (viewOn == undefined) {
253             throw Error("Must specify a backing view or collection");
254         }
255 
256         // Since we allow a single stage pipeline to be specified as an object
257         // in aggregation, we need to account for that here for consistency.
258         if (pipeline != undefined) {
259             if (!Array.isArray(pipeline)) {
260                 pipeline = [pipeline];
261             }
262         }
263         options.pipeline = pipeline;
264         options.viewOn = viewOn;
265 
266         Object.extend(cmd, options);
267 
268         return this._dbCommand(cmd);
269     };
270 
271     /**
272      * @deprecated use getProfilingStatus
273      *  Returns the current profiling level of this database
274      *  @return SOMETHING_FIXME or null on error
275      */
276     DB.prototype.getProfilingLevel = function() {
277         var res = assert.commandWorked(this._dbCommand({profile: -1}));
278         return res ? res.was : null;
279     };
280 
281     /**
282      *  @return the current profiling status
283      *  example { was : 0, slowms : 100 }
284      *  @return SOMETHING_FIXME or null on error
285      */
286     DB.prototype.getProfilingStatus = function() {
287         var res = this._dbCommand({profile: -1});
288         if (!res.ok)
289             throw _getErrorWithCode(res, "profile command failed: " + tojson(res));
290         delete res.ok;
291         return res;
292     };
293 
294     /**
295       Erase the entire database.  (!)
296 
297      * @return Object returned has member ok set to true if operation succeeds, false otherwise.
298      */
299     DB.prototype.dropDatabase = function() {
300         if (arguments.length)
301             throw Error("dropDatabase doesn't take arguments");
302         return this._dbCommand({dropDatabase: 1});
303     };
304 
305     /**
306      * Shuts down the database.  Must be run while using the admin database.
307      * @param opts Options for shutdown. Possible options are:
308      *   - force: (boolean) if the server should shut down, even if there is no
309      *     up-to-date slave
310      *   - timeoutSecs: (number) the server will continue checking over timeoutSecs
311      *     if any other servers have caught up enough for it to shut down.
312      */
313     DB.prototype.shutdownServer = function(opts) {
314         if ("admin" != this._name) {
315             return "shutdown command only works with the admin database; try 'use admin'";
316         }
317 
318         var cmd = {'shutdown': 1};
319         opts = opts || {};
320         for (var o in opts) {
321             cmd[o] = opts[o];
322         }
323 
324         try {
325             var res = this.runCommand(cmd);
326             if (!res.ok) {
327                 throw _getErrorWithCode(res, 'shutdownServer failed: ' + tojson(res));
328             }
329             throw Error('shutdownServer failed: server is still up.');
330         } catch (e) {
331             // we expect the command to not return a response, as the server will shut down
332             // immediately.
333             if (e.message.indexOf("error doing query: failed") >= 0) {
334                 print('server should be down...');
335                 return;
336             }
337             throw e;
338         }
339     };
340 
341     /**
342       Clone database on another server to here.
343       <p>
344       Generally, you should dropDatabase() first as otherwise the cloned information will MERGE
345       into whatever data is already present in this database.  (That is however a valid way to use
346       clone if you are trying to do something intentionally, such as union three non-overlapping
347       databases into one.)
348       <p>
349       This is a low level administrative function will is not typically used.
350 
351      * @param {String} from Where to clone from (dbhostname[:port]).  May not be this database
352                        (self) as you cannot clone to yourself.
353      * @return Object returned has member ok set to true if operation succeeds, false otherwise.
354      * See also: db.copyDatabase()
355      */
356     DB.prototype.cloneDatabase = function(from) {
357         assert(isString(from) && from.length);
358         return this._dbCommand({clone: from});
359     };
360 
361     /**
362      Clone collection on another server to here.
363      <p>
364      Generally, you should drop() first as otherwise the cloned information will MERGE
365      into whatever data is already present in this collection.  (That is however a valid way to use
366      clone if you are trying to do something intentionally, such as union three non-overlapping
367      collections into one.)
368      <p>
369      This is a low level administrative function is not typically used.
370 
371      * @param {String} from mongod instance from which to clnoe (dbhostname:port).  May
372      not be this mongod instance, as clone from self is not allowed.
373      * @param {String} collection name of collection to clone.
374      * @param {Object} query query specifying which elements of collection are to be cloned.
375      * @return Object returned has member ok set to true if operation succeeds, false otherwise.
376      * See also: db.cloneDatabase()
377      */
378     DB.prototype.cloneCollection = function(from, collection, query) {
379         assert(isString(from) && from.length);
380         assert(isString(collection) && collection.length);
381         collection = this._name + "." + collection;
382         query = query || {};
383         return this._dbCommand({cloneCollection: collection, from: from, query: query});
384     };
385 
386     /**
387       Copy database from one server or name to another server or name.
388 
389       Generally, you should dropDatabase() first as otherwise the copied information will MERGE
390       into whatever data is already present in this database (and you will get duplicate objects
391       in collections potentially.)
392 
393       For security reasons this function only works when executed on the "admin" db.  However,
394       if you have access to said db, you can copy any database from one place to another.
395 
396       This method provides a way to "rename" a database by copying it to a new db name and
397       location.  Additionally, it effectively provides a repair facility.
398 
399       * @param {String} fromdb database name from which to copy.
400       * @param {String} todb database name to copy to.
401       * @param {String} fromhost hostname of the database (and optionally, ":port") from which to
402                         copy the data.  default if unspecified is to copy from self.
403       * @return Object returned has member ok set to true if operation succeeds, false otherwise.
404       * See also: db.clone()
405     */
406     DB.prototype.copyDatabase = function(fromdb, todb, fromhost, username, password, mechanism) {
407         assert(isString(fromdb) && fromdb.length);
408         assert(isString(todb) && todb.length);
409         fromhost = fromhost || "";
410 
411         if (!mechanism) {
412             mechanism = this._getDefaultAuthenticationMechanism();
413         }
414         assert(mechanism == "SCRAM-SHA-1" || mechanism == "MONGODB-CR");
415 
416         // Check for no auth or copying from localhost
417         if (!username || !password || fromhost == "") {
418             return this._adminCommand({copydb: 1, fromhost: fromhost, fromdb: fromdb, todb: todb});
419         }
420 
421         // Use the copyDatabase native helper for SCRAM-SHA-1
422         if (mechanism == "SCRAM-SHA-1") {
423             return this.getMongo().copyDatabaseWithSCRAM(
424                 fromdb, todb, fromhost, username, password);
425         }
426 
427         // Fall back to MONGODB-CR
428         var n = this._adminCommand({copydbgetnonce: 1, fromhost: fromhost});
429         return this._adminCommand({
430             copydb: 1,
431             fromhost: fromhost,
432             fromdb: fromdb,
433             todb: todb,
434             username: username,
435             nonce: n.nonce,
436             key: this.__pwHash(n.nonce, username, password)
437         });
438     };
439 
440     /**
441       Repair database.
442 
443      * @return Object returned has member ok set to true if operation succeeds, false otherwise.
444     */
445     DB.prototype.repairDatabase = function() {
446         return this._dbCommand({repairDatabase: 1});
447     };
448 
449     DB.prototype.help = function() {
450         print("DB methods:");
451         print(
452             "\tdb.adminCommand(nameOrDocument) - switches to 'admin' db, and runs command [ just calls db.runCommand(...) ]");
453         print("\tdb.auth(username, password)");
454         print("\tdb.cloneDatabase(fromhost)");
455         print("\tdb.commandHelp(name) returns the help for the command");
456         print("\tdb.copyDatabase(fromdb, todb, fromhost)");
457         print("\tdb.createCollection(name, { size : ..., capped : ..., max : ... } )");
458         print("\tdb.createView(name, viewOn : ..., pipeline : [ { $operator: {...}}, ... ] )");
459         print("\tdb.createUser(userDocument)");
460         print("\tdb.currentOp() displays currently executing operations in the db");
461         print("\tdb.dropDatabase()");
462         print("\tdb.eval() - deprecated");
463         print("\tdb.fsyncLock() flush data to disk and lock server for backups");
464         print("\tdb.fsyncUnlock() unlocks server following a db.fsyncLock()");
465         print("\tdb.getCollection(cname) same as db['cname'] or db.cname");
466         print(
467             "\tdb.getCollectionInfos([filter]) - returns a list that contains the names and options" +
468             " of the db's collections");
469         print("\tdb.getCollectionNames()");
470         print("\tdb.getLastError() - just returns the err msg string");
471         print("\tdb.getLastErrorObj() - return full status object");
472         print("\tdb.getLogComponents()");
473         print("\tdb.getMongo() get the server connection object");
474         print("\tdb.getMongo().setSlaveOk() allow queries on a replication slave server");
475         print("\tdb.getName()");
476         print("\tdb.getPrevError()");
477         print("\tdb.getProfilingLevel() - deprecated");
478         print("\tdb.getProfilingStatus() - returns if profiling is on and slow threshold");
479         print("\tdb.getReplicationInfo()");
480         print("\tdb.getSiblingDB(name) get the db at the same server as this one");
481         print(
482             "\tdb.getWriteConcern() - returns the write concern used for any operations on this db, inherited from server object if set");
483         print("\tdb.hostInfo() get details about the server's host");
484         print("\tdb.isMaster() check replica primary status");
485         print("\tdb.killOp(opid) kills the current operation in the db");
486         print("\tdb.listCommands() lists all the db commands");
487         print("\tdb.loadServerScripts() loads all the scripts in db.system.js");
488         print("\tdb.logout()");
489         print("\tdb.printCollectionStats()");
490         print("\tdb.printReplicationInfo()");
491         print("\tdb.printShardingStatus()");
492         print("\tdb.printSlaveReplicationInfo()");
493         print("\tdb.dropUser(username)");
494         print("\tdb.repairDatabase()");
495         print("\tdb.resetError()");
496         print(
497             "\tdb.runCommand(cmdObj) run a database command.  if cmdObj is a string, turns it into { cmdObj : 1 }");
498         print("\tdb.serverStatus()");
499         print("\tdb.setLogLevel(level,<component>)");
500         print("\tdb.setProfilingLevel(level,<slowms>) 0=off 1=slow 2=all");
501         print(
502             "\tdb.setWriteConcern( <write concern doc> ) - sets the write concern for writes to the db");
503         print(
504             "\tdb.unsetWriteConcern( <write concern doc> ) - unsets the write concern for writes to the db");
505         print("\tdb.setVerboseShell(flag) display extra information in shell output");
506         print("\tdb.shutdownServer()");
507         print("\tdb.stats()");
508         print("\tdb.version() current version of the server");
509 
510         return __magicNoPrint;
511     };
512 
513     DB.prototype.printCollectionStats = function(scale) {
514         if (arguments.length > 1) {
515             print("printCollectionStats() has a single optional argument (scale)");
516             return;
517         }
518         if (typeof scale != 'undefined') {
519             if (typeof scale != 'number') {
520                 print("scale has to be a number >= 1");
521                 return;
522             }
523             if (scale < 1) {
524                 print("scale has to be >= 1");
525                 return;
526             }
527         }
528         var mydb = this;
529         this.getCollectionNames().forEach(function(z) {
530             print(z);
531             printjson(mydb.getCollection(z).stats(scale));
532             print("---");
533         });
534     };
535 
536     /**
537      * <p> Set profiling level for your db.  Profiling gathers stats on query performance. </p>
538      *
539      * <p>Default is off, and resets to off on a database restart -- so if you want it on,
540      *    turn it on periodically. </p>
541      *
542      *  <p>Levels :</p>
543      *   <ul>
544      *    <li>0=off</li>
545      *    <li>1=log very slow operations; optional argument slowms specifies slowness threshold</li>
546      *    <li>2=log all</li>
547      *  @param {String} level Desired level of profiling
548      *  @param {String} slowms For slow logging, query duration that counts as slow (default 100ms)
549      *  @return SOMETHING_FIXME or null on error
550      */
551     DB.prototype.setProfilingLevel = function(level, slowms) {
552 
553         if (level < 0 || level > 2) {
554             var errorText = "input level " + level + " is out of range [0..2]";
555             var errorObject = new Error(errorText);
556             errorObject['dbSetProfilingException'] = errorText;
557             throw errorObject;
558         }
559 
560         var cmd = {profile: level};
561         if (isNumber(slowms))
562             cmd["slowms"] = slowms;
563         return assert.commandWorked(this._dbCommand(cmd));
564     };
565 
566     /**
567      * @deprecated
568      *  <p> Evaluate a js expression at the database server.</p>
569      *
570      * <p>Useful if you need to touch a lot of data lightly; in such a scenario
571      *  the network transfer of the data could be a bottleneck.  A good example
572      *  is "select count(*)" -- can be done server side via this mechanism.
573      * </p>
574      *
575      * <p>
576      * If the eval fails, an exception is thrown of the form:
577      * </p>
578      * <code>{ dbEvalException: { retval: functionReturnValue, ok: num [, errno: num] [, errmsg:
579      *str] } }</code>
580      *
581      * <p>Example: </p>
582      * <code>print( "mycount: " + db.eval( function(){db.mycoll.find({},{_id:ObjId()}).length();}
583      *);</code>
584      *
585      * @param {Function} jsfunction Javascript function to run on server.  Note this it not a
586      *closure, but rather just "code".
587      * @return result of your function, or null if error
588      *
589      */
590     DB.prototype.eval = function(jsfunction) {
591         print("WARNING: db.eval is deprecated");
592 
593         var cmd = {$eval: jsfunction};
594         if (arguments.length > 1) {
595             cmd.args = Array.from(arguments).slice(1);
596         }
597 
598         var res = this._dbCommand(cmd);
599 
600         if (!res.ok)
601             throw _getErrorWithCode(res, tojson(res));
602 
603         return res.retval;
604     };
605 
606     DB.prototype.dbEval = DB.prototype.eval;
607 
608     /**
609      *
610      *  <p>
611      *   Similar to SQL group by.  For example: </p>
612      *
613      *  <code>select a,b,sum(c) csum from coll where active=1 group by a,b</code>
614      *
615      *  <p>
616      *    corresponds to the following in 10gen:
617      *  </p>
618      *
619      *  <code>
620         db.group(
621             {
622                 ns: "coll",
623                 key: { a:true, b:true },
624                 // keyf: ...,
625                 cond: { active:1 },
626                 reduce: function(obj,prev) { prev.csum += obj.c; },
627                 initial: { csum: 0 }
628             });
629         </code>
630      *
631      *
632      * <p>
633      *  An array of grouped items is returned.  The array must fit in RAM, thus this function is not
634      * suitable when the return set is extremely large.
635      * </p>
636      * <p>
637      * To order the grouped data, simply sort it client side upon return.
638      * <p>
639        Defaults
640          cond may be null if you want to run against all rows in the collection
641          keyf is a function which takes an object and returns the desired key.  set either key or
642      keyf (not both).
643      * </p>
644      */
645     DB.prototype.groupeval = function(parmsObj) {
646 
647         var groupFunction = function() {
648             var parms = args[0];
649             var c = db[parms.ns].find(parms.cond || {});
650             var map = new Map();
651             var pks = parms.key ? Object.keySet(parms.key) : null;
652             var pkl = pks ? pks.length : 0;
653             var key = {};
654 
655             while (c.hasNext()) {
656                 var obj = c.next();
657                 if (pks) {
658                     for (var i = 0; i < pkl; i++) {
659                         var k = pks[i];
660                         key[k] = obj[k];
661                     }
662                 } else {
663                     key = parms.$keyf(obj);
664                 }
665 
666                 var aggObj = map.get(key);
667                 if (aggObj == null) {
668                     var newObj = Object.extend({}, key);  // clone
669                     aggObj = Object.extend(newObj, parms.initial);
670                     map.put(key, aggObj);
671                 }
672                 parms.$reduce(obj, aggObj);
673             }
674 
675             return map.values();
676         };
677 
678         return this.eval(groupFunction, this._groupFixParms(parmsObj));
679     };
680 
681     DB.prototype.groupcmd = function(parmsObj) {
682         var ret = this.runCommand({"group": this._groupFixParms(parmsObj)});
683         if (!ret.ok) {
684             throw _getErrorWithCode(ret, "group command failed: " + tojson(ret));
685         }
686         return ret.retval;
687     };
688 
689     DB.prototype.group = DB.prototype.groupcmd;
690 
691     DB.prototype._groupFixParms = function(parmsObj) {
692         var parms = Object.extend({}, parmsObj);
693 
694         if (parms.reduce) {
695             parms.$reduce = parms.reduce;  // must have $ to pass to db
696             delete parms.reduce;
697         }
698 
699         if (parms.keyf) {
700             parms.$keyf = parms.keyf;
701             delete parms.keyf;
702         }
703 
704         return parms;
705     };
706 
707     DB.prototype.resetError = function() {
708         return this.runCommand({reseterror: 1});
709     };
710 
711     DB.prototype.forceError = function() {
712         return this.runCommand({forceerror: 1});
713     };
714 
715     DB.prototype.getLastError = function(w, wtimeout) {
716         var res = this.getLastErrorObj(w, wtimeout);
717         if (!res.ok)
718             throw _getErrorWithCode(ret, "getlasterror failed: " + tojson(res));
719         return res.err;
720     };
721     DB.prototype.getLastErrorObj = function(w, wtimeout) {
722         var cmd = {getlasterror: 1};
723         if (w) {
724             cmd.w = w;
725             if (wtimeout)
726                 cmd.wtimeout = wtimeout;
727         }
728         var res = this.runCommand(cmd);
729 
730         if (!res.ok)
731             throw _getErrorWithCode(res, "getlasterror failed: " + tojson(res));
732         return res;
733     };
734     DB.prototype.getLastErrorCmd = DB.prototype.getLastErrorObj;
735 
736     /* Return the last error which has occurred, even if not the very last error.
737 
738        Returns:
739         { err : <error message>, nPrev : <how_many_ops_back_occurred>, ok : 1 }
740 
741        result.err will be null if no error has occurred.
742      */
743     DB.prototype.getPrevError = function() {
744         return this.runCommand({getpreverror: 1});
745     };
746 
747     DB.prototype._getCollectionInfosSystemNamespaces = function(filter) {
748         var all = [];
749 
750         var dbNamePrefix = this._name + ".";
751 
752         // Create a shallow copy of 'filter' in case we modify its 'name' property. Also defaults
753         // 'filter' to {} if the parameter was not specified.
754         filter = Object.extend({}, filter);
755         if (typeof filter.name === "string") {
756             // Queries on the 'name' field need to qualify the namespace with the database name for
757             // consistency with the command variant.
758             filter.name = dbNamePrefix + filter.name;
759         }
760 
761         var c = this.getCollection("system.namespaces").find(filter);
762         while (c.hasNext()) {
763             var infoObj = c.next();
764 
765             if (infoObj.name.indexOf("$") >= 0 && infoObj.name.indexOf(".oplog.$") < 0)
766                 continue;
767 
768             // Remove the database name prefix from the collection info object.
769             infoObj.name = infoObj.name.substring(dbNamePrefix.length);
770 
771             all.push(infoObj);
772         }
773 
774         // Return list of objects sorted by collection name.
775         return all.sort(function(coll1, coll2) {
776             return coll1.name.localeCompare(coll2.name);
777         });
778     };
779 
780     DB.prototype._getCollectionInfosCommand = function(filter) {
781         filter = filter || {};
782         var res = this.runCommand({listCollections: 1, filter: filter});
783         if (res.code == 59) {
784             // command doesn't exist, old mongod
785             return null;
786         }
787 
788         if (!res.ok) {
789             if (res.errmsg && res.errmsg.startsWith("no such cmd")) {
790                 return null;
791             }
792 
793             throw _getErrorWithCode(res, "listCollections failed: " + tojson(res));
794         }
795 
796         return new DBCommandCursor(this._mongo, res).toArray().sort(compareOn("name"));
797     };
798 
799     /**
800      * Returns a list that contains the names and options of this database's collections, sorted by
801      * collection name. An optional filter can be specified to match only collections with certain
802      * metadata.
803      */
804     DB.prototype.getCollectionInfos = function(filter) {
805         var res = this._getCollectionInfosCommand(filter);
806         if (res) {
807             return res;
808         }
809         return this._getCollectionInfosSystemNamespaces(filter);
810     };
811 
812     /**
813      * Returns this database's list of collection names in sorted order.
814      */
815     DB.prototype.getCollectionNames = function() {
816         return this.getCollectionInfos().map(function(infoObj) {
817             return infoObj.name;
818         });
819     };
820 
821     DB.prototype.tojson = function() {
822         return this._name;
823     };
824 
825     DB.prototype.toString = function() {
826         return this._name;
827     };
828 
829     DB.prototype.isMaster = function() {
830         return this.runCommand("isMaster");
831     };
832 
833     var commandUnsupported = function(res) {
834         return (!res.ok &&
835                 (res.errmsg.startsWith("no such cmd") || res.errmsg.startsWith("no such command") ||
836                  res.code === 59 /* CommandNotFound */));
837     };
838 
839     DB.prototype.currentOp = function(arg) {
840         var q = {};
841         if (arg) {
842             if (typeof(arg) == "object")
843                 Object.extend(q, arg);
844             else if (arg)
845                 q["$all"] = true;
846         }
847 
848         var commandObj = {"currentOp": 1};
849         Object.extend(commandObj, q);
850         var res = this.adminCommand(commandObj);
851         if (commandUnsupported(res)) {
852             // always send legacy currentOp with default (null) read preference (SERVER-17951)
853             var _readPref = this.getMongo().getReadPrefMode();
854             try {
855                 this.getMongo().setReadPref(null);
856                 res = this.getSiblingDB("admin").$cmd.sys.inprog.findOne(q);
857             } finally {
858                 this.getMongo().setReadPref(_readPref);
859             }
860         }
861         return res;
862     };
863     DB.prototype.currentOP = DB.prototype.currentOp;
864 
865     DB.prototype.killOp = function(op) {
866         if (!op)
867             throw Error("no opNum to kill specified");
868         var res = this.adminCommand({'killOp': 1, 'op': op});
869         if (commandUnsupported(res)) {
870             // fall back for old servers
871             var _readPref = this.getMongo().getReadPrefMode();
872             try {
873                 this.getMongo().setReadPref(null);
874                 res = this.getSiblingDB("admin").$cmd.sys.killop.findOne({'op': op});
875             } finally {
876                 this.getMongo().setReadPref(_readPref);
877             }
878         }
879         return res;
880     };
881     DB.prototype.killOP = DB.prototype.killOp;
882 
883     DB.tsToSeconds = function(x) {
884         if (x.t && x.i)
885             return x.t;
886         return x / 4294967296;  // low 32 bits are ordinal #s within a second
887     };
888 
889     /**
890       Get a replication log information summary.
891       <p>
892       This command is for the database/cloud administer and not applicable to most databases.
893       It is only used with the local database.  One might invoke from the JS shell:
894       <pre>
895            use local
896            db.getReplicationInfo();
897       </pre>
898       It is assumed that this database is a replication master -- the information returned is
899       about the operation log stored at local.oplog.$main on the replication master.  (It also
900       works on a machine in a replica pair: for replica pairs, both machines are "masters" from
901       an internal database perspective.
902       <p>
903       * @return Object timeSpan: time span of the oplog from start to end  if slave is more out
904       *                          of date than that, it can't recover without a complete resync
905     */
906     DB.prototype.getReplicationInfo = function() {
907         var localdb = this.getSiblingDB("local");
908 
909         var result = {};
910         var oplog;
911         var localCollections = localdb.getCollectionNames();
912         if (localCollections.indexOf('oplog.rs') >= 0) {
913             oplog = 'oplog.rs';
914         } else if (localCollections.indexOf('oplog.$main') >= 0) {
915             oplog = 'oplog.$main';
916         } else {
917             result.errmsg = "neither master/slave nor replica set replication detected";
918             return result;
919         }
920 
921         var ol = localdb.getCollection(oplog);
922         var ol_stats = ol.stats();
923         if (ol_stats && ol_stats.maxSize) {
924             result.logSizeMB = ol_stats.maxSize / (1024 * 1024);
925         } else {
926             result.errmsg = "Could not get stats for local." + oplog + " collection. " +
927                 "collstats returned: " + tojson(ol_stats);
928             return result;
929         }
930 
931         result.usedMB = ol_stats.size / (1024 * 1024);
932         result.usedMB = Math.ceil(result.usedMB * 100) / 100;
933 
934         var firstc = ol.find().sort({$natural: 1}).limit(1);
935         var lastc = ol.find().sort({$natural: -1}).limit(1);
936         if (!firstc.hasNext() || !lastc.hasNext()) {
937             result.errmsg =
938                 "objects not found in local.oplog.$main -- is this a new and empty db instance?";
939             result.oplogMainRowCount = ol.count();
940             return result;
941         }
942 
943         var first = firstc.next();
944         var last = lastc.next();
945         var tfirst = first.ts;
946         var tlast = last.ts;
947 
948         if (tfirst && tlast) {
949             tfirst = DB.tsToSeconds(tfirst);
950             tlast = DB.tsToSeconds(tlast);
951             result.timeDiff = tlast - tfirst;
952             result.timeDiffHours = Math.round(result.timeDiff / 36) / 100;
953             result.tFirst = (new Date(tfirst * 1000)).toString();
954             result.tLast = (new Date(tlast * 1000)).toString();
955             result.now = Date();
956         } else {
957             result.errmsg = "ts element not found in oplog objects";
958         }
959 
960         return result;
961     };
962 
963     DB.prototype.printReplicationInfo = function() {
964         var result = this.getReplicationInfo();
965         if (result.errmsg) {
966             var isMaster = this.isMaster();
967             if (isMaster.arbiterOnly) {
968                 print("cannot provide replication status from an arbiter.");
969                 return;
970             } else if (!isMaster.ismaster) {
971                 print("this is a slave, printing slave replication info.");
972                 this.printSlaveReplicationInfo();
973                 return;
974             }
975             print(tojson(result));
976             return;
977         }
978         print("configured oplog size:   " + result.logSizeMB + "MB");
979         print("log length start to end: " + result.timeDiff + "secs (" + result.timeDiffHours +
980               "hrs)");
981         print("oplog first event time:  " + result.tFirst);
982         print("oplog last event time:   " + result.tLast);
983         print("now:                     " + result.now);
984     };
985 
986     DB.prototype.printSlaveReplicationInfo = function() {
987         var startOptimeDate = null;
988         var primary = null;
989 
990         function getReplLag(st) {
991             assert(startOptimeDate, "how could this be null (getReplLag startOptimeDate)");
992             print("\tsyncedTo: " + st.toString());
993             var ago = (startOptimeDate - st) / 1000;
994             var hrs = Math.round(ago / 36) / 100;
995             var suffix = "";
996             if (primary) {
997                 suffix = "primary ";
998             } else {
999                 suffix = "freshest member (no primary available at the moment)";
1000             }
1001             print("\t" + Math.round(ago) + " secs (" + hrs + " hrs) behind the " + suffix);
1002         }
1003 
1004         function getMaster(members) {
1005             for (i in members) {
1006                 var row = members[i];
1007                 if (row.state === 1) {
1008                     return row;
1009                 }
1010             }
1011 
1012             return null;
1013         }
1014 
1015         function g(x) {
1016             assert(x, "how could this be null (printSlaveReplicationInfo gx)");
1017             print("source: " + x.host);
1018             if (x.syncedTo) {
1019                 var st = new Date(DB.tsToSeconds(x.syncedTo) * 1000);
1020                 getReplLag(st);
1021             } else {
1022                 print("\tdoing initial sync");
1023             }
1024         }
1025 
1026         function r(x) {
1027             assert(x, "how could this be null (printSlaveReplicationInfo rx)");
1028             if (x.state == 1 || x.state == 7) {  // ignore primaries (1) and arbiters (7)
1029                 return;
1030             }
1031 
1032             print("source: " + x.name);
1033             if (x.optime) {
1034                 getReplLag(x.optimeDate);
1035             } else {
1036                 print("\tno replication info, yet.  State: " + x.stateStr);
1037             }
1038         }
1039 
1040         var L = this.getSiblingDB("local");
1041 
1042         if (L.system.replset.count() != 0) {
1043             var status = this.adminCommand({'replSetGetStatus': 1});
1044             primary = getMaster(status.members);
1045             if (primary) {
1046                 startOptimeDate = primary.optimeDate;
1047             }
1048             // no primary, find the most recent op among all members
1049             else {
1050                 startOptimeDate = new Date(0, 0);
1051                 for (i in status.members) {
1052                     if (status.members[i].optimeDate > startOptimeDate) {
1053                         startOptimeDate = status.members[i].optimeDate;
1054                     }
1055                 }
1056             }
1057 
1058             for (i in status.members) {
1059                 r(status.members[i]);
1060             }
1061         } else if (L.sources.count() != 0) {
1062             startOptimeDate = new Date();
1063             L.sources.find().forEach(g);
1064         } else {
1065             print("local.sources is empty; is this db a --slave?");
1066             return;
1067         }
1068     };
1069 
1070     DB.prototype.serverBuildInfo = function() {
1071         return this._adminCommand("buildinfo");
1072     };
1073 
1074     // Used to trim entries from the metrics.commands that have never been executed
1075     getActiveCommands = function(tree) {
1076         var result = {};
1077         for (var i in tree) {
1078             if (!tree.hasOwnProperty(i))
1079                 continue;
1080             if (tree[i].hasOwnProperty("total")) {
1081                 if (tree[i].total > 0) {
1082                     result[i] = tree[i];
1083                 }
1084                 continue;
1085             }
1086             if (i == "<UNKNOWN>") {
1087                 if (tree[i] > 0) {
1088                     result[i] = tree[i];
1089                 }
1090                 continue;
1091             }
1092             // Handles nested commands
1093             var subStatus = getActiveCommands(tree[i]);
1094             if (Object.keys(subStatus).length > 0) {
1095                 result[i] = tree[i];
1096             }
1097         }
1098         return result;
1099     };
1100 
1101     DB.prototype.serverStatus = function(options) {
1102         var cmd = {serverStatus: 1};
1103         if (options) {
1104             Object.extend(cmd, options);
1105         }
1106         var res = this._adminCommand(cmd);
1107         // Only prune if we have a metrics tree with commands.
1108         if (res.metrics && res.metrics.commands) {
1109             res.metrics.commands = getActiveCommands(res.metrics.commands);
1110         }
1111         return res;
1112     };
1113 
1114     DB.prototype.hostInfo = function() {
1115         return this._adminCommand("hostInfo");
1116     };
1117 
1118     DB.prototype.serverCmdLineOpts = function() {
1119         return this._adminCommand("getCmdLineOpts");
1120     };
1121 
1122     DB.prototype.version = function() {
1123         return this.serverBuildInfo().version;
1124     };
1125 
1126     DB.prototype.serverBits = function() {
1127         return this.serverBuildInfo().bits;
1128     };
1129 
1130     DB.prototype.listCommands = function() {
1131         var x = this.runCommand("listCommands");
1132         for (var name in x.commands) {
1133             var c = x.commands[name];
1134 
1135             var s = name + ": ";
1136 
1137             if (c.adminOnly)
1138                 s += " adminOnly ";
1139             if (c.slaveOk)
1140                 s += " slaveOk ";
1141 
1142             s += "\n  ";
1143             s += c.help.replace(/\n/g, '\n  ');
1144             s += "\n";
1145 
1146             print(s);
1147         }
1148     };
1149 
1150     DB.prototype.printShardingStatus = function(verbose) {
1151         printShardingStatus(this.getSiblingDB("config"), verbose);
1152     };
1153 
1154     DB.prototype.fsyncLock = function() {
1155         return this.adminCommand({fsync: 1, lock: true});
1156     };
1157 
1158     DB.prototype.fsyncUnlock = function() {
1159         var res = this.adminCommand({fsyncUnlock: 1});
1160         if (commandUnsupported(res)) {
1161             var _readPref = this.getMongo().getReadPrefMode();
1162             try {
1163                 this.getMongo().setReadPref(null);
1164                 res = this.getSiblingDB("admin").$cmd.sys.unlock.findOne();
1165             } finally {
1166                 this.getMongo().setReadPref(_readPref);
1167             }
1168         }
1169         return res;
1170     };
1171 
1172     DB.autocomplete = function(obj) {
1173         var colls = obj.getCollectionNames();
1174         var ret = [];
1175         for (var i = 0; i < colls.length; i++) {
1176             if (colls[i].match(/^[a-zA-Z0-9_.\$]+$/))
1177                 ret.push(colls[i]);
1178         }
1179         return ret;
1180     };
1181 
1182     DB.prototype.setSlaveOk = function(value) {
1183         if (value == undefined)
1184             value = true;
1185         this._slaveOk = value;
1186     };
1187 
1188     DB.prototype.getSlaveOk = function() {
1189         if (this._slaveOk != undefined)
1190             return this._slaveOk;
1191         return this._mongo.getSlaveOk();
1192     };
1193 
1194     DB.prototype.getQueryOptions = function() {
1195         var options = 0;
1196         if (this.getSlaveOk())
1197             options |= 4;
1198         return options;
1199     };
1200 
1201     /* Loads any scripts contained in system.js into the client shell.
1202     */
1203     DB.prototype.loadServerScripts = function() {
1204         var global = Function('return this')();
1205         this.system.js.find().forEach(function(u) {
1206             if (u.value.constructor === Code) {
1207                 global[u._id] = eval("(" + u.value.code + ")");
1208             } else {
1209                 global[u._id] = u.value;
1210             }
1211         });
1212     };
1213 
1214     ////////////////////////////////////////////////////////////////////////////////////////////////////
1215     //////////////////////////// Security shell helpers below
1216     /////////////////////////////////////////////
1217     ////////////////////////////////////////////////////////////////////////////////////////////////////
1218 
1219     var _defaultWriteConcern = {w: 'majority', wtimeout: 60 * 1000};
1220 
1221     function getUserObjString(userObj) {
1222         var pwd = userObj.pwd;
1223         delete userObj.pwd;
1224         var toreturn = tojson(userObj);
1225         userObj.pwd = pwd;
1226         return toreturn;
1227     }
1228 
1229     DB.prototype._modifyCommandToDigestPasswordIfNecessary = function(cmdObj, username) {
1230         if (!cmdObj["pwd"]) {
1231             return;
1232         }
1233         if (cmdObj.hasOwnProperty("digestPassword")) {
1234             throw Error(
1235                 "Cannot specify 'digestPassword' through the user management shell helpers, " +
1236                 "use 'passwordDigestor' instead");
1237         }
1238         var passwordDigestor = cmdObj["passwordDigestor"] ? cmdObj["passwordDigestor"] : "client";
1239         if (passwordDigestor == "server") {
1240             cmdObj["digestPassword"] = true;
1241         } else if (passwordDigestor == "client") {
1242             cmdObj["pwd"] = _hashPassword(username, cmdObj["pwd"]);
1243             cmdObj["digestPassword"] = false;
1244         } else {
1245             throw Error("'passwordDigestor' must be either 'server' or 'client', got: '" +
1246                         passwordDigestor + "'");
1247         }
1248         delete cmdObj["passwordDigestor"];
1249     };
1250 
1251     DB.prototype.createUser = function(userObj, writeConcern) {
1252         var name = userObj["user"];
1253         var cmdObj = {createUser: name};
1254         cmdObj = Object.extend(cmdObj, userObj);
1255         delete cmdObj["user"];
1256 
1257         this._modifyCommandToDigestPasswordIfNecessary(cmdObj, name);
1258 
1259         cmdObj["writeConcern"] = writeConcern ? writeConcern : _defaultWriteConcern;
1260 
1261         var res = this.runCommand(cmdObj);
1262 
1263         if (res.ok) {
1264             print("Successfully added user: " + getUserObjString(userObj));
1265             return;
1266         }
1267 
1268         if (res.errmsg == "no such cmd: createUser") {
1269             throw Error("'createUser' command not found.  This is most likely because you are " +
1270                         "talking to an old (pre v2.6) MongoDB server");
1271         }
1272 
1273         if (res.errmsg == "timeout") {
1274             throw Error("timed out while waiting for user authentication to replicate - " +
1275                         "database will not be fully secured until replication finishes");
1276         }
1277 
1278         throw _getErrorWithCode(res, "couldn't add user: " + res.errmsg);
1279     };
1280 
1281     function _hashPassword(username, password) {
1282         if (typeof password != 'string') {
1283             throw Error("User passwords must be of type string. Was given password with type: " +
1284                         typeof(password));
1285         }
1286         return hex_md5(username + ":mongo:" + password);
1287     }
1288 
1289     /**
1290      * Used for updating users in systems with V1 style user information
1291      * (ie MongoDB v2.4 and prior)
1292      */
1293     DB.prototype._updateUserV1 = function(name, updateObject, writeConcern) {
1294         var setObj = {};
1295         if (updateObject.pwd) {
1296             setObj["pwd"] = _hashPassword(name, updateObject.pwd);
1297         }
1298         if (updateObject.extraData) {
1299             setObj["extraData"] = updateObject.extraData;
1300         }
1301         if (updateObject.roles) {
1302             setObj["roles"] = updateObject.roles;
1303         }
1304 
1305         this.system.users.update({user: name, userSource: null}, {$set: setObj});
1306         var errObj = this.getLastErrorObj(writeConcern['w'], writeConcern['wtimeout']);
1307         if (errObj.err) {
1308             throw _getErrorWithCode(errObj, "Updating user failed: " + errObj.err);
1309         }
1310     };
1311 
1312     DB.prototype.updateUser = function(name, updateObject, writeConcern) {
1313         var cmdObj = {updateUser: name};
1314         cmdObj = Object.extend(cmdObj, updateObject);
1315         cmdObj['writeConcern'] = writeConcern ? writeConcern : _defaultWriteConcern;
1316         this._modifyCommandToDigestPasswordIfNecessary(cmdObj, name);
1317 
1318         var res = this.runCommand(cmdObj);
1319         if (res.ok) {
1320             return;
1321         }
1322 
1323         if (res.errmsg == "no such cmd: updateUser") {
1324             this._updateUserV1(name, updateObject, cmdObj['writeConcern']);
1325             return;
1326         }
1327 
1328         throw _getErrorWithCode(res, "Updating user failed: " + res.errmsg);
1329     };
1330 
1331     DB.prototype.changeUserPassword = function(username, password, writeConcern) {
1332         this.updateUser(username, {pwd: password}, writeConcern);
1333     };
1334 
1335     DB.prototype.logout = function() {
1336         return this.getMongo().logout(this.getName());
1337     };
1338 
1339     // For backwards compatibility
1340     DB.prototype.removeUser = function(username, writeConcern) {
1341         print("WARNING: db.removeUser has been deprecated, please use db.dropUser instead");
1342         return this.dropUser(username, writeConcern);
1343     };
1344 
1345     DB.prototype.dropUser = function(username, writeConcern) {
1346         var cmdObj = {
1347             dropUser: username,
1348             writeConcern: writeConcern ? writeConcern : _defaultWriteConcern
1349         };
1350         var res = this.runCommand(cmdObj);
1351 
1352         if (res.ok) {
1353             return true;
1354         }
1355 
1356         if (res.code == 11) {  // Code 11 = UserNotFound
1357             return false;
1358         }
1359 
1360         if (res.errmsg == "no such cmd: dropUsers") {
1361             return this._removeUserV1(username, cmdObj['writeConcern']);
1362         }
1363 
1364         throw _getErrorWithCode(res, res.errmsg);
1365     };
1366 
1367     /**
1368      * Used for removing users in systems with V1 style user information
1369      * (ie MongoDB v2.4 and prior)
1370      */
1371     DB.prototype._removeUserV1 = function(username, writeConcern) {
1372         this.getCollection("system.users").remove({user: username});
1373 
1374         var le = this.getLastErrorObj(writeConcern['w'], writeConcern['wtimeout']);
1375 
1376         if (le.err) {
1377             throw _getErrorWithCode(le, "Couldn't remove user: " + le.err);
1378         }
1379 
1380         if (le.n == 1) {
1381             return true;
1382         } else {
1383             return false;
1384         }
1385     };
1386 
1387     DB.prototype.dropAllUsers = function(writeConcern) {
1388         var res = this.runCommand({
1389             dropAllUsersFromDatabase: 1,
1390             writeConcern: writeConcern ? writeConcern : _defaultWriteConcern
1391         });
1392 
1393         if (!res.ok) {
1394             throw _getErrorWithCode(res, res.errmsg);
1395         }
1396 
1397         return res.n;
1398     };
1399 
1400     DB.prototype.__pwHash = function(nonce, username, pass) {
1401         return hex_md5(nonce + username + _hashPassword(username, pass));
1402     };
1403 
1404     DB.prototype._defaultAuthenticationMechanism = null;
1405 
1406     DB.prototype._getDefaultAuthenticationMechanism = function() {
1407         // Use the default auth mechanism if set on the command line.
1408         if (this._defaultAuthenticationMechanism != null)
1409             return this._defaultAuthenticationMechanism;
1410 
1411         // Use MONGODB-CR for v2.6 and earlier.
1412         maxWireVersion = this.isMaster().maxWireVersion;
1413         if (maxWireVersion == undefined || maxWireVersion < 3) {
1414             return "MONGODB-CR";
1415         }
1416         return "SCRAM-SHA-1";
1417     };
1418 
1419     DB.prototype._defaultGssapiServiceName = null;
1420 
1421     DB.prototype._authOrThrow = function() {
1422         var params;
1423         if (arguments.length == 2) {
1424             params = {user: arguments[0], pwd: arguments[1]};
1425         } else if (arguments.length == 1) {
1426             if (typeof(arguments[0]) != "object")
1427                 throw Error("Single-argument form of auth expects a parameter object");
1428             params = Object.extend({}, arguments[0]);
1429         } else {
1430             throw Error(
1431                 "auth expects either (username, password) or ({ user: username, pwd: password })");
1432         }
1433 
1434         if (params.mechanism === undefined)
1435             params.mechanism = this._getDefaultAuthenticationMechanism();
1436 
1437         if (params.db !== undefined) {
1438             throw Error("Do not override db field on db.auth(). Use getMongo().auth(), instead.");
1439         }
1440 
1441         if (params.mechanism == "GSSAPI" && params.serviceName == null &&
1442             this._defaultGssapiServiceName != null) {
1443             params.serviceName = this._defaultGssapiServiceName;
1444         }
1445 
1446         params.db = this.getName();
1447         var good = this.getMongo().auth(params);
1448         if (good) {
1449             // auth enabled, and should try to use isMaster and replSetGetStatus to build prompt
1450             this.getMongo().authStatus = {
1451                 authRequired: true,
1452                 isMaster: true,
1453                 replSetGetStatus: true
1454             };
1455         }
1456 
1457         return good;
1458     };
1459 
1460     DB.prototype.auth = function() {
1461         var ex;
1462         try {
1463             this._authOrThrow.apply(this, arguments);
1464         } catch (ex) {
1465             print(ex);
1466             return 0;
1467         }
1468         return 1;
1469     };
1470 
1471     DB.prototype.grantRolesToUser = function(username, roles, writeConcern) {
1472         var cmdObj = {
1473             grantRolesToUser: username,
1474             roles: roles,
1475             writeConcern: writeConcern ? writeConcern : _defaultWriteConcern
1476         };
1477         var res = this.runCommand(cmdObj);
1478         if (!res.ok) {
1479             throw _getErrorWithCode(res, res.errmsg);
1480         }
1481     };
1482 
1483     DB.prototype.revokeRolesFromUser = function(username, roles, writeConcern) {
1484         var cmdObj = {
1485             revokeRolesFromUser: username,
1486             roles: roles,
1487             writeConcern: writeConcern ? writeConcern : _defaultWriteConcern
1488         };
1489         var res = this.runCommand(cmdObj);
1490         if (!res.ok) {
1491             throw _getErrorWithCode(res, res.errmsg);
1492         }
1493     };
1494 
1495     DB.prototype.getUser = function(username, args) {
1496         if (typeof username != "string") {
1497             throw Error("User name for getUser shell helper must be a string");
1498         }
1499         var cmdObj = {usersInfo: username};
1500         Object.extend(cmdObj, args);
1501 
1502         var res = this.runCommand(cmdObj);
1503         if (!res.ok) {
1504             throw _getErrorWithCode(res, res.errmsg);
1505         }
1506 
1507         if (res.users.length == 0) {
1508             return null;
1509         }
1510         return res.users[0];
1511     };
1512 
1513     DB.prototype.getUsers = function(args) {
1514         var cmdObj = {usersInfo: 1};
1515         Object.extend(cmdObj, args);
1516         var res = this.runCommand(cmdObj);
1517         if (!res.ok) {
1518             var authSchemaIncompatibleCode = 69;
1519             if (res.code == authSchemaIncompatibleCode ||
1520                 (res.code == null && res.errmsg == "no such cmd: usersInfo")) {
1521                 // Working with 2.4 schema user data
1522                 return this.system.users.find({}).toArray();
1523             }
1524 
1525             throw _getErrorWithCode(res, res.errmsg);
1526         }
1527 
1528         return res.users;
1529     };
1530 
1531     DB.prototype.createRole = function(roleObj, writeConcern) {
1532         var name = roleObj["role"];
1533         var cmdObj = {createRole: name};
1534         cmdObj = Object.extend(cmdObj, roleObj);
1535         delete cmdObj["role"];
1536         cmdObj["writeConcern"] = writeConcern ? writeConcern : _defaultWriteConcern;
1537 
1538         var res = this.runCommand(cmdObj);
1539 
1540         if (!res.ok) {
1541             throw _getErrorWithCode(res, res.errmsg);
1542         }
1543         printjson(roleObj);
1544     };
1545 
1546     DB.prototype.updateRole = function(name, updateObject, writeConcern) {
1547         var cmdObj = {updateRole: name};
1548         cmdObj = Object.extend(cmdObj, updateObject);
1549         cmdObj['writeConcern'] = writeConcern ? writeConcern : _defaultWriteConcern;
1550         var res = this.runCommand(cmdObj);
1551         if (!res.ok) {
1552             throw _getErrorWithCode(res, res.errmsg);
1553         }
1554     };
1555 
1556     DB.prototype.dropRole = function(name, writeConcern) {
1557         var cmdObj = {
1558             dropRole: name,
1559             writeConcern: writeConcern ? writeConcern : _defaultWriteConcern
1560         };
1561         var res = this.runCommand(cmdObj);
1562 
1563         if (res.ok) {
1564             return true;
1565         }
1566 
1567         if (res.code == 31) {  // Code 31 = RoleNotFound
1568             return false;
1569         }
1570 
1571         throw _getErrorWithCode(res, res.errmsg);
1572     };
1573 
1574     DB.prototype.dropAllRoles = function(writeConcern) {
1575         var res = this.runCommand({
1576             dropAllRolesFromDatabase: 1,
1577             writeConcern: writeConcern ? writeConcern : _defaultWriteConcern
1578         });
1579 
1580         if (!res.ok) {
1581             throw _getErrorWithCode(res, res.errmsg);
1582         }
1583 
1584         return res.n;
1585     };
1586 
1587     DB.prototype.grantRolesToRole = function(rolename, roles, writeConcern) {
1588         var cmdObj = {
1589             grantRolesToRole: rolename,
1590             roles: roles,
1591             writeConcern: writeConcern ? writeConcern : _defaultWriteConcern
1592         };
1593         var res = this.runCommand(cmdObj);
1594         if (!res.ok) {
1595             throw _getErrorWithCode(res, res.errmsg);
1596         }
1597     };
1598 
1599     DB.prototype.revokeRolesFromRole = function(rolename, roles, writeConcern) {
1600         var cmdObj = {
1601             revokeRolesFromRole: rolename,
1602             roles: roles,
1603             writeConcern: writeConcern ? writeConcern : _defaultWriteConcern
1604         };
1605         var res = this.runCommand(cmdObj);
1606         if (!res.ok) {
1607             throw _getErrorWithCode(res, res.errmsg);
1608         }
1609     };
1610 
1611     DB.prototype.grantPrivilegesToRole = function(rolename, privileges, writeConcern) {
1612         var cmdObj = {
1613             grantPrivilegesToRole: rolename,
1614             privileges: privileges,
1615             writeConcern: writeConcern ? writeConcern : _defaultWriteConcern
1616         };
1617         var res = this.runCommand(cmdObj);
1618         if (!res.ok) {
1619             throw _getErrorWithCode(res, res.errmsg);
1620         }
1621     };
1622 
1623     DB.prototype.revokePrivilegesFromRole = function(rolename, privileges, writeConcern) {
1624         var cmdObj = {
1625             revokePrivilegesFromRole: rolename,
1626             privileges: privileges,
1627             writeConcern: writeConcern ? writeConcern : _defaultWriteConcern
1628         };
1629         var res = this.runCommand(cmdObj);
1630         if (!res.ok) {
1631             throw _getErrorWithCode(res, res.errmsg);
1632         }
1633     };
1634 
1635     DB.prototype.getRole = function(rolename, args) {
1636         if (typeof rolename != "string") {
1637             throw Error("Role name for getRole shell helper must be a string");
1638         }
1639         var cmdObj = {rolesInfo: rolename};
1640         Object.extend(cmdObj, args);
1641         var res = this.runCommand(cmdObj);
1642         if (!res.ok) {
1643             throw _getErrorWithCode(res, res.errmsg);
1644         }
1645 
1646         if (res.roles.length == 0) {
1647             return null;
1648         }
1649         return res.roles[0];
1650     };
1651 
1652     DB.prototype.getRoles = function(args) {
1653         var cmdObj = {rolesInfo: 1};
1654         Object.extend(cmdObj, args);
1655         var res = this.runCommand(cmdObj);
1656         if (!res.ok) {
1657             throw _getErrorWithCode(res, res.errmsg);
1658         }
1659 
1660         return res.roles;
1661     };
1662 
1663     DB.prototype.setWriteConcern = function(wc) {
1664         if (wc instanceof WriteConcern) {
1665             this._writeConcern = wc;
1666         } else {
1667             this._writeConcern = new WriteConcern(wc);
1668         }
1669     };
1670 
1671     DB.prototype.getWriteConcern = function() {
1672         if (this._writeConcern)
1673             return this._writeConcern;
1674 
1675         if (this._mongo.getWriteConcern())
1676             return this._mongo.getWriteConcern();
1677 
1678         return null;
1679     };
1680 
1681     DB.prototype.unsetWriteConcern = function() {
1682         delete this._writeConcern;
1683     };
1684 
1685     DB.prototype.getLogComponents = function() {
1686         return this.getMongo().getLogComponents();
1687     };
1688 
1689     DB.prototype.setLogLevel = function(logLevel, component) {
1690         return this.getMongo().setLogLevel(logLevel, component);
1691     };
1692 
1693 }());
1694