diff options
author | Jonathan Abrahams <jonathan@mongodb.com> | 2016-03-09 11:51:31 -0500 |
---|---|---|
committer | Jonathan Abrahams <jonathan@mongodb.com> | 2016-03-09 11:51:31 -0500 |
commit | a025d43f3ce2efc1fb1282a718f5d286fa0a4dc1 (patch) | |
tree | a71ec459d031c8b76ebe1efc20a1150339161949 | |
parent | 4019e38d105597df8320e4958cc2be784157378d (diff) | |
download | mongo-a025d43f3ce2efc1fb1282a718f5d286fa0a4dc1.tar.gz |
SERVER-22469 Format JS code with approved style in src/mongo/shell & src/mongo/scripting
-rw-r--r-- | src/mongo/scripting/mozjs/mongohelpers.js | 11 | ||||
-rw-r--r-- | src/mongo/shell/assert.js | 247 | ||||
-rw-r--r-- | src/mongo/shell/bridge.js | 115 | ||||
-rw-r--r-- | src/mongo/shell/bulk_api.js | 2130 | ||||
-rw-r--r-- | src/mongo/shell/collection.js | 1205 | ||||
-rw-r--r-- | src/mongo/shell/crud_api.js | 188 | ||||
-rw-r--r-- | src/mongo/shell/db.js | 2987 | ||||
-rw-r--r-- | src/mongo/shell/explain_query.js | 18 | ||||
-rw-r--r-- | src/mongo/shell/explainable.js | 46 | ||||
-rw-r--r-- | src/mongo/shell/mongo.js | 175 | ||||
-rw-r--r-- | src/mongo/shell/mr.js | 88 | ||||
-rw-r--r-- | src/mongo/shell/query.js | 336 | ||||
-rw-r--r-- | src/mongo/shell/replsettest.js | 382 | ||||
-rw-r--r--[-rwxr-xr-x] | src/mongo/shell/servers.js | 1804 | ||||
-rw-r--r-- | src/mongo/shell/servers_misc.js | 210 | ||||
-rw-r--r-- | src/mongo/shell/shardingtest.js | 529 | ||||
-rw-r--r-- | src/mongo/shell/types.js | 406 | ||||
-rw-r--r-- | src/mongo/shell/upgrade_check.js | 384 | ||||
-rw-r--r-- | src/mongo/shell/utils.js | 937 | ||||
-rw-r--r-- | src/mongo/shell/utils_auth.js | 44 | ||||
-rw-r--r-- | src/mongo/shell/utils_sh.js | 916 |
21 files changed, 6797 insertions, 6361 deletions
diff --git a/src/mongo/scripting/mozjs/mongohelpers.js b/src/mongo/scripting/mozjs/mongohelpers.js index 2396d74efc6..b0b35bb2fe8 100644 --- a/src/mongo/scripting/mozjs/mongohelpers.js +++ b/src/mongo/scripting/mozjs/mongohelpers.js @@ -32,11 +32,11 @@ // this dictionary will be removed from the global scope. exportToMongoHelpers = { // This function accepts an expression or function body and returns a function definition - 'functionExpressionParser': function functionExpressionParser(fnSrc) { + 'functionExpressionParser': function functionExpressionParser(fnSrc) { var parseTree; try { parseTree = this.Reflect.parse(fnSrc); - } catch(e) { + } catch (e) { if (e == 'SyntaxError: function statement requires a name') { return fnSrc; } else if (e == 'SyntaxError: return not in function') { @@ -59,8 +59,7 @@ exportToMongoHelpers = { prevExprEnd = parseTree.body[lastStatement - 1].loc.end; if (prevExprEnd.line != loc.line) { prevExprEnd = 0; - } - else { + } else { prevExprEnd = prevExprEnd.column; } } @@ -73,7 +72,7 @@ exportToMongoHelpers = { // The parser has a weird behavior where sometimes if you have an expression like // ((x == 5)), it says that the expression string is "x == 5))", so we may need to // adjust where we prepend "return". - while(col >= prevExprEnd) { + while (col >= prevExprEnd) { var modLine = origLine.substr(0, col) + "return " + origLine.substr(col); lines[loc.line - 1] = modLine; fnSrc = '{ ' + lines.join('\n') + ' }'; @@ -87,7 +86,7 @@ exportToMongoHelpers = { } return "function() " + fnSrc; - } else if(lastStatementType == 'FunctionDeclaration') { + } else if (lastStatementType == 'FunctionDeclaration') { return fnSrc; } else { return 'function() { ' + fnSrc + ' }'; diff --git a/src/mongo/shell/assert.js b/src/mongo/shell/assert.js index 98e013600d8..2ae21259ec1 100644 --- a/src/mongo/shell/assert.js +++ b/src/mongo/shell/assert.js @@ -3,7 +3,7 @@ doassert = function(msg, obj) { if (typeof(msg) == "function") msg = msg(); - if (typeof (msg) == "string" && msg.indexOf("assert") == 0) + if (typeof(msg) == "string" && msg.indexOf("assert") == 0) print(msg); else print("assert: " + msg); @@ -18,14 +18,15 @@ doassert = function(msg, obj) { throw ex; }; -assert = function(b, msg){ +assert = function(b, msg) { if (arguments.length > 2) { doassert("Too many parameters to assert()."); } if (arguments.length > 1 && typeof(msg) !== "string") { doassert("Non-string 'msg' parameters are invalid for assert()."); } - if (assert._debug && msg) print("in assert for: " + msg); + if (assert._debug && msg) + print("in assert for: " + msg); if (b) return; doassert(msg == undefined ? "assert failed" : "assert failed : " + msg); @@ -37,8 +38,9 @@ assert.automsg = function(b) { assert._debug = false; -assert.eq = function(a, b, msg){ - if (assert._debug && msg) print("in assert for: " + msg); +assert.eq = function(a, b, msg) { + if (assert._debug && msg) + print("in assert for: " + msg); if (a == b) return; @@ -55,11 +57,10 @@ sortDoc = function(doc) { // Helper to sort the elements of the array var sortElementsOfArray = function(arr) { var newArr = []; - if(!arr || arr.constructor != Array) + if (!arr || arr.constructor != Array) return arr; - for(var i=0; i < arr.length; i++) { + for (var i = 0; i < arr.length; i++) { newArr.push(sortDoc(arr[i])); - } return newArr; }; @@ -76,7 +77,7 @@ sortDoc = function(doc) { var fields = Object.keys(doc); if (fields.length > 0) { fields.sort(); - for(var i=0; i < fields.length; i++){ + for (var i = 0; i < fields.length; i++) { var field = fields[i]; if (doc.hasOwnProperty(field)) { var tmp = doc[field]; @@ -99,7 +100,8 @@ sortDoc = function(doc) { }; assert.docEq = function(a, b, msg) { - if (assert._debug && msg) print("in assert for: " + msg); + if (assert._debug && msg) + print("in assert for: " + msg); if (a == b) return; @@ -117,41 +119,42 @@ assert.eq.automsg = function(a, b) { assert.eq(eval(a), eval(b), "[" + a + "] != [" + b + "]"); }; -assert.neq = function(a, b, msg){ - if (assert._debug && msg) print("in assert for: " + msg); +assert.neq = function(a, b, msg) { + if (assert._debug && msg) + print("in assert for: " + msg); if (a != b) return; doassert("[" + a + "] != [" + b + "] are equal : " + msg); }; -assert.contains = function(o, arr, msg){ +assert.contains = function(o, arr, msg) { var wasIn = false; if (!Array.isArray(arr)) { throw new Error("The second argument to assert.contains must be an array."); } - for(var i = 0; i < arr.length; i++){ + for (var i = 0; i < arr.length; i++) { wasIn = arr[i] == o || ((arr[i] != null && o != null) && friendlyEqual(arr[i], o)); - if(wasIn) { + if (wasIn) { break; } } - if(!wasIn) { + if (!wasIn) { doassert(tojson(o) + " was not in " + tojson(arr) + " : " + msg); } }; assert.soon = function(f, msg, timeout /*ms*/, interval) { - if (assert._debug && msg) print("in assert for: " + msg); + if (assert._debug && msg) + print("in assert for: " + msg); if (msg) { if (typeof(msg) != "function") { msg = "assert.soon failed, msg:" + msg; } - } - else { + } else { msg = "assert.soon failed: " + f; } @@ -159,12 +162,11 @@ assert.soon = function(f, msg, timeout /*ms*/, interval) { timeout = timeout || 30000; interval = interval || 200; var last; - while(1) { - if (typeof(f) == "string"){ + while (1) { + if (typeof(f) == "string") { if (eval(f)) return; - } - else { + } else { if (f()) return; } @@ -178,47 +180,48 @@ assert.soon = function(f, msg, timeout /*ms*/, interval) { }; assert.time = function(f, msg, timeout /*ms*/) { - if (assert._debug && msg) print("in assert for: " + msg); + if (assert._debug && msg) + print("in assert for: " + msg); var start = new Date(); timeout = timeout || 30000; - if (typeof(f) == "string"){ - res = eval(f); - } - else { - res = f(); - } + if (typeof(f) == "string") { + res = eval(f); + } else { + res = f(); + } - diff = (new Date()).getTime() - start.getTime(); - if (diff > timeout) - doassert("assert.time failed timeout " + timeout + "ms took " + diff + "ms : " + f + ", msg:" + msg); - return res; + diff = (new Date()).getTime() - start.getTime(); + if (diff > timeout) + doassert("assert.time failed timeout " + timeout + "ms took " + diff + "ms : " + f + + ", msg:" + msg); + return res; }; assert.throws = function(func, params, msg) { - if (assert._debug && msg) print("in assert for: " + msg); + if (assert._debug && msg) + print("in assert for: " + msg); if (params && typeof(params) == "string") { - throw ("2nd argument to assert.throws has to be an array, not " + params); + throw("2nd argument to assert.throws has to be an array, not " + params); } try { func.apply(null, params); - } - catch (e) { + } catch (e) { return e; } doassert("did not throw exception: " + msg); }; assert.doesNotThrow = function(func, params, msg) { - if (assert._debug && msg) print("in assert for: " + msg); + if (assert._debug && msg) + print("in assert for: " + msg); if (params && typeof(params) == "string") { - throw ("2nd argument to assert.throws has to be an array, not " + params); + throw("2nd argument to assert.throws has to be an array, not " + params); } var res; try { res = func.apply(null, params); - } - catch (e) { + } catch (e) { doassert("threw unexpected exception: " + e + " : " + msg); } return res; @@ -232,85 +235,102 @@ assert.doesNotThrow.automsg = function(func, params) { assert.doesNotThrow(func, params, func.toString()); }; -assert.commandWorked = function(res, msg){ - if (assert._debug && msg) print("in assert for: " + msg); +assert.commandWorked = function(res, msg) { + if (assert._debug && msg) + print("in assert for: " + msg); if (res.ok == 1) return res; doassert("command failed: " + tojson(res) + " : " + msg, res); }; -assert.commandFailed = function(res, msg){ - if (assert._debug && msg) print("in assert for: " + msg); +assert.commandFailed = function(res, msg) { + if (assert._debug && msg) + print("in assert for: " + msg); if (res.ok == 0) return res; doassert("command worked when it should have failed: " + tojson(res) + " : " + msg); }; -assert.commandFailedWithCode = function(res, code, msg){ - if (assert._debug && msg) print("in assert for: " + msg); +assert.commandFailedWithCode = function(res, code, msg) { + if (assert._debug && msg) + print("in assert for: " + msg); - assert(!res.ok, "Command result indicates success, but expected failure with code " + code + - ": " + tojson(res) + " : " + msg); - assert.eq(res.code, code, "Expected failure code did not match actual in command result: " + - tojson(res) + " : " + msg); + assert(!res.ok, + "Command result indicates success, but expected failure with code " + code + ": " + + tojson(res) + " : " + msg); + assert.eq(res.code, + code, + "Expected failure code did not match actual in command result: " + tojson(res) + + " : " + msg); return res; }; -assert.isnull = function(what, msg){ - if (assert._debug && msg) print("in assert for: " + msg); +assert.isnull = function(what, msg) { + if (assert._debug && msg) + print("in assert for: " + msg); if (what == null) return; doassert("supposed to be null (" + (msg || "") + ") was: " + tojson(what)); }; -assert.lt = function(a, b, msg){ - if (assert._debug && msg) print("in assert for: " + msg); +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); +assert.gt = function(a, b, msg) { + if (assert._debug && msg) + print("in assert for: " + msg); if (a > b) return; doassert(a + " is not greater than " + b + " : " + msg); }; -assert.lte = function(a, b, msg){ - if (assert._debug && msg) print("in assert for: " + msg); +assert.lte = function(a, b, msg) { + if (assert._debug && msg) + print("in assert for: " + msg); if (a <= b) return; doassert(a + " is not less than or eq " + b + " : " + msg); }; -assert.gte = function(a, b, msg){ - if (assert._debug && msg) print("in assert for: " + msg); +assert.gte = function(a, b, msg) { + if (assert._debug && msg) + print("in assert for: " + msg); if (a >= b) return; doassert(a + " is not greater than or eq " + b + " : " + msg); }; -assert.between = function(a, b, c, msg, inclusive){ - if (assert._debug && msg) print("in assert for: " + msg); +assert.between = function(a, b, c, msg, inclusive) { + if (assert._debug && msg) + print("in assert for: " + msg); - if((inclusive == undefined || inclusive == true) && - a <= b && b <= c) return; - else if(a < b && b < c) return; + if ((inclusive == undefined || inclusive == true) && a <= b && b <= c) + return; + else if (a < b && b < c) + return; doassert(b + " is not between " + a + " and " + c + " : " + msg); }; -assert.betweenIn = function(a, b, c, msg){ assert.between(a, b, c, msg, true); }; -assert.betweenEx = function(a, b, c, msg){ assert.between(a, b, c, msg, false); }; +assert.betweenIn = function(a, b, c, msg) { + assert.between(a, b, c, msg, true); +}; +assert.betweenEx = function(a, b, c, msg) { + assert.between(a, b, c, msg, false); +}; -assert.close = function(a, b, msg, places){ +assert.close = function(a, b, msg, places) { if (places === undefined) { places = 4; } @@ -327,9 +347,8 @@ assert.close = function(a, b, msg, places){ return; } - doassert(a + " is not equal to " + b + " within " + places + - " places, absolute error: " + absoluteError + - ", relative error: " + relativeError + " : " + msg); + doassert(a + " is not equal to " + b + " within " + places + " places, absolute error: " + + absoluteError + ", relative error: " + relativeError + " : " + msg); }; /** @@ -344,116 +363,104 @@ assert.closeWithinMS = function(a, b, msg, deltaMS) { var aMS = a instanceof Date ? a.getTime() : a; var bMS = b instanceof Date ? b.getTime() : b; var actualDelta = Math.abs(Math.abs(aMS) - Math.abs(bMS)); - if (actualDelta <= deltaMS) return; + if (actualDelta <= deltaMS) + return; - doassert(a + " is not equal to " + b + " within " + deltaMS + - " millis, actual delta: " + actualDelta + " millis : " + msg); + doassert(a + " is not equal to " + b + " within " + deltaMS + " millis, actual delta: " + + actualDelta + " millis : " + msg); }; assert.writeOK = function(res, msg) { - + var errMsg = null; if (res instanceof WriteResult) { if (res.hasWriteError()) { - errMsg = "write failed with error: " + tojson(res); - } - else if(res.hasWriteConcernError()) { + errMsg = "write failed with error: " + tojson(res); + } else if (res.hasWriteConcernError()) { errMsg = "write concern failed with errors: " + tojson(res); } - } - else if (res instanceof BulkWriteResult) { + } else if (res instanceof BulkWriteResult) { // Can only happen with bulk inserts if (res.hasWriteErrors()) { errMsg = "write failed with errors: " + tojson(res); - } - else if(res.hasWriteConcernError()) { + } else if (res.hasWriteConcernError()) { errMsg = "write concern failed with errors: " + tojson(res); } - } - else if (res instanceof WriteCommandError) { + } else if (res instanceof WriteCommandError) { // Can only happen with bulk inserts errMsg = "write command failed: " + tojson(res); - } - else { + } else { if (!res || !res.ok) { - errMsg = "unknown type of write result, cannot check ok: " - + tojson(res); + errMsg = "unknown type of write result, cannot check ok: " + tojson(res); } } - + if (errMsg) { if (msg) errMsg = errMsg + ": " + msg; doassert(errMsg, res); } - + return res; }; assert.writeError = function(res, msg) { - + var errMsg = null; if (res instanceof WriteResult) { if (!res.hasWriteError() && !res.hasWriteConcernError()) { - errMsg = "no write error: " + tojson(res); + errMsg = "no write error: " + tojson(res); } - } - else if (res instanceof BulkWriteResult) { + } else if (res instanceof BulkWriteResult) { // Can only happen with bulk inserts if (!res.hasWriteErrors() && !res.hasWriteConcernError()) { errMsg = "no write errors: " + tojson(res); } - } - else if (res instanceof WriteCommandError) { + } else if (res instanceof WriteCommandError) { // Can only happen with bulk inserts // No-op since we're expecting an error - } - else { + } else { if (!res || res.ok) { - errMsg = "unknown type of write result, cannot check error: " - + tojson(res); + errMsg = "unknown type of write result, cannot check error: " + tojson(res); } } - + if (errMsg) { if (msg) errMsg = errMsg + ": " + msg; doassert(errMsg); } - + return res; }; assert.gleOK = function(res, msg) { - + var errMsg = null; if (!res) { errMsg = "missing first argument, no response to check"; - } - else if (!res.ok) { + } else if (!res.ok) { errMsg = "getLastError failed: " + tojson(res); - } - else if ('code' in res || 'errmsg' in res - || ('err' in res && res['err'] != null)) { + } else if ('code' in res || 'errmsg' in res || ('err' in res && res['err'] != null)) { errMsg = "write or write concern failed: " + tojson(res); } - + if (errMsg) { if (msg) errMsg = errMsg + ": " + msg; doassert(errMsg, res); } - + return res; }; assert.gleSuccess = function(dbOrGLEDoc, msg) { var gle = dbOrGLEDoc instanceof DB ? dbOrGLEDoc.getLastErrorObj() : dbOrGLEDoc; if (gle.err) { - if (typeof(msg) == "function") + if (typeof(msg) == "function") msg = msg(gle); doassert("getLastError not null:" + tojson(gle) + " :" + msg, gle); } @@ -463,7 +470,7 @@ assert.gleSuccess = function(dbOrGLEDoc, msg) { assert.gleError = function(dbOrGLEDoc, msg) { var gle = dbOrGLEDoc instanceof DB ? dbOrGLEDoc.getLastErrorObj() : dbOrGLEDoc; if (!gle.err) { - if (typeof(msg) == "function") + if (typeof(msg) == "function") msg = msg(gle); doassert("getLastError is null: " + tojson(gle) + " :" + msg); } @@ -472,19 +479,19 @@ assert.gleError = function(dbOrGLEDoc, msg) { assert.gleErrorCode = function(dbOrGLEDoc, code, msg) { var gle = dbOrGLEDoc instanceof DB ? dbOrGLEDoc.getLastErrorObj() : dbOrGLEDoc; if (!gle.err || gle.code != code) { - if (typeof(msg) == "function") + if (typeof(msg) == "function") msg = msg(gle); - doassert("getLastError is null or has code other than \"" + code + "\": " - + tojson(gle) + " :" + msg); + doassert("getLastError is null or has code other than \"" + code + "\": " + tojson(gle) + + " :" + msg); } }; assert.gleErrorRegex = function(dbOrGLEDoc, regex, msg) { var gle = dbOrGLEDoc instanceof DB ? dbOrGLEDoc.getLastErrorObj() : dbOrGLEDoc; if (!gle.err || !regex.test(gle.err)) { - if (typeof(msg) == "function") + if (typeof(msg) == "function") msg = msg(gle); - doassert("getLastError is null or doesn't match regex (" + regex + "): " - + tojson(gle) + " :" + msg); + doassert("getLastError is null or doesn't match regex (" + regex + "): " + tojson(gle) + + " :" + msg); } }; diff --git a/src/mongo/shell/bridge.js b/src/mongo/shell/bridge.js index a9efb51feea..7f621f14a52 100644 --- a/src/mongo/shell/bridge.js +++ b/src/mongo/shell/bridge.js @@ -36,11 +36,7 @@ function MongoBridge(options) { // Start the mongobridge on port 'this.port' routing network traffic to 'this.dest'. var args = ['mongobridge', '--port', this.port, '--dest', this.dest]; - var keysToSkip = [ - 'dest', - 'hostName', - 'port', - ]; + var keysToSkip = ['dest', 'hostName', 'port', ]; // Append any command line arguments that are optional for mongobridge. Object.keys(options).forEach(function(key) { @@ -99,12 +95,14 @@ function MongoBridge(options) { // connection object that is equivalent to its 'host' property. Certain functions in // ReplSetTest and ShardingTest use the 'name' property instead of the 'host' property, so // we define it here for consistency. - Object.defineProperty(userConn, 'name', { - enumerable: true, - get: function() { - return this.host; - }, - }); + Object.defineProperty(userConn, + 'name', + { + enumerable: true, + get: function() { + return this.host; + }, + }); controlConn = new Mongo(hostName + ':' + this.port); }; @@ -191,8 +189,9 @@ function MongoBridge(options) { bridges.forEach(bridge => { var res = runBridgeCommand(controlConn, 'acceptConnectionsFrom', {host: bridge.dest}); - assert.commandWorked(res, 'failed to configure the mongobridge listening on port ' + - this.port + ' to accept new connections from ' + bridge.dest); + assert.commandWorked(res, + 'failed to configure the mongobridge listening on port ' + + this.port + ' to accept new connections from ' + bridge.dest); }); }; @@ -210,8 +209,9 @@ function MongoBridge(options) { bridges.forEach(bridge => { var res = runBridgeCommand(controlConn, 'rejectConnectionsFrom', {host: bridge.dest}); - assert.commandWorked(res, 'failed to configure the mongobridge listening on port ' + - this.port + ' to hang up connections from ' + bridge.dest); + assert.commandWorked(res, + 'failed to configure the mongobridge listening on port ' + + this.port + ' to hang up connections from ' + bridge.dest); }); }; @@ -229,13 +229,16 @@ function MongoBridge(options) { bridges.forEach(throwErrorIfNotMongoBridgeInstance); bridges.forEach(bridge => { - var res = runBridgeCommand(controlConn, 'delayMessagesFrom', { - host: bridge.dest, - delay: delay, - }); - assert.commandWorked(res, 'failed to configure the mongobridge listening on port ' + - this.port + ' to delay messages from ' + bridge.dest + ' by ' + - delay + ' milliseconds'); + var res = runBridgeCommand(controlConn, + 'delayMessagesFrom', + { + host: bridge.dest, + delay: delay, + }); + assert.commandWorked(res, + 'failed to configure the mongobridge listening on port ' + + this.port + ' to delay messages from ' + bridge.dest + ' by ' + + delay + ' milliseconds'); }); }; @@ -253,40 +256,48 @@ function MongoBridge(options) { bridges.forEach(throwErrorIfNotMongoBridgeInstance); bridges.forEach(bridge => { - var res = runBridgeCommand(controlConn, 'discardMessagesFrom', { - host: bridge.dest, - loss: lossProbability, - }); - assert.commandWorked(res, 'failed to configure the mongobridge listening on port ' + - this.port + ' to discard messages from ' + bridge.dest + - ' with probability ' + lossProbability); + var res = runBridgeCommand(controlConn, + 'discardMessagesFrom', + { + host: bridge.dest, + loss: lossProbability, + }); + assert.commandWorked(res, + 'failed to configure the mongobridge listening on port ' + + this.port + ' to discard messages from ' + bridge.dest + + ' with probability ' + lossProbability); }); }; // Use a Proxy to "extend" the underlying connection object. The C++ functions, e.g. // runCommand(), require that they are called on the Mongo instance itself and so typical // prototypical inheritance isn't possible. - return new Proxy(this, { - get: function get(target, property, receiver) { - // If the property is defined on the MongoBridge instance itself, then return it. - // Otherwise, get the value of the property from the Mongo instance. - if (target.hasOwnProperty(property)) { - return target[property]; - } - var value = userConn[property]; - if (typeof value === 'function') { - return value.bind(userConn); - } - return value; - }, - - set: function set(target, property, value, receiver) { - // Delegate setting the value of any property to the Mongo instance so that it can be - // accessed in functions acting on the Mongo instance directly instead of this Proxy. - // For example, the "slaveOk" property needs to be set on the Mongo instance in order - // for the query options bit to be set correctly. - userConn[property] = value; - return true; - }, - }); + return new Proxy(this, + { + get: function get(target, property, receiver) { + // If the property is defined on the MongoBridge instance itself, then + // return it. + // Otherwise, get the value of the property from the Mongo instance. + if (target.hasOwnProperty(property)) { + return target[property]; + } + var value = userConn[property]; + if (typeof value === 'function') { + return value.bind(userConn); + } + return value; + }, + + set: function set(target, property, value, receiver) { + // Delegate setting the value of any property to the Mongo instance so + // that it can be + // accessed in functions acting on the Mongo instance directly instead of + // this Proxy. + // For example, the "slaveOk" property needs to be set on the Mongo + // instance in order + // for the query options bit to be set correctly. + userConn[property] = value; + return true; + }, + }); } diff --git a/src/mongo/shell/bulk_api.js b/src/mongo/shell/bulk_api.js index 96e078ec49a..0afe20a9785 100644 --- a/src/mongo/shell/bulk_api.js +++ b/src/mongo/shell/bulk_api.js @@ -3,1205 +3,1224 @@ // var _bulk_api_module = (function() { - // Batch types - var NONE = 0; - var INSERT = 1; - var UPDATE = 2; - var REMOVE = 3; - - // Error codes - var UNKNOWN_ERROR = 8; - var WRITE_CONCERN_FAILED = 64; - var UNKNOWN_REPL_WRITE_CONCERN = 79; - var NOT_MASTER = 10107; - - // Constants - var IndexCollPattern = new RegExp('system\.indexes$'); - - /** - * Helper function to define properties - */ - var defineReadOnlyProperty = function(self, name, value) { - Object.defineProperty(self, name, { - enumerable: true - , get: function() { - return value; - } - }); - }; - - /** - * Shell representation of WriteConcern, possibly includes: - * j: write waits for journal - * w: write waits until replicated to number of servers (including primary), or mode (string) - * wtimeout: how long to wait for "w" replication - * fsync: waits for data flush (either journal, nor database files depending on server conf) - * - * Accepts { w : x, j : x, wtimeout : x, fsync: x } or w, wtimeout, j - */ - var WriteConcern = function(wValue, wTimeout, jValue) { - - if(!(this instanceof WriteConcern)) - return new WriteConcern(wValue, wTimeout, jValue); - - var opts = {}; - if (typeof wValue == 'object') { - if (arguments.length == 1) - opts = Object.merge(wValue); - else - throw Error("If the first arg is an Object then no additional args are allowed!"); - } else { - if (typeof wValue != 'undefined') - opts.w = wValue; - if (typeof wTimeout != 'undefined') - opts.wtimeout = wTimeout; - if (typeof jValue != 'undefined') - opts.j = jValue; - } - - // Do basic validation. - if (typeof opts.w != 'undefined' && typeof opts.w != 'number' && typeof opts.w != 'string') - throw Error("w value must be a number or string but was found to be a " + typeof opts.w); - if (typeof opts.w == 'number' && NumberInt( opts.w ).toNumber() < 0) - throw Error("Numeric w value must be equal to or larger than 0, not " + opts.w); - - if (typeof opts.wtimeout != 'undefined') { - if (typeof opts.wtimeout != 'number') - throw Error("wtimeout must be a number, not " + opts.wtimeout); - if (NumberInt( opts.wtimeout ).toNumber() < 0) - throw Error("wtimeout must be a number greater than or equal to 0, not " + opts.wtimeout); - } - - if (typeof opts.j != 'undefined' && typeof opts.j != 'boolean') - throw Error("j value must be true or false if defined, not " + opts.j); - - this._wc = opts; - - this.toJSON = function() { - return Object.merge({}, this._wc); - }; - + // Batch types + var NONE = 0; + var INSERT = 1; + var UPDATE = 2; + var REMOVE = 3; + + // Error codes + var UNKNOWN_ERROR = 8; + var WRITE_CONCERN_FAILED = 64; + var UNKNOWN_REPL_WRITE_CONCERN = 79; + var NOT_MASTER = 10107; + + // Constants + var IndexCollPattern = new RegExp('system\.indexes$'); + /** - * @return {string} + * Helper function to define properties */ - this.tojson = function(indent, nolint) { - return tojson(this.toJSON(), indent, nolint); - }; - - this.toString = function() { - return "WriteConcern(" + this.tojson() + ")"; + var defineReadOnlyProperty = function(self, name, value) { + Object.defineProperty(self, + name, + { + enumerable: true, + get: function() { + return value; + } + }); }; - - this.shellPrint = function() { - return this.toString(); - }; - - }; - - /** - * Wraps the result for write commands and presents a convenient api for accessing - * single results & errors (returns the last one if there are multiple). - * singleBatchType is passed in on bulk operations consisting of a single batch and - * are used to filter the WriteResult to only include relevant result fields. - */ - var WriteResult = function(bulkResult, singleBatchType, writeConcern) { - - if(!(this instanceof WriteResult)) - return new WriteResult(bulkResult, singleBatchType, writeConcern); - - // Define properties - defineReadOnlyProperty(this, "ok", bulkResult.ok); - defineReadOnlyProperty(this, "nInserted", bulkResult.nInserted); - defineReadOnlyProperty(this, "nUpserted", bulkResult.nUpserted); - defineReadOnlyProperty(this, "nMatched", bulkResult.nMatched); - defineReadOnlyProperty(this, "nModified", bulkResult.nModified); - defineReadOnlyProperty(this, "nRemoved", bulkResult.nRemoved); - // - // Define access methods - this.getUpsertedId = function() { - if (bulkResult.upserted.length == 0) { - return null; - } + /** + * Shell representation of WriteConcern, possibly includes: + * j: write waits for journal + * w: write waits until replicated to number of servers (including primary), or mode (string) + * wtimeout: how long to wait for "w" replication + * fsync: waits for data flush (either journal, nor database files depending on server conf) + * + * Accepts { w : x, j : x, wtimeout : x, fsync: x } or w, wtimeout, j + */ + var WriteConcern = function(wValue, wTimeout, jValue) { - return bulkResult.upserted[bulkResult.upserted.length - 1]; - }; + if (!(this instanceof WriteConcern)) + return new WriteConcern(wValue, wTimeout, jValue); - this.getRawResponse = function() { - return bulkResult; - }; + var opts = {}; + if (typeof wValue == 'object') { + if (arguments.length == 1) + opts = Object.merge(wValue); + else + throw Error("If the first arg is an Object then no additional args are allowed!"); + } else { + if (typeof wValue != 'undefined') + opts.w = wValue; + if (typeof wTimeout != 'undefined') + opts.wtimeout = wTimeout; + if (typeof jValue != 'undefined') + opts.j = jValue; + } - this.getWriteError = function() { - if (bulkResult.writeErrors.length == 0) { - return null; - } else { - return bulkResult.writeErrors[bulkResult.writeErrors.length - 1]; - } - }; + // Do basic validation. + if (typeof opts.w != 'undefined' && typeof opts.w != 'number' && typeof opts.w != 'string') + throw Error("w value must be a number or string but was found to be a " + + typeof opts.w); + if (typeof opts.w == 'number' && NumberInt(opts.w).toNumber() < 0) + throw Error("Numeric w value must be equal to or larger than 0, not " + opts.w); + + if (typeof opts.wtimeout != 'undefined') { + if (typeof opts.wtimeout != 'number') + throw Error("wtimeout must be a number, not " + opts.wtimeout); + if (NumberInt(opts.wtimeout).toNumber() < 0) + throw Error("wtimeout must be a number greater than or equal to 0, not " + + opts.wtimeout); + } - this.hasWriteError = function() { - return this.getWriteError() != null; - }; - - this.getWriteConcernError = function() { - if (bulkResult.writeConcernErrors.length == 0) { - return null; - } else { - return bulkResult.writeConcernErrors[0]; - } - }; - - this.hasWriteConcernError = function() { - return this.getWriteConcernError() != null; - }; + if (typeof opts.j != 'undefined' && typeof opts.j != 'boolean') + throw Error("j value must be true or false if defined, not " + opts.j); - /** - * @return {string} - */ - this.tojson = function(indent, nolint) { - var result = {}; + this._wc = opts; - if(singleBatchType == INSERT) { - result.nInserted = this.nInserted; - } + this.toJSON = function() { + return Object.merge({}, this._wc); + }; - if(singleBatchType == UPDATE) { - result.nMatched = this.nMatched; - result.nUpserted = this.nUpserted; + /** + * @return {string} + */ + this.tojson = function(indent, nolint) { + return tojson(this.toJSON(), indent, nolint); + }; - if(this.nModified != undefined) - result.nModified = this.nModified; + this.toString = function() { + return "WriteConcern(" + this.tojson() + ")"; + }; - if(Array.isArray(bulkResult.upserted) - && bulkResult.upserted.length == 1) { - result._id = bulkResult.upserted[0]._id; - } - } + this.shellPrint = function() { + return this.toString(); + }; - if(singleBatchType == REMOVE) { - result.nRemoved = bulkResult.nRemoved; - } + }; - if(this.getWriteError() != null) { - result.writeError = {}; - result.writeError.code = this.getWriteError().code; - result.writeError.errmsg = this.getWriteError().errmsg; - } + /** + * Wraps the result for write commands and presents a convenient api for accessing + * single results & errors (returns the last one if there are multiple). + * singleBatchType is passed in on bulk operations consisting of a single batch and + * are used to filter the WriteResult to only include relevant result fields. + */ + var WriteResult = function(bulkResult, singleBatchType, writeConcern) { + + if (!(this instanceof WriteResult)) + return new WriteResult(bulkResult, singleBatchType, writeConcern); + + // Define properties + defineReadOnlyProperty(this, "ok", bulkResult.ok); + defineReadOnlyProperty(this, "nInserted", bulkResult.nInserted); + defineReadOnlyProperty(this, "nUpserted", bulkResult.nUpserted); + defineReadOnlyProperty(this, "nMatched", bulkResult.nMatched); + defineReadOnlyProperty(this, "nModified", bulkResult.nModified); + defineReadOnlyProperty(this, "nRemoved", bulkResult.nRemoved); + + // + // Define access methods + this.getUpsertedId = function() { + if (bulkResult.upserted.length == 0) { + return null; + } - if(this.getWriteConcernError() != null) { - result.writeConcernError = this.getWriteConcernError(); - } + return bulkResult.upserted[bulkResult.upserted.length - 1]; + }; - return tojson(result, indent, nolint); - }; + this.getRawResponse = function() { + return bulkResult; + }; - this.toString = function() { - // Suppress all output for the write concern w:0, since the client doesn't care. - if(writeConcern && writeConcern.w == 0) { - return "WriteResult(" + tojson({}) + ")"; - } - return "WriteResult(" + this.tojson() + ")"; - }; + this.getWriteError = function() { + if (bulkResult.writeErrors.length == 0) { + return null; + } else { + return bulkResult.writeErrors[bulkResult.writeErrors.length - 1]; + } + }; - this.shellPrint = function() { - return this.toString(); - }; - }; - - /** - * Wraps the result for the commands - */ - var BulkWriteResult = function(bulkResult, singleBatchType, writeConcern) { - - if(!(this instanceof BulkWriteResult) && !(this instanceof BulkWriteError)) - return new BulkWriteResult(bulkResult, singleBatchType, writeConcern); - - // Define properties - defineReadOnlyProperty(this, "ok", bulkResult.ok); - defineReadOnlyProperty(this, "nInserted", bulkResult.nInserted); - defineReadOnlyProperty(this, "nUpserted", bulkResult.nUpserted); - defineReadOnlyProperty(this, "nMatched", bulkResult.nMatched); - defineReadOnlyProperty(this, "nModified", bulkResult.nModified); - defineReadOnlyProperty(this, "nRemoved", bulkResult.nRemoved); + this.hasWriteError = function() { + return this.getWriteError() != null; + }; - // - // Define access methods - this.getUpsertedIds = function() { - return bulkResult.upserted; - }; + this.getWriteConcernError = function() { + if (bulkResult.writeConcernErrors.length == 0) { + return null; + } else { + return bulkResult.writeConcernErrors[0]; + } + }; - this.getUpsertedIdAt = function(index) { - return bulkResult.upserted[index]; - }; + this.hasWriteConcernError = function() { + return this.getWriteConcernError() != null; + }; - this.getRawResponse = function() { - return bulkResult; - }; + /** + * @return {string} + */ + this.tojson = function(indent, nolint) { + var result = {}; - this.hasWriteErrors = function() { - return bulkResult.writeErrors.length > 0; - }; + if (singleBatchType == INSERT) { + result.nInserted = this.nInserted; + } - this.getWriteErrorCount = function() { - return bulkResult.writeErrors.length; - }; + if (singleBatchType == UPDATE) { + result.nMatched = this.nMatched; + result.nUpserted = this.nUpserted; - this.getWriteErrorAt = function(index) { - if(index < bulkResult.writeErrors.length) { - return bulkResult.writeErrors[index]; - } - return null; - }; + if (this.nModified != undefined) + result.nModified = this.nModified; - // - // Get all errors - this.getWriteErrors = function() { - return bulkResult.writeErrors; - }; + if (Array.isArray(bulkResult.upserted) && bulkResult.upserted.length == 1) { + result._id = bulkResult.upserted[0]._id; + } + } - this.hasWriteConcernError = function() { - return bulkResult.writeConcernErrors.length > 0; - }; + if (singleBatchType == REMOVE) { + result.nRemoved = bulkResult.nRemoved; + } - this.getWriteConcernError = function() { - if(bulkResult.writeConcernErrors.length == 0) { - return null; - } else if(bulkResult.writeConcernErrors.length == 1) { - // Return the error - return bulkResult.writeConcernErrors[0]; - } else { - - // Combine the errors - var errmsg = ""; - for(var i = 0; i < bulkResult.writeConcernErrors.length; i++) { - var err = bulkResult.writeConcernErrors[i]; - errmsg = errmsg + err.errmsg; - // TODO: Something better - if (i != bulkResult.writeConcernErrors.length - 1) { - errmsg = errmsg + " and "; - } - } + if (this.getWriteError() != null) { + result.writeError = {}; + result.writeError.code = this.getWriteError().code; + result.writeError.errmsg = this.getWriteError().errmsg; + } + + if (this.getWriteConcernError() != null) { + result.writeConcernError = this.getWriteConcernError(); + } - return new WriteConcernError({ errmsg : errmsg, code : WRITE_CONCERN_FAILED }); - } + return tojson(result, indent, nolint); + }; + + this.toString = function() { + // Suppress all output for the write concern w:0, since the client doesn't care. + if (writeConcern && writeConcern.w == 0) { + return "WriteResult(" + tojson({}) + ")"; + } + return "WriteResult(" + this.tojson() + ")"; + }; + + this.shellPrint = function() { + return this.toString(); + }; }; /** - * @return {string} + * Wraps the result for the commands */ - this.tojson = function(indent, nolint) { - return tojson(bulkResult, indent, nolint); - }; + var BulkWriteResult = function(bulkResult, singleBatchType, writeConcern) { + + if (!(this instanceof BulkWriteResult) && !(this instanceof BulkWriteError)) + return new BulkWriteResult(bulkResult, singleBatchType, writeConcern); + + // Define properties + defineReadOnlyProperty(this, "ok", bulkResult.ok); + defineReadOnlyProperty(this, "nInserted", bulkResult.nInserted); + defineReadOnlyProperty(this, "nUpserted", bulkResult.nUpserted); + defineReadOnlyProperty(this, "nMatched", bulkResult.nMatched); + defineReadOnlyProperty(this, "nModified", bulkResult.nModified); + defineReadOnlyProperty(this, "nRemoved", bulkResult.nRemoved); + + // + // Define access methods + this.getUpsertedIds = function() { + return bulkResult.upserted; + }; - this.toString = function() { - // Suppress all output for the write concern w:0, since the client doesn't care. - if(writeConcern && writeConcern.w == 0) { - return "BulkWriteResult(" + tojson({}) + ")"; - } - return "BulkWriteResult(" + this.tojson() + ")"; - }; + this.getUpsertedIdAt = function(index) { + return bulkResult.upserted[index]; + }; - this.shellPrint = function() { - return this.toString(); - }; + this.getRawResponse = function() { + return bulkResult; + }; - this.hasErrors = function() { - return this.hasWriteErrors() || this.hasWriteConcernError(); - }; + this.hasWriteErrors = function() { + return bulkResult.writeErrors.length > 0; + }; - this.toError = function() { - if (this.hasErrors()) { - - // Create a combined error message - var message = ""; - var numWriteErrors = this.getWriteErrorCount(); - if (numWriteErrors == 1) { - message += "write error at item " + this.getWriteErrors()[0].index; - } - else if (numWriteErrors > 1) { - message += numWriteErrors + " write errors"; - } + this.getWriteErrorCount = function() { + return bulkResult.writeErrors.length; + }; - var hasWCError = this.hasWriteConcernError(); - if (numWriteErrors > 0 && hasWCError) { - message += " and "; - } + this.getWriteErrorAt = function(index) { + if (index < bulkResult.writeErrors.length) { + return bulkResult.writeErrors[index]; + } + return null; + }; - if (hasWCError) { - message += "problem enforcing write concern"; - } - message += " in bulk operation"; - - return new BulkWriteError(bulkResult, singleBatchType, writeConcern, message); - } - else { - throw Error("batch was successful, cannot create BulkWriteError"); - } - }; + // + // Get all errors + this.getWriteErrors = function() { + return bulkResult.writeErrors; + }; - /** - * @return {WriteResult} the simplified results condensed into one. - */ - this.toSingleResult = function() { - if(singleBatchType == null) throw Error( - "Cannot output single WriteResult from multiple batch result"); - return new WriteResult(bulkResult, singleBatchType, writeConcern); - }; - }; - - /** - * Represents a bulk write error, identical to a BulkWriteResult but thrown - */ - var BulkWriteError = function(bulkResult, singleBatchType, writeConcern, message) { - - if(!(this instanceof BulkWriteError)) - return new BulkWriteError(bulkResult, singleBatchType, writeConcern, message); - - this.name = 'BulkWriteError'; - this.message = message || 'unknown bulk write error'; - - // Bulk errors are basically bulk results with additional error information - BulkWriteResult.apply(this, arguments); - - // Override some particular methods - delete this.toError; - - this.toString = function() { - return "BulkWriteError(" + this.tojson() + ")"; - }; - this.stack = this.toString() + "\n" + (new Error().stack); + this.hasWriteConcernError = function() { + return bulkResult.writeConcernErrors.length > 0; + }; - this.toResult = function() { - return new BulkWriteResult(bulkResult, singleBatchType, writeConcern); - }; - }; - - BulkWriteError.prototype = new Error(); - BulkWriteError.prototype.constructor = BulkWriteError; - - var getEmptyBulkResult = function() { - return { - writeErrors: [] - , writeConcernErrors: [] - , nInserted: 0 - , nUpserted: 0 - , nMatched: 0 - , nModified: 0 - , nRemoved: 0 - , upserted: [] - }; - }; + this.getWriteConcernError = function() { + if (bulkResult.writeConcernErrors.length == 0) { + return null; + } else if (bulkResult.writeConcernErrors.length == 1) { + // Return the error + return bulkResult.writeConcernErrors[0]; + } else { + // Combine the errors + var errmsg = ""; + for (var i = 0; i < bulkResult.writeConcernErrors.length; i++) { + var err = bulkResult.writeConcernErrors[i]; + errmsg = errmsg + err.errmsg; + // TODO: Something better + if (i != bulkResult.writeConcernErrors.length - 1) { + errmsg = errmsg + " and "; + } + } - /** - * Wraps a command error - */ - var WriteCommandError = function(commandError) { - - if(!(this instanceof WriteCommandError)) return new WriteCommandError(commandError); + return new WriteConcernError({errmsg: errmsg, code: WRITE_CONCERN_FAILED}); + } + }; - // Define properties - defineReadOnlyProperty(this, "code", commandError.code); - defineReadOnlyProperty(this, "errmsg", commandError.errmsg); + /** + * @return {string} + */ + this.tojson = function(indent, nolint) { + return tojson(bulkResult, indent, nolint); + }; - this.name = 'WriteCommandError'; - this.message = this.errmsg; + this.toString = function() { + // Suppress all output for the write concern w:0, since the client doesn't care. + if (writeConcern && writeConcern.w == 0) { + return "BulkWriteResult(" + tojson({}) + ")"; + } + return "BulkWriteResult(" + this.tojson() + ")"; + }; - /** - * @return {string} - */ - this.tojson = function(indent, nolint) { - return tojson(commandError, indent, nolint); - }; + this.shellPrint = function() { + return this.toString(); + }; - this.toString = function() { - return "WriteCommandError(" + this.tojson() + ")"; - }; - this.stack = this.toString() + "\n" + (new Error().stack); + this.hasErrors = function() { + return this.hasWriteErrors() || this.hasWriteConcernError(); + }; - this.shellPrint = function() { - return this.toString(); - }; + this.toError = function() { + if (this.hasErrors()) { + // Create a combined error message + var message = ""; + var numWriteErrors = this.getWriteErrorCount(); + if (numWriteErrors == 1) { + message += "write error at item " + this.getWriteErrors()[0].index; + } else if (numWriteErrors > 1) { + message += numWriteErrors + " write errors"; + } - this.toSingleResult = function() { - // This is *only* safe to do with a WriteCommandError from the bulk api when the bulk is - // known to be of size == 1 - var bulkResult = getEmptyBulkResult(); - bulkResult.writeErrors.push({ code : this.code, index : 0, errmsg : this.errmsg }); - return new BulkWriteResult(bulkResult, NONE).toSingleResult(); - }; - }; - - WriteCommandError.prototype = new Error(); - WriteCommandError.prototype.constructor = WriteCommandError; - - /** - * Wraps an error for a single write - */ - var WriteError = function(err) { - if(!(this instanceof WriteError)) return new WriteError(err); - - // Define properties - defineReadOnlyProperty(this, "code", err.code); - defineReadOnlyProperty(this, "index", err.index); - defineReadOnlyProperty(this, "errmsg", err.errmsg); + var hasWCError = this.hasWriteConcernError(); + if (numWriteErrors > 0 && hasWCError) { + message += " and "; + } - // - // Define access methods - this.getOperation = function() { - return err.op; + if (hasWCError) { + message += "problem enforcing write concern"; + } + message += " in bulk operation"; + + return new BulkWriteError(bulkResult, singleBatchType, writeConcern, message); + } else { + throw Error("batch was successful, cannot create BulkWriteError"); + } + }; + + /** + * @return {WriteResult} the simplified results condensed into one. + */ + this.toSingleResult = function() { + if (singleBatchType == null) + throw Error("Cannot output single WriteResult from multiple batch result"); + return new WriteResult(bulkResult, singleBatchType, writeConcern); + }; }; /** - * @return {string} + * Represents a bulk write error, identical to a BulkWriteResult but thrown */ - this.tojson = function(indent, nolint) { - return tojson(err, indent, nolint); - }; + var BulkWriteError = function(bulkResult, singleBatchType, writeConcern, message) { - this.toString = function() { - return "WriteError(" + tojson(err) + ")"; - }; + if (!(this instanceof BulkWriteError)) + return new BulkWriteError(bulkResult, singleBatchType, writeConcern, message); - this.shellPrint = function() { - return this.toString(); - }; - }; + this.name = 'BulkWriteError'; + this.message = message || 'unknown bulk write error'; - /** - * Wraps a write concern error - */ - var WriteConcernError = function(err) { - if(!(this instanceof WriteConcernError)) return new WriteConcernError(err); + // Bulk errors are basically bulk results with additional error information + BulkWriteResult.apply(this, arguments); - // Define properties - defineReadOnlyProperty(this, "code", err.code); - defineReadOnlyProperty(this, "errInfo", err.errInfo); - defineReadOnlyProperty(this, "errmsg", err.errmsg); + // Override some particular methods + delete this.toError; - /** - * @return {string} - */ - this.tojson = function(indent, nolint) { - return tojson(err, indent, nolint); - }; + this.toString = function() { + return "BulkWriteError(" + this.tojson() + ")"; + }; + this.stack = this.toString() + "\n" + (new Error().stack); - this.toString = function() { - return "WriteConcernError(" + tojson(err) + ")"; + this.toResult = function() { + return new BulkWriteResult(bulkResult, singleBatchType, writeConcern); + }; }; - this.shellPrint = function() { - return this.toString(); + BulkWriteError.prototype = new Error(); + BulkWriteError.prototype.constructor = BulkWriteError; + + var getEmptyBulkResult = function() { + return { + writeErrors: [], + writeConcernErrors: [], + nInserted: 0, + nUpserted: 0, + nMatched: 0, + nModified: 0, + nRemoved: 0, + upserted: [] + }; }; - }; - - /** - * Keeps the state of an unordered batch so we can rewrite the results - * correctly after command execution - */ - var Batch = function(batchType, originalZeroIndex) { - this.originalZeroIndex = originalZeroIndex; - this.batchType = batchType; - this.operations = []; - }; - - /** - * Wraps a legacy operation so we can correctly rewrite its error - */ - var LegacyOp = function(batchType, operation, index) { - this.batchType = batchType; - this.index = index; - this.operation = operation; - }; - - /*********************************************************** - * Wraps the operations done for the batch - ***********************************************************/ - var Bulk = function(collection, ordered) { - var self = this; - var coll = collection; - var executed = false; - - // Set max byte size - var maxBatchSizeBytes = 1024 * 1024 * 16; - var maxNumberOfDocsInBatch = 1000; - var writeConcern = null; - var currentOp; - - // Final results - var bulkResult = getEmptyBulkResult(); - - // Current batch - var currentBatch = null; - var currentIndex = 0; - var currentBatchSize = 0; - var currentBatchSizeBytes = 0; - var batches = []; - - var defineBatchTypeCounter = function(self, name, type) { - Object.defineProperty(self, name, { - enumerable: true - , get: function() { - var counter = 0; - - for(var i = 0; i < batches.length; i++) { - if(batches[i].batchType == type) { - counter += batches[i].operations.length; - } - } - if(currentBatch && currentBatch.batchType == type) { - counter += currentBatch.operations.length; - } + /** + * Wraps a command error + */ + var WriteCommandError = function(commandError) { - return counter; - } - }); - }; + if (!(this instanceof WriteCommandError)) + return new WriteCommandError(commandError); - defineBatchTypeCounter(this, "nInsertOps", INSERT); - defineBatchTypeCounter(this, "nUpdateOps", UPDATE); - defineBatchTypeCounter(this, "nRemoveOps", REMOVE); + // Define properties + defineReadOnlyProperty(this, "code", commandError.code); + defineReadOnlyProperty(this, "errmsg", commandError.errmsg); - // Convert bulk into string - this.toString = function() { - return this.tojson(); - }; + this.name = 'WriteCommandError'; + this.message = this.errmsg; - this.tojson = function() { - return tojson({ - nInsertOps: this.nInsertOps - , nUpdateOps: this.nUpdateOps - , nRemoveOps: this.nRemoveOps - , nBatches: batches.length + (currentBatch == null ? 0 : 1) - }); - }; + /** + * @return {string} + */ + this.tojson = function(indent, nolint) { + return tojson(commandError, indent, nolint); + }; + + this.toString = function() { + return "WriteCommandError(" + this.tojson() + ")"; + }; + this.stack = this.toString() + "\n" + (new Error().stack); + + this.shellPrint = function() { + return this.toString(); + }; - this.getOperations = function() { - return batches; + this.toSingleResult = function() { + // This is *only* safe to do with a WriteCommandError from the bulk api when the bulk is + // known to be of size == 1 + var bulkResult = getEmptyBulkResult(); + bulkResult.writeErrors.push({code: this.code, index: 0, errmsg: this.errmsg}); + return new BulkWriteResult(bulkResult, NONE).toSingleResult(); + }; }; - var finalizeBatch = function(newDocType) { - // Save the batch to the execution stack - batches.push(currentBatch); + WriteCommandError.prototype = new Error(); + WriteCommandError.prototype.constructor = WriteCommandError; - // Create a new batch - currentBatch = new Batch(newDocType, currentIndex); + /** + * Wraps an error for a single write + */ + var WriteError = function(err) { + if (!(this instanceof WriteError)) + return new WriteError(err); + + // Define properties + defineReadOnlyProperty(this, "code", err.code); + defineReadOnlyProperty(this, "index", err.index); + defineReadOnlyProperty(this, "errmsg", err.errmsg); + + // + // Define access methods + this.getOperation = function() { + return err.op; + }; - // Reset the current size trackers - currentBatchSize = 0; - currentBatchSizeBytes = 0; - }; + /** + * @return {string} + */ + this.tojson = function(indent, nolint) { + return tojson(err, indent, nolint); + }; + + this.toString = function() { + return "WriteError(" + tojson(err) + ")"; + }; - // Add to internal list of documents - var addToOperationsList = function(docType, document) { - - if (Array.isArray(document)) - throw Error("operation passed in cannot be an Array"); - - // Get the bsonSize - var bsonSize = Object.bsonsize(document); - - // Create a new batch object if we don't have a current one - if(currentBatch == null) currentBatch = new Batch(docType, currentIndex); - - // Finalize and create a new batch if this op would take us over the - // limits *or* if this op is of a different type - if(currentBatchSize + 1 > maxNumberOfDocsInBatch - || (currentBatchSize > 0 && - currentBatchSizeBytes + bsonSize >= maxBatchSizeBytes) - || currentBatch.batchType != docType) { - finalizeBatch(docType); - } - - currentBatch.operations.push(document); - currentIndex = currentIndex + 1; - // Update current batch size - currentBatchSize = currentBatchSize + 1; - currentBatchSizeBytes = currentBatchSizeBytes + bsonSize; + this.shellPrint = function() { + return this.toString(); + }; }; /** - * @return {Object} a new document with an _id: ObjectId if _id is not present. - * Otherwise, returns the same object passed. + * Wraps a write concern error */ - var addIdIfNeeded = function(obj) { - if ( typeof( obj._id ) == "undefined" && ! Array.isArray( obj ) ){ - var tmp = obj; // don't want to modify input - obj = {_id: new ObjectId()}; - for (var key in tmp){ - obj[key] = tmp[key]; - } - } + var WriteConcernError = function(err) { + if (!(this instanceof WriteConcernError)) + return new WriteConcernError(err); + + // Define properties + defineReadOnlyProperty(this, "code", err.code); + defineReadOnlyProperty(this, "errInfo", err.errInfo); + defineReadOnlyProperty(this, "errmsg", err.errmsg); + + /** + * @return {string} + */ + this.tojson = function(indent, nolint) { + return tojson(err, indent, nolint); + }; + + this.toString = function() { + return "WriteConcernError(" + tojson(err) + ")"; + }; - return obj; + this.shellPrint = function() { + return this.toString(); + }; }; /** - * Add the insert document. - * - * @param document {Object} the document to insert. + * Keeps the state of an unordered batch so we can rewrite the results + * correctly after command execution */ - this.insert = function(document) { - if (!IndexCollPattern.test(coll.getName())) { - collection._validateForStorage(document); - } + var Batch = function(batchType, originalZeroIndex) { + this.originalZeroIndex = originalZeroIndex; + this.batchType = batchType; + this.operations = []; + }; - return addToOperationsList(INSERT, document); + /** + * Wraps a legacy operation so we can correctly rewrite its error + */ + var LegacyOp = function(batchType, operation, index) { + this.batchType = batchType; + this.index = index; + this.operation = operation; }; - // - // Find based operations - var findOperations = { - update: function(updateDocument) { - collection._validateUpdateDoc(updateDocument); - - // Set the top value for the update 0 = multi true, 1 = multi false - var upsert = typeof currentOp.upsert == 'boolean' ? currentOp.upsert : false; - // Establish the update command - var document = { - q: currentOp.selector - , u: updateDocument - , multi: true - , upsert: upsert + /*********************************************************** + * Wraps the operations done for the batch + ***********************************************************/ + var Bulk = function(collection, ordered) { + var self = this; + var coll = collection; + var executed = false; + + // Set max byte size + var maxBatchSizeBytes = 1024 * 1024 * 16; + var maxNumberOfDocsInBatch = 1000; + var writeConcern = null; + var currentOp; + + // Final results + var bulkResult = getEmptyBulkResult(); + + // Current batch + var currentBatch = null; + var currentIndex = 0; + var currentBatchSize = 0; + var currentBatchSizeBytes = 0; + var batches = []; + + var defineBatchTypeCounter = function(self, name, type) { + Object.defineProperty(self, + name, + { + enumerable: true, + get: function() { + var counter = 0; + + for (var i = 0; i < batches.length; i++) { + if (batches[i].batchType == type) { + counter += batches[i].operations.length; + } + } + + if (currentBatch && currentBatch.batchType == type) { + counter += currentBatch.operations.length; + } + + return counter; + } + }); }; - // Clear out current Op - currentOp = null; - // Add the update document to the list - return addToOperationsList(UPDATE, document); - }, - - updateOne: function(updateDocument) { - collection._validateUpdateDoc(updateDocument); - - // Set the top value for the update 0 = multi true, 1 = multi false - var upsert = typeof currentOp.upsert == 'boolean' ? currentOp.upsert : false; - // Establish the update command - var document = { - q: currentOp.selector - , u: updateDocument - , multi: false - , upsert: upsert + defineBatchTypeCounter(this, "nInsertOps", INSERT); + defineBatchTypeCounter(this, "nUpdateOps", UPDATE); + defineBatchTypeCounter(this, "nRemoveOps", REMOVE); + + // Convert bulk into string + this.toString = function() { + return this.tojson(); }; - // Clear out current Op - currentOp = null; - // Add the update document to the list - return addToOperationsList(UPDATE, document); - }, - - replaceOne: function(updateDocument) { - findOperations.updateOne(updateDocument); - }, - - upsert: function() { - currentOp.upsert = true; - // Return the findOperations - return findOperations; - }, - - removeOne: function() { - collection._validateRemoveDoc(currentOp.selector); - - // Establish the removeOne command - var document = { - q: currentOp.selector - , limit: 1 + this.tojson = function() { + return tojson({ + nInsertOps: this.nInsertOps, + nUpdateOps: this.nUpdateOps, + nRemoveOps: this.nRemoveOps, + nBatches: batches.length + (currentBatch == null ? 0 : 1) + }); }; - // Clear out current Op - currentOp = null; - // Add the remove document to the list - return addToOperationsList(REMOVE, document); - }, + this.getOperations = function() { + return batches; + }; - remove: function() { - collection._validateRemoveDoc(currentOp.selector); + var finalizeBatch = function(newDocType) { + // Save the batch to the execution stack + batches.push(currentBatch); - // Establish the remove command - var document = { - q: currentOp.selector - , limit: 0 + // Create a new batch + currentBatch = new Batch(newDocType, currentIndex); + + // Reset the current size trackers + currentBatchSize = 0; + currentBatchSizeBytes = 0; }; - // Clear out current Op - currentOp = null; - // Add the remove document to the list - return addToOperationsList(REMOVE, document); - } - }; + // Add to internal list of documents + var addToOperationsList = function(docType, document) { - // - // Start of update and remove operations - this.find = function(selector) { - if (selector == undefined) throw Error("find() requires query criteria"); - // Save a current selector - currentOp = { - selector: selector - }; - - // Return the find Operations - return findOperations; - }; + if (Array.isArray(document)) + throw Error("operation passed in cannot be an Array"); - // - // Merge write command result into aggregated results object - var mergeBatchResults = function(batch, bulkResult, result) { + // Get the bsonSize + var bsonSize = Object.bsonsize(document); - // If we have an insert Batch type - if(batch.batchType == INSERT) { - bulkResult.nInserted = bulkResult.nInserted + result.n; - } + // Create a new batch object if we don't have a current one + if (currentBatch == null) + currentBatch = new Batch(docType, currentIndex); - // If we have a remove batch type - if(batch.batchType == REMOVE) { - bulkResult.nRemoved = bulkResult.nRemoved + result.n; - } + // Finalize and create a new batch if this op would take us over the + // limits *or* if this op is of a different type + if (currentBatchSize + 1 > maxNumberOfDocsInBatch || + (currentBatchSize > 0 && currentBatchSizeBytes + bsonSize >= maxBatchSizeBytes) || + currentBatch.batchType != docType) { + finalizeBatch(docType); + } - var nUpserted = 0; + currentBatch.operations.push(document); + currentIndex = currentIndex + 1; + // Update current batch size + currentBatchSize = currentBatchSize + 1; + currentBatchSizeBytes = currentBatchSizeBytes + bsonSize; + }; - // We have an array of upserted values, we need to rewrite the indexes - if(Array.isArray(result.upserted)) { + /** + * @return {Object} a new document with an _id: ObjectId if _id is not present. + * Otherwise, returns the same object passed. + */ + var addIdIfNeeded = function(obj) { + if (typeof(obj._id) == "undefined" && !Array.isArray(obj)) { + var tmp = obj; // don't want to modify input + obj = { + _id: new ObjectId() + }; + for (var key in tmp) { + obj[key] = tmp[key]; + } + } - nUpserted = result.upserted.length; + return obj; + }; - for(var i = 0; i < result.upserted.length; i++) { - bulkResult.upserted.push({ - index: result.upserted[i].index + batch.originalZeroIndex - , _id: result.upserted[i]._id - }); - } - } else if(result.upserted) { - - nUpserted = 1; - - bulkResult.upserted.push({ - index: batch.originalZeroIndex - , _id: result.upserted - }); - } - - // If we have an update Batch type - if(batch.batchType == UPDATE) { - bulkResult.nUpserted = bulkResult.nUpserted + nUpserted; - bulkResult.nMatched = bulkResult.nMatched + (result.n - nUpserted); - if(result.nModified == undefined) { - bulkResult.nModified = undefined; - } else if(bulkResult.nModified != undefined) { - bulkResult.nModified = bulkResult.nModified + result.nModified; - } - } + /** + * Add the insert document. + * + * @param document {Object} the document to insert. + */ + this.insert = function(document) { + if (!IndexCollPattern.test(coll.getName())) { + collection._validateForStorage(document); + } - if(Array.isArray(result.writeErrors)) { - for(var i = 0; i < result.writeErrors.length; i++) { + return addToOperationsList(INSERT, document); + }; - var writeError = { - index: batch.originalZeroIndex + result.writeErrors[i].index - , code: result.writeErrors[i].code - , errmsg: result.writeErrors[i].errmsg - , op: batch.operations[result.writeErrors[i].index] - }; + // + // Find based operations + var findOperations = { + update: function(updateDocument) { + collection._validateUpdateDoc(updateDocument); + + // Set the top value for the update 0 = multi true, 1 = multi false + var upsert = typeof currentOp.upsert == 'boolean' ? currentOp.upsert : false; + // Establish the update command + var document = { + q: currentOp.selector, + u: updateDocument, + multi: true, + upsert: upsert + }; + + // Clear out current Op + currentOp = null; + // Add the update document to the list + return addToOperationsList(UPDATE, document); + }, + + updateOne: function(updateDocument) { + collection._validateUpdateDoc(updateDocument); + + // Set the top value for the update 0 = multi true, 1 = multi false + var upsert = typeof currentOp.upsert == 'boolean' ? currentOp.upsert : false; + // Establish the update command + var document = { + q: currentOp.selector, + u: updateDocument, + multi: false, + upsert: upsert + }; + + // Clear out current Op + currentOp = null; + // Add the update document to the list + return addToOperationsList(UPDATE, document); + }, + + replaceOne: function(updateDocument) { + findOperations.updateOne(updateDocument); + }, + + upsert: function() { + currentOp.upsert = true; + // Return the findOperations + return findOperations; + }, + + removeOne: function() { + collection._validateRemoveDoc(currentOp.selector); + + // Establish the removeOne command + var document = { + q: currentOp.selector, + limit: 1 + }; + + // Clear out current Op + currentOp = null; + // Add the remove document to the list + return addToOperationsList(REMOVE, document); + }, + + remove: function() { + collection._validateRemoveDoc(currentOp.selector); + + // Establish the remove command + var document = { + q: currentOp.selector, + limit: 0 + }; + + // Clear out current Op + currentOp = null; + // Add the remove document to the list + return addToOperationsList(REMOVE, document); + } + }; - bulkResult.writeErrors.push(new WriteError(writeError)); - } - } + // + // Start of update and remove operations + this.find = function(selector) { + if (selector == undefined) + throw Error("find() requires query criteria"); + // Save a current selector + currentOp = { + selector: selector + }; + + // Return the find Operations + return findOperations; + }; - if(result.writeConcernError) { - bulkResult.writeConcernErrors.push(new WriteConcernError(result.writeConcernError)); - } - }; + // + // Merge write command result into aggregated results object + var mergeBatchResults = function(batch, bulkResult, result) { - // - // Constructs the write batch command. - var buildBatchCmd = function(batch) { - var cmd = null; - - // Generate the right update - if(batch.batchType == UPDATE) { - cmd = { update: coll.getName(), updates: batch.operations, ordered: ordered }; - } else if(batch.batchType == INSERT) { - var transformedInserts = []; - batch.operations.forEach(function(insertDoc) { - transformedInserts.push(addIdIfNeeded(insertDoc)); - }); - batch.operations = transformedInserts; - - cmd = { insert: coll.getName(), documents: batch.operations, ordered: ordered }; - } else if(batch.batchType == REMOVE) { - cmd = { delete: coll.getName(), deletes: batch.operations, ordered: ordered }; - } - - // If we have a write concern - if(writeConcern) { - cmd.writeConcern = writeConcern; - } - - return cmd; - }; + // If we have an insert Batch type + if (batch.batchType == INSERT) { + bulkResult.nInserted = bulkResult.nInserted + result.n; + } - // - // Execute the batch - var executeBatch = function(batch) { - var result = null; - var cmd = buildBatchCmd(batch); - - // Run the command (may throw) - - // Get command collection - var cmdColl = collection._db.getCollection('$cmd'); - // Bypass runCommand to ignore slaveOk and read pref settings - result = new DBQuery(collection.getMongo(), collection._db, - cmdColl, cmdColl.getFullName(), cmd, - {} /* proj */, -1 /* limit */, 0 /* skip */, 0 /* batchSize */, - 0 /* flags */).next(); - - if(result.ok == 0) { - throw new WriteCommandError(result); - } - - // Merge the results - mergeBatchResults(batch, bulkResult, result); - }; - - // Execute a single legacy op - var executeLegacyOp = function(_legacyOp) { - // Handle the different types of operation types - if(_legacyOp.batchType == INSERT) { - if (Array.isArray(_legacyOp.operation)) { - var transformedInserts = []; - _legacyOp.operation.forEach(function(insertDoc) { - transformedInserts.push(addIdIfNeeded(insertDoc)); - }); - _legacyOp.operation = transformedInserts; - } - else { - _legacyOp.operation = addIdIfNeeded(_legacyOp.operation); - } + // If we have a remove batch type + if (batch.batchType == REMOVE) { + bulkResult.nRemoved = bulkResult.nRemoved + result.n; + } - collection.getMongo().insert(collection.getFullName(), - _legacyOp.operation, - ordered); - } else if(_legacyOp.batchType == UPDATE) { - collection.getMongo().update(collection.getFullName(), - _legacyOp.operation.q, - _legacyOp.operation.u, - _legacyOp.operation.upsert, - _legacyOp.operation.multi); - } else if(_legacyOp.batchType == REMOVE) { - var single = Boolean(_legacyOp.operation.limit); - - collection.getMongo().remove(collection.getFullName(), - _legacyOp.operation.q, - single); - } - }; + var nUpserted = 0; - /** - * Parses the getLastError response and properly sets the write errors and - * write concern errors. - * Should kept be up to date with BatchSafeWriter::extractGLEErrors. - * - * @return {object} an object with the format: - * - * { - * writeError: {object|null} raw write error object without the index. - * wcError: {object|null} raw write concern error object. - * } - */ - var extractGLEErrors = function(gleResponse) { - var isOK = gleResponse.ok? true : false; - var err = (gleResponse.err)? gleResponse.err : ''; - var errMsg = (gleResponse.errmsg)? gleResponse.errmsg : ''; - var wNote = (gleResponse.wnote)? gleResponse.wnote : ''; - var jNote = (gleResponse.jnote)? gleResponse.jnote : ''; - var code = gleResponse.code; - var timeout = gleResponse.wtimeout? true : false; - - var extractedErr = { writeError: null, wcError: null, unknownError: null }; - - if (err == 'norepl' || err == 'noreplset') { - // Know this is legacy gle and the repl not enforced - write concern error in 2.4. - var errObj = { code: WRITE_CONCERN_FAILED }; - - if (errMsg != '') { - errObj.errmsg = errMsg; - } - else if (wNote != '') { - errObj.errmsg = wNote; - } - else { - errObj.errmsg = err; - } + // We have an array of upserted values, we need to rewrite the indexes + if (Array.isArray(result.upserted)) { + nUpserted = result.upserted.length; - extractedErr.wcError = errObj; - } - else if (timeout) { - // Know there was not write error. - var errObj = { code: WRITE_CONCERN_FAILED }; + for (var i = 0; i < result.upserted.length; i++) { + bulkResult.upserted.push({ + index: result.upserted[i].index + batch.originalZeroIndex, + _id: result.upserted[i]._id + }); + } + } else if (result.upserted) { + nUpserted = 1; - if (errMsg != '') { - errObj.errmsg = errMsg; - } - else { - errObj.errmsg = err; - } + bulkResult.upserted.push({index: batch.originalZeroIndex, _id: result.upserted}); + } + + // If we have an update Batch type + if (batch.batchType == UPDATE) { + bulkResult.nUpserted = bulkResult.nUpserted + nUpserted; + bulkResult.nMatched = bulkResult.nMatched + (result.n - nUpserted); + if (result.nModified == undefined) { + bulkResult.nModified = undefined; + } else if (bulkResult.nModified != undefined) { + bulkResult.nModified = bulkResult.nModified + result.nModified; + } + } + + if (Array.isArray(result.writeErrors)) { + for (var i = 0; i < result.writeErrors.length; i++) { + var writeError = { + index: batch.originalZeroIndex + result.writeErrors[i].index, + code: result.writeErrors[i].code, + errmsg: result.writeErrors[i].errmsg, + op: batch.operations[result.writeErrors[i].index] + }; - errObj.errInfo = { wtimeout: true }; - extractedErr.wcError = errObj; - } - else if (code == 19900 || // No longer primary - code == 16805 || // replicatedToNum no longer primary - code == 14330 || // gle wmode changed; invalid - code == NOT_MASTER || - code == UNKNOWN_REPL_WRITE_CONCERN || - code == WRITE_CONCERN_FAILED) { - extractedErr.wcError = { - code: code, - errmsg: errMsg + bulkResult.writeErrors.push(new WriteError(writeError)); + } + } + + if (result.writeConcernError) { + bulkResult.writeConcernErrors.push(new WriteConcernError(result.writeConcernError)); + } }; - } - else if (!isOK) { - // This is a GLE failure we don't understand - extractedErr.unknownError = { - code: code - , errmsg: errMsg + + // + // Constructs the write batch command. + var buildBatchCmd = function(batch) { + var cmd = null; + + // Generate the right update + if (batch.batchType == UPDATE) { + cmd = { + update: coll.getName(), + updates: batch.operations, + ordered: ordered + }; + } else if (batch.batchType == INSERT) { + var transformedInserts = []; + batch.operations.forEach(function(insertDoc) { + transformedInserts.push(addIdIfNeeded(insertDoc)); + }); + batch.operations = transformedInserts; + + cmd = { + insert: coll.getName(), + documents: batch.operations, + ordered: ordered + }; + } else if (batch.batchType == REMOVE) { + cmd = { + delete: coll.getName(), + deletes: batch.operations, + ordered: ordered + }; + } + + // If we have a write concern + if (writeConcern) { + cmd.writeConcern = writeConcern; + } + + return cmd; }; - } - else if (err != '') { - extractedErr.writeError = { - code: (code == 0)? UNKNOWN_ERROR : code, - errmsg: err + + // + // Execute the batch + var executeBatch = function(batch) { + var result = null; + var cmd = buildBatchCmd(batch); + + // Run the command (may throw) + + // Get command collection + var cmdColl = collection._db.getCollection('$cmd'); + // Bypass runCommand to ignore slaveOk and read pref settings + result = new DBQuery(collection.getMongo(), + collection._db, + cmdColl, + cmdColl.getFullName(), + cmd, + {} /* proj */, + -1 /* limit */, + 0 /* skip */, + 0 /* batchSize */, + 0 /* flags */).next(); + + if (result.ok == 0) { + throw new WriteCommandError(result); + } + + // Merge the results + mergeBatchResults(batch, bulkResult, result); }; - } - else if (jNote != '') { - extractedErr.writeError = { - code: WRITE_CONCERN_FAILED, - errmsg: jNote + + // Execute a single legacy op + var executeLegacyOp = function(_legacyOp) { + // Handle the different types of operation types + if (_legacyOp.batchType == INSERT) { + if (Array.isArray(_legacyOp.operation)) { + var transformedInserts = []; + _legacyOp.operation.forEach(function(insertDoc) { + transformedInserts.push(addIdIfNeeded(insertDoc)); + }); + _legacyOp.operation = transformedInserts; + } else { + _legacyOp.operation = addIdIfNeeded(_legacyOp.operation); + } + + collection.getMongo().insert( + collection.getFullName(), _legacyOp.operation, ordered); + } else if (_legacyOp.batchType == UPDATE) { + collection.getMongo().update(collection.getFullName(), + _legacyOp.operation.q, + _legacyOp.operation.u, + _legacyOp.operation.upsert, + _legacyOp.operation.multi); + } else if (_legacyOp.batchType == REMOVE) { + var single = Boolean(_legacyOp.operation.limit); + + collection.getMongo().remove( + collection.getFullName(), _legacyOp.operation.q, single); + } }; - } - // Handling of writeback not needed for mongo shell. - return extractedErr; - }; + /** + * Parses the getLastError response and properly sets the write errors and + * write concern errors. + * Should kept be up to date with BatchSafeWriter::extractGLEErrors. + * + * @return {object} an object with the format: + * + * { + * writeError: {object|null} raw write error object without the index. + * wcError: {object|null} raw write concern error object. + * } + */ + var extractGLEErrors = function(gleResponse) { + var isOK = gleResponse.ok ? true : false; + var err = (gleResponse.err) ? gleResponse.err : ''; + var errMsg = (gleResponse.errmsg) ? gleResponse.errmsg : ''; + var wNote = (gleResponse.wnote) ? gleResponse.wnote : ''; + var jNote = (gleResponse.jnote) ? gleResponse.jnote : ''; + var code = gleResponse.code; + var timeout = gleResponse.wtimeout ? true : false; + + var extractedErr = { + writeError: null, + wcError: null, + unknownError: null + }; + + if (err == 'norepl' || err == 'noreplset') { + // Know this is legacy gle and the repl not enforced - write concern error in 2.4. + var errObj = { + code: WRITE_CONCERN_FAILED + }; + + if (errMsg != '') { + errObj.errmsg = errMsg; + } else if (wNote != '') { + errObj.errmsg = wNote; + } else { + errObj.errmsg = err; + } - /** - * getLastErrorMethod that supports all write concerns - */ - var executeGetLastError = function(db, options) { - var cmd = { getlasterror : 1 }; - cmd = Object.extend(cmd, options); - // Execute the getLastErrorCommand - return db.runCommand( cmd ); - }; - - // Execute the operations, serially - var executeBatchWithLegacyOps = function(batch) { - - var batchResult = { - n: 0 - , writeErrors: [] - , upserted: [] - }; - - var extractedErr = null; - - var totalToExecute = batch.operations.length; - // Run over all the operations - for(var i = 0; i < batch.operations.length; i++) { - - if(batchResult.writeErrors.length > 0 && ordered) break; - - var _legacyOp = new LegacyOp(batch.batchType, batch.operations[i], i); - executeLegacyOp(_legacyOp); - - var result = executeGetLastError(collection.getDB(), { w: 1 }); - extractedErr = extractGLEErrors(result); - - if (extractedErr.unknownError) { - throw new WriteCommandError({ - ok : 0.0 - , code : extractedErr.unknownError.code - , errmsg : extractedErr.unknownError.errmsg - }); - } + extractedErr.wcError = errObj; + } else if (timeout) { + // Know there was not write error. + var errObj = { + code: WRITE_CONCERN_FAILED + }; + + if (errMsg != '') { + errObj.errmsg = errMsg; + } else { + errObj.errmsg = err; + } - if (extractedErr.writeError != null) { - // Create the emulated result set - var errResult = { - index: _legacyOp.index - , code: extractedErr.writeError.code - , errmsg: extractedErr.writeError.errmsg - , op: batch.operations[_legacyOp.index] - }; + errObj.errInfo = { + wtimeout: true + }; + extractedErr.wcError = errObj; + } else if (code == 19900 || // No longer primary + code == 16805 || // replicatedToNum no longer primary + code == 14330 || // gle wmode changed; invalid + code == NOT_MASTER || + code == UNKNOWN_REPL_WRITE_CONCERN || code == WRITE_CONCERN_FAILED) { + extractedErr.wcError = { + code: code, + errmsg: errMsg + }; + } else if (!isOK) { + // This is a GLE failure we don't understand + extractedErr.unknownError = { + code: code, + errmsg: errMsg + }; + } else if (err != '') { + extractedErr.writeError = { + code: (code == 0) ? UNKNOWN_ERROR : code, + errmsg: err + }; + } else if (jNote != '') { + extractedErr.writeError = { + code: WRITE_CONCERN_FAILED, + errmsg: jNote + }; + } - batchResult.writeErrors.push(errResult); - } - else if(_legacyOp.batchType == INSERT) { - // Inserts don't give us "n" back, so we can only infer - batchResult.n = batchResult.n + 1; - } + // Handling of writeback not needed for mongo shell. + return extractedErr; + }; + + /** + * getLastErrorMethod that supports all write concerns + */ + var executeGetLastError = function(db, options) { + var cmd = { + getlasterror: 1 + }; + cmd = Object.extend(cmd, options); + // Execute the getLastErrorCommand + return db.runCommand(cmd); + }; + + // Execute the operations, serially + var executeBatchWithLegacyOps = function(batch) { - if(_legacyOp.batchType == UPDATE) { + var batchResult = { + n: 0, + writeErrors: [], + upserted: [] + }; - // Unfortunately v2.4 GLE does not include the upserted field when - // the upserted _id is non-OID type. We can detect this by the - // updatedExisting field + an n of 1 - var upserted = result.upserted !== undefined || - (result.updatedExisting === false && result.n == 1); + var extractedErr = null; - if(upserted) { - batchResult.n = batchResult.n + 1; + var totalToExecute = batch.operations.length; + // Run over all the operations + for (var i = 0; i < batch.operations.length; i++) { + if (batchResult.writeErrors.length > 0 && ordered) + break; - // If we don't have an upserted value, see if we can pull it from the update or the - // query - if (result.upserted === undefined) { - result.upserted = _legacyOp.operation.u._id; - if (result.upserted === undefined) { - result.upserted = _legacyOp.operation.q._id; + var _legacyOp = new LegacyOp(batch.batchType, batch.operations[i], i); + executeLegacyOp(_legacyOp); + + var result = executeGetLastError(collection.getDB(), {w: 1}); + extractedErr = extractGLEErrors(result); + + if (extractedErr.unknownError) { + throw new WriteCommandError({ + ok: 0.0, + code: extractedErr.unknownError.code, + errmsg: extractedErr.unknownError.errmsg + }); } - } - batchResult.upserted.push({ - index: _legacyOp.index - , _id: result.upserted - }); - } else if(result.n) { - batchResult.n = batchResult.n + result.n; - } - } + if (extractedErr.writeError != null) { + // Create the emulated result set + var errResult = { + index: _legacyOp.index, + code: extractedErr.writeError.code, + errmsg: extractedErr.writeError.errmsg, + op: batch.operations[_legacyOp.index] + }; + + batchResult.writeErrors.push(errResult); + } else if (_legacyOp.batchType == INSERT) { + // Inserts don't give us "n" back, so we can only infer + batchResult.n = batchResult.n + 1; + } - if(_legacyOp.batchType == REMOVE && result.n) { - batchResult.n = batchResult.n + result.n; - } - } - - var needToEnforceWC = writeConcern != null && - bsonWoCompare(writeConcern, { w: 1 }) != 0 && - bsonWoCompare(writeConcern, { w: 0 }) != 0; - - extractedErr = null; - if (needToEnforceWC && - (batchResult.writeErrors.length == 0 || - (!ordered && - // not all errored. - batchResult.writeErrors.length < batch.operations.length))) { - - // if last write errored - if( batchResult.writeErrors.length > 0 && - batchResult.writeErrors[batchResult.writeErrors.length - 1].index == - (batch.operations.length - 1)) { - // Reset previous errors so we can apply the write concern no matter what - // as long as it is valid. - collection.getDB().runCommand({ resetError: 1 }); - } - - result = executeGetLastError(collection.getDB(), writeConcern); - extractedErr = extractGLEErrors(result); - - if (extractedErr.unknownError) { - // Report as a wc failure - extractedErr.wcError = extractedErr.unknownError; - } - } - - if (extractedErr != null && extractedErr.wcError != null) { - bulkResult.writeConcernErrors.push(extractedErr.wcError); - } - - // Merge the results - mergeBatchResults(batch, bulkResult, batchResult); - }; + if (_legacyOp.batchType == UPDATE) { + // Unfortunately v2.4 GLE does not include the upserted field when + // the upserted _id is non-OID type. We can detect this by the + // updatedExisting field + an n of 1 + var upserted = result.upserted !== undefined || + (result.updatedExisting === false && result.n == 1); + + if (upserted) { + batchResult.n = batchResult.n + 1; + + // If we don't have an upserted value, see if we can pull it from the update + // or the + // query + if (result.upserted === undefined) { + result.upserted = _legacyOp.operation.u._id; + if (result.upserted === undefined) { + result.upserted = _legacyOp.operation.q._id; + } + } + + batchResult.upserted.push({index: _legacyOp.index, _id: result.upserted}); + } else if (result.n) { + batchResult.n = batchResult.n + result.n; + } + } - // - // Execute the batch - this.execute = function(_writeConcern) { - if(executed) throw Error("A bulk operation cannot be re-executed"); + if (_legacyOp.batchType == REMOVE && result.n) { + batchResult.n = batchResult.n + result.n; + } + } - // If writeConcern set, use it, else get from collection (which will inherit from db/mongo) - writeConcern = _writeConcern ? _writeConcern : coll.getWriteConcern(); - if (writeConcern instanceof WriteConcern) - writeConcern = writeConcern.toJSON(); + var needToEnforceWC = writeConcern != null && + bsonWoCompare(writeConcern, {w: 1}) != 0 && + bsonWoCompare(writeConcern, {w: 0}) != 0; + + extractedErr = null; + if (needToEnforceWC && (batchResult.writeErrors.length == 0 || + (!ordered && + // not all errored. + batchResult.writeErrors.length < batch.operations.length))) { + // if last write errored + if (batchResult.writeErrors.length > 0 && + batchResult.writeErrors[batchResult.writeErrors.length - 1].index == + (batch.operations.length - 1)) { + // Reset previous errors so we can apply the write concern no matter what + // as long as it is valid. + collection.getDB().runCommand({resetError: 1}); + } - // If we have current batch - if(currentBatch) batches.push(currentBatch); + result = executeGetLastError(collection.getDB(), writeConcern); + extractedErr = extractGLEErrors(result); - // Total number of batches to execute - var totalNumberToExecute = batches.length; + if (extractedErr.unknownError) { + // Report as a wc failure + extractedErr.wcError = extractedErr.unknownError; + } + } - var useWriteCommands = collection.getMongo().useWriteCommands(); + if (extractedErr != null && extractedErr.wcError != null) { + bulkResult.writeConcernErrors.push(extractedErr.wcError); + } - // Execute all the batches - for(var i = 0; i < batches.length; i++) { + // Merge the results + mergeBatchResults(batch, bulkResult, batchResult); + }; + // // Execute the batch - if(collection.getMongo().hasWriteCommands() && - collection.getMongo().writeMode() == "commands") { - executeBatch(batches[i]); - } else { - executeBatchWithLegacyOps(batches[i]); - } + this.execute = function(_writeConcern) { + if (executed) + throw Error("A bulk operation cannot be re-executed"); + + // If writeConcern set, use it, else get from collection (which will inherit from + // db/mongo) + writeConcern = _writeConcern ? _writeConcern : coll.getWriteConcern(); + if (writeConcern instanceof WriteConcern) + writeConcern = writeConcern.toJSON(); + + // If we have current batch + if (currentBatch) + batches.push(currentBatch); + + // Total number of batches to execute + var totalNumberToExecute = batches.length; + + var useWriteCommands = collection.getMongo().useWriteCommands(); + + // Execute all the batches + for (var i = 0; i < batches.length; i++) { + // Execute the batch + if (collection.getMongo().hasWriteCommands() && + collection.getMongo().writeMode() == "commands") { + executeBatch(batches[i]); + } else { + executeBatchWithLegacyOps(batches[i]); + } - // If we are ordered and have errors and they are - // not all replication errors terminate the operation - if(bulkResult.writeErrors.length > 0 && ordered) { - // Ordered batches can't enforce full-batch write concern if they fail - they fail-fast - bulkResult.writeConcernErrors = []; - break; - } - } + // If we are ordered and have errors and they are + // not all replication errors terminate the operation + if (bulkResult.writeErrors.length > 0 && ordered) { + // Ordered batches can't enforce full-batch write concern if they fail - they + // fail-fast + bulkResult.writeConcernErrors = []; + break; + } + } + + // Set as executed + executed = true; + + // Create final result object + typedResult = new BulkWriteResult( + bulkResult, batches.length == 1 ? batches[0].batchType : null, writeConcern); + // Throw on error + if (typedResult.hasErrors()) { + throw typedResult.toError(); + } + + return typedResult; + }; - // Set as executed - executed = true; + // Generate an explain command for the bulk operation. Currently we only support single + // batches + // of size 1, which must be either delete or update. + this.convertToExplainCmd = function(verbosity) { + // If we have current batch + if (currentBatch) { + batches.push(currentBatch); + } - // Create final result object - typedResult = new BulkWriteResult(bulkResult, - batches.length == 1 ? batches[0].batchType : null, - writeConcern); - // Throw on error - if (typedResult.hasErrors()) { - throw typedResult.toError(); - } + // We can only explain singleton batches. + if (batches.length !== 1) { + throw Error("Explained bulk operations must consist of exactly 1 batch"); + } - return typedResult; + var explainBatch = batches[0]; + var writeCmd = buildBatchCmd(explainBatch); + return { + "explain": writeCmd, + "verbosity": verbosity + }; + }; }; - // Generate an explain command for the bulk operation. Currently we only support single batches - // of size 1, which must be either delete or update. - this.convertToExplainCmd = function(verbosity) { - // If we have current batch - if (currentBatch) { - batches.push(currentBatch); - } - - // We can only explain singleton batches. - if (batches.length !== 1) { - throw Error("Explained bulk operations must consist of exactly 1 batch"); - } - - var explainBatch = batches[0]; - var writeCmd = buildBatchCmd(explainBatch); - return {"explain": writeCmd, "verbosity": verbosity}; + // + // Exports + // + + module = {}; + module.WriteConcern = WriteConcern; + module.WriteResult = WriteResult; + module.BulkWriteResult = BulkWriteResult; + module.BulkWriteError = BulkWriteError; + module.WriteCommandError = WriteCommandError; + module.initializeUnorderedBulkOp = function() { + return new Bulk(this, false); + }; + module.initializeOrderedBulkOp = function() { + return new Bulk(this, true); }; - }; - - // - // Exports - // - - module = {}; - module.WriteConcern = WriteConcern; - module.WriteResult = WriteResult; - module.BulkWriteResult = BulkWriteResult; - module.BulkWriteError = BulkWriteError; - module.WriteCommandError = WriteCommandError; - module.initializeUnorderedBulkOp = function() { - return new Bulk(this, false); - }; - module.initializeOrderedBulkOp = function() { - return new Bulk(this, true); - }; - - return module; + + return module; })(); @@ -1217,4 +1236,3 @@ WriteCommandError = _bulk_api_module.WriteCommandError; ***********************************************************/ DBCollection.prototype.initializeUnorderedBulkOp = _bulk_api_module.initializeUnorderedBulkOp; DBCollection.prototype.initializeOrderedBulkOp = _bulk_api_module.initializeOrderedBulkOp; - diff --git a/src/mongo/shell/collection.js b/src/mongo/shell/collection.js index 7acb73bf71c..566913867d3 100644 --- a/src/mongo/shell/collection.js +++ b/src/mongo/shell/collection.js @@ -2,8 +2,8 @@ // db.colName is a DBCollection object // or db["colName"] -if ( ( typeof DBCollection ) == "undefined" ){ - DBCollection = function( mongo , db , shortName , fullName ){ +if ((typeof DBCollection) == "undefined") { + DBCollection = function(mongo, db, shortName, fullName) { this._mongo = mongo; this._db = db; this._shortName = shortName; @@ -13,116 +13,170 @@ if ( ( typeof DBCollection ) == "undefined" ){ }; } -DBCollection.prototype.verify = function(){ - assert( this._fullName , "no fullName" ); - assert( this._shortName , "no shortName" ); - assert( this._db , "no db" ); +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.eq(this._fullName, this._db._name + "." + this._shortName, "name mismatch"); - assert( this._mongo , "no mongo in DBCollection" ); - assert( this.getMongo() , "no mongo from getMongo()" ); + assert(this._mongo, "no mongo in DBCollection"); + assert(this.getMongo(), "no mongo from getMongo()"); }; -DBCollection.prototype.getName = function(){ +DBCollection.prototype.getName = function() { return this._shortName; }; -DBCollection.prototype.help = function () { +DBCollection.prototype.help = function() { var shortName = this.getName(); print("DBCollection help"); print("\tdb." + shortName + ".find().help() - show DBCursor help"); - print("\tdb." + shortName + ".bulkWrite( operations, <optional params> ) - bulk execute write operations, optional parameters are: w, wtimeout, j"); - print("\tdb." + shortName + ".count( query = {}, <optional params> ) - count the number of documents that matches the query, optional parameters are: limit, skip, hint, maxTimeMS"); - print("\tdb." + shortName + ".copyTo(newColl) - duplicates collection by copying all documents to newColl; no indexes are copied."); - print("\tdb." + shortName + ".convertToCapped(maxBytes) - calls {convertToCapped:'" + shortName + "', size:maxBytes}} command"); + print( + "\tdb." + shortName + + ".bulkWrite( operations, <optional params> ) - bulk execute write operations, optional parameters are: w, wtimeout, j"); + print( + "\tdb." + shortName + + ".count( query = {}, <optional params> ) - count the number of documents that matches the query, optional parameters are: limit, skip, hint, maxTimeMS"); + print( + "\tdb." + shortName + + ".copyTo(newColl) - duplicates collection by copying all documents to newColl; no indexes are copied."); + print("\tdb." + shortName + ".convertToCapped(maxBytes) - calls {convertToCapped:'" + + shortName + "', size:maxBytes}} command"); print("\tdb." + shortName + ".createIndex(keypattern[,options])"); print("\tdb." + shortName + ".createIndexes([keypatterns], <options>)"); print("\tdb." + shortName + ".dataSize()"); - print("\tdb." + shortName + ".deleteOne( filter, <optional params> ) - delete first matching document, optional parameters are: w, wtimeout, j"); - print("\tdb." + shortName + ".deleteMany( filter, <optional params> ) - delete all matching documents, optional parameters are: w, wtimeout, j"); - print("\tdb." + shortName + ".distinct( key, query, <optional params> ) - e.g. db." + shortName + ".distinct( 'x' ), optional parameters are: maxTimeMS"); + print( + "\tdb." + shortName + + ".deleteOne( filter, <optional params> ) - delete first matching document, optional parameters are: w, wtimeout, j"); + print( + "\tdb." + shortName + + ".deleteMany( filter, <optional params> ) - delete all matching documents, optional parameters are: w, wtimeout, j"); + print("\tdb." + shortName + ".distinct( key, query, <optional params> ) - e.g. db." + + shortName + ".distinct( 'x' ), optional parameters are: maxTimeMS"); print("\tdb." + shortName + ".drop() drop the collection"); - print("\tdb." + shortName + ".dropIndex(index) - e.g. db." + shortName + ".dropIndex( \"indexName\" ) or db." + shortName + ".dropIndex( { \"indexKey\" : 1 } )"); + print("\tdb." + shortName + ".dropIndex(index) - e.g. db." + shortName + + ".dropIndex( \"indexName\" ) or db." + shortName + ".dropIndex( { \"indexKey\" : 1 } )"); print("\tdb." + shortName + ".dropIndexes()"); - print("\tdb." + shortName + ".ensureIndex(keypattern[,options]) - DEPRECATED, use createIndex() instead"); + print("\tdb." + shortName + + ".ensureIndex(keypattern[,options]) - DEPRECATED, use createIndex() instead"); print("\tdb." + shortName + ".explain().help() - show explain help"); print("\tdb." + shortName + ".reIndex()"); - print("\tdb." + shortName + ".find([query],[fields]) - query is an optional query filter. fields is optional set of fields to return."); - print("\t e.g. db." + shortName + ".find( {x:77} , {name:1, x:1} )"); + print( + "\tdb." + shortName + + ".find([query],[fields]) - query is an optional query filter. fields is optional set of fields to return."); + print("\t e.g. db." + shortName + + ".find( {x:77} , {name:1, x:1} )"); print("\tdb." + shortName + ".find(...).count()"); print("\tdb." + shortName + ".find(...).limit(n)"); print("\tdb." + shortName + ".find(...).skip(n)"); print("\tdb." + shortName + ".find(...).sort(...)"); print("\tdb." + shortName + ".findOne([query], [fields], [options], [readConcern])"); - print("\tdb." + shortName + ".findOneAndDelete( filter, <optional params> ) - delete first matching document, optional parameters are: projection, sort, maxTimeMS"); - print("\tdb." + shortName + ".findOneAndReplace( filter, replacement, <optional params> ) - replace first matching document, optional parameters are: projection, sort, maxTimeMS, upsert, returnNewDocument"); - print("\tdb." + shortName + ".findOneAndUpdate( filter, update, <optional params> ) - update first matching document, optional parameters are: projection, sort, maxTimeMS, upsert, returnNewDocument"); + print( + "\tdb." + shortName + + ".findOneAndDelete( filter, <optional params> ) - delete first matching document, optional parameters are: projection, sort, maxTimeMS"); + print( + "\tdb." + shortName + + ".findOneAndReplace( filter, replacement, <optional params> ) - replace first matching document, optional parameters are: projection, sort, maxTimeMS, upsert, returnNewDocument"); + print( + "\tdb." + shortName + + ".findOneAndUpdate( filter, update, <optional params> ) - update first matching document, optional parameters are: projection, sort, maxTimeMS, upsert, returnNewDocument"); print("\tdb." + shortName + ".getDB() get DB object associated with collection"); print("\tdb." + shortName + ".getPlanCache() get query plan cache associated with collection"); print("\tdb." + shortName + ".getIndexes()"); print("\tdb." + shortName + ".group( { key : ..., initial: ..., reduce : ...[, cond: ...] } )"); print("\tdb." + shortName + ".insert(obj)"); - print("\tdb." + shortName + ".insertOne( obj, <optional params> ) - insert a document, optional parameters are: w, wtimeout, j"); - print("\tdb." + shortName + ".insertMany( [objects], <optional params> ) - insert multiple documents, optional parameters are: w, wtimeout, j"); + print( + "\tdb." + shortName + + ".insertOne( obj, <optional params> ) - insert a document, optional parameters are: w, wtimeout, j"); + print( + "\tdb." + shortName + + ".insertMany( [objects], <optional params> ) - insert multiple documents, optional parameters are: w, wtimeout, j"); print("\tdb." + shortName + ".mapReduce( mapFunction , reduceFunction , <optional params> )"); - print("\tdb." + shortName + ".aggregate( [pipeline], <optional params> ) - performs an aggregation on a collection; returns a cursor"); + print( + "\tdb." + shortName + + ".aggregate( [pipeline], <optional params> ) - performs an aggregation on a collection; returns a cursor"); print("\tdb." + shortName + ".remove(query)"); - print("\tdb." + shortName + ".replaceOne( filter, replacement, <optional params> ) - replace the first matching document, optional parameters are: upsert, w, wtimeout, j"); - print("\tdb." + shortName + ".renameCollection( newName , <dropTarget> ) renames the collection."); - print("\tdb." + shortName + ".runCommand( name , <options> ) runs a db command with the given name where the first param is the collection name"); + print( + "\tdb." + shortName + + ".replaceOne( filter, replacement, <optional params> ) - replace the first matching document, optional parameters are: upsert, w, wtimeout, j"); + print("\tdb." + shortName + + ".renameCollection( newName , <dropTarget> ) renames the collection."); + print( + "\tdb." + shortName + + ".runCommand( name , <options> ) runs a db command with the given name where the first param is the collection name"); print("\tdb." + shortName + ".save(obj)"); print("\tdb." + shortName + ".stats({scale: N, indexDetails: true/false, " + "indexDetailsKey: <index key>, indexDetailsName: <index name>})"); - // print("\tdb." + shortName + ".diskStorageStats({[extent: <num>,] [granularity: <bytes>,] ...}) - analyze record layout on disk"); - // print("\tdb." + shortName + ".pagesInRAM({[extent: <num>,] [granularity: <bytes>,] ...}) - analyze resident memory pages"); - print("\tdb." + shortName + ".storageSize() - includes free space allocated to this collection"); + // print("\tdb." + shortName + ".diskStorageStats({[extent: <num>,] [granularity: <bytes>,] + // ...}) - analyze record layout on disk"); + // print("\tdb." + shortName + ".pagesInRAM({[extent: <num>,] [granularity: <bytes>,] ...}) - + // analyze resident memory pages"); + print("\tdb." + shortName + + ".storageSize() - includes free space allocated to this collection"); print("\tdb." + shortName + ".totalIndexSize() - size in bytes of all the indexes"); print("\tdb." + shortName + ".totalSize() - storage allocated for all data and indexes"); - print("\tdb." + shortName + ".update( query, object[, upsert_bool, multi_bool] ) - instead of two flags, you can pass an object with fields: upsert, multi"); - print("\tdb." + shortName + ".updateOne( filter, update, <optional params> ) - update the first matching document, optional parameters are: upsert, w, wtimeout, j"); - print("\tdb." + shortName + ".updateMany( filter, update, <optional params> ) - update all matching documents, optional parameters are: upsert, w, wtimeout, j"); + print( + "\tdb." + shortName + + ".update( query, object[, upsert_bool, multi_bool] ) - instead of two flags, you can pass an object with fields: upsert, multi"); + print( + "\tdb." + shortName + + ".updateOne( filter, update, <optional params> ) - update the first matching document, optional parameters are: upsert, w, wtimeout, j"); + print( + "\tdb." + shortName + + ".updateMany( filter, update, <optional params> ) - update all matching documents, optional parameters are: upsert, w, wtimeout, j"); print("\tdb." + shortName + ".validate( <full> ) - SLOW"); print("\tdb." + shortName + ".getShardVersion() - only for use with sharding"); - print("\tdb." + shortName + ".getShardDistribution() - prints statistics about data distribution in the cluster"); - print("\tdb." + shortName + ".getSplitKeysForChunks( <maxChunkSize> ) - calculates split points over all chunks and returns splitter function"); - print("\tdb." + shortName + ".getWriteConcern() - returns the write concern used for any operations on this collection, inherited from server/db if set"); - print("\tdb." + shortName + ".setWriteConcern( <write concern doc> ) - sets the write concern for writes to the collection"); - print("\tdb." + shortName + ".unsetWriteConcern( <write concern doc> ) - unsets the write concern for writes to the collection"); - // print("\tdb." + shortName + ".getDiskStorageStats({...}) - prints a summary of disk usage statistics"); - // print("\tdb." + shortName + ".getPagesInRAM({...}) - prints a summary of storage pages currently in physical memory"); + print("\tdb." + shortName + + ".getShardDistribution() - prints statistics about data distribution in the cluster"); + print( + "\tdb." + shortName + + ".getSplitKeysForChunks( <maxChunkSize> ) - calculates split points over all chunks and returns splitter function"); + print( + "\tdb." + shortName + + ".getWriteConcern() - returns the write concern used for any operations on this collection, inherited from server/db if set"); + print( + "\tdb." + shortName + + ".setWriteConcern( <write concern doc> ) - sets the write concern for writes to the collection"); + print( + "\tdb." + shortName + + ".unsetWriteConcern( <write concern doc> ) - unsets the write concern for writes to the collection"); + // print("\tdb." + shortName + ".getDiskStorageStats({...}) - prints a summary of disk usage + // statistics"); + // print("\tdb." + shortName + ".getPagesInRAM({...}) - prints a summary of storage pages + // currently in physical memory"); return __magicNoPrint; }; -DBCollection.prototype.getFullName = function(){ +DBCollection.prototype.getFullName = function() { return this._fullName; }; -DBCollection.prototype.getMongo = function(){ +DBCollection.prototype.getMongo = function() { return this._db.getMongo(); }; -DBCollection.prototype.getDB = function(){ +DBCollection.prototype.getDB = function() { return this._db; }; -DBCollection.prototype._makeCommand = function (cmd, params) { +DBCollection.prototype._makeCommand = function(cmd, params) { var c = {}; c[cmd] = this.getName(); - if ( params ) + if (params) Object.extend(c, params); return c; }; -DBCollection.prototype._dbCommand = function( cmd , params ){ - if (typeof( cmd ) === "object") +DBCollection.prototype._dbCommand = function(cmd, params) { + if (typeof(cmd) === "object") return this._db._dbCommand(cmd, {}, this.getQueryOptions()); return this._db._dbCommand(this._makeCommand(cmd, params), {}, this.getQueryOptions()); }; // Like _dbCommand, but applies $readPreference -DBCollection.prototype._dbReadCommand = function( cmd , params ){ - if (typeof( cmd ) === "object") - return this._db._dbReadCommand( cmd , {}, this.getQueryOptions()); +DBCollection.prototype._dbReadCommand = function(cmd, params) { + if (typeof(cmd) === "object") + return this._db._dbReadCommand(cmd, {}, this.getQueryOptions()); return this._db._dbReadCommand(this._makeCommand(cmd, params), {}, this.getQueryOptions()); }; @@ -131,69 +185,90 @@ DBCollection.prototype.runCommand = DBCollection.prototype._dbCommand; DBCollection.prototype.runReadCommand = DBCollection.prototype._dbReadCommand; -DBCollection.prototype._massageObject = function( q ){ - if ( ! q ) +DBCollection.prototype._massageObject = function(q) { + if (!q) return {}; var type = typeof q; - if ( type == "function" ) - return { $where : q }; + if (type == "function") + return { + $where: q + }; - if ( q.isObjectId ) - return { _id : q }; + if (q.isObjectId) + return { + _id: q + }; - if ( type == "object" ) + if (type == "object") return q; - if ( type == "string" ){ - if ( q.length == 24 ) - return { _id : q }; + if (type == "string") { + if (q.length == 24) + return { + _id: q + }; - return { $where : q }; + return { + $where: q + }; } - throw Error( "don't know how to massage : " + type ); + throw Error("don't know how to massage : " + type); }; - -DBCollection.prototype._validateObject = function( o ){ +DBCollection.prototype._validateObject = function(o) { // Hidden property for testing purposes. - if (this.getMongo()._skipValidation) return; + if (this.getMongo()._skipValidation) + return; if (typeof(o) != "object") - throw Error( "attempted to save a " + typeof(o) + " value. document expected." ); + throw Error("attempted to save a " + typeof(o) + " value. document expected."); - if ( o._ensureSpecial && o._checkModify ) - throw Error( "can't save a DBQuery object" ); + if (o._ensureSpecial && o._checkModify) + throw Error("can't save a DBQuery object"); }; -DBCollection._allowedFields = { $id : 1 , $ref : 1 , $db : 1 }; +DBCollection._allowedFields = { + $id: 1, + $ref: 1, + $db: 1 +}; -DBCollection.prototype._validateForStorage = function( o ){ +DBCollection.prototype._validateForStorage = function(o) { // Hidden property for testing purposes. - if (this.getMongo()._skipValidation) return; + if (this.getMongo()._skipValidation) + return; - this._validateObject( o ); - for ( var k in o ){ - if ( k.indexOf( "." ) >= 0 ) { - throw Error( "can't have . in field names [" + k + "]" ); + this._validateObject(o); + for (var k in o) { + if (k.indexOf(".") >= 0) { + throw Error("can't have . in field names [" + k + "]"); } - if ( k.indexOf( "$" ) == 0 && ! DBCollection._allowedFields[k] ) { - throw Error( "field names cannot start with $ [" + k + "]" ); + if (k.indexOf("$") == 0 && !DBCollection._allowedFields[k]) { + throw Error("field names cannot start with $ [" + k + "]"); } - if ( o[k] !== null && typeof( o[k] ) === "object" ) { - this._validateForStorage( o[k] ); + if (o[k] !== null && typeof(o[k]) === "object") { + this._validateForStorage(o[k]); } } }; -DBCollection.prototype.find = function( query , fields , limit , skip, batchSize, options ){ - var cursor = new DBQuery( this._mongo , this._db , this , - this._fullName , this._massageObject( query ) , fields , limit , skip , batchSize , options || this.getQueryOptions() ); +DBCollection.prototype.find = function(query, fields, limit, skip, batchSize, options) { + var cursor = new DBQuery(this._mongo, + this._db, + this, + this._fullName, + this._massageObject(query), + fields, + limit, + skip, + batchSize, + options || this.getQueryOptions()); var connObj = this.getMongo(); var readPrefMode = connObj.getReadPrefMode(); @@ -204,37 +279,36 @@ DBCollection.prototype.find = function( query , fields , limit , skip, batchSize return cursor; }; -DBCollection.prototype.findOne = function( query , fields, options, readConcern ){ - var cursor = this.find(query, fields, -1 /* limit */, 0 /* skip*/, - 0 /* batchSize */, options); +DBCollection.prototype.findOne = function(query, fields, options, readConcern) { + var cursor = this.find(query, fields, -1 /* limit */, 0 /* skip*/, 0 /* batchSize */, options); - if ( readConcern ) { + if (readConcern) { cursor = cursor.readConcern(readConcern); } - if ( ! cursor.hasNext() ) + if (!cursor.hasNext()) return null; var ret = cursor.next(); - if ( cursor.hasNext() ) throw Error( "findOne has more than 1 result!" ); - if ( ret.$err ) + if (cursor.hasNext()) + throw Error("findOne has more than 1 result!"); + if (ret.$err) throw _getErrorWithCode(ret, "error " + tojson(ret)); return ret; }; -DBCollection.prototype.insert = function( obj , options, _allow_dot ){ - if ( ! obj ) - throw Error( "no object passed to insert!" ); +DBCollection.prototype.insert = function(obj, options, _allow_dot) { + if (!obj) + throw Error("no object passed to insert!"); var flags = 0; var wc = undefined; var allowDottedFields = false; - if ( options === undefined ) { + if (options === undefined) { // do nothing - } - else if ( typeof(options) == 'object' ) { + } else if (typeof(options) == 'object') { if (options.ordered === undefined) { - //do nothing, like above + // do nothing, like above } else { flags = options.ordered ? 0 : 1; } @@ -254,10 +328,10 @@ DBCollection.prototype.insert = function( obj , options, _allow_dot ){ wc = this.getWriteConcern(); var result = undefined; - var startTime = (typeof(_verboseShell) === 'undefined' || - !_verboseShell) ? 0 : new Date().getTime(); + var startTime = + (typeof(_verboseShell) === 'undefined' || !_verboseShell) ? 0 : new Date().getTime(); - if ( this.getMongo().writeMode() != "legacy" ) { + if (this.getMongo().writeMode() != "legacy") { // Bit 1 of option flag is continueOnError. Bit 0 (stop on error) is the default. var bulk = ordered ? this.initializeOrderedBulkOp() : this.initializeUnorderedBulkOp(); var isMultiInsert = Array.isArray(obj); @@ -266,8 +340,7 @@ DBCollection.prototype.insert = function( obj , options, _allow_dot ){ obj.forEach(function(doc) { bulk.insert(doc); }); - } - else { + } else { bulk.insert(obj); } @@ -275,34 +348,32 @@ DBCollection.prototype.insert = function( obj , options, _allow_dot ){ result = bulk.execute(wc); if (!isMultiInsert) result = result.toSingleResult(); - } - catch( ex ) { - if ( ex instanceof BulkWriteError ) { + } catch (ex) { + if (ex instanceof BulkWriteError) { result = isMultiInsert ? ex.toResult() : ex.toSingleResult(); - } - else if ( ex instanceof WriteCommandError ) { + } else if (ex instanceof WriteCommandError) { result = isMultiInsert ? ex : ex.toSingleResult(); - } - else { + } else { // Other exceptions thrown throw Error(ex); } } - } - else { - if ( ! _allow_dot ) { - this._validateForStorage( obj ); + } else { + if (!_allow_dot) { + this._validateForStorage(obj); } - if ( typeof( obj._id ) == "undefined" && ! Array.isArray( obj ) ){ - var tmp = obj; // don't want to modify input - obj = {_id: new ObjectId()}; - for (var key in tmp){ + if (typeof(obj._id) == "undefined" && !Array.isArray(obj)) { + var tmp = obj; // don't want to modify input + obj = { + _id: new ObjectId() + }; + for (var key in tmp) { obj[key] = tmp[key]; } } - this.getMongo().insert( this._fullName , obj, flags ); + this.getMongo().insert(this._fullName, obj, flags); // enforce write concern, if required if (wc) @@ -316,11 +387,12 @@ DBCollection.prototype.insert = function( obj , options, _allow_dot ){ DBCollection.prototype._validateRemoveDoc = function(doc) { // Hidden property for testing purposes. - if (this.getMongo()._skipValidation) return; + if (this.getMongo()._skipValidation) + return; for (var k in doc) { - if (k == "_id" && typeof( doc[k] ) == "undefined") { - throw new Error("can't have _id set to undefined in a remove expression"); + if (k == "_id" && typeof(doc[k]) == "undefined") { + throw new Error("can't have _id set to undefined in a remove expression"); } } }; @@ -329,8 +401,9 @@ DBCollection.prototype._validateRemoveDoc = function(doc) { * Does validation of the remove args. Throws if the parse is not successful, otherwise * returns a document {query: <query>, justOne: <limit>, wc: <writeConcern>}. */ -DBCollection.prototype._parseRemove = function( t , justOne ) { - if (undefined === t) throw Error("remove needs a query"); +DBCollection.prototype._parseRemove = function(t, justOne) { + if (undefined === t) + throw Error("remove needs a query"); var query = this._massageObject(t); @@ -349,52 +422,50 @@ DBCollection.prototype._parseRemove = function( t , justOne ) { wc = this.getWriteConcern(); } - return {"query": query, "justOne": justOne, "wc": wc}; + return { + "query": query, + "justOne": justOne, + "wc": wc + }; }; -DBCollection.prototype.remove = function( t , justOne ){ +DBCollection.prototype.remove = function(t, justOne) { var parsed = this._parseRemove(t, justOne); var query = parsed.query; var justOne = parsed.justOne; var wc = parsed.wc; var result = undefined; - var startTime = (typeof(_verboseShell) === 'undefined' || - !_verboseShell) ? 0 : new Date().getTime(); + var startTime = + (typeof(_verboseShell) === 'undefined' || !_verboseShell) ? 0 : new Date().getTime(); - - if ( this.getMongo().writeMode() != "legacy" ) { + if (this.getMongo().writeMode() != "legacy") { var bulk = this.initializeOrderedBulkOp(); var removeOp = bulk.find(query); if (justOne) { removeOp.removeOne(); - } - else { + } else { removeOp.remove(); } try { result = bulk.execute(wc).toSingleResult(); - } - catch( ex ) { - if ( ex instanceof BulkWriteError || ex instanceof WriteCommandError ) { + } catch (ex) { + if (ex instanceof BulkWriteError || ex instanceof WriteCommandError) { result = ex.toSingleResult(); - } - else { + } else { // Other exceptions thrown throw Error(ex); } } - } - else { + } else { this._validateRemoveDoc(t); - this.getMongo().remove(this._fullName, query, justOne ); + this.getMongo().remove(this._fullName, query, justOne); // enforce write concern, if required if (wc) result = this.runCommand("getLastError", wc instanceof WriteConcern ? wc.toJSON() : wc); - } this._printExtraInfo("Removed", startTime); @@ -403,17 +474,21 @@ DBCollection.prototype.remove = function( t , justOne ){ DBCollection.prototype._validateUpdateDoc = function(doc) { // Hidden property for testing purposes. - if (this.getMongo()._skipValidation) return; + if (this.getMongo()._skipValidation) + return; var firstKey = null; - for (var key in doc) { firstKey = key; break; } + for (var key in doc) { + firstKey = key; + break; + } if (firstKey != null && firstKey[0] == '$') { // for mods we only validate partially, for example keys may have dots - this._validateObject( doc ); + this._validateObject(doc); } else { // we're basically inserting a brand new object, do full validation - this._validateForStorage( doc ); + this._validateForStorage(doc); } }; @@ -423,13 +498,15 @@ DBCollection.prototype._validateUpdateDoc = function(doc) { * * Throws if the arguments are invalid. */ -DBCollection.prototype._parseUpdate = function( query , obj , upsert , multi ){ - if (!query) throw Error("need a query"); - if (!obj) throw Error("need an object"); +DBCollection.prototype._parseUpdate = function(query, obj, upsert, multi) { + if (!query) + throw Error("need a query"); + if (!obj) + throw Error("need an object"); var wc = undefined; // can pass options via object for improved readability - if ( typeof(upsert) === "object" ) { + if (typeof(upsert) === "object") { if (multi) { throw Error("Fourth argument must be empty when specifying " + "upsert and multi with an object."); @@ -449,14 +526,16 @@ DBCollection.prototype._parseUpdate = function( query , obj , upsert , multi ){ wc = this.getWriteConcern(); } - return {"query": query, - "obj": obj, - "upsert": upsert, - "multi": multi, - "wc": wc}; + return { + "query": query, + "obj": obj, + "upsert": upsert, + "multi": multi, + "wc": wc + }; }; -DBCollection.prototype.update = function( query , obj , upsert , multi ){ +DBCollection.prototype.update = function(query, obj, upsert, multi) { var parsed = this._parseUpdate(query, obj, upsert, multi); var query = parsed.query; var obj = parsed.obj; @@ -465,10 +544,10 @@ DBCollection.prototype.update = function( query , obj , upsert , multi ){ var wc = parsed.wc; var result = undefined; - var startTime = (typeof(_verboseShell) === 'undefined' || - !_verboseShell) ? 0 : new Date().getTime(); + var startTime = + (typeof(_verboseShell) === 'undefined' || !_verboseShell) ? 0 : new Date().getTime(); - if ( this.getMongo().writeMode() != "legacy" ) { + if (this.getMongo().writeMode() != "legacy") { var bulk = this.initializeOrderedBulkOp(); var updateOp = bulk.find(query); @@ -478,25 +557,21 @@ DBCollection.prototype.update = function( query , obj , upsert , multi ){ if (multi) { updateOp.update(obj); - } - else { + } else { updateOp.updateOne(obj); } try { result = bulk.execute(wc).toSingleResult(); - } - catch( ex ) { - if ( ex instanceof BulkWriteError || ex instanceof WriteCommandError ) { + } catch (ex) { + if (ex instanceof BulkWriteError || ex instanceof WriteCommandError) { result = ex.toSingleResult(); - } - else { + } else { // Other exceptions thrown throw Error(ex); } } - } - else { + } else { this._validateUpdateDoc(obj); this.getMongo().update(this._fullName, query, obj, upsert, multi); @@ -510,30 +585,29 @@ DBCollection.prototype.update = function( query , obj , upsert , multi ){ return result; }; -DBCollection.prototype.save = function( obj , opts ){ - if ( obj == null ) +DBCollection.prototype.save = function(obj, opts) { + if (obj == null) throw Error("can't save a null"); - if ( typeof( obj ) == "number" || typeof( obj) == "string" ) + if (typeof(obj) == "number" || typeof(obj) == "string") throw Error("can't save a number or string"); - if ( typeof( obj._id ) == "undefined" ){ + if (typeof(obj._id) == "undefined") { obj._id = new ObjectId(); - return this.insert( obj , opts ); - } - else { - return this.update( { _id : obj._id } , obj , Object.merge({ upsert:true }, opts)); + return this.insert(obj, opts); + } else { + return this.update({_id: obj._id}, obj, Object.merge({upsert: true}, opts)); } }; -DBCollection.prototype._genIndexName = function( keys ){ +DBCollection.prototype._genIndexName = function(keys) { var name = ""; - for ( var k in keys ){ + for (var k in keys) { var v = keys[k]; - if ( typeof v == "function" ) + if (typeof v == "function") continue; - if ( name.length > 0 ) + if (name.length > 0) name += "_"; name += k + "_"; @@ -542,48 +616,49 @@ DBCollection.prototype._genIndexName = function( keys ){ return name; }; -DBCollection.prototype._indexSpec = function( keys, options ) { - var ret = { ns : this._fullName , key : keys , name : this._genIndexName( keys ) }; +DBCollection.prototype._indexSpec = function(keys, options) { + var ret = { + ns: this._fullName, + key: keys, + name: this._genIndexName(keys) + }; - if ( ! options ){ - } - else if ( typeof ( options ) == "string" ) + if (!options) { + } else if (typeof(options) == "string") ret.name = options; - else if ( typeof ( options ) == "boolean" ) + else if (typeof(options) == "boolean") ret.unique = true; - else if ( typeof ( options ) == "object" ){ - if ( Array.isArray(options) ){ + else if (typeof(options) == "object") { + if (Array.isArray(options)) { if (options.length > 3) { throw new Error("Index options that are supplied in array form may only specify" + " three values: name, unique, dropDups"); } var nb = 0; - for ( var i=0; i<options.length; i++ ){ - if ( typeof ( options[i] ) == "string" ) + 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 ) + else if (typeof(options[i]) == "boolean") { + if (options[i]) { + if (nb == 0) ret.unique = true; - if ( nb == 1 ) + if (nb == 1) ret.dropDups = true; } nb++; } } + } else { + Object.extend(ret, options); } - else { - Object.extend( ret , options ); - } - } - else { - throw Error( "can't handle: " + typeof( options ) ); + } else { + throw Error("can't handle: " + typeof(options)); } return ret; }; -DBCollection.prototype.createIndex = function(keys , options) { +DBCollection.prototype.createIndex = function(keys, options) { return this.createIndexes([keys], options); }; @@ -595,34 +670,37 @@ DBCollection.prototype.createIndexes = function(keys, options) { if (this.getMongo().writeMode() == "commands") { for (i = 0; i++; i < indexSpecs.length) { - delete (indexSpecs[i].ns); // ns is passed to the first element in the command. + delete (indexSpecs[i].ns); // ns is passed to the first element in the command. } - return this._db.runCommand({createIndexes: this.getName(), indexes: indexSpecs}); - } - else if(this.getMongo().writeMode() == "compatibility") { + return this._db.runCommand({createIndexes: this.getName(), indexes: indexSpecs}); + } else if (this.getMongo().writeMode() == "compatibility") { // Use the downconversion machinery of the bulk api to do a safe write, report response as a // command response - var result = this._db.getCollection("system.indexes").insert(indexSpecs , 0, true); + var result = this._db.getCollection("system.indexes").insert(indexSpecs, 0, true); if (result.hasWriteErrors() || result.hasWriteConcernError()) { // Return the first error - var error = result.hasWriteErrors() ? result.getWriteErrors()[0] : - result.getWriteConcernError(); - return {ok : 0.0, code : error.code, errmsg : error.errmsg}; - } - else { - return {ok : 1.0}; + var error = result.hasWriteErrors() ? result.getWriteErrors()[0] + : result.getWriteConcernError(); + return { + ok: 0.0, + code: error.code, + errmsg: error.errmsg + }; + } else { + return { + ok: 1.0 + }; } - } - else { + } else { this._db.getCollection("system.indexes").insert(indexSpecs, 0, true); } }; -DBCollection.prototype.ensureIndex = function( keys , options ){ +DBCollection.prototype.ensureIndex = function(keys, options) { var result = this.createIndex(keys, options); - if ( this.getMongo().writeMode() != "legacy" ) { + if (this.getMongo().writeMode() != "legacy") { return result; } @@ -634,46 +712,47 @@ DBCollection.prototype.ensureIndex = function( keys , options ){ }; DBCollection.prototype.reIndex = function() { - return this._db.runCommand({ reIndex: this.getName() }); + return this._db.runCommand({reIndex: this.getName()}); }; -DBCollection.prototype.dropIndexes = function(){ - if ( arguments.length ) +DBCollection.prototype.dropIndexes = function() { + if (arguments.length) throw Error("dropIndexes doesn't take arguments"); - var res = this._db.runCommand( { deleteIndexes: this.getName(), index: "*" } ); - assert( res , "no result from dropIndex result" ); - if ( res.ok ) + 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/ ) ) + if (res.errmsg.match(/not found/)) return res; throw _getErrorWithCode(res, "error dropping indexes : " + tojson(res)); }; - -DBCollection.prototype.drop = function(){ - if ( arguments.length > 0 ) +DBCollection.prototype.drop = function() { + if (arguments.length > 0) throw Error("drop takes no argument"); - var ret = this._db.runCommand( { drop: this.getName() } ); - if ( ! ret.ok ){ - if ( ret.errmsg == "ns not found" ) + var ret = this._db.runCommand({drop: this.getName()}); + if (!ret.ok) { + if (ret.errmsg == "ns not found") return false; throw _getErrorWithCode(ret, "drop failed: " + tojson(ret)); } return true; }; -DBCollection.prototype.findAndModify = function(args){ - var cmd = { findandmodify: this.getName() }; - for (var key in args){ +DBCollection.prototype.findAndModify = function(args) { + var cmd = { + findandmodify: this.getName() + }; + for (var key in args) { cmd[key] = args[key]; } - var ret = this._db.runCommand( cmd ); - if ( ! ret.ok ){ - if (ret.errmsg == "No matching object found"){ + var ret = this._db.runCommand(cmd); + if (!ret.ok) { + if (ret.errmsg == "No matching object found") { return null; } throw _getErrorWithCode(ret, "findAndModifyFailed failed: " + tojson(ret)); @@ -681,15 +760,17 @@ DBCollection.prototype.findAndModify = function(args){ 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.renameCollection = function(newName, dropTarget) { + return this._db._adminCommand({ + renameCollection: this._fullName, + to: this._db._name + "." + newName, + dropTarget: dropTarget + }); }; // Display verbose information about the operation DBCollection.prototype._printExtraInfo = function(action, startTime) { - if ( typeof _verboseShell === 'undefined' || !_verboseShell ) { + if (typeof _verboseShell === 'undefined' || !_verboseShell) { __callLastError = true; return; } @@ -716,14 +797,16 @@ DBCollection.prototype._printExtraInfo = function(action, startTime) { }; DBCollection.prototype.validate = function(full) { - var cmd = { validate: this.getName() }; + var cmd = { + validate: this.getName() + }; - if (typeof(full) == 'object') // support arbitrary options here + if (typeof(full) == 'object') // support arbitrary options here Object.extend(cmd, full); else cmd.full = full; - var res = this._db.runCommand( cmd ); + var res = this._db.runCommand(cmd); if (typeof(res.valid) == 'undefined') { // old-style format just put everything in a string. Now using proper fields @@ -732,14 +815,14 @@ DBCollection.prototype.validate = function(full) { var raw = res.result || res.raw; - if ( raw ){ - var str = "-" + tojson( raw ); - res.valid = ! ( str.match( /exception/ ) || str.match( /corrupt/ ) ); + if (raw) { + var str = "-" + tojson(raw); + res.valid = !(str.match(/exception/) || str.match(/corrupt/)); var p = /lastExtentSize:(\d+)/; - var r = p.exec( str ); - if ( r ){ - res.lastExtentSize = Number( r[1] ); + var r = p.exec(str); + if (r) { + res.lastExtentSize = Number(r[1]); } } } @@ -753,8 +836,12 @@ DBCollection.prototype.validate = function(full) { * getDiskStorageStats provides a human-readable summary of the command output */ DBCollection.prototype.diskStorageStats = function(opt) { - var cmd = { storageDetails: this.getName(), analyze: 'diskStorage' }; - if (typeof(opt) == 'object') Object.extend(cmd, opt); + var cmd = { + storageDetails: this.getName(), + analyze: 'diskStorage' + }; + if (typeof(opt) == 'object') + Object.extend(cmd, opt); var res = this._db.runCommand(cmd); if (!res.ok && res.errmsg.match(/no such cmd/)) { @@ -782,14 +869,13 @@ DBCollection.prototype.getDiskStorageStats = function(params) { var bar = _barFormat([ [data.bsonBytes / data.onDiskBytes, "="], [(data.recBytes - data.bsonBytes) / data.onDiskBytes, "-"] - ], BAR_WIDTH); - - return sh._dataFormat(data.onDiskBytes).pad(9) + " " + - data.numEntries.toFixed(0).pad(10) + " " + - bar + " " + - (data.bsonBytes / data.onDiskBytes).toPercentStr().pad(8) + " " + - (data.recBytes / data.onDiskBytes).toPercentStr().pad(8) + " " + - (data.recBytes / data.bsonBytes).toFixed(4).pad(8); + ], + BAR_WIDTH); + + return sh._dataFormat(data.onDiskBytes).pad(9) + " " + data.numEntries.toFixed(0).pad(10) + + " " + bar + " " + (data.bsonBytes / data.onDiskBytes).toPercentStr().pad(8) + " " + + (data.recBytes / data.onDiskBytes).toPercentStr().pad(8) + " " + + (data.recBytes / data.bsonBytes).toFixed(4).pad(8); }; var printExtent = function(ex, rng) { @@ -829,8 +915,12 @@ DBCollection.prototype.getDiskStorageStats = function(params) { * getPagesInRAM provides a human-readable summary of the command output */ DBCollection.prototype.pagesInRAM = function(opt) { - var cmd = { storageDetails: this.getName(), analyze: 'pagesInRAM' }; - if (typeof(opt) == 'object') Object.extend(cmd, opt); + var cmd = { + storageDetails: this.getName(), + analyze: 'pagesInRAM' + }; + if (typeof(opt) == 'object') + Object.extend(cmd, opt); var res = this._db.runCommand(cmd); if (!res.ok && res.errmsg.match(/no such cmd/)) { @@ -849,9 +939,8 @@ DBCollection.prototype.getPagesInRAM = function(params) { var BAR_WIDTH = 70; var formatExtentData = function(data) { - return "size".pad(8) + " " + - _barFormat([ [data.inMem, '='] ], BAR_WIDTH) + " " + - data.inMem.toPercentStr().pad(7); + return "size".pad(8) + " " + _barFormat([[data.inMem, '=']], BAR_WIDTH) + " " + + data.inMem.toPercentStr().pad(7); }; var printExtent = function(ex, rng) { @@ -871,11 +960,16 @@ DBCollection.prototype.getPagesInRAM = function(params) { line = "\t" + sh._dataFormat(ex.sliceBytes * c).pad(8) + " ["; } var inMem = ex.slices[c]; - if (inMem <= .001) line += " "; - else if (inMem <= .25) line += "."; - else if (inMem <= .5) line += "_"; - else if (inMem <= .75) line += "="; - else line += "#"; + if (inMem <= .001) + line += " "; + else if (inMem <= .25) + line += "."; + else if (inMem <= .5) + line += "_"; + else if (inMem <= .75) + line += "="; + else + line += "#"; } print(line + "]"); print(); @@ -903,34 +997,35 @@ DBCollection.prototype.getPagesInRAM = function(params) { } }; -DBCollection.prototype.getShardVersion = function(){ - return this._db._adminCommand( { getShardVersion : this._fullName } ); +DBCollection.prototype.getShardVersion = function() { + return this._db._adminCommand({getShardVersion: this._fullName}); }; -DBCollection.prototype._getIndexesSystemIndexes = function(filter){ - var si = this.getDB().getCollection( "system.indexes" ); - var query = { ns : this.getFullName() }; +DBCollection.prototype._getIndexesSystemIndexes = function(filter) { + var si = this.getDB().getCollection("system.indexes"); + var query = { + ns: this.getFullName() + }; if (filter) query = Object.extend(query, filter); - return si.find( query ).toArray(); + return si.find(query).toArray(); }; -DBCollection.prototype._getIndexesCommand = function(filter){ - var res = this.runCommand( "listIndexes", filter ); - - if ( !res.ok ) { +DBCollection.prototype._getIndexesCommand = function(filter) { + var res = this.runCommand("listIndexes", filter); - if ( res.code == 59 ) { + if (!res.ok) { + if (res.code == 59) { // command doesn't exist, old mongod return null; } - if ( res.code == 26 ) { + if (res.code == 26) { // NamespaceNotFound, for compatability, return [] return []; } - if ( res.errmsg && res.errmsg.startsWith( "no such cmd" ) ) { + if (res.errmsg && res.errmsg.startsWith("no such cmd")) { return null; } @@ -940,9 +1035,9 @@ DBCollection.prototype._getIndexesCommand = function(filter){ return new DBCommandCursor(this._mongo, res).toArray(); }; -DBCollection.prototype.getIndexes = function(filter){ +DBCollection.prototype.getIndexes = function(filter) { var res = this._getIndexesCommand(filter); - if ( res ) { + if (res) { return res; } return this._getIndexesSystemIndexes(filter); @@ -951,22 +1046,21 @@ DBCollection.prototype.getIndexes = function(filter){ 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.getIndexKeys = function() { + return this.getIndexes().map(function(i) { + return i.key; + }); }; - DBCollection.prototype.hashAllDocs = function() { - var cmd = { dbhash : 1, - collections : [ this._shortName ] }; - var res = this._dbCommand( cmd ); + var cmd = { + dbhash: 1, + collections: [this._shortName] + }; + var res = this._dbCommand(cmd); var hash = res.collections[this._shortName]; - assert( hash ); - assert( typeof(hash) == "string" ); + assert(hash); + assert(typeof(hash) == "string"); return hash; }; @@ -974,7 +1068,8 @@ DBCollection.prototype.hashAllDocs = function() { * <p>Drop a specified index.</p> * * <p> - * "index" is the name of the index in the system.indexes name field (run db.system.indexes.find() to + * "index" is the name of the index in the system.indexes name field (run db.system.indexes.find() + *to * see example data), or an object holding the key(s) used to create the index. * For example: * db.collectionName.dropIndex( "myIndexName" ); @@ -984,34 +1079,32 @@ DBCollection.prototype.hashAllDocs = function() { * @param {String} name or key object 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" ); - var res = this._dbCommand( "deleteIndexes", { index: index } ); +DBCollection.prototype.dropIndex = function(index) { + assert(index, "need to specify index to dropIndex"); + var res = this._dbCommand("deleteIndexes", {index: index}); 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; +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 ); - } + var cursor = from.find(); + while (cursor.hasNext()) { + var o = cursor.next(); + count++; + to.save(o); + } - return count; - } , this.getName() , newName - ); + return count; + }, this.getName(), newName); }; -DBCollection.prototype.getCollection = function( subName ){ - return this._db.getCollection( this._shortName + "." + subName ); +DBCollection.prototype.getCollection = function(subName) { + return this._db.getCollection(this._shortName + "." + subName); }; /** @@ -1041,7 +1134,8 @@ DBCollection.prototype.stats = function(args) { } var getIndexName = function(collection, indexKey) { - if (!isObject(indexKey)) return undefined; + if (!isObject(indexKey)) + return undefined; var indexName; collection.getIndexes().forEach(function(spec) { if (friendlyEqual(spec.key, options.indexDetailsKey)) { @@ -1051,18 +1145,20 @@ DBCollection.prototype.stats = function(args) { return indexName; }; - var filterIndexName = - options.indexDetailsName || getIndexName(this, options.indexDetailsKey); + var filterIndexName = options.indexDetailsName || getIndexName(this, options.indexDetailsKey); var updateStats = function(stats, keepIndexDetails, indexName) { - if (!stats.indexDetails) return; + if (!stats.indexDetails) + return; if (!keepIndexDetails) { delete stats.indexDetails; return; } - if (!indexName) return; + if (!indexName) + return; for (var key in stats.indexDetails) { - if (key == indexName) continue; + if (key == indexName) + continue; delete stats.indexDetails[key]; } }; @@ -1078,26 +1174,25 @@ DBCollection.prototype.stats = function(args) { return res; }; -DBCollection.prototype.dataSize = function(){ +DBCollection.prototype.dataSize = function() { return this.stats().size; }; -DBCollection.prototype.storageSize = function(){ +DBCollection.prototype.storageSize = function() { return this.stats().storageSize; }; -DBCollection.prototype.totalIndexSize = function( verbose ){ +DBCollection.prototype.totalIndexSize = function(verbose) { var stats = this.stats(); - if (verbose){ - for (var ns in stats.indexSizes){ - print( ns + "\t" + stats.indexSizes[ns] ); + if (verbose) { + for (var ns in stats.indexSizes) { + print(ns + "\t" + stats.indexSizes[ns]); } } return stats.totalIndexSize; }; - -DBCollection.prototype.totalSize = function(){ +DBCollection.prototype.totalSize = function() { var total = this.storageSize(); var totalIndexSize = this.totalIndexSize(); if (totalIndexSize) { @@ -1106,33 +1201,31 @@ DBCollection.prototype.totalSize = function(){ return total; }; - -DBCollection.prototype.convertToCapped = function( bytes ){ - if ( ! bytes ) +DBCollection.prototype.convertToCapped = function(bytes) { + if (!bytes) throw Error("have to specify # of bytes"); - return this._dbCommand( { convertToCapped : this._shortName , size : bytes } ); + return this._dbCommand({convertToCapped: this._shortName, size: bytes}); }; -DBCollection.prototype.exists = function(){ - var res = this._db.runCommand( "listCollections", - { filter : { name : this._shortName } } ); - if ( res.ok ) { - var cursor = new DBCommandCursor( this._mongo, res ); - if ( !cursor.hasNext() ) +DBCollection.prototype.exists = function() { + var res = this._db.runCommand("listCollections", {filter: {name: this._shortName}}); + if (res.ok) { + var cursor = new DBCommandCursor(this._mongo, res); + if (!cursor.hasNext()) return null; return cursor.next(); } - if ( res.errmsg && res.errmsg.startsWith( "no such cmd" ) ) { - return this._db.system.namespaces.findOne( { name : this._fullName } ); + if (res.errmsg && res.errmsg.startsWith("no such cmd")) { + return this._db.system.namespaces.findOne({name: this._fullName}); } throw _getErrorWithCode(res, "listCollections failed: " + tojson(res)); }; -DBCollection.prototype.isCapped = function(){ +DBCollection.prototype.isCapped = function() { var e = this.exists(); - return ( e && e.options && e.options.capped ) ? true : false; + return (e && e.options && e.options.capped) ? true : false; }; // @@ -1175,7 +1268,9 @@ DBCollection.prototype.aggregate = function(pipeline, aggregateOptions) { // Assign the cleaned up options aggregateOptions = copy; // Create the initial command document - var cmd = {pipeline: pipeline}; + var cmd = { + pipeline: pipeline + }; Object.extend(cmd, aggregateOptions); if (!('cursor' in cmd)) { @@ -1190,28 +1285,30 @@ DBCollection.prototype.aggregate = function(pipeline, aggregateOptions) { var doAgg = function(cmd) { // if we don't have an out stage, we could run on a secondary // so we need to attach readPreference - return hasOutStage ? - this.runCommand("aggregate", cmd) : this.runReadCommand("aggregate", cmd); + return hasOutStage ? this.runCommand("aggregate", cmd) + : this.runReadCommand("aggregate", cmd); }.bind(this); var res = doAgg(cmd); - if (!res.ok - && (res.code == 17020 || res.errmsg == "unrecognized field \"cursor") - && !("cursor" in aggregateOptions)) { - // If the command failed because cursors aren't supported and the user didn't explicitly - // request a cursor, try again without requesting a cursor. - delete cmd.cursor; - - res = doAgg(cmd); - - if ('result' in res && !("cursor" in res)) { - // convert old-style output to cursor-style output - res.cursor = {ns: '', id: NumberLong(0)}; - res.cursor.firstBatch = res.result; - delete res.result; - } + if (!res.ok && (res.code == 17020 || res.errmsg == "unrecognized field \"cursor") && + !("cursor" in aggregateOptions)) { + // If the command failed because cursors aren't supported and the user didn't explicitly + // request a cursor, try again without requesting a cursor. + delete cmd.cursor; + + res = doAgg(cmd); + + if ('result' in res && !("cursor" in res)) { + // convert old-style output to cursor-style output + res.cursor = { + ns: '', + id: NumberLong(0) + }; + res.cursor.firstBatch = res.result; + delete res.result; } + } assert.commandWorked(res, "aggregate failed"); @@ -1222,38 +1319,38 @@ DBCollection.prototype.aggregate = function(pipeline, aggregateOptions) { return res; }; -DBCollection.prototype.group = function( params ){ +DBCollection.prototype.group = function(params) { params.ns = this._shortName; - return this._db.group( params ); + return this._db.group(params); }; -DBCollection.prototype.groupcmd = function( params ){ +DBCollection.prototype.groupcmd = function(params) { params.ns = this._shortName; - return this._db.groupcmd( params ); + return this._db.groupcmd(params); }; -MapReduceResult = function( db , o ){ - Object.extend( this , o ); +MapReduceResult = function(db, o) { + Object.extend(this, o); this._o = o; - this._keys = Object.keySet( o ); + this._keys = Object.keySet(o); this._db = db; - if ( this.result != null ) { - this._coll = this._db.getCollection( this.result ); + if (this.result != null) { + this._coll = this._db.getCollection(this.result); } }; -MapReduceResult.prototype._simpleKeys = function(){ +MapReduceResult.prototype._simpleKeys = function() { return this._o; }; -MapReduceResult.prototype.find = function(){ - if ( this.results ) +MapReduceResult.prototype.find = function() { + if (this.results) return this.results; - return DBCollection.prototype.find.apply( this._coll , arguments ); + return DBCollection.prototype.find.apply(this._coll, arguments); }; -MapReduceResult.prototype.drop = function(){ - if ( this._coll ) { +MapReduceResult.prototype.drop = function() { + if (this._coll) { return this._coll.drop(); } }; @@ -1261,30 +1358,38 @@ MapReduceResult.prototype.drop = function(){ /** * just for debugging really */ -MapReduceResult.prototype.convertToSingleObject = function(){ +MapReduceResult.prototype.convertToSingleObject = function() { var z = {}; var it = this.results != null ? this.results : this._coll.find(); - it.forEach( function(a){ z[a._id] = a.value; } ); + it.forEach(function(a) { + z[a._id] = a.value; + }); return z; }; -DBCollection.prototype.convertToSingleObject = function(valueField){ +DBCollection.prototype.convertToSingleObject = function(valueField) { var z = {}; - this.find().forEach( function(a){ z[a._id] = a[valueField]; } ); + this.find().forEach(function(a) { + z[a._id] = a[valueField]; + }); return z; }; /** * @param optional object of optional fields; */ -DBCollection.prototype.mapReduce = function( map , reduce , optionsOrOutString ){ - var c = { mapreduce : this._shortName , map : map , reduce : reduce }; - assert( optionsOrOutString , "need to supply an optionsOrOutString" ); +DBCollection.prototype.mapReduce = function(map, reduce, optionsOrOutString) { + var c = { + mapreduce: this._shortName, + map: map, + reduce: reduce + }; + assert(optionsOrOutString, "need to supply an optionsOrOutString"); - if ( typeof( optionsOrOutString ) == "string" ) + if (typeof(optionsOrOutString) == "string") c["out"] = optionsOrOutString; else - Object.extend( c , optionsOrOutString ); + Object.extend(c, optionsOrOutString); var raw; @@ -1296,41 +1401,41 @@ DBCollection.prototype.mapReduce = function( map , reduce , optionsOrOutString ) raw = this._db.runCommand(c); } - if ( ! raw.ok ){ + if (!raw.ok) { __mrerror__ = raw; throw _getErrorWithCode(raw, "map reduce failed:" + tojson(raw)); } - return new MapReduceResult( this._db , raw ); + return new MapReduceResult(this._db, raw); }; -DBCollection.prototype.toString = function(){ +DBCollection.prototype.toString = function() { return this.getFullName(); }; -DBCollection.prototype.toString = function(){ +DBCollection.prototype.toString = function() { return this.getFullName(); }; - DBCollection.prototype.tojson = DBCollection.prototype.toString; DBCollection.prototype.shellPrint = DBCollection.prototype.toString; -DBCollection.autocomplete = function(obj){ +DBCollection.autocomplete = function(obj) { var colls = DB.autocomplete(obj.getDB()); var ret = []; - for (var i=0; i<colls.length; i++){ + for (var i = 0; i < colls.length; i++) { var c = colls[i]; - if (c.length <= obj.getName().length) continue; - if (c.slice(0,obj.getName().length+1) != obj.getName()+'.') continue; + if (c.length <= obj.getName().length) + continue; + if (c.slice(0, obj.getName().length + 1) != obj.getName() + '.') + continue; - ret.push(c.slice(obj.getName().length+1)); + ret.push(c.slice(obj.getName().length + 1)); } return ret; }; - // Sharding additions /* @@ -1342,186 +1447,187 @@ Loading custom sharding extensions... true > var collection = db.getMongo().getCollection("foo.bar") -> collection.getShardDistribution() // prints statistics related to the collection's data distribution +> collection.getShardDistribution() // prints statistics related to the collection's data +distribution -> collection.getSplitKeysForChunks() // generates split points for all chunks in the collection, based on the +> collection.getSplitKeysForChunks() // generates split points for all chunks in the collection, +based on the // default maxChunkSize or alternately a specified chunk size > collection.getSplitKeysForChunks( 10 ) // Mb -> var splitter = collection.getSplitKeysForChunks() // by default, the chunks are not split, the keys are just - // found. A splitter function is returned which will actually +> var splitter = collection.getSplitKeysForChunks() // by default, the chunks are not split, the +keys are just + // found. A splitter function is returned which +will actually // do the splits. > splitter() // ! Actually executes the splits on the cluster ! */ -DBCollection.prototype.getShardDistribution = function(){ - - var stats = this.stats(); - - if( ! stats.sharded ){ - print( "Collection " + this + " is not sharded." ); - return; - } - - var config = this.getMongo().getDB("config"); +DBCollection.prototype.getShardDistribution = function() { - var numChunks = 0; + var stats = this.stats(); - for( var shard in stats.shards ){ + if (!stats.sharded) { + print("Collection " + this + " is not sharded."); + return; + } - var shardDoc = config.shards.findOne({ _id : shard }); + var config = this.getMongo().getDB("config"); - print( "\nShard " + shard + " at " + shardDoc.host ); + var numChunks = 0; - var shardStats = stats.shards[ shard ]; + for (var shard in stats.shards) { + var shardDoc = config.shards.findOne({_id: shard}); - var chunks = config.chunks.find({ _id : sh._collRE( this ), shard : shard }).toArray(); + print("\nShard " + shard + " at " + shardDoc.host); - numChunks += chunks.length; + var shardStats = stats.shards[shard]; - var estChunkData = shardStats.size / chunks.length; - var estChunkCount = Math.floor( shardStats.count / chunks.length ); + var chunks = config.chunks.find({_id: sh._collRE(this), shard: shard}).toArray(); - print( " data : " + sh._dataFormat( shardStats.size ) + - " docs : " + shardStats.count + - " chunks : " + chunks.length ); - print( " estimated data per chunk : " + sh._dataFormat( estChunkData ) ); - print( " estimated docs per chunk : " + estChunkCount ); + numChunks += chunks.length; - } + var estChunkData = shardStats.size / chunks.length; + var estChunkCount = Math.floor(shardStats.count / chunks.length); - print( "\nTotals" ); - print( " data : " + sh._dataFormat( stats.size ) + - " docs : " + stats.count + - " chunks : " + numChunks ); - for( var shard in stats.shards ){ + print(" data : " + sh._dataFormat(shardStats.size) + " docs : " + shardStats.count + + " chunks : " + chunks.length); + print(" estimated data per chunk : " + sh._dataFormat(estChunkData)); + print(" estimated docs per chunk : " + estChunkCount); + } - var shardStats = stats.shards[ shard ]; + print("\nTotals"); + print(" data : " + sh._dataFormat(stats.size) + " docs : " + stats.count + " chunks : " + + numChunks); + for (var shard in stats.shards) { + var shardStats = stats.shards[shard]; - var estDataPercent = Math.floor( shardStats.size / stats.size * 10000 ) / 100; - var estDocPercent = Math.floor( shardStats.count / stats.count * 10000 ) / 100; + var estDataPercent = Math.floor(shardStats.size / stats.size * 10000) / 100; + var estDocPercent = Math.floor(shardStats.count / stats.count * 10000) / 100; - print( " Shard " + shard + " contains " + estDataPercent + "% data, " + estDocPercent + "% docs in cluster, " + - "avg obj size on shard : " + sh._dataFormat( stats.shards[ shard ].avgObjSize ) ); - } + print(" Shard " + shard + " contains " + estDataPercent + "% data, " + estDocPercent + + "% docs in cluster, " + "avg obj size on shard : " + + sh._dataFormat(stats.shards[shard].avgObjSize)); + } - print( "\n" ); + print("\n"); }; +DBCollection.prototype.getSplitKeysForChunks = function(chunkSize) { -DBCollection.prototype.getSplitKeysForChunks = function( chunkSize ){ - - var stats = this.stats(); - - if( ! stats.sharded ){ - print( "Collection " + this + " is not sharded." ); - return; - } - - var config = this.getMongo().getDB("config"); - - if( ! chunkSize ){ - chunkSize = config.settings.findOne({ _id : "chunksize" }).value; - print( "Chunk size not set, using default of " + chunkSize + "MB" ); - } - else{ - print( "Using chunk size of " + chunkSize + "MB" ); - } - - var shardDocs = config.shards.find().toArray(); - - var allSplitPoints = {}; - var numSplits = 0; + var stats = this.stats(); - for( var i = 0; i < shardDocs.length; i++ ){ + if (!stats.sharded) { + print("Collection " + this + " is not sharded."); + return; + } - var shardDoc = shardDocs[i]; - var shard = shardDoc._id; - var host = shardDoc.host; - var sconn = new Mongo( host ); + var config = this.getMongo().getDB("config"); - var chunks = config.chunks.find({ _id : sh._collRE( this ), shard : shard }).toArray(); + if (!chunkSize) { + chunkSize = config.settings.findOne({_id: "chunksize"}).value; + print("Chunk size not set, using default of " + chunkSize + "MB"); + } else { + print("Using chunk size of " + chunkSize + "MB"); + } - print( "\nGetting split points for chunks on shard " + shard + " at " + host ); + var shardDocs = config.shards.find().toArray(); - var splitPoints = []; + var allSplitPoints = {}; + var numSplits = 0; - for( var j = 0; j < chunks.length; j++ ){ - var chunk = chunks[j]; - var result = sconn.getDB("admin").runCommand({ splitVector : this + "", min : chunk.min, max : chunk.max, maxChunkSize : chunkSize }); - if( ! result.ok ){ - print( " Had trouble getting split keys for chunk " + sh._pchunk( chunk ) + " :\n" ); - printjson( result ); - } - else{ - splitPoints = splitPoints.concat( result.splitKeys ); + for (var i = 0; i < shardDocs.length; i++) { + var shardDoc = shardDocs[i]; + var shard = shardDoc._id; + var host = shardDoc.host; + var sconn = new Mongo(host); - if( result.splitKeys.length > 0 ) - print( " Added " + result.splitKeys.length + " split points for chunk " + sh._pchunk( chunk ) ); - } - } + var chunks = config.chunks.find({_id: sh._collRE(this), shard: shard}).toArray(); - print( "Total splits for shard " + shard + " : " + splitPoints.length ); + print("\nGetting split points for chunks on shard " + shard + " at " + host); - numSplits += splitPoints.length; - allSplitPoints[ shard ] = splitPoints; + var splitPoints = []; - } + for (var j = 0; j < chunks.length; j++) { + var chunk = chunks[j]; + var result = sconn.getDB("admin").runCommand( + {splitVector: this + "", min: chunk.min, max: chunk.max, maxChunkSize: chunkSize}); + if (!result.ok) { + print(" Had trouble getting split keys for chunk " + sh._pchunk(chunk) + " :\n"); + printjson(result); + } else { + splitPoints = splitPoints.concat(result.splitKeys); - // Get most recent migration - var migration = config.changelog.find({ what : /^move.*/ }).sort({ time : -1 }).limit( 1 ).toArray(); - if( migration.length == 0 ) - print( "\nNo migrations found in changelog." ); - else { - migration = migration[0]; - print( "\nMost recent migration activity was on " + migration.ns + " at " + migration.time ); - } + if (result.splitKeys.length > 0) + print(" Added " + result.splitKeys.length + " split points for chunk " + + sh._pchunk(chunk)); + } + } - var admin = this.getMongo().getDB("admin"); - var coll = this; - var splitFunction = function(){ + print("Total splits for shard " + shard + " : " + splitPoints.length); - // Turn off the balancer, just to be safe - print( "Turning off balancer..." ); - config.settings.update({ _id : "balancer" }, { $set : { stopped : true } }, true ); - print( "Sleeping for 30s to allow balancers to detect change. To be extra safe, check config.changelog" + - " for recent migrations." ); - sleep( 30000 ); + numSplits += splitPoints.length; + allSplitPoints[shard] = splitPoints; + } - for( var shard in allSplitPoints ){ - for( var i = 0; i < allSplitPoints[ shard ].length; i++ ){ - var splitKey = allSplitPoints[ shard ][i]; - print( "Splitting at " + tojson( splitKey ) ); - printjson( admin.runCommand({ split : coll + "", middle : splitKey }) ); - } - } + // Get most recent migration + var migration = config.changelog.find({what: /^move.*/}).sort({time: -1}).limit(1).toArray(); + if (migration.length == 0) + print("\nNo migrations found in changelog."); + else { + migration = migration[0]; + print("\nMost recent migration activity was on " + migration.ns + " at " + migration.time); + } + + var admin = this.getMongo().getDB("admin"); + var coll = this; + var splitFunction = function() { + + // Turn off the balancer, just to be safe + print("Turning off balancer..."); + config.settings.update({_id: "balancer"}, {$set: {stopped: true}}, true); + print( + "Sleeping for 30s to allow balancers to detect change. To be extra safe, check config.changelog" + + " for recent migrations."); + sleep(30000); + + for (var shard in allSplitPoints) { + for (var i = 0; i < allSplitPoints[shard].length; i++) { + var splitKey = allSplitPoints[shard][i]; + print("Splitting at " + tojson(splitKey)); + printjson(admin.runCommand({split: coll + "", middle: splitKey})); + } + } - print( "Turning the balancer back on." ); - config.settings.update({ _id : "balancer" }, { $set : { stopped : false } } ); - sleep( 1 ); - }; + print("Turning the balancer back on."); + config.settings.update({_id: "balancer"}, {$set: {stopped: false}}); + sleep(1); + }; - splitFunction.getSplitPoints = function(){ return allSplitPoints; }; + splitFunction.getSplitPoints = function() { + return allSplitPoints; + }; - print( "\nGenerated " + numSplits + " split keys, run output function to perform splits.\n" + - " ex : \n" + - " > var splitter = <collection>.getSplitKeysForChunks()\n" + - " > splitter() // Execute splits on cluster !\n" ); + print("\nGenerated " + numSplits + " split keys, run output function to perform splits.\n" + + " ex : \n" + " > var splitter = <collection>.getSplitKeysForChunks()\n" + + " > splitter() // Execute splits on cluster !\n"); - return splitFunction; + return splitFunction; }; -DBCollection.prototype.setSlaveOk = function( value ) { - if( value == undefined ) value = true; +DBCollection.prototype.setSlaveOk = function(value) { + if (value == undefined) + value = true; this._slaveOk = value; }; DBCollection.prototype.getSlaveOk = function() { - if (this._slaveOk != undefined) return this._slaveOk; + if (this._slaveOk != undefined) + return this._slaveOk; return this._db.getSlaveOk(); }; @@ -1535,18 +1641,17 @@ DBCollection.prototype.getQueryOptions = function() { * Returns a PlanCache for the collection. */ DBCollection.prototype.getPlanCache = function() { - return new PlanCache( this ); + return new PlanCache(this); }; // Overrides connection-level settings. // -DBCollection.prototype.setWriteConcern = function( wc ) { - if ( wc instanceof WriteConcern ) { +DBCollection.prototype.setWriteConcern = function(wc) { + if (wc instanceof WriteConcern) { this._writeConcern = wc; - } - else { - this._writeConcern = new WriteConcern( wc ); + } else { + this._writeConcern = new WriteConcern(wc); } }; @@ -1554,7 +1659,6 @@ DBCollection.prototype.getWriteConcern = function() { if (this._writeConcern) return this._writeConcern; - if (this._db.getWriteConcern()) return this._db.getWriteConcern(); @@ -1611,33 +1715,37 @@ DBCollection.prototype.count = function(query, options) { }; /** -* The distinct command returns returns a list of distinct values for the given key across a collection. +* The distinct command returns returns a list of distinct values for the given key across a +*collection. * * @method * @param {string} key Field of the document to find distinct values for. -* @param {object} query The query for filtering the set of documents to which we apply the distinct filter. +* @param {object} query The query for filtering the set of documents to which we apply the distinct +*filter. * @param {object} [options=null] Optional settings. * @param {number} [options.maxTimeMS=null] The maximum amount of time to allow the query to run. * @return {object} */ -DBCollection.prototype.distinct = function(keyString, query, options){ +DBCollection.prototype.distinct = function(keyString, query, options) { var opts = Object.extend({}, options || {}); var keyStringType = typeof keyString; var queryType = typeof query; if (keyStringType != "string") { - throw new Error("The first argument to the distinct command must be a string but was a " + keyStringType); + throw new Error("The first argument to the distinct command must be a string but was a " + + keyStringType); } if (query != null && queryType != "object") { - throw new Error("The query argument to the distinct command must be a document but was a " + queryType); + throw new Error("The query argument to the distinct command must be a document but was a " + + queryType); } // Distinct command var cmd = { - distinct : this.getName(), - key : keyString, - query : query || {} + distinct: this.getName(), + key: keyString, + query: query || {} }; // Set maxTimeMS if provided @@ -1654,8 +1762,8 @@ DBCollection.prototype.distinct = function(keyString, query, options){ return res.values; }; -DBCollection.prototype._distinct = function( keyString , query ){ - return this._dbReadCommand( { distinct : this._shortName , key : keyString , query : query || {} } ); +DBCollection.prototype._distinct = function(keyString, query) { + return this._dbReadCommand({distinct: this._shortName, key: keyString, query: query || {}}); }; /** @@ -1663,8 +1771,8 @@ DBCollection.prototype._distinct = function( keyString , query ){ * Holds a reference to the collection. * Proxy for planCache* commands. */ -if ( ( typeof PlanCache ) == "undefined" ){ - PlanCache = function( collection ){ +if ((typeof PlanCache) == "undefined") { + PlanCache = function(collection) { this._collection = collection; }; } @@ -1673,15 +1781,14 @@ if ( ( typeof PlanCache ) == "undefined" ){ * Name of PlanCache. * Same as collection. */ -PlanCache.prototype.getName = function(){ +PlanCache.prototype.getName = function() { return this._collection.getName(); }; - /** * toString prints the name of the collection */ -PlanCache.prototype.toString = function(){ +PlanCache.prototype.toString = function() { return "PlanCache for collection " + this.getName() + '. Type help() for more info.'; }; @@ -1690,7 +1797,7 @@ PlanCache.prototype.shellPrint = PlanCache.prototype.toString; /** * Displays help for a PlanCache object. */ -PlanCache.prototype.help = function () { +PlanCache.prototype.help = function() { var shortName = this.getName(); print("PlanCache help"); print("\tdb." + shortName + ".getPlanCache().help() - show PlanCache help"); diff --git a/src/mongo/shell/crud_api.js b/src/mongo/shell/crud_api.js index dcd1edf9854..7c83b5093aa 100644 --- a/src/mongo/shell/crud_api.js +++ b/src/mongo/shell/crud_api.js @@ -8,10 +8,8 @@ DBCollection.prototype._createWriteConcern = function(options) { } // Only merge in write concern options if at least one is specified in options - if (options.w != null - || options.wtimeout != null - || options.j != null - || options.fsync != null) { + if (options.w != null || options.wtimeout != null || options.j != null || + options.fsync != null) { writeConcern = {}; writeConcernOptions.forEach(function(wc) { @@ -29,11 +27,13 @@ DBCollection.prototype._createWriteConcern = function(options) { * Otherwise, returns the same object passed. */ DBCollection.prototype.addIdIfNeeded = function(obj) { - if ( typeof( obj._id ) == "undefined" && ! Array.isArray( obj ) ){ - var tmp = obj; // don't want to modify input - obj = {_id: new ObjectId()}; + if (typeof(obj._id) == "undefined" && !Array.isArray(obj)) { + var tmp = obj; // don't want to modify input + obj = { + _id: new ObjectId() + }; - for (var key in tmp){ + for (var key in tmp) { obj[key] = tmp[key]; } } @@ -74,20 +74,20 @@ DBCollection.prototype.bulkWrite = function(operations, options) { var writeConcern = this._createWriteConcern(opts); // Result - var result = {acknowledged: (writeConcern && writeConcern.w == 0) ? false: true}; + var result = { + acknowledged: (writeConcern && writeConcern.w == 0) ? false : true + }; // Use bulk operation API already in the shell - var bulkOp = opts.ordered - ? this.initializeOrderedBulkOp() - : this.initializeUnorderedBulkOp(); + var bulkOp = opts.ordered ? this.initializeOrderedBulkOp() : this.initializeUnorderedBulkOp(); // Contains all inserted _ids var insertedIds = {}; // For each of the operations we need to add the op to the bulk operations.forEach(function(op, index) { - if(op.insertOne) { - if(!op.insertOne.document) { + if (op.insertOne) { + if (!op.insertOne.document) { throw new Error('insertOne bulkWrite operation expects the document field'); } @@ -97,63 +97,63 @@ DBCollection.prototype.bulkWrite = function(operations, options) { insertedIds[index] = op.insertOne.document._id; // Translate operation to bulk operation bulkOp.insert(op.insertOne.document); - } else if(op.updateOne) { - if(!op.updateOne.filter) { + } else if (op.updateOne) { + if (!op.updateOne.filter) { throw new Error('updateOne bulkWrite operation expects the filter field'); } - if(!op.updateOne.update) { + if (!op.updateOne.update) { throw new Error('updateOne bulkWrite operation expects the update field'); } // Translate operation to bulk operation var operation = bulkOp.find(op.updateOne.filter); - if(op.updateOne.upsert) { + if (op.updateOne.upsert) { operation = operation.upsert(); } operation.updateOne(op.updateOne.update); - } else if(op.updateMany) { - if(!op.updateMany.filter) { + } else if (op.updateMany) { + if (!op.updateMany.filter) { throw new Error('updateMany bulkWrite operation expects the filter field'); } - if(!op.updateMany.update) { + if (!op.updateMany.update) { throw new Error('updateMany bulkWrite operation expects the update field'); } // Translate operation to bulk operation var operation = bulkOp.find(op.updateMany.filter); - if(op.updateMany.upsert) { + if (op.updateMany.upsert) { operation = operation.upsert(); } operation.update(op.updateMany.update); - } else if(op.replaceOne) { - if(!op.replaceOne.filter) { + } else if (op.replaceOne) { + if (!op.replaceOne.filter) { throw new Error('replaceOne bulkWrite operation expects the filter field'); } - if(!op.replaceOne.replacement) { + if (!op.replaceOne.replacement) { throw new Error('replaceOne bulkWrite operation expects the replacement field'); } // Translate operation to bulkOp operation var operation = bulkOp.find(op.replaceOne.filter); - if(op.replaceOne.upsert) { + if (op.replaceOne.upsert) { operation = operation.upsert(); } operation.replaceOne(op.replaceOne.replacement); - } else if(op.deleteOne) { - if(!op.deleteOne.filter) { + } else if (op.deleteOne) { + if (!op.deleteOne.filter) { throw new Error('deleteOne bulkWrite operation expects the filter field'); } // Translate operation to bulkOp operation bulkOp.find(op.deleteOne.filter).removeOne(); - } else if(op.deleteMany) { - if(!op.deleteMany.filter) { + } else if (op.deleteMany) { + if (!op.deleteMany.filter) { throw new Error('deleteMany bulkWrite operation expects the filter field'); } @@ -164,7 +164,7 @@ DBCollection.prototype.bulkWrite = function(operations, options) { // Execute bulkOp operation var response = bulkOp.execute(writeConcern); - if(!result.acknowledged) { + if (!result.acknowledged) { return result; } @@ -206,7 +206,9 @@ DBCollection.prototype.insertOne = function(document, options) { var writeConcern = this._createWriteConcern(opts); // Result - var result = {acknowledged: (writeConcern && writeConcern.w == 0) ? false: true}; + var result = { + acknowledged: (writeConcern && writeConcern.w == 0) ? false : true + }; // Use bulk operation API already in the shell var bulk = this.initializeOrderedBulkOp(); @@ -216,11 +218,11 @@ DBCollection.prototype.insertOne = function(document, options) { // Execute insert bulk.execute(writeConcern); } catch (err) { - if(err.hasWriteErrors()) { + if (err.hasWriteErrors()) { throw err.getWriteErrorAt(0); } - if(err.hasWriteConcernError()) { + if (err.hasWriteConcernError()) { throw err.getWriteConcernError(); } @@ -263,12 +265,12 @@ DBCollection.prototype.insertMany = function(documents, options) { var writeConcern = this._createWriteConcern(opts); // Result - var result = {acknowledged: (writeConcern && writeConcern.w == 0) ? false: true}; + var result = { + acknowledged: (writeConcern && writeConcern.w == 0) ? false : true + }; // Use bulk operation API already in the shell - var bulk = opts.ordered - ? this.initializeOrderedBulkOp() - : this.initializeUnorderedBulkOp(); + var bulk = opts.ordered ? this.initializeOrderedBulkOp() : this.initializeUnorderedBulkOp(); // Add all operations to the bulk operation documents.forEach(function(doc) { @@ -284,7 +286,7 @@ DBCollection.prototype.insertMany = function(documents, options) { // Set all the created inserts result.insertedIds = documents.map(function(x) { - return x._id; + return x._id; }); // Return the result @@ -309,7 +311,9 @@ DBCollection.prototype.deleteOne = function(filter, options) { var writeConcern = this._createWriteConcern(opts); // Result - var result = {acknowledged: (writeConcern && writeConcern.w == 0) ? false: true}; + var result = { + acknowledged: (writeConcern && writeConcern.w == 0) ? false : true + }; // Use bulk operation API already in the shell var bulk = this.initializeOrderedBulkOp(); @@ -321,11 +325,11 @@ DBCollection.prototype.deleteOne = function(filter, options) { // Remove the first document that matches the selector var r = bulk.execute(writeConcern); } catch (err) { - if(err.hasWriteErrors()) { + if (err.hasWriteErrors()) { throw err.getWriteErrorAt(0); } - if(err.hasWriteConcernError()) { + if (err.hasWriteConcernError()) { throw err.getWriteConcernError(); } @@ -358,7 +362,9 @@ DBCollection.prototype.deleteMany = function(filter, options) { var writeConcern = this._createWriteConcern(opts); // Result - var result = {acknowledged: (writeConcern && writeConcern.w == 0) ? false: true}; + var result = { + acknowledged: (writeConcern && writeConcern.w == 0) ? false : true + }; // Use bulk operation API already in the shell var bulk = this.initializeOrderedBulkOp(); @@ -370,11 +376,11 @@ DBCollection.prototype.deleteMany = function(filter, options) { // Remove all documents that matche the selector var r = bulk.execute(writeConcern); } catch (err) { - if(err.hasWriteErrors()) { + if (err.hasWriteErrors()) { throw err.getWriteErrorAt(0); } - if(err.hasWriteConcernError()) { + if (err.hasWriteConcernError()) { throw err.getWriteConcernError(); } @@ -408,15 +414,17 @@ DBCollection.prototype.replaceOne = function(filter, replacement, options) { // Check if first key in update statement contains a $ var keys = Object.keys(replacement); // Check if first key does not have the $ - if(keys.length > 0 && keys[0][0] == "$") { - throw new Error('the replace operation document must not contain atomic operators'); + if (keys.length > 0 && keys[0][0] == "$") { + throw new Error('the replace operation document must not contain atomic operators'); } // Get the write concern var writeConcern = this._createWriteConcern(opts); // Result - var result = {acknowledged: (writeConcern && writeConcern.w == 0) ? false: true }; + var result = { + acknowledged: (writeConcern && writeConcern.w == 0) ? false : true + }; // Use bulk operation API already in the shell var bulk = this.initializeOrderedBulkOp(); @@ -433,11 +441,11 @@ DBCollection.prototype.replaceOne = function(filter, replacement, options) { // Replace the document var r = bulk.execute(writeConcern); } catch (err) { - if(err.hasWriteErrors()) { + if (err.hasWriteErrors()) { throw err.getWriteErrorAt(0); } - if(err.hasWriteConcernError()) { + if (err.hasWriteConcernError()) { throw err.getWriteConcernError(); } @@ -476,20 +484,22 @@ DBCollection.prototype.updateOne = function(filter, update, options) { // Check if first key in update statement contains a $ var keys = Object.keys(update); - if(keys.length == 0) { - throw new Error("the update operation document must contain at least one atomic operator"); + if (keys.length == 0) { + throw new Error("the update operation document must contain at least one atomic operator"); } // Check if first key does not have the $ - if(keys[0][0] != "$") { - throw new Error('the update operation document must contain atomic operators'); + if (keys[0][0] != "$") { + throw new Error('the update operation document must contain atomic operators'); } // Get the write concern var writeConcern = this._createWriteConcern(opts); // Result - var result = {acknowledged: (writeConcern && writeConcern.w == 0) ? false: true}; + var result = { + acknowledged: (writeConcern && writeConcern.w == 0) ? false : true + }; // Use bulk operation API already in the shell var bulk = this.initializeOrderedBulkOp(); @@ -506,11 +516,11 @@ DBCollection.prototype.updateOne = function(filter, update, options) { // Update the first document that matches the selector var r = bulk.execute(writeConcern); } catch (err) { - if(err.hasWriteErrors()) { + if (err.hasWriteErrors()) { throw err.getWriteErrorAt(0); } - if(err.hasWriteConcernError()) { + if (err.hasWriteConcernError()) { throw err.getWriteConcernError(); } @@ -549,20 +559,22 @@ DBCollection.prototype.updateMany = function(filter, update, options) { // Check if first key in update statement contains a $ var keys = Object.keys(update); - if(keys.length == 0) { - throw new Error("the update operation document must contain at least one atomic operator"); + if (keys.length == 0) { + throw new Error("the update operation document must contain at least one atomic operator"); } // Check if first key does not have the $ - if(keys[0][0] != "$") { - throw new Error('the update operation document must contain atomic operators'); + if (keys[0][0] != "$") { + throw new Error('the update operation document must contain atomic operators'); } // Get the write concern var writeConcern = this._createWriteConcern(opts); // Result - var result = {acknowledged: (writeConcern && writeConcern.w == 0) ? false: true}; + var result = { + acknowledged: (writeConcern && writeConcern.w == 0) ? false : true + }; // Use bulk operation API already in the shell var bulk = this.initializeOrderedBulkOp(); @@ -579,11 +591,11 @@ DBCollection.prototype.updateMany = function(filter, update, options) { // Update all documents that match the selector var r = bulk.execute(writeConcern); } catch (err) { - if(err.hasWriteErrors()) { + if (err.hasWriteErrors()) { throw err.getWriteErrorAt(0); } - if(err.hasWriteConcernError()) { + if (err.hasWriteConcernError()) { throw err.getWriteConcernError(); } @@ -612,14 +624,18 @@ DBCollection.prototype.updateMany = function(filter, update, options) { * @param {object} filter Document selection filter. * @param {object} [options=null] Optional settings. * @param {object} [options.projection=null] Limits the fields to return for all matching documents. -* @param {object} [options.sort=null] Determines which document the operation modifies if the query selects multiple documents. +* @param {object} [options.sort=null] Determines which document the operation modifies if the query +*selects multiple documents. * @param {number} [options.maxTimeMS=null] The maximum amount of time to allow the query to run. * @return {object} */ DBCollection.prototype.findOneAndDelete = function(filter, options) { var opts = Object.extend({}, options || {}); // Set up the command - var cmd = {query: filter, remove: true}; + var cmd = { + query: filter, + remove: true + }; if (opts.sort) { cmd.sort = opts.sort; @@ -646,17 +662,20 @@ DBCollection.prototype.findOneAndDelete = function(filter, options) { }; /** -* Find a document and replace it in one atomic operation, requires a write lock for the duration of the operation. +* Find a document and replace it in one atomic operation, requires a write lock for the duration of +*the operation. * * @method * @param {object} filter Document selection filter. * @param {object} replacement Document replacing the matching document. * @param {object} [options=null] Optional settings. * @param {object} [options.projection=null] Limits the fields to return for all matching documents. -* @param {object} [options.sort=null] Determines which document the operation modifies if the query selects multiple documents. +* @param {object} [options.sort=null] Determines which document the operation modifies if the query +*selects multiple documents. * @param {number} [options.maxTimeMS=null] The maximum amount of time to allow the query to run. * @param {boolean} [options.upsert=false] Upsert the document if it does not exist. -* @param {boolean} [options.returnNewDocument=false] When true, returns the updated document rather than the original. The default is false. +* @param {boolean} [options.returnNewDocument=false] When true, returns the updated document rather +*than the original. The default is false. * @return {object} */ DBCollection.prototype.findOneAndReplace = function(filter, replacement, options) { @@ -665,12 +684,15 @@ DBCollection.prototype.findOneAndReplace = function(filter, replacement, options // Check if first key in update statement contains a $ var keys = Object.keys(replacement); // Check if first key does not have the $ - if(keys.length > 0 && keys[0][0] == "$") { - throw new Error("the replace operation document must not contain atomic operators"); + if (keys.length > 0 && keys[0][0] == "$") { + throw new Error("the replace operation document must not contain atomic operators"); } // Set up the command - var cmd = {query: filter, update: replacement}; + var cmd = { + query: filter, + update: replacement + }; if (opts.sort) { cmd.sort = opts.sort; } @@ -700,17 +722,20 @@ DBCollection.prototype.findOneAndReplace = function(filter, replacement, options }; /** -* Find a document and update it in one atomic operation, requires a write lock for the duration of the operation. +* Find a document and update it in one atomic operation, requires a write lock for the duration of +*the operation. * * @method * @param {object} filter Document selection filter. * @param {object} update Update operations to be performed on the document * @param {object} [options=null] Optional settings. * @param {object} [options.projection=null] Limits the fields to return for all matching documents. -* @param {object} [options.sort=null] Determines which document the operation modifies if the query selects multiple documents. +* @param {object} [options.sort=null] Determines which document the operation modifies if the query +*selects multiple documents. * @param {number} [options.maxTimeMS=null] The maximum amount of time to allow the query to run. * @param {boolean} [options.upsert=false] Upsert the document if it does not exist. -* @param {boolean} [options.returnNewDocument=false] When true, returns the updated document rather than the original. The default is false. +* @param {boolean} [options.returnNewDocument=false] When true, returns the updated document rather +*than the original. The default is false. * @return {object} */ DBCollection.prototype.findOneAndUpdate = function(filter, update, options) { @@ -718,17 +743,20 @@ DBCollection.prototype.findOneAndUpdate = function(filter, update, options) { // Check if first key in update statement contains a $ var keys = Object.keys(update); - if(keys.length == 0) { - throw new Error("the update operation document must contain at least one atomic operator"); + if (keys.length == 0) { + throw new Error("the update operation document must contain at least one atomic operator"); } // Check if first key does not have the $ - if(keys[0][0] != "$") { - throw new Error("the update operation document must contain atomic operators"); + if (keys[0][0] != "$") { + throw new Error("the update operation document must contain atomic operators"); } // Set up the command - var cmd = {query: filter, update: update}; + var cmd = { + query: filter, + update: update + }; if (opts.sort) { cmd.sort = opts.sort; } diff --git a/src/mongo/shell/db.js b/src/mongo/shell/db.js index b4a5739c265..6330b9abe32 100644 --- a/src/mongo/shell/db.js +++ b/src/mongo/shell/db.js @@ -4,1625 +4,1696 @@ var DB; (function() { -if (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.getSiblingDB = function( name ){ - return this.getMongo().getDB( name ); -}; - -DB.prototype.getSisterDB = DB.prototype.getSiblingDB; - -DB.prototype.getName = function(){ - return this._name; -}; - -DB.prototype.stats = function(scale){ - return this.runCommand( { dbstats : 1 , scale : scale } ); -}; - -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; - var res = this.runCommand( c ); - if ( ! res.ok ) - throw _getErrorWithCode(res, res.errmsg); - return res.help; -}; - - // utility to attach readPreference if needed. - DB.prototype._attachReadPreferenceToCommand = function (cmdObj, readPref) { - "use strict"; - // if the user has not set a readpref, return the original cmdObj - if ((readPref === null) || typeof(readPref) !== "object") { - return cmdObj; - } - - // if user specifies $readPreference manually, then don't change it - if (cmdObj.hasOwnProperty("$readPreference")) { - return cmdObj; - } - - // copy object so we don't mutate the original - var clonedCmdObj = Object.extend({}, cmdObj); - // The server selection spec mandates that the key is '$query', but - // the shell has historically used 'query'. The server accepts both, - // so we maintain the existing behavior - var cmdObjWithReadPref = { query: clonedCmdObj, $readPreference: readPref }; - return cmdObjWithReadPref; - }; - - // if someone passes i.e. runCommand("foo", {bar: "baz"} - // we merge it in to runCommand({foo: 1, bar: "baz"} - // this helper abstracts that logic. - DB.prototype._mergeCommandOptions = function(commandName, extraKeys) { - "use strict"; - var mergedCmdObj = {}; - mergedCmdObj[commandName] = 1; - - if (typeof(extraKeys) === "object") { - // this will traverse the prototype chain of extra, but keeping - // to maintain legacy behavior - for (var key in extraKeys) { - mergedCmdObj[key] = extraKeys[key]; - } - } - return mergedCmdObj; - }; - - // Like runCommand but applies readPreference if one has been set - // on the connection. Also sets slaveOk if a (non-primary) readPref has been set. - DB.prototype.runReadCommand = function (obj, extra, queryOptions) { - "use strict"; - - // Support users who call this function with a string commandName, e.g. - // db.runReadCommand("commandName", {arg1: "value", arg2: "value"}). - var mergedObj = (typeof(obj) === "string") ? this._mergeCommandOptions(obj, extra) : obj; - var cmdObjWithReadPref = - this._attachReadPreferenceToCommand(mergedObj, - this.getMongo().getReadPref()); - - var options = (typeof(queryOptions) !== "undefined") ? queryOptions : this.getQueryOptions(); - var readPrefMode = this.getMongo().getReadPrefMode(); - - // Set slaveOk if readPrefMode has been explicitly set with a readPreference other than - // primary. - if (!!readPrefMode && readPrefMode !== "primary") { - options |= 4; - } - - // The 'extra' parameter is not used as we have already created a merged command object. - return this.runCommand(cmdObjWithReadPref, null, options); - }; - - // runCommand uses this impl to actually execute the command - DB.prototype._runCommandImpl = function(name, obj, options){ - return this.getMongo().runCommand(name, obj, options); - }; - - DB.prototype.runCommand = function( obj, extra, queryOptions ){ - var mergedObj = (typeof(obj) === "string") ? this._mergeCommandOptions(obj, extra) : obj; - // if options were passed (i.e. because they were overridden on a collection), use them. - // Otherwise use getQueryOptions. - var options = (typeof(queryOptions) !== "undefined") ? queryOptions : this.getQueryOptions(); - var res; - try { - res = this._runCommandImpl(this._name, mergedObj, options); - } - catch (ex) { - // When runCommand flowed through query, a connection error resulted in the message - // "error doing query: failed". Even though this message is arguably incorrect - // for a command failing due to a connection failure, we preserve it for backwards - // compatibility. See SERVER-18334 for details. - if (ex.message.indexOf("network error") >= 0) { - throw new Error("error doing query: failed: " + ex.message); - } - throw ex; - } - return res; - }; - -DB.prototype.runCommandWithMetadata = function(commandName, commandArgs, metadata) { - return this.getMongo().runCommandWithMetadata(this._name, commandName, metadata, commandArgs); -}; - -DB.prototype._dbCommand = DB.prototype.runCommand; -DB.prototype._dbReadCommand = DB.prototype.runReadCommand; - -DB.prototype.adminCommand = function( obj, extra ){ - if ( this._name == "admin" ) - return this.runCommand( obj, extra ); - return this.getSiblingDB( "admin" ).runCommand( obj, extra ); -}; - -DB.prototype._adminCommand = DB.prototype.adminCommand; // alias old name - -/** - 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> - <li> usePowerOf2Sizes: if true, set usePowerOf2Sizes allocation for the collection.</li> - <li> - storageEngine: BSON document containing storage engine specific options. Format: - { - storageEngine: { - storageEngine1: { - ... - }, - storageEngine2: { - ... - }, - ... - } - } - </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 || {}; - - // We have special handling for the 'flags' field, and provide sugar for specific flags. If the - // user specifies any flags we send the field in the command. Otherwise, we leave it blank and - // use the server's defaults. - var sendFlags = false; - var flags = 0; - if (options.usePowerOf2Sizes != undefined) { - print("WARNING: The 'usePowerOf2Sizes' flag is ignored in 3.0 and higher as all MMAPv1 " - + "collections use fixed allocation sizes unless the 'noPadding' flag is specified"); - - sendFlags = true; - if (options.usePowerOf2Sizes) { - flags |= 1; // Flag_UsePowerOf2Sizes - } - delete options.usePowerOf2Sizes; - } - if (options.noPadding != undefined) { - sendFlags = true; - if (options.noPadding) { - flags |= 2; // Flag_NoPadding - } - delete options.noPadding; + if (DB === undefined) { + DB = function(mongo, name) { + this._mongo = mongo; + this._name = name; + }; } - // New flags must be added above here. - if (sendFlags) { - if (options.flags != undefined) - throw Error("Can't set 'flags' with either 'usePowerOf2Sizes' or 'noPadding'"); - options.flags = flags; - } + DB.prototype.getMongo = function() { + assert(this._mongo, "why no mongo!"); + return this._mongo; + }; - var cmd = { create: name }; - Object.extend(cmd, options); - - return this._dbCommand(cmd); -}; - -/** - * @deprecated use getProfilingStatus - * Returns the current profiling level of this database - * @return SOMETHING_FIXME or null on error - */ -DB.prototype.getProfilingLevel = function() { - var res = assert.commandWorked(this._dbCommand( { profile: -1 } )); - return res ? res.was : null; -}; - -/** - * @return the current profiling status - * example { was : 0, slowms : 100 } - * @return SOMETHING_FIXME or null on error - */ -DB.prototype.getProfilingStatus = function() { - var res = this._dbCommand( { profile: -1 } ); - if ( ! res.ok ) - throw _getErrorWithCode(res, "profile command failed: " + tojson(res)); - delete res.ok; - return res; -}; - - -/** - 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 Error("dropDatabase doesn't take arguments"); - return this._dbCommand( { dropDatabase: 1 } ); -}; - -/** - * Shuts down the database. Must be run while using the admin database. - * @param opts Options for shutdown. Possible options are: - * - force: (boolean) if the server should shut down, even if there is no - * up-to-date slave - * - timeoutSecs: (number) the server will continue checking over timeoutSecs - * if any other servers have caught up enough for it to shut down. - */ -DB.prototype.shutdownServer = function(opts) { - if( "admin" != this._name ){ - return "shutdown command only works with the admin database; try 'use admin'"; - } + DB.prototype.getSiblingDB = function(name) { + return this.getMongo().getDB(name); + }; - var cmd = {'shutdown' : 1}; - opts = opts || {}; - for (var o in opts) { - cmd[o] = opts[o]; - } + DB.prototype.getSisterDB = DB.prototype.getSiblingDB; - try { - var res = this.runCommand(cmd); - if (!res.ok) { - throw _getErrorWithCode(res, 'shutdownServer failed: ' + tojson(res)); + DB.prototype.getName = function() { + return this._name; + }; + + DB.prototype.stats = function(scale) { + return this.runCommand({dbstats: 1, scale: scale}); + }; + + 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; + var res = this.runCommand(c); + if (!res.ok) + throw _getErrorWithCode(res, res.errmsg); + return res.help; + }; + + // utility to attach readPreference if needed. + DB.prototype._attachReadPreferenceToCommand = function(cmdObj, readPref) { + "use strict"; + // if the user has not set a readpref, return the original cmdObj + if ((readPref === null) || typeof(readPref) !== "object") { + return cmdObj; } - throw Error('shutdownServer failed: server is still up.'); - } - catch (e) { - // we expect the command to not return a response, as the server will shut down immediately. - if (e.message.indexOf("error doing query: failed") >= 0) { - print('server should be down...'); - return; + + // if user specifies $readPreference manually, then don't change it + if (cmdObj.hasOwnProperty("$readPreference")) { + return cmdObj; } - throw e; - } -}; - -/** - 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 ); - 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 || {}; - 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, username, password, mechanism) { - assert( isString(fromdb) && fromdb.length ); - assert( isString(todb) && todb.length ); - fromhost = fromhost || ""; - - if (!mechanism) { - mechanism = this._getDefaultAuthenticationMechanism(); - } - assert(mechanism == "SCRAM-SHA-1" || mechanism == "MONGODB-CR"); - // Check for no auth or copying from localhost - if (!username || !password || fromhost == "") { - return this._adminCommand( { copydb:1, fromhost:fromhost, fromdb:fromdb, todb:todb } ); - } + // copy object so we don't mutate the original + var clonedCmdObj = Object.extend({}, cmdObj); + // The server selection spec mandates that the key is '$query', but + // the shell has historically used 'query'. The server accepts both, + // so we maintain the existing behavior + var cmdObjWithReadPref = { + query: clonedCmdObj, + $readPreference: readPref + }; + return cmdObjWithReadPref; + }; - // Use the copyDatabase native helper for SCRAM-SHA-1 - if (mechanism == "SCRAM-SHA-1") { - return this.getMongo().copyDatabaseWithSCRAM(fromdb, todb, fromhost, username, password); - } + // if someone passes i.e. runCommand("foo", {bar: "baz"} + // we merge it in to runCommand({foo: 1, bar: "baz"} + // this helper abstracts that logic. + DB.prototype._mergeCommandOptions = function(commandName, extraKeys) { + "use strict"; + var mergedCmdObj = {}; + mergedCmdObj[commandName] = 1; + + if (typeof(extraKeys) === "object") { + // this will traverse the prototype chain of extra, but keeping + // to maintain legacy behavior + for (var key in extraKeys) { + mergedCmdObj[key] = extraKeys[key]; + } + } + return mergedCmdObj; + }; - // Fall back to MONGODB-CR - var n = this._adminCommand({ copydbgetnonce : 1, fromhost:fromhost}); - return this._adminCommand({ copydb:1, fromhost:fromhost, fromdb:fromdb, todb:todb, - username:username, nonce:n.nonce, - key:this.__pwHash(n.nonce, username, password) }); -}; - -/** - 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.adminCommand(nameOrDocument) - switches to 'admin' db, and runs command [ just calls db.runCommand(...) ]"); - 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.createUser(userDocument)"); - print("\tdb.currentOp() displays currently executing operations in the db"); - print("\tdb.dropDatabase()"); - print("\tdb.eval() - deprecated"); - print("\tdb.fsyncLock() flush data to disk and lock server for backups"); - print("\tdb.fsyncUnlock() unlocks server following a db.fsyncLock()"); - print("\tdb.getCollection(cname) same as db['cname'] or db.cname"); - print("\tdb.getCollectionInfos([filter]) - returns a list that contains the names and options" - + " of the db's collections"); - print("\tdb.getCollectionNames()"); - print("\tdb.getLastError() - just returns the err msg string"); - print("\tdb.getLastErrorObj() - return full status object"); - print("\tdb.getLogComponents()"); - print("\tdb.getMongo() get the server connection object"); - print("\tdb.getMongo().setSlaveOk() allow queries on a replication slave server"); - print("\tdb.getName()"); - print("\tdb.getPrevError()"); - print("\tdb.getProfilingLevel() - deprecated"); - print("\tdb.getProfilingStatus() - returns if profiling is on and slow threshold"); - print("\tdb.getReplicationInfo()"); - print("\tdb.getSiblingDB(name) get the db at the same server as this one"); - print("\tdb.getWriteConcern() - returns the write concern used for any operations on this db, inherited from server object if set"); - print("\tdb.hostInfo() get details about the server's host"); - print("\tdb.isMaster() check replica primary status"); - print("\tdb.killOp(opid) kills the current operation in the db"); - print("\tdb.listCommands() lists all the db commands"); - print("\tdb.loadServerScripts() loads all the scripts in db.system.js"); - print("\tdb.logout()"); - print("\tdb.printCollectionStats()"); - print("\tdb.printReplicationInfo()"); - print("\tdb.printShardingStatus()"); - print("\tdb.printSlaveReplicationInfo()"); - print("\tdb.dropUser(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.serverStatus()"); - print("\tdb.setLogLevel(level,<component>)"); - print("\tdb.setProfilingLevel(level,<slowms>) 0=off 1=slow 2=all"); - print("\tdb.setWriteConcern( <write concern doc> ) - sets the write concern for writes to the db"); - print("\tdb.unsetWriteConcern( <write concern doc> ) - unsets the write concern for writes to the db"); - print("\tdb.setVerboseShell(flag) display extra information in shell output"); - print("\tdb.shutdownServer()"); - print("\tdb.stats()"); - print("\tdb.version() current version of the server"); - - return __magicNoPrint; -}; - -DB.prototype.printCollectionStats = function(scale) { - if (arguments.length > 1) { - print("printCollectionStats() has a single optional argument (scale)"); - return; - } - if (typeof scale != 'undefined') { - if(typeof scale != 'number') { - print("scale has to be a number >= 1"); - return; + // Like runCommand but applies readPreference if one has been set + // on the connection. Also sets slaveOk if a (non-primary) readPref has been set. + DB.prototype.runReadCommand = function(obj, extra, queryOptions) { + "use strict"; + + // Support users who call this function with a string commandName, e.g. + // db.runReadCommand("commandName", {arg1: "value", arg2: "value"}). + var mergedObj = (typeof(obj) === "string") ? this._mergeCommandOptions(obj, extra) : obj; + var cmdObjWithReadPref = + this._attachReadPreferenceToCommand(mergedObj, this.getMongo().getReadPref()); + + var options = + (typeof(queryOptions) !== "undefined") ? queryOptions : this.getQueryOptions(); + var readPrefMode = this.getMongo().getReadPrefMode(); + + // Set slaveOk if readPrefMode has been explicitly set with a readPreference other than + // primary. + if (!!readPrefMode && readPrefMode !== "primary") { + options |= 4; } - if (scale < 1) { - print("scale has to be >= 1"); - return; + + // The 'extra' parameter is not used as we have already created a merged command object. + return this.runCommand(cmdObjWithReadPref, null, options); + }; + + // runCommand uses this impl to actually execute the command + DB.prototype._runCommandImpl = function(name, obj, options) { + return this.getMongo().runCommand(name, obj, options); + }; + + DB.prototype.runCommand = function(obj, extra, queryOptions) { + var mergedObj = (typeof(obj) === "string") ? this._mergeCommandOptions(obj, extra) : obj; + // if options were passed (i.e. because they were overridden on a collection), use them. + // Otherwise use getQueryOptions. + var options = + (typeof(queryOptions) !== "undefined") ? queryOptions : this.getQueryOptions(); + var res; + try { + res = this._runCommandImpl(this._name, mergedObj, options); + } catch (ex) { + // When runCommand flowed through query, a connection error resulted in the message + // "error doing query: failed". Even though this message is arguably incorrect + // for a command failing due to a connection failure, we preserve it for backwards + // compatibility. See SERVER-18334 for details. + if (ex.message.indexOf("network error") >= 0) { + throw new Error("error doing query: failed: " + ex.message); + } + throw ex; } - } - var mydb = this; - this.getCollectionNames().forEach( - function(z) { - print( z ); - printjson( mydb.getCollection(z).stats(scale) ); - 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 operations; optional argument slowms specifies slowness threshold</li> - * <li>2=log all</li> - * @param {String} level Desired level of profiling - * @param {String} slowms For slow logging, query duration that counts as slow (default 100ms) - * @return SOMETHING_FIXME or null on error - */ -DB.prototype.setProfilingLevel = function(level,slowms) { - - if (level < 0 || level > 2) { - var errorText = "input level " + level + " is out of range [0..2]"; - var errorObject = new Error(errorText); - errorObject['dbSetProfilingException'] = errorText; - throw errorObject; - } + return res; + }; - var cmd = { profile: level }; - if ( isNumber( slowms ) ) - cmd["slowms"] = slowms; - return assert.commandWorked(this._dbCommand( cmd )); -}; - -/** - * @deprecated - * <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) { - print("WARNING: db.eval is deprecated"); - - var cmd = { $eval : jsfunction }; - if ( arguments.length > 1 ) { - cmd.args = Array.from(arguments).slice(1); - } - - var res = this._dbCommand( cmd ); - - if (!res.ok) - throw _getErrorWithCode(res, 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]; - } + DB.prototype.runCommandWithMetadata = function(commandName, commandArgs, metadata) { + return this.getMongo().runCommandWithMetadata( + this._name, commandName, metadata, commandArgs); + }; + + DB.prototype._dbCommand = DB.prototype.runCommand; + DB.prototype._dbReadCommand = DB.prototype.runReadCommand; + + DB.prototype.adminCommand = function(obj, extra) { + if (this._name == "admin") + return this.runCommand(obj, extra); + return this.getSiblingDB("admin").runCommand(obj, extra); + }; + + DB.prototype._adminCommand = DB.prototype.adminCommand; // alias old name + + /** + 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> + <li> usePowerOf2Sizes: if true, set usePowerOf2Sizes allocation for the collection.</li> + <li> + storageEngine: BSON document containing storage engine specific options. Format: + { + storageEngine: { + storageEngine1: { + ... + }, + storageEngine2: { + ... + }, + ... + } + } + </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 || {}; + + // We have special handling for the 'flags' field, and provide sugar for specific flags. If + // the + // user specifies any flags we send the field in the command. Otherwise, we leave it blank + // and + // use the server's defaults. + var sendFlags = false; + var flags = 0; + if (options.usePowerOf2Sizes != undefined) { + print( + "WARNING: The 'usePowerOf2Sizes' flag is ignored in 3.0 and higher as all MMAPv1 " + + "collections use fixed allocation sizes unless the 'noPadding' flag is specified"); + + sendFlags = true; + if (options.usePowerOf2Sizes) { + flags |= 1; // Flag_UsePowerOf2Sizes } - else { - key = parms.$keyf(obj); + delete options.usePowerOf2Sizes; + } + if (options.noPadding != undefined) { + sendFlags = true; + if (options.noPadding) { + flags |= 2; // Flag_NoPadding } + delete options.noPadding; + } + + // New flags must be added above here. + if (sendFlags) { + if (options.flags != undefined) + throw Error("Can't set 'flags' with either 'usePowerOf2Sizes' or 'noPadding'"); + options.flags = flags; + } + + var cmd = { + create: name + }; + Object.extend(cmd, options); + + return this._dbCommand(cmd); + }; + + /** + * @deprecated use getProfilingStatus + * Returns the current profiling level of this database + * @return SOMETHING_FIXME or null on error + */ + DB.prototype.getProfilingLevel = function() { + var res = assert.commandWorked(this._dbCommand({profile: -1})); + return res ? res.was : null; + }; + + /** + * @return the current profiling status + * example { was : 0, slowms : 100 } + * @return SOMETHING_FIXME or null on error + */ + DB.prototype.getProfilingStatus = function() { + var res = this._dbCommand({profile: -1}); + if (!res.ok) + throw _getErrorWithCode(res, "profile command failed: " + tojson(res)); + delete res.ok; + return res; + }; + + /** + 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 Error("dropDatabase doesn't take arguments"); + return this._dbCommand({dropDatabase: 1}); + }; + + /** + * Shuts down the database. Must be run while using the admin database. + * @param opts Options for shutdown. Possible options are: + * - force: (boolean) if the server should shut down, even if there is no + * up-to-date slave + * - timeoutSecs: (number) the server will continue checking over timeoutSecs + * if any other servers have caught up enough for it to shut down. + */ + DB.prototype.shutdownServer = function(opts) { + if ("admin" != this._name) { + return "shutdown command only works with the admin database; try 'use admin'"; + } - var aggObj = map.get(key); - if( aggObj == null ) { - var newObj = Object.extend({}, key); // clone - aggObj = Object.extend(newObj, parms.initial); - map.put( key, aggObj ); + var cmd = { + 'shutdown': 1 + }; + opts = opts || {}; + for (var o in opts) { + cmd[o] = opts[o]; + } + + try { + var res = this.runCommand(cmd); + if (!res.ok) { + throw _getErrorWithCode(res, 'shutdownServer failed: ' + tojson(res)); + } + throw Error('shutdownServer failed: server is still up.'); + } catch (e) { + // we expect the command to not return a response, as the server will shut down + // immediately. + if (e.message.indexOf("error doing query: failed") >= 0) { + print('server should be down...'); + return; } - parms.$reduce(obj, aggObj); + throw e; } + }; - return map.values(); + /** + 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); + return this._dbCommand({clone: from}); }; - return this.eval(groupFunction, this._groupFixParms( parmsObj )); -}; + /** + 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 || {}; + return this._dbCommand({cloneCollection: collection, from: from, query: query}); + }; -DB.prototype.groupcmd = function( parmsObj ){ - var ret = this.runCommand( { "group" : this._groupFixParms( parmsObj ) } ); - if ( ! ret.ok ){ - throw _getErrorWithCode(ret, "group command failed: " + tojson(ret)); - } - return ret.retval; -}; + /** + 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, username, password, mechanism) { + assert(isString(fromdb) && fromdb.length); + assert(isString(todb) && todb.length); + fromhost = fromhost || ""; + + if (!mechanism) { + mechanism = this._getDefaultAuthenticationMechanism(); + } + assert(mechanism == "SCRAM-SHA-1" || mechanism == "MONGODB-CR"); -DB.prototype.group = DB.prototype.groupcmd; + // Check for no auth or copying from localhost + if (!username || !password || fromhost == "") { + return this._adminCommand({copydb: 1, fromhost: fromhost, fromdb: fromdb, todb: todb}); + } -DB.prototype._groupFixParms = function( parmsObj ){ - var parms = Object.extend({}, parmsObj); + // Use the copyDatabase native helper for SCRAM-SHA-1 + if (mechanism == "SCRAM-SHA-1") { + return this.getMongo().copyDatabaseWithSCRAM( + fromdb, todb, fromhost, username, password); + } - if( parms.reduce ) { - parms.$reduce = parms.reduce; // must have $ to pass to db - delete parms.reduce; - } + // Fall back to MONGODB-CR + var n = this._adminCommand({copydbgetnonce: 1, fromhost: fromhost}); + return this._adminCommand({ + copydb: 1, + fromhost: fromhost, + fromdb: fromdb, + todb: todb, + username: username, + nonce: n.nonce, + key: this.__pwHash(n.nonce, username, password) + }); + }; - if( parms.keyf ) { - parms.$keyf = parms.keyf; - delete parms.keyf; - } + /** + Repair database. - return parms; -}; - -DB.prototype.resetError = function(){ - return this.runCommand( { reseterror : 1 } ); -}; - -DB.prototype.forceError = function(){ - return this.runCommand( { forceerror : 1 } ); -}; - -DB.prototype.getLastError = function( w , wtimeout ){ - var res = this.getLastErrorObj( w , wtimeout ); - if ( ! res.ok ) - throw _getErrorWithCode(ret, "getlasterror failed: " + tojson(res)); - return res.err; -}; -DB.prototype.getLastErrorObj = function( w , wtimeout ){ - var cmd = { getlasterror : 1 }; - if ( w ){ - cmd.w = w; - if ( wtimeout ) - cmd.wtimeout = wtimeout; - } - var res = this.runCommand( cmd ); + * @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.adminCommand(nameOrDocument) - switches to 'admin' db, and runs command [ just calls db.runCommand(...) ]"); + 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.createUser(userDocument)"); + print("\tdb.currentOp() displays currently executing operations in the db"); + print("\tdb.dropDatabase()"); + print("\tdb.eval() - deprecated"); + print("\tdb.fsyncLock() flush data to disk and lock server for backups"); + print("\tdb.fsyncUnlock() unlocks server following a db.fsyncLock()"); + print("\tdb.getCollection(cname) same as db['cname'] or db.cname"); + print( + "\tdb.getCollectionInfos([filter]) - returns a list that contains the names and options" + + " of the db's collections"); + print("\tdb.getCollectionNames()"); + print("\tdb.getLastError() - just returns the err msg string"); + print("\tdb.getLastErrorObj() - return full status object"); + print("\tdb.getLogComponents()"); + print("\tdb.getMongo() get the server connection object"); + print("\tdb.getMongo().setSlaveOk() allow queries on a replication slave server"); + print("\tdb.getName()"); + print("\tdb.getPrevError()"); + print("\tdb.getProfilingLevel() - deprecated"); + print("\tdb.getProfilingStatus() - returns if profiling is on and slow threshold"); + print("\tdb.getReplicationInfo()"); + print("\tdb.getSiblingDB(name) get the db at the same server as this one"); + print( + "\tdb.getWriteConcern() - returns the write concern used for any operations on this db, inherited from server object if set"); + print("\tdb.hostInfo() get details about the server's host"); + print("\tdb.isMaster() check replica primary status"); + print("\tdb.killOp(opid) kills the current operation in the db"); + print("\tdb.listCommands() lists all the db commands"); + print("\tdb.loadServerScripts() loads all the scripts in db.system.js"); + print("\tdb.logout()"); + print("\tdb.printCollectionStats()"); + print("\tdb.printReplicationInfo()"); + print("\tdb.printShardingStatus()"); + print("\tdb.printSlaveReplicationInfo()"); + print("\tdb.dropUser(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.serverStatus()"); + print("\tdb.setLogLevel(level,<component>)"); + print("\tdb.setProfilingLevel(level,<slowms>) 0=off 1=slow 2=all"); + print( + "\tdb.setWriteConcern( <write concern doc> ) - sets the write concern for writes to the db"); + print( + "\tdb.unsetWriteConcern( <write concern doc> ) - unsets the write concern for writes to the db"); + print("\tdb.setVerboseShell(flag) display extra information in shell output"); + print("\tdb.shutdownServer()"); + print("\tdb.stats()"); + print("\tdb.version() current version of the server"); + + return __magicNoPrint; + }; - if ( ! res.ok ) - throw _getErrorWithCode(res, "getlasterror failed: " + tojson(res)); - return res; -}; -DB.prototype.getLastErrorCmd = DB.prototype.getLastErrorObj; + DB.prototype.printCollectionStats = function(scale) { + if (arguments.length > 1) { + print("printCollectionStats() has a single optional argument (scale)"); + return; + } + if (typeof scale != 'undefined') { + if (typeof scale != 'number') { + print("scale has to be a number >= 1"); + return; + } + if (scale < 1) { + print("scale has to be >= 1"); + return; + } + } + var mydb = this; + this.getCollectionNames().forEach(function(z) { + print(z); + printjson(mydb.getCollection(z).stats(scale)); + 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 operations; optional argument slowms specifies slowness threshold</li> + * <li>2=log all</li> + * @param {String} level Desired level of profiling + * @param {String} slowms For slow logging, query duration that counts as slow (default 100ms) + * @return SOMETHING_FIXME or null on error + */ + DB.prototype.setProfilingLevel = function(level, slowms) { + + if (level < 0 || level > 2) { + var errorText = "input level " + level + " is out of range [0..2]"; + var errorObject = new Error(errorText); + errorObject['dbSetProfilingException'] = errorText; + throw errorObject; + } -/* Return the last error which has occurred, even if not the very last error. + var cmd = { + profile: level + }; + if (isNumber(slowms)) + cmd["slowms"] = slowms; + return assert.commandWorked(this._dbCommand(cmd)); + }; - Returns: - { err : <error message>, nPrev : <how_many_ops_back_occurred>, ok : 1 } + /** + * @deprecated + * <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) { + print("WARNING: db.eval is deprecated"); + + var cmd = { + $eval: jsfunction + }; + if (arguments.length > 1) { + cmd.args = Array.from(arguments).slice(1); + } - result.err will be null if no error has occurred. - */ -DB.prototype.getPrevError = function(){ - return this.runCommand( { getpreverror : 1 } ); -}; + var res = this._dbCommand(cmd); -DB.prototype._getCollectionInfosSystemNamespaces = function(filter) { - var all = []; + if (!res.ok) + throw _getErrorWithCode(res, tojson(res)); - var dbNamePrefix = this._name + "."; + return res.retval; + }; - // Create a shallow copy of 'filter' in case we modify its 'name' property. Also defaults - // 'filter' to {} if the parameter was not specified. - filter = Object.extend({}, filter); - if (typeof filter.name === "string") { - // Queries on the 'name' field need to qualify the namespace with the database name for - // consistency with the command variant. - filter.name = dbNamePrefix + filter.name; - } + 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 c = this.getCollection( "system.namespaces" ).find(filter); - while ( c.hasNext() ){ - var infoObj = c.next(); - - if ( infoObj.name.indexOf( "$" ) >= 0 && infoObj.name.indexOf( ".oplog.$" ) < 0 ) - continue; - - // Remove the database name prefix from the collection info object. - infoObj.name = infoObj.name.substring(dbNamePrefix.length); - - all.push( infoObj ); - } - - // Return list of objects sorted by collection name. - return all.sort(function(coll1, coll2) { return coll1.name.localeCompare(coll2.name); }); -}; + 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(); + }; -DB.prototype._getCollectionInfosCommand = function(filter) { - filter = filter || {}; - var res = this.runCommand({listCollections: 1, filter: filter}); - if ( res.code == 59 ) { - // command doesn't exist, old mongod - return null; - } + return this.eval(groupFunction, this._groupFixParms(parmsObj)); + }; - if ( !res.ok ) { - if ( res.errmsg && res.errmsg.startsWith( "no such cmd" ) ) { - return null; + DB.prototype.groupcmd = function(parmsObj) { + var ret = this.runCommand({"group": this._groupFixParms(parmsObj)}); + if (!ret.ok) { + throw _getErrorWithCode(ret, "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; } - throw _getErrorWithCode(res, "listCollections failed: " + tojson(res)); - } + if (parms.keyf) { + parms.$keyf = parms.keyf; + delete parms.keyf; + } + + return parms; + }; - return new DBCommandCursor(this._mongo, res).toArray().sort(compareOn("name")); -}; - -/** - * Returns a list that contains the names and options of this database's collections, sorted by - * collection name. An optional filter can be specified to match only collections with certain - * metadata. - */ -DB.prototype.getCollectionInfos = function(filter) { - var res = this._getCollectionInfosCommand(filter); - if ( res ) { + DB.prototype.resetError = function() { + return this.runCommand({reseterror: 1}); + }; + + DB.prototype.forceError = function() { + return this.runCommand({forceerror: 1}); + }; + + DB.prototype.getLastError = function(w, wtimeout) { + var res = this.getLastErrorObj(w, wtimeout); + if (!res.ok) + throw _getErrorWithCode(ret, "getlasterror failed: " + tojson(res)); + return res.err; + }; + DB.prototype.getLastErrorObj = function(w, wtimeout) { + var cmd = { + getlasterror: 1 + }; + if (w) { + cmd.w = w; + if (wtimeout) + cmd.wtimeout = wtimeout; + } + var res = this.runCommand(cmd); + + if (!res.ok) + throw _getErrorWithCode(res, "getlasterror failed: " + tojson(res)); return res; - } - return this._getCollectionInfosSystemNamespaces(filter); -}; - -/** - * Returns this database's list of collection names in sorted order. - */ -DB.prototype.getCollectionNames = function() { - return this.getCollectionInfos().map(function(infoObj) { return infoObj.name; }); -}; - -DB.prototype.tojson = function(){ - return this._name; -}; - -DB.prototype.toString = function(){ - return this._name; -}; - -DB.prototype.isMaster = function () { return this.runCommand("isMaster"); }; - -var commandUnsupported = function(res) { - return (!res.ok && - (res.errmsg.startsWith("no such cmd") || - res.errmsg.startsWith("no such command") || - res.code === 59 /* CommandNotFound */)); -}; - -DB.prototype.currentOp = function(arg) { - var q = {}; - if ( arg ) { - if ( typeof( arg ) == "object" ) - Object.extend( q , arg ); - else if ( arg ) - q["$all"] = true; - } + }; + DB.prototype.getLastErrorCmd = DB.prototype.getLastErrorObj; - var commandObj = {"currentOp": 1}; - Object.extend(commandObj, q); - var res = this.adminCommand(commandObj); - if (commandUnsupported(res)) { - // always send legacy currentOp with default (null) read preference (SERVER-17951) - var _readPref = this.getMongo().getReadPrefMode(); - try { - this.getMongo().setReadPref(null); - res = this.getSiblingDB("admin").$cmd.sys.inprog.findOne( q ); - } finally { - this.getMongo().setReadPref(_readPref); + /* 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._getCollectionInfosSystemNamespaces = function(filter) { + var all = []; + + var dbNamePrefix = this._name + "."; + + // Create a shallow copy of 'filter' in case we modify its 'name' property. Also defaults + // 'filter' to {} if the parameter was not specified. + filter = Object.extend({}, filter); + if (typeof filter.name === "string") { + // Queries on the 'name' field need to qualify the namespace with the database name for + // consistency with the command variant. + filter.name = dbNamePrefix + filter.name; } - } - return res; -}; -DB.prototype.currentOP = DB.prototype.currentOp; - -DB.prototype.killOp = function(op) { - if( !op ) - throw Error("no opNum to kill specified"); - var res = this.adminCommand({'killOp': 1, 'op': op}); - if (commandUnsupported(res)) { - // fall back for old servers - var _readPref = this.getMongo().getReadPrefMode(); - try { - this.getMongo().setReadPref(null); - res = this.getSiblingDB("admin").$cmd.sys.killop.findOne({'op': op}); - } finally { - this.getMongo().setReadPref(_readPref); + + var c = this.getCollection("system.namespaces").find(filter); + while (c.hasNext()) { + var infoObj = c.next(); + + if (infoObj.name.indexOf("$") >= 0 && infoObj.name.indexOf(".oplog.$") < 0) + continue; + + // Remove the database name prefix from the collection info object. + infoObj.name = infoObj.name.substring(dbNamePrefix.length); + + all.push(infoObj); } - } - return res; -}; -DB.prototype.killOP = DB.prototype.killOp; - -DB.tsToSeconds = function(x){ - if ( x.t && x.i ) - return x.t; - 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 localdb = this.getSiblingDB("local"); - - var result = { }; - var oplog; - var localCollections = localdb.getCollectionNames(); - if (localCollections.indexOf('oplog.rs') >= 0) { - oplog = 'oplog.rs'; - } - else if (localCollections.indexOf('oplog.$main') >= 0) { - oplog = 'oplog.$main'; - } - else { - result.errmsg = "neither master/slave nor replica set replication detected"; - return result; - } - var ol = localdb.getCollection(oplog); - var ol_stats = ol.stats(); - if( ol_stats && ol_stats.maxSize ) { - result.logSizeMB = ol_stats.maxSize / ( 1024 * 1024 ); - } else { - result.errmsg = "Could not get stats for local."+oplog+" collection. " + - "collstats returned: " + tojson(ol_stats); - return result; - } + // Return list of objects sorted by collection name. + return all.sort(function(coll1, coll2) { + return coll1.name.localeCompare(coll2.name); + }); + }; - result.usedMB = ol_stats.size / ( 1024 * 1024 ); - result.usedMB = Math.ceil( result.usedMB * 100 ) / 100; + DB.prototype._getCollectionInfosCommand = function(filter) { + filter = filter || {}; + var res = this.runCommand({listCollections: 1, filter: filter}); + if (res.code == 59) { + // command doesn't exist, old mongod + return null; + } - var firstc = ol.find().sort({$natural:1}).limit(1); - var lastc = ol.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 = ol.count(); - return result; - } + if (!res.ok) { + if (res.errmsg && res.errmsg.startsWith("no such cmd")) { + return null; + } - 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"; - } + throw _getErrorWithCode(res, "listCollections failed: " + tojson(res)); + } - return result; -}; + return new DBCommandCursor(this._mongo, res).toArray().sort(compareOn("name")); + }; -DB.prototype.printReplicationInfo = function() { - var result = this.getReplicationInfo(); - if( result.errmsg ) { - var isMaster = this.isMaster(); - if (isMaster.arbiterOnly) { - print("cannot provide replication status from an arbiter."); - return; + /** + * Returns a list that contains the names and options of this database's collections, sorted by + * collection name. An optional filter can be specified to match only collections with certain + * metadata. + */ + DB.prototype.getCollectionInfos = function(filter) { + var res = this._getCollectionInfosCommand(filter); + if (res) { + return res; } - else if (!isMaster.ismaster) { - print("this is a slave, printing slave replication info."); - this.printSlaveReplicationInfo(); - return; + return this._getCollectionInfosSystemNamespaces(filter); + }; + + /** + * Returns this database's list of collection names in sorted order. + */ + DB.prototype.getCollectionNames = function() { + return this.getCollectionInfos().map(function(infoObj) { + return infoObj.name; + }); + }; + + DB.prototype.tojson = function() { + return this._name; + }; + + DB.prototype.toString = function() { + return this._name; + }; + + DB.prototype.isMaster = function() { + return this.runCommand("isMaster"); + }; + + var commandUnsupported = function(res) { + return (!res.ok && + (res.errmsg.startsWith("no such cmd") || res.errmsg.startsWith("no such command") || + res.code === 59 /* CommandNotFound */)); + }; + + DB.prototype.currentOp = function(arg) { + var q = {}; + if (arg) { + if (typeof(arg) == "object") + Object.extend(q, arg); + else if (arg) + q["$all"] = true; } - 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() { - var startOptimeDate = null; - var primary = null; - - function getReplLag(st) { - assert( startOptimeDate , "how could this be null (getReplLag startOptimeDate)" ); - print("\tsyncedTo: " + st.toString() ); - var ago = (startOptimeDate-st)/1000; - var hrs = Math.round(ago/36)/100; - var suffix = ""; - if (primary) { - suffix = "primary "; - } - else { - suffix = "freshest member (no primary available at the moment)"; - } - print("\t" + Math.round(ago) + " secs (" + hrs + " hrs) behind the " + suffix); - } - function getMaster(members) { - for (i in members) { - var row = members[i]; - if (row.state === 1) { - return row; + var commandObj = { + "currentOp": 1 + }; + Object.extend(commandObj, q); + var res = this.adminCommand(commandObj); + if (commandUnsupported(res)) { + // always send legacy currentOp with default (null) read preference (SERVER-17951) + var _readPref = this.getMongo().getReadPrefMode(); + try { + this.getMongo().setReadPref(null); + res = this.getSiblingDB("admin").$cmd.sys.inprog.findOne(q); + } finally { + this.getMongo().setReadPref(_readPref); + } + } + return res; + }; + DB.prototype.currentOP = DB.prototype.currentOp; + + DB.prototype.killOp = function(op) { + if (!op) + throw Error("no opNum to kill specified"); + var res = this.adminCommand({'killOp': 1, 'op': op}); + if (commandUnsupported(res)) { + // fall back for old servers + var _readPref = this.getMongo().getReadPrefMode(); + try { + this.getMongo().setReadPref(null); + res = this.getSiblingDB("admin").$cmd.sys.killop.findOne({'op': op}); + } finally { + this.getMongo().setReadPref(_readPref); } } + return res; + }; + DB.prototype.killOP = DB.prototype.killOp; - return null; - } + DB.tsToSeconds = function(x) { + if (x.t && x.i) + return x.t; + 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 localdb = this.getSiblingDB("local"); + + var result = {}; + var oplog; + var localCollections = localdb.getCollectionNames(); + if (localCollections.indexOf('oplog.rs') >= 0) { + oplog = 'oplog.rs'; + } else if (localCollections.indexOf('oplog.$main') >= 0) { + oplog = 'oplog.$main'; + } else { + result.errmsg = "neither master/slave nor replica set replication detected"; + return result; + } - function g(x) { - assert( x , "how could this be null (printSlaveReplicationInfo gx)" ); - print("source: " + x.host); - if ( x.syncedTo ){ - var st = new Date( DB.tsToSeconds( x.syncedTo ) * 1000 ); - getReplLag(st); + var ol = localdb.getCollection(oplog); + var ol_stats = ol.stats(); + if (ol_stats && ol_stats.maxSize) { + result.logSizeMB = ol_stats.maxSize / (1024 * 1024); + } else { + result.errmsg = "Could not get stats for local." + oplog + " collection. " + + "collstats returned: " + tojson(ol_stats); + return result; } - else { - print( "\tdoing initial sync" ); + + result.usedMB = ol_stats.size / (1024 * 1024); + result.usedMB = Math.ceil(result.usedMB * 100) / 100; + + var firstc = ol.find().sort({$natural: 1}).limit(1); + var lastc = ol.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 = ol.count(); + return result; } - } - function r(x) { - assert( x , "how could this be null (printSlaveReplicationInfo rx)" ); - if ( x.state == 1 || x.state == 7 ) { // ignore primaries (1) and arbiters (7) - return; + 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"; } - - print("source: " + x.name); - if ( x.optime ) { - getReplLag(x.optimeDate); + + return result; + }; + + DB.prototype.printReplicationInfo = function() { + var result = this.getReplicationInfo(); + if (result.errmsg) { + var isMaster = this.isMaster(); + if (isMaster.arbiterOnly) { + print("cannot provide replication status from an arbiter."); + return; + } else if (!isMaster.ismaster) { + print("this is a slave, printing slave replication info."); + this.printSlaveReplicationInfo(); + return; + } + print(tojson(result)); + return; } - else { - print( "\tno replication info, yet. State: " + x.stateStr ); + 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() { + var startOptimeDate = null; + var primary = null; + + function getReplLag(st) { + assert(startOptimeDate, "how could this be null (getReplLag startOptimeDate)"); + print("\tsyncedTo: " + st.toString()); + var ago = (startOptimeDate - st) / 1000; + var hrs = Math.round(ago / 36) / 100; + var suffix = ""; + if (primary) { + suffix = "primary "; + } else { + suffix = "freshest member (no primary available at the moment)"; + } + print("\t" + Math.round(ago) + " secs (" + hrs + " hrs) behind the " + suffix); } - } - - var L = this.getSiblingDB("local"); - - if (L.system.replset.count() != 0) { - var status = this.adminCommand({'replSetGetStatus' : 1}); - primary = getMaster(status.members); - if (primary) { - startOptimeDate = primary.optimeDate; - } - // no primary, find the most recent op among all members - else { - startOptimeDate = new Date(0, 0); - for (i in status.members) { - if (status.members[i].optimeDate > startOptimeDate) { - startOptimeDate = status.members[i].optimeDate; + + function getMaster(members) { + for (i in members) { + var row = members[i]; + if (row.state === 1) { + return row; } } + + return null; } - for (i in status.members) { - r(status.members[i]); + function g(x) { + assert(x, "how could this be null (printSlaveReplicationInfo gx)"); + print("source: " + x.host); + if (x.syncedTo) { + var st = new Date(DB.tsToSeconds(x.syncedTo) * 1000); + getReplLag(st); + } else { + print("\tdoing initial sync"); + } } - } - else if( L.sources.count() != 0 ) { - startOptimeDate = new Date(); - L.sources.find().forEach(g); - } - else { - print("local.sources is empty; is this db a --slave?"); - return; - } -}; - -DB.prototype.serverBuildInfo = function(){ - return this._adminCommand( "buildinfo" ); -}; - -// Used to trim entries from the metrics.commands that have never been executed -getActiveCommands = function(tree) { - var result = { }; - for (var i in tree) { - if (!tree.hasOwnProperty(i)) - continue; - if (tree[i].hasOwnProperty("total")) { - if (tree[i].total > 0) { - result[i] = tree[i]; + + function r(x) { + assert(x, "how could this be null (printSlaveReplicationInfo rx)"); + if (x.state == 1 || x.state == 7) { // ignore primaries (1) and arbiters (7) + return; + } + + print("source: " + x.name); + if (x.optime) { + getReplLag(x.optimeDate); + } else { + print("\tno replication info, yet. State: " + x.stateStr); } - continue; } - if (i == "<UNKNOWN>") { - if(tree[i] > 0) { - result[i] = tree[i]; + + var L = this.getSiblingDB("local"); + + if (L.system.replset.count() != 0) { + var status = this.adminCommand({'replSetGetStatus': 1}); + primary = getMaster(status.members); + if (primary) { + startOptimeDate = primary.optimeDate; + } + // no primary, find the most recent op among all members + else { + startOptimeDate = new Date(0, 0); + for (i in status.members) { + if (status.members[i].optimeDate > startOptimeDate) { + startOptimeDate = status.members[i].optimeDate; + } + } + } + + for (i in status.members) { + r(status.members[i]); } - continue; + } else if (L.sources.count() != 0) { + startOptimeDate = new Date(); + L.sources.find().forEach(g); + } else { + print("local.sources is empty; is this db a --slave?"); + return; } - // Handles nested commands - var subStatus = getActiveCommands(tree[i]); - if (Object.keys(subStatus).length > 0) { - result[i] = tree[i]; + }; + + DB.prototype.serverBuildInfo = function() { + return this._adminCommand("buildinfo"); + }; + + // Used to trim entries from the metrics.commands that have never been executed + getActiveCommands = function(tree) { + var result = {}; + for (var i in tree) { + if (!tree.hasOwnProperty(i)) + continue; + if (tree[i].hasOwnProperty("total")) { + if (tree[i].total > 0) { + result[i] = tree[i]; + } + continue; + } + if (i == "<UNKNOWN>") { + if (tree[i] > 0) { + result[i] = tree[i]; + } + continue; + } + // Handles nested commands + var subStatus = getActiveCommands(tree[i]); + if (Object.keys(subStatus).length > 0) { + result[i] = tree[i]; + } } - } - return result; -}; + return result; + }; -DB.prototype.serverStatus = function( options ){ - var cmd = { serverStatus : 1 }; - if ( options ) { - Object.extend( cmd, options ); - } - var res = this._adminCommand( cmd ); - // Only prune if we have a metrics tree with commands. - if (res.metrics && res.metrics.commands) { + DB.prototype.serverStatus = function(options) { + var cmd = { + serverStatus: 1 + }; + if (options) { + Object.extend(cmd, options); + } + var res = this._adminCommand(cmd); + // Only prune if we have a metrics tree with commands. + if (res.metrics && res.metrics.commands) { res.metrics.commands = getActiveCommands(res.metrics.commands); - } - return res; -}; + } + return res; + }; -DB.prototype.hostInfo = function(){ - return this._adminCommand( "hostInfo" ); -}; + DB.prototype.hostInfo = function() { + return this._adminCommand("hostInfo"); + }; -DB.prototype.serverCmdLineOpts = function(){ - return this._adminCommand( "getCmdLineOpts" ); -}; + DB.prototype.serverCmdLineOpts = function() { + return this._adminCommand("getCmdLineOpts"); + }; -DB.prototype.version = function(){ - return this.serverBuildInfo().version; -}; + DB.prototype.version = function() { + return this.serverBuildInfo().version; + }; -DB.prototype.serverBits = function(){ - return this.serverBuildInfo().bits; -}; + DB.prototype.serverBits = function() { + return this.serverBuildInfo().bits; + }; -DB.prototype.listCommands = function(){ - var x = this.runCommand( "listCommands" ); - for ( var name in x.commands ){ - var c = x.commands[name]; + DB.prototype.listCommands = function() { + var x = this.runCommand("listCommands"); + for (var name in x.commands) { + var c = x.commands[name]; - var s = name + ": "; + var s = name + ": "; - if (c.adminOnly) s += " adminOnly "; - if (c.slaveOk) s += " slaveOk "; + if (c.adminOnly) + s += " adminOnly "; + if (c.slaveOk) + s += " slaveOk "; - s += "\n "; - s += c.help.replace(/\n/g, '\n '); - s += "\n"; - - print( s ); - } -}; + s += "\n "; + s += c.help.replace(/\n/g, '\n '); + s += "\n"; -DB.prototype.printShardingStatus = function( verbose ){ - printShardingStatus( this.getSiblingDB( "config" ) , verbose ); -}; + print(s); + } + }; -DB.prototype.fsyncLock = function() { - return this.adminCommand({fsync:1, lock:true}); -}; + DB.prototype.printShardingStatus = function(verbose) { + printShardingStatus(this.getSiblingDB("config"), verbose); + }; -DB.prototype.fsyncUnlock = function() { - var res = this.adminCommand({fsyncUnlock: 1}); - if (commandUnsupported(res)) { - var _readPref = this.getMongo().getReadPrefMode(); - try { - this.getMongo().setReadPref(null); - res = this.getSiblingDB("admin").$cmd.sys.unlock.findOne(); - } finally { - this.getMongo().setReadPref(_readPref); - } - } - return res; -}; - -DB.autocomplete = function(obj){ - var colls = obj.getCollectionNames(); - var ret=[]; - for (var i=0; i<colls.length; i++){ - if (colls[i].match(/^[a-zA-Z0-9_.\$]+$/)) - ret.push(colls[i]); - } - return ret; -}; - -DB.prototype.setSlaveOk = function( value ) { - if( value == undefined ) value = true; - this._slaveOk = value; -}; - -DB.prototype.getSlaveOk = function() { - if (this._slaveOk != undefined) return this._slaveOk; - return this._mongo.getSlaveOk(); -}; - -DB.prototype.getQueryOptions = function() { - var options = 0; - if (this.getSlaveOk()) options |= 4; - return options; -}; - -/* Loads any scripts contained in system.js into the client shell. -*/ -DB.prototype.loadServerScripts = function(){ - var global = Function('return this')(); - this.system.js.find().forEach(function(u) { - if (u.value.constructor === Code) { - global[u._id] = eval("(" + u.value.code + ")"); - } - else { - global[u._id] = u.value; - } - }); -}; - - -//////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////// Security shell helpers below ////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////// - -var _defaultWriteConcern = { w: 'majority', wtimeout: 30 * 1000 }; - -function getUserObjString(userObj) { - var pwd = userObj.pwd; - delete userObj.pwd; - var toreturn = tojson(userObj); - userObj.pwd = pwd; - return toreturn; -} - -DB.prototype._modifyCommandToDigestPasswordIfNecessary = function(cmdObj, username) { - if (!cmdObj["pwd"]) { - return; - } - if (cmdObj.hasOwnProperty("digestPassword")) { - throw Error("Cannot specify 'digestPassword' through the user management shell helpers, " + - "use 'passwordDigestor' instead"); - } - var passwordDigestor = cmdObj["passwordDigestor"] ? cmdObj["passwordDigestor"] : "client"; - if (passwordDigestor == "server") { - cmdObj["digestPassword"] = true; - } else if (passwordDigestor == "client") { - cmdObj["pwd"] = _hashPassword(username, cmdObj["pwd"]); - cmdObj["digestPassword"] = false; - } else { - throw Error("'passwordDigestor' must be either 'server' or 'client', got: '" + - passwordDigestor + "'"); - } - delete cmdObj["passwordDigestor"]; -}; + DB.prototype.fsyncLock = function() { + return this.adminCommand({fsync: 1, lock: true}); + }; -DB.prototype.createUser = function(userObj, writeConcern) { - var name = userObj["user"]; - var cmdObj = {createUser:name}; - cmdObj = Object.extend(cmdObj, userObj); - delete cmdObj["user"]; + DB.prototype.fsyncUnlock = function() { + var res = this.adminCommand({fsyncUnlock: 1}); + if (commandUnsupported(res)) { + var _readPref = this.getMongo().getReadPrefMode(); + try { + this.getMongo().setReadPref(null); + res = this.getSiblingDB("admin").$cmd.sys.unlock.findOne(); + } finally { + this.getMongo().setReadPref(_readPref); + } + } + return res; + }; - this._modifyCommandToDigestPasswordIfNecessary(cmdObj, name); + DB.autocomplete = function(obj) { + var colls = obj.getCollectionNames(); + var ret = []; + for (var i = 0; i < colls.length; i++) { + if (colls[i].match(/^[a-zA-Z0-9_.\$]+$/)) + ret.push(colls[i]); + } + return ret; + }; - cmdObj["writeConcern"] = writeConcern ? writeConcern : _defaultWriteConcern; + DB.prototype.setSlaveOk = function(value) { + if (value == undefined) + value = true; + this._slaveOk = value; + }; - var res = this.runCommand(cmdObj); + DB.prototype.getSlaveOk = function() { + if (this._slaveOk != undefined) + return this._slaveOk; + return this._mongo.getSlaveOk(); + }; - if (res.ok) { - print("Successfully added user: " + getUserObjString(userObj)); - return; - } + DB.prototype.getQueryOptions = function() { + var options = 0; + if (this.getSlaveOk()) + options |= 4; + return options; + }; - if (res.errmsg == "no such cmd: createUser") { - throw Error("'createUser' command not found. This is most likely because you are " + - "talking to an old (pre v2.6) MongoDB server"); - } + /* Loads any scripts contained in system.js into the client shell. + */ + DB.prototype.loadServerScripts = function() { + var global = Function('return this')(); + this.system.js.find().forEach(function(u) { + if (u.value.constructor === Code) { + global[u._id] = eval("(" + u.value.code + ")"); + } else { + global[u._id] = u.value; + } + }); + }; - if (res.errmsg == "timeout") { - throw Error("timed out while waiting for user authentication to replicate - " + - "database will not be fully secured until replication finishes"); - } + //////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////// Security shell helpers below + ///////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////////////////// - throw _getErrorWithCode(res, "couldn't add user: " + res.errmsg); -}; + var _defaultWriteConcern = { + w: 'majority', + wtimeout: 30 * 1000 + }; -function _hashPassword(username, password) { - if (typeof password != 'string') { - throw Error("User passwords must be of type string. Was given password with type: " + - typeof(password)); - } - return hex_md5(username + ":mongo:" + password); -} - -/** - * Used for updating users in systems with V1 style user information - * (ie MongoDB v2.4 and prior) - */ -DB.prototype._updateUserV1 = function(name, updateObject, writeConcern) { - var setObj = {}; - if (updateObject.pwd) { - setObj["pwd"] = _hashPassword(name, updateObject.pwd); - } - if (updateObject.extraData) { - setObj["extraData"] = updateObject.extraData; - } - if (updateObject.roles) { - setObj["roles"] = updateObject.roles; + function getUserObjString(userObj) { + var pwd = userObj.pwd; + delete userObj.pwd; + var toreturn = tojson(userObj); + userObj.pwd = pwd; + return toreturn; } - this.system.users.update({user : name, userSource : null}, - {$set : setObj}); - var errObj = this.getLastErrorObj(writeConcern['w'], writeConcern['wtimeout']); - if (errObj.err) { - throw _getErrorWithCode(errObj, "Updating user failed: " + errObj.err); - } -}; + DB.prototype._modifyCommandToDigestPasswordIfNecessary = function(cmdObj, username) { + if (!cmdObj["pwd"]) { + return; + } + if (cmdObj.hasOwnProperty("digestPassword")) { + throw Error( + "Cannot specify 'digestPassword' through the user management shell helpers, " + + "use 'passwordDigestor' instead"); + } + var passwordDigestor = cmdObj["passwordDigestor"] ? cmdObj["passwordDigestor"] : "client"; + if (passwordDigestor == "server") { + cmdObj["digestPassword"] = true; + } else if (passwordDigestor == "client") { + cmdObj["pwd"] = _hashPassword(username, cmdObj["pwd"]); + cmdObj["digestPassword"] = false; + } else { + throw Error("'passwordDigestor' must be either 'server' or 'client', got: '" + + passwordDigestor + "'"); + } + delete cmdObj["passwordDigestor"]; + }; -DB.prototype.updateUser = function(name, updateObject, writeConcern) { - var cmdObj = {updateUser:name}; - cmdObj = Object.extend(cmdObj, updateObject); - cmdObj['writeConcern'] = writeConcern ? writeConcern : _defaultWriteConcern; - this._modifyCommandToDigestPasswordIfNecessary(cmdObj, name); + DB.prototype.createUser = function(userObj, writeConcern) { + var name = userObj["user"]; + var cmdObj = { + createUser: name + }; + cmdObj = Object.extend(cmdObj, userObj); + delete cmdObj["user"]; - var res = this.runCommand(cmdObj); - if (res.ok) { - return; - } + this._modifyCommandToDigestPasswordIfNecessary(cmdObj, name); - if (res.errmsg == "no such cmd: updateUser") { - this._updateUserV1(name, updateObject, cmdObj['writeConcern']); - return; - } + cmdObj["writeConcern"] = writeConcern ? writeConcern : _defaultWriteConcern; - throw _getErrorWithCode(res, "Updating user failed: " + res.errmsg); -}; + var res = this.runCommand(cmdObj); -DB.prototype.changeUserPassword = function(username, password, writeConcern) { - this.updateUser(username, {pwd:password}, writeConcern); -}; + if (res.ok) { + print("Successfully added user: " + getUserObjString(userObj)); + return; + } -DB.prototype.logout = function(){ - return this.getMongo().logout(this.getName()); -}; + if (res.errmsg == "no such cmd: createUser") { + throw Error("'createUser' command not found. This is most likely because you are " + + "talking to an old (pre v2.6) MongoDB server"); + } -// For backwards compatibility -DB.prototype.removeUser = function( username, writeConcern ) { - print("WARNING: db.removeUser has been deprecated, please use db.dropUser instead"); - return this.dropUser(username, writeConcern); -}; + if (res.errmsg == "timeout") { + throw Error("timed out while waiting for user authentication to replicate - " + + "database will not be fully secured until replication finishes"); + } -DB.prototype.dropUser = function( username, writeConcern ){ - var cmdObj = {dropUser: username, - writeConcern: writeConcern ? writeConcern : _defaultWriteConcern}; - var res = this.runCommand(cmdObj); + throw _getErrorWithCode(res, "couldn't add user: " + res.errmsg); + }; - if (res.ok) { - return true; + function _hashPassword(username, password) { + if (typeof password != 'string') { + throw Error("User passwords must be of type string. Was given password with type: " + + typeof(password)); + } + return hex_md5(username + ":mongo:" + password); } - if (res.code == 11) { // Code 11 = UserNotFound - return false; - } + /** + * Used for updating users in systems with V1 style user information + * (ie MongoDB v2.4 and prior) + */ + DB.prototype._updateUserV1 = function(name, updateObject, writeConcern) { + var setObj = {}; + if (updateObject.pwd) { + setObj["pwd"] = _hashPassword(name, updateObject.pwd); + } + if (updateObject.extraData) { + setObj["extraData"] = updateObject.extraData; + } + if (updateObject.roles) { + setObj["roles"] = updateObject.roles; + } - if (res.errmsg == "no such cmd: dropUsers") { - return this._removeUserV1(username, cmdObj['writeConcern']); - } + this.system.users.update({user: name, userSource: null}, {$set: setObj}); + var errObj = this.getLastErrorObj(writeConcern['w'], writeConcern['wtimeout']); + if (errObj.err) { + throw _getErrorWithCode(errObj, "Updating user failed: " + errObj.err); + } + }; - throw _getErrorWithCode(res, res.errmsg); -}; + DB.prototype.updateUser = function(name, updateObject, writeConcern) { + var cmdObj = { + updateUser: name + }; + cmdObj = Object.extend(cmdObj, updateObject); + cmdObj['writeConcern'] = writeConcern ? writeConcern : _defaultWriteConcern; + this._modifyCommandToDigestPasswordIfNecessary(cmdObj, name); -/** - * Used for removing users in systems with V1 style user information - * (ie MongoDB v2.4 and prior) - */ -DB.prototype._removeUserV1 = function(username, writeConcern) { - this.getCollection( "system.users" ).remove( { user : username } ); + var res = this.runCommand(cmdObj); + if (res.ok) { + return; + } - var le = this.getLastErrorObj(writeConcern['w'], writeConcern['wtimeout']); + if (res.errmsg == "no such cmd: updateUser") { + this._updateUserV1(name, updateObject, cmdObj['writeConcern']); + return; + } - if (le.err) { - throw _getErrorWithCode(le, "Couldn't remove user: " + le.err); - } + throw _getErrorWithCode(res, "Updating user failed: " + res.errmsg); + }; - if (le.n == 1) { - return true; - } else { - return false; - } -}; + DB.prototype.changeUserPassword = function(username, password, writeConcern) { + this.updateUser(username, {pwd: password}, writeConcern); + }; -DB.prototype.dropAllUsers = function(writeConcern) { - var res = this.runCommand({dropAllUsersFromDatabase:1, - writeConcern: writeConcern ? writeConcern : _defaultWriteConcern}); + DB.prototype.logout = function() { + return this.getMongo().logout(this.getName()); + }; + + // For backwards compatibility + DB.prototype.removeUser = function(username, writeConcern) { + print("WARNING: db.removeUser has been deprecated, please use db.dropUser instead"); + return this.dropUser(username, writeConcern); + }; + + DB.prototype.dropUser = function(username, writeConcern) { + var cmdObj = { + dropUser: username, + writeConcern: writeConcern ? writeConcern : _defaultWriteConcern + }; + var res = this.runCommand(cmdObj); + + if (res.ok) { + return true; + } + + if (res.code == 11) { // Code 11 = UserNotFound + return false; + } + + if (res.errmsg == "no such cmd: dropUsers") { + return this._removeUserV1(username, cmdObj['writeConcern']); + } - if (!res.ok) { throw _getErrorWithCode(res, res.errmsg); - } + }; - return res.n; -}; + /** + * Used for removing users in systems with V1 style user information + * (ie MongoDB v2.4 and prior) + */ + DB.prototype._removeUserV1 = function(username, writeConcern) { + this.getCollection("system.users").remove({user: username}); -DB.prototype.__pwHash = function( nonce, username, pass ) { - return hex_md5(nonce + username + _hashPassword(username, pass)); -}; + var le = this.getLastErrorObj(writeConcern['w'], writeConcern['wtimeout']); -DB.prototype._defaultAuthenticationMechanism = null; + if (le.err) { + throw _getErrorWithCode(le, "Couldn't remove user: " + le.err); + } -DB.prototype._getDefaultAuthenticationMechanism = function() { - // Use the default auth mechanism if set on the command line. - if (this._defaultAuthenticationMechanism != null) - return this._defaultAuthenticationMechanism; + if (le.n == 1) { + return true; + } else { + return false; + } + }; - // Use MONGODB-CR for v2.6 and earlier. - maxWireVersion = this.isMaster().maxWireVersion; - if (maxWireVersion == undefined || maxWireVersion < 3) { - return "MONGODB-CR"; - } - return "SCRAM-SHA-1"; -}; + DB.prototype.dropAllUsers = function(writeConcern) { + var res = this.runCommand({ + dropAllUsersFromDatabase: 1, + writeConcern: writeConcern ? writeConcern : _defaultWriteConcern + }); -DB.prototype._defaultGssapiServiceName = null; + if (!res.ok) { + throw _getErrorWithCode(res, res.errmsg); + } -DB.prototype._authOrThrow = function () { - var params; - if (arguments.length == 2) { - params = { user: arguments[0], pwd: arguments[1] }; - } - else if (arguments.length == 1) { - if (typeof(arguments[0]) != "object") - throw Error("Single-argument form of auth expects a parameter object"); - params = Object.extend({}, arguments[0]); - } - else { - throw Error( - "auth expects either (username, password) or ({ user: username, pwd: password })"); - } + return res.n; + }; - if (params.mechanism === undefined) - params.mechanism = this._getDefaultAuthenticationMechanism(); + DB.prototype.__pwHash = function(nonce, username, pass) { + return hex_md5(nonce + username + _hashPassword(username, pass)); + }; - if (params.db !== undefined) { - throw Error("Do not override db field on db.auth(). Use getMongo().auth(), instead."); - } + DB.prototype._defaultAuthenticationMechanism = null; - if (params.mechanism == "GSSAPI" && - params.serviceName == null && - this._defaultGssapiServiceName != null) { + DB.prototype._getDefaultAuthenticationMechanism = function() { + // Use the default auth mechanism if set on the command line. + if (this._defaultAuthenticationMechanism != null) + return this._defaultAuthenticationMechanism; - params.serviceName = this._defaultGssapiServiceName; - } + // Use MONGODB-CR for v2.6 and earlier. + maxWireVersion = this.isMaster().maxWireVersion; + if (maxWireVersion == undefined || maxWireVersion < 3) { + return "MONGODB-CR"; + } + return "SCRAM-SHA-1"; + }; - params.db = this.getName(); - var good = this.getMongo().auth(params); - if (good) { - // auth enabled, and should try to use isMaster and replSetGetStatus to build prompt - this.getMongo().authStatus = {authRequired:true, isMaster:true, replSetGetStatus:true}; - } + DB.prototype._defaultGssapiServiceName = null; + + DB.prototype._authOrThrow = function() { + var params; + if (arguments.length == 2) { + params = { + user: arguments[0], + pwd: arguments[1] + }; + } else if (arguments.length == 1) { + if (typeof(arguments[0]) != "object") + throw Error("Single-argument form of auth expects a parameter object"); + params = Object.extend({}, arguments[0]); + } else { + throw Error( + "auth expects either (username, password) or ({ user: username, pwd: password })"); + } - return good; -}; + if (params.mechanism === undefined) + params.mechanism = this._getDefaultAuthenticationMechanism(); + if (params.db !== undefined) { + throw Error("Do not override db field on db.auth(). Use getMongo().auth(), instead."); + } -DB.prototype.auth = function() { - var ex; - try { - this._authOrThrow.apply(this, arguments); - } catch (ex) { - print(ex); - return 0; - } - return 1; -}; - -DB.prototype.grantRolesToUser = function(username, roles, writeConcern) { - var cmdObj = {grantRolesToUser: username, - roles: roles, - writeConcern: writeConcern ? writeConcern : _defaultWriteConcern}; - var res = this.runCommand(cmdObj); - if (!res.ok) { - throw _getErrorWithCode(res, res.errmsg); - } -}; - -DB.prototype.revokeRolesFromUser = function(username, roles, writeConcern) { - var cmdObj = {revokeRolesFromUser: username, - roles: roles, - writeConcern: writeConcern ? writeConcern : _defaultWriteConcern}; - var res = this.runCommand(cmdObj); - if (!res.ok) { - throw _getErrorWithCode(res, res.errmsg); - } -}; + if (params.mechanism == "GSSAPI" && params.serviceName == null && + this._defaultGssapiServiceName != null) { + params.serviceName = this._defaultGssapiServiceName; + } -DB.prototype.getUser = function(username, args) { - if (typeof username != "string") { - throw Error("User name for getUser shell helper must be a string"); - } - var cmdObj = {usersInfo: username}; - Object.extend(cmdObj, args); + params.db = this.getName(); + var good = this.getMongo().auth(params); + if (good) { + // auth enabled, and should try to use isMaster and replSetGetStatus to build prompt + this.getMongo().authStatus = { + authRequired: true, + isMaster: true, + replSetGetStatus: true + }; + } - var res = this.runCommand(cmdObj); - if (!res.ok) { - throw _getErrorWithCode(res, res.errmsg); - } + return good; + }; - if (res.users.length == 0) { - return null; - } - return res.users[0]; -}; - -DB.prototype.getUsers = function(args) { - var cmdObj = {usersInfo: 1}; - Object.extend(cmdObj, args); - var res = this.runCommand(cmdObj); - if (!res.ok) { - var authSchemaIncompatibleCode = 69; - if (res.code == authSchemaIncompatibleCode || + DB.prototype.auth = function() { + var ex; + try { + this._authOrThrow.apply(this, arguments); + } catch (ex) { + print(ex); + return 0; + } + return 1; + }; + + DB.prototype.grantRolesToUser = function(username, roles, writeConcern) { + var cmdObj = { + grantRolesToUser: username, + roles: roles, + writeConcern: writeConcern ? writeConcern : _defaultWriteConcern + }; + var res = this.runCommand(cmdObj); + if (!res.ok) { + throw _getErrorWithCode(res, res.errmsg); + } + }; + + DB.prototype.revokeRolesFromUser = function(username, roles, writeConcern) { + var cmdObj = { + revokeRolesFromUser: username, + roles: roles, + writeConcern: writeConcern ? writeConcern : _defaultWriteConcern + }; + var res = this.runCommand(cmdObj); + if (!res.ok) { + throw _getErrorWithCode(res, res.errmsg); + } + }; + + DB.prototype.getUser = function(username, args) { + if (typeof username != "string") { + throw Error("User name for getUser shell helper must be a string"); + } + var cmdObj = { + usersInfo: username + }; + Object.extend(cmdObj, args); + + var res = this.runCommand(cmdObj); + if (!res.ok) { + throw _getErrorWithCode(res, res.errmsg); + } + + if (res.users.length == 0) { + return null; + } + return res.users[0]; + }; + + DB.prototype.getUsers = function(args) { + var cmdObj = { + usersInfo: 1 + }; + Object.extend(cmdObj, args); + var res = this.runCommand(cmdObj); + if (!res.ok) { + var authSchemaIncompatibleCode = 69; + if (res.code == authSchemaIncompatibleCode || (res.code == null && res.errmsg == "no such cmd: usersInfo")) { - // Working with 2.4 schema user data - return this.system.users.find({}).toArray(); + // Working with 2.4 schema user data + return this.system.users.find({}).toArray(); + } + + throw _getErrorWithCode(res, res.errmsg); } - throw _getErrorWithCode(res, res.errmsg); - } + return res.users; + }; - return res.users; -}; + DB.prototype.createRole = function(roleObj, writeConcern) { + var name = roleObj["role"]; + var cmdObj = { + createRole: name + }; + cmdObj = Object.extend(cmdObj, roleObj); + delete cmdObj["role"]; + cmdObj["writeConcern"] = writeConcern ? writeConcern : _defaultWriteConcern; -DB.prototype.createRole = function(roleObj, writeConcern) { - var name = roleObj["role"]; - var cmdObj = {createRole:name}; - cmdObj = Object.extend(cmdObj, roleObj); - delete cmdObj["role"]; - cmdObj["writeConcern"] = writeConcern ? writeConcern : _defaultWriteConcern; + var res = this.runCommand(cmdObj); - var res = this.runCommand(cmdObj); + if (!res.ok) { + throw _getErrorWithCode(res, res.errmsg); + } + printjson(roleObj); + }; + + DB.prototype.updateRole = function(name, updateObject, writeConcern) { + var cmdObj = { + updateRole: name + }; + cmdObj = Object.extend(cmdObj, updateObject); + cmdObj['writeConcern'] = writeConcern ? writeConcern : _defaultWriteConcern; + var res = this.runCommand(cmdObj); + if (!res.ok) { + throw _getErrorWithCode(res, res.errmsg); + } + }; + + DB.prototype.dropRole = function(name, writeConcern) { + var cmdObj = { + dropRole: name, + writeConcern: writeConcern ? writeConcern : _defaultWriteConcern + }; + var res = this.runCommand(cmdObj); + + if (res.ok) { + return true; + } + + if (res.code == 31) { // Code 31 = RoleNotFound + return false; + } - if (!res.ok) { - throw _getErrorWithCode(res, res.errmsg); - } - printjson(roleObj); -}; - -DB.prototype.updateRole = function(name, updateObject, writeConcern) { - var cmdObj = {updateRole:name}; - cmdObj = Object.extend(cmdObj, updateObject); - cmdObj['writeConcern'] = writeConcern ? writeConcern : _defaultWriteConcern; - var res = this.runCommand(cmdObj); - if (!res.ok) { throw _getErrorWithCode(res, res.errmsg); - } -}; + }; -DB.prototype.dropRole = function(name, writeConcern) { - var cmdObj = {dropRole:name, - writeConcern: writeConcern ? writeConcern : _defaultWriteConcern}; - var res = this.runCommand(cmdObj); + DB.prototype.dropAllRoles = function(writeConcern) { + var res = this.runCommand({ + dropAllRolesFromDatabase: 1, + writeConcern: writeConcern ? writeConcern : _defaultWriteConcern + }); - if (res.ok) { - return true; - } + if (!res.ok) { + throw _getErrorWithCode(res, res.errmsg); + } - if (res.code == 31) { // Code 31 = RoleNotFound - return false; - } + return res.n; + }; - throw _getErrorWithCode(res, res.errmsg); -}; + DB.prototype.grantRolesToRole = function(rolename, roles, writeConcern) { + var cmdObj = { + grantRolesToRole: rolename, + roles: roles, + writeConcern: writeConcern ? writeConcern : _defaultWriteConcern + }; + var res = this.runCommand(cmdObj); + if (!res.ok) { + throw _getErrorWithCode(res, res.errmsg); + } + }; -DB.prototype.dropAllRoles = function(writeConcern) { - var res = this.runCommand({dropAllRolesFromDatabase:1, - writeConcern: writeConcern ? writeConcern : _defaultWriteConcern}); + DB.prototype.revokeRolesFromRole = function(rolename, roles, writeConcern) { + var cmdObj = { + revokeRolesFromRole: rolename, + roles: roles, + writeConcern: writeConcern ? writeConcern : _defaultWriteConcern + }; + var res = this.runCommand(cmdObj); + if (!res.ok) { + throw _getErrorWithCode(res, res.errmsg); + } + }; - if (!res.ok) { - throw _getErrorWithCode(res, res.errmsg); - } + DB.prototype.grantPrivilegesToRole = function(rolename, privileges, writeConcern) { + var cmdObj = { + grantPrivilegesToRole: rolename, + privileges: privileges, + writeConcern: writeConcern ? writeConcern : _defaultWriteConcern + }; + var res = this.runCommand(cmdObj); + if (!res.ok) { + throw _getErrorWithCode(res, res.errmsg); + } + }; - return res.n; -}; + DB.prototype.revokePrivilegesFromRole = function(rolename, privileges, writeConcern) { + var cmdObj = { + revokePrivilegesFromRole: rolename, + privileges: privileges, + writeConcern: writeConcern ? writeConcern : _defaultWriteConcern + }; + var res = this.runCommand(cmdObj); + if (!res.ok) { + throw _getErrorWithCode(res, res.errmsg); + } + }; -DB.prototype.grantRolesToRole = function(rolename, roles, writeConcern) { - var cmdObj = {grantRolesToRole: rolename, - roles: roles, - writeConcern: writeConcern ? writeConcern : _defaultWriteConcern}; - var res = this.runCommand(cmdObj); - if (!res.ok) { - throw _getErrorWithCode(res, res.errmsg); - } -}; - -DB.prototype.revokeRolesFromRole = function(rolename, roles, writeConcern) { - var cmdObj = {revokeRolesFromRole: rolename, - roles: roles, - writeConcern: writeConcern ? writeConcern : _defaultWriteConcern}; - var res = this.runCommand(cmdObj); - if (!res.ok) { - throw _getErrorWithCode(res, res.errmsg); - } -}; - -DB.prototype.grantPrivilegesToRole = function(rolename, privileges, writeConcern) { - var cmdObj = {grantPrivilegesToRole: rolename, - privileges: privileges, - writeConcern: writeConcern ? writeConcern : _defaultWriteConcern}; - var res = this.runCommand(cmdObj); - if (!res.ok) { - throw _getErrorWithCode(res, res.errmsg); - } -}; - -DB.prototype.revokePrivilegesFromRole = function(rolename, privileges, writeConcern) { - var cmdObj = {revokePrivilegesFromRole: rolename, - privileges: privileges, - writeConcern: writeConcern ? writeConcern : _defaultWriteConcern}; - var res = this.runCommand(cmdObj); - if (!res.ok) { - throw _getErrorWithCode(res, res.errmsg); - } -}; + DB.prototype.getRole = function(rolename, args) { + if (typeof rolename != "string") { + throw Error("Role name for getRole shell helper must be a string"); + } + var cmdObj = { + rolesInfo: rolename + }; + Object.extend(cmdObj, args); + var res = this.runCommand(cmdObj); + if (!res.ok) { + throw _getErrorWithCode(res, res.errmsg); + } -DB.prototype.getRole = function(rolename, args) { - if (typeof rolename != "string") { - throw Error("Role name for getRole shell helper must be a string"); - } - var cmdObj = {rolesInfo:rolename}; - Object.extend(cmdObj, args); - var res = this.runCommand(cmdObj); - if (!res.ok) { - throw _getErrorWithCode(res, res.errmsg); - } + if (res.roles.length == 0) { + return null; + } + return res.roles[0]; + }; - if (res.roles.length == 0) { - return null; - } - return res.roles[0]; -}; - -DB.prototype.getRoles = function(args) { - var cmdObj = {rolesInfo:1}; - Object.extend(cmdObj, args); - var res = this.runCommand(cmdObj); - if (!res.ok) { - throw _getErrorWithCode(res, res.errmsg); - } + DB.prototype.getRoles = function(args) { + var cmdObj = { + rolesInfo: 1 + }; + Object.extend(cmdObj, args); + var res = this.runCommand(cmdObj); + if (!res.ok) { + throw _getErrorWithCode(res, res.errmsg); + } - return res.roles; -}; + return res.roles; + }; -DB.prototype.setWriteConcern = function( wc ) { - if ( wc instanceof WriteConcern ) { - this._writeConcern = wc; - } - else { - this._writeConcern = new WriteConcern( wc ); - } -}; + DB.prototype.setWriteConcern = function(wc) { + if (wc instanceof WriteConcern) { + this._writeConcern = wc; + } else { + this._writeConcern = new WriteConcern(wc); + } + }; -DB.prototype.getWriteConcern = function() { - if (this._writeConcern) - return this._writeConcern; - - if (this._mongo.getWriteConcern()) - return this._mongo.getWriteConcern(); + DB.prototype.getWriteConcern = function() { + if (this._writeConcern) + return this._writeConcern; - return null; -}; + if (this._mongo.getWriteConcern()) + return this._mongo.getWriteConcern(); -DB.prototype.unsetWriteConcern = function() { - delete this._writeConcern; -}; + return null; + }; -DB.prototype.getLogComponents = function() { - return this.getMongo().getLogComponents(); -}; + DB.prototype.unsetWriteConcern = function() { + delete this._writeConcern; + }; -DB.prototype.setLogLevel = function(logLevel, component) { - return this.getMongo().setLogLevel(logLevel, component); -}; + DB.prototype.getLogComponents = function() { + return this.getMongo().getLogComponents(); + }; + + DB.prototype.setLogLevel = function(logLevel, component) { + return this.getMongo().setLogLevel(logLevel, component); + }; }()); diff --git a/src/mongo/shell/explain_query.js b/src/mongo/shell/explain_query.js index 86f3ed23a2b..3a90e17d4eb 100644 --- a/src/mongo/shell/explain_query.js +++ b/src/mongo/shell/explain_query.js @@ -24,12 +24,12 @@ var DBExplainQuery = (function() { delete obj.stats; if (typeof(obj.length) === "number") { - for (var i=0; i < obj.length; i++) { + for (var i = 0; i < obj.length; i++) { removeVerboseFields(obj[i]); } } - if (obj.shards){ + if (obj.shards) { for (var key in obj.shards) { removeVerboseFields(obj.shards[key]); } @@ -75,7 +75,6 @@ var DBExplainQuery = (function() { } function constructor(query, verbosity) { - // // Private vars. // @@ -136,7 +135,7 @@ var DBExplainQuery = (function() { // Explain always gets pretty printed. this._query._prettyShell = true; - + if (this._mongo.hasExplainCommand()) { // The wire protocol version indicates that the server has the explain command. // Convert this explain query into an explain command, and send the command to @@ -145,13 +144,14 @@ var DBExplainQuery = (function() { if (this._isCount) { // True means to always apply the skip and limit values. innerCmd = this._query._convertToCountCmd(this._applySkipLimit); - } - else { + } else { var canAttachReadPref = false; innerCmd = this._query._convertToCommand(canAttachReadPref); } - var explainCmd = {explain: innerCmd}; + var explainCmd = { + explain: innerCmd + }; explainCmd["verbosity"] = this._verbosity; var explainDb = this._query._db; @@ -171,8 +171,7 @@ var DBExplainQuery = (function() { } return Explainable.throwOrReturn(explainResult); - } - else { + } else { return explainWithLegacyQueryOption(this); } }; @@ -240,7 +239,6 @@ var DBExplainQuery = (function() { print("\t.sort(sortSpec)"); return __magicNoPrint; }; - } return constructor; diff --git a/src/mongo/shell/explainable.js b/src/mongo/shell/explainable.js index b4fcf21020a..5a6d4932c40 100644 --- a/src/mongo/shell/explainable.js +++ b/src/mongo/shell/explainable.js @@ -17,13 +17,10 @@ var Explainable = (function() { } // If we're here, then the verbosity is a string. We reject invalid strings. - if (verbosity !== "queryPlanner" && - verbosity !== "executionStats" && + if (verbosity !== "queryPlanner" && verbosity !== "executionStats" && verbosity !== "allPlansExecution") { - throw Error("explain verbosity must be one of {" + - "'queryPlanner'," + - "'executionStats'," + - "'allPlansExecution'}"); + throw Error("explain verbosity must be one of {" + "'queryPlanner'," + + "'executionStats'," + "'allPlansExecution'}"); } return verbosity; @@ -38,7 +35,6 @@ var Explainable = (function() { }; function constructor(collection, verbosity) { - // // Private vars. // @@ -85,7 +81,7 @@ var Explainable = (function() { // this.toString = function() { - return "Explainable(" + this._collection.getFullName() + ")"; + return "Explainable(" + this._collection.getFullName() + ")"; }; this.shellPrint = function() { @@ -130,24 +126,37 @@ var Explainable = (function() { this.findAndModify = function(params) { var famCmd = Object.extend({"findAndModify": this._collection.getName()}, params); - var explainCmd = {"explain": famCmd, "verbosity": this._verbosity}; + var explainCmd = { + "explain": famCmd, + "verbosity": this._verbosity + }; var explainResult = this._collection.runReadCommand(explainCmd); return throwOrReturn(explainResult); }; this.group = function(params) { params.ns = this._collection.getName(); - var grpCmd = {"group": this._collection.getDB()._groupFixParms(params)}; - var explainCmd = {"explain": grpCmd, "verbosity": this._verbosity}; + var grpCmd = { + "group": this._collection.getDB()._groupFixParms(params) + }; + var explainCmd = { + "explain": grpCmd, + "verbosity": this._verbosity + }; var explainResult = this._collection.runReadCommand(explainCmd); return throwOrReturn(explainResult); }; this.distinct = function(keyString, query) { - var distinctCmd = {distinct: this._collection.getName(), - key: keyString, - query: query || {}}; - var explainCmd = {explain: distinctCmd, verbosity: this._verbosity}; + var distinctCmd = { + distinct: this._collection.getName(), + key: keyString, + query: query || {} + }; + var explainCmd = { + explain: distinctCmd, + verbosity: this._verbosity + }; var explainResult = this._collection.runReadCommand(explainCmd); return throwOrReturn(explainResult); }; @@ -161,8 +170,7 @@ var Explainable = (function() { var removeOp = bulk.find(query); if (justOne) { removeOp.removeOne(); - } - else { + } else { removeOp.remove(); } @@ -187,8 +195,7 @@ var Explainable = (function() { if (multi) { updateOp.update(obj); - } - else { + } else { updateOp.updateOne(obj); } @@ -196,7 +203,6 @@ var Explainable = (function() { var explainResult = this._collection.runCommand(explainCmd); return throwOrReturn(explainResult); }; - } // diff --git a/src/mongo/shell/mongo.js b/src/mongo/shell/mongo.js index 68bc0ac54d8..35eae747f24 100644 --- a/src/mongo/shell/mongo.js +++ b/src/mongo/shell/mongo.js @@ -1,31 +1,40 @@ // 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 (typeof Mongo == "undefined") { + Mongo = function(host) { + this.init(host); }; } -if ( ! Mongo.prototype ){ +if (!Mongo.prototype) { throw Error("Mongo.prototype not defined"); } -if ( ! Mongo.prototype.find ) - Mongo.prototype.find = function( ns , query , fields , limit , skip , batchSize , options ){ throw Error("find not implemented"); }; -if ( ! Mongo.prototype.insert ) - Mongo.prototype.insert = function( ns , obj ){ throw Error("insert not implemented"); }; -if ( ! Mongo.prototype.remove ) - Mongo.prototype.remove = function( ns , pattern ){ throw Error("remove not implemented"); }; -if ( ! Mongo.prototype.update ) - Mongo.prototype.update = function( ns , query , obj , upsert ){ throw Error("update not implemented"); }; - -if ( typeof mongoInject == "function" ){ - mongoInject( Mongo.prototype ); +if (!Mongo.prototype.find) + Mongo.prototype.find = function(ns, query, fields, limit, skip, batchSize, options) { + throw Error("find not implemented"); + }; +if (!Mongo.prototype.insert) + Mongo.prototype.insert = function(ns, obj) { + throw Error("insert not implemented"); + }; +if (!Mongo.prototype.remove) + Mongo.prototype.remove = function(ns, pattern) { + throw Error("remove not implemented"); + }; +if (!Mongo.prototype.update) + Mongo.prototype.update = function(ns, query, obj, upsert) { + throw Error("update not implemented"); + }; + +if (typeof mongoInject == "function") { + mongoInject(Mongo.prototype); } -Mongo.prototype.setSlaveOk = function( value ) { - if( value == undefined ) value = true; +Mongo.prototype.setSlaveOk = function(value) { + if (value == undefined) + value = true; this.slaveOk = value; }; @@ -33,9 +42,9 @@ Mongo.prototype.getSlaveOk = function() { return this.slaveOk || false; }; -Mongo.prototype.getDB = function( name ){ +Mongo.prototype.getDB = function(name) { if ((jsTest.options().keyFile) && - ((typeof this.authenticated == 'undefined') || !this.authenticated)) { + ((typeof this.authenticated == 'undefined') || !this.authenticated)) { jsTest.authenticate(this); } // There is a weird issue where typeof(db._name) !== "string" when the db name @@ -44,25 +53,25 @@ Mongo.prototype.getDB = function( name ){ if (typeof(name) === "object") { name = name.toString(); } - return new DB( this , name ); + return new DB(this, name); }; -Mongo.prototype.getDBs = function(){ - var res = this.getDB( "admin" ).runCommand( { "listDatabases" : 1 } ); - if ( ! res.ok ) +Mongo.prototype.getDBs = function() { + var res = this.getDB("admin").runCommand({"listDatabases": 1}); + if (!res.ok) throw _getErrorWithCode(res, "listDatabases failed:" + tojson(res)); return res; }; -Mongo.prototype.adminCommand = function( cmd ){ - return this.getDB( "admin" ).runCommand( cmd ); +Mongo.prototype.adminCommand = function(cmd) { + return this.getDB("admin").runCommand(cmd); }; /** * Returns all log components and current verbosity values */ Mongo.prototype.getLogComponents = function() { - var res = this.adminCommand({ getParameter:1, logComponentVerbosity:1 }); + var res = this.adminCommand({getParameter: 1, logComponentVerbosity: 1}); if (!res.ok) throw _getErrorWithCode(res, "getLogComponents failed:" + tojson(res)); return res.logComponentVerbosity; @@ -76,43 +85,42 @@ Mongo.prototype.setLogLevel = function(logLevel, component) { componentNames = []; if (typeof component === "string") { componentNames = component.split("."); + } else if (component !== undefined) { + throw Error("setLogLevel component must be a string:" + tojson(component)); } - else if (component !== undefined) { - throw Error( "setLogLevel component must be a string:" + tojson(component)); - } - var vDoc = { verbosity: logLevel }; + var vDoc = { + verbosity: logLevel + }; // nest vDoc - for (var key,obj; componentNames.length > 0;) { + for (var key, obj; componentNames.length > 0;) { obj = {}; key = componentNames.pop(); - obj[ key ] = vDoc; + obj[key] = vDoc; vDoc = obj; } - var res = this.adminCommand({ setParameter : 1, logComponentVerbosity : vDoc }); + var res = this.adminCommand({setParameter: 1, logComponentVerbosity: vDoc}); if (!res.ok) throw _getErrorWithCode(res, "setLogLevel failed:" + tojson(res)); return res; }; -Mongo.prototype.getDBNames = function(){ - return this.getDBs().databases.map( - function(z){ - return z.name; - } - ); +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 ) +Mongo.prototype.getCollection = function(ns) { + var idx = ns.indexOf("."); + if (idx < 0) throw Error("need . in ns"); - var db = ns.substring( 0 , idx ); - var c = ns.substring( idx + 1 ); - return this.getDB( db ).getCollection( c ); + var db = ns.substring(0, idx); + var c = ns.substring(idx + 1); + return this.getDB(db).getCollection(c); }; -Mongo.prototype.toString = function(){ +Mongo.prototype.toString = function() { return "connection to " + this.host; }; Mongo.prototype.tojson = Mongo.prototype.toString; @@ -125,9 +133,8 @@ Mongo.prototype.tojson = Mongo.prototype.toString; * @param tagSet {Array.<Object>} optional. The list of tags to use, order matters. * Note that this object only keeps a shallow copy of this array. */ -Mongo.prototype.setReadPref = function (mode, tagSet) { - if ((this._readPrefMode === "primary") && - (typeof(tagSet) !== "undefined") && +Mongo.prototype.setReadPref = function(mode, tagSet) { + if ((this._readPrefMode === "primary") && (typeof(tagSet) !== "undefined") && (Object.keys(tagSet).length > 0)) { // we allow empty arrays/objects or no tagSet for compatibility reasons throw Error("Can not supply tagSet with readPref mode primary"); @@ -141,21 +148,20 @@ Mongo.prototype._setReadPrefUnsafe = function(mode, tagSet) { this._readPrefTagSet = tagSet; }; -Mongo.prototype.getReadPrefMode = function () { +Mongo.prototype.getReadPrefMode = function() { return this._readPrefMode; }; -Mongo.prototype.getReadPrefTagSet = function () { +Mongo.prototype.getReadPrefTagSet = function() { return this._readPrefTagSet; }; // Returns a readPreference object of the type expected by mongos. -Mongo.prototype.getReadPref = function () { +Mongo.prototype.getReadPref = function() { var obj = {}, mode, tagSet; if (typeof(mode = this.getReadPrefMode()) === "string") { obj.mode = mode; - } - else { + } else { return null; } // Server Selection Spec: - if readPref mode is "primary" then the tags field MUST @@ -182,8 +188,8 @@ connect = function(url, user, pass) { throw Error("Missing connection string"); } if (urlType != "string") { - throw Error("Incorrect type \"" + urlType + - "\" for connection string \"" + tojson(url) + "\""); + throw Error("Incorrect type \"" + urlType + "\" for connection string \"" + tojson(url) + + "\""); } url = url.trim(); if (0 == url.length) { @@ -200,11 +206,10 @@ connect = function(url, user, pass) { } if (colon != -1 && colon < slash) { var portNumber = url.substring(colon + 1, slash); - if (portNumber.length > 5 || - !/^\d*$/.test(portNumber) || - parseInt(portNumber) > 65535) { - throw Error("Invalid port number \"" + portNumber + - "\" in connection string \"" + url + "\""); + if (portNumber.length > 5 || !/^\d*$/.test(portNumber) || + parseInt(portNumber) > 65535) { + throw Error("Invalid port number \"" + portNumber + "\" in connection string \"" + + url + "\""); } } if (slash == url.length - 1) { @@ -234,54 +239,51 @@ connect = function(url, user, pass) { }; /** deprecated, use writeMode below - * + * */ Mongo.prototype.useWriteCommands = function() { - return (this.writeMode() != "legacy"); + return (this.writeMode() != "legacy"); }; -Mongo.prototype.forceWriteMode = function( mode ) { +Mongo.prototype.forceWriteMode = function(mode) { this._writeMode = mode; }; Mongo.prototype.hasWriteCommands = function() { - var hasWriteCommands = (this.getMinWireVersion() <= 2 && - 2 <= this.getMaxWireVersion()); + var hasWriteCommands = (this.getMinWireVersion() <= 2 && 2 <= this.getMaxWireVersion()); return hasWriteCommands; }; Mongo.prototype.hasExplainCommand = function() { - var hasExplain = (this.getMinWireVersion() <= 3 && - 3 <= this.getMaxWireVersion()); + var hasExplain = (this.getMinWireVersion() <= 3 && 3 <= this.getMaxWireVersion()); return hasExplain; }; /** * {String} Returns the current mode set. Will be commands/legacy/compatibility - * + * * Sends isMaster to determine if the connection is capable of using bulk write operations, and * caches the result. */ Mongo.prototype.writeMode = function() { - if ( '_writeMode' in this ) { + if ('_writeMode' in this) { return this._writeMode; } // get default from shell params - if ( _writeMode ) + if (_writeMode) this._writeMode = _writeMode(); - + // can't use "commands" mode unless server version is good. - if ( this.hasWriteCommands() ) { + if (this.hasWriteCommands()) { // good with whatever is already set - } - else if ( this._writeMode == "commands" ) { + } else if (this._writeMode == "commands") { print("Cannot use commands write mode, degrading to compatibility mode"); this._writeMode = "compatibility"; } - + return this._writeMode; }; @@ -320,22 +322,18 @@ Mongo.prototype.readMode = function() { if (this.hasOwnProperty("_readMode") && this._readMode !== "compatibility") { // We already have determined our read mode. Just return it. return this._readMode; - } - else { + } else { // We're in compatibility mode. Determine whether the server supports the find/getMore // commands. If it does, use commands mode. If not, degrade to legacy mode. try { - var hasReadCommands = (this.getMinWireVersion() <= 4 && - 4 <= this.getMaxWireVersion()); + var hasReadCommands = (this.getMinWireVersion() <= 4 && 4 <= this.getMaxWireVersion()); if (hasReadCommands) { this._readMode = "commands"; - } - else { + } else { print("Cannot use 'commands' readMode, degrading to 'legacy' mode"); this._readMode = "legacy"; } - } - catch (e) { + } catch (e) { // We failed trying to determine whether the remote node supports the find/getMore // commands. In this case, we keep _readMode as "compatibility" and the shell should // issue legacy reads. Next time around we will issue another isMaster to try to @@ -351,12 +349,11 @@ Mongo.prototype.readMode = function() { // overridden at the collection level. // -Mongo.prototype.setWriteConcern = function( wc ) { - if ( wc instanceof WriteConcern ) { +Mongo.prototype.setWriteConcern = function(wc) { + if (wc instanceof WriteConcern) { this._writeConcern = wc; - } - else { - this._writeConcern = new WriteConcern( wc ); + } else { + this._writeConcern = new WriteConcern(wc); } }; diff --git a/src/mongo/shell/mr.js b/src/mongo/shell/mr.js index fbc7d2bbf17..a810a7f0d79 100644 --- a/src/mongo/shell/mr.js +++ b/src/mongo/shell/mr.js @@ -2,94 +2,94 @@ MR = {}; -MR.init = function(){ +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 + gc(); // this is just so that keep memory size sane }; -MR.cleanup = function(){ +MR.cleanup = function() { MR.init(); gc(); }; -MR.emit = function(k,v){ +MR.emit = function(k, v) { $numEmits++; - var num = nativeHelper.apply( get_num_ , [ k ] ); + var num = nativeHelper.apply(get_num_, [k]); var data = $arr[num]; - if ( ! data ){ - data = { key : k , values : new Array(1000) , count : 0 }; + 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 ); + $max = Math.max($max, data.count); }; -MR.doReduce = function( useDB ){ +MR.doReduce = function(useDB) { $numReduces++; - if ( useDB ) + if (useDB) $numReducesToDB++; $max = 0; - for ( var i=0; i<$arr.length; i++){ + for (var i = 0; i < $arr.length; i++) { var data = $arr[i]; - if ( ! data ) + if (!data) continue; - - if ( useDB ){ - var x = tempcoll.findOne( { _id : data.key } ); - if ( x ){ + + 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; + 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{ + } 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 ) } ); + + $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.check = function() { + if ($max < 2000 && $arr.length < 1000) { + return 0; } MR.doReduce(); - if ( $max < 2000 && $arr.length < 1000 ){ + if ($max < 2000 && $arr.length < 1000) { return 1; } - MR.doReduce( true ); - $arr = []; - $max = 0; + 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 ); - } - ); +MR.finalize = function() { + tempcoll.find().forEach(function(z) { + z.value = $finalize(z._id, z.value); + tempcoll.save(z); + }); }; diff --git a/src/mongo/shell/query.js b/src/mongo/shell/query.js index ac9f811a19f..d18aeff12f6 100644 --- a/src/mongo/shell/query.js +++ b/src/mongo/shell/query.js @@ -1,17 +1,17 @@ // query.js -if ( typeof DBQuery == "undefined" ){ - DBQuery = function( mongo , db , collection , ns , query , fields , limit , skip , batchSize , options ){ - - 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 +if (typeof DBQuery == "undefined") { + DBQuery = function(mongo, db, collection, ns, query, fields, limit, skip, batchSize, options) { + + 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._batchSize = batchSize || 0; this._options = options || 0; @@ -20,10 +20,10 @@ if ( typeof DBQuery == "undefined" ){ this._special = false; this._prettyShell = false; }; - print( "DBQuery probably won't have array access " ); + print("DBQuery probably won't have array access "); } -DBQuery.prototype.help = function () { +DBQuery.prototype.help = function() { print("find(<predicate>, <projection>) modifiers"); print("\t.sort({...})"); print("\t.limit(<n>)"); @@ -32,9 +32,11 @@ DBQuery.prototype.help = function () { print("\t.hint({...})"); print("\t.readConcern(<level>)"); print("\t.readPref(<mode>, <tagset>)"); - print("\t.count(<applySkipLimit>) - total # of objects matching query. by default ignores skip,limit"); + print( + "\t.count(<applySkipLimit>) - total # of objects matching query. by default ignores skip,limit"); print("\t.size() - total # of objects cursor would return, honors skip,limit"); - print("\t.explain(<verbosity>) - accepted verbosities are {'queryPlanner', 'executionStats', 'allPlansExecution'}"); + print( + "\t.explain(<verbosity>) - accepted verbosities are {'queryPlanner', 'executionStats', 'allPlansExecution'}"); print("\t.min({...})"); print("\t.max({...})"); print("\t.maxScan(<n>)"); @@ -54,32 +56,43 @@ DBQuery.prototype.help = function () { print("\t.hasNext()"); print("\t.next()"); print("\t.close()"); - print("\t.objsLeftInBatch() - returns count of docs left in current batch (when exhausted, a new getMore will be issued)"); + print( + "\t.objsLeftInBatch() - returns count of docs left in current batch (when exhausted, a new getMore will be issued)"); print("\t.itcount() - iterates through documents and counts them"); - print("\t.getQueryPlan() - get query plans associated with shape. To get more info on query plans, " + - "call getQueryPlan().help()."); + print( + "\t.getQueryPlan() - get query plans associated with shape. To get more info on query plans, " + + "call getQueryPlan().help()."); print("\t.pretty() - pretty print each document, possibly over multiple lines"); }; -DBQuery.prototype.clone = function(){ - var q = new DBQuery( this._mongo , this._db , this._collection , this._ns , - this._query , this._fields , - this._limit , this._skip , this._batchSize , this._options ); +DBQuery.prototype.clone = function() { + var q = new DBQuery(this._mongo, + this._db, + this._collection, + this._ns, + this._query, + this._fields, + this._limit, + this._skip, + this._batchSize, + this._options); q._special = this._special; return q; }; -DBQuery.prototype._ensureSpecial = function(){ - if ( this._special ) +DBQuery.prototype._ensureSpecial = function() { + if (this._special) return; - var n = { query : this._query }; + var n = { + query: this._query + }; this._query = n; this._special = true; }; -DBQuery.prototype._checkModify = function(){ - if ( this._cursor ) +DBQuery.prototype._checkModify = function() { + if (this._cursor) throw Error("query already executed"); }; @@ -89,13 +102,13 @@ DBQuery.prototype._canUseFindCommand = function() { // // We also forbid queries with the exhaust option from running as find commands, because the // find command does not support exhaust. - return (this._collection.getName().indexOf("$cmd") !== 0) - && (this._options & DBQuery.Option.exhaust) === 0; + return (this._collection.getName().indexOf("$cmd") !== 0) && + (this._options & DBQuery.Option.exhaust) === 0; }; -DBQuery.prototype._exec = function(){ - if ( ! this._cursor ){ - assert.eq( 0 , this._numReturned ); +DBQuery.prototype._exec = function() { + if (!this._cursor) { + assert.eq(0, this._numReturned); this._cursorSeen = 0; if (this._mongo.useReadCommands() && this._canUseFindCommand()) { @@ -103,8 +116,7 @@ DBQuery.prototype._exec = function(){ var findCmd = this._convertToCommand(canAttachReadPref); var cmdRes = this._db.runReadCommand(findCmd, null, this._options); this._cursor = new DBCommandCursor(this._mongo, cmdRes, this._batchSize); - } - else { + } else { if (this._special && this._query.readConcern) { throw new Error("readConcern requires use of read commands"); } @@ -136,8 +148,7 @@ DBQuery.prototype._convertToCommand = function(canAttachReadPref) { if (this._query.query) { cmd["filter"] = this._query.query; } - } - else if (this._query) { + } else if (this._query) { cmd["filter"] = this._query; } @@ -147,10 +158,10 @@ DBQuery.prototype._convertToCommand = function(canAttachReadPref) { if (this._batchSize) { if (this._batchSize < 0) { - cmd["batchSize"] = -this._batchSize; - cmd["singleBatch"] = true; + cmd["batchSize"] = -this._batchSize; + cmd["singleBatch"] = true; } else { - cmd["batchSize"] = this._batchSize; + cmd["batchSize"] = this._batchSize; } } @@ -158,8 +169,7 @@ DBQuery.prototype._convertToCommand = function(canAttachReadPref) { if (this._limit < 0) { cmd["limit"] = -this._limit; cmd["singleBatch"] = true; - } - else { + } else { cmd["limit"] = this._limit; cmd["singleBatch"] = false; } @@ -244,34 +254,33 @@ DBQuery.prototype._convertToCommand = function(canAttachReadPref) { return cmd; }; -DBQuery.prototype.limit = function( limit ){ +DBQuery.prototype.limit = function(limit) { this._checkModify(); this._limit = limit; return this; }; -DBQuery.prototype.batchSize = function( batchSize ){ +DBQuery.prototype.batchSize = function(batchSize) { this._checkModify(); this._batchSize = batchSize; return this; }; - -DBQuery.prototype.addOption = function( option ){ +DBQuery.prototype.addOption = function(option) { this._options |= option; return this; }; -DBQuery.prototype.skip = function( skip ){ +DBQuery.prototype.skip = function(skip) { this._checkModify(); this._skip = skip; return this; }; -DBQuery.prototype.hasNext = function(){ +DBQuery.prototype.hasNext = function() { this._exec(); - if ( this._limit > 0 && this._cursorSeen >= this._limit ) { + if (this._limit > 0 && this._cursorSeen >= this._limit) { this._cursor.close(); return false; } @@ -279,102 +288,104 @@ DBQuery.prototype.hasNext = function(){ return o; }; -DBQuery.prototype.next = function(){ +DBQuery.prototype.next = function() { this._exec(); var o = this._cursor.hasNext(); - if ( o ) + if (o) this._cursorSeen++; else - throw Error( "error hasNext: " + o ); + throw Error("error hasNext: " + o); var ret = this._cursor.next(); - if ( ret.$err ) { - throw _getErrorWithCode(ret, "error: " + tojson( ret )); + if (ret.$err) { + throw _getErrorWithCode(ret, "error: " + tojson(ret)); } this._numReturned++; return ret; }; -DBQuery.prototype.objsLeftInBatch = function(){ +DBQuery.prototype.objsLeftInBatch = function() { this._exec(); var ret = this._cursor.objsLeftInBatch(); - if ( ret.$err ) - throw _getErrorWithCode(ret, "error: " + tojson( ret )); + if (ret.$err) + throw _getErrorWithCode(ret, "error: " + tojson(ret)); return ret; }; -DBQuery.prototype.readOnly = function(){ +DBQuery.prototype.readOnly = function() { this._exec(); this._cursor.readOnly(); return this; }; -DBQuery.prototype.toArray = function(){ - if ( this._arr ) +DBQuery.prototype.toArray = function() { + if (this._arr) return this._arr; var a = []; - while ( this.hasNext() ) - a.push( this.next() ); + while (this.hasNext()) + a.push(this.next()); this._arr = a; return a; }; -DBQuery.prototype._convertToCountCmd = function( applySkipLimit ) { - var cmd = { count: this._collection.getName() }; +DBQuery.prototype._convertToCountCmd = function(applySkipLimit) { + var cmd = { + count: this._collection.getName() + }; - if ( this._query ) { - if ( this._special ) { + if (this._query) { + if (this._special) { cmd.query = this._query.query; - if ( this._query.$maxTimeMS ) { + if (this._query.$maxTimeMS) { cmd.maxTimeMS = this._query.$maxTimeMS; } - if ( this._query.$hint ) { + if (this._query.$hint) { cmd.hint = this._query.$hint; } - if ( this._query.readConcern ) { + if (this._query.readConcern) { cmd.readConcern = this._query.readConcern; } - } - else { + } else { cmd.query = this._query; } } cmd.fields = this._fields || {}; - if ( applySkipLimit ) { - if ( this._limit ) + if (applySkipLimit) { + if (this._limit) cmd.limit = this._limit; - if ( this._skip ) + if (this._skip) cmd.skip = this._skip; } return cmd; }; -DBQuery.prototype.count = function( applySkipLimit ) { - var cmd = this._convertToCountCmd( applySkipLimit ); +DBQuery.prototype.count = function(applySkipLimit) { + var cmd = this._convertToCountCmd(applySkipLimit); - var res = this._db.runReadCommand( cmd ); - if( res && res.n != null ) return res.n; - throw _getErrorWithCode(res, "count failed: " + tojson( res )); + var res = this._db.runReadCommand(cmd); + if (res && res.n != null) + return res.n; + throw _getErrorWithCode(res, "count failed: " + tojson(res)); }; -DBQuery.prototype.size = function(){ - return this.count( true ); +DBQuery.prototype.size = function() { + return this.count(true); }; -DBQuery.prototype.countReturn = function(){ +DBQuery.prototype.countReturn = function() { var c = this.count(); - if ( this._skip ) + if (this._skip) c = c - this._skip; - if ( this._limit > 0 && this._limit < c ) + if (this._limit > 0 && this._limit < c) return this._limit; return c; @@ -383,7 +394,7 @@ DBQuery.prototype.countReturn = function(){ /** * iterative count - only for testing */ -DBQuery.prototype.itcount = function(){ +DBQuery.prototype.itcount = function() { var num = 0; // Track how many bytes we've used this cursor to iterate iterated. This function can be called @@ -393,7 +404,7 @@ DBQuery.prototype.itcount = function(){ // TODO: migrate this function into c++ var bytesSinceGC = 0; - while ( this.hasNext() ){ + while (this.hasNext()) { num++; var nextDoc = this.next(); bytesSinceGC += Object.bsonsize(nextDoc); @@ -407,30 +418,30 @@ DBQuery.prototype.itcount = function(){ return num; }; -DBQuery.prototype.length = function(){ +DBQuery.prototype.length = function() { return this.toArray().length; }; -DBQuery.prototype._addSpecial = function( name , value ){ +DBQuery.prototype._addSpecial = function(name, value) { this._ensureSpecial(); this._query[name] = value; return this; }; -DBQuery.prototype.sort = function( sortBy ){ - return this._addSpecial( "orderby" , sortBy ); +DBQuery.prototype.sort = function(sortBy) { + return this._addSpecial("orderby", sortBy); }; -DBQuery.prototype.hint = function( hint ){ - return this._addSpecial( "$hint" , hint ); +DBQuery.prototype.hint = function(hint) { + return this._addSpecial("$hint", hint); }; -DBQuery.prototype.min = function( min ) { - return this._addSpecial( "$min" , min ); +DBQuery.prototype.min = function(min) { + return this._addSpecial("$min", min); }; -DBQuery.prototype.max = function( max ) { - return this._addSpecial( "$max" , max ); +DBQuery.prototype.max = function(max) { + return this._addSpecial("$max", max); }; /** @@ -444,16 +455,16 @@ DBQuery.prototype.showRecordId = function() { return this._addSpecial("$showDiskLoc", true); }; -DBQuery.prototype.maxTimeMS = function( maxTimeMS ) { - return this._addSpecial( "$maxTimeMS" , maxTimeMS ); +DBQuery.prototype.maxTimeMS = function(maxTimeMS) { + return this._addSpecial("$maxTimeMS", maxTimeMS); }; -DBQuery.prototype.readConcern = function( level ) { +DBQuery.prototype.readConcern = function(level) { var readConcernObj = { level: level }; - return this._addSpecial( "readConcern", readConcernObj ); + return this._addSpecial("readConcern", readConcernObj); }; /** @@ -465,83 +476,81 @@ DBQuery.prototype.readConcern = function( level ) { * * @return this cursor */ -DBQuery.prototype.readPref = function( mode, tagSet ) { +DBQuery.prototype.readPref = function(mode, tagSet) { var readPrefObj = { mode: mode }; - if ( tagSet ){ + if (tagSet) { readPrefObj.tags = tagSet; } - return this._addSpecial( "$readPreference", readPrefObj ); + return this._addSpecial("$readPreference", readPrefObj); }; -DBQuery.prototype.forEach = function( func ){ - while ( this.hasNext() ) - func( this.next() ); +DBQuery.prototype.forEach = function(func) { + while (this.hasNext()) + func(this.next()); }; -DBQuery.prototype.map = function( func ){ +DBQuery.prototype.map = function(func) { var a = []; - while ( this.hasNext() ) - a.push( func( this.next() ) ); + while (this.hasNext()) + a.push(func(this.next())); return a; }; -DBQuery.prototype.arrayAccess = function( idx ){ +DBQuery.prototype.arrayAccess = function(idx) { return this.toArray()[idx]; }; -DBQuery.prototype.comment = function (comment) { - return this._addSpecial( "$comment" , comment ); +DBQuery.prototype.comment = function(comment) { + return this._addSpecial("$comment", comment); }; -DBQuery.prototype.explain = function (verbose) { +DBQuery.prototype.explain = function(verbose) { var explainQuery = new DBExplainQuery(this, verbose); return explainQuery.finish(); }; -DBQuery.prototype.snapshot = function(){ - return this._addSpecial( "$snapshot" , true ); +DBQuery.prototype.snapshot = function() { + return this._addSpecial("$snapshot", true); }; -DBQuery.prototype.returnKey = function(){ - return this._addSpecial( "$returnKey" , true ); +DBQuery.prototype.returnKey = function() { + return this._addSpecial("$returnKey", true); }; -DBQuery.prototype.maxScan = function(n){ - return this._addSpecial( "$maxScan" , n ); +DBQuery.prototype.maxScan = function(n) { + return this._addSpecial("$maxScan", n); }; -DBQuery.prototype.pretty = function(){ +DBQuery.prototype.pretty = function() { this._prettyShell = true; return this; }; -DBQuery.prototype.shellPrint = function(){ +DBQuery.prototype.shellPrint = function() { try { var start = new Date().getTime(); var n = 0; - while ( this.hasNext() && n < DBQuery.shellBatchSize ){ - var s = this._prettyShell ? tojson( this.next() ) : tojson( this.next() , "" , true ); - print( s ); + while (this.hasNext() && n < DBQuery.shellBatchSize) { + var s = this._prettyShell ? tojson(this.next()) : tojson(this.next(), "", true); + print(s); n++; } if (typeof _verboseShell !== 'undefined' && _verboseShell) { var time = new Date().getTime() - start; print("Fetched " + n + " record(s) in " + time + "ms"); } - if ( this.hasNext() ){ - print( "Type \"it\" for more" ); - ___it___ = this; - } - else { - ___it___ = null; + if (this.hasNext()) { + print("Type \"it\" for more"); + ___it___ = this; + } else { + ___it___ = null; } - } - catch ( e ){ - print( e ); + } catch (e) { + print(e); } }; @@ -550,11 +559,11 @@ DBQuery.prototype.shellPrint = function(){ * Returns a QueryPlan for the query. */ DBQuery.prototype.getQueryPlan = function() { - return new QueryPlan( this ); + return new QueryPlan(this); }; -DBQuery.prototype.toString = function(){ - return "DBQuery: " + this._ns + " -> " + tojson( this._query ); +DBQuery.prototype.toString = function() { + return "DBQuery: " + this._ns + " -> " + tojson(this._query); }; // @@ -620,7 +629,8 @@ DBQuery.prototype.projection = function(document) { * * @method * @see http://docs.mongodb.org/manual/tutorial/create-tailable-cursor/ -* @param {boolean} [awaitData=true] cursor blocks for a few seconds to wait for data if no documents found. +* @param {boolean} [awaitData=true] cursor blocks for a few seconds to wait for data if no documents +*found. * @return {DBQuery} */ DBQuery.prototype.tailable = function(awaitData) { @@ -646,13 +656,13 @@ DBQuery.prototype.tailable = function(awaitData) { DBQuery.prototype.modifiers = function(document) { this._checkModify(); - for(var name in document) { - if(name[0] != '$') { + for (var name in document) { + if (name[0] != '$') { throw new Error('All modifiers must start with a $ such as $maxScan or $returnKey'); } } - for(var name in document) { + for (var name in document) { this._addSpecial(name, document[name]); } @@ -684,7 +694,7 @@ function DBCommandCursor(mongo, cmdResult, batchSize) { throw _getErrorWithCode(cmdResult, "error: " + tojson(cmdResult)); } - this._batch = cmdResult.cursor.firstBatch.reverse(); // modifies input to allow popping + this._batch = cmdResult.cursor.firstBatch.reverse(); // modifies input to allow popping if (mongo.useReadCommands()) { this._useReadCommands = true; @@ -713,7 +723,7 @@ DBCommandCursor.prototype.close = function() { } else if (this._cursorid != 0) { var killCursorCmd = { killCursors: this._collName, - cursors: [ this._cursorid ], + cursors: [this._cursorid], }; var cmdRes = this._db.runCommand(killCursorCmd); if (cmdRes.ok != 1) { @@ -749,17 +759,16 @@ DBCommandCursor.prototype._runGetMoreCommand = function() { } if (this._ns !== cmdRes.cursor.ns) { - throw Error("unexpected collection in getMore response: " + - this._ns + " != " + cmdRes.cursor.ns); + throw Error("unexpected collection in getMore response: " + this._ns + " != " + + cmdRes.cursor.ns); } if (!cmdRes.cursor.id.compare(NumberLong("0"))) { this._cursorHandle.zeroCursorId(); this._cursorid = NumberLong("0"); - } - else if (this._cursorid.compare(cmdRes.cursor.id)) { - throw Error("unexpected cursor id: " + - this._cursorid.toString() + " != " + cmdRes.cursor.id.toString()); + } else if (this._cursorid.compare(cmdRes.cursor.id)) { + throw Error("unexpected cursor id: " + this._cursorid.toString() + " != " + + cmdRes.cursor.id.toString()); } // Successfully retrieved the next batch. @@ -792,18 +801,18 @@ DBCommandCursor.prototype.next = function() { if (this._batch.length) { // $err wouldn't be in _firstBatch since ok was true. return this._batch.pop(); - } - else if (this._useReadCommands) { + } else if (this._useReadCommands) { // Have to call hasNext() here, as this is where we may issue a getMore in order to retrieve // the next batch of results. - if (!this.hasNext()) throw Error("error hasNext: false"); + if (!this.hasNext()) + throw Error("error hasNext: false"); return this._batch.pop(); - } - else { - if (!this._cursor.hasNext()) throw Error("error hasNext: false"); + } else { + if (!this._cursor.hasNext()) + throw Error("error hasNext: false"); var ret = this._cursor.next(); - if ( ret.$err ) + if (ret.$err) throw _getErrorWithCode(ret, "error: " + tojson(ret)); return ret; } @@ -811,16 +820,14 @@ DBCommandCursor.prototype.next = function() { DBCommandCursor.prototype.objsLeftInBatch = function() { if (this._useReadCommands) { return this._batch.length; - } - else if (this._batch.length) { + } else if (this._batch.length) { return this._batch.length; - } - else { + } else { return this._cursor.objsLeftInBatch(); } }; -DBCommandCursor.prototype.help = function () { +DBCommandCursor.prototype.help = function() { // This is the same as the "Cursor Methods" section of DBQuery.help(). print("\nCursor methods"); print("\t.toArray() - iterates through docs and returns an array of the results"); @@ -828,7 +835,8 @@ DBCommandCursor.prototype.help = function () { print("\t.map( func )"); print("\t.hasNext()"); print("\t.next()"); - print("\t.objsLeftInBatch() - returns count of docs left in current batch (when exhausted, a new getMore will be issued)"); + print( + "\t.objsLeftInBatch() - returns count of docs left in current batch (when exhausted, a new getMore will be issued)"); print("\t.itcount() - iterates through documents and counts them"); print("\t.pretty() - pretty print each document, possibly over multiple lines"); print("\t.close()"); @@ -847,8 +855,8 @@ DBCommandCursor.prototype.pretty = DBQuery.prototype.pretty; * Holds a reference to the cursor. * Proxy for planCache* query shape-specific commands. */ -if ( ( typeof QueryPlan ) == "undefined" ){ - QueryPlan = function( cursor ){ +if ((typeof QueryPlan) == "undefined") { + QueryPlan = function(cursor) { this._cursor = cursor; }; } @@ -872,7 +880,7 @@ QueryPlan.prototype.tojson = function(indent, nolint) { /** * Displays help for a PlanCache object. */ -QueryPlan.prototype.help = function () { +QueryPlan.prototype.help = function() { var shortName = this.getName(); print("QueryPlan help"); print("\t.help() - show QueryPlan help"); diff --git a/src/mongo/shell/replsettest.js b/src/mongo/shell/replsettest.js index 6e6eef6fae1..f9b17281620 100644 --- a/src/mongo/shell/replsettest.js +++ b/src/mongo/shell/replsettest.js @@ -75,7 +75,10 @@ var ReplSetTest = function(opts) { var self = this; // Replica set health states - var Health = { UP: 1, DOWN: 0 }; + var Health = { + UP: 1, + DOWN: 0 + }; var _alldbpaths; var _configSettings; @@ -92,14 +95,17 @@ var ReplSetTest = function(opts) { * Populates a reference to all reachable nodes. */ function _clearLiveNodes() { - self.liveNodes = { master: null, slaves: [] }; + self.liveNodes = { + master: null, + slaves: [] + }; } /** * Returns the config document reported from the specified connection. */ function _replSetGetConfig(conn) { - return assert.commandWorked(conn.adminCommand({ replSetGetConfig: 1 })).config; + return assert.commandWorked(conn.adminCommand({replSetGetConfig: 1})).config; } /** @@ -111,16 +117,14 @@ var ReplSetTest = function(opts) { self.nodes.forEach(function(node) { try { - var n = node.getDB('admin').runCommand({ ismaster: 1 }); + var n = node.getDB('admin').runCommand({ismaster: 1}); if (n.ismaster == true) { self.liveNodes.master = node; - } - else { + } else { node.setSlaveOk(); self.liveNodes.slaves.push(node); } - } - catch (err) { + } catch (err) { print("ReplSetTest Could not call ismaster on node " + node + ": " + tojson(err)); } }); @@ -139,7 +143,7 @@ var ReplSetTest = function(opts) { function _waitForIndicator(node, states, ind, timeout) { if (node.length) { var nodes = node; - for(var i = 0; i < nodes.length; i++) { + for (var i = 0; i < nodes.length; i++) { if (states.length) _waitForIndicator(nodes[i], states[i], ind, timeout); else @@ -175,10 +179,11 @@ var ReplSetTest = function(opts) { } // Try again to load connection - if (!conn) return false; + if (!conn) + return false; var getStatusFunc = function() { - status = conn.getDB('admin').runCommand({ replSetGetStatus: 1 }); + status = conn.getDB('admin').runCommand({replSetGetStatus: 1}); }; if (self.keyFile) { @@ -187,8 +192,7 @@ var ReplSetTest = function(opts) { } else { getStatusFunc(); } - } - catch (ex) { + } catch (ex) { print("ReplSetTest waitForIndicator could not get status: " + tojson(ex)); return false; } @@ -196,8 +200,8 @@ var ReplSetTest = function(opts) { var printStatus = false; if (lastTime == null || (currTime = new Date().getTime()) - (1000 * 5) > lastTime) { if (lastTime == null) { - print("ReplSetTest waitForIndicator Initial status (timeout : " - + timeout + ") :"); + print("ReplSetTest waitForIndicator Initial status (timeout : " + timeout + + ") :"); } printjson(status); @@ -209,22 +213,22 @@ var ReplSetTest = function(opts) { return false; } - for(var i = 0; i < status.members.length; i++) { + for (var i = 0; i < status.members.length; i++) { if (printStatus) { - print("Status for : " + status.members[i].name + ", checking " + - node.host + "/" + node.name); + print("Status for : " + status.members[i].name + ", checking " + node.host + + "/" + node.name); } if (status.members[i].name == node.host || status.members[i].name == node.name) { - for(var j = 0; j < states.length; j++) { + for (var j = 0; j < states.length; j++) { if (printStatus) { print("Status -- " + " current state: " + status.members[i][ind] + - ", target state : " + states[j]); + ", target state : " + states[j]); } if (typeof(states[j]) != "number") { - throw new Error("State was not an number -- type:" + - typeof(states[j]) + ", value:" + states[j]); + throw new Error("State was not an number -- type:" + typeof(states[j]) + + ", value:" + states[j]); } if (status.members[i][ind] == states[j]) { return true; @@ -261,11 +265,11 @@ var ReplSetTest = function(opts) { try { return func(); } catch (e) { - if (msg) { + if (msg) { print(msg); - } - print("ReplSetTest caught exception " + e); - return false; + } + print("ReplSetTest caught exception " + e); + return false; } } @@ -281,7 +285,7 @@ var ReplSetTest = function(opts) { */ function _getLastOpTime(conn) { var replSetStatus = - assert.commandWorked(conn.getDB("admin").runCommand({ replSetGetStatus: 1 })); + assert.commandWorked(conn.getDB("admin").runCommand({replSetGetStatus: 1})); var connStatus = replSetStatus.members.filter(m => m.self)[0]; return connStatus.optime; } @@ -306,9 +310,11 @@ var ReplSetTest = function(opts) { */ function _getLastCommittedOpTime(conn) { var replSetStatus = - assert.commandWorked(conn.getDB("admin").runCommand({ replSetGetStatus: 1 })); - return replSetStatus.OpTimes.lastCommittedOpTime || - { ts: Timestamp(0, 0), t: NumberLong(0) }; + assert.commandWorked(conn.getDB("admin").runCommand({replSetGetStatus: 1})); + return replSetStatus.OpTimes.lastCommittedOpTime || { + ts: Timestamp(0, 0), + t: NumberLong(0) + }; } /** @@ -319,15 +325,23 @@ var ReplSetTest = function(opts) { */ function _getReadConcernMajorityOpTime(conn) { var replSetStatus = - assert.commandWorked(conn.getDB("admin").runCommand({ replSetGetStatus: 1 })); - return replSetStatus.OpTimes.readConcernMajorityOpTime || - { ts: Timestamp(0, 0), t: NumberLong(0) }; + assert.commandWorked(conn.getDB("admin").runCommand({replSetGetStatus: 1})); + return replSetStatus.OpTimes.readConcernMajorityOpTime || { + ts: Timestamp(0, 0), + t: NumberLong(0) + }; } function _isEarlierOpTime(ot1, ot2) { // Make sure both optimes have a timestamp and a term. - ot1 = ot1.t ? ot1 : { ts: ot1, t: NumberLong(-1) }; - ot2 = ot2.t ? ot2 : { ts: ot2, t: NumberLong(-1) }; + ot1 = ot1.t ? ot1 : { + ts: ot1, + t: NumberLong(-1) + }; + ot2 = ot2.t ? ot2 : { + ts: ot2, + t: NumberLong(-1) + }; // If both optimes have a term that's not -1 and one has a lower term, return that optime. if (!friendlyEqual(ot1.t, NumberLong(-1)) && !friendlyEqual(ot2.t, NumberLong(-1))) { @@ -345,7 +359,7 @@ var ReplSetTest = function(opts) { */ this.nodeList = function() { var list = []; - for(var i=0; i<this.ports.length; i++) { + for (var i = 0; i < this.ports.length; i++) { list.push(this.host + ":" + this.ports[i]); } @@ -357,14 +371,14 @@ var ReplSetTest = function(opts) { return parseInt(node); } - for(var i = 0; i < this.nodes.length; i++) { + for (var i = 0; i < this.nodes.length; i++) { if (this.nodes[i] == node) { return i; } } if (node instanceof ObjectId) { - for(i = 0; i < this.nodes.length; i++) { + for (i = 0; i < this.nodes.length; i++) { if (this.nodes[i].runId == node) { return i; } @@ -437,7 +451,7 @@ var ReplSetTest = function(opts) { this.getURL = function() { var hosts = []; - for(var i = 0; i < this.ports.length; i++) { + for (var i = 0; i < this.ports.length; i++) { hosts.push(this.host + ":" + this.ports[i]); } @@ -453,7 +467,7 @@ var ReplSetTest = function(opts) { print("ReplSetTest starting set"); var nodes = []; - for(var n = 0 ; n < this.ports.length; n++) { + for (var n = 0; n < this.ports.length; n++) { nodes.push(this.start(n, options)); } @@ -475,8 +489,8 @@ var ReplSetTest = function(opts) { var len = slaves.length; var ready = true; - for(var i = 0; i < len; i++) { - var isMaster = slaves[i].getDB("admin").runCommand({ ismaster: 1 }); + for (var i = 0; i < len; i++) { + var isMaster = slaves[i].getDB("admin").runCommand({ismaster: 1}); var arbiter = (isMaster.arbiterOnly == undefined ? false : isMaster.arbiterOnly); ready = ready && (isMaster.secondary || arbiter); } @@ -513,7 +527,7 @@ var ReplSetTest = function(opts) { this.getSecondaries = function(timeout) { var master = this.getPrimary(timeout); var secs = []; - for(var i = 0; i < this.nodes.length; i++) { + for (var i = 0; i < this.nodes.length; i++) { if (this.nodes[i] != master) { secs.push(this.nodes[i]); } @@ -532,7 +546,7 @@ var ReplSetTest = function(opts) { master = this.liveNodes.slaves[0]; } - return master.getDB("admin").runCommand({ replSetGetStatus: 1 }); + return master.getDB("admin").runCommand({replSetGetStatus: 1}); }; /** @@ -568,12 +582,13 @@ var ReplSetTest = function(opts) { }; this.initiate = function(cfg, initCmd, timeout) { - var master = this.nodes[0].getDB("admin"); - var config = cfg || this.getReplSetConfig(); - var cmd = {}; - var cmdKey = initCmd || 'replSetInitiate'; + var master = this.nodes[0].getDB("admin"); + var config = cfg || this.getReplSetConfig(); + var cmd = {}; + var cmdKey = initCmd || 'replSetInitiate'; timeout = timeout || 120000; - if (jsTestOptions().useLegacyReplicationProtocol && !config.hasOwnProperty("protocolVersion")) { + if (jsTestOptions().useLegacyReplicationProtocol && + !config.hasOwnProperty("protocolVersion")) { config.protocolVersion = 0; } cmd[cmdKey] = config; @@ -611,13 +626,13 @@ var ReplSetTest = function(opts) { var newVersion = this.getReplSetConfigFromNode().version + 1; config.version = newVersion; - if (jsTestOptions().useLegacyReplicationProtocol && !config.hasOwnProperty("protocolVersion")) { + if (jsTestOptions().useLegacyReplicationProtocol && + !config.hasOwnProperty("protocolVersion")) { config.protocolVersion = 0; } try { assert.commandWorked(this.getPrimary().adminCommand({replSetReconfig: config})); - } - catch (e) { + } catch (e) { if (tojson(e).indexOf("error doing query: failed") < 0) { throw e; } @@ -635,19 +650,19 @@ var ReplSetTest = function(opts) { var masterOpTime = _getLastOpTime(master); print("Waiting for op with OpTime " + tojson(masterOpTime) + - " to be committed on all secondaries"); + " to be committed on all secondaries"); _assertSoonNoExcept(function() { for (var i = 0; i < rst.nodes.length; i++) { var node = rst.nodes[i]; // Continue if we're connected to an arbiter - var res = assert.commandWorked(node.adminCommand({ replSetGetStatus: 1 })); + var res = assert.commandWorked(node.adminCommand({replSetGetStatus: 1})); if (res.myState == ReplSetTest.State.ARBITER) { continue; } var rcmOpTime = _getReadConcernMajorityOpTime(node); - if (friendlyEqual(rcmOpTime, { ts: Timestamp(0, 0), t: NumberLong(0) })) { + if (friendlyEqual(rcmOpTime, {ts: Timestamp(0, 0), t: NumberLong(0)})) { return false; } if (_isEarlierOpTime(rcmOpTime, masterOpTime)) { @@ -672,8 +687,7 @@ var ReplSetTest = function(opts) { _assertSoonNoExcept(function() { try { masterLatestOpTime = _getLastOpTimeTimestamp(master); - } - catch(e) { + } catch (e) { print("ReplSetTest caught exception " + e); return false; } @@ -684,7 +698,8 @@ var ReplSetTest = function(opts) { awaitLastOpTimeWrittenFn(); - // get the latest config version from master. if there is a problem, grab master and try again + // get the latest config version from master. if there is a problem, grab master and try + // again var configVersion; var masterOpTime; var masterName; @@ -694,41 +709,41 @@ var ReplSetTest = function(opts) { master = this.getPrimary(); configVersion = this.getReplSetConfigFromNode().version; masterOpTime = _getLastOpTimeTimestamp(master); - masterName = master.toString().substr(14); // strip "connection to " - } - catch (e) { + masterName = master.toString().substr(14); // strip "connection to " + } catch (e) { master = this.getPrimary(); configVersion = this.getReplSetConfigFromNode().version; masterOpTime = _getLastOpTimeTimestamp(master); - masterName = master.toString().substr(14); // strip "connection to " + masterName = master.toString().substr(14); // strip "connection to " } print("ReplSetTest awaitReplication: starting: timestamp for primary, " + masterName + - ", is " + tojson(masterLatestOpTime) + - ", last oplog entry is " + tojsononeline(masterOpTime)); + ", is " + tojson(masterLatestOpTime) + ", last oplog entry is " + + tojsononeline(masterOpTime)); _assertSoonNoExcept(function() { - try { + try { print("ReplSetTest awaitReplication: checking secondaries against timestamp " + tojson(masterLatestOpTime)); var secondaryCount = 0; for (var i = 0; i < self.liveNodes.slaves.length; i++) { var slave = self.liveNodes.slaves[i]; - var slaveName = slave.toString().substr(14); // strip "connection to " + var slaveName = slave.toString().substr(14); // strip "connection to " var slaveConfigVersion = slave.getDB("local")['system.replset'].findOne().version; if (configVersion != slaveConfigVersion) { - print("ReplSetTest awaitReplication: secondary #" + secondaryCount + - ", " + slaveName + ", has config version #" + slaveConfigVersion + + print("ReplSetTest awaitReplication: secondary #" + secondaryCount + ", " + + slaveName + ", has config version #" + slaveConfigVersion + ", but expected config version #" + configVersion); if (slaveConfigVersion > configVersion) { master = this.getPrimary(); - configVersion = master.getDB("local")['system.replset'].findOne().version; + configVersion = + master.getDB("local")['system.replset'].findOne().version; masterOpTime = _getLastOpTimeTimestamp(master); - masterName = master.toString().substr(14); // strip "connection to " + masterName = master.toString().substr(14); // strip "connection to " print("ReplSetTest awaitReplication: timestamp for primary, " + masterName + ", is " + tojson(masterLatestOpTime) + @@ -739,19 +754,20 @@ var ReplSetTest = function(opts) { } // Continue if we're connected to an arbiter - var res = assert.commandWorked(slave.adminCommand({ replSetGetStatus: 1 })); + var res = assert.commandWorked(slave.adminCommand({replSetGetStatus: 1})); if (res.myState == ReplSetTest.State.ARBITER) { continue; } ++secondaryCount; - print("ReplSetTest awaitReplication: checking secondary #" + - secondaryCount + ": " + slaveName); + print("ReplSetTest awaitReplication: checking secondary #" + secondaryCount + + ": " + slaveName); slave.getDB("admin").getMongo().setSlaveOk(); var ts = _getLastOpTimeTimestamp(slave); - if (masterLatestOpTime.t < ts.t || (masterLatestOpTime.t == ts.t && masterLatestOpTime.i < ts.i)) { + if (masterLatestOpTime.t < ts.t || + (masterLatestOpTime.t == ts.t && masterLatestOpTime.i < ts.i)) { masterLatestOpTime = _getLastOpTimeTimestamp(master); print("ReplSetTest awaitReplication: timestamp for " + slaveName + " is newer, resetting latest to " + tojson(masterLatestOpTime)); @@ -762,20 +778,19 @@ var ReplSetTest = function(opts) { print("ReplSetTest awaitReplication: timestamp for secondary #" + secondaryCount + ", " + slaveName + ", is " + tojson(ts) + " but latest is " + tojson(masterLatestOpTime)); - print("ReplSetTest awaitReplication: secondary #" + - secondaryCount + ", " + slaveName + ", is NOT synced"); + print("ReplSetTest awaitReplication: secondary #" + secondaryCount + ", " + + slaveName + ", is NOT synced"); return false; } - print("ReplSetTest awaitReplication: secondary #" + - secondaryCount + ", " + slaveName + ", is synced"); + print("ReplSetTest awaitReplication: secondary #" + secondaryCount + ", " + + slaveName + ", is synced"); } print("ReplSetTest awaitReplication: finished: all " + secondaryCount + " secondaries synced at timestamp " + tojson(masterLatestOpTime)); return true; - } - catch (e) { + } catch (e) { print("ReplSetTest awaitReplication: caught exception " + e + ';\n' + e.stack); // We might have a new master now @@ -793,7 +808,9 @@ var ReplSetTest = function(opts) { this.getPrimary(); var res = {}; res.master = this.liveNodes.master.getDB(db).runCommand("dbhash"); - res.slaves = this.liveNodes.slaves.map(function(z) { return z.getDB(db).runCommand("dbhash"); }); + res.slaves = this.liveNodes.slaves.map(function(z) { + return z.getDB(db).runCommand("dbhash"); + }); return res; }; @@ -817,7 +834,7 @@ var ReplSetTest = function(opts) { var nodes = n; var started = []; - for(var i = 0; i < nodes.length; i++) { + for (var i = 0; i < nodes.length; i++) { if (this.start(nodes[i], Object.merge({}, options), restart, wait)) { started.push(nodes[i]); } @@ -831,14 +848,16 @@ var ReplSetTest = function(opts) { print("ReplSetTest n is : " + n); - var defaults = { useHostName : this.useHostName, - oplogSize : this.oplogSize, - keyFile : this.keyFile, - port : _useBridge ? _unbridgedPorts[n] : this.ports[n], - noprealloc : "", - smallfiles : "", - replSet : this.useSeedList ? this.getURL() : this.name, - dbpath : "$set-$node" }; + var defaults = { + useHostName: this.useHostName, + oplogSize: this.oplogSize, + keyFile: this.keyFile, + port: _useBridge ? _unbridgedPorts[n] : this.ports[n], + noprealloc: "", + smallfiles: "", + replSet: this.useSeedList ? this.getURL() : this.name, + dbpath: "$set-$node" + }; // // Note : this replaces the binVersion of the shared startSet() options the first time @@ -846,8 +865,7 @@ var ReplSetTest = function(opts) { // start() independently, independent version choices will be made // if (options && options.binVersion) { - options.binVersion = - MongoRunner.versionIterator(options.binVersion); + options.binVersion = MongoRunner.versionIterator(options.binVersion); } options = Object.merge(defaults, options); @@ -856,7 +874,10 @@ var ReplSetTest = function(opts) { options.restart = options.restart || restart; - var pathOpts = { node : n, set : this.name }; + var pathOpts = { + node: n, + set: this.name + }; options.pathOpts = Object.merge(options.pathOpts || {}, pathOpts); if (tojson(options) != tojson({})) @@ -866,13 +887,15 @@ var ReplSetTest = function(opts) { if (_useBridge) { var bridgeOptions = Object.merge(_bridgeOptions, options.bridgeOptions || {}); - bridgeOptions = Object.merge(bridgeOptions, { - hostName: this.host, - port: this.ports[n], - // The mongod processes identify themselves to mongobridge as host:port, where the - // host is the actual hostname of the machine and not localhost. - dest: getHostName() + ":" + _unbridgedPorts[n], - }); + bridgeOptions = Object.merge( + bridgeOptions, + { + hostName: this.host, + port: this.ports[n], + // The mongod processes identify themselves to mongobridge as host:port, where the + // host is the actual hostname of the machine and not localhost. + dest: getHostName() + ":" + _unbridgedPorts[n], + }); this.nodes[n] = new MongoBridge(bridgeOptions); } @@ -898,9 +921,11 @@ var ReplSetTest = function(opts) { printjson(this.nodes); wait = wait || false; - if (! wait.toFixed) { - if (wait) wait = 0; - else wait = -1; + if (!wait.toFixed) { + if (wait) + wait = 0; + else + wait = -1; } if (wait >= 0) { @@ -934,7 +959,7 @@ var ReplSetTest = function(opts) { if (jsTestOptions().keyFile) { if (started.length) { - // if n was an array of conns, start will return an array of connections + // if n was an array of conns, start will return an array of connections for (var i = 0; i < started.length; i++) { jsTest.authenticate(started[i]); } @@ -964,7 +989,7 @@ var ReplSetTest = function(opts) { var nodes = n; var stopped = []; - for(var i = 0; i < nodes.length; i++) { + for (var i = 0; i < nodes.length; i++) { if (this.stop(nodes[i], signal, opts)) stopped.push(nodes[i]); } @@ -983,8 +1008,8 @@ var ReplSetTest = function(opts) { print('ReplSetTest stop *** Shutting down mongod in port ' + port + ' ***'); var ret = MongoRunner.stopMongod(port, signal, opts); - print('ReplSetTest stop *** Mongod in port ' + port + - ' shutdown with code (' + ret + ') ***'); + print('ReplSetTest stop *** Mongod in port ' + port + ' shutdown with code (' + ret + + ') ***'); if (_useBridge) { this.nodes[n].stop(); @@ -1001,15 +1026,17 @@ var ReplSetTest = function(opts) { * @param {Object} opts @see MongoRunner.stopMongod */ this.stopSet = function(signal, forRestart, opts) { - for(var i=0; i < this.ports.length; i++) { + for (var i = 0; i < this.ports.length; i++) { this.stop(i, signal, opts); } - if (forRestart) { return; } + if (forRestart) { + return; + } if (_alldbpaths) { print("ReplSetTest stopSet deleting all dbpaths"); - for(var i = 0; i < _alldbpaths.length; i++) { + for (var i = 0; i < _alldbpaths.length; i++) { resetDbpath(_alldbpaths[i]); } } @@ -1024,46 +1051,48 @@ var ReplSetTest = function(opts) { */ this.ensureOplogsMatch = function() { var OplogReader = function(mongo) { - this.next = function() { - if (!this.cursor) - throw Error("reader is not open!"); - - var nextDoc = this.cursor.next(); - if (nextDoc) - this.lastDoc = nextDoc; - return nextDoc; + this.next = function() { + if (!this.cursor) + throw Error("reader is not open!"); + + var nextDoc = this.cursor.next(); + if (nextDoc) + this.lastDoc = nextDoc; + return nextDoc; + }; + + this.getLastDoc = function() { + if (this.lastDoc) + return this.lastDoc; + return this.next(); + }; + + this.hasNext = function() { + if (!this.cursor) + throw Error("reader is not open!"); + return this.cursor.hasNext(); + }; + + this.query = function(ts) { + var coll = this.getOplogColl(); + var query = { + "ts": {"$gte": ts ? ts : new Timestamp()} }; + this.cursor = coll.find(query).sort({$natural: 1}); + this.cursor.addOption(DBQuery.Option.oplogReplay); + }; - this.getLastDoc = function() { - if (this.lastDoc) - return this.lastDoc; - return this.next(); - }; + this.getFirstDoc = function() { + return this.getOplogColl().find().sort({$natural: 1}).limit(-1).next(); + }; - this.hasNext = function() { - if (!this.cursor) - throw Error("reader is not open!"); - return this.cursor.hasNext(); - }; + this.getOplogColl = function() { + return this.mongo.getDB("local")["oplog.rs"]; + }; - this.query = function(ts) { - var coll = this.getOplogColl(); - var query = {"ts": {"$gte": ts ? ts : new Timestamp()}}; - this.cursor = coll.find(query).sort({$natural:1}); - this.cursor.addOption(DBQuery.Option.oplogReplay); - }; - - this.getFirstDoc = function() { - return this.getOplogColl().find().sort({$natural:1}).limit(-1).next(); - }; - - this.getOplogColl = function () { - return this.mongo.getDB("local")["oplog.rs"]; - }; - - this.lastDoc = null; - this.cursor = null; - this.mongo = mongo; + this.lastDoc = null; + this.cursor = null; + this.mongo = mongo; }; if (this.nodes.length && this.nodes.length > 1) { @@ -1087,18 +1116,16 @@ var ReplSetTest = function(opts) { var firstReader = readers[0]; while (firstReader.hasNext()) { var ts = firstReader.next().ts; - for(i = 1; i < rsSize; i++) { - assert.eq(ts, - readers[i].next().ts, - " non-matching ts for node: " + readers[i].mongo); + for (i = 1; i < rsSize; i++) { + assert.eq( + ts, readers[i].next().ts, " non-matching ts for node: " + readers[i].mongo); } } // ensure no other node has more oplog for (i = 1; i < rsSize; i++) { - assert.eq(false, - readers[i].hasNext(), - "" + readers[i] + " shouldn't have more oplog."); + assert.eq( + false, readers[i].hasNext(), "" + readers[i] + " shouldn't have more oplog."); } } }; @@ -1134,11 +1161,11 @@ var ReplSetTest = function(opts) { * Constructor, which initializes the ReplSetTest object by starting new instances. */ function _constructStartNewInstances(opts) { - self.name = opts.name || "testReplSet"; + self.name = opts.name || "testReplSet"; print('Starting new replica set ' + self.name); self.useHostName = opts.useHostName == undefined ? true : opts.useHostName; - self.host = self.useHostName ? (opts.host || getHostName()) : 'localhost'; + self.host = self.useHostName ? (opts.host || getHostName()) : 'localhost'; self.oplogSize = opts.oplogSize || 40; self.useSeedList = opts.useSeedList || false; self.keyFile = opts.keyFile; @@ -1156,9 +1183,9 @@ var ReplSetTest = function(opts) { if (isObject(opts.nodes)) { var len = 0; - for(var i in opts.nodes) { - var options = self.nodeOptions["n" + len] = Object.merge(opts.nodeOptions, - opts.nodes[i]); + for (var i in opts.nodes) { + var options = self.nodeOptions["n" + len] = + Object.merge(opts.nodeOptions, opts.nodes[i]); if (i.startsWith("a")) { options.arbiter = true; } @@ -1167,15 +1194,13 @@ var ReplSetTest = function(opts) { } numNodes = len; - } - else if (Array.isArray(opts.nodes)) { - for(var i = 0; i < opts.nodes.length; i++) { + } else if (Array.isArray(opts.nodes)) { + for (var i = 0; i < opts.nodes.length; i++) { self.nodeOptions["n" + i] = Object.merge(opts.nodeOptions, opts.nodes[i]); } numNodes = opts.nodes.length; - } - else { + } else { for (var i = 0; i < opts.nodes; i++) { self.nodeOptions["n" + i] = opts.nodeOptions; } @@ -1206,8 +1231,7 @@ var ReplSetTest = function(opts) { if (typeof opts === 'string' || opts instanceof String) { _constructFromExistingSeedNode(opts); - } - else { + } else { _constructStartNewInstances(opts); } }; @@ -1234,7 +1258,7 @@ ReplSetTest.State = { ReplSetTest.awaitRSClientHosts = function(conn, host, hostOk, rs, timeout) { var hostCount = host.length; if (hostCount) { - for(var i = 0; i < hostCount; i++) { + for (var i = 0; i < hostCount; i++) { ReplSetTest.awaitRSClientHosts(conn, host[i], hostOk, rs); } @@ -1243,9 +1267,14 @@ ReplSetTest.awaitRSClientHosts = function(conn, host, hostOk, rs, timeout) { timeout = timeout || 60000; - if (hostOk == undefined) hostOk = { ok: true }; - if (host.host) host = host.host; - if (rs) rs = rs.name; + if (hostOk == undefined) + hostOk = { + ok: true + }; + if (host.host) + host = host.host; + if (rs) + rs = rs.name; print("Awaiting " + host + " to be " + tojson(hostOk) + " for " + conn + " (rs: " + rs + ")"); @@ -1258,22 +1287,23 @@ ReplSetTest.awaitRSClientHosts = function(conn, host, hostOk, rs, timeout) { } for (var rsName in rsClientHosts) { - if (rs && rs != rsName) continue; + if (rs && rs != rsName) + continue; for (var i = 0; i < rsClientHosts[rsName].hosts.length; i++) { var clientHost = rsClientHosts[rsName].hosts[i]; - if (clientHost.addr != host) continue; + if (clientHost.addr != host) + continue; // Check that *all* host properties are set correctly var propOk = true; - for(var prop in hostOk) { + for (var prop in hostOk) { if (isObject(hostOk[prop])) { if (!friendlyEqual(hostOk[prop], clientHost[prop])) { propOk = false; break; } - } - else if (clientHost[prop] != hostOk[prop]) { + } else if (clientHost[prop] != hostOk[prop]) { propOk = false; break; } @@ -1286,7 +1316,5 @@ ReplSetTest.awaitRSClientHosts = function(conn, host, hostOk, rs, timeout) { } return false; - }, - 'timed out waiting for replica set client to recognize hosts', - timeout); + }, 'timed out waiting for replica set client to recognize hosts', timeout); }; diff --git a/src/mongo/shell/servers.js b/src/mongo/shell/servers.js index d7754ef3064..0e7ce533e9c 100755..100644 --- a/src/mongo/shell/servers.js +++ b/src/mongo/shell/servers.js @@ -1,1000 +1,1020 @@ // Wrap whole file in a function to avoid polluting the global namespace (function() { -_parsePath = function() { - var dbpath = ""; - for( var i = 0; i < arguments.length; ++i ) - if ( arguments[ i ] == "--dbpath" ) - dbpath = arguments[ i + 1 ]; - - if ( dbpath == "" ) - throw Error("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 Error("No port specified"); - return port; -}; - -createMongoArgs = function( binaryName , args ){ - if (!Array.isArray(args)) { - throw new Error("The second argument to createMongoArgs must be an array"); - } + _parsePath = function() { + var dbpath = ""; + for (var i = 0; i < arguments.length; ++i) + if (arguments[i] == "--dbpath") + dbpath = arguments[i + 1]; + + if (dbpath == "") + throw Error("No dbpath specified"); + + return dbpath; + }; - var fullArgs = [ binaryName ]; - - if ( args.length == 1 && isObject( args[0] ) ){ - var o = args[0]; - for ( var k in o ){ - if ( o.hasOwnProperty(k) ){ - if ( k == "v" && isNumber( o[k] ) ){ - var n = o[k]; - if ( n > 0 ){ - if ( n > 10 ) n = 10; - var temp = "-"; - while ( n-- > 0 ) temp += "v"; - fullArgs.push( temp ); + _parsePort = function() { + var port = ""; + for (var i = 0; i < arguments.length; ++i) + if (arguments[i] == "--port") + port = arguments[i + 1]; + + if (port == "") + throw Error("No port specified"); + return port; + }; + + createMongoArgs = function(binaryName, args) { + if (!Array.isArray(args)) { + throw new Error("The second argument to createMongoArgs must be an array"); + } + + var fullArgs = [binaryName]; + + if (args.length == 1 && isObject(args[0])) { + var o = args[0]; + for (var k in o) { + if (o.hasOwnProperty(k)) { + if (k == "v" && isNumber(o[k])) { + var n = o[k]; + if (n > 0) { + if (n > 10) + n = 10; + var temp = "-"; + while (n-- > 0) + temp += "v"; + fullArgs.push(temp); + } + } else { + fullArgs.push("--" + k); + if (o[k] != "") + fullArgs.push("" + o[k]); + } } } - 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; + }; + + MongoRunner = function() {}; + + MongoRunner.dataDir = "/data/db"; + MongoRunner.dataPath = "/data/db/"; + + MongoRunner.VersionSub = function(regex, version) { + this.regex = regex; + this.version = version; + }; + + // These patterns allow substituting the binary versions used for each version string to support + // the + // dev/stable MongoDB release cycle. + // + // If you add a new version substitution to this list, you should add it to the lists of + // versions + // being checked in '0_test_launching.js' to verify it is susbstituted correctly. + MongoRunner.binVersionSubs = [ + new MongoRunner.VersionSub(/^latest$/, ""), + // To-be-updated when we branch for the next release. + new MongoRunner.VersionSub(/^last-stable$/, "3.2"), + new MongoRunner.VersionSub(/^3\.3(\..*){0,1}/, ""), + new MongoRunner.VersionSub(/^3\.4(\..*){0,1}/, "") + ]; + + MongoRunner.getBinVersionFor = function(version) { + + // If this is a version iterator, iterate the version via toString() + if (version instanceof MongoRunner.versionIterator.iterator) { + version = version.toString(); + } + + // No version set means we use no suffix, this is *different* from "latest" + // since latest may be mapped to a different version. + if (version == null) + version = ""; + version = version.trim(); + if (version === "") + return ""; + + // See if this version is affected by version substitutions + for (var i = 0; i < MongoRunner.binVersionSubs.length; i++) { + var sub = MongoRunner.binVersionSubs[i]; + if (sub.regex.test(version)) { + version = sub.version; } - } } - } - else { - for ( var i=0; i<args.length; i++ ) - fullArgs.push( args[i] ); - } - return fullArgs; -}; + return version; + }; + MongoRunner.areBinVersionsTheSame = function(versionA, versionB) { -MongoRunner = function(){}; - -MongoRunner.dataDir = "/data/db"; -MongoRunner.dataPath = "/data/db/"; + versionA = MongoRunner.getBinVersionFor(versionA); + versionB = MongoRunner.getBinVersionFor(versionB); -MongoRunner.VersionSub = function(regex, version) { - this.regex = regex; - this.version = version; -}; + if (versionA === "" || versionB === "") { + return versionA === versionB; + } -// These patterns allow substituting the binary versions used for each version string to support the -// dev/stable MongoDB release cycle. -// -// If you add a new version substitution to this list, you should add it to the lists of versions -// being checked in '0_test_launching.js' to verify it is susbstituted correctly. -MongoRunner.binVersionSubs = [ new MongoRunner.VersionSub(/^latest$/, ""), - // To-be-updated when we branch for the next release. - new MongoRunner.VersionSub(/^last-stable$/, "3.2"), - new MongoRunner.VersionSub(/^3\.3(\..*){0,1}/, ""), - new MongoRunner.VersionSub(/^3\.4(\..*){0,1}/, "") ]; + return versionA.startsWith(versionB) || versionB.startsWith(versionA); + }; -MongoRunner.getBinVersionFor = function(version) { + MongoRunner.logicalOptions = { + runId: true, + pathOpts: true, + remember: true, + noRemember: true, + appendOptions: true, + restart: true, + noCleanData: true, + cleanData: true, + startClean: true, + forceLock: true, + useLogFiles: true, + logFile: true, + useHostName: true, + useHostname: true, + noReplSet: true, + forgetPort: true, + arbiter: true, + noJournalPrealloc: true, + noJournal: true, + binVersion: true, + waitForConnect: true, + bridgeOptions: true + }; - // If this is a version iterator, iterate the version via toString() - if (version instanceof MongoRunner.versionIterator.iterator) { - version = version.toString(); - } + MongoRunner.toRealPath = function(path, pathOpts) { - // No version set means we use no suffix, this is *different* from "latest" - // since latest may be mapped to a different version. - if (version == null) version = ""; - version = version.trim(); - if (version === "") return ""; + // Replace all $pathOptions with actual values + pathOpts = pathOpts || {}; + path = path.replace(/\$dataPath/g, MongoRunner.dataPath); + path = path.replace(/\$dataDir/g, MongoRunner.dataDir); + for (var key in pathOpts) { + path = path.replace(RegExp("\\$" + RegExp.escape(key), "g"), pathOpts[key]); + } + + // Relative path + // Detect Unix and Windows absolute paths + // as well as Windows drive letters + // Also captures Windows UNC paths - // See if this version is affected by version substitutions - for (var i = 0; i < MongoRunner.binVersionSubs.length; i++) { - var sub = MongoRunner.binVersionSubs[i]; - if (sub.regex.test(version)) { - version = sub.version; + if (!path.match(/^(\/|\\|[A-Za-z]:)/)) { + if (path != "" && !path.endsWith("/")) + path += "/"; + + path = MongoRunner.dataPath + path; } - } - return version; -}; + return path; -MongoRunner.areBinVersionsTheSame = function(versionA, versionB) { + }; - versionA = MongoRunner.getBinVersionFor(versionA); - versionB = MongoRunner.getBinVersionFor(versionB); + MongoRunner.toRealDir = function(path, pathOpts) { - if (versionA === "" || versionB === "") { - return versionA === versionB; - } + path = MongoRunner.toRealPath(path, pathOpts); - return versionA.startsWith(versionB) || - versionB.startsWith(versionA); -}; - -MongoRunner.logicalOptions = { runId : true, - pathOpts : true, - remember : true, - noRemember : true, - appendOptions : true, - restart : true, - noCleanData : true, - cleanData : true, - startClean : true, - forceLock : true, - useLogFiles : true, - logFile : true, - useHostName : true, - useHostname : true, - noReplSet : true, - forgetPort : true, - arbiter : true, - noJournalPrealloc : true, - noJournal : true, - binVersion : true, - waitForConnect : true, - bridgeOptions : true }; - -MongoRunner.toRealPath = function( path, pathOpts ){ - - // Replace all $pathOptions with actual values - pathOpts = pathOpts || {}; - path = path.replace( /\$dataPath/g, MongoRunner.dataPath ); - path = path.replace( /\$dataDir/g, MongoRunner.dataDir ); - for( var key in pathOpts ){ - path = path.replace( RegExp( "\\$" + RegExp.escape(key), "g" ), pathOpts[ key ] ); - } - - // Relative path - // Detect Unix and Windows absolute paths - // as well as Windows drive letters - // Also captures Windows UNC paths - - if( ! path.match( /^(\/|\\|[A-Za-z]:)/ ) ){ - if( path != "" && ! path.endsWith( "/" ) ) - path += "/"; - - path = MongoRunner.dataPath + path; - } - - return path; - -}; - -MongoRunner.toRealDir = function( path, pathOpts ){ - - path = MongoRunner.toRealPath( path, pathOpts ); - - if( path.endsWith( "/" ) ) - path = path.substring( 0, path.length - 1 ); - - return path; -}; - -MongoRunner.toRealFile = MongoRunner.toRealDir; - -/** - * Returns an iterator object which yields successive versions on toString(), starting from a - * random initial position, from an array of versions. - * - * If passed a single version string or an already-existing version iterator, just returns the - * object itself, since it will yield correctly on toString() - * - * @param {Array.<String>}|{String}|{versionIterator} - */ -MongoRunner.versionIterator = function( arr, isRandom ){ - - // If this isn't an array of versions, or is already an iterator, just use it - if( typeof arr == "string" ) return arr; - if( arr.isVersionIterator ) return arr; - - if (isRandom == undefined) isRandom = false; - - // Starting pos - var i = isRandom ? parseInt( Random.rand() * arr.length ) : 0; - - return new MongoRunner.versionIterator.iterator(i, arr); -}; - -MongoRunner.versionIterator.iterator = function(i, arr) { - - this.toString = function() { - i = ( i + 1 ) % arr.length; - print( "Returning next version : " + i + - " (" + arr[i] + ") from " + tojson( arr ) + "..." ); - return arr[ i ]; + if (path.endsWith("/")) + path = path.substring(0, path.length - 1); + + return path; }; - this.isVersionIterator = true; - -}; - -/** - * Converts the args object by pairing all keys with their value and appending - * dash-dash (--) to the keys. The only exception to this rule are keys that - * are defined in MongoRunner.logicalOptions, of which they will be ignored. - * - * @param {string} binaryName - * @param {Object} args - * - * @return {Array.<String>} an array of parameter strings that can be passed - * to the binary. - */ -MongoRunner.arrOptions = function( binaryName , args ){ - - var fullArgs = [ "" ]; - - // isObject returns true even if "args" is an array, so the else branch of this statement is - // dead code. See SERVER-14220. - if ( isObject( args ) || ( args.length == 1 && isObject( args[0] ) ) ){ - - var o = isObject( args ) ? args : args[0]; - - // If we've specified a particular binary version, use that - if (o.binVersion && o.binVersion != "") { - binaryName += "-" + o.binVersion; - } - - // Manage legacy options - var isValidOptionForBinary = function( option, value ){ - - if( ! o.binVersion ) return true; - - // Version 1.x options - if( o.binVersion.startsWith( "1." ) ){ - - return [ "nopreallocj" ].indexOf( option ) < 0; - } - - return true; + MongoRunner.toRealFile = MongoRunner.toRealDir; + + /** + * Returns an iterator object which yields successive versions on toString(), starting from a + * random initial position, from an array of versions. + * + * If passed a single version string or an already-existing version iterator, just returns the + * object itself, since it will yield correctly on toString() + * + * @param {Array.<String>}|{String}|{versionIterator} + */ + MongoRunner.versionIterator = function(arr, isRandom) { + + // If this isn't an array of versions, or is already an iterator, just use it + if (typeof arr == "string") + return arr; + if (arr.isVersionIterator) + return arr; + + if (isRandom == undefined) + isRandom = false; + + // Starting pos + var i = isRandom ? parseInt(Random.rand() * arr.length) : 0; + + return new MongoRunner.versionIterator.iterator(i, arr); + }; + + MongoRunner.versionIterator.iterator = function(i, arr) { + + this.toString = function() { + i = (i + 1) % arr.length; + print("Returning next version : " + i + " (" + arr[i] + ") from " + tojson(arr) + + "..."); + return arr[i]; }; - - for ( var k in o ){ - - // Make sure our logical option should be added to the array of options - if( ! o.hasOwnProperty( k ) || - k in MongoRunner.logicalOptions || - ! isValidOptionForBinary( k, o[k] ) ) continue; - - if ( ( k == "v" || k == "verbose" ) && isNumber( o[k] ) ){ - var n = o[k]; - if ( n > 0 ){ - if ( n > 10 ) n = 10; - var temp = "-"; - while ( n-- > 0 ) temp += "v"; - fullArgs.push( temp ); - } - } - else { - if( o[k] == undefined || o[k] == null ) continue; - fullArgs.push( "--" + k ); - if ( o[k] != "" ) - fullArgs.push( "" + o[k] ); - } - } - } - else { - for ( var i=0; i<args.length; i++ ) - fullArgs.push( args[i] ); - } - fullArgs[ 0 ] = binaryName; - return fullArgs; -}; - -MongoRunner.arrToOpts = function( arr ){ - - var opts = {}; - for( var i = 1; i < arr.length; i++ ){ - if( arr[i].startsWith( "-" ) ){ - var opt = arr[i].replace( /^-/, "" ).replace( /^-/, "" ); - - if( arr.length > i + 1 && ! arr[ i + 1 ].startsWith( "-" ) ){ - opts[ opt ] = arr[ i + 1 ]; - i++; + this.isVersionIterator = true; + + }; + + /** + * Converts the args object by pairing all keys with their value and appending + * dash-dash (--) to the keys. The only exception to this rule are keys that + * are defined in MongoRunner.logicalOptions, of which they will be ignored. + * + * @param {string} binaryName + * @param {Object} args + * + * @return {Array.<String>} an array of parameter strings that can be passed + * to the binary. + */ + MongoRunner.arrOptions = function(binaryName, args) { + + var fullArgs = [""]; + + // isObject returns true even if "args" is an array, so the else branch of this statement is + // dead code. See SERVER-14220. + if (isObject(args) || (args.length == 1 && isObject(args[0]))) { + var o = isObject(args) ? args : args[0]; + + // If we've specified a particular binary version, use that + if (o.binVersion && o.binVersion != "") { + binaryName += "-" + o.binVersion; } - else{ - opts[ opt ] = ""; + + // Manage legacy options + var isValidOptionForBinary = function(option, value) { + + if (!o.binVersion) + return true; + + // Version 1.x options + if (o.binVersion.startsWith("1.")) { + return ["nopreallocj"].indexOf(option) < 0; + } + + return true; + }; + + for (var k in o) { + // Make sure our logical option should be added to the array of options + if (!o.hasOwnProperty(k) || k in MongoRunner.logicalOptions || + !isValidOptionForBinary(k, o[k])) + continue; + + if ((k == "v" || k == "verbose") && isNumber(o[k])) { + var n = o[k]; + if (n > 0) { + if (n > 10) + n = 10; + var temp = "-"; + while (n-- > 0) + temp += "v"; + fullArgs.push(temp); + } + } else { + if (o[k] == undefined || o[k] == null) + continue; + fullArgs.push("--" + k); + if (o[k] != "") + fullArgs.push("" + o[k]); + } } - - if( opt.replace( /v/g, "" ) == "" ){ - opts[ "verbose" ] = opt.length; + } else { + for (var i = 0; i < args.length; i++) + fullArgs.push(args[i]); + } + + fullArgs[0] = binaryName; + return fullArgs; + }; + + MongoRunner.arrToOpts = function(arr) { + + var opts = {}; + for (var i = 1; i < arr.length; i++) { + if (arr[i].startsWith("-")) { + var opt = arr[i].replace(/^-/, "").replace(/^-/, ""); + + if (arr.length > i + 1 && !arr[i + 1].startsWith("-")) { + opts[opt] = arr[i + 1]; + i++; + } else { + opts[opt] = ""; + } + + if (opt.replace(/v/g, "") == "") { + opts["verbose"] = opt.length; + } } } - } - - return opts; -}; - -MongoRunner.savedOptions = {}; - -MongoRunner.mongoOptions = function(opts) { - // Don't remember waitForConnect - var waitForConnect = opts.waitForConnect; - delete opts.waitForConnect; - - // If we're a mongo object - if( opts.getDB ){ - opts = { restart : opts.runId }; - } - - // Initialize and create a copy of the opts - opts = Object.merge( opts || {}, {} ); - - if( ! opts.restart ) opts.restart = false; - - // RunId can come from a number of places - // If restart is passed as an old connection - if( opts.restart && opts.restart.getDB ){ - opts.runId = opts.restart.runId; - opts.restart = true; - } - // If it's the runId itself - else if( isObject( opts.restart ) ){ - opts.runId = opts.restart; - opts.restart = true; - } - - if( isObject( opts.remember ) ){ - opts.runId = opts.remember; - opts.remember = true; - } - else if( opts.remember == undefined ){ - // Remember by default if we're restarting - opts.remember = opts.restart; - } - - // If we passed in restart : <conn> or runId : <conn> - if( isObject( opts.runId ) && opts.runId.runId ) opts.runId = opts.runId.runId; - - if (opts.restart && opts.remember) { - opts = Object.merge(MongoRunner.savedOptions[ opts.runId ], opts); - } - // Create a new runId - opts.runId = opts.runId || ObjectId(); + return opts; + }; - if (opts.forgetPort) { - delete opts.port; - } + MongoRunner.savedOptions = {}; - // Normalize and get the binary version to use - if (opts.hasOwnProperty('binVersion')) { - opts.binVersion = MongoRunner.getBinVersionFor(opts.binVersion); - } + MongoRunner.mongoOptions = function(opts) { + // Don't remember waitForConnect + var waitForConnect = opts.waitForConnect; + delete opts.waitForConnect; - // Default for waitForConnect is true - opts.waitForConnect = - (waitForConnect == undefined || waitForConnect == null) ? true : waitForConnect; + // If we're a mongo object + if (opts.getDB) { + opts = { + restart: opts.runId + }; + } - opts.port = opts.port || allocatePort(); + // Initialize and create a copy of the opts + opts = Object.merge(opts || {}, {}); - opts.pathOpts = Object.merge(opts.pathOpts || {}, - { port : "" + opts.port, runId : "" + opts.runId }); + if (!opts.restart) + opts.restart = false; - var shouldRemember = - (!opts.restart && !opts.noRemember ) || (opts.restart && opts.appendOptions); - if (shouldRemember) { - MongoRunner.savedOptions[opts.runId] = Object.merge(opts, {}); - } + // RunId can come from a number of places + // If restart is passed as an old connection + if (opts.restart && opts.restart.getDB) { + opts.runId = opts.restart.runId; + opts.restart = true; + } + // If it's the runId itself + else if (isObject(opts.restart)) { + opts.runId = opts.restart; + opts.restart = true; + } - return opts; -}; - -/** - * @option {object} opts - * - * { - * dbpath {string} - * useLogFiles {boolean}: use with logFile option. - * logFile {string}: path to the log file. If not specified and useLogFiles - * is true, automatically creates a log file inside dbpath. - * noJournalPrealloc {boolean} - * noJournal {boolean} - * keyFile - * replSet - * oplogSize - * } - */ -MongoRunner.mongodOptions = function( opts ){ - - opts = MongoRunner.mongoOptions( opts ); - - opts.dbpath = MongoRunner.toRealDir( opts.dbpath || "$dataDir/mongod-$port", - opts.pathOpts ); - - opts.pathOpts = Object.merge( opts.pathOpts, { dbpath : opts.dbpath } ); - - if( ! opts.logFile && opts.useLogFiles ){ - opts.logFile = opts.dbpath + "/mongod.log"; - } - else if( opts.logFile ){ - opts.logFile = MongoRunner.toRealFile( opts.logFile, opts.pathOpts ); - } + if (isObject(opts.remember)) { + opts.runId = opts.remember; + opts.remember = true; + } else if (opts.remember == undefined) { + // Remember by default if we're restarting + opts.remember = opts.restart; + } - if ( opts.logFile !== undefined ) { - opts.logpath = opts.logFile; - } - - if( jsTestOptions().noJournalPrealloc || opts.noJournalPrealloc ) - opts.nopreallocj = ""; + // If we passed in restart : <conn> or runId : <conn> + if (isObject(opts.runId) && opts.runId.runId) + opts.runId = opts.runId.runId; - if((jsTestOptions().noJournal || opts.noJournal) && - !('journal' in opts) && + if (opts.restart && opts.remember) { + opts = Object.merge(MongoRunner.savedOptions[opts.runId], opts); + } + + // Create a new runId + opts.runId = opts.runId || ObjectId(); + + if (opts.forgetPort) { + delete opts.port; + } + + // Normalize and get the binary version to use + if (opts.hasOwnProperty('binVersion')) { + opts.binVersion = MongoRunner.getBinVersionFor(opts.binVersion); + } + + // Default for waitForConnect is true + opts.waitForConnect = + (waitForConnect == undefined || waitForConnect == null) ? true : waitForConnect; + + opts.port = opts.port || allocatePort(); + + opts.pathOpts = + Object.merge(opts.pathOpts || {}, {port: "" + opts.port, runId: "" + opts.runId}); + + var shouldRemember = + (!opts.restart && !opts.noRemember) || (opts.restart && opts.appendOptions); + if (shouldRemember) { + MongoRunner.savedOptions[opts.runId] = Object.merge(opts, {}); + } + + return opts; + }; + + /** + * @option {object} opts + * + * { + * dbpath {string} + * useLogFiles {boolean}: use with logFile option. + * logFile {string}: path to the log file. If not specified and useLogFiles + * is true, automatically creates a log file inside dbpath. + * noJournalPrealloc {boolean} + * noJournal {boolean} + * keyFile + * replSet + * oplogSize + * } + */ + MongoRunner.mongodOptions = function(opts) { + + opts = MongoRunner.mongoOptions(opts); + + opts.dbpath = MongoRunner.toRealDir(opts.dbpath || "$dataDir/mongod-$port", opts.pathOpts); + + opts.pathOpts = Object.merge(opts.pathOpts, {dbpath: opts.dbpath}); + + if (!opts.logFile && opts.useLogFiles) { + opts.logFile = opts.dbpath + "/mongod.log"; + } else if (opts.logFile) { + opts.logFile = MongoRunner.toRealFile(opts.logFile, opts.pathOpts); + } + + if (opts.logFile !== undefined) { + opts.logpath = opts.logFile; + } + + if (jsTestOptions().noJournalPrealloc || opts.noJournalPrealloc) + opts.nopreallocj = ""; + + if ((jsTestOptions().noJournal || opts.noJournal) && !('journal' in opts) && !('configsvr' in opts)) { - opts.nojournal = ""; - } + opts.nojournal = ""; + } - if( jsTestOptions().keyFile && !opts.keyFile) { - opts.keyFile = jsTestOptions().keyFile; - } + if (jsTestOptions().keyFile && !opts.keyFile) { + opts.keyFile = jsTestOptions().keyFile; + } + + if (opts.hasOwnProperty("enableEncryption")) { + // opts.enableEncryption, if set, must be an empty string + if (opts.enableEncryption !== "") { + throw new Error("The enableEncryption option must be an empty string if it is " + + "specified"); + } + } else if (jsTestOptions().enableEncryption !== undefined) { + if (jsTestOptions().enableEncryption !== "") { + throw new Error("The enableEncryption option must be an empty string if it is " + + "specified"); + } + opts.enableEncryption = ""; + } - if (opts.hasOwnProperty("enableEncryption")) { - // opts.enableEncryption, if set, must be an empty string - if (opts.enableEncryption !== "") { - throw new Error("The enableEncryption option must be an empty string if it is " + - "specified"); + if (opts.hasOwnProperty("encryptionKeyFile")) { + // opts.encryptionKeyFile, if set, must be a string + if (typeof opts.encryptionKeyFile !== "string") { + throw new Error("The encryptionKeyFile option must be a string if it is specified"); + } + } else if (jsTestOptions().encryptionKeyFile !== undefined) { + if (typeof(jsTestOptions().encryptionKeyFile) !== "string") { + throw new Error("The encryptionKeyFile option must be a string if it is specified"); + } + opts.encryptionKeyFile = jsTestOptions().encryptionKeyFile; } - } else if (jsTestOptions().enableEncryption !== undefined) { - if (jsTestOptions().enableEncryption !== "") { - throw new Error("The enableEncryption option must be an empty string if it is " + - "specified"); + + if (opts.hasOwnProperty("auditDestination")) { + // opts.auditDestination, if set, must be a string + if (typeof opts.auditDestination !== "string") { + throw new Error("The auditDestination option must be a string if it is specified"); + } + } else if (jsTestOptions().auditDestination !== undefined) { + if (typeof(jsTestOptions().auditDestination) !== "string") { + throw new Error("The auditDestination option must be a string if it is specified"); + } + opts.auditDestination = jsTestOptions().auditDestination; } - opts.enableEncryption = ""; - } - if (opts.hasOwnProperty("encryptionKeyFile")) { - // opts.encryptionKeyFile, if set, must be a string - if (typeof opts.encryptionKeyFile !== "string") { - throw new Error("The encryptionKeyFile option must be a string if it is specified"); + if (opts.hasOwnProperty("enableMajorityReadConcern")) { + // opts.enableMajorityReadConcern, if set, must be an empty string + if (opts.enableMajorityReadConcern !== "") { + throw new Error("The enableMajorityReadConcern option must be an empty string if " + + "it is specified"); + } + } else if (jsTestOptions().enableMajorityReadConcern !== undefined) { + if (jsTestOptions().enableMajorityReadConcern !== "") { + throw new Error("The enableMajorityReadConcern option must be an empty string if " + + "it is specified"); + } + opts.enableMajorityReadConcern = ""; } - } else if (jsTestOptions().encryptionKeyFile !== undefined) { - if (typeof(jsTestOptions().encryptionKeyFile) !== "string") { - throw new Error("The encryptionKeyFile option must be a string if it is specified"); + + if (opts.noReplSet) + opts.replSet = null; + if (opts.arbiter) + opts.oplogSize = 1; + + return opts; + }; + + MongoRunner.mongosOptions = function(opts) { + opts = MongoRunner.mongoOptions(opts); + + // Normalize configdb option to be host string if currently a host + if (opts.configdb && opts.configdb.getDB) { + opts.configdb = opts.configdb.host; } - opts.encryptionKeyFile = jsTestOptions().encryptionKeyFile; - } - if (opts.hasOwnProperty("auditDestination")) { - // opts.auditDestination, if set, must be a string - if (typeof opts.auditDestination !== "string") { - throw new Error("The auditDestination option must be a string if it is specified"); + opts.pathOpts = + Object.merge(opts.pathOpts, {configdb: opts.configdb.replace(/:|\/|,/g, "-")}); + + if (!opts.logFile && opts.useLogFiles) { + opts.logFile = + MongoRunner.toRealFile("$dataDir/mongos-$configdb-$port.log", opts.pathOpts); + } else if (opts.logFile) { + opts.logFile = MongoRunner.toRealFile(opts.logFile, opts.pathOpts); } - } else if (jsTestOptions().auditDestination !== undefined) { - if (typeof(jsTestOptions().auditDestination) !== "string") { - throw new Error("The auditDestination option must be a string if it is specified"); + + if (opts.logFile !== undefined) { + opts.logpath = opts.logFile; } - opts.auditDestination = jsTestOptions().auditDestination; - } - if (opts.hasOwnProperty("enableMajorityReadConcern")) { - // opts.enableMajorityReadConcern, if set, must be an empty string - if (opts.enableMajorityReadConcern !== "") { - throw new Error("The enableMajorityReadConcern option must be an empty string if " + - "it is specified"); + var testOptions = jsTestOptions(); + if (testOptions.keyFile && !opts.keyFile) { + opts.keyFile = testOptions.keyFile; } - } else if (jsTestOptions().enableMajorityReadConcern !== undefined) { - if (jsTestOptions().enableMajorityReadConcern !== "") { - throw new Error("The enableMajorityReadConcern option must be an empty string if " + - "it is specified"); + + if (opts.hasOwnProperty("auditDestination")) { + // opts.auditDestination, if set, must be a string + if (typeof opts.auditDestination !== "string") { + throw new Error("The auditDestination option must be a string if it is specified"); + } + } else if (testOptions.auditDestination !== undefined) { + if (typeof(testOptions.auditDestination) !== "string") { + throw new Error("The auditDestination option must be a string if it is specified"); + } + opts.auditDestination = testOptions.auditDestination; } - opts.enableMajorityReadConcern = ""; - } - if( opts.noReplSet ) opts.replSet = null; - if( opts.arbiter ) opts.oplogSize = 1; - - return opts; -}; - -MongoRunner.mongosOptions = function( opts ) { - opts = MongoRunner.mongoOptions(opts); - - // Normalize configdb option to be host string if currently a host - if( opts.configdb && opts.configdb.getDB ){ - opts.configdb = opts.configdb.host; - } - - opts.pathOpts = Object.merge( opts.pathOpts, - { configdb : opts.configdb.replace( /:|\/|,/g, "-" ) } ); - - if( ! opts.logFile && opts.useLogFiles ){ - opts.logFile = MongoRunner.toRealFile( "$dataDir/mongos-$configdb-$port.log", - opts.pathOpts ); - } - else if( opts.logFile ){ - opts.logFile = MongoRunner.toRealFile( opts.logFile, opts.pathOpts ); - } + if (!opts.hasOwnProperty('binVersion') && testOptions.mongosBinVersion) { + opts.binVersion = MongoRunner.getBinVersionFor(testOptions.mongosBinVersion); + } - if ( opts.logFile !== undefined ){ - opts.logpath = opts.logFile; - } + return opts; + }; - var testOptions = jsTestOptions(); - if (testOptions.keyFile && !opts.keyFile) { - opts.keyFile = testOptions.keyFile; - } + /** + * Starts a mongod instance. + * + * @param {Object} opts + * + * { + * useHostName {boolean}: Uses hostname of machine if true. + * forceLock {boolean}: Deletes the lock file if set to true. + * dbpath {string}: location of db files. + * cleanData {boolean}: Removes all files in dbpath if true. + * startClean {boolean}: same as cleanData. + * noCleanData {boolean}: Do not clean files (cleanData takes priority). + * binVersion {string}: version for binary (also see MongoRunner.binVersionSubs). + * + * @see MongoRunner.mongodOptions for other options + * } + * + * @return {Mongo} connection object to the started mongod instance. + * + * @see MongoRunner.arrOptions + */ + MongoRunner.runMongod = function(opts) { + + opts = opts || {}; + var useHostName = true; + var runId = null; + var waitForConnect = true; + var fullOptions = opts; + + if (isObject(opts)) { + opts = MongoRunner.mongodOptions(opts); + fullOptions = opts; + + if (opts.useHostName != undefined) { + useHostName = opts.useHostName; + } else if (opts.useHostname != undefined) { + useHostName = opts.useHostname; + } else { + useHostName = true; // Default to true + } + runId = opts.runId; + waitForConnect = opts.waitForConnect; + + if (opts.forceLock) + removeFile(opts.dbpath + "/mongod.lock"); + if ((opts.cleanData || opts.startClean) || (!opts.restart && !opts.noCleanData)) { + print("Resetting db path '" + opts.dbpath + "'"); + resetDbpath(opts.dbpath); + } - if (opts.hasOwnProperty("auditDestination")) { - // opts.auditDestination, if set, must be a string - if (typeof opts.auditDestination !== "string") { - throw new Error("The auditDestination option must be a string if it is specified"); + opts = MongoRunner.arrOptions("mongod", opts); } - } else if (testOptions.auditDestination !== undefined) { - if (typeof(testOptions.auditDestination) !== "string") { - throw new Error("The auditDestination option must be a string if it is specified"); + + var mongod = MongoRunner.startWithArgs(opts, waitForConnect); + if (!waitForConnect) + mongod = {}; + if (!mongod) + return null; + + mongod.commandLine = MongoRunner.arrToOpts(opts); + mongod.name = (useHostName ? getHostName() : "localhost") + ":" + mongod.commandLine.port; + mongod.host = mongod.name; + mongod.port = parseInt(mongod.commandLine.port); + mongod.runId = runId || ObjectId(); + mongod.dbpath = fullOptions.dbpath; + mongod.savedOptions = MongoRunner.savedOptions[mongod.runId]; + mongod.fullOptions = fullOptions; + + return mongod; + }; + + MongoRunner.runMongos = function(opts) { + opts = opts || {}; + + var useHostName = false; + var runId = null; + var waitForConnect = true; + var fullOptions = opts; + + if (isObject(opts)) { + opts = MongoRunner.mongosOptions(opts); + fullOptions = opts; + + useHostName = opts.useHostName || opts.useHostname; + runId = opts.runId; + waitForConnect = opts.waitForConnect; + + opts = MongoRunner.arrOptions("mongos", opts); } - opts.auditDestination = testOptions.auditDestination; - } - if (!opts.hasOwnProperty('binVersion') && testOptions.mongosBinVersion) { - opts.binVersion = MongoRunner.getBinVersionFor(testOptions.mongosBinVersion); - } + var mongos = MongoRunner.startWithArgs(opts, waitForConnect); + if (!waitForConnect) + mongos = {}; + if (!mongos) + return null; + + mongos.commandLine = MongoRunner.arrToOpts(opts); + mongos.name = (useHostName ? getHostName() : "localhost") + ":" + mongos.commandLine.port; + mongos.host = mongos.name; + mongos.port = parseInt(mongos.commandLine.port); + mongos.runId = runId || ObjectId(); + mongos.savedOptions = MongoRunner.savedOptions[mongos.runId]; + mongos.fullOptions = fullOptions; + + return mongos; + }; - return opts; -}; - -/** - * Starts a mongod instance. - * - * @param {Object} opts - * - * { - * useHostName {boolean}: Uses hostname of machine if true. - * forceLock {boolean}: Deletes the lock file if set to true. - * dbpath {string}: location of db files. - * cleanData {boolean}: Removes all files in dbpath if true. - * startClean {boolean}: same as cleanData. - * noCleanData {boolean}: Do not clean files (cleanData takes priority). - * binVersion {string}: version for binary (also see MongoRunner.binVersionSubs). - * - * @see MongoRunner.mongodOptions for other options - * } - * - * @return {Mongo} connection object to the started mongod instance. - * - * @see MongoRunner.arrOptions - */ -MongoRunner.runMongod = function( opts ){ - - opts = opts || {}; - var useHostName = true; - var runId = null; - var waitForConnect = true; - var fullOptions = opts; - - if( isObject( opts ) ) { - - opts = MongoRunner.mongodOptions( opts ); - fullOptions = opts; - - if (opts.useHostName != undefined) { - useHostName = opts.useHostName; - } - else if (opts.useHostname != undefined) { - useHostName = opts.useHostname; - } - else { - useHostName = true; // Default to true - } - runId = opts.runId; - waitForConnect = opts.waitForConnect; - - if( opts.forceLock ) removeFile( opts.dbpath + "/mongod.lock" ); - if( ( opts.cleanData || opts.startClean ) || ( ! opts.restart && ! opts.noCleanData ) ){ - print( "Resetting db path '" + opts.dbpath + "'" ); - resetDbpath( opts.dbpath ); - } - - opts = MongoRunner.arrOptions( "mongod", opts ); - } + MongoRunner.StopError = function(message, returnCode) { + this.name = "StopError"; + this.returnCode = returnCode || "non-zero"; + this.message = message || "MongoDB process stopped with exit code: " + this.returnCode; + this.stack = this.toString() + "\n" + (new Error()).stack; + }; - var mongod = MongoRunner.startWithArgs(opts, waitForConnect); - if (!waitForConnect) mongod = {}; - if (!mongod) return null; - - mongod.commandLine = MongoRunner.arrToOpts( opts ); - mongod.name = (useHostName ? getHostName() : "localhost") + ":" + mongod.commandLine.port; - mongod.host = mongod.name; - mongod.port = parseInt( mongod.commandLine.port ); - mongod.runId = runId || ObjectId(); - mongod.dbpath = fullOptions.dbpath; - mongod.savedOptions = MongoRunner.savedOptions[ mongod.runId ]; - mongod.fullOptions = fullOptions; - - return mongod; -}; - -MongoRunner.runMongos = function(opts) { - opts = opts || {}; - - var useHostName = false; - var runId = null; - var waitForConnect = true; - var fullOptions = opts; - - if (isObject(opts)) { - opts = MongoRunner.mongosOptions( opts ); - fullOptions = opts; - - useHostName = opts.useHostName || opts.useHostname; - runId = opts.runId; - waitForConnect = opts.waitForConnect; - - opts = MongoRunner.arrOptions("mongos", opts); - } - - var mongos = MongoRunner.startWithArgs(opts, waitForConnect); - if (!waitForConnect) mongos = {}; - if (!mongos) return null; - - mongos.commandLine = MongoRunner.arrToOpts( opts ); - mongos.name = (useHostName ? getHostName() : "localhost") + ":" + mongos.commandLine.port; - mongos.host = mongos.name; - mongos.port = parseInt( mongos.commandLine.port ); - mongos.runId = runId || ObjectId(); - mongos.savedOptions = MongoRunner.savedOptions[ mongos.runId ]; - mongos.fullOptions = fullOptions; - - return mongos; -}; - - -MongoRunner.StopError = function(message, returnCode) { - this.name = "StopError"; - this.returnCode = returnCode || "non-zero"; - this.message = message || "MongoDB process stopped with exit code: " + this.returnCode; - this.stack = this.toString() + "\n" + (new Error()).stack; -}; - -MongoRunner.StopError.prototype = Object.create(Error.prototype); -MongoRunner.StopError.prototype.constructor = MongoRunner.StopError; - -// Constants for exit codes of MongoDB processes -MongoRunner.EXIT_CLEAN = 0; -MongoRunner.EXIT_BADOPTIONS = 2; -MongoRunner.EXIT_REPLICATION_ERROR = 3; -MongoRunner.EXIT_NEED_UPGRADE = 4; -MongoRunner.EXIT_SHARDING_ERROR = 5; -MongoRunner.EXIT_KILL = 12; -MongoRunner.EXIT_ABRUPT = 14; -MongoRunner.EXIT_NTSERVICE_ERROR = 20; -MongoRunner.EXIT_JAVA = 21; -MongoRunner.EXIT_OOM_MALLOC = 42; -MongoRunner.EXIT_OOM_REALLOC = 43; -MongoRunner.EXIT_FS = 45; -MongoRunner.EXIT_CLOCK_SKEW = 47; // OpTime clock skew; deprecated -MongoRunner.EXIT_NET_ERROR = 48; -MongoRunner.EXIT_WINDOWS_SERVICE_STOP = 49; -MongoRunner.EXIT_POSSIBLE_CORRUPTION = 60; -MongoRunner.EXIT_UNCAUGHT = 100; // top level exception that wasn't caught -MongoRunner.EXIT_TEST = 101; - -/** - * Kills a mongod process. - * - * @param {number} port the port of the process to kill - * @param {number} signal The signal number to use for killing - * @param {Object} opts Additional options. Format: - * { - * auth: { - * user {string}: admin user name - * pwd {string}: admin password - * } - * } - * - * Note: The auth option is required in a authenticated mongod running in Windows since - * it uses the shutdown command, which requires admin credentials. - */ -MongoRunner.stopMongod = function( port, signal, opts ){ - - if( ! port ) { - print( "Cannot stop mongo process " + port ); - return; - } + MongoRunner.StopError.prototype = Object.create(Error.prototype); + MongoRunner.StopError.prototype.constructor = MongoRunner.StopError; + + // Constants for exit codes of MongoDB processes + MongoRunner.EXIT_CLEAN = 0; + MongoRunner.EXIT_BADOPTIONS = 2; + MongoRunner.EXIT_REPLICATION_ERROR = 3; + MongoRunner.EXIT_NEED_UPGRADE = 4; + MongoRunner.EXIT_SHARDING_ERROR = 5; + MongoRunner.EXIT_KILL = 12; + MongoRunner.EXIT_ABRUPT = 14; + MongoRunner.EXIT_NTSERVICE_ERROR = 20; + MongoRunner.EXIT_JAVA = 21; + MongoRunner.EXIT_OOM_MALLOC = 42; + MongoRunner.EXIT_OOM_REALLOC = 43; + MongoRunner.EXIT_FS = 45; + MongoRunner.EXIT_CLOCK_SKEW = 47; // OpTime clock skew; deprecated + MongoRunner.EXIT_NET_ERROR = 48; + MongoRunner.EXIT_WINDOWS_SERVICE_STOP = 49; + MongoRunner.EXIT_POSSIBLE_CORRUPTION = 60; + MongoRunner.EXIT_UNCAUGHT = 100; // top level exception that wasn't caught + MongoRunner.EXIT_TEST = 101; + + /** + * Kills a mongod process. + * + * @param {number} port the port of the process to kill + * @param {number} signal The signal number to use for killing + * @param {Object} opts Additional options. Format: + * { + * auth: { + * user {string}: admin user name + * pwd {string}: admin password + * } + * } + * + * Note: The auth option is required in a authenticated mongod running in Windows since + * it uses the shutdown command, which requires admin credentials. + */ + MongoRunner.stopMongod = function(port, signal, opts) { + + if (!port) { + print("Cannot stop mongo process " + port); + return; + } - signal = parseInt(signal) || 15; - opts = opts || {}; + signal = parseInt(signal) || 15; + opts = opts || {}; - var allowedExitCodes = [ - MongoRunner.EXIT_CLEAN - ]; + var allowedExitCodes = [MongoRunner.EXIT_CLEAN]; - if (_isWindows()) { - // Return code of processes killed with TerminateProcess on Windows - allowedExitCodes.push(1); - } - else { - // Return code of processes killed with SIGKILL on POSIX systems - allowedExitCodes.push(-9); - } + if (_isWindows()) { + // Return code of processes killed with TerminateProcess on Windows + allowedExitCodes.push(1); + } else { + // Return code of processes killed with SIGKILL on POSIX systems + allowedExitCodes.push(-9); + } - if (opts.allowedExitCodes) { - allowedExitCodes = allowedExitCodes.concat(opts.allowedExitCodes); - } + if (opts.allowedExitCodes) { + allowedExitCodes = allowedExitCodes.concat(opts.allowedExitCodes); + } - if( port.port ) - port = parseInt( port.port ); - - if( port instanceof ObjectId ){ - var opts = MongoRunner.savedOptions( port ); - if( opts ) port = parseInt( opts.port ); - } + if (port.port) + port = parseInt(port.port); - var returnCode = _stopMongoProgram(parseInt(port), signal, opts); + if (port instanceof ObjectId) { + var opts = MongoRunner.savedOptions(port); + if (opts) + port = parseInt(opts.port); + } - if (!Array.contains(allowedExitCodes, returnCode)) { - throw new MongoRunner.StopError( + var returnCode = _stopMongoProgram(parseInt(port), signal, opts); + + if (!Array.contains(allowedExitCodes, returnCode)) { + throw new MongoRunner.StopError( // clang-format off `MongoDB process on port ${port} exited with error code ${returnCode}`, // clang-format on returnCode ); - } + } - return returnCode; -}; - -MongoRunner.stopMongos = MongoRunner.stopMongod; - -/** - * Starts an instance of the specified mongo tool - * - * @param {String} binaryName The name of the tool to run - * @param {Object} opts options to pass to the tool - * { - * binVersion {string}: version of tool to run - * } - * - * @see MongoRunner.arrOptions - */ -MongoRunner.runMongoTool = function( binaryName, opts ){ - - var opts = opts || {}; - // Normalize and get the binary version to use - opts.binVersion = MongoRunner.getBinVersionFor(opts.binVersion); - - var argsArray = MongoRunner.arrOptions(binaryName, opts); - - return runMongoProgram.apply(null, argsArray); - -}; - -// Given a test name figures out a directory for that test to use for dump files and makes sure -// that directory exists and is empty. -MongoRunner.getAndPrepareDumpDirectory = function(testName) { - var dir = MongoRunner.dataPath + testName + "_external/"; - resetDbpath(dir); - return dir; -}; - -// 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. -// var conn = _startMongodEmpty("--port", 30000, "--dbpath", "asdf"); -_startMongodEmpty = function () { - var args = createMongoArgs("mongod", Array.from(arguments)); - - var dbpath = _parsePath.apply(null, args); - resetDbpath(dbpath); - - return startMongoProgram.apply(null, args); -}; - -_startMongod = function () { - print("startMongod WARNING DELETES DATA DIRECTORY THIS IS FOR TESTING ONLY"); - return _startMongodEmpty.apply(null, arguments); -}; - -_startMongodNoReset = function(){ - var args = createMongoArgs( "mongod" , Array.from(arguments) ); - return startMongoProgram.apply( null, args ); -}; - -/** - * Returns a new argArray with any test-specific arguments added. - */ -function appendSetParameterArgs(argArray) { - var programName = argArray[0]; - if (programName.endsWith('mongod') || programName.endsWith('mongos') - || programName.startsWith('mongod-') || programName.startsWith('mongos-')) { - if (jsTest.options().enableTestCommands) { - argArray.push(...['--setParameter', "enableTestCommands=1"]); - } - if (jsTest.options().authMechanism && jsTest.options().authMechanism != "SCRAM-SHA-1") { - var hasAuthMechs = false; - for (i in argArray) { - if (typeof argArray[i] === 'string' && - argArray[i].indexOf('authenticationMechanisms') != -1) { - hasAuthMechs = true; - break; + return returnCode; + }; + + MongoRunner.stopMongos = MongoRunner.stopMongod; + + /** + * Starts an instance of the specified mongo tool + * + * @param {String} binaryName The name of the tool to run + * @param {Object} opts options to pass to the tool + * { + * binVersion {string}: version of tool to run + * } + * + * @see MongoRunner.arrOptions + */ + MongoRunner.runMongoTool = function(binaryName, opts) { + + var opts = opts || {}; + // Normalize and get the binary version to use + opts.binVersion = MongoRunner.getBinVersionFor(opts.binVersion); + + var argsArray = MongoRunner.arrOptions(binaryName, opts); + + return runMongoProgram.apply(null, argsArray); + + }; + + // Given a test name figures out a directory for that test to use for dump files and makes sure + // that directory exists and is empty. + MongoRunner.getAndPrepareDumpDirectory = function(testName) { + var dir = MongoRunner.dataPath + testName + "_external/"; + resetDbpath(dir); + return dir; + }; + + // 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. + // var conn = _startMongodEmpty("--port", 30000, "--dbpath", "asdf"); + _startMongodEmpty = function() { + var args = createMongoArgs("mongod", Array.from(arguments)); + + var dbpath = _parsePath.apply(null, args); + resetDbpath(dbpath); + + return startMongoProgram.apply(null, args); + }; + + _startMongod = function() { + print("startMongod WARNING DELETES DATA DIRECTORY THIS IS FOR TESTING ONLY"); + return _startMongodEmpty.apply(null, arguments); + }; + + _startMongodNoReset = function() { + var args = createMongoArgs("mongod", Array.from(arguments)); + return startMongoProgram.apply(null, args); + }; + + /** + * Returns a new argArray with any test-specific arguments added. + */ + function appendSetParameterArgs(argArray) { + var programName = argArray[0]; + if (programName.endsWith('mongod') || programName.endsWith('mongos') || + programName.startsWith('mongod-') || programName.startsWith('mongos-')) { + if (jsTest.options().enableTestCommands) { + argArray.push(...['--setParameter', "enableTestCommands=1"]); + } + if (jsTest.options().authMechanism && jsTest.options().authMechanism != "SCRAM-SHA-1") { + var hasAuthMechs = false; + for (i in argArray) { + if (typeof argArray[i] === 'string' && + argArray[i].indexOf('authenticationMechanisms') != -1) { + hasAuthMechs = true; + break; + } + } + if (!hasAuthMechs) { + argArray.push(...[ + '--setParameter', + "authenticationMechanisms=" + jsTest.options().authMechanism + ]); } } - if (!hasAuthMechs) { - argArray.push(...['--setParameter', - "authenticationMechanisms=" + jsTest.options().authMechanism]); + if (jsTest.options().auth) { + argArray.push(...['--setParameter', "enableLocalhostAuthBypass=false"]); } - } - if (jsTest.options().auth) { - argArray.push(...['--setParameter', "enableLocalhostAuthBypass=false"]); - } - // mongos only options. Note: excludes mongos with version suffix (ie. mongos-3.0). - if (programName.endsWith('mongos')) { - // apply setParameters for mongos - if (jsTest.options().setParametersMongos) { - var params = jsTest.options().setParametersMongos.split(","); - if (params && params.length > 0) { - params.forEach(function(p) { - if (p) argArray.push(...['--setParameter', p]); - }); + // mongos only options. Note: excludes mongos with version suffix (ie. mongos-3.0). + if (programName.endsWith('mongos')) { + // apply setParameters for mongos + if (jsTest.options().setParametersMongos) { + var params = jsTest.options().setParametersMongos.split(","); + if (params && params.length > 0) { + params.forEach(function(p) { + if (p) + argArray.push(...['--setParameter', p]); + }); + } } } - } - // mongod only options. Note: excludes mongos with version suffix (ie. mongos-3.0). - else if (programName.endsWith('mongod')) { - // set storageEngine for mongod - if (jsTest.options().storageEngine) { - if ( argArray.indexOf( "--storageEngine" ) < 0 ) { - argArray.push(...['--storageEngine', jsTest.options().storageEngine]); + // mongod only options. Note: excludes mongos with version suffix (ie. mongos-3.0). + else if (programName.endsWith('mongod')) { + // set storageEngine for mongod + if (jsTest.options().storageEngine) { + if (argArray.indexOf("--storageEngine") < 0) { + argArray.push(...['--storageEngine', jsTest.options().storageEngine]); + } } - } - if (jsTest.options().wiredTigerEngineConfigString) { - argArray.push(...['--wiredTigerEngineConfigString', jsTest.options().wiredTigerEngineConfigString]); - } - if (jsTest.options().wiredTigerCollectionConfigString) { - argArray.push(...['--wiredTigerCollectionConfigString', jsTest.options().wiredTigerCollectionConfigString]); - } - if (jsTest.options().wiredTigerIndexConfigString) { - argArray.push(...['--wiredTigerIndexConfigString', jsTest.options().wiredTigerIndexConfigString]); - } - // apply setParameters for mongod - if (jsTest.options().setParameters) { - var params = jsTest.options().setParameters.split(","); - if (params && params.length > 0) { - params.forEach(function(p) { - if (p) argArray.push(...['--setParameter', p]); - }); + if (jsTest.options().wiredTigerEngineConfigString) { + argArray.push(...[ + '--wiredTigerEngineConfigString', + jsTest.options().wiredTigerEngineConfigString + ]); + } + if (jsTest.options().wiredTigerCollectionConfigString) { + argArray.push(...[ + '--wiredTigerCollectionConfigString', + jsTest.options().wiredTigerCollectionConfigString + ]); + } + if (jsTest.options().wiredTigerIndexConfigString) { + argArray.push(...[ + '--wiredTigerIndexConfigString', + jsTest.options().wiredTigerIndexConfigString + ]); + } + // apply setParameters for mongod + if (jsTest.options().setParameters) { + var params = jsTest.options().setParameters.split(","); + if (params && params.length > 0) { + params.forEach(function(p) { + if (p) + argArray.push(...['--setParameter', p]); + }); + } } } } + return argArray; } - return argArray; -} - -/** - * Start a mongo process with a particular argument array. If we aren't waiting for connect, - * return null. - */ -MongoRunner.startWithArgs = function(argArray, waitForConnect) { - // TODO: Make there only be one codepath for starting mongo processes - - argArray = appendSetParameterArgs(argArray); - var port = _parsePort.apply(null, argArray); - var pid = _startMongoProgram.apply(null, argArray); - - var conn = null; - if (waitForConnect) { - assert.soon( function() { + + /** + * Start a mongo process with a particular argument array. If we aren't waiting for connect, + * return null. + */ + MongoRunner.startWithArgs = function(argArray, waitForConnect) { + // TODO: Make there only be one codepath for starting mongo processes + + argArray = appendSetParameterArgs(argArray); + var port = _parsePort.apply(null, argArray); + var pid = _startMongoProgram.apply(null, argArray); + + var conn = null; + if (waitForConnect) { + assert.soon(function() { + try { + conn = new Mongo("127.0.0.1:" + port); + return true; + } catch (e) { + if (!checkProgram(pid)) { + print("Could not start mongo program at " + port + ", process ended"); + + // Break out + return true; + } + } + return false; + }, "unable to connect to mongo program on port " + port, 600 * 1000); + } + + return conn; + }; + + /** + * DEPRECATED + * + * Start mongod or mongos and return a Mongo() object connected to there. + * This function's first argument is "mongod" or "mongos" 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); + + // Enable test commands. + // TODO: Make this work better with multi-version testing so that we can support + // enabling this on 2.4 when testing 2.6 + var args = Array.from(arguments); + args = appendSetParameterArgs(args); + var pid = _startMongoProgram.apply(null, args); + + var m; + assert.soon(function() { try { - conn = new Mongo("127.0.0.1:" + port); + m = new Mongo("127.0.0.1:" + port); return true; - } catch( e ) { + } catch (e) { if (!checkProgram(pid)) { - print("Could not start mongo program at " + port + ", process ended"); - + // Break out + m = null; return true; } } return false; }, "unable to connect to mongo program on port " + port, 600 * 1000); - } - return conn; -}; - -/** - * DEPRECATED - * - * Start mongod or mongos and return a Mongo() object connected to there. - * This function's first argument is "mongod" or "mongos" 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 ); - - // Enable test commands. - // TODO: Make this work better with multi-version testing so that we can support - // enabling this on 2.4 when testing 2.6 - var args = Array.from(arguments); - args = appendSetParameterArgs(args); - var pid = _startMongoProgram.apply( null, args ); - - var m; - assert.soon - ( function() { - try { - m = new Mongo( "127.0.0.1:" + port ); - return true; - } catch( e ) { - if (!checkProgram(pid)) { - - print("Could not start mongo program at " + port + ", process ended"); - - // Break out - m = null; - return true; - } - } - return false; - }, "unable to connect to mongo program on port " + port, 600 * 1000 ); - - return m; -}; + return m; + }; -runMongoProgram = function() { - var args = Array.from(arguments); - args = appendSetParameterArgs(args); - var progName = args[0]; + runMongoProgram = function() { + var args = Array.from(arguments); + args = appendSetParameterArgs(args); + var progName = args[0]; + + if (jsTestOptions().auth) { + args = args.slice(1); + args.unshift(progName, + '-u', + jsTestOptions().authUser, + '-p', + jsTestOptions().authPassword, + '--authenticationDatabase=admin'); + } - if ( jsTestOptions().auth ) { - args = args.slice(1); - args.unshift( progName, - '-u', jsTestOptions().authUser, - '-p', jsTestOptions().authPassword, - '--authenticationDatabase=admin' - ); - } + if (progName == 'mongo' && !_useWriteCommandsDefault()) { + progName = args[0]; + args = args.slice(1); + args.unshift(progName, '--useLegacyWriteOps'); + } - if (progName == 'mongo' && !_useWriteCommandsDefault()) { - progName = args[0]; - args = args.slice(1); - args.unshift(progName, '--useLegacyWriteOps'); - } + return _runMongoProgram.apply(null, args); + }; - return _runMongoProgram.apply( null, args ); -}; - -// 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() { - var args = Array.from(arguments); - args = appendSetParameterArgs(args); - var progName = args[0]; - - if ( jsTestOptions().auth ) { - args = args.slice(1); - args.unshift(progName, - '-u', jsTestOptions().authUser, - '-p', jsTestOptions().authPassword, - '--authenticationDatabase=admin'); - } + // 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() { + var args = Array.from(arguments); + args = appendSetParameterArgs(args); + var progName = args[0]; + + if (jsTestOptions().auth) { + args = args.slice(1); + args.unshift(progName, + '-u', + jsTestOptions().authUser, + '-p', + jsTestOptions().authPassword, + '--authenticationDatabase=admin'); + } - if (progName == 'mongo' && !_useWriteCommandsDefault()) { - args = args.slice(1); - args.unshift(progName, '--useLegacyWriteOps'); - } + if (progName == 'mongo' && !_useWriteCommandsDefault()) { + args = args.slice(1); + args.unshift(progName, '--useLegacyWriteOps'); + } - return _startMongoProgram.apply( null, args ); -}; + return _startMongoProgram.apply(null, args); + }; -myPort = function() { - var m = db.getMongo(); - if ( m.host.match( /:/ ) ) - return m.host.match( /:(.*)/ )[ 1 ]; - else - return 27017; -}; + myPort = function() { + var m = db.getMongo(); + if (m.host.match(/:/)) + return m.host.match(/:(.*)/)[1]; + else + return 27017; + }; }()); diff --git a/src/mongo/shell/servers_misc.js b/src/mongo/shell/servers_misc.js index 1739edae0e4..f27cd754b58 100644 --- a/src/mongo/shell/servers_misc.js +++ b/src/mongo/shell/servers_misc.js @@ -1,4 +1,4 @@ -ToolTest = function( name, extraOptions ){ +ToolTest = function(name, extraOptions) { this.name = name; this.options = extraOptions; this.port = allocatePort(); @@ -7,131 +7,134 @@ ToolTest = function( name, extraOptions ){ this.dbpath = this.root + "/"; this.ext = this.root + "_external/"; this.extFile = this.root + "_external/a"; - resetDbpath( this.dbpath ); - resetDbpath( this.ext ); + resetDbpath(this.dbpath); + resetDbpath(this.ext); }; -ToolTest.prototype.startDB = function( coll ){ - assert( ! this.m , "db already running" ); +ToolTest.prototype.startDB = function(coll) { + assert(!this.m, "db already running"); - var options = {port : this.port, - dbpath : this.dbpath, - nohttpinterface : "", - noprealloc : "", - smallfiles : "", - bind_ip : "127.0.0.1"}; + var options = { + port: this.port, + dbpath: this.dbpath, + nohttpinterface: "", + noprealloc: "", + smallfiles: "", + bind_ip: "127.0.0.1" + }; Object.extend(options, this.options); this.m = startMongoProgram.apply(null, MongoRunner.arrOptions("mongod", options)); - this.db = this.m.getDB( this.baseName ); - if ( coll ) - return this.db.getCollection( coll ); + this.db = this.m.getDB(this.baseName); + if (coll) + return this.db.getCollection(coll); return this.db; }; -ToolTest.prototype.stop = function(){ - if ( ! this.m ) +ToolTest.prototype.stop = function() { + if (!this.m) return; - _stopMongoProgram( this.port ); + _stopMongoProgram(this.port); this.m = null; this.db = null; print('*** ' + this.name + " completed successfully ***"); }; -ToolTest.prototype.runTool = function(){ - var a = [ "mongo" + arguments[0] ]; +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" ) + + 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 ); + if (!hasdbpath) { + a.push("--host"); + a.push("127.0.0.1:" + this.port); } - return runMongoProgram.apply( null , a ); + return runMongoProgram.apply(null, a); }; - -ReplTest = function( name, ports ){ +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.getPort = function(master) { + if (master) + return this.ports[0]; + return this.ports[1]; }; -ReplTest.prototype.getPath = function( master ){ +ReplTest.prototype.getPath = function(master) { var p = MongoRunner.dataPath + this.name + "-"; - if ( master ) + if (master) p += "master"; else p += "slave"; return p; }; -ReplTest.prototype.getOptions = function( master , extra , putBinaryFirst, norepl ){ +ReplTest.prototype.getOptions = function(master, extra, putBinaryFirst, norepl) { - if ( ! extra ) + if (!extra) extra = {}; - if ( ! extra.oplogSize ) + if (!extra.oplogSize) extra.oplogSize = "40"; - + 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( jsTestOptions().noJournal && !('journal' in extra)) a.push( "--nojournal" ); - if( jsTestOptions().noJournalPrealloc ) a.push( "--nopreallocj" ); - if( jsTestOptions().keyFile ) { - a.push( "--keyFile" ); - a.push( jsTestOptions().keyFile ); + 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 (jsTestOptions().noJournal && !('journal' in extra)) + a.push("--nojournal"); + if (jsTestOptions().noJournalPrealloc) + a.push("--nopreallocj"); + if (jsTestOptions().keyFile) { + a.push("--keyFile"); + a.push(jsTestOptions().keyFile); } - if ( !norepl ) { - if ( master ){ - a.push( "--master" ); - } - else { - a.push( "--slave" ); - a.push( "--source" ); - a.push( "127.0.0.1:" + this.ports[0] ); + if (!norepl) { + 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 ){ + + for (var k in extra) { var v = extra[k]; - if( k in MongoRunner.logicalOptions ) continue; - a.push( "--" + k ); - if ( v != null && v !== "") - a.push( v ); + if (k in MongoRunner.logicalOptions) + continue; + a.push("--" + k); + if (v != null && v !== "") + a.push(v); } return a; }; -ReplTest.prototype.start = function( master , options , restart, norepl ){ - var lockFile = this.getPath( master ) + "/mongod.lock"; - removeFile( lockFile ); - var o = this.getOptions( master , options , restart, norepl ); +ReplTest.prototype.start = function(master, options, restart, norepl) { + var lockFile = this.getPath(master) + "/mongod.lock"; + removeFile(lockFile); + var o = this.getOptions(master, options, restart, norepl); if (restart) { var conn = startMongoProgram.apply(null, o); @@ -151,15 +154,15 @@ ReplTest.prototype.start = function( master , options , restart, norepl ){ } }; -ReplTest.prototype.stop = function( master , signal ){ - if ( arguments.length == 0 ){ - this.stop( true ); - this.stop( false ); +ReplTest.prototype.stop = function(master, signal) { + if (arguments.length == 0) { + this.stop(true); + this.stop(false); return; } print('*** ' + this.name + " completed successfully ***"); - return _stopMongoProgram( this.getPort( master ) , signal || 15 ); + return _stopMongoProgram(this.getPort(master), signal || 15); }; /** @@ -198,52 +201,53 @@ allocatePorts = function(numPorts) { return ports; }; -SyncCCTest = function( testName , extraMongodOptions ){ +SyncCCTest = function(testName, extraMongodOptions) { this._testName = testName; this._connections = []; - - for ( var i=0; i<3; i++ ){ + + for (var i = 0; i < 3; i++) { this._connections.push(MongoRunner.runMongod(extraMongodOptions)); } - - this.url = this._connections.map( function(z){ return z.name; } ).join( "," ); - this.conn = new Mongo( this.url ); + + this.url = this._connections.map(function(z) { + return z.name; + }).join(","); + this.conn = new Mongo(this.url); }; -SyncCCTest.prototype.stop = function(){ - for ( var i=0; i<this._connections.length; i++){ - _stopMongoProgram( 30000 + i ); +SyncCCTest.prototype.stop = function() { + for (var i = 0; i < this._connections.length; i++) { + _stopMongoProgram(30000 + i); } print('*** ' + this._testName + " completed successfully ***"); }; -SyncCCTest.prototype.checkHashes = function( dbname , msg ){ - var hashes = this._connections.map( - function(z){ - return z.getDB( dbname ).runCommand( "dbhash" ); - } - ); +SyncCCTest.prototype.checkHashes = function(dbname, msg) { + var hashes = this._connections.map(function(z) { + return z.getDB(dbname).runCommand("dbhash"); + }); - for ( var i=1; i<hashes.length; i++ ){ - assert.eq( hashes[0].md5 , hashes[i].md5 , "checkHash on " + dbname + " " + msg + "\n" + tojson( hashes ) ); + for (var i = 1; i < hashes.length; i++) { + assert.eq(hashes[0].md5, + hashes[i].md5, + "checkHash on " + dbname + " " + msg + "\n" + tojson(hashes)); } }; -SyncCCTest.prototype.tempKill = function( num ){ +SyncCCTest.prototype.tempKill = function(num) { num = num || 0; MongoRunner.stopMongod(this._connections[num]); }; -SyncCCTest.prototype.tempStart = function( num ){ +SyncCCTest.prototype.tempStart = function(num) { num = num || 0; var old = this._connections[num]; - this._connections[num] = MongoRunner.runMongod({ - restart: true, cleanData: false, port: old.port, dbpath: old.dbpath}); + this._connections[num] = MongoRunner.runMongod( + {restart: true, cleanData: false, port: old.port, dbpath: old.dbpath}); }; - -function startParallelShell( jsCode, port, noConnect ){ +function startParallelShell(jsCode, port, noConnect) { var args = ["mongo"]; if (typeof db == "object") { @@ -261,13 +265,13 @@ function startParallelShell( jsCode, port, noConnect ){ // Convert function into call-string if (typeof(jsCode) == "function") { jsCode = "(" + jsCode.toString() + ")();"; + } else if (typeof(jsCode) == "string") { } - else if(typeof(jsCode) == "string") {} - // do nothing + // do nothing else { throw Error("bad first argument to startParallelShell"); } - + if (noConnect) { args.push("--nodb"); } else if (typeof(db) == "object") { diff --git a/src/mongo/shell/shardingtest.js b/src/mongo/shell/shardingtest.js index 34f511b630e..4d38aa88544 100644 --- a/src/mongo/shell/shardingtest.js +++ b/src/mongo/shell/shardingtest.js @@ -1,20 +1,20 @@ /** * Starts up a sharded cluster with the given specifications. The cluster * will be fully operational after the execution of this constructor function. - * + * * @param {Object} params Contains the key-value pairs for the cluster * configuration. Accepted keys are: - * + * * { * name {string}: name for this test * verbose {number}: the verbosity for the mongos * keyFile {string}: the location of the keyFile * chunksize {number}: * nopreallocj {boolean|number}: - * + * * mongos {number|Object|Array.<Object>}: number of mongos or mongos * configuration object(s)(*). @see MongoRunner.runMongos - * + * * rs {Object|Array.<Object>}: replica set configuration object. Can * contain: * { @@ -25,32 +25,32 @@ * to ReplSetTest.prototype.initiate(). * For other options, @see ReplSetTest#start * } - * + * * shards {number|Object|Array.<Object>}: number of shards or shard * configuration object(s)(*). @see MongoRunner.runMongod - * + * * config {number|Object|Array.<Object>}: number of config server or * config server configuration object(s)(*). If this field has 3 or * more members, it implies other.sync = true. @see MongoRunner.runMongod - * + * * (*) There are two ways For multiple configuration objects. * (1) Using the object format. Example: - * + * * { d0: { verbose: 5 }, d1: { auth: '' }, rs2: { oplogsize: 10 }} - * + * * In this format, d = mongod, s = mongos & c = config servers - * + * * (2) Using the array format. Example: - * + * * [{ verbose: 5 }, { auth: '' }] - * + * * Note: you can only have single server shards for array format. * * Note: A special "bridgeOptions" property can be specified in both the object and array * formats to configure the options for the mongobridge corresponding to that node. These * options are merged with the params.bridgeOptions options, where the node-specific * options take precedence. - * + * * other: { * nopreallocj: same as above * rs: same as above @@ -58,7 +58,7 @@ * * shardOptions {Object}: same as the shards property above. * Can be used to specify options that are common all shards. - * + * * sync {boolean}: Use SyncClusterConnection, and readies * 1 or 3 config servers, based on the value of numConfigs. * configOptions {Object}: same as the config property above. @@ -120,15 +120,15 @@ var ShardingTest = function(params) { try { conn = new Mongo(url); return true; - } catch (e) { - print("Error connecting to " + url + ": " + e); - return false; - } + } catch (e) { + print("Error connecting to " + url + ": " + e); + return false; + } }); return conn; } - + /** * Constructs a human-readable string representing a chunk's range. */ @@ -149,7 +149,7 @@ var ShardingTest = function(params) { } if (dbName) { - var x = self.config.databases.findOne({ _id : dbname }); + var x = self.config.databases.findOne({_id: dbname}); if (x) return x.partitioned; else @@ -157,7 +157,7 @@ var ShardingTest = function(params) { } if (collName) { - var x = self.config.collections.findOne({ _id : collName }); + var x = self.config.collections.findOne({_id: collName}); if (x) return true; else @@ -165,49 +165,56 @@ var ShardingTest = function(params) { } } - function connectionURLTheSame( a , b ){ - if ( a == b ) + function connectionURLTheSame(a, b) { + if (a == b) return true; - if ( ! a || ! b ) + if (!a || !b) return false; - if( a.host ) return connectionURLTheSame( a.host, b ); - if( b.host ) return connectionURLTheSame( a, b.host ); + if (a.host) + return connectionURLTheSame(a.host, b); + if (b.host) + return connectionURLTheSame(a, b.host); - if( a.name ) return connectionURLTheSame( a.name, b ); - if( b.name ) return connectionURLTheSame( a, b.name ); + if (a.name) + return connectionURLTheSame(a.name, b); + if (b.name) + return connectionURLTheSame(a, b.name); - if( a.indexOf( "/" ) < 0 && b.indexOf( "/" ) < 0 ){ - a = a.split( ":" ); - b = b.split( ":" ); + if (a.indexOf("/") < 0 && b.indexOf("/") < 0) { + a = a.split(":"); + b = b.split(":"); - if( a.length != b.length ) return false; + if (a.length != b.length) + return false; - if( a.length == 2 && a[1] != b[1] ) return false; + if (a.length == 2 && a[1] != b[1]) + return false; - if( a[0] == "localhost" || a[0] == "127.0.0.1" ) a[0] = getHostName(); - if( b[0] == "localhost" || b[0] == "127.0.0.1" ) b[0] = getHostName(); + if (a[0] == "localhost" || a[0] == "127.0.0.1") + a[0] = getHostName(); + if (b[0] == "localhost" || b[0] == "127.0.0.1") + b[0] = getHostName(); return a[0] == b[0]; - } - else { - var a0 = a.split( "/" )[0]; - var b0 = b.split( "/" )[0]; + } else { + var a0 = a.split("/")[0]; + var b0 = b.split("/")[0]; return a0 == b0; } } - assert( connectionURLTheSame( "foo" , "foo" ) ); - assert( ! connectionURLTheSame( "foo" , "bar" ) ); + assert(connectionURLTheSame("foo", "foo")); + assert(!connectionURLTheSame("foo", "bar")); - assert( connectionURLTheSame( "foo/a,b" , "foo/b,a" ) ); - assert( ! connectionURLTheSame( "foo/a,b" , "bar/a,b" ) ); + assert(connectionURLTheSame("foo/a,b", "foo/b,a")); + assert(!connectionURLTheSame("foo/a,b", "bar/a,b")); // ShardingTest API this.getRSEntry = function(setName) { - for (var i=0; i<this._rs.length; i++) + for (var i = 0; i < this._rs.length; i++) if (this._rs[i].setName == setName) return this._rs[i]; throw Error("can't find rs: " + setName); @@ -221,7 +228,7 @@ var ShardingTest = function(params) { * Finds the _id of the primary shard for database 'dbname', e.g., 'test-rs0' */ this.getPrimaryShardIdForDatabase = function(dbname) { - var x = this.config.databases.findOne({ _id : "" + dbname }); + var x = this.config.databases.findOne({_id: "" + dbname}); if (x) { return x.primary; } @@ -231,23 +238,24 @@ var ShardingTest = function(params) { countDBsFound++; printjson(db); }); - throw Error("couldn't find dbname: " + dbname + " in config.databases. Total DBs: " - + countDBsFound); + throw Error("couldn't find dbname: " + dbname + " in config.databases. Total DBs: " + + countDBsFound); }; this.getNonPrimaries = function(dbname) { - var x = this.config.databases.findOne({ _id : dbname }); - if (! x) { + var x = this.config.databases.findOne({_id: dbname}); + if (!x) { this.config.databases.find().forEach(printjson); - throw Error("couldn't find dbname: " + dbname + " total: " + this.config.databases.count()); + throw Error("couldn't find dbname: " + dbname + " total: " + + this.config.databases.count()); } - return this.config.shards.find({ _id: { $ne: x.primary } }).map(z => z._id); + return this.config.shards.find({_id: {$ne: x.primary}}).map(z => z._id); }; this.getConnNames = function() { var names = []; - for (var i=0; i<this._connections.length; i++) { + for (var i = 0; i < this._connections.length; i++) { names.push(this._connections[i].name); } return names; @@ -258,13 +266,13 @@ var ShardingTest = function(params) { */ this.getPrimaryShard = function(dbname) { var dbPrimaryShardId = this.getPrimaryShardIdForDatabase(dbname); - var primaryShard = this.config.shards.findOne({ _id : dbPrimaryShardId }); + var primaryShard = this.config.shards.findOne({_id: dbPrimaryShardId}); if (primaryShard) { shardConnectionString = primaryShard.host; var rsName = shardConnectionString.substring(0, shardConnectionString.indexOf("/")); - for (var i=0; i<this._connections.length; i++) { + for (var i = 0; i < this._connections.length; i++) { var c = this._connections[i]; if (connectionURLTheSame(shardConnectionString, c.name) || connectionURLTheSame(rsName, c.name)) @@ -272,12 +280,12 @@ var ShardingTest = function(params) { } } - throw Error("can't find server connection for db '" + dbname + "'s primary shard: " - + tojson(primaryShard)); + throw Error("can't find server connection for db '" + dbname + "'s primary shard: " + + tojson(primaryShard)); }; this.normalize = function(x) { - var z = this.config.shards.findOne({ host : x }); + var z = this.config.shards.findOne({host: x}); if (z) return z._id; return x; @@ -295,7 +303,7 @@ var ShardingTest = function(params) { one = one._mongo; } - for(var i = 0; i < this._connections.length; i++) { + for (var i = 0; i < this._connections.length; i++) { if (this._connections[i] != one) { return this._connections[i]; } @@ -313,7 +321,7 @@ var ShardingTest = function(params) { one = one._mongo; } - for(var i = 0; i < this._connections.length; i++) { + for (var i = 0; i < this._connections.length; i++) { if (this._connections[i] == one) return this._connections[(i + 1) % this._connections.length]; } @@ -358,7 +366,7 @@ var ShardingTest = function(params) { var timeMillis = new Date().getTime() - _startTime.getTime(); print('*** ShardingTest ' + this._testName + " completed successfully in " + - (timeMillis / 1000) + " seconds ***"); + (timeMillis / 1000) + " seconds ***"); }; this.adminCommand = function(cmd) { @@ -377,12 +385,12 @@ var ShardingTest = function(params) { msg += " " + z.ns + "\t"; if (z.what == "split") { - msg += _rangeToString(z.details.before) + " -->> (" + _rangeToString(z.details.left) + "), (" + _rangeToString(z.details.right) + ")"; - } - else if (z.what == "multi-split") { - msg += _rangeToString(z.details.before) + " -->> (" + z.details.number + "/" + z.details.of + " " + _rangeToString(z.details.chunk) + ")"; - } - else { + msg += _rangeToString(z.details.before) + " -->> (" + + _rangeToString(z.details.left) + "), (" + _rangeToString(z.details.right) + ")"; + } else if (z.what == "multi-split") { + msg += _rangeToString(z.details.before) + " -->> (" + z.details.number + "/" + + z.details.of + " " + _rangeToString(z.details.chunk) + ")"; + } else { msg += tojsononeline(z.details); } @@ -397,10 +405,12 @@ var ShardingTest = function(params) { } var s = ""; - this.config.chunks.find(q).sort({ ns : 1, min : 1 }).forEach(function(z) { - s += " " + z._id + "\t" + z.lastmod.t + "|" + z.lastmod.i + "\t" + - tojson(z.min) + " -> " + tojson(z.max) + " " + z.shard + " " + z.ns + "\n"; - }); + this.config.chunks.find(q) + .sort({ns: 1, min: 1}) + .forEach(function(z) { + s += " " + z._id + "\t" + z.lastmod.t + "|" + z.lastmod.i + "\t" + tojson(z.min) + + " -> " + tojson(z.max) + " " + z.shard + " " + z.ns + "\n"; + }); return s; }; @@ -420,16 +430,16 @@ var ShardingTest = function(params) { } out += "sharding collection info: " + ns + "\n"; - for (var i = 0; i<this._connections.length; i++) { + for (var i = 0; i < this._connections.length; i++) { var c = this._connections[i]; out += " mongod " + c + " " + - tojson(c.getCollection(ns).getShardVersion(), " ", true) + "\n"; + 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"; + tojson(c.getCollection(ns).getShardVersion(), " ", true) + "\n"; } out += this.getChunksString(ns); @@ -479,12 +489,13 @@ var ShardingTest = function(params) { x[z._id] = 0; }); - this.config.chunks.find({ ns : dbName + "." + collName }).forEach(function(z) { - if (x[z.shard]) - x[z.shard]++; - else - x[z.shard] = 1; - }); + this.config.chunks.find({ns: dbName + "." + collName}) + .forEach(function(z) { + if (x[z.shard]) + x[z.shard]++; + else + x[z.shard] = 1; + }); return x; }; @@ -522,9 +533,11 @@ var ShardingTest = function(params) { this.getShardNames = function() { var shards = []; - this.s.getCollection("config.shards").find().forEach(function(shardDoc) { - shards.push(shardDoc._id); - }); + this.s.getCollection("config.shards") + .find() + .forEach(function(shardDoc) { + shards.push(shardDoc._id); + }); return shards; }; @@ -549,18 +562,18 @@ var ShardingTest = function(params) { var plannerShards = explain.queryPlanner.winningPlan.shards; if (execStages.shards) { - for(var i = 0; i < execStages.shards.length; i++) { + for (var i = 0; i < execStages.shards.length; i++) { var hasResults = execStages.shards[i].executionStages.nReturned && - execStages.shards[i].executionStages.nReturned > 0; + execStages.shards[i].executionStages.nReturned > 0; if (includeEmpty || hasResults) { shards.push(plannerShards[i].connectionString); } } } - for(var i = 0; i < shards.length; i++) { - for(var j = 0; j < this._connections.length; j++) { - if (connectionURLTheSame( this._connections[j], shards[i])) { + for (var i = 0; i < shards.length; i++) { + for (var j = 0; j < this._connections.length; j++) { + if (connectionURLTheSame(this._connections[j], shards[i])) { shards[i] = this._connections[j]; break; } @@ -576,7 +589,7 @@ var ShardingTest = function(params) { if (collName.getDB) dbName = "" + collName.getDB(); - else + else dbName = dbName || "test"; var c = dbName + "." + collName; @@ -587,10 +600,10 @@ var ShardingTest = function(params) { var isEmpty = (this.s.getCollection(c).count() == 0); if (!_isSharded(dbName)) { - this.s.adminCommand({ enableSharding : dbName }); + this.s.adminCommand({enableSharding: dbName}); } - var result = this.s.adminCommand({ shardcollection : c, key : key }); + var result = this.s.adminCommand({shardcollection: c, key: key}); if (!result.ok) { printjson(result); assert(false); @@ -600,8 +613,8 @@ var ShardingTest = function(params) { return; } - result = this.s.adminCommand({ split : c, middle : split }); - if (! result.ok) { + result = this.s.adminCommand({split: c, middle: split}); + if (!result.ok) { printjson(result); assert(false); } @@ -611,13 +624,12 @@ var ShardingTest = function(params) { } var result; - for(var i = 0; i < 5; i++) { + for (var i = 0; i < 5; i++) { var otherShard = this.getOther(this.getPrimaryShard(dbName)).name; - result = this.s.adminCommand({ movechunk: c, - find: move, - to: otherShard, - _waitForDelete: waitForDelete }); - if (result.ok) break; + result = this.s.adminCommand( + {movechunk: c, find: move, to: otherShard, _waitForDelete: waitForDelete}); + if (result.ok) + break; sleep(5 * 1000); } @@ -638,8 +650,7 @@ var ShardingTest = function(params) { try { assert.writeOK(sh.setBalancerState(false)); sh.waitForBalancer(false, timeout, interval); - } - finally { + } finally { db = oldDB; } }; @@ -656,8 +667,7 @@ var ShardingTest = function(params) { try { assert.writeOK(sh.setBalancerState(true)); sh.waitForBalancer(true, timeout, interval); - } - finally { + } finally { db = oldDB; } }; @@ -666,8 +676,8 @@ var ShardingTest = function(params) { * Returns true after the balancer has completed a balancing round. * * Checks that three pings were sent to config.mongos. The balancer writes a ping - * at the start and end of a balancing round. If the balancer is in the middle of - * a round, there could be three pings before the first full balancing round + * at the start and end of a balancing round. If the balancer is in the middle of + * a round, there could be three pings before the first full balancing round * completes: end ping of a round, and start and end pings of the following round. */ this.waitForBalancerRound = function() { @@ -677,7 +687,7 @@ var ShardingTest = function(params) { var oldDB = db; db = this.config; - var getPings = function(){ + var getPings = function() { return sh._getConfigDB().mongos.find().toArray(); }; @@ -685,15 +695,14 @@ var ShardingTest = function(params) { // If sh.waitForPingChange returns a non-empty array, config.mongos // was not successfully updated and no balancer round was reported. for (var i = 0; i < 3; ++i) { - if ( sh.waitForPingChange(getPings()).length != 0 ) { + if (sh.waitForPingChange(getPings()).length != 0) { return false; } } db = oldDB; return true; - } - catch (e) { + } catch (e) { print("Error running waitForPingChange: " + tojson(e)); db = oldDB; return false; @@ -701,13 +710,12 @@ var ShardingTest = function(params) { }; this.isAnyBalanceInFlight = function() { - if (this.config.locks.find({ _id : { $ne : "balancer" }, state : 2 }).count() > 0) + if (this.config.locks.find({_id: {$ne: "balancer"}, state: 2}).count() > 0) return true; var allCurrent = this.s.getDB("admin").currentOp().inprog; for (var i = 0; i < allCurrent.length; i++) { - if (allCurrent[i].desc && - allCurrent[i].desc.indexOf("cleanupOldData") == 0) + if (allCurrent[i].desc && allCurrent[i].desc.indexOf("cleanupOldData") == 0) return true; } return false; @@ -772,16 +780,18 @@ var ShardingTest = function(params) { this.stopMongos(n); if (otherParams.useBridge) { - var bridgeOptions = (opts !== mongos) ? opts.bridgeOptions - : mongos.fullOptions.bridgeOptions; + var bridgeOptions = + (opts !== mongos) ? opts.bridgeOptions : mongos.fullOptions.bridgeOptions; bridgeOptions = Object.merge(otherParams.bridgeOptions, bridgeOptions || {}); - bridgeOptions = Object.merge(bridgeOptions, { - hostName: otherParams.useHostname ? hostName : "localhost", - port: this._mongos[n].port, - // The mongos processes identify themselves to mongobridge as host:port, where the - // host is the actual hostname of the machine and not localhost. - dest: hostName + ":" + opts.port, - }); + bridgeOptions = Object.merge( + bridgeOptions, + { + hostName: otherParams.useHostname ? hostName : "localhost", + port: this._mongos[n].port, + // The mongos processes identify themselves to mongobridge as host:port, where the + // host is the actual hostname of the machine and not localhost. + dest: hostName + ":" + opts.port, + }); this._mongos[n] = new MongoBridge(bridgeOptions); } @@ -826,15 +836,17 @@ var ShardingTest = function(params) { this.stopMongod(n); if (otherParams.useBridge) { - var bridgeOptions = Object.merge(otherParams.bridgeOptions, - mongod.fullOptions.bridgeOptions || {}); - bridgeOptions = Object.merge(bridgeOptions, { - hostName: otherParams.useHostname ? hostName : "localhost", - port: this._connections[n].port, - // The mongod processes identify themselves to mongobridge as host:port, where the - // host is the actual hostname of the machine and not localhost. - dest: hostName + ":" + mongod.port, - }); + var bridgeOptions = + Object.merge(otherParams.bridgeOptions, mongod.fullOptions.bridgeOptions || {}); + bridgeOptions = Object.merge( + bridgeOptions, + { + hostName: otherParams.useHostname ? hostName : "localhost", + port: this._connections[n].port, + // The mongod processes identify themselves to mongobridge as host:port, where the + // host is the actual hostname of the machine and not localhost. + dest: hostName + ":" + mongod.port, + }); this._connections[n] = new MongoBridge(bridgeOptions); } @@ -876,15 +888,17 @@ var ShardingTest = function(params) { this.stopConfigServer(n); if (otherParams.useBridge) { - var bridgeOptions = Object.merge(otherParams.bridgeOptions, - mongod.fullOptions.bridgeOptions || {}); - bridgeOptions = Object.merge(bridgeOptions, { - hostName: otherParams.useHostname ? hostName : "localhost", - port: this._configServers[n].port, - // The mongod processes identify themselves to mongobridge as host:port, where the - // host is the actual hostname of the machine and not localhost. - dest: hostName + ":" + mongod.port, - }); + var bridgeOptions = + Object.merge(otherParams.bridgeOptions, mongod.fullOptions.bridgeOptions || {}); + bridgeOptions = Object.merge( + bridgeOptions, + { + hostName: otherParams.useHostname ? hostName : "localhost", + port: this._configServers[n].port, + // The mongod processes identify themselves to mongobridge as host:port, where the + // host is the actual hostname of the machine and not localhost. + dest: hostName + ":" + mongod.port, + }); this._configServers[n] = new MongoBridge(bridgeOptions); } @@ -912,7 +926,7 @@ var ShardingTest = function(params) { */ this.ensurePrimaryShard = function(dbName, shardName) { var db = this.s0.getDB('admin'); - var res = db.adminCommand({ movePrimary: dbName, to: shardName }); + var res = db.adminCommand({movePrimary: dbName, to: shardName}); assert(res.ok || res.errmsg == "it is already the primary", tojson(res)); }; @@ -933,15 +947,14 @@ var ShardingTest = function(params) { // config : [ { smallfiles : "" } ], // shards : { rs : true, d : true } } if (Array.isArray(numShards)) { - for(var i = 0; i < numShards.length; i++) { + for (var i = 0; i < numShards.length; i++) { otherParams["d" + i] = numShards[i]; } numShards = numShards.length; - } - else if (isObject(numShards)) { + } else if (isObject(numShards)) { var tempCount = 0; - for(var i in numShards) { + for (var i in numShards) { otherParams[i] = numShards[i]; tempCount++; } @@ -950,15 +963,14 @@ var ShardingTest = function(params) { } if (Array.isArray(numMongos)) { - for(var i = 0; i < numMongos.length; i++) { + for (var i = 0; i < numMongos.length; i++) { otherParams["s" + i] = numMongos[i]; } numMongos = numMongos.length; - } - else if (isObject(numMongos)) { + } else if (isObject(numMongos)) { var tempCount = 0; - for(var i in numMongos) { + for (var i in numMongos) { otherParams[i] = numMongos[i]; tempCount++; } @@ -967,15 +979,14 @@ var ShardingTest = function(params) { } if (Array.isArray(numConfigs)) { - for(var i = 0; i < numConfigs.length; i++) { + for (var i = 0; i < numConfigs.length; i++) { otherParams["c" + i] = numConfigs[i]; } numConfigs = numConfigs.length; - } - else if (isObject(numConfigs)) { + } else if (isObject(numConfigs)) { var tempCount = 0; - for(var i in numConfigs) { + for (var i in numConfigs) { otherParams[i] = numConfigs[i]; tempCount++; } @@ -984,8 +995,7 @@ var ShardingTest = function(params) { } otherParams.extraOptions = otherParams.extraOptions || {}; - otherParams.useHostname = otherParams.useHostname == undefined ? - true : otherParams.useHostname; + otherParams.useHostname = otherParams.useHostname == undefined ? true : otherParams.useHostname; otherParams.useBridge = otherParams.useBridge || false; otherParams.bridgeOptions = otherParams.bridgeOptions || {}; @@ -995,9 +1005,11 @@ var ShardingTest = function(params) { this._testName = testName; this._otherParams = otherParams; - var pathOpts = { testName: testName }; + var pathOpts = { + testName: testName + }; - for(var k in otherParams) { + for (var k in otherParams) { if (k.startsWith("rs") && otherParams[k] != undefined) { break; } @@ -1018,10 +1030,12 @@ var ShardingTest = function(params) { if (otherParams.rs || otherParams["rs" + i]) { var setName = testName + "-rs" + i; - var rsDefaults = { useHostname : otherParams.useHostname, - noJournalPrealloc : otherParams.nopreallocj, - oplogSize : 16, - pathOpts : Object.merge(pathOpts, { shard : i })}; + var rsDefaults = { + useHostname: otherParams.useHostname, + noJournalPrealloc: otherParams.nopreallocj, + oplogSize: 16, + pathOpts: Object.merge(pathOpts, {shard: i}) + }; rsDefaults = Object.merge(rsDefaults, otherParams.rs); rsDefaults = Object.merge(rsDefaults, otherParams.rsOptions); @@ -1036,19 +1050,23 @@ var ShardingTest = function(params) { var initiateTimeout = rsDefaults.initiateTimeout; delete rsDefaults.initiateTimeout; - var rs = new ReplSetTest({ name : setName, - nodes : numReplicas, - useHostName : otherParams.useHostname, - useBridge: otherParams.useBridge, - bridgeOptions: otherParams.bridgeOptions, - keyFile : keyFile, - protocolVersion: protocolVersion, - shardSvr : true }); + var rs = new ReplSetTest({ + name: setName, + nodes: numReplicas, + useHostName: otherParams.useHostname, + useBridge: otherParams.useBridge, + bridgeOptions: otherParams.bridgeOptions, + keyFile: keyFile, + protocolVersion: protocolVersion, + shardSvr: true + }); - this._rs[i] = { setName : setName, - test : rs, - nodes : rs.startSet(rsDefaults), - url : rs.getURL() }; + this._rs[i] = { + setName: setName, + test: rs, + nodes: rs.startSet(rsDefaults), + url: rs.getURL() + }; rs.initiate(null, null, initiateTimeout); @@ -1061,8 +1079,7 @@ var ShardingTest = function(params) { if (otherParams.useBridge) { unbridgedConnections.push(null); } - } - else { + } else { var options = { useHostname: otherParams.useHostname, noJournalPrealloc: otherParams.nopreallocj, @@ -1082,14 +1099,16 @@ var ShardingTest = function(params) { options.port = options.port || allocatePort(); if (otherParams.useBridge) { - var bridgeOptions = Object.merge(otherParams.bridgeOptions, - options.bridgeOptions || {}); - bridgeOptions = Object.merge(bridgeOptions, { - hostName: otherParams.useHostname ? hostName : "localhost", - // The mongod processes identify themselves to mongobridge as host:port, where - // the host is the actual hostname of the machine and not localhost. - dest: hostName + ":" + options.port, - }); + var bridgeOptions = + Object.merge(otherParams.bridgeOptions, options.bridgeOptions || {}); + bridgeOptions = Object.merge( + bridgeOptions, + { + hostName: otherParams.useHostname ? hostName : "localhost", + // The mongod processes identify themselves to mongobridge as host:port, where + // the host is the actual hostname of the machine and not localhost. + dest: hostName + ":" + options.port, + }); var bridge = new MongoBridge(bridgeOptions); } @@ -1123,10 +1142,14 @@ var ShardingTest = function(params) { } var rs = this._rs[i].test; - rs.getPrimary().getDB("admin").foo.save({ x : 1 }); + rs.getPrimary().getDB("admin").foo.save({x: 1}); if (keyFile) { - authutil.asCluster(rs.nodes, keyFile, function() { rs.awaitReplication(); }); + authutil.asCluster(rs.nodes, + keyFile, + function() { + rs.awaitReplication(); + }); } rs.awaitSecondaryNodes(); @@ -1141,9 +1164,8 @@ var ShardingTest = function(params) { // Default to using 3-node legacy config servers if jsTestOptions().useLegacyOptions is true // and the user didn't explicity specify a different config server configuration - if (jsTestOptions().useLegacyConfigServers && - otherParams.sync !== false && - (typeof(otherParams.config) === 'undefined' || numConfigs === 3)) { + if (jsTestOptions().useLegacyConfigServers && otherParams.sync !== false && + (typeof(otherParams.config) === 'undefined' || numConfigs === 3)) { otherParams.sync = true; } @@ -1157,14 +1179,16 @@ var ShardingTest = function(params) { var configNames = []; for (var i = 0; i < numConfigs; i++) { - var options = { useHostname : otherParams.useHostname, - noJournalPrealloc : otherParams.nopreallocj, - pathOpts : Object.merge(pathOpts, { config : i }), - dbpath : "$testName-config$config", - keyFile : keyFile, - // Ensure that journaling is always enabled for config servers. - journal : "", - configsvr : "" }; + var options = { + useHostname: otherParams.useHostname, + noJournalPrealloc: otherParams.nopreallocj, + pathOpts: Object.merge(pathOpts, {config: i}), + dbpath: "$testName-config$config", + keyFile: keyFile, + // Ensure that journaling is always enabled for config servers. + journal: "", + configsvr: "" + }; if (otherParams.configOptions && otherParams.configOptions.binVersion) { otherParams.configOptions.binVersion = @@ -1177,14 +1201,16 @@ var ShardingTest = function(params) { options.port = options.port || allocatePort(); if (otherParams.useBridge) { - var bridgeOptions = Object.merge(otherParams.bridgeOptions, - options.bridgeOptions || {}); - bridgeOptions = Object.merge(bridgeOptions, { - hostName: otherParams.useHostname ? hostName : "localhost", - // The mongod processes identify themselves to mongobridge as host:port, where - // the host is the actual hostname of the machine and not localhost. - dest: hostName + ":" + options.port, - }); + var bridgeOptions = + Object.merge(otherParams.bridgeOptions, options.bridgeOptions || {}); + bridgeOptions = Object.merge( + bridgeOptions, + { + hostName: otherParams.useHostname ? hostName : "localhost", + // The mongod processes identify themselves to mongobridge as host:port, where + // the host is the actual hostname of the machine and not localhost. + dest: hostName + ":" + options.port, + }); var bridge = new MongoBridge(bridgeOptions); } @@ -1210,24 +1236,25 @@ var ShardingTest = function(params) { } this._configDB = configNames.join(','); - } - else { + } else { // Using replica set for config servers - var rstOptions = { useHostName : otherParams.useHostname, - useBridge : otherParams.useBridge, - bridgeOptions : otherParams.bridgeOptions, - keyFile : keyFile, - name: testName + "-configRS", - }; + var rstOptions = { + useHostName: otherParams.useHostname, + useBridge: otherParams.useBridge, + bridgeOptions: otherParams.bridgeOptions, + keyFile: keyFile, + name: testName + "-configRS", + }; // when using CSRS, always use wiredTiger as the storage engine - var startOptions = { pathOpts: pathOpts, - // Ensure that journaling is always enabled for config servers. - journal : "", - configsvr : "", - noJournalPrealloc : otherParams.nopreallocj, - storageEngine : "wiredTiger", - }; + var startOptions = { + pathOpts: pathOpts, + // Ensure that journaling is always enabled for config servers. + journal: "", + configsvr: "", + noJournalPrealloc: otherParams.nopreallocj, + storageEngine: "wiredTiger", + }; if (otherParams.configOptions && otherParams.configOptions.binVersion) { otherParams.configOptions.binVersion = @@ -1253,7 +1280,7 @@ var ShardingTest = function(params) { var initiateTimeout = otherParams.rsOptions && otherParams.rsOptions.initiateTimeout; this.configRS.initiate(config, null, initiateTimeout); - this.configRS.getPrimary(); // Wait for master to be elected before starting mongos + this.configRS.getPrimary(); // Wait for master to be elected before starting mongos this._configDB = this.configRS.getURL(); this._configServers = this.configRS.nodes; @@ -1269,16 +1296,14 @@ var ShardingTest = function(params) { var configConnection = _connectWithRetry(this._configDB); print("ShardingTest " + this._testName + " :\n" + - tojson({ config : this._configDB, shards : this._connections })); + tojson({config: this._configDB, shards: this._connections})); if (numMongos == 0 && !otherParams.noChunkSize) { if (keyFile) { throw Error("Cannot set chunk size without any mongos when using auth"); } else { - configConnection.getDB("config").settings.insert({ - _id : "chunksize", - value : otherParams.chunksize || otherParams.chunkSize || 50 - }); + configConnection.getDB("config").settings.insert( + {_id: "chunksize", value: otherParams.chunksize || otherParams.chunkSize || 50}); } } @@ -1310,14 +1335,16 @@ var ShardingTest = function(params) { options.port = options.port || allocatePort(); if (otherParams.useBridge) { - var bridgeOptions = Object.merge(otherParams.bridgeOptions, - options.bridgeOptions || {}); - bridgeOptions = Object.merge(bridgeOptions, { - hostName: otherParams.useHostname ? hostName : "localhost", - // The mongos processes identify themselves to mongobridge as host:port, where the - // host is the actual hostname of the machine and not localhost. - dest: hostName + ":" + options.port, - }); + var bridgeOptions = + Object.merge(otherParams.bridgeOptions, options.bridgeOptions || {}); + bridgeOptions = Object.merge( + bridgeOptions, + { + hostName: otherParams.useHostname ? hostName : "localhost", + // The mongos processes identify themselves to mongobridge as host:port, where the + // host is the actual hostname of the machine and not localhost. + dest: hostName + ":" + options.port, + }); var bridge = new MongoBridge(bridgeOptions); } @@ -1347,11 +1374,13 @@ var ShardingTest = function(params) { // If auth is enabled for the test, login the mongos connections as system in order to // configure the instances and then log them out again. if (keyFile) { - authutil.assertAuthenticate(this._mongos, 'admin', { - user: '__system', - mechanism: 'MONGODB-CR', - pwd: cat(keyFile).replace(/[\011-\015\040]/g, '') - }); + authutil.assertAuthenticate(this._mongos, + 'admin', + { + user: '__system', + mechanism: 'MONGODB-CR', + pwd: cat(keyFile).replace(/[\011-\015\040]/g, '') + }); } try { @@ -1365,7 +1394,7 @@ var ShardingTest = function(params) { // errors. This attempt is best-effort and failure should not have effect on the actual // test execution, just the execution time. this._mongos.forEach(function(mongos) { - var res = mongos.adminCommand({ setParameter: 1, replMonitorMaxFailedChecks: 2 }); + var res = mongos.adminCommand({setParameter: 1, replMonitorMaxFailedChecks: 2}); // For tests, which use x509 certificate for authentication, the command above will not // work due to authorization error. @@ -1386,25 +1415,23 @@ var ShardingTest = function(params) { var admin = this.admin; var shardNames = this._shardNames; - this._connections.forEach( - function(z) { - var n = z.name; + this._connections.forEach(function(z) { + var n = z.name; + if (!n) { + n = z.host; if (!n) { - n = z.host; - if (!n) { - n = z; - } + n = z; } + } - print("ShardingTest " + testName + " going to add shard : " + n); + print("ShardingTest " + testName + " going to add shard : " + n); - var result = admin.runCommand({ addshard: n }); - assert.commandWorked(result, "Failed to add shard " + n); + var result = admin.runCommand({addshard: n}); + assert.commandWorked(result, "Failed to add shard " + n); - shardNames.push(result.shardAdded); - z.shardName = result.shardAdded; - } - ); + shardNames.push(result.shardAdded); + z.shardName = result.shardAdded; + }); } if (jsTestOptions().keyFile) { diff --git a/src/mongo/shell/types.js b/src/mongo/shell/types.js index c93e68fbcbe..74019720909 100644 --- a/src/mongo/shell/types.js +++ b/src/mongo/shell/types.js @@ -1,5 +1,5 @@ // Date and time types -if (typeof(Timestamp) != "undefined"){ +if (typeof(Timestamp) != "undefined") { Timestamp.prototype.tojson = function() { return this.toString(); }; @@ -15,32 +15,31 @@ if (typeof(Timestamp) != "undefined"){ Timestamp.prototype.toString = function() { return "Timestamp(" + this.t + ", " + this.i + ")"; }; -} -else { +} else { print("warning: no Timestamp class"); } -Date.timeFunc = function(theFunc, numTimes){ +Date.timeFunc = function(theFunc, numTimes) { var start = new Date(); numTimes = numTimes || 1; - for (var i=0; i<numTimes; i++){ + for (var i = 0; i < numTimes; i++) { theFunc.apply(null, Array.from(arguments).slice(2)); } return (new Date()).getTime() - start.getTime(); }; -Date.prototype.tojson = function(){ +Date.prototype.tojson = function() { var UTC = 'UTC'; - var year = this['get'+UTC+'FullYear']().zeroPad(4); - var month = (this['get'+UTC+'Month']() + 1).zeroPad(2); - var date = this['get'+UTC+'Date']().zeroPad(2); - var hour = this['get'+UTC+'Hours']().zeroPad(2); - var minute = this['get'+UTC+'Minutes']().zeroPad(2); - var sec = this['get'+UTC+'Seconds']().zeroPad(2); + var year = this['get' + UTC + 'FullYear']().zeroPad(4); + var month = (this['get' + UTC + 'Month']() + 1).zeroPad(2); + var date = this['get' + UTC + 'Date']().zeroPad(2); + var hour = this['get' + UTC + 'Hours']().zeroPad(2); + var minute = this['get' + UTC + 'Minutes']().zeroPad(2); + var sec = this['get' + UTC + 'Seconds']().zeroPad(2); - if (this['get'+UTC+'Milliseconds']()) - sec += '.' + this['get'+UTC+'Milliseconds']().zeroPad(3); + if (this['get' + UTC + 'Milliseconds']()) + sec += '.' + this['get' + UTC + 'Milliseconds']().zeroPad(3); var ofs = 'Z'; // // print a non-UTC time @@ -50,25 +49,27 @@ Date.prototype.tojson = function(){ // ofs += (ofsmin/60).zeroPad(2) // ofs += (ofsmin%60).zeroPad(2) // } - return 'ISODate("'+year+'-'+month+'-'+date+'T'+hour+':'+minute+':'+sec+ofs+'")'; + return 'ISODate("' + year + '-' + month + '-' + date + 'T' + hour + ':' + minute + ':' + sec + + ofs + '")'; }; -ISODate = function(isoDateStr){ +ISODate = function(isoDateStr) { if (!isoDateStr) return new Date(); - var isoDateRegex = /(\d{4})-?(\d{2})-?(\d{2})([T ](\d{2})(:?(\d{2})(:?(\d{2}(\.\d+)?))?)?(Z|([+-])(\d{2}):?(\d{2})?)?)?/; + var isoDateRegex = + /(\d{4})-?(\d{2})-?(\d{2})([T ](\d{2})(:?(\d{2})(:?(\d{2}(\.\d+)?))?)?(Z|([+-])(\d{2}):?(\d{2})?)?)?/; var res = isoDateRegex.exec(isoDateStr); if (!res) throw Error("invalid ISO date"); - var year = parseInt(res[1],10) || 1970; // this should always be present - var month = (parseInt(res[2],10) || 1) - 1; - var date = parseInt(res[3],10) || 0; - var hour = parseInt(res[5],10) || 0; - var min = parseInt(res[7],10) || 0; - var sec = parseInt((res[9] && res[9].substr(0,2)),10) || 0; + var year = parseInt(res[1], 10) || 1970; // this should always be present + var month = (parseInt(res[2], 10) || 1) - 1; + var date = parseInt(res[3], 10) || 0; + var hour = parseInt(res[5], 10) || 0; + var min = parseInt(res[7], 10) || 0; + var sec = parseInt((res[9] && res[9].substr(0, 2)), 10) || 0; var ms = Math.round((parseFloat(res[10]) || 0) * 1000); if (ms == 1000) { ms = 0; @@ -83,9 +84,9 @@ ISODate = function(isoDateStr){ ++hour; } if (hour == 24) { - hour = 0; // the day wrapped, let JavaScript figure out the rest + hour = 0; // the day wrapped, let JavaScript figure out the rest var tempTime = Date.UTC(year, month, date, hour, min, sec, ms); - tempTime += 24 * 60 * 60 * 1000; // milliseconds in a day + tempTime += 24 * 60 * 60 * 1000; // milliseconds in a day var tempDate = new Date(tempTime); year = tempDate.getUTCFullYear(); month = tempDate.getUTCMonth(); @@ -94,11 +95,11 @@ ISODate = function(isoDateStr){ var time = Date.UTC(year, month, date, hour, min, sec, ms); - if (res[11] && res[11] != 'Z'){ + if (res[11] && res[11] != 'Z') { var ofs = 0; - ofs += (parseInt(res[13],10) || 0) * 60*60*1000; // hours - ofs += (parseInt(res[14],10) || 0) * 60*1000; // mins - if (res[12] == '+') // if ahead subtract + ofs += (parseInt(res[13], 10) || 0) * 60 * 60 * 1000; // hours + ofs += (parseInt(res[14], 10) || 0) * 60 * 1000; // mins + if (res[12] == '+') // if ahead subtract ofs *= -1; time += ofs; @@ -108,47 +109,47 @@ ISODate = function(isoDateStr){ }; // Regular Expression -RegExp.escape = function(text){ +RegExp.escape = function(text) { return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); }; RegExp.prototype.tojson = RegExp.prototype.toString; // Array -Array.contains = function(a, x){ +Array.contains = function(a, x) { if (!Array.isArray(a)) { throw new Error("The first argument to Array.contains must be an array"); } - for (var i=0; i<a.length; i++){ + for (var i = 0; i < a.length; i++) { if (a[i] == x) return true; } return false; }; -Array.unique = function(a){ +Array.unique = function(a) { if (!Array.isArray(a)) { throw new Error("The first argument to Array.unique must be an array"); } var u = []; - for (var i=0; i<a.length; i++){ + for (var i = 0; i < a.length; i++) { var o = a[i]; - if (! Array.contains(u, o)){ + if (!Array.contains(u, o)) { u.push(o); } } return u; }; -Array.shuffle = function(arr){ +Array.shuffle = function(arr) { if (!Array.isArray(arr)) { throw new Error("The first argument to Array.shuffle must be an array"); } - for (var i=0; i<arr.length-1; i++){ - var pos = i+Random.randInt(arr.length-i); + 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; @@ -156,7 +157,7 @@ Array.shuffle = function(arr){ return arr; }; -Array.tojson = function(a, indent, nolint){ +Array.tojson = function(a, indent, nolint) { if (!Array.isArray(a)) { throw new Error("The first argument to Array.tojson must be an array"); } @@ -176,30 +177,30 @@ Array.tojson = function(a, indent, nolint){ // add to indent if we are pretty if (!nolint) - indent += "\t"; + indent += "\t"; - for (var i=0; i<a.length; i++){ + for (var i = 0; i < a.length; i++) { s += indent + tojson(a[i], indent, nolint); - if (i < a.length - 1){ + if (i < a.length - 1) { s += "," + elementSeparator; } } // remove from indent if we are pretty if (!nolint) - indent = indent.substring(1); + indent = indent.substring(1); s += elementSeparator + indent + "]"; return s; }; -Array.fetchRefs = function(arr, coll){ +Array.fetchRefs = function(arr, coll) { if (!Array.isArray(arr)) { throw new Error("The first argument to Array.fetchRefs must be an array"); } var n = []; - for (var i=0; i<arr.length; i ++){ + for (var i = 0; i < arr.length; i++) { var z = arr[i]; if (coll && coll != z.getCollection()) continue; @@ -208,7 +209,7 @@ Array.fetchRefs = function(arr, coll){ return n; }; -Array.sum = function(arr){ +Array.sum = function(arr) { if (!Array.isArray(arr)) { throw new Error("The first argument to Array.sum must be an array"); } @@ -216,12 +217,12 @@ Array.sum = function(arr){ if (arr.length == 0) return null; var s = arr[0]; - for (var i=1; i<arr.length; i++) + for (var i = 1; i < arr.length; i++) s += arr[i]; return s; }; -Array.avg = function(arr){ +Array.avg = function(arr) { if (!Array.isArray(arr)) { throw new Error("The first argument to Array.avg must be an array"); } @@ -231,7 +232,7 @@ Array.avg = function(arr){ return Array.sum(arr) / arr.length; }; -Array.stdDev = function(arr){ +Array.stdDev = function(arr) { if (!Array.isArray(arr)) { throw new Error("The first argument to Array.stdDev must be an array"); } @@ -239,7 +240,7 @@ Array.stdDev = function(arr){ var avg = Array.avg(arr); var sum = 0; - for (var i=0; i<arr.length; i++){ + for (var i = 0; i < arr.length; i++) { sum += Math.pow(arr[i] - avg, 2); } @@ -247,16 +248,16 @@ Array.stdDev = function(arr){ }; // Object -Object.extend = function(dst, src, deep){ - for (var k in src){ +Object.extend = function(dst, src, deep) { + for (var k in src) { var v = src[k]; - if (deep && typeof(v) == "object"){ - if (v.constructor === ObjectId) { // convert ObjectId properly + if (deep && typeof(v) == "object") { + if (v.constructor === ObjectId) { // convert ObjectId properly eval("v = " + tojson(v)); - } else if ("floatApprox" in v) { // convert NumberLong properly + } else if ("floatApprox" in v) { // convert NumberLong properly eval("v = " + tojson(v)); } else { - v = Object.extend(typeof (v.length) == "number" ? [] : {}, v, true); + v = Object.extend(typeof(v.length) == "number" ? [] : {}, v, true); } } dst[k] = v; @@ -264,15 +265,15 @@ Object.extend = function(dst, src, deep){ return dst; }; -Object.merge = function(dst, src, deep){ +Object.merge = function(dst, src, deep) { var clone = Object.extend({}, dst, deep); return Object.extend(clone, src, deep); }; Object.keySet = function(o) { var ret = new Array(); - for(var i in o) { - if (!(i in o.__proto__ && o[ i ] === o.__proto__[ i ])) { + for (var i in o) { + if (!(i in o.__proto__ && o[i] === o.__proto__[i])) { ret.push(i); } } @@ -282,17 +283,17 @@ Object.keySet = function(o) { // String if (String.prototype.trim === undefined) { String.prototype.trim = function() { - return this.replace(/^\s+|\s+$/g,""); + return this.replace(/^\s+|\s+$/g, ""); }; } if (String.prototype.trimLeft === undefined) { String.prototype.trimLeft = function() { - return this.replace(/^\s+/,""); + return this.replace(/^\s+/, ""); }; } if (String.prototype.trimRight === undefined) { String.prototype.trimRight = function() { - return this.replace(/\s+$/,""); + return this.replace(/\s+$/, ""); }; } @@ -300,18 +301,19 @@ if (String.prototype.trimRight === undefined) { String.prototype.ltrim = String.prototype.trimLeft; String.prototype.rtrim = String.prototype.trimRight; -String.prototype.startsWith = function(str){ +String.prototype.startsWith = function(str) { return this.indexOf(str) == 0; }; -String.prototype.endsWith = function(str){ +String.prototype.endsWith = function(str) { return this.indexOf(str, this.length - str.length) !== -1; }; // Polyfill taken from // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes if (!String.prototype.includes) { - String.prototype.includes = function() {'use strict'; + String.prototype.includes = function() { + 'use strict'; return String.prototype.indexOf.apply(this, arguments) !== -1; }; } @@ -324,7 +326,8 @@ if (!String.prototype.includes) { // @param chr character to be used for padding, defaults to whitespace // @return the padded string String.prototype.pad = function(length, right, chr) { - if (typeof chr == 'undefined') chr = ' '; + if (typeof chr == 'undefined') + chr = ' '; var str = this; for (var i = length - str.length; i > 0; i--) { if (right) { @@ -346,7 +349,7 @@ Number.prototype.zeroPad = function(width) { }; // NumberLong -if (! NumberLong.prototype) { +if (!NumberLong.prototype) { NumberLong.prototype = {}; } @@ -355,7 +358,7 @@ NumberLong.prototype.tojson = function() { }; // NumberInt -if (! NumberInt.prototype) { +if (!NumberInt.prototype) { NumberInt.prototype = {}; } @@ -365,7 +368,7 @@ NumberInt.prototype.tojson = function() { // NumberDecimal if (typeof NumberDecimal !== 'undefined') { - if (! NumberDecimal.prototype) { + if (!NumberDecimal.prototype) { NumberDecimal.prototype = {}; } @@ -375,28 +378,28 @@ if (typeof NumberDecimal !== 'undefined') { } // ObjectId -if (! ObjectId.prototype) +if (!ObjectId.prototype) ObjectId.prototype = {}; -ObjectId.prototype.toString = function(){ +ObjectId.prototype.toString = function() { return "ObjectId(" + tojson(this.str) + ")"; }; -ObjectId.prototype.tojson = function(){ +ObjectId.prototype.tojson = function() { return this.toString(); }; -ObjectId.prototype.valueOf = function(){ +ObjectId.prototype.valueOf = function() { return this.str; }; ObjectId.prototype.isObjectId = true; -ObjectId.prototype.getTimestamp = function(){ - return new Date(parseInt(this.valueOf().slice(0,8), 16)*1000); +ObjectId.prototype.getTimestamp = function() { + return new Date(parseInt(this.valueOf().slice(0, 8), 16) * 1000); }; -ObjectId.prototype.equals = function(other){ +ObjectId.prototype.equals = function(other) { return this.str == other.str; }; @@ -420,10 +423,10 @@ ObjectId.fromDate = function(source) { } // Convert date object to seconds since Unix epoch. - var seconds = Math.floor(sourceDate.getTime()/1000); + var seconds = Math.floor(sourceDate.getTime() / 1000); // Generate hex timestamp with padding. - var hexTimestamp = seconds.toString(16).pad(8,false,'0') + "0000000000000000"; + var hexTimestamp = seconds.toString(16).pad(8, false, '0') + "0000000000000000"; // Create an ObjectId with hex timestamp. var objectId = ObjectId(hexTimestamp); @@ -432,72 +435,71 @@ ObjectId.fromDate = function(source) { }; // DBPointer -if (typeof(DBPointer) != "undefined"){ - DBPointer.prototype.fetch = function(){ +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 }); + return db[this.ns].findOne({_id: this.id}); }; - DBPointer.prototype.tojson = function(indent){ + DBPointer.prototype.tojson = function(indent) { return this.toString(); }; - DBPointer.prototype.getCollection = function(){ + DBPointer.prototype.getCollection = function() { return this.ns; }; - DBPointer.prototype.getId = function(){ + DBPointer.prototype.getId = function() { return this.id; }; - DBPointer.prototype.toString = function(){ + DBPointer.prototype.toString = function() { return "DBPointer(" + tojson(this.ns) + ", " + tojson(this.id) + ")"; }; -} -else { +} else { print("warning: no DBPointer"); } // DBRef -if (typeof(DBRef) != "undefined"){ - DBRef.prototype.fetch = function(){ +if (typeof(DBRef) != "undefined") { + DBRef.prototype.fetch = function() { assert(this.$ref, "need a ns"); assert(this.$id, "need an id"); var coll = this.$db ? db.getSiblingDB(this.$db).getCollection(this.$ref) : db[this.$ref]; - return coll.findOne({ _id : this.$id }); + return coll.findOne({_id: this.$id}); }; - DBRef.prototype.tojson = function(indent){ + DBRef.prototype.tojson = function(indent) { return this.toString(); }; - DBRef.prototype.getDb = function(){ + DBRef.prototype.getDb = function() { return this.$db || undefined; }; - DBRef.prototype.getCollection = function(){ + DBRef.prototype.getCollection = function() { return this.$ref; }; - DBRef.prototype.getRef = function(){ + DBRef.prototype.getRef = function() { return this.$ref; }; - DBRef.prototype.getId = function(){ + DBRef.prototype.getId = function() { return this.$id; }; - DBRef.prototype.toString = function(){ - return "DBRef(" + tojson(this.$ref) + ", " + tojson(this.$id) + (this.$db ? ", " + tojson(this.$db) : "") + ")"; + DBRef.prototype.toString = function() { + return "DBRef(" + tojson(this.$ref) + ", " + tojson(this.$id) + + (this.$db ? ", " + tojson(this.$db) : "") + ")"; }; -} -else { +} else { print("warning: no DBRef"); } // BinData -if (typeof(BinData) != "undefined"){ +if (typeof(BinData) != "undefined") { BinData.prototype.tojson = function() { return this.toString(); }; @@ -508,87 +510,91 @@ if (typeof(BinData) != "undefined"){ BinData.prototype.length = function() { return this.len; }; -} -else { +} else { print("warning: no BinData class"); } // Map -if (typeof(Map) == "undefined"){ - Map = function(){ +if (typeof(Map) == "undefined") { + Map = function() { this._data = {}; }; } -Map.hash = function(val){ - if (! val) +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; + 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 Error( "can't hash : " + typeof(val) ); + throw Error("can't hash : " + typeof(val)); }; -Map.prototype.put = function(key, value){ +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){ +Map.prototype.get = function(key) { return this._get(key).value; }; -Map.prototype._get = function(key){ +Map.prototype._get = function(key) { var h = Map.hash(key); var a = this._data[h]; - if (! a){ + if (!a) { a = []; this._data[h] = a; } - for (var i=0; i<a.length; i++){ - if (friendlyEqual(key, a[i].key)){ + for (var i = 0; i < a.length; i++) { + if (friendlyEqual(key, a[i].key)) { return a[i]; } } - var o = { key : key, value : null }; + var o = { + key: key, + value: null + }; a.push(o); return o; }; -Map.prototype.values = function(){ +Map.prototype.values = function() { var all = []; - for (var k in this._data){ - this._data[k].forEach(function(z){ all.push(z.value); }); + for (var k in this._data) { + this._data[k].forEach(function(z) { + all.push(z.value); + }); } return all; }; -if (typeof(gc) == "undefined"){ - gc = function(){ +if (typeof(gc) == "undefined") { + gc = function() { print("warning: using noop gc()"); }; } // Free Functions -tojsononeline = function(x){ +tojsononeline = function(x) { return tojson(x, " ", true); }; -tojson = function(x, indent, nolint){ +tojson = function(x, indent, nolint) { if (x === null) return "null"; @@ -599,68 +605,69 @@ tojson = function(x, indent, nolint){ indent = ""; switch (typeof x) { - case "string": { - var out = new Array(x.length+1); - out[0] = '"'; - for (var i=0; i<x.length; i++){ - switch (x[i]) { - case '"': - out[out.length] = '\\"'; - break; - case '\\': - out[out.length] = '\\\\'; - break; - case '\b': - out[out.length] = '\\b'; - break; - case '\f': - out[out.length] = '\\f'; - break; - case '\n': - out[out.length] = '\\n'; - break; - case '\r': - out[out.length] = '\\r'; - break; - case '\t': - out[out.length] = '\\t'; - break; - - default: { - var code = x.charCodeAt(i); - if (code < 0x20){ - out[out.length] = - (code < 0x10 ? '\\u000' : '\\u00') + code.toString(16); - } else { - out[out.length] = x[i]; + case "string": { + var out = new Array(x.length + 1); + out[0] = '"'; + for (var i = 0; i < x.length; i++) { + switch (x[i]) { + case '"': + out[out.length] = '\\"'; + break; + case '\\': + out[out.length] = '\\\\'; + break; + case '\b': + out[out.length] = '\\b'; + break; + case '\f': + out[out.length] = '\\f'; + break; + case '\n': + out[out.length] = '\\n'; + break; + case '\r': + out[out.length] = '\\r'; + break; + case '\t': + out[out.length] = '\\t'; + break; + + default: { + var code = x.charCodeAt(i); + if (code < 0x20) { + out[out.length] = + (code < 0x10 ? '\\u000' : '\\u00') + code.toString(16); + } else { + out[out.length] = x[i]; + } } } } - } - return out.join('') + "\""; - } - 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(/[\t\r\n]+/gm, " "); + return out.join('') + "\""; } - return s; - } - case "function": - if (x === MinKey || x === MaxKey) - return x.tojson(); - return x.toString(); - default: - throw Error( "tojson can't handle type " + (typeof x) ); + 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(/[\t\r\n]+/gm, " "); + } + return s; + } + case "function": + if (x === MinKey || x === MaxKey) + return x.tojson(); + return x.toString(); + default: + throw Error("tojson can't handle type " + (typeof x)); } }; -tojsonObject = function(x, indent, nolint){ +tojsonObject = function(x, indent, nolint) { var lineEnding = nolint ? " " : "\n"; var tabSpace = nolint ? "" : "\t"; assert.eq((typeof x), "object", "tojsonObject needs object, not [" + (typeof x) + "]"); @@ -669,10 +676,11 @@ tojsonObject = function(x, indent, nolint){ indent = ""; if (typeof(x.tojson) == "function" && x.tojson != tojson) { - return x.tojson(indent,nolint); + return x.tojson(indent, nolint); } - if (x.constructor && typeof(x.constructor.tojson) == "function" && x.constructor.tojson != tojson) { + if (x.constructor && typeof(x.constructor.tojson) == "function" && + x.constructor.tojson != tojson) { return x.constructor.tojson(x, indent, nolint); } @@ -682,8 +690,7 @@ tojsonObject = function(x, indent, nolint){ try { x.toString(); - } - catch(e) { + } catch (e) { // toString not callable return "[object]"; } @@ -697,7 +704,7 @@ tojsonObject = function(x, indent, nolint){ if (typeof(x._simpleKeys) == "function") keys = x._simpleKeys(); var fieldStrings = []; - for (var k in keys){ + for (var k in keys) { var val = x[k]; // skip internal DB types to avoid issues with interceptors @@ -711,8 +718,7 @@ tojsonObject = function(x, indent, nolint){ if (fieldStrings.length > 0) { s += fieldStrings.join("," + lineEnding); - } - else { + } else { s += indent; } s += lineEnding; @@ -722,23 +728,23 @@ tojsonObject = function(x, indent, nolint){ return s + indent + "}"; }; -printjson = function(x){ - print( tojson( x ) ); +printjson = function(x) { + print(tojson(x)); }; -printjsononeline = function(x){ - print( tojsononeline( x ) ); +printjsononeline = function(x) { + print(tojsononeline(x)); }; -isString = function(x){ +isString = function(x) { return typeof(x) == "string"; }; -isNumber = function(x){ +isNumber = function(x) { return typeof(x) == "number"; }; // This function returns true even if the argument is an array. See SERVER-14220. -isObject = function(x){ +isObject = function(x) { return typeof(x) == "object"; }; diff --git a/src/mongo/shell/upgrade_check.js b/src/mongo/shell/upgrade_check.js index 7f5d7b17fb0..0bd61972ffc 100644 --- a/src/mongo/shell/upgrade_check.js +++ b/src/mongo/shell/upgrade_check.js @@ -1,215 +1,211 @@ (function() { -"use strict"; - -/** - * Check a document - */ -var documentUpgradeCheck = function(indexes, doc) { - var goodSoFar = true; - var invalidForStorage = Object.invalidForStorage(doc); - if (invalidForStorage) { - print("Document Error: document is no longer valid in 2.6 because " + invalidForStorage + - ": "+ tojsononeline(doc)); - goodSoFar = false; - } - indexes.forEach(function(idx) { - if (isKeyTooLarge(idx, doc)) { - print("Document Error: key for index " + tojsononeline(idx) + - " too long for document: " + tojsononeline(doc)); + "use strict"; + + /** + * Check a document + */ + var documentUpgradeCheck = function(indexes, doc) { + var goodSoFar = true; + var invalidForStorage = Object.invalidForStorage(doc); + if (invalidForStorage) { + print("Document Error: document is no longer valid in 2.6 because " + + invalidForStorage + ": " + tojsononeline(doc)); goodSoFar = false; } - }); - return goodSoFar; -}; - -var indexUpgradeCheck = function(index) { - var goodSoFar = true; - var indexValid = validateIndexKey(index.key); - if (!indexValid.ok) { - print("Index Error: invalid index spec for index '" + index.name + "': " + - tojsononeline(index.key)); - goodSoFar = false; - } - return goodSoFar; -}; - -var collUpgradeCheck = function(collObj, checkDocs) { - var fullName = collObj.getFullName(); - var collName = collObj.getName(); - var dbName = collObj.getDB().getName(); - print("\nChecking collection " + fullName + " (db:" + dbName + " coll:" + collName + ")"); - var dbObj = collObj.getDB(); - var goodSoFar = true; - - // check for _id index if and only if it should be present - // no $, not oplog, not system, not config db - var checkIdIdx = true; - if (dbName == "config") { - checkIdIdx = false; - } - else if (dbName == "local") { - if ( collName == "oplog.rs" || - collName == "oplog.$main" || - collName == "startup_log" || - collName == "me") { - checkIdIdx = false; + indexes.forEach(function(idx) { + if (isKeyTooLarge(idx, doc)) { + print("Document Error: key for index " + tojsononeline(idx) + + " too long for document: " + tojsononeline(doc)); + goodSoFar = false; + } + }); + return goodSoFar; + }; + + var indexUpgradeCheck = function(index) { + var goodSoFar = true; + var indexValid = validateIndexKey(index.key); + if (!indexValid.ok) { + print("Index Error: invalid index spec for index '" + index.name + "': " + + tojsononeline(index.key)); + goodSoFar = false; } - } - - if (collName.indexOf('$') !== -1 || collName.indexOf("system.") === 0) { - checkIdIdx = false; - } - var indexes = collObj.getIndexes(); - var foundIdIndex = false; - - // run index level checks on each index on the collection - indexes.forEach(function(index) { - - if (index.name == "_id_") { - foundIdIndex = true; + return goodSoFar; + }; + + var collUpgradeCheck = function(collObj, checkDocs) { + var fullName = collObj.getFullName(); + var collName = collObj.getName(); + var dbName = collObj.getDB().getName(); + print("\nChecking collection " + fullName + " (db:" + dbName + " coll:" + collName + ")"); + var dbObj = collObj.getDB(); + var goodSoFar = true; + + // check for _id index if and only if it should be present + // no $, not oplog, not system, not config db + var checkIdIdx = true; + if (dbName == "config") { + checkIdIdx = false; + } else if (dbName == "local") { + if (collName == "oplog.rs" || collName == "oplog.$main" || collName == "startup_log" || + collName == "me") { + checkIdIdx = false; + } } - - if (!indexUpgradeCheck(index)) { - goodSoFar = false; + + if (collName.indexOf('$') !== -1 || collName.indexOf("system.") === 0) { + checkIdIdx = false; } - else { - // add its key to the list of index keys to check documents against - if (index["v"] !== 1) { - print("Warning: upgradeCheck only supports V1 indexes. Skipping index: " + - tojsononeline(index)); + var indexes = collObj.getIndexes(); + var foundIdIndex = false; + + // run index level checks on each index on the collection + indexes.forEach(function(index) { + + if (index.name == "_id_") { + foundIdIndex = true; } - else { - indexes.push(index); + + if (!indexUpgradeCheck(index)) { + goodSoFar = false; + } else { + // add its key to the list of index keys to check documents against + if (index["v"] !== 1) { + print("Warning: upgradeCheck only supports V1 indexes. Skipping index: " + + tojsononeline(index)); + } else { + indexes.push(index); + } } + }); + + // If we need to validate the _id_ index, see if we found it. + if (checkIdIdx && !foundIdIndex) { + print("Collection Error: lack of _id index on collection: " + fullName); + goodSoFar = false; + } + // do not validate the documents in system collections + if (collName.indexOf("system.") === 0) { + checkDocs = false; + } + // do not validate the documents in config dbs + if (dbName == "config") { + checkDocs = false; } - }); - - // If we need to validate the _id_ index, see if we found it. - if (checkIdIdx && !foundIdIndex) { - print("Collection Error: lack of _id index on collection: " + fullName); - goodSoFar = false; - } - // do not validate the documents in system collections - if (collName.indexOf("system.") === 0) { - checkDocs = false; - } - // do not validate the documents in config dbs - if (dbName == "config") { - checkDocs = false; - } - // do not validate docs in local db for some collections - else if (dbName === "local") { - if ( collName == "oplog.rs" || //skip document validation for oplogs + // do not validate docs in local db for some collections + else if (dbName === "local") { + if (collName == "oplog.rs" || // skip document validation for oplogs collName == "oplog.$main" || - collName == "replset.minvalid" // skip document validation for minvalid coll + collName == "replset.minvalid" // skip document validation for minvalid coll ) { - checkDocs = false; + checkDocs = false; + } } - } - - if (checkDocs) { - var lastAlertTime = Date.now(); - var alertInterval = 10 * 1000; // 10 seconds - var numDocs = 0; - // run document level checks on each document in the collection - var theColl = dbObj.getSiblingDB(dbName).getCollection(collName); - theColl.find().addOption(DBQuery.Option.noTimeout).sort({$natural: 1}).forEach( - function(doc) { - numDocs++; - - if (!documentUpgradeCheck(indexes, doc)) { - goodSoFar = false; - lastAlertTime = Date.now(); - } - var nowTime = Date.now(); - if (nowTime - lastAlertTime > alertInterval) { - print(numDocs + " documents processed"); - lastAlertTime = nowTime; - } - }); - } - - return goodSoFar; -}; - -var dbUpgradeCheck = function(dbObj, checkDocs) { - print("\nChecking database " + dbObj.getName()); - var goodSoFar = true; - - // run collection level checks on each collection in the db - dbObj.getCollectionNames().forEach(function(collName) { - if (!collUpgradeCheck(dbObj.getCollection(collName), checkDocs)) { - goodSoFar = false; + + if (checkDocs) { + var lastAlertTime = Date.now(); + var alertInterval = 10 * 1000; // 10 seconds + var numDocs = 0; + // run document level checks on each document in the collection + var theColl = dbObj.getSiblingDB(dbName).getCollection(collName); + theColl.find() + .addOption(DBQuery.Option.noTimeout) + .sort({$natural: 1}) + .forEach(function(doc) { + numDocs++; + + if (!documentUpgradeCheck(indexes, doc)) { + goodSoFar = false; + lastAlertTime = Date.now(); + } + var nowTime = Date.now(); + if (nowTime - lastAlertTime > alertInterval) { + print(numDocs + " documents processed"); + lastAlertTime = nowTime; + } + }); } - }); - - return goodSoFar; -}; - -DB.prototype.upgradeCheck = function(obj, checkDocs) { - var self = this; - // parse args if there are any - if (obj) { - // check collection if a collection is passed - if (obj["collection"]) { - // make sure a string was passed in for the collection - if (typeof obj["collection"] !== "string") { - throw Error("The collection field must contain a string"); + + return goodSoFar; + }; + + var dbUpgradeCheck = function(dbObj, checkDocs) { + print("\nChecking database " + dbObj.getName()); + var goodSoFar = true; + + // run collection level checks on each collection in the db + dbObj.getCollectionNames().forEach(function(collName) { + if (!collUpgradeCheck(dbObj.getCollection(collName), checkDocs)) { + goodSoFar = false; } - else { - print("Checking collection '" + self.getName() + '.' + obj["collection"] + - "' for 2.6 upgrade compatibility"); - if (collUpgradeCheck(self.getCollection(obj["collection"]))) { - print("Everything in '" + self.getName() + '.' + obj["collection"] + - "' is ready for the upgrade!"); - return true; + }); + + return goodSoFar; + }; + + DB.prototype.upgradeCheck = function(obj, checkDocs) { + var self = this; + // parse args if there are any + if (obj) { + // check collection if a collection is passed + if (obj["collection"]) { + // make sure a string was passed in for the collection + if (typeof obj["collection"] !== "string") { + throw Error("The collection field must contain a string"); + } else { + print("Checking collection '" + self.getName() + '.' + obj["collection"] + + "' for 2.6 upgrade compatibility"); + if (collUpgradeCheck(self.getCollection(obj["collection"]))) { + print("Everything in '" + self.getName() + '.' + obj["collection"] + + "' is ready for the upgrade!"); + return true; + } + print("To fix the problems above please consult " + + "http://dochub.mongodb.org/core/upgrade_checker_help"); + return false; } - print("To fix the problems above please consult " + - "http://dochub.mongodb.org/core/upgrade_checker_help"); - return false; + } else { + throw Error( + "When providing an argument to upgradeCheck, it must be of the form " + + "{collection: <collectionNameString>}. Otherwise, it will check every " + + "collection in the database. If you would like to check all databases, " + + "run db.upgradeCheckAllDBs() from the admin database."); } } - else { - throw Error("When providing an argument to upgradeCheck, it must be of the form " + - "{collection: <collectionNameString>}. Otherwise, it will check every " + - "collection in the database. If you would like to check all databases, " + - "run db.upgradeCheckAllDBs() from the admin database."); + + print("database '" + self.getName() + "' for 2.6 upgrade compatibility"); + if (dbUpgradeCheck(self, checkDocs)) { + print("Everything in '" + self.getName() + "' is ready for the upgrade!"); + return true; } - } - - print("database '" + self.getName() + "' for 2.6 upgrade compatibility"); - if (dbUpgradeCheck(self, checkDocs)) { - print("Everything in '" + self.getName() + "' is ready for the upgrade!"); - return true; - } - print("To fix the problems above please consult " + - "http://dochub.mongodb.org/core/upgrade_checker_help"); - return false; -}; - -DB.prototype.upgradeCheckAllDBs = function(checkDocs) { - var self = this; - if (self.getName() !== "admin") { - throw Error("db.upgradeCheckAllDBs() can only be run from the admin database"); - } - - var dbs = self.getMongo().getDBs(); - var goodSoFar = true; - - // run db level checks on each db - dbs.databases.forEach(function(dbObj) { - if (!dbUpgradeCheck(self.getSiblingDB(dbObj.name), checkDocs)) { - goodSoFar = false; + print("To fix the problems above please consult " + + "http://dochub.mongodb.org/core/upgrade_checker_help"); + return false; + }; + + DB.prototype.upgradeCheckAllDBs = function(checkDocs) { + var self = this; + if (self.getName() !== "admin") { + throw Error("db.upgradeCheckAllDBs() can only be run from the admin database"); + } + + var dbs = self.getMongo().getDBs(); + var goodSoFar = true; + + // run db level checks on each db + dbs.databases.forEach(function(dbObj) { + if (!dbUpgradeCheck(self.getSiblingDB(dbObj.name), checkDocs)) { + goodSoFar = false; + } + }); + + if (goodSoFar) { + print("Everything is ready for the upgrade!"); + return true; } - }); - - if (goodSoFar) { - print("Everything is ready for the upgrade!"); - return true; - } - print("To fix the problems above please consult " + - "http://dochub.mongodb.org/core/upgrade_checker_help"); - return false; -}; + print("To fix the problems above please consult " + + "http://dochub.mongodb.org/core/upgrade_checker_help"); + return false; + }; })(); diff --git a/src/mongo/shell/utils.js b/src/mongo/shell/utils.js index 594bcfb97da..75bf3b9f7f8 100644 --- a/src/mongo/shell/utils.js +++ b/src/mongo/shell/utils.js @@ -1,22 +1,24 @@ __quiet = false; -__magicNoPrint = { __magicNoPrint : 1111 }; -__callLastError = false; +__magicNoPrint = { + __magicNoPrint: 1111 +}; +__callLastError = false; _verboseShell = false; -chatty = function(s){ - if ( ! __quiet ) - print( s ); +chatty = function(s) { + if (!__quiet) + print(s); }; function reconnect(db) { assert.soon(function() { - try { - db.runCommand({ping:1}); - return true; - } catch (x) { - return false; - } - }); + try { + db.runCommand({ping: 1}); + return true; + } catch (x) { + return false; + } + }); } function _getErrorWithCode(codeOrObj, message) { @@ -36,32 +38,32 @@ function _getErrorWithCode(codeOrObj, message) { } // Please consider using bsonWoCompare instead of this as much as possible. -friendlyEqual = function( a , b ){ - if ( a == b ) +friendlyEqual = function(a, b) { + if (a == b) return true; - - a = tojson(a,false,true); - b = tojson(b,false,true); - if ( a == b ) + a = tojson(a, false, true); + b = tojson(b, false, true); + + if (a == b) return true; - var clean = function( s ){ - s = s.replace( /NumberInt\((\-?\d+)\)/g , "$1" ); + var clean = function(s) { + s = s.replace(/NumberInt\((\-?\d+)\)/g, "$1"); return s; }; - + a = clean(a); b = clean(b); - if ( a == b ) + if (a == b) return true; - + return false; }; -printStackTrace = function(){ - try{ +printStackTrace = function() { + try { throw new Error("Printing Stack Trace"); } catch (e) { print(e.stack); @@ -69,13 +71,15 @@ printStackTrace = function(){ }; /** - * <p> Set the shell verbosity. If verbose the shell will display more information about command results. </> + * <p> Set the shell verbosity. If verbose the shell will display more information about command + * results. </> * <p> Default is off. <p> * @param {Bool} verbosity on / off */ -setVerboseShell = function( value ) { - if( value == undefined ) value = true; - _verboseShell = value; +setVerboseShell = function(value) { + if (value == undefined) + value = true; + _verboseShell = value; }; // Formats a simple stacked horizontal histogram bar in the shell. @@ -101,92 +105,98 @@ _barFormat = function(data, width) { return res; }; - -//these two are helpers for Array.sort(func) -compare = function(l, r){ return (l == r ? 0 : (l < r ? -1 : 1)); }; +// these two are helpers for Array.sort(func) +compare = function(l, r) { + return (l == r ? 0 : (l < r ? -1 : 1)); +}; // arr.sort(compareOn('name')) -compareOn = function(field){ - return function(l, r) { return compare(l[field], r[field]); }; +compareOn = function(field) { + return function(l, r) { + return compare(l[field], r[field]); + }; }; - -shellPrint = function( x ){ +shellPrint = function(x) { it = x; - if ( x != undefined ) - shellPrintHelper( x ); - - if ( db ){ + 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 ) ); + 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" ); + print("an error " + tojson(e.err) + " occurred " + e.nPrev + + " operations back in the command invocation"); } db.resetError(); } }; -print.captureAllOutput = function (fn, args) { +print.captureAllOutput = function(fn, args) { var res = {}; res.output = []; var __orig_print = print; - print = function () { - Array.prototype.push.apply(res.output, Array.prototype.slice.call(arguments).join(" ").split("\n")); + print = function() { + Array.prototype.push.apply(res.output, + Array.prototype.slice.call(arguments).join(" ").split("\n")); }; try { res.result = fn.apply(undefined, args); - } - finally { + } finally { // Stop capturing print() output print = __orig_print; } return res; }; -if ( typeof TestData == "undefined" ){ +if (typeof TestData == "undefined") { TestData = undefined; } -jsTestName = function(){ - if( TestData ) return TestData.testName; +jsTestName = function() { + if (TestData) + return TestData.testName; return "__unknown_name__"; }; -var _jsTestOptions = { enableTestCommands : true }; // Test commands should be enabled by default - -jsTestOptions = function(){ - if( TestData ) { - return Object.merge(_jsTestOptions, - { setParameters : TestData.setParameters, - setParametersMongos : TestData.setParametersMongos, - storageEngine: TestData.storageEngine, - wiredTigerEngineConfigString: TestData.wiredTigerEngineConfigString, - wiredTigerCollectionConfigString: TestData.wiredTigerCollectionConfigString, - wiredTigerIndexConfigString: TestData.wiredTigerIndexConfigString, - noJournal : TestData.noJournal, - noJournalPrealloc : TestData.noJournalPrealloc, - auth : TestData.auth, - keyFile : TestData.keyFile, - authUser : "__system", - authPassword : TestData.keyFileData, - authMechanism : TestData.authMechanism, - adminUser : TestData.adminUser || "admin", - adminPassword : TestData.adminPassword || "password", - useLegacyConfigServers: TestData.useLegacyConfigServers || false, - useLegacyReplicationProtocol: - TestData.useLegacyReplicationProtocol || false, - enableMajorityReadConcern: TestData.enableMajorityReadConcern, - enableEncryption: TestData.enableEncryption, - encryptionKeyFile: TestData.encryptionKeyFile, - auditDestination: TestData.auditDestination, - minPort: TestData.minPort, - maxPort: TestData.maxPort, - // Note: does not support the array version - mongosBinVersion: TestData.mongosBinVersion || "", - } - ); +var _jsTestOptions = { + enableTestCommands: true +}; // Test commands should be enabled by default + +jsTestOptions = function() { + if (TestData) { + return Object.merge( + _jsTestOptions, + { + setParameters: TestData.setParameters, + setParametersMongos: TestData.setParametersMongos, + storageEngine: TestData.storageEngine, + wiredTigerEngineConfigString: TestData.wiredTigerEngineConfigString, + wiredTigerCollectionConfigString: TestData.wiredTigerCollectionConfigString, + wiredTigerIndexConfigString: TestData.wiredTigerIndexConfigString, + noJournal: TestData.noJournal, + noJournalPrealloc: TestData.noJournalPrealloc, + auth: TestData.auth, + keyFile: TestData.keyFile, + authUser: "__system", + authPassword: TestData.keyFileData, + authMechanism: TestData.authMechanism, + adminUser: TestData.adminUser || "admin", + adminPassword: TestData.adminPassword || "password", + useLegacyConfigServers: TestData.useLegacyConfigServers || false, + useLegacyReplicationProtocol: TestData.useLegacyReplicationProtocol || false, + enableMajorityReadConcern: TestData.enableMajorityReadConcern, + enableEncryption: TestData.enableEncryption, + encryptionKeyFile: TestData.encryptionKeyFile, + auditDestination: TestData.auditDestination, + minPort: TestData.minPort, + maxPort: TestData.maxPort, + // Note: does not support the array version + mongosBinVersion: TestData.mongosBinVersion || "", + }); } return _jsTestOptions; }; @@ -195,8 +205,8 @@ setJsTestOption = function(name, value) { _jsTestOptions[name] = value; }; -jsTestLog = function(msg){ - print( "\n\n----\n" + msg + "\n----\n\n" ); +jsTestLog = function(msg) { + print("\n\n----\n" + msg + "\n----\n\n"); }; jsTest = {}; @@ -220,9 +230,9 @@ jsTest.authenticate = function(conn) { // Set authenticated to stop an infinite recursion from getDB calling // back into authenticate. conn.authenticated = true; - print ("Authenticating as internal " + jsTestOptions().authUser + " user with mechanism " + - DB.prototype._defaultAuthenticationMechanism + - " on connection: " + conn); + print("Authenticating as internal " + jsTestOptions().authUser + + " user with mechanism " + DB.prototype._defaultAuthenticationMechanism + + " on connection: " + conn); conn.authenticated = conn.getDB('admin').auth({ user: jsTestOptions().authUser, pwd: jsTestOptions().authPassword, @@ -241,10 +251,10 @@ jsTest.authenticateNodes = function(nodes) { for (var i = 0; i < nodes.length; i++) { // Don't try to authenticate to arbiters res = nodes[i].getDB("admin").runCommand({replSetGetStatus: 1}); - if(res.myState == 7) { + if (res.myState == 7) { continue; } - if(jsTest.authenticate(nodes[i]) != 1) { + if (jsTest.authenticate(nodes[i]) != 1) { return false; } } @@ -253,7 +263,7 @@ jsTest.authenticateNodes = function(nodes) { }; jsTest.isMongos = function(conn) { - return conn.getDB('admin').isMaster().msg=='isdbgrid'; + return conn.getDB('admin').isMaster().msg == 'isdbgrid'; }; defaultPrompt = function() { @@ -279,12 +289,19 @@ defaultPrompt = function() { try { var prompt = replSetMemberStatePrompt(); // set our status that it was good - db.getMongo().authStatus = {replSetGetStatus:true, isMaster: true}; + db.getMongo().authStatus = { + replSetGetStatus: true, + isMaster: true + }; return prefix + prompt; } catch (e) { // don't have permission to run that, or requires auth - //print(e); - status = {authRequired: true, replSetGetStatus: false, isMaster: true}; + // print(e); + status = { + authRequired: true, + replSetGetStatus: false, + isMaster: true + }; } } // auth detected @@ -299,7 +316,7 @@ defaultPrompt = function() { return prefix + prompt; } catch (e) { // don't have permission to run that, or requires auth - //print(e); + // print(e); status.authRequired = true; status.replSetGetStatus = false; } @@ -320,7 +337,9 @@ defaultPrompt = function() { } catch (ex) { printjson(ex); // reset status and let it figure it out next time. - status = {isMaster:true}; + status = { + isMaster: true + }; } db.getMongo().authStatus = status; @@ -329,34 +348,34 @@ defaultPrompt = function() { replSetMemberStatePrompt = function() { var state = ''; - var stateInfo = db.getSiblingDB( 'admin' ).runCommand( { replSetGetStatus:1, forShell:1 } ); - if ( stateInfo.ok ) { + var stateInfo = db.getSiblingDB('admin').runCommand({replSetGetStatus: 1, forShell: 1}); + if (stateInfo.ok) { // Report the self member's stateStr if it's present. - stateInfo.members.forEach( function( member ) { - if ( member.self ) { - state = member.stateStr; - } - } ); + stateInfo.members.forEach(function(member) { + if (member.self) { + state = member.stateStr; + } + }); // Otherwise fall back to reporting the numeric myState field (mongodb 1.6). - if ( !state ) { + if (!state) { state = stateInfo.myState; } state = '' + stateInfo.set + ':' + state; } else { - var info = stateInfo.info; - if ( info && info.length < 20 ) { - state = info; // "mongos", "configsvr" - } else { - throw _getErrorWithCode(stateInfo, "Failed:" + info); - } + var info = stateInfo.info; + if (info && info.length < 20) { + state = info; // "mongos", "configsvr" + } else { + throw _getErrorWithCode(stateInfo, "Failed:" + info); + } } return state + '> '; }; isMasterStatePrompt = function() { var state = ''; - var isMaster = db.runCommand({isMaster:1, forShell:1}); - if ( isMaster.ok ) { + var isMaster = db.runCommand({isMaster: 1, forShell: 1}); + if (isMaster.ok) { var role = ""; if (isMaster.msg == "isdbgrid") { @@ -384,27 +403,31 @@ isMasterStatePrompt = function() { if (typeof(_useWriteCommandsDefault) == 'undefined') { // This is for cases when the v8 engine is used other than the mongo shell, like map reduce. - _useWriteCommandsDefault = function() { return false; }; + _useWriteCommandsDefault = function() { + return false; + }; } if (typeof(_writeMode) == 'undefined') { // This is for cases when the v8 engine is used other than the mongo shell, like map reduce. - _writeMode = function() { return "commands"; }; + _writeMode = function() { + return "commands"; + }; } if (typeof(_readMode) == 'undefined') { // This is for cases when the v8 engine is used other than the mongo shell, like map reduce. - _readMode = function() { return "legacy"; }; + _readMode = function() { + return "legacy"; + }; } -shellPrintHelper = function (x) { - if (typeof (x) == "undefined") { +shellPrintHelper = function(x) { + if (typeof(x) == "undefined") { // Make sure that we have a db var before we use it // TODO: This implicit calling of GLE can cause subtle, hard to track issues - remove? - if (__callLastError && typeof( db ) != "undefined" && - db.getMongo && - db.getMongo().writeMode() == "legacy") { - + if (__callLastError && typeof(db) != "undefined" && db.getMongo && + db.getMongo().writeMode() == "legacy") { __callLastError = false; // explicit w:1 so that replset getLastErrorDefaults aren't used here which would be bad var err = db.getLastError(1); @@ -440,136 +463,166 @@ shellPrintHelper = function (x) { print(tojson(x)); }; -shellAutocomplete = function ( /*prefix*/ ) { // outer scope function called on init. Actual function at end - - var universalMethods = "constructor prototype toString valueOf toLocaleString hasOwnProperty propertyIsEnumerable".split( ' ' ); - - var builtinMethods = {}; // uses constructor objects as keys - builtinMethods[Array] = "length concat join pop push reverse shift slice sort splice unshift indexOf lastIndexOf every filter forEach map some isArray reduce reduceRight".split( ' ' ); - builtinMethods[Boolean] = "".split( ' ' ); // nothing more than universal methods - builtinMethods[Date] = "getDate getDay getFullYear getHours getMilliseconds getMinutes getMonth getSeconds getTime getTimezoneOffset getUTCDate getUTCDay getUTCFullYear getUTCHours getUTCMilliseconds getUTCMinutes getUTCMonth getUTCSeconds getYear parse setDate setFullYear setHours setMilliseconds setMinutes setMonth setSeconds setTime setUTCDate setUTCFullYear setUTCHours setUTCMilliseconds setUTCMinutes setUTCMonth setUTCSeconds setYear toDateString toGMTString toISOString toLocaleDateString toLocaleTimeString toTimeString toUTCString UTC now".split( ' ' ); - if (typeof JSON != "undefined") { // JSON is new in V8 +shellAutocomplete = function( + /*prefix*/) { // outer scope function called on init. Actual function at end + + var universalMethods = + "constructor prototype toString valueOf toLocaleString hasOwnProperty propertyIsEnumerable" + .split(' '); + + var builtinMethods = {}; // uses constructor objects as keys + builtinMethods[Array] = + "length concat join pop push reverse shift slice sort splice unshift indexOf lastIndexOf every filter forEach map some isArray reduce reduceRight" + .split(' '); + builtinMethods[Boolean] = "".split(' '); // nothing more than universal methods + builtinMethods[Date] = + "getDate getDay getFullYear getHours getMilliseconds getMinutes getMonth getSeconds getTime getTimezoneOffset getUTCDate getUTCDay getUTCFullYear getUTCHours getUTCMilliseconds getUTCMinutes getUTCMonth getUTCSeconds getYear parse setDate setFullYear setHours setMilliseconds setMinutes setMonth setSeconds setTime setUTCDate setUTCFullYear setUTCHours setUTCMilliseconds setUTCMinutes setUTCMonth setUTCSeconds setYear toDateString toGMTString toISOString toLocaleDateString toLocaleTimeString toTimeString toUTCString UTC now" + .split(' '); + if (typeof JSON != "undefined") { // JSON is new in V8 builtinMethods["[object JSON]"] = "parse stringify".split(' '); } - builtinMethods[Math] = "E LN2 LN10 LOG2E LOG10E PI SQRT1_2 SQRT2 abs acos asin atan atan2 ceil cos exp floor log max min pow random round sin sqrt tan".split(' '); - builtinMethods[Number] = "MAX_VALUE MIN_VALUE NEGATIVE_INFINITY POSITIVE_INFINITY toExponential toFixed toPrecision".split( ' ' ); - builtinMethods[RegExp] = "global ignoreCase lastIndex multiline source compile exec test".split( ' ' ); - builtinMethods[String] = "length charAt charCodeAt concat fromCharCode indexOf lastIndexOf match replace search slice split substr substring toLowerCase toUpperCase trim trimLeft trimRight".split(' '); - builtinMethods[Function] = "call apply bind".split( ' ' ); - builtinMethods[Object] = "bsonsize create defineProperty defineProperties getPrototypeOf keys seal freeze preventExtensions isSealed isFrozen isExtensible getOwnPropertyDescriptor getOwnPropertyNames".split(' '); + builtinMethods[Math] = + "E LN2 LN10 LOG2E LOG10E PI SQRT1_2 SQRT2 abs acos asin atan atan2 ceil cos exp floor log max min pow random round sin sqrt tan" + .split(' '); + builtinMethods[Number] = + "MAX_VALUE MIN_VALUE NEGATIVE_INFINITY POSITIVE_INFINITY toExponential toFixed toPrecision" + .split(' '); + builtinMethods[RegExp] = + "global ignoreCase lastIndex multiline source compile exec test".split(' '); + builtinMethods[String] = + "length charAt charCodeAt concat fromCharCode indexOf lastIndexOf match replace search slice split substr substring toLowerCase toUpperCase trim trimLeft trimRight" + .split(' '); + builtinMethods[Function] = "call apply bind".split(' '); + builtinMethods[Object] = + "bsonsize create defineProperty defineProperties getPrototypeOf keys seal freeze preventExtensions isSealed isFrozen isExtensible getOwnPropertyDescriptor getOwnPropertyNames" + .split(' '); builtinMethods[Mongo] = "find update insert remove".split(' '); builtinMethods[BinData] = "hex base64 length subtype".split(' '); - var extraGlobals = "Infinity NaN undefined null true false decodeURI decodeURIComponent encodeURI encodeURIComponent escape eval isFinite isNaN parseFloat parseInt unescape Array Boolean Date Math Number RegExp String print load gc MinKey MaxKey Mongo NumberInt NumberLong ObjectId DBPointer UUID BinData HexData MD5 Map Timestamp JSON".split( ' ' ); + var extraGlobals = + "Infinity NaN undefined null true false decodeURI decodeURIComponent encodeURI encodeURIComponent escape eval isFinite isNaN parseFloat parseInt unescape Array Boolean Date Math Number RegExp String print load gc MinKey MaxKey Mongo NumberInt NumberLong ObjectId DBPointer UUID BinData HexData MD5 Map Timestamp JSON" + .split(' '); if (typeof NumberDecimal !== 'undefined') { extraGlobals[extraGlobals.length] = "NumberDecimal"; } - var isPrivate = function( name ) { - if ( shellAutocomplete.showPrivate ) return false; - if ( name == '_id' ) return false; - if ( name[0] == '_' ) return true; - if ( name[name.length - 1] == '_' ) return true; // some native functions have an extra name_ method + var isPrivate = function(name) { + if (shellAutocomplete.showPrivate) + return false; + if (name == '_id') + return false; + if (name[0] == '_') + return true; + if (name[name.length - 1] == '_') + return true; // some native functions have an extra name_ method return false; }; - var customComplete = function( obj ) { + var customComplete = function(obj) { try { - if ( obj.__proto__.constructor.autocomplete ) { - var ret = obj.constructor.autocomplete( obj ); - if ( ret.constructor != Array ) { - print( "\nautocompleters must return real Arrays" ); + if (obj.__proto__.constructor.autocomplete) { + var ret = obj.constructor.autocomplete(obj); + if (ret.constructor != Array) { + print("\nautocompleters must return real Arrays"); return []; } return ret; } else { return []; } - } catch ( e ) { + } catch (e) { // print( e ); // uncomment if debugging custom completers return []; } }; - var worker = function( prefix ) { - var global = ( function() { return this; } ).call(); // trick to get global object + var worker = function(prefix) { + var global = (function() { + return this; + }).call(); // trick to get global object var curObj = global; - var parts = prefix.split( '.' ); - for ( var p = 0; p < parts.length - 1; p++ ) { // doesn't include last part + var parts = prefix.split('.'); + for (var p = 0; p < parts.length - 1; p++) { // doesn't include last part curObj = curObj[parts[p]]; - if ( curObj == null ) + if (curObj == null) return []; } var lastPrefix = parts[parts.length - 1] || ''; var lastPrefixLowercase = lastPrefix.toLowerCase(); - var beginning = parts.slice( 0, parts.length - 1 ).join( '.' ); - if ( beginning.length ) + var beginning = parts.slice(0, parts.length - 1).join('.'); + if (beginning.length) beginning += '.'; - var possibilities = new Array().concat( - universalMethods, - Object.keySet( curObj ), - Object.keySet( curObj.__proto__ ), - builtinMethods[curObj] || [], // curObj is a builtin constructor - builtinMethods[curObj.__proto__.constructor] || [], // curObj is made from a builtin constructor - curObj == global ? extraGlobals : [], - customComplete( curObj ) - ); - - var noDuplicates = {}; // see http://dreaminginjavascript.wordpress.com/2008/08/22/eliminating-duplicates/ - for ( var i = 0; i < possibilities.length; i++ ) { + var possibilities = + new Array().concat(universalMethods, + Object.keySet(curObj), + Object.keySet(curObj.__proto__), + builtinMethods[curObj] || [], // curObj is a builtin constructor + builtinMethods[curObj.__proto__.constructor] || + [], // curObj is made from a builtin constructor + curObj == global ? extraGlobals : [], + customComplete(curObj)); + + var noDuplicates = + {}; // see http://dreaminginjavascript.wordpress.com/2008/08/22/eliminating-duplicates/ + for (var i = 0; i < possibilities.length; i++) { var p = possibilities[i]; - if ( typeof ( curObj[p] ) == "undefined" && curObj != global ) continue; // extraGlobals aren't in the global object - if ( p.length == 0 || p.length < lastPrefix.length ) continue; - if ( lastPrefix[0] != '_' && isPrivate( p ) ) continue; - if ( p.match( /^[0-9]+$/ ) ) continue; // don't array number indexes - if ( p.substr( 0, lastPrefix.length ).toLowerCase() != lastPrefixLowercase ) continue; + if (typeof(curObj[p]) == "undefined" && curObj != global) + continue; // extraGlobals aren't in the global object + if (p.length == 0 || p.length < lastPrefix.length) + continue; + if (lastPrefix[0] != '_' && isPrivate(p)) + continue; + if (p.match(/^[0-9]+$/)) + continue; // don't array number indexes + if (p.substr(0, lastPrefix.length).toLowerCase() != lastPrefixLowercase) + continue; var completion = beginning + p; - if ( curObj[p] && curObj[p].constructor == Function && p != 'constructor' ) + if (curObj[p] && curObj[p].constructor == Function && p != 'constructor') completion += '('; noDuplicates[completion] = 0; } var ret = []; - for ( var i in noDuplicates ) - ret.push( i ); + for (var i in noDuplicates) + ret.push(i); return ret; }; // this is the actual function that gets assigned to shellAutocomplete - return function( prefix ) { + return function(prefix) { try { - __autocomplete__ = worker( prefix ).sort(); - } catch ( e ) { - print( "exception during autocomplete: " + tojson( e.message ) ); + __autocomplete__ = worker(prefix).sort(); + } catch (e) { + print("exception during autocomplete: " + tojson(e.message)); __autocomplete__ = []; } }; -} (); +}(); -shellAutocomplete.showPrivate = false; // toggle to show (useful when working on internals) +shellAutocomplete.showPrivate = false; // toggle to show (useful when working on internals) -shellHelper = function( command , rest , shouldPrint ){ +shellHelper = function(command, rest, shouldPrint) { command = command.trim(); - var args = rest.trim().replace(/\s*;$/,"").split( "\s+" ); - - if ( ! shellHelper[command] ) - throw Error( "no command [" + command + "]" ); - - var res = shellHelper[command].apply( null , args ); - if ( shouldPrint ){ - shellPrintHelper( res ); + var args = rest.trim().replace(/\s*;$/, "").split("\s+"); + + if (!shellHelper[command]) + throw Error("no command [" + command + "]"); + + var res = shellHelper[command].apply(null, args); + if (shouldPrint) { + shellPrintHelper(res); } return res; }; -shellHelper.use = function (dbname) { +shellHelper.use = function(dbname) { var s = "" + dbname; if (s == "") { print("bad use parameter"); @@ -579,7 +632,7 @@ shellHelper.use = function (dbname) { print("switched to db " + db.getName()); }; -shellHelper.set = function (str) { +shellHelper.set = function(str) { if (str == "") { print("bad use parameter"); return; @@ -587,11 +640,14 @@ shellHelper.set = function (str) { tokens = str.split(" "); param = tokens[0]; value = tokens[1]; - - if ( value == undefined ) value = true; + + if (value == undefined) + value = true; // value comes in as a string.. - if ( value == "true" ) value = true; - if ( value == "false" ) value = false; + if (value == "true") + value = true; + if (value == "false") + value = false; if (param == "verbose") { _verboseShell = value; @@ -599,18 +655,18 @@ shellHelper.set = function (str) { print("set " + param + " to " + value); }; -shellHelper.it = function(){ - if ( typeof( ___it___ ) == "undefined" || ___it___ == null ){ - print( "no cursor" ); +shellHelper.it = function() { + if (typeof(___it___) == "undefined" || ___it___ == null) { + print("no cursor"); return; } - shellPrintHelper( ___it___ ); + shellPrintHelper(___it___); }; -shellHelper.show = function (what) { +shellHelper.show = function(what) { assert(typeof what == "string"); - var args = what.split( /\s+/ ); + var args = what.split(/\s+/); what = args[0]; args = args.splice(1); @@ -619,35 +675,34 @@ shellHelper.show = function (what) { 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 { + } else { print(); - db.system.profile.find({ millis: { $gt: 0} }).sort({ $natural: -1 }).limit(5).forEach( - function (x) { - print("" + x.op + "\t" + x.ns + " " + x.millis + "ms " + String(x.ts).substring(0, 24)); + db.system.profile.find({millis: {$gt: 0}}) + .sort({$natural: -1}) + .limit(5) + .forEach(function(x) { + print("" + x.op + "\t" + x.ns + " " + x.millis + "ms " + + String(x.ts).substring(0, 24)); var l = ""; - for ( var z in x ){ - if ( z == "op" || z == "ns" || z == "millis" || z == "ts" ) + for (var z in x) { + if (z == "op" || z == "ns" || z == "millis" || z == "ts") continue; - + var val = x[z]; var mytype = typeof(val); - - if ( mytype == "string" || - mytype == "number" ) + + if (mytype == "string" || mytype == "number") l += z + ":" + val + " "; - else if ( mytype == "object" ) - l += z + ":" + tojson(val ) + " "; - else if ( mytype == "boolean" ) + else if (mytype == "object") + l += z + ":" + tojson(val) + " "; + else if (mytype == "boolean") l += z + " "; else l += z + ":" + val + " "; - } - print( l ); - print("\n"); - } - ); + print(l); + print("\n"); + }); } return ""; } @@ -663,7 +718,9 @@ shellHelper.show = function (what) { } if (what == "collections" || what == "tables") { - db.getCollectionNames().forEach(function (x) { print(x); }); + db.getCollectionNames().forEach(function(x) { + print(x); + }); return ""; } @@ -673,27 +730,29 @@ shellHelper.show = function (what) { var maxNameLength = 0; var maxGbDigits = 0; - dbs.databases.forEach(function (x){ + dbs.databases.forEach(function(x) { var sizeStr = (x.sizeOnDisk / 1024 / 1024 / 1024).toFixed(3); var nameLength = x.name.length; var gbDigits = sizeStr.indexOf("."); - if( nameLength > maxNameLength) maxNameLength = nameLength; - if( gbDigits > maxGbDigits ) maxGbDigits = gbDigits; + if (nameLength > maxNameLength) + maxNameLength = nameLength; + if (gbDigits > maxGbDigits) + maxGbDigits = gbDigits; dbinfo.push({ - name: x.name, - size: x.sizeOnDisk, - size_str: sizeStr, + name: x.name, + size: x.sizeOnDisk, + size_str: sizeStr, name_size: nameLength, gb_digits: gbDigits }); }); dbinfo.sort(compareOn('name')); - dbinfo.forEach(function (db) { + dbinfo.forEach(function(db) { var namePadding = maxNameLength - db.name_size; - var sizePadding = maxGbDigits - db.gb_digits; + var sizePadding = maxGbDigits - db.gb_digits; var padding = Array(namePadding + sizePadding + 3).join(" "); if (db.size > 1) { print(db.name + padding + db.size_str + "GB"); @@ -704,36 +763,36 @@ shellHelper.show = function (what) { return ""; } - - if (what == "log" ) { + + if (what == "log") { var n = "global"; - if ( args.length > 0 ) + if (args.length > 0) n = args[0]; - - var res = db.adminCommand( { getLog : n } ); - if ( ! res.ok ) { + + var res = db.adminCommand({getLog: n}); + if (!res.ok) { print("Error while trying to show " + n + " log: " + res.errmsg); return ""; } - for ( var i=0; i<res.log.length; i++){ - print( res.log[i] ); + for (var i = 0; i < res.log.length; i++) { + print(res.log[i]); } return ""; } - if (what == "logs" ) { - var res = db.adminCommand( { getLog : "*" } ); - if ( ! res.ok ) { + if (what == "logs") { + var res = db.adminCommand({getLog: "*"}); + if (!res.ok) { print("Error while trying to show logs: " + res.errmsg); return ""; } - for ( var i=0; i<res.names.length; i++){ - print( res.names[i] ); + for (var i = 0; i < res.names.length; i++) { + print(res.names[i]); } return ""; } - if (what == "startupWarnings" ) { + if (what == "startupWarnings") { var dbDeclared, ex; try { // !!db essentially casts db to a boolean @@ -743,21 +802,20 @@ shellHelper.show = function (what) { dbDeclared = false; } if (dbDeclared) { - var res = db.adminCommand( { getLog : "startupWarnings" } ); - if ( res.ok ) { + var res = db.adminCommand({getLog: "startupWarnings"}); + if (res.ok) { if (res.log.length == 0) { return ""; } - print( "Server has startup warnings: " ); - for ( var i=0; i<res.log.length; i++){ - print( res.log[i] ); + print("Server has startup warnings: "); + for (var i = 0; i < res.log.length; i++) { + print(res.log[i]); } return ""; - } else if (res.errmsg == "no such cmd: getLog" ) { + } else if (res.errmsg == "no such cmd: getLog") { // Don't print if the command is not available return ""; - } else if (res.code == 13 /*unauthorized*/ || - res.errmsg == "unauthorized" || + } else if (res.code == 13 /*unauthorized*/ || res.errmsg == "unauthorized" || res.errmsg == "need to login") { // Don't print if startupWarnings command failed due to auth return ""; @@ -771,22 +829,22 @@ shellHelper.show = function (what) { } } - throw Error( "don't know how to show [" + what + "]" ); + throw Error("don't know how to show [" + what + "]"); }; -Math.sigFig = function( x , N ){ - if ( ! N ){ +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; + var p = Math.pow(10, N - Math.ceil(Math.log(Math.abs(x)) / Math.log(10))); + return Math.round(x * p) / p; }; var Random = (function() { var initialized = false; - var errorMsg = "The random number generator hasn't been seeded yet; " + - "call Random.setRandomSeed()"; + var errorMsg = + "The random number generator hasn't been seeded yet; " + "call Random.setRandomSeed()"; // Set the random generator seed. function srand(s) { @@ -840,7 +898,7 @@ var Random = (function() { throw new Error(errorMsg); } // See http://en.wikipedia.org/wiki/Marsaglia_polar_method - while ( true ) { + while (true) { var x = (2 * rand()) - 1; var y = (2 * rand()) - 1; var s = (x * x) + (y * y); @@ -864,71 +922,70 @@ var Random = (function() { })(); Geo = {}; -Geo.distance = function( a , b ){ +Geo.distance = function(a, b) { var ax = null; var ay = null; var bx = null; var by = null; - - for ( var key in a ){ - if ( ax == null ) + + for (var key in a) { + if (ax == null) ax = a[key]; - else if ( ay == null ) + else if (ay == null) ay = a[key]; } - - for ( var key in b ){ - if ( bx == null ) + + for (var key in b) { + if (bx == null) bx = b[key]; - else if ( by == null ) + else if (by == null) by = b[key]; } - return Math.sqrt( Math.pow( by - ay , 2 ) + - Math.pow( bx - ax , 2 ) ); + return Math.sqrt(Math.pow(by - ay, 2) + Math.pow(bx - ax, 2)); }; -Geo.sphereDistance = function( a , b ){ +Geo.sphereDistance = function(a, b) { var ax = null; var ay = null; var bx = null; var by = null; - + // TODO swap order of x and y when done on server - for ( var key in a ){ - if ( ax == null ) - ax = a[key] * (Math.PI/180); - else if ( ay == null ) - ay = a[key] * (Math.PI/180); + for (var key in a) { + if (ax == null) + ax = a[key] * (Math.PI / 180); + else if (ay == null) + ay = a[key] * (Math.PI / 180); } - - for ( var key in b ){ - if ( bx == null ) - bx = b[key] * (Math.PI/180); - else if ( by == null ) - by = b[key] * (Math.PI/180); + + for (var key in b) { + if (bx == null) + bx = b[key] * (Math.PI / 180); + else if (by == null) + by = b[key] * (Math.PI / 180); } - var sin_x1=Math.sin(ax), cos_x1=Math.cos(ax); - var sin_y1=Math.sin(ay), cos_y1=Math.cos(ay); - var sin_x2=Math.sin(bx), cos_x2=Math.cos(bx); - var sin_y2=Math.sin(by), cos_y2=Math.cos(by); + var sin_x1 = Math.sin(ax), cos_x1 = Math.cos(ax); + var sin_y1 = Math.sin(ay), cos_y1 = Math.cos(ay); + var sin_x2 = Math.sin(bx), cos_x2 = Math.cos(bx); + var sin_y2 = Math.sin(by), cos_y2 = Math.cos(by); - var cross_prod = - (cos_y1*cos_x1 * cos_y2*cos_x2) + - (cos_y1*sin_x1 * cos_y2*sin_x2) + - (sin_y1 * sin_y2); + var cross_prod = (cos_y1 * cos_x1 * cos_y2 * cos_x2) + (cos_y1 * sin_x1 * cos_y2 * sin_x2) + + (sin_y1 * sin_y2); - if (cross_prod >= 1 || cross_prod <= -1){ + if (cross_prod >= 1 || cross_prod <= -1) { // fun with floats - assert( Math.abs(cross_prod)-1 < 1e-6 ); + assert(Math.abs(cross_prod) - 1 < 1e-6); return cross_prod > 0 ? 0 : Math.PI; } return Math.acos(cross_prod); }; -rs = function () { return "try rs.help()"; }; +rs = function() { + return "try rs.help()"; +}; /** * This method is intended to aid in the writing of tests. It takes a host's address, desired state, @@ -941,116 +998,144 @@ _awaitRSHostViaRSMonitor = function(hostAddr, desiredState, rsName, timeout) { timeout = timeout || 60 * 1000; if (desiredState == undefined) { - desiredState = {ok: true}; + desiredState = { + ok: true + }; } print("Awaiting " + hostAddr + " to be " + tojson(desiredState) + " in " + " rs " + rsName); var tests = 0; - assert.soon(function() { - var stats = _replMonitorStats(rsName); - if (tests++ % 10 == 0) { - printjson(stats); - } - - for (var i=0; i<stats.length; i++) { - var node = stats[i]; - printjson(node); - if (node["addr"] !== hostAddr) - continue; + assert.soon( + function() { + var stats = _replMonitorStats(rsName); + if (tests++ % 10 == 0) { + printjson(stats); + } - // Check that *all* hostAddr properties match desiredState properties - var stateReached = true; - for(var prop in desiredState) { - if (isObject(desiredState[prop])) { - if (!friendlyEqual(sortDoc(desiredState[prop]), sortDoc(node[prop]))) { + for (var i = 0; i < stats.length; i++) { + var node = stats[i]; + printjson(node); + if (node["addr"] !== hostAddr) + continue; + + // Check that *all* hostAddr properties match desiredState properties + var stateReached = true; + for (var prop in desiredState) { + if (isObject(desiredState[prop])) { + if (!friendlyEqual(sortDoc(desiredState[prop]), sortDoc(node[prop]))) { + stateReached = false; + break; + } + } else if (node[prop] !== desiredState[prop]) { stateReached = false; break; } } - else if (node[prop] !== desiredState[prop]) { - stateReached = false; - break; + if (stateReached) { + printjson(stats); + return true; } } - if (stateReached) { - printjson(stats); - return true; - } - } - return false; - }, "timed out waiting for replica set member: " + hostAddr + " to reach state: " + + return false; + }, + "timed out waiting for replica set member: " + hostAddr + " to reach state: " + tojson(desiredState), - timeout); + timeout); }; -rs.help = function () { - print("\trs.status() { replSetGetStatus : 1 } checks repl set status"); - print("\trs.initiate() { replSetInitiate : null } initiates set with default settings"); - print("\trs.initiate(cfg) { replSetInitiate : cfg } initiates set with configuration cfg"); - print("\trs.conf() get the current configuration object from local.system.replset"); - print("\trs.reconfig(cfg) updates the configuration of a running replica set with cfg (disconnects)"); - print("\trs.add(hostportstr) add a new member to the set with default attributes (disconnects)"); - print("\trs.add(membercfgobj) add a new member to the set with extra attributes (disconnects)"); - print("\trs.addArb(hostportstr) add a new member which is arbiterOnly:true (disconnects)"); +rs.help = function() { + print( + "\trs.status() { replSetGetStatus : 1 } checks repl set status"); + print( + "\trs.initiate() { replSetInitiate : null } initiates set with default settings"); + print( + "\trs.initiate(cfg) { replSetInitiate : cfg } initiates set with configuration cfg"); + print( + "\trs.conf() get the current configuration object from local.system.replset"); + print( + "\trs.reconfig(cfg) updates the configuration of a running replica set with cfg (disconnects)"); + print( + "\trs.add(hostportstr) add a new member to the set with default attributes (disconnects)"); + print( + "\trs.add(membercfgobj) add a new member to the set with extra attributes (disconnects)"); + print( + "\trs.addArb(hostportstr) add a new member which is arbiterOnly:true (disconnects)"); print("\trs.stepDown([stepdownSecs, catchUpSecs]) step down as primary (disconnects)"); - print("\trs.syncFrom(hostportstr) make a secondary sync from the given member"); - print("\trs.freeze(secs) make a node ineligible to become primary for the time specified"); - print("\trs.remove(hostportstr) remove a host from the replica set (disconnects)"); + print( + "\trs.syncFrom(hostportstr) make a secondary sync from the given member"); + print( + "\trs.freeze(secs) make a node ineligible to become primary for the time specified"); + print( + "\trs.remove(hostportstr) remove a host from the replica set (disconnects)"); print("\trs.slaveOk() allow queries on secondary nodes"); print(); print("\trs.printReplicationInfo() check oplog size and time range"); - print("\trs.printSlaveReplicationInfo() check replica set members and replication lag"); + print( + "\trs.printSlaveReplicationInfo() check replica set members and replication lag"); print("\tdb.isMaster() check who is primary"); print(); print("\treconfiguration helpers disconnect from the database so the shell will display"); print("\tan error, even if the command succeeds."); }; -rs.slaveOk = function (value) { return db.getMongo().setSlaveOk(value); }; -rs.status = function () { return db._adminCommand("replSetGetStatus"); }; -rs.isMaster = function () { return db.isMaster(); }; -rs.initiate = function (c) { return db._adminCommand({ replSetInitiate: c }); }; -rs.printSlaveReplicationInfo = function () { return db.printSlaveReplicationInfo(); }; -rs.printReplicationInfo = function () { return db.printReplicationInfo(); }; -rs._runCmd = function (c) { +rs.slaveOk = function(value) { + return db.getMongo().setSlaveOk(value); +}; +rs.status = function() { + return db._adminCommand("replSetGetStatus"); +}; +rs.isMaster = function() { + return db.isMaster(); +}; +rs.initiate = function(c) { + return db._adminCommand({replSetInitiate: c}); +}; +rs.printSlaveReplicationInfo = function() { + return db.printSlaveReplicationInfo(); +}; +rs.printReplicationInfo = function() { + return db.printReplicationInfo(); +}; +rs._runCmd = function(c) { // after the command, catch the disconnect and reconnect if necessary var res = null; try { res = db.adminCommand(c); - } - catch (e) { + } catch (e) { if (("" + e).indexOf("error doing query") >= 0) { // closed connection. reconnect. db.getLastErrorObj(); var o = db.getLastErrorObj(); if (o.ok) { print("reconnected to server after rs command (which is normal)"); - } - else { + } else { printjson(o); } - } - else { + } else { print("shell got exception during repl set operation: " + e); - print("in some circumstances, the primary steps down and closes connections on a reconfig"); + print( + "in some circumstances, the primary steps down and closes connections on a reconfig"); } return ""; } return res; }; -rs.reconfig = function (cfg, options) { +rs.reconfig = function(cfg, options) { cfg.version = rs.conf().version + 1; - cmd = { replSetReconfig: cfg }; + cmd = { + replSetReconfig: cfg + }; for (var i in options) { cmd[i] = options[i]; } return this._runCmd(cmd); }; -rs.add = function (hostport, arb) { +rs.add = function(hostport, arb) { var cfg = hostport; var local = db.getSisterDB("local"); - assert(local.system.replset.count() <= 1, "error: local.system.replset has unexpected contents"); + assert(local.system.replset.count() <= 1, + "error: local.system.replset has unexpected contents"); var c = local.system.replset.findOne(); assert(c, "no config object retrievable from local.system.replset"); @@ -1058,47 +1143,59 @@ rs.add = function (hostport, arb) { var max = 0; for (var i in c.members) - if (c.members[i]._id > max) max = c.members[i]._id; + if (c.members[i]._id > max) + max = c.members[i]._id; if (isString(hostport)) { - cfg = { _id: max + 1, host: hostport }; + cfg = { + _id: max + 1, + host: hostport + }; if (arb) cfg.arbiterOnly = true; - } - else if (arb == true) { + } else if (arb == true) { throw Error("Expected first parameter to be a host-and-port string of arbiter, but got " + tojson(hostport)); } - if (cfg._id == null){ - cfg._id = max+1; + if (cfg._id == null) { + cfg._id = max + 1; } c.members.push(cfg); - return this._runCmd({ replSetReconfig: c }); + return this._runCmd({replSetReconfig: c}); +}; +rs.syncFrom = function(host) { + return db._adminCommand({replSetSyncFrom: host}); }; -rs.syncFrom = function (host) { return db._adminCommand({replSetSyncFrom : host}); }; -rs.stepDown = function (stepdownSecs, catchUpSecs) { - var cmdObj = {replSetStepDown: stepdownSecs === undefined ? 60 : stepdownSecs}; +rs.stepDown = function(stepdownSecs, catchUpSecs) { + var cmdObj = { + replSetStepDown: stepdownSecs === undefined ? 60 : stepdownSecs + }; if (catchUpSecs !== undefined) { cmdObj['secondaryCatchUpPeriodSecs'] = catchUpSecs; } return db._adminCommand(cmdObj); }; -rs.freeze = function (secs) { return db._adminCommand({replSetFreeze:secs}); }; -rs.addArb = function (hn) { return this.add(hn, true); }; +rs.freeze = function(secs) { + return db._adminCommand({replSetFreeze: secs}); +}; +rs.addArb = function(hn) { + return this.add(hn, true); +}; -rs.conf = function () { - var resp = db._adminCommand({replSetGetConfig:1}); +rs.conf = function() { + var resp = db._adminCommand({replSetGetConfig: 1}); if (resp.ok && !(resp.errmsg) && resp.config) return resp.config; else if (resp.errmsg && resp.errmsg.startsWith("no such cmd")) - return db.getSisterDB("local").system.replset.findOne(); + return db.getSisterDB("local").system.replset.findOne(); throw new Error("Could not retrieve replica set config: " + tojson(resp)); }; rs.config = rs.conf; -rs.remove = function (hn) { +rs.remove = function(hn) { var local = db.getSisterDB("local"); - assert(local.system.replset.count() <= 1, "error: local.system.replset has unexpected contents"); + assert(local.system.replset.count() <= 1, + "error: local.system.replset has unexpected contents"); var c = local.system.replset.findOne(); assert(c, "no config object retrievable from local.system.replset"); c.version++; @@ -1106,31 +1203,34 @@ rs.remove = function (hn) { for (var i in c.members) { if (c.members[i].host == hn) { c.members.splice(i, 1); - return db._adminCommand({ replSetReconfig : c}); + return db._adminCommand({replSetReconfig: c}); } } - return "error: couldn't find "+hn+" in "+tojson(c.members); + return "error: couldn't find " + hn + " in " + tojson(c.members); }; rs.debug = {}; rs.debug.nullLastOpWritten = function(primary, secondary) { - var p = connect(primary+"/local"); - var s = connect(secondary+"/local"); + var p = connect(primary + "/local"); + var s = connect(secondary + "/local"); s.getMongo().setSlaveOk(); - var secondToLast = s.oplog.rs.find().sort({$natural : -1}).limit(1).next(); - var last = p.runCommand({findAndModify : "oplog.rs", - query : {ts : {$gt : secondToLast.ts}}, - sort : {$natural : 1}, - update : {$set : {op : "n"}}}); + var secondToLast = s.oplog.rs.find().sort({$natural: -1}).limit(1).next(); + var last = p.runCommand({ + findAndModify: "oplog.rs", + query: {ts: {$gt: secondToLast.ts}}, + sort: {$natural: 1}, + update: {$set: {op: "n"}} + }); if (!last.value.o || !last.value.o._id) { print("couldn't find an _id?"); - } - else { - last.value.o = {_id : last.value.o._id}; + } else { + last.value.o = { + _id: last.value.o._id + }; } print("nulling out this op:"); @@ -1140,15 +1240,14 @@ rs.debug.nullLastOpWritten = function(primary, secondary) { rs.debug.getLastOpWritten = function(server) { var s = db.getSisterDB("local"); if (server) { - s = connect(server+"/local"); + s = connect(server + "/local"); } s.getMongo().setSlaveOk(); - return s.oplog.rs.find().sort({$natural : -1}).limit(1).next(); + return s.oplog.rs.find().sort({$natural: -1}).limit(1).next(); }; - -help = shellHelper.help = function (x) { +help = shellHelper.help = function(x) { if (x == "mr") { print("\nSee also http://dochub.mongodb.org/core/mapreduce"); print("\nfunction mapf() {"); @@ -1170,27 +1269,29 @@ help = shellHelper.help = function (x) { print(" [, verbose : true]}\n"); return; } else if (x == "connect") { - print("\nNormally one specifies the server on the mongo shell command line. Run mongo --help to see those options."); + print( + "\nNormally one specifies the server on the mongo shell command line. Run mongo --help to see those options."); print("Additional connections may be opened:\n"); print(" var x = new Mongo('host[:port]');"); print(" var mydb = x.getDB('mydb');"); print(" or"); print(" var mydb = connect('host[:port]/mydb');"); - print("\nNote: the REPL prompt only auto-reports getLastError() for the shell command line connection.\n"); + print( + "\nNote: the REPL prompt only auto-reports getLastError() for the shell command line connection.\n"); return; - } - else if (x == "keys") { + } else if (x == "keys") { print("Tab completion and command history is available at the command prompt.\n"); print("Some emacs keystrokes are available too:"); print(" Ctrl-A start of line"); print(" Ctrl-E end of line"); print(" Ctrl-K del to end of line"); print("\nMulti-line commands"); - print("You can enter a multi line javascript expression. If parens, braces, etc. are not closed, you will see a new line "); - print("beginning with '...' characters. Type the rest of your expression. Press Ctrl-C to abort the data entry if you"); + print( + "You can enter a multi line javascript expression. If parens, braces, etc. are not closed, you will see a new line "); + print( + "beginning with '...' characters. Type the rest of your expression. Press Ctrl-C to abort the data entry if you"); print("get stuck.\n"); - } - else if (x == "misc") { + } else if (x == "misc") { print("\tb = new BinData(subtype,base64str) create a BSON BinData value"); print("\tb.subtype() the BinData subtype (0..255)"); print("\tb.length() length of the BinData data in bytes"); @@ -1198,22 +1299,26 @@ help = shellHelper.help = function (x) { print("\tb.base64() the data as a base 64 encoded string"); print("\tb.toString()"); print(); - print("\tb = HexData(subtype,hexstr) create a BSON BinData value from a hex string"); + print( + "\tb = HexData(subtype,hexstr) create a BSON BinData value from a hex string"); print("\tb = UUID(hexstr) create a BSON BinData value of UUID subtype"); print("\tb = MD5(hexstr) create a BSON BinData value of MD5 subtype"); - print("\t\"hexstr\" string, sequence of hex characters (no 0x prefix)"); + print( + "\t\"hexstr\" string, sequence of hex characters (no 0x prefix)"); print(); print("\to = new ObjectId() create a new ObjectId"); - print("\to.getTimestamp() return timestamp derived from first 32 bits of the OID"); + print( + "\to.getTimestamp() return timestamp derived from first 32 bits of the OID"); print("\to.isObjectId"); print("\to.toString()"); print("\to.equals(otherid)"); print(); - print("\td = ISODate() like Date() but behaves more intuitively when used"); - print("\td = ISODate('YYYY-MM-DD hh:mm:ss') without an explicit \"new \" prefix on construction"); + print( + "\td = ISODate() like Date() but behaves more intuitively when used"); + print( + "\td = ISODate('YYYY-MM-DD hh:mm:ss') without an explicit \"new \" prefix on construction"); return; - } - else if (x == "admin") { + } else if (x == "admin") { print("\tls([path]) list files"); print("\tpwd() returns current directory"); print("\tlistFiles([path]) returns file list"); @@ -1226,13 +1331,11 @@ help = shellHelper.help = function (x) { print("\tsleep(m) sleep m milliseconds"); print("\tgetMemInfo() diagnostic"); return; - } - else if (x == "test") { + } else if (x == "test") { print("\tMongoRunner.runMongod(args) DELETES DATA DIR and then starts mongod"); print("\t returns a connection to the new server"); return; - } - else if (x == "") { + } else if (x == "") { print("\t" + "db.help() help on db methods"); print("\t" + "db.mycoll.help() help on collection methods"); print("\t" + "sh.help() sharding helpers"); @@ -1246,16 +1349,22 @@ help = shellHelper.help = function (x) { 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" + + "show profile show most recent system.profile entries with time >= 1ms"); print("\t" + "show logs show the accessible logger names"); - print("\t" + "show log [name] prints out the last segment of log in memory, 'global' is default"); + print( + "\t" + + "show log [name] prints out the last segment of log in memory, 'global' is default"); print("\t" + "use <db_name> set current database"); 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"); - print("\t" + "DBQuery.shellBatchSize = x set default number of items to display on shell"); + print( + "\t" + + "it result of the last line evaluated; use to further iterate"); + print("\t" + + "DBQuery.shellBatchSize = x set default number of items to display on shell"); print("\t" + "exit quit the mongo shell"); - } - else + } else print("unknown help option"); }; diff --git a/src/mongo/shell/utils_auth.js b/src/mongo/shell/utils_auth.js index f8534f616b1..e096e2c8c67 100644 --- a/src/mongo/shell/utils_auth.js +++ b/src/mongo/shell/utils_auth.js @@ -10,7 +10,7 @@ var authutil; authutil.logout = function(conn, dbname) { var i; if (null == conn.length) { - conn = [ conn ]; + conn = [conn]; } for (i = 0; i < conn.length; ++i) { conn[i].getDB(dbname).logout(); @@ -26,40 +26,38 @@ var authutil; authutil.assertAuthenticate = function(conns, dbName, authParams) { var conn, i, ex, ex2; if (conns.length == null) - conns = [ conns ]; + conns = [conns]; try { for (i = 0; i < conns.length; ++i) { conn = conns[i]; assert(conn.getDB(dbName).auth(authParams), "Failed to authenticate " + conn + " to " + dbName + " using parameters " + - tojson(authParams)); + tojson(authParams)); } - } - catch (ex) { + } catch (ex) { try { authutil.logout(conns, dbName); - } - catch (ex2) { + } catch (ex2) { } throw ex; } }; - /** - * Authenticates all connections in "conns" using "authParams" on database "dbName". - * Raises in exception if any of the authentications succeed. - */ + /** + * Authenticates all connections in "conns" using "authParams" on database "dbName". + * Raises in exception if any of the authentications succeed. + */ authutil.assertAuthenticateFails = function(conns, dbName, authParams) { var conn, i; if (conns.length == null) - conns = [ conns ]; + conns = [conns]; for (i = 0; i < conns.length; ++i) { conn = conns[i]; assert(!conn.getDB(dbName).auth(authParams), "Unexpectedly authenticated " + conn + " to " + dbName + " using parameters " + - tojson(authParams)); + tojson(authParams)); } }; @@ -69,22 +67,22 @@ var authutil; */ authutil.asCluster = function(conn, keyfile, action) { var ex; - authutil.assertAuthenticate(conn, 'local', { - user: '__system', - mechanism: 'SCRAM-SHA-1', - pwd: cat(keyfile).replace(/[\011-\015\040]/g, '') - }); + authutil.assertAuthenticate(conn, + 'local', + { + user: '__system', + mechanism: 'SCRAM-SHA-1', + pwd: cat(keyfile).replace(/[\011-\015\040]/g, '') + }); try { return action(); - } - finally { + } finally { try { authutil.logout(conn, 'local'); - } - catch (ex) { + } catch (ex) { } } }; - }()); +}()); diff --git a/src/mongo/shell/utils_sh.js b/src/mongo/shell/utils_sh.js index eb5123443b8..8aa5e5d19ec 100644 --- a/src/mongo/shell/utils_sh.js +++ b/src/mongo/shell/utils_sh.js @@ -1,126 +1,142 @@ -sh = function() { return "try sh.help();"; }; +sh = function() { + return "try sh.help();"; +}; sh._checkMongos = function() { - var x = db.runCommand( "ismaster" ); - if ( x.msg != "isdbgrid" ) + var x = db.runCommand("ismaster"); + if (x.msg != "isdbgrid") throw Error("not connected to a mongos"); }; -sh._checkFullName = function( fullName ) { - assert( fullName , "need a full name" ); - assert( fullName.indexOf( "." ) > 0 , "name needs to be fully qualified <db>.<collection>'" ); +sh._checkFullName = function(fullName) { + assert(fullName, "need a full name"); + assert(fullName.indexOf(".") > 0, "name needs to be fully qualified <db>.<collection>'"); }; -sh._adminCommand = function( cmd , skipCheck ) { - if ( ! skipCheck ) sh._checkMongos(); - return db.getSisterDB( "admin" ).runCommand( cmd ); +sh._adminCommand = function(cmd, skipCheck) { + if (!skipCheck) + sh._checkMongos(); + return db.getSisterDB("admin").runCommand(cmd); }; sh._getConfigDB = function() { sh._checkMongos(); - return db.getSiblingDB( "config" ); + return db.getSiblingDB("config"); }; -sh._dataFormat = function( bytes ){ - if( bytes < 1024 ) return Math.floor( bytes ) + "B"; - if( bytes < 1024 * 1024 ) return Math.floor( bytes / 1024 ) + "KiB"; - if( bytes < 1024 * 1024 * 1024 ) return Math.floor( ( Math.floor( bytes / 1024 ) / 1024 ) * 100 ) / 100 + "MiB"; - return Math.floor( ( Math.floor( bytes / ( 1024 * 1024 ) ) / 1024 ) * 100 ) / 100 + "GiB"; +sh._dataFormat = function(bytes) { + if (bytes < 1024) + return Math.floor(bytes) + "B"; + if (bytes < 1024 * 1024) + return Math.floor(bytes / 1024) + "KiB"; + if (bytes < 1024 * 1024 * 1024) + return Math.floor((Math.floor(bytes / 1024) / 1024) * 100) / 100 + "MiB"; + return Math.floor((Math.floor(bytes / (1024 * 1024)) / 1024) * 100) / 100 + "GiB"; }; -sh._collRE = function( coll ){ - return RegExp( "^" + RegExp.escape(coll + "") + "-.*" ); +sh._collRE = function(coll) { + return RegExp("^" + RegExp.escape(coll + "") + "-.*"); }; -sh._pchunk = function( chunk ){ - return "[" + tojson( chunk.min ) + " -> " + tojson( chunk.max ) + "]"; +sh._pchunk = function(chunk) { + return "[" + tojson(chunk.min) + " -> " + tojson(chunk.max) + "]"; }; sh.help = function() { - print( "\tsh.addShard( host ) server:port OR setname/server:port" ); - print( "\tsh.enableSharding(dbname) enables sharding on the database dbname" ); - print( "\tsh.shardCollection(fullName,key,unique) shards the collection" ); + print("\tsh.addShard( host ) server:port OR setname/server:port"); + print("\tsh.enableSharding(dbname) enables sharding on the database dbname"); + print("\tsh.shardCollection(fullName,key,unique) shards the collection"); + + print( + "\tsh.splitFind(fullName,find) splits the chunk that find is in at the median"); + print( + "\tsh.splitAt(fullName,middle) splits the chunk that middle is in at middle"); + print( + "\tsh.moveChunk(fullName,find,to) move the chunk where 'find' is to 'to' (name of shard)"); - print( "\tsh.splitFind(fullName,find) splits the chunk that find is in at the median" ); - print( "\tsh.splitAt(fullName,middle) splits the chunk that middle is in at middle" ); - print( "\tsh.moveChunk(fullName,find,to) move the chunk where 'find' is to 'to' (name of shard)"); - - print( "\tsh.setBalancerState( <bool on or not> ) turns the balancer on or off true=on, false=off" ); - print( "\tsh.getBalancerState() return true if enabled" ); - print( "\tsh.isBalancerRunning() return true if the balancer has work in progress on any mongos" ); + print( + "\tsh.setBalancerState( <bool on or not> ) turns the balancer on or off true=on, false=off"); + print("\tsh.getBalancerState() return true if enabled"); + print( + "\tsh.isBalancerRunning() return true if the balancer has work in progress on any mongos"); - print( "\tsh.disableBalancing(coll) disable balancing on one collection" ); - print( "\tsh.enableBalancing(coll) re-enable balancing on one collection" ); + print("\tsh.disableBalancing(coll) disable balancing on one collection"); + print("\tsh.enableBalancing(coll) re-enable balancing on one collection"); - print( "\tsh.addShardTag(shard,tag) adds the tag to the shard" ); - print( "\tsh.removeShardTag(shard,tag) removes the tag from the shard" ); - print( "\tsh.addTagRange(fullName,min,max,tag) tags the specified range of the given collection" ); - print( "\tsh.removeTagRange(fullName,min,max,tag) removes the tagged range of the given collection" ); + print("\tsh.addShardTag(shard,tag) adds the tag to the shard"); + print("\tsh.removeShardTag(shard,tag) removes the tag from the shard"); + print( + "\tsh.addTagRange(fullName,min,max,tag) tags the specified range of the given collection"); + print( + "\tsh.removeTagRange(fullName,min,max,tag) removes the tagged range of the given collection"); - print( "\tsh.status() prints a general overview of the cluster" ); + print("\tsh.status() prints a general overview of the cluster"); }; -sh.status = function( verbose , configDB ) { +sh.status = function(verbose, configDB) { // TODO: move the actual command here - printShardingStatus( configDB , verbose ); + printShardingStatus(configDB, verbose); }; -sh.addShard = function( url ){ - return sh._adminCommand( { addShard : url } , true ); +sh.addShard = function(url) { + return sh._adminCommand({addShard: url}, true); }; -sh.enableSharding = function( dbname ) { - assert( dbname , "need a valid dbname" ); - return sh._adminCommand( { enableSharding : dbname } ); +sh.enableSharding = function(dbname) { + assert(dbname, "need a valid dbname"); + return sh._adminCommand({enableSharding: dbname}); }; -sh.shardCollection = function( fullName , key , unique ) { - sh._checkFullName( fullName ); - assert( key , "need a key" ); - assert( typeof( key ) == "object" , "key needs to be an object" ); - - var cmd = { shardCollection : fullName , key : key }; - if ( unique ) +sh.shardCollection = function(fullName, key, unique) { + sh._checkFullName(fullName); + assert(key, "need a key"); + assert(typeof(key) == "object", "key needs to be an object"); + + var cmd = { + shardCollection: fullName, + key: key + }; + if (unique) cmd.unique = true; - return sh._adminCommand( cmd ); + return sh._adminCommand(cmd); }; -sh.splitFind = function( fullName , find ) { - sh._checkFullName( fullName ); - return sh._adminCommand( { split : fullName , find : find } ); +sh.splitFind = function(fullName, find) { + sh._checkFullName(fullName); + return sh._adminCommand({split: fullName, find: find}); }; -sh.splitAt = function( fullName , middle ) { - sh._checkFullName( fullName ); - return sh._adminCommand( { split : fullName , middle : middle } ); +sh.splitAt = function(fullName, middle) { + sh._checkFullName(fullName); + return sh._adminCommand({split: fullName, middle: middle}); }; -sh.moveChunk = function( fullName , find , to ) { - sh._checkFullName( fullName ); - return sh._adminCommand( { moveChunk : fullName , find : find , to : to } ); +sh.moveChunk = function(fullName, find, to) { + sh._checkFullName(fullName); + return sh._adminCommand({moveChunk: fullName, find: find, to: to}); }; -sh.setBalancerState = function( onOrNot ) { - return sh._getConfigDB().settings.update({ _id: 'balancer' }, - { $set : { stopped: onOrNot ? false : true } }, - { upsert: true, - writeConcern: { w: 'majority', timeout: 30 } }); +sh.setBalancerState = function(onOrNot) { + return sh._getConfigDB().settings.update( + {_id: 'balancer'}, + {$set: {stopped: onOrNot ? false : true}}, + {upsert: true, writeConcern: {w: 'majority', timeout: 30}}); }; sh.getBalancerState = function(configDB) { if (configDB === undefined) configDB = sh._getConfigDB(); - var x = configDB.settings.findOne({ _id: "balancer" } ); - if ( x == null ) + var x = configDB.settings.findOne({_id: "balancer"}); + if (x == null) return true; - return ! x.stopped; + return !x.stopped; }; -sh.isBalancerRunning = function (configDB) { +sh.isBalancerRunning = function(configDB) { if (configDB === undefined) configDB = sh._getConfigDB(); - var x = configDB.locks.findOne({ _id: "balancer" }); + var x = configDB.locks.findOne({_id: "balancer"}); if (x == null) { print("config.locks collection empty or missing. be sure you are connected to a mongos"); return false; @@ -131,284 +147,290 @@ sh.isBalancerRunning = function (configDB) { sh.getBalancerHost = function(configDB) { if (configDB === undefined) configDB = sh._getConfigDB(); - var x = configDB.locks.findOne({ _id: "balancer" }); - if( x == null ){ - print("config.locks collection does not contain balancer lock. be sure you are connected to a mongos"); + var x = configDB.locks.findOne({_id: "balancer"}); + if (x == null) { + print( + "config.locks collection does not contain balancer lock. be sure you are connected to a mongos"); return ""; } return x.process.match(/[^:]+:[^:]+/)[0]; }; -sh.stopBalancer = function( timeout, interval ) { - sh.setBalancerState( false ); - sh.waitForBalancer( false, timeout, interval ); +sh.stopBalancer = function(timeout, interval) { + sh.setBalancerState(false); + sh.waitForBalancer(false, timeout, interval); }; -sh.startBalancer = function( timeout, interval ) { - sh.setBalancerState( true ); - sh.waitForBalancer( true, timeout, interval ); +sh.startBalancer = function(timeout, interval) { + sh.setBalancerState(true); + sh.waitForBalancer(true, timeout, interval); }; -sh.waitForDLock = function( lockId, onOrNot, timeout, interval ){ +sh.waitForDLock = function(lockId, onOrNot, timeout, interval) { // Wait for balancer to be on or off // Can also wait for particular balancer state var state = onOrNot; var configDB = sh._getConfigDB(); - + var beginTS = undefined; - if( state == undefined ){ - var currLock = configDB.locks.findOne({ _id : lockId }); - if( currLock != null ) beginTS = currLock.ts; - } - - var lockStateOk = function(){ - var lock = configDB.locks.findOne({ _id : lockId }); - - if( state == false ) return ! lock || lock.state == 0; - if( state == true ) return lock && lock.state == 2; - if( state == undefined ) return (beginTS == undefined && lock) || - (beginTS != undefined && ( !lock || lock.ts + "" != beginTS + "" ) ); - else return lock && lock.state == state; + if (state == undefined) { + var currLock = configDB.locks.findOne({_id: lockId}); + if (currLock != null) + beginTS = currLock.ts; + } + + var lockStateOk = function() { + var lock = configDB.locks.findOne({_id: lockId}); + + if (state == false) + return !lock || lock.state == 0; + if (state == true) + return lock && lock.state == 2; + if (state == undefined) + return (beginTS == undefined && lock) || + (beginTS != undefined && (!lock || lock.ts + "" != beginTS + "")); + else + return lock && lock.state == state; }; - - assert.soon( lockStateOk, - "Waited too long for lock " + lockId + " to " + - (state == true ? "lock" : ( state == false ? "unlock" : - "change to state " + state ) ), - timeout, - interval - ); -}; - -sh.waitForPingChange = function( activePings, timeout, interval ){ - - var isPingChanged = function( activePing ){ - var newPing = sh._getConfigDB().mongos.findOne({ _id : activePing._id }); - return ! newPing || newPing.ping + "" != activePing.ping + ""; + + assert.soon( + lockStateOk, + "Waited too long for lock " + lockId + " to " + + (state == true ? "lock" : (state == false ? "unlock" : "change to state " + state)), + timeout, + interval); +}; + +sh.waitForPingChange = function(activePings, timeout, interval) { + + var isPingChanged = function(activePing) { + var newPing = sh._getConfigDB().mongos.findOne({_id: activePing._id}); + return !newPing || newPing.ping + "" != activePing.ping + ""; }; - + // First wait for all active pings to change, so we're sure a settings reload // happened - + // Timeout all pings on the same clock var start = new Date(); - + var remainingPings = []; - for( var i = 0; i < activePings.length; i++ ){ - - var activePing = activePings[ i ]; - print( "Waiting for active host " + activePing._id + " to recognize new settings... (ping : " + activePing.ping + ")" ); - + for (var i = 0; i < activePings.length; i++) { + var activePing = activePings[i]; + print("Waiting for active host " + activePing._id + + " to recognize new settings... (ping : " + activePing.ping + ")"); + // Do a manual timeout here, avoid scary assert.soon errors var timeout = timeout || 30000; var interval = interval || 200; - while( isPingChanged( activePing ) != true ){ - if( ( new Date() ).getTime() - start.getTime() > timeout ){ - print( "Waited for active ping to change for host " + activePing._id + - ", a migration may be in progress or the host may be down." ); - remainingPings.push( activePing ); + while (isPingChanged(activePing) != true) { + if ((new Date()).getTime() - start.getTime() > timeout) { + print("Waited for active ping to change for host " + activePing._id + + ", a migration may be in progress or the host may be down."); + remainingPings.push(activePing); break; } - sleep( interval ); + sleep(interval); } - } - + return remainingPings; }; -sh.waitForBalancerOff = function( timeout, interval ){ +sh.waitForBalancerOff = function(timeout, interval) { var pings = sh._getConfigDB().mongos.find().toArray(); var activePings = []; - for( var i = 0; i < pings.length; i++ ){ - if( ! pings[i].waiting ) activePings.push( pings[i] ); - } - - print( "Waiting for active hosts..." ); - - activePings = sh.waitForPingChange( activePings, 60 * 1000 ); - - // After 1min, we assume that all hosts with unchanged pings are either + for (var i = 0; i < pings.length; i++) { + if (!pings[i].waiting) + activePings.push(pings[i]); + } + + print("Waiting for active hosts..."); + + activePings = sh.waitForPingChange(activePings, 60 * 1000); + + // After 1min, we assume that all hosts with unchanged pings are either // offline (this is enough time for a full errored balance round, if a network // issue, which would reload settings) or balancing, which we wait for next // Legacy hosts we always have to wait for - - print( "Waiting for the balancer lock..." ); - + + print("Waiting for the balancer lock..."); + // Wait for the balancer lock to become inactive // We can guess this is stale after 15 mins, but need to double-check manually - try{ - sh.waitForDLock( "balancer", false, 15 * 60 * 1000 ); - } - catch( e ){ - print( "Balancer still may be active, you must manually verify this is not the case using the config.changelog collection." ); + try { + sh.waitForDLock("balancer", false, 15 * 60 * 1000); + } catch (e) { + print( + "Balancer still may be active, you must manually verify this is not the case using the config.changelog collection."); throw Error(e); } - - print( "Waiting again for active hosts after balancer is off..." ); - + + print("Waiting again for active hosts after balancer is off..."); + // Wait a short time afterwards, to catch the host which was balancing earlier - activePings = sh.waitForPingChange( activePings, 5 * 1000 ); - + activePings = sh.waitForPingChange(activePings, 5 * 1000); + // Warn about all the stale host pings remaining - for( var i = 0; i < activePings.length; i++ ){ - print( "Warning : host " + activePings[i]._id + " seems to have been offline since " + activePings[i].ping ); + for (var i = 0; i < activePings.length; i++) { + print("Warning : host " + activePings[i]._id + " seems to have been offline since " + + activePings[i].ping); } - + }; -sh.waitForBalancer = function( onOrNot, timeout, interval ){ - +sh.waitForBalancer = function(onOrNot, timeout, interval) { + // If we're waiting for the balancer to turn on or switch state or // go to a particular state - if( onOrNot ){ + if (onOrNot) { // Just wait for the balancer lock to change, can't ensure we'll ever see it // actually locked - sh.waitForDLock( "balancer", undefined, timeout, interval ); - } - else { + sh.waitForDLock("balancer", undefined, timeout, interval); + } else { // Otherwise we need to wait until we're sure balancing stops - sh.waitForBalancerOff( timeout, interval ); + sh.waitForBalancerOff(timeout, interval); } - + }; -sh.disableBalancing = function( coll ){ +sh.disableBalancing = function(coll) { if (coll === undefined) { throw Error("Must specify collection"); } var dbase = db; - if( coll instanceof DBCollection ) { + if (coll instanceof DBCollection) { dbase = coll.getDB(); } else { sh._checkMongos(); } - dbase.getSisterDB( "config" ).collections.update({ _id : coll + "" }, { $set : { "noBalance" : true } }); + dbase.getSisterDB("config").collections.update({_id: coll + ""}, {$set: {"noBalance": true}}); }; -sh.enableBalancing = function( coll ){ +sh.enableBalancing = function(coll) { if (coll === undefined) { throw Error("Must specify collection"); } var dbase = db; - if( coll instanceof DBCollection ) { + if (coll instanceof DBCollection) { dbase = coll.getDB(); } else { sh._checkMongos(); } - dbase.getSisterDB( "config" ).collections.update({ _id : coll + "" }, { $set : { "noBalance" : false } }); + dbase.getSisterDB("config").collections.update({_id: coll + ""}, {$set: {"noBalance": false}}); }; /* - * Can call _lastMigration( coll ), _lastMigration( db ), _lastMigration( st ), _lastMigration( mongos ) + * Can call _lastMigration( coll ), _lastMigration( db ), _lastMigration( st ), _lastMigration( + * mongos ) */ -sh._lastMigration = function( ns ){ - +sh._lastMigration = function(ns) { + var coll = null; var dbase = null; var config = null; - - if( ! ns ){ - config = db.getSisterDB( "config" ); - } - else if( ns instanceof DBCollection ){ + + if (!ns) { + config = db.getSisterDB("config"); + } else if (ns instanceof DBCollection) { coll = ns; - config = coll.getDB().getSisterDB( "config" ); - } - else if( ns instanceof DB ){ + config = coll.getDB().getSisterDB("config"); + } else if (ns instanceof DB) { dbase = ns; - config = dbase.getSisterDB( "config" ); - } - else if( ns instanceof ShardingTest ){ - config = ns.s.getDB( "config" ); - } - else if( ns instanceof Mongo ){ - config = ns.getDB( "config" ); - } - else { + config = dbase.getSisterDB("config"); + } else if (ns instanceof ShardingTest) { + config = ns.s.getDB("config"); + } else if (ns instanceof Mongo) { + config = ns.getDB("config"); + } else { // String namespace ns = ns + ""; - if( ns.indexOf( "." ) > 0 ){ - config = db.getSisterDB( "config" ); - coll = db.getMongo().getCollection( ns ); - } - else{ - config = db.getSisterDB( "config" ); - dbase = db.getSisterDB( ns ); + if (ns.indexOf(".") > 0) { + config = db.getSisterDB("config"); + coll = db.getMongo().getCollection(ns); + } else { + config = db.getSisterDB("config"); + dbase = db.getSisterDB(ns); } } - - var searchDoc = { what : /^moveChunk/ }; - if( coll ) searchDoc.ns = coll + ""; - if( dbase ) searchDoc.ns = new RegExp( "^" + dbase + "\\." ); - - var cursor = config.changelog.find( searchDoc ).sort({ time : -1 }).limit( 1 ); - if( cursor.hasNext() ) return cursor.next(); - else return null; + + var searchDoc = { + what: /^moveChunk/ + }; + if (coll) + searchDoc.ns = coll + ""; + if (dbase) + searchDoc.ns = new RegExp("^" + dbase + "\\."); + + var cursor = config.changelog.find(searchDoc).sort({time: -1}).limit(1); + if (cursor.hasNext()) + return cursor.next(); + else + return null; }; -sh._checkLastError = function( mydb ) { +sh._checkLastError = function(mydb) { var errObj = mydb.getLastErrorObj(); if (errObj.err) throw _getErrorWithCode(errObj, "error: " + errObj.err); }; -sh.addShardTag = function( shard, tag ) { +sh.addShardTag = function(shard, tag) { var config = sh._getConfigDB(); - if ( config.shards.findOne( { _id : shard } ) == null ) { - throw Error( "can't find a shard with name: " + shard ); + if (config.shards.findOne({_id: shard}) == null) { + throw Error("can't find a shard with name: " + shard); } - config.shards.update( { _id : shard } , { $addToSet : { tags : tag } } ); - sh._checkLastError( config ); + config.shards.update({_id: shard}, {$addToSet: {tags: tag}}); + sh._checkLastError(config); }; -sh.removeShardTag = function( shard, tag ) { +sh.removeShardTag = function(shard, tag) { var config = sh._getConfigDB(); - if ( config.shards.findOne( { _id : shard } ) == null ) { - throw Error( "can't find a shard with name: " + shard ); + if (config.shards.findOne({_id: shard}) == null) { + throw Error("can't find a shard with name: " + shard); } - config.shards.update( { _id : shard } , { $pull : { tags : tag } } ); - sh._checkLastError( config ); + config.shards.update({_id: shard}, {$pull: {tags: tag}}); + sh._checkLastError(config); }; -sh.addTagRange = function( ns, min, max, tag ) { - if ( bsonWoCompare( min, max ) == 0 ) { +sh.addTagRange = function(ns, min, max, tag) { + if (bsonWoCompare(min, max) == 0) { throw new Error("min and max cannot be the same"); } var config = sh._getConfigDB(); - config.tags.update( {_id: { ns : ns , min : min } } , - {_id: { ns : ns , min : min }, ns : ns , min : min , max : max , tag : tag } , - true ); - sh._checkLastError( config ); + config.tags.update({_id: {ns: ns, min: min}}, + {_id: {ns: ns, min: min}, ns: ns, min: min, max: max, tag: tag}, + true); + sh._checkLastError(config); }; -sh.removeTagRange = function( ns, min, max, tag ) { +sh.removeTagRange = function(ns, min, max, tag) { var config = sh._getConfigDB(); // warn if the namespace does not exist, even dropped - if ( config.collections.findOne( { _id : ns } ) == null ) { - print( "Warning: can't find the namespace: " + ns + " - collection likely never sharded" ); + if (config.collections.findOne({_id: ns}) == null) { + print("Warning: can't find the namespace: " + ns + " - collection likely never sharded"); } // warn if the tag being removed is still in use - if ( config.shards.findOne( { tags : tag } ) ) { - print( "Warning: tag still in use by at least one shard" ); + if (config.shards.findOne({tags: tag})) { + print("Warning: tag still in use by at least one shard"); } // max and tag criteria not really needed, but including them avoids potentially unexpected // behavior. - config.tags.remove( { _id : { ns : ns , min : min } , max : max , tag : tag } ); - sh._checkLastError( config ); + config.tags.remove({_id: {ns: ns, min: min}, max: max, tag: tag}); + sh._checkLastError(config); }; sh.getBalancerLockDetails = function(configDB) { if (configDB === undefined) configDB = db.getSiblingDB('config'); - var lock = configDB.locks.findOne({ _id : 'balancer' }); + var lock = configDB.locks.findOne({_id: 'balancer'}); if (lock == null) { return null; } - if (lock.state == 0){ + if (lock.state == 0) { return null; } return lock; @@ -417,11 +439,11 @@ sh.getBalancerLockDetails = function(configDB) { sh.getBalancerWindow = function(configDB) { if (configDB === undefined) configDB = db.getSiblingDB('config'); - var settings = configDB.settings.findOne({ _id : 'balancer' }); - if ( settings == null ) { + var settings = configDB.settings.findOne({_id: 'balancer'}); + if (settings == null) { return null; } - if (settings.hasOwnProperty("activeWindow")){ + if (settings.hasOwnProperty("activeWindow")) { return settings.activeWindow; } return null; @@ -430,11 +452,11 @@ sh.getBalancerWindow = function(configDB) { sh.getActiveMigrations = function(configDB) { if (configDB === undefined) configDB = db.getSiblingDB('config'); - var activeLocks = configDB.locks.find( { _id : { $ne : "balancer" }, state: {$eq:2} }); + var activeLocks = configDB.locks.find({_id: {$ne: "balancer"}, state: {$eq: 2}}); var result = []; - if( activeLocks != null ){ - activeLocks.forEach( function(lock){ - result.push({_id:lock._id, when:lock.when}); + if (activeLocks != null) { + activeLocks.forEach(function(lock) { + result.push({_id: lock._id, when: lock.when}); }); } return result; @@ -443,11 +465,15 @@ sh.getActiveMigrations = function(configDB) { sh.getRecentFailedRounds = function(configDB) { if (configDB === undefined) configDB = db.getSiblingDB('config'); - var balErrs = configDB.actionlog.find({what:"balancer.round"}).sort({time:-1}).limit(5); - var result = { count : 0, lastErr : "", lastTime : " "}; - if(balErrs != null) { - balErrs.forEach( function(r){ - if(r.details.errorOccured){ + var balErrs = configDB.actionlog.find({what: "balancer.round"}).sort({time: -1}).limit(5); + var result = { + count: 0, + lastErr: "", + lastTime: " " + }; + if (balErrs != null) { + balErrs.forEach(function(r) { + if (r.details.errorOccured) { result.count += 1; result.lastErr = r.details.errmsg; result.lastTime = r.time; @@ -465,63 +491,42 @@ sh.getRecentFailedRounds = function(configDB) { sh.getRecentMigrations = function(configDB) { if (configDB === undefined) configDB = sh._getConfigDB(); - var yesterday = new Date( new Date() - 24 * 60 * 60 * 1000 ); + var yesterday = new Date(new Date() - 24 * 60 * 60 * 1000); // Successful migrations. var result = configDB.changelog.aggregate([ { - $match: { - time: { $gt: yesterday }, - what: "moveChunk.from", - 'details.errmsg': { $exists: false }, - 'details.note': 'success' - } - }, - { - $group: { - _id: { - msg: "$details.errmsg" - }, - count : { $sum: 1 } - } + $match: { + time: {$gt: yesterday}, + what: "moveChunk.from", 'details.errmsg': {$exists: false}, 'details.note': 'success' + } }, - { - $project: { - _id: { $ifNull: [ "$_id.msg", "Success" ] }, - count: "$count" - } - } + {$group: {_id: {msg: "$details.errmsg"}, count: {$sum: 1}}}, + {$project: {_id: {$ifNull: ["$_id.msg", "Success"]}, count: "$count"}} ]).toArray(); // Failed migrations. result = result.concat(configDB.changelog.aggregate([ { - $match: { - time: { $gt: yesterday }, - what : "moveChunk.from", - $or: [ - { 'details.errmsg': { $exists: true }}, - { 'details.note': { $ne: 'success' }} - ] - } + $match: { + time: {$gt: yesterday}, + what: "moveChunk.from", + $or: [{'details.errmsg': {$exists: true}}, {'details.note': {$ne: 'success'}}] + } }, { - $group: { - _id: { - msg: "$details.errmsg", - from : "$details.from", - to: "$details.to" - }, - count: { $sum: 1 } - } + $group: { + _id: {msg: "$details.errmsg", from: "$details.from", to: "$details.to"}, + count: {$sum: 1} + } }, { - $project: { - _id: { $ifNull: [ '$_id.msg', 'aborted' ]}, - from: "$_id.from", - to: "$_id.to", - count: "$count" - } + $project: { + _id: {$ifNull: ['$_id.msg', 'aborted']}, + from: "$_id.from", + to: "$_id.to", + count: "$count" + } } ]).toArray()); @@ -534,29 +539,28 @@ function printShardingStatus(configDB, verbose) { if (configDB === undefined) configDB = db.getSisterDB('config'); - var version = configDB.getCollection( "version" ).findOne(); - if ( version == null ){ - print( "printShardingStatus: this db does not have sharding enabled. be sure you are connecting to a mongos from the shell and not to a mongod." ); + var version = configDB.getCollection("version").findOne(); + if (version == null) { + print( + "printShardingStatus: this db does not have sharding enabled. be sure you are connecting to a mongos from the shell and not to a mongod."); return; } var raw = ""; - var output = function(s){ + var output = function(s) { raw += s + "\n"; }; - output( "--- Sharding Status --- " ); - output( " sharding version: " + tojson( configDB.getCollection( "version" ).findOne() ) ); + output("--- Sharding Status --- "); + output(" sharding version: " + tojson(configDB.getCollection("version").findOne())); - output( " shards:" ); - configDB.shards.find().sort( { _id : 1 } ).forEach( - function(z){ - output( "\t" + tojsononeline( z ) ); - } - ); + output(" shards:"); + configDB.shards.find().sort({_id: 1}).forEach(function(z) { + output("\t" + tojsononeline(z)); + }); // (most recently) active mongoses var mongosActiveThresholdMs = 60000; - var mostRecentMongos = configDB.mongos.find().sort( { ping : -1 } ).limit(1); + var mostRecentMongos = configDB.mongos.find().sort({ping: -1}).limit(1); var mostRecentMongosTime = null; var mongosAdjective = "most recently active"; if (mostRecentMongos.hasNext()) { @@ -569,181 +573,178 @@ function printShardingStatus(configDB, verbose) { } } - output( " " + mongosAdjective + " mongoses:" ); + output(" " + mongosAdjective + " mongoses:"); if (mostRecentMongosTime === null) { - output( "\tnone" ); + output("\tnone"); } else { var recentMongosQuery = { ping: { - $gt: (function () { + $gt: (function() { var d = mostRecentMongosTime; d.setTime(d.getTime() - mongosActiveThresholdMs); return d; - } )() + })() } }; - if ( verbose ) { - configDB.mongos.find( recentMongosQuery ).sort( { ping : -1 } ).forEach( - function (z) { - output( "\t" + tojsononeline( z ) ); - } - ); + if (verbose) { + configDB.mongos.find(recentMongosQuery) + .sort({ping: -1}) + .forEach(function(z) { + output("\t" + tojsononeline(z)); + }); } else { - configDB.mongos.aggregate( [ - { $match: recentMongosQuery }, - { $group: { _id: "$mongoVersion", num: { $sum: 1 } } }, - { $sort: { num: -1 } } - ] ).forEach( - function (z) { - output( "\t" + tojson( z._id ) + " : " + z.num ); - } - ); + configDB.mongos.aggregate([ + {$match: recentMongosQuery}, + {$group: {_id: "$mongoVersion", num: {$sum: 1}}}, + {$sort: {num: -1}} + ]) + .forEach(function(z) { + output("\t" + tojson(z._id) + " : " + z.num); + }); } } - output( " balancer:" ); + output(" balancer:"); - //Is the balancer currently enabled - output( "\tCurrently enabled: " + ( sh.getBalancerState(configDB) ? "yes" : "no" ) ); + // Is the balancer currently enabled + output("\tCurrently enabled: " + (sh.getBalancerState(configDB) ? "yes" : "no")); - //Is the balancer currently active - output( "\tCurrently running: " + ( sh.isBalancerRunning(configDB) ? "yes" : "no" ) ); + // Is the balancer currently active + output("\tCurrently running: " + (sh.isBalancerRunning(configDB) ? "yes" : "no")); - //Output details of the current balancer round + // Output details of the current balancer round var balLock = sh.getBalancerLockDetails(configDB); - if ( balLock ) { - output( "\t\tBalancer lock taken at " + balLock.when + " by " + balLock.who ); + if (balLock) { + output("\t\tBalancer lock taken at " + balLock.when + " by " + balLock.who); } - //Output the balancer window + // Output the balancer window var balSettings = sh.getBalancerWindow(configDB); - if ( balSettings ) { - output( "\t\tBalancer active window is set between " + - balSettings.start + " and " + balSettings.stop + " server local time"); + if (balSettings) { + output("\t\tBalancer active window is set between " + balSettings.start + " and " + + balSettings.stop + " server local time"); } - //Output the list of active migrations + // Output the list of active migrations var activeMigrations = sh.getActiveMigrations(configDB); - if (activeMigrations.length > 0 ){ + if (activeMigrations.length > 0) { output("\tCollections with active migrations: "); - activeMigrations.forEach( function(migration){ - output("\t\t"+migration._id+ " started at " + migration.when ); + activeMigrations.forEach(function(migration) { + output("\t\t" + migration._id + " started at " + migration.when); }); } // Actionlog and version checking only works on 2.7 and greater var versionHasActionlog = false; var metaDataVersion = configDB.getCollection("version").findOne().currentVersion; - if ( metaDataVersion > 5 ) { + if (metaDataVersion > 5) { versionHasActionlog = true; } - if ( metaDataVersion == 5 ) { + if (metaDataVersion == 5) { var verArray = db.serverBuildInfo().versionArray; - if (verArray[0] == 2 && verArray[1] > 6){ + if (verArray[0] == 2 && verArray[1] > 6) { versionHasActionlog = true; } } - if ( versionHasActionlog ) { - //Review config.actionlog for errors + if (versionHasActionlog) { + // Review config.actionlog for errors var actionReport = sh.getRecentFailedRounds(configDB); - //Always print the number of failed rounds - output( "\tFailed balancer rounds in last 5 attempts: " + actionReport.count ); + // Always print the number of failed rounds + output("\tFailed balancer rounds in last 5 attempts: " + actionReport.count); - //Only print the errors if there are any - if ( actionReport.count > 0 ){ - output( "\tLast reported error: " + actionReport.lastErr ); - output( "\tTime of Reported error: " + actionReport.lastTime ); + // Only print the errors if there are any + if (actionReport.count > 0) { + output("\tLast reported error: " + actionReport.lastErr); + output("\tTime of Reported error: " + actionReport.lastTime); } output("\tMigration Results for the last 24 hours: "); var migrations = sh.getRecentMigrations(configDB); - if(migrations.length > 0) { - migrations.forEach( function(x) { - if (x._id === "Success"){ - output( "\t\t" + x.count + " : " + x._id); + if (migrations.length > 0) { + migrations.forEach(function(x) { + if (x._id === "Success") { + output("\t\t" + x.count + " : " + x._id); } else { - output( "\t\t" + x.count + " : Failed with error '" + x._id - + "', from " + x.from + " to " + x.to ); + output("\t\t" + x.count + " : Failed with error '" + x._id + "', from " + + x.from + " to " + x.to); } }); } else { - output( "\t\tNo recent migrations"); + output("\t\tNo recent migrations"); } } - output( " databases:" ); - configDB.databases.find().sort( { name : 1 } ).forEach( - function(db){ - var truthy = function (value) { - return !!value; - }; - var nonBooleanNote = function (name, value) { - // If the given value is not a boolean, return a string of the - // form " (<name>: <value>)", where <value> is converted to JSON. - var t = typeof(value); - var s = ""; - if (t != "boolean" && t != "undefined") { - s = " (" + name + ": " + tojson(value) + ")"; - } - return s; - }; - - output( "\t" + tojsononeline(db,"",true) ); - - if (db.partitioned){ - configDB.collections.find( { _id : new RegExp( "^" + - RegExp.escape(db._id) + "\\." ) } ). - sort( { _id : 1 } ).forEach( function( coll ){ - if ( ! coll.dropped ){ - output( "\t\t" + coll._id ); - output( "\t\t\tshard key: " + tojson(coll.key) ); - output( "\t\t\tunique: " + truthy(coll.unique) - + nonBooleanNote("unique", coll.unique) ); - output( "\t\t\tbalancing: " + !truthy(coll.noBalance) - + nonBooleanNote("noBalance", coll.noBalance) ); - output( "\t\t\tchunks:" ); - - res = configDB.chunks.aggregate( { $match : { ns : coll._id } } , - { $group : { _id : "$shard" , - cnt : { $sum : 1 } } } , - { $project : { _id : 0 , - shard : "$_id" , - nChunks : "$cnt" } } , - { $sort : { shard : 1 } } ).toArray(); - var totalChunks = 0; - res.forEach( function(z){ - totalChunks += z.nChunks; - output( "\t\t\t\t" + z.shard + "\t" + z.nChunks ); - } ); - - if ( totalChunks < 20 || verbose ){ - configDB.chunks.find( { "ns" : coll._id } ).sort( { min : 1 } ).forEach( - function(chunk){ - output( "\t\t\t" + tojson( chunk.min ) + " -->> " + tojson( chunk.max ) + - " on : " + chunk.shard + " " + tojson( chunk.lastmod ) + " " + - ( chunk.jumbo ? "jumbo " : "" ) ); - } - ); - } - else { - output( "\t\t\ttoo many chunks to print, use verbose if you want to force print" ); - } - - configDB.tags.find( { ns : coll._id } ).sort( { min : 1 } ).forEach( - function( tag ) { - output( "\t\t\t tag: " + tag.tag + " " + tojson( tag.min ) + " -->> " + tojson( tag.max ) ); - } - ); + output(" databases:"); + configDB.databases.find().sort({name: 1}).forEach(function(db) { + var truthy = function(value) { + return !!value; + }; + var nonBooleanNote = function(name, value) { + // If the given value is not a boolean, return a string of the + // form " (<name>: <value>)", where <value> is converted to JSON. + var t = typeof(value); + var s = ""; + if (t != "boolean" && t != "undefined") { + s = " (" + name + ": " + tojson(value) + ")"; + } + return s; + }; + + output("\t" + tojsononeline(db, "", true)); + + if (db.partitioned) { + configDB.collections.find({_id: new RegExp("^" + RegExp.escape(db._id) + "\\.")}) + .sort({_id: 1}) + .forEach(function(coll) { + if (!coll.dropped) { + output("\t\t" + coll._id); + output("\t\t\tshard key: " + tojson(coll.key)); + output("\t\t\tunique: " + truthy(coll.unique) + + nonBooleanNote("unique", coll.unique)); + output("\t\t\tbalancing: " + !truthy(coll.noBalance) + + nonBooleanNote("noBalance", coll.noBalance)); + output("\t\t\tchunks:"); + + res = configDB.chunks + .aggregate({$match: {ns: coll._id}}, + {$group: {_id: "$shard", cnt: {$sum: 1}}}, + {$project: {_id: 0, shard: "$_id", nChunks: "$cnt"}}, + {$sort: {shard: 1}}) + .toArray(); + var totalChunks = 0; + res.forEach(function(z) { + totalChunks += z.nChunks; + output("\t\t\t\t" + z.shard + "\t" + z.nChunks); + }); + + if (totalChunks < 20 || verbose) { + configDB.chunks.find({"ns": coll._id}) + .sort({min: 1}) + .forEach(function(chunk) { + output("\t\t\t" + tojson(chunk.min) + " -->> " + + tojson(chunk.max) + " on : " + chunk.shard + " " + + tojson(chunk.lastmod) + " " + + (chunk.jumbo ? "jumbo " : "")); + }); + } else { + output( + "\t\t\ttoo many chunks to print, use verbose if you want to force print"); } + + configDB.tags.find({ns: coll._id}) + .sort({min: 1}) + .forEach(function(tag) { + output("\t\t\t tag: " + tag.tag + " " + tojson(tag.min) + + " -->> " + tojson(tag.max)); + }); } - ); - } + }); } - ); - - print( raw ); + }); + + print(raw); } function printShardingSizes(configDB) { @@ -752,59 +753,56 @@ function printShardingSizes(configDB) { if (configDB === undefined) configDB = db.getSisterDB('config'); - var version = configDB.getCollection( "version" ).findOne(); - if ( version == null ){ - print( "printShardingSizes : not a shard db!" ); + var version = configDB.getCollection("version").findOne(); + if (version == null) { + print("printShardingSizes : not a shard db!"); return; } var raw = ""; - var output = function(s){ + var output = function(s) { raw += s + "\n"; }; - output( "--- Sharding Status --- " ); - output( " sharding version: " + tojson( configDB.getCollection( "version" ).findOne() ) ); + output("--- Sharding Status --- "); + output(" sharding version: " + tojson(configDB.getCollection("version").findOne())); - output( " shards:" ); + output(" shards:"); var shards = {}; - configDB.shards.find().forEach( - function(z){ - shards[z._id] = new Mongo(z.host); - output( " " + tojson(z) ); - } - ); + configDB.shards.find().forEach(function(z) { + shards[z._id] = new Mongo(z.host); + output(" " + tojson(z)); + }); var saveDB = db; - output( " databases:" ); - configDB.databases.find().sort( { name : 1 } ).forEach( - function(db){ - output( "\t" + tojson(db,"",true) ); - - if (db.partitioned){ - configDB.collections.find( { _id : new RegExp( "^" + - RegExp.escape(db._id) + "\." ) } ). - sort( { _id : 1 } ).forEach( function( coll ){ - output("\t\t" + coll._id + " chunks:"); - configDB.chunks.find( { "ns" : coll._id } ).sort( { min : 1 } ).forEach( - function(chunk){ - var mydb = shards[chunk.shard].getDB(db._id); - var out = mydb.runCommand({dataSize: coll._id, - keyPattern: coll.key, - min: chunk.min, - max: chunk.max }); - delete out.millis; - delete out.ok; - - output( "\t\t\t" + tojson( chunk.min ) + " -->> " + tojson( chunk.max ) + - " on : " + chunk.shard + " " + tojson( out ) ); - - } - ); - } - ); - } + output(" databases:"); + configDB.databases.find().sort({name: 1}).forEach(function(db) { + output("\t" + tojson(db, "", true)); + + if (db.partitioned) { + configDB.collections.find({_id: new RegExp("^" + RegExp.escape(db._id) + "\.")}) + .sort({_id: 1}) + .forEach(function(coll) { + output("\t\t" + coll._id + " chunks:"); + configDB.chunks.find({"ns": coll._id}) + .sort({min: 1}) + .forEach(function(chunk) { + var mydb = shards[chunk.shard].getDB(db._id); + var out = mydb.runCommand({ + dataSize: coll._id, + keyPattern: coll.key, + min: chunk.min, + max: chunk.max + }); + delete out.millis; + delete out.ok; + + output("\t\t\t" + tojson(chunk.min) + " -->> " + tojson(chunk.max) + + " on : " + chunk.shard + " " + tojson(out)); + + }); + }); } - ); - - print( raw ); + }); + + print(raw); } |