diff options
author | Greg Studer <greg@10gen.com> | 2014-03-14 15:21:49 -0400 |
---|---|---|
committer | Greg Studer <greg@10gen.com> | 2014-03-18 15:26:54 -0400 |
commit | 43be3d5a87c70a82a36f4116b733d35a514564ae (patch) | |
tree | 03629c1b91eec4b09896c61269f7930cf419c1c4 | |
parent | 510e2dda4e8e2e229adb9aec5f077050ebfae1da (diff) | |
download | mongo-43be3d5a87c70a82a36f4116b733d35a514564ae.tar.gz |
SERVER-13057 push shell write errors more in line with other drivers
(cherry picked from commit 6ee3a36bdb672e0e7b893a77334e932d8d1eab8c)
33 files changed, 521 insertions, 339 deletions
diff --git a/jstests/core/big_object1.js b/jstests/core/big_object1.js index be61dbd3041..e567cf8ce93 100644 --- a/jstests/core/big_object1.js +++ b/jstests/core/big_object1.js @@ -23,7 +23,7 @@ if ( db.adminCommand( "buildinfo" ).bits == 64 ){ break; } - if ( result.hasWriteErrors() ) + if ( result.hasWriteError() ) break; x++; } diff --git a/jstests/core/bulk_api_ordered.js b/jstests/core/bulk_api_ordered.js index fd0f528cb9c..beef9d24411 100644 --- a/jstests/core/bulk_api_ordered.js +++ b/jstests/core/bulk_api_ordered.js @@ -80,8 +80,9 @@ var executeTests = function() { bulkOp.insert({b:1, a:1}); bulkOp.find({b:2}).upsert().updateOne({$set: {a:1}}); bulkOp.insert({b:3, a:2}); - var result = bulkOp.execute(); - + var result = assert.throws( function() { bulkOp.execute(); } ); + assert(result instanceof BulkWriteError); + assert(result instanceof Error); // Basic properties check assert.eq(1, result.nInserted); assert.eq(true, result.hasWriteErrors()); @@ -118,7 +119,7 @@ var executeTests = function() { bulkOp.find({b:2}).upsert().updateOne({$set: {a:1}}); bulkOp.insert({b:4, a:3}); bulkOp.insert({b:5, a:1}); - var result = bulkOp.execute(); + var result = assert.throws( function() { bulkOp.execute(); } ); // Basic properties check assert.eq(1, result.nInserted); diff --git a/jstests/core/bulk_api_unordered.js b/jstests/core/bulk_api_unordered.js index db6c372fb76..1c8607dec37 100644 --- a/jstests/core/bulk_api_unordered.js +++ b/jstests/core/bulk_api_unordered.js @@ -83,7 +83,8 @@ var executeTests = function() { bulkOp.insert({b:1, a:1}); bulkOp.find({b:2}).upsert().updateOne({$set: {a:1}}); bulkOp.insert({b:3, a:2}); - var result = bulkOp.execute(); + var result = assert.throws( function() { bulkOp.execute(); } ); + // Basic properties check assert.eq(2, result.nInserted); assert.eq(true, result.hasWriteErrors()); @@ -116,7 +117,8 @@ var executeTests = function() { bulkOp.find({b:2}).upsert().updateOne({$set: {a:1}}); bulkOp.insert({b:4, a:3}); bulkOp.insert({b:5, a:1}); - var result = bulkOp.execute(); + var result = assert.throws( function() { bulkOp.execute(); } ); + // Basic properties check assert.eq(2, result.nInserted); assert.eq(1, result.nUpserted); diff --git a/jstests/core/bulk_insert.js b/jstests/core/bulk_insert.js index e26b323c6d9..e2e625e0a3c 100644 --- a/jstests/core/bulk_insert.js +++ b/jstests/core/bulk_insert.js @@ -3,7 +3,9 @@ var coll = db.bulkInsertTest coll.drop() -Random.srand( new Date().getTime() ) +var seed = new Date().getTime(); +Random.srand( seed ); +print("Seed for randomized test is " + seed); var bulkSize = Math.floor( Random.rand() * 200 ) + 1 var numInserts = Math.floor( Random.rand() * 300 ) + 1 diff --git a/jstests/core/bulk_legacy_enforce_gle.js b/jstests/core/bulk_legacy_enforce_gle.js index bec11749274..4efc280ab37 100644 --- a/jstests/core/bulk_legacy_enforce_gle.js +++ b/jstests/core/bulk_legacy_enforce_gle.js @@ -10,7 +10,7 @@ var coll = db.bulk_legacy_enforce_gle; coll.drop(); var bulk = coll.initializeUnorderedBulkOp(); bulk.find({ none: 1 }).upsert().updateOne({ _id: 1 }); -assert.writeOK(bulk.execute()); +assert( bulk.execute() instanceof BulkWriteResult ); var gle = db.runCommand({ getLastError: 1 }); assert(gle.ok, tojson(gle)); @@ -21,7 +21,7 @@ coll.drop(); coll.insert({ _id: 1 }); bulk = coll.initializeUnorderedBulkOp(); bulk.find({ none: 1 }).upsert().updateOne({ _id: 1 }); -assert.writeError(bulk.execute()); +assert.throws( function() { bulk.execute(); } ); gle = db.runCommand({ getLastError: 1 }); assert(gle.ok, tojson(gle)); @@ -34,7 +34,7 @@ bulk = coll.initializeUnorderedBulkOp(); bulk.find({ none: 1 }).upsert().updateOne({ _id: 1 }); bulk.find({ none: 1 }).upsert().updateOne({ _id: 1 }); bulk.find({ none: 1 }).upsert().updateOne({ _id: 0 }); -var res = assert.writeError(bulk.execute()); +var res = assert.throws( function() { bulk.execute(); } ); assert.eq(2, res.getWriteErrors().length); gle = db.runCommand({ getLastError: 1 }); @@ -48,7 +48,7 @@ bulk = coll.initializeUnorderedBulkOp(); bulk.find({ none: 1 }).upsert().updateOne({ _id: 0 }); bulk.find({ none: 1 }).upsert().updateOne({ _id: 1 }); bulk.find({ none: 1 }).upsert().updateOne({ _id: 2 }); -res = assert.writeError(bulk.execute()); +var res = assert.throws( function() { bulk.execute(); } ); assert.eq(1, res.getWriteErrors().length); gle = db.runCommand({ getLastError: 1 }); @@ -63,7 +63,7 @@ bulk = coll.initializeUnorderedBulkOp(); bulk.find({ none: 1 }).upsert().updateOne({ _id: 0 }); bulk.find({ none: 1 }).upsert().updateOne({ _id: 1 }); bulk.find({ none: 1 }).upsert().updateOne({ _id: 2 }); -res = assert.writeError(bulk.execute()); +res = assert.throws( function() { bulk.execute(); } ); assert.eq(1, res.getWriteErrors().length); gle = db.runCommand({ getLastError: 1 }); @@ -77,7 +77,7 @@ bulk = coll.initializeUnorderedBulkOp(); bulk.find({ none: 1 }).upsert().updateOne({ _id: 0 }); bulk.find({ none: 1 }).upsert().updateOne({ _id: 1 }); bulk.find({ none: 1 }).upsert().updateOne({ _id: 2 }); -res = assert.writeError(bulk.execute()); +res = assert.throws( function() { bulk.execute(); } ); assert.eq(1, res.getWriteErrors().length); gle = db.runCommand({ getLastError: 1, w: 1 }); @@ -91,7 +91,7 @@ bulk = coll.initializeUnorderedBulkOp(); bulk.find({ none: 1 }).upsert().updateOne({ _id: 0 }); bulk.find({ none: 1 }).upsert().updateOne({ _id: 1 }); bulk.find({ none: 1 }).upsert().updateOne({ _id: 2 }); -res = assert.writeError(bulk.execute()); +res = assert.throws( function() { bulk.execute(); } ); assert.eq(1, res.getWriteErrors().length); gle = db.runCommand({ getLastError: 1, w: 0 }); diff --git a/jstests/core/cappeda.js b/jstests/core/cappeda.js index 3244ffae84f..4292a989511 100644 --- a/jstests/core/cappeda.js +++ b/jstests/core/cappeda.js @@ -14,7 +14,7 @@ function q() { function u() { var res = t.update( { _id : 5 } , { $set : { x : 2 } } ); - if ( res.hasWriteErrors() ) + if ( res.hasWriteError() ) throw res; } diff --git a/jstests/core/geo_borders.js b/jstests/core/geo_borders.js index 20781409b1e..32c4889eccd 100644 --- a/jstests/core/geo_borders.js +++ b/jstests/core/geo_borders.js @@ -27,7 +27,7 @@ assert.neq(null, res); // Create a point index only slightly bigger than the points we have res = t.ensureIndex( { loc : "2d" }, { max : overallMax + epsilon, min : overallMin - epsilon } ); -assert.writeOK(res); +assert.commandWorked(res); // ************ // Box Tests diff --git a/jstests/core/geo_multinest0.js b/jstests/core/geo_multinest0.js index 634d581589c..7cde4f87280 100644 --- a/jstests/core/geo_multinest0.js +++ b/jstests/core/geo_multinest0.js @@ -39,7 +39,7 @@ t.insert( { zip : "10001", data : [ { loc : [ [ 10, 10 ], { lat : 50, long : 50 t.insert( { zip : "10002", data : [ { loc : [ 20, 20 ], type : "home" }, { loc : [ 50, 50 ], type : "work" } ] } ) res = t.insert({ zip: "10003", data: [{ loc: [{ x: 30, y: 30 }, [ 50, 50 ]], type: "home" }]}); -assert( !res.hasWriteErrors() ); +assert( !res.hasWriteError() ); assert.commandWorked(t.ensureIndex( { "data.loc" : "2d", zip : 1 } )); assert.eq( 2, t.getIndexKeys().length ) diff --git a/jstests/core/geo_polygon2.js b/jstests/core/geo_polygon2.js index c626064f153..26477651a06 100644 --- a/jstests/core/geo_polygon2.js +++ b/jstests/core/geo_polygon2.js @@ -247,7 +247,7 @@ for ( var test = 0; test < numTests; test++ ) { } var res = t.ensureIndex({ loc: "2d" }, { bits: 1 + bits, max: bounds[1], min: bounds[0] }); - assert.writeOK( res ); + assert.commandWorked( res ); t.insert( { loc : allPointsIn } ); t.insert( { loc : allPointsOut } ); diff --git a/jstests/core/geo_s2index.js b/jstests/core/geo_s2index.js index fea45dbb901..11d86a3bf2f 100755 --- a/jstests/core/geo_s2index.js +++ b/jstests/core/geo_s2index.js @@ -35,7 +35,7 @@ t.insert( {geo : somepoly, nonGeo: "somepoly" }) var res = t.ensureIndex( { geo : "2dsphere", nonGeo: 1 } ); // We have a point without any geo data. Don't error. -assert.writeOK(res); +assert.commandWorked(res); res = t.find({ "geo" : { "$geoIntersects" : { "$geometry" : pointA} } }); assert.eq(res.itcount(), 3); @@ -96,7 +96,7 @@ assert.writeOK(res); t.drop(); t.save({loc: [0,0]}) res = t.ensureIndex({ loc: "2dsphere" }, { finestIndexedLevel: 17, coarsestIndexedLevel: 5 }); -assert.writeOK(res); +assert.commandWorked(res); t.drop(); t.save({loc: [0,0]}) diff --git a/jstests/core/indexOtherNamespace.js b/jstests/core/indexOtherNamespace.js index 7df55188606..da026616cc6 100644 --- a/jstests/core/indexOtherNamespace.js +++ b/jstests/core/indexOtherNamespace.js @@ -7,18 +7,8 @@ otherDB.foo.insert({a:1}) assert.eq(1, otherDB.system.indexes.count()); assert.eq("BasicCursor", otherDB.foo.find({a:1}).explain().cursor); -if (db.getMongo().writeMode() == 'commands') { - assert.throws(function() { - otherDB.randomNS.system.indexes.insert({ ns: "indexOtherNS.foo", - key: { a: 1}, name: "a_1" }); - }); -} -else { - assert.writeError(otherDB.randomNS.system.indexes.insert({ ns: "indexOtherNS.foo", - key: { a: 1 }, name: "a_1"})); -} - - +assert.writeError(otherDB.randomNS.system.indexes.insert({ ns: "indexOtherNS.foo", + key: { a: 1 }, name: "a_1"})); // Assert that index didn't actually get built assert.eq(1, otherDB.system.indexes.count()); diff --git a/jstests/core/index_big1.js b/jstests/core/index_big1.js index 6fbffa4415e..dffebc351cf 100644 --- a/jstests/core/index_big1.js +++ b/jstests/core/index_big1.js @@ -14,7 +14,7 @@ for ( i=0; i<N; i++ ) { bulk.insert( { a : i + .5 , x : s } ); s += "x"; } -assert.writeError(bulk.execute()); +assert.throws( function() { bulk.execute(); } ); assert.eq( 2 , t.getIndexes().length ); diff --git a/jstests/core/indexapi.js b/jstests/core/indexapi.js index 3e0b70ff15f..911e58e980c 100644 --- a/jstests/core/indexapi.js +++ b/jstests/core/indexapi.js @@ -37,12 +37,6 @@ assert( idx[1].unique , "M3" ); //printjson( idx ); // Test that attempting to create index in an invalid namespace fails. -if (db.getMongo().writeMode() == 'commands') { - assert.throws(function() { - db.system.indexes.insert( { ns : "test" , key : { x : 1 } , name : "x" } ); - }); -} -else { - assert.writeError(db.system.indexes.insert( { ns : "test" , key : { x : 1 } , name : "x" } )); -} +assert.writeError(db.system.indexes.insert( { ns : "test" , key : { x : 1 } , name : "x" } )); + diff --git a/jstests/core/indexi.js b/jstests/core/indexi.js index d0e77471fda..06f185fb689 100644 --- a/jstests/core/indexi.js +++ b/jstests/core/indexi.js @@ -5,21 +5,12 @@ t.drop(); idx = db.jstests_indexi.$_id_; -var expectWriteError = function(func) { - if (db.getMongo().writeMode() == 'commands') { - assert.throws(func); - } - else { - assert.writeError(func()); - } -}; - // Test that accessing the index namespace fails. function checkFailingOperations() { assert.throws(function() { idx.find().itcount(); }); - expectWriteError(function() { return idx.insert({ x: 1 }); }); - expectWriteError(function() { return idx.update({ x: 1 }, { x: 2 }); }); - expectWriteError(function() { return idx.remove({ x: 1 }); }); + assert.writeError( idx.insert({ x: 1 }) ); + assert.writeError( idx.update({ x: 1 }, { x: 2 }) ); + assert.writeError( idx.remove({ x: 1 }) ); assert.commandFailed( idx.runCommand( 'compact' ) ); assert.commandFailed( idx.ensureIndex({ x: 1 })); } diff --git a/jstests/core/ns_length.js b/jstests/core/ns_length.js index 203e68ead58..557e3110195 100644 --- a/jstests/core/ns_length.js +++ b/jstests/core/ns_length.js @@ -21,7 +21,7 @@ function canMakeCollectionWithName(name) { var success = false; try { // may either throw or return an error - success = !(myDb[name].insert({}).hasWriteErrors()); + success = !(myDb[name].insert({}).hasWriteError()); } catch (e) { success = false; } diff --git a/jstests/core/push2.js b/jstests/core/push2.js index ae09b20fb30..2ce34b7bf9a 100644 --- a/jstests/core/push2.js +++ b/jstests/core/push2.js @@ -11,7 +11,7 @@ gotError = null; for ( x=0; x<100; x++ ){ print (x + " pushes"); var res = t.update( {} , { $push : { a : s } } ); - gotError = res.hasWriteErrors(); + gotError = res.hasWriteError(); if ( gotError ) break; } diff --git a/jstests/core/remove6.js b/jstests/core/remove6.js index 838ca1c5bfd..96c5481c813 100644 --- a/jstests/core/remove6.js +++ b/jstests/core/remove6.js @@ -21,7 +21,7 @@ function test( n , idx ){ if ( idx ) t.ensureIndex( idx ); var res = del(); - assert( !res.hasWriteErrors() , "error deleting: " + res.toString() ); + assert( !res.hasWriteError() , "error deleting: " + res.toString() ); assert.eq( 0 , t.count() , n + " B " + idx ); } diff --git a/jstests/core/removeb.js b/jstests/core/removeb.js index 230a8de012f..1e6658bd7a9 100644 --- a/jstests/core/removeb.js +++ b/jstests/core/removeb.js @@ -30,7 +30,7 @@ p = startParallelShell( // Remove using the a:1 index in ascending direction. var res = t.remove( { a:{ $gte:0 } } ); -assert( !res.hasWriteErrors(), 'The remove operation failed.' ); +assert( !res.hasWriteError(), 'The remove operation failed.' ); p(); diff --git a/jstests/core/rename4.js b/jstests/core/rename4.js index d6ded8cbee4..85cd5e882b5 100644 --- a/jstests/core/rename4.js +++ b/jstests/core/rename4.js @@ -6,7 +6,7 @@ function bad( f ) { var res = eval( f ); //Ensure error - if (!res.hasWriteErrors()) { + if (!res.hasWriteError()) { print("Error:" + res.toString()); print("Existing docs (before)") printjson(docsBeforeUpdate); diff --git a/jstests/core/storefunc.js b/jstests/core/storefunc.js index b4cda6f52f0..3b5fc1ab9ab 100644 --- a/jstests/core/storefunc.js +++ b/jstests/core/storefunc.js @@ -7,7 +7,7 @@ s.remove({}); assert.eq( 0 , s.count() , "setup - A" ); res = s.save( { _id : "x" , value : "3" } ); -assert( !res.hasWriteErrors() , "setup - B" ); +assert( !res.hasWriteError() , "setup - B" ); assert.eq( 1 , s.count() , "setup - C" ); s.remove( { _id : "x" } ); diff --git a/jstests/core/update_dbref.js b/jstests/core/update_dbref.js index e978a4888c5..d4c9ed7354f 100644 --- a/jstests/core/update_dbref.js +++ b/jstests/core/update_dbref.js @@ -5,15 +5,15 @@ t = db.jstests_update_dbref; t.drop(); res = t.save({_id:1, a: new DBRef("a", "b")}); -assert(!res.hasWriteErrors(), "failed to save dbref"); +assert(!res.hasWriteError(), "failed to save dbref"); assert.docEq({_id:1, a: new DBRef("a", "b")}, t.findOne()); res = t.update({}, {$set: {"a.$id": 2}}); -assert(!res.hasWriteErrors(), "a.$id update"); +assert(!res.hasWriteError(), "a.$id update"); assert.docEq({_id:1, a: new DBRef("a", 2)}, t.findOne()); res = t.update({}, {$set: {"a.$ref": "b"}}); -assert(!res.hasWriteErrors(), "a.$ref update"); +assert(!res.hasWriteError(), "a.$ref update"); assert.docEq({_id:1, a: new DBRef("b", 2)}, t.findOne()); @@ -34,7 +34,7 @@ assert(/\$db/.test(res.getWriteError()), "expected bad update because of $db"); assert.docEq({_id:1, a: new DBRef("b", 2)}, t.findOne()); res = t.update({}, {$set: {"b.$id": 2}}); -assert(res.hasWriteErrors(), "b.$id update should fail -- doc:" + tojson(t.findOne()) + " result:" + res.toString()); +assert(res.hasWriteError(), "b.$id update should fail -- doc:" + tojson(t.findOne()) + " result:" + res.toString()); res = t.update({}, {$set: {"b.$ref": 2}}); -assert(res.hasWriteErrors(), "b.$ref update should fail -- doc:" + tojson(t.findOne()) + " result:" + res.toString()); +assert(res.hasWriteError(), "b.$ref update should fail -- doc:" + tojson(t.findOne()) + " result:" + res.toString()); diff --git a/jstests/core/update_replace.js b/jstests/core/update_replace.js index ff9312baa95..ebfe2a0bd6f 100644 --- a/jstests/core/update_replace.js +++ b/jstests/core/update_replace.js @@ -15,38 +15,38 @@ conn._skipValidation = true; // Should not allow "." in field names res = t.save({_id:1, "a.a":1}) -assert(res.hasWriteErrors(), "a.a"); +assert(res.hasWriteError(), "a.a"); // Should not allow "." in field names, embedded res = t.save({_id:1, a :{"a.a":1}}) -assert(res.hasWriteErrors(), "a: a.a"); +assert(res.hasWriteError(), "a: a.a"); // Should not allow "$"-prefixed field names, caught before "." check res = t.save({_id:1, $a :{"a.a":1}}) -assert(res.hasWriteErrors(), "$a: a.a"); +assert(res.hasWriteError(), "$a: a.a"); // Should not allow "$"-prefixed field names res = t.save({_id:1, $a: 1}) -assert(res.hasWriteErrors(), "$a"); +assert(res.hasWriteError(), "$a"); // _id validation checks // Should not allow regex _id res = t.save({_id: /a/}) -assert(res.hasWriteErrors(), "_id regex"); +assert(res.hasWriteError(), "_id regex"); // Should not allow regex _id, even if not first res = t.save({a:2, _id: /a/}) -assert(res.hasWriteErrors(), "a _id regex"); +assert(res.hasWriteError(), "a _id regex"); // Should not allow array _id res = t.save({_id: [9]}) -assert(res.hasWriteErrors(), "_id array"); +assert(res.hasWriteError(), "_id array"); // This is fine since _id isn't a top level field res = t.save({a :{ _id: [9]}}) -assert(!res.hasWriteErrors(), "embedded _id array"); +assert(!res.hasWriteError(), "embedded _id array"); // This is fine since _id isn't a top level field res = t.save({b:1, a :{ _id: [9]}}) -assert(!res.hasWriteErrors(), "b embedded _id array"); +assert(!res.hasWriteError(), "b embedded _id array"); diff --git a/jstests/core/update_setOnInsert.js b/jstests/core/update_setOnInsert.js index 105e3493bb7..9656ac9b48d 100644 --- a/jstests/core/update_setOnInsert.js +++ b/jstests/core/update_setOnInsert.js @@ -36,10 +36,10 @@ dotest( true ); t.drop(); res = t.update( {_id: 1} , { $setOnInsert: { "_id.a": new Date() } } , true ); -assert(res.hasWriteErrors(), "$setOnInsert _id.a - " + res.toString() + tojson(t.findOne())); +assert(res.hasWriteError(), "$setOnInsert _id.a - " + res.toString() + tojson(t.findOne())); res = t.update( {"_id.a": 4} , { $setOnInsert: { "_id.b": 1 } } , true ); -assert(res.hasWriteErrors(), "$setOnInsert _id.b - " + res.toString() + tojson(t.findOne())); +assert(res.hasWriteError(), "$setOnInsert _id.b - " + res.toString() + tojson(t.findOne())); res = t.update( {"_id.a": 4} , { $setOnInsert: { "_id": {a:4, b:1} } } , true ); -assert(res.hasWriteErrors(), "$setOnInsert _id 3 - " + res.toString() + tojson(t.findOne())); +assert(res.hasWriteError(), "$setOnInsert _id 3 - " + res.toString() + tojson(t.findOne())); diff --git a/jstests/core/updatel.js b/jstests/core/updatel.js index 7253872232c..4f7ba0cee7b 100644 --- a/jstests/core/updatel.js +++ b/jstests/core/updatel.js @@ -14,7 +14,7 @@ t.drop(); // The collection is empty, forcing an upsert. In this case the query has no array position match // to substiture for the positional operator. SERVER-4713 res = t.update( {}, { $set:{ 'a.$.b':1 } }, true ); -assert( res.hasWriteErrors(), "An error is reported." ); +assert( res.hasWriteError(), "An error is reported." ); assert.eq( 0, t.count(), "No upsert occurred." ); @@ -25,14 +25,14 @@ t.save( { _id:0 } ); // Now, with an existing document, trigger an update rather than an upsert. The query has no array // position match to substiture for the positional operator. SERVER-6669 res = t.update( {}, { $set:{ 'a.$.b':1 } } ); -assert( res.hasWriteErrors(), "An error is reported." ); +assert( res.hasWriteError(), "An error is reported." ); assert.eq( [ { _id:0 } ], t.find().toArray(), "No update occurred." ); // Now, try with an update by _id (without a query array match). res = t.update( { _id:0 }, { $set:{ 'a.$.b':1 } } ); -assert( res.hasWriteErrors(), "An error is reported." ); +assert( res.hasWriteError(), "An error is reported." ); assert.eq( [ { _id:0 } ], t.find().toArray(), "No update occurred." ); @@ -45,5 +45,5 @@ t.save( { _id:0, a:[ { b:{ c:1 } } ] } ); // query match for the first positional operator but not the second. Note that dollar sign // substitution for multiple positional opertors is not implemented (SERVER-831). res = t.update( { 'a.b.c':1 }, { $set:{ 'a.$.b.$.c':2 } } ); -assert( res.hasWriteErrors(), "An error is reported" ); +assert( res.hasWriteError(), "An error is reported" ); assert.eq( [ { _id:0, a:[ { b:{ c:1 } } ] } ], t.find().toArray(), "No update occurred." ); diff --git a/jstests/core/upsert1.js b/jstests/core/upsert1.js index 85999a189f5..f8c97e41c84 100644 --- a/jstests/core/upsert1.js +++ b/jstests/core/upsert1.js @@ -44,7 +44,7 @@ db.no_id.drop(); db.createCollection("no_id", {autoIndexId:false}) l = db.no_id.update({foo:1}, {$set:{a:1}}, true) assert( l.getUpsertedId() , "H1 - " + tojson(l) ); -assert( !l.hasWriteErrors(), "H1.5 No error expected - " + tojson(l) ) +assert( !l.hasWriteError(), "H1.5 No error expected - " + tojson(l) ) assert.eq( 0, db.no_id.getIndexes().length, "H2" ); assert.eq( 1, db.no_id.count(), "H3" ); var newDoc = db.no_id.findOne(); diff --git a/jstests/core/write_result.js b/jstests/core/write_result.js index 47f04f00b8c..c008c5ae404 100644 --- a/jstests/core/write_result.js +++ b/jstests/core/write_result.js @@ -143,7 +143,7 @@ coll.remove({}); printjson( result = coll.insert([{ foo : "bar" }, { foo : "baz" }]) ); assert.eq(result.nInserted, 2); assert(!result.hasWriteErrors()); -assert(!result.getWriteConcernError()); +assert(!result.hasWriteConcernError()); assert.eq(coll.count(), 2); // @@ -155,7 +155,7 @@ printjson( result = coll.insert([{ _id : id, foo : "bar" }, { _id : id, foo : "baz" }]) ); assert.eq(result.nInserted, 1); assert(result.hasWriteErrors()); -assert(!result.getWriteConcernError()); +assert(!result.hasWriteConcernError()); assert.eq(coll.count(), 1); // @@ -174,16 +174,11 @@ coll.unsetWriteConcern(); // Write concern error // NOTE: Non-throwing write concern failures require replication to trigger coll.remove({}); -coll.setWriteConcern({ w : "invalid" }); -assert.throws( function() { - printjson( coll.insert({ foo : "bar" }) ); -}); +assert.writeError( coll.insert({ foo : "bar" }, { writeConcern : { w : "invalid" } }) ); if (coll.getMongo().writeMode() == "commands") assert.eq(coll.count(), 0); else assert.eq(coll.count(), 1); -coll.unsetWriteConcern(); - diff --git a/jstests/replsets/bulk_api_wc.js b/jstests/replsets/bulk_api_wc.js index de6be1b8d4d..e05f01cead0 100644 --- a/jstests/replsets/bulk_api_wc.js +++ b/jstests/replsets/bulk_api_wc.js @@ -37,7 +37,7 @@ var executeTests = function() { bulk.insert({a:1}); bulk.insert({a:2}); bulk.insert({a:2}); - var result = bulk.execute({ w : 'invalid' }); + var result = assert.throws( function() { bulk.execute({ w : 'invalid' }); } ); assert.eq(result.nInserted, 2); assert.eq(result.getWriteErrors()[0].index, 2); assert(!result.getWriteConcernError()); @@ -54,7 +54,7 @@ var executeTests = function() { bulk.insert({a:1}); bulk.insert({a:2}); bulk.insert({a:2}); - var result = bulk.execute({ w : 'invalid' }); + var result = assert.throws( function(){ bulk.execute({ w : 'invalid' }); } ); assert.eq(result.nInserted, 2); assert.eq(result.getWriteErrors()[0].index, 2); assert(result.getWriteConcernError()); @@ -69,7 +69,7 @@ var executeTests = function() { bulk.insert({a:1}); bulk.insert({a:2}); bulk.insert({a:2}); - var result = bulk.execute({ w : 3, wtimeout : 1 }); + var result = assert.throws( function() { bulk.execute({ w : 3, wtimeout : 1 }); } ); assert.eq(result.nInserted, 2); assert.eq(result.getWriteErrors()[0].index, 2); assert(result.getWriteConcernError().errInfo.wtimeout); @@ -83,7 +83,7 @@ var executeTests = function() { bulk.insert({a:2}); bulk.find({a:3}).upsert().updateOne({a:3}); bulk.insert({a:3}); - var result = bulk.execute({ w : 'invalid' }); + var result = assert.throws( function(){ bulk.execute({ w : 'invalid' }); } ); assert.eq(result.nInserted, 2); assert.eq(result.nUpserted, 1); assert.eq(result.getUpsertedIds()[0].index, 2); diff --git a/jstests/replsets/localhostAuthBypass.js b/jstests/replsets/localhostAuthBypass.js index 6f00002a61b..720c77b4b00 100644 --- a/jstests/replsets/localhostAuthBypass.js +++ b/jstests/replsets/localhostAuthBypass.js @@ -20,18 +20,10 @@ var assertCannotRunCommands = function(mongo) { var test = mongo.getDB("test"); assert.throws( function() { test.system.users.findOne(); }); assert.throws( function() { test.foo.findOne({ _id: 0 }); }); - - assert.throws(function() { - test.foo.save({ _id: 0 }) - }); - - assert.throws(function() { - test.foo.update({ _id: 0 }, { $set: { x: 20 }}) - }); - - assert.throws(function() { - test.foo.remove({ _id: 0 }) - }); + + assert.writeError(test.foo.save({ _id: 0 })); + assert.writeError(test.foo.update({ _id: 0 }, { $set: { x: 20 }})); + assert.writeError(test.foo.remove({ _id: 0 })); assert.throws(function() { test.foo.mapReduce( diff --git a/jstests/replsets/replset7.js b/jstests/replsets/replset7.js index c6ee3d6f943..dec1832ad58 100644 --- a/jstests/replsets/replset7.js +++ b/jstests/replsets/replset7.js @@ -18,7 +18,7 @@ for( i = 0; i < doccount; ++i ) { } assert.writeOK(bulk.execute()); -assert.writeOK(mdc.ensureIndex( { x : 1 }, { unique: true } )); +assert.commandWorked(mdc.ensureIndex( { x : 1 }, { unique: true } )); // add a secondary var slave = rt.add(); diff --git a/jstests/shell_writeconcern.js b/jstests/shell_writeconcern.js index 74247026b31..e59bd471294 100644 --- a/jstests/shell_writeconcern.js +++ b/jstests/shell_writeconcern.js @@ -24,7 +24,7 @@ assert.eq(undefined, collB.getWriteConcern()) assert.eq(undefined, db.getWriteConcern()) // test methods, by generating an error -var res = assert.writeOK(collA.save({_id:1}, {writeConcern:{w:1}})); +var res = assert.gleOK(collA.save({_id:1}, {writeConcern:{w:1}})); if (!db.getMongo().useWriteCommands() ) { assert.eq(1, res.n, tojson(res)); assert.eq(1, res.upserted, tojson(res)); @@ -32,41 +32,41 @@ if (!db.getMongo().useWriteCommands() ) { assert.eq(1, res.nUpserted, tojson(res)); } -var res = assert.writeOK(collA.update({_id:1}, {_id:1}, {writeConcern:{w:1}})); +var res = assert.gleOK(collA.update({_id:1}, {_id:1}, {writeConcern:{w:1}})); if (!db.getMongo().useWriteCommands() ) { assert.eq(1, res.n, tojson(res)); } else { assert.eq(1, res.nMatched, tojson(res)); } -var res = assert.writeOK(collA.update({_id:1}, {_id:1}, {writeConcern:{w:1}})); +var res = assert.gleOK(collA.update({_id:1}, {_id:1}, {writeConcern:{w:1}})); if (!db.getMongo().useWriteCommands() ) { assert.eq(1, res.n, tojson(res)); } else { assert.eq(1, res.nMatched, tojson(res)); } -var res = assert.writeOK(collA.insert({_id:2}, {writeConcern:{w:1}})); +var res = assert.gleOK(collA.insert({_id:2}, {writeConcern:{w:1}})); if (!db.getMongo().useWriteCommands() ) { assert.eq(0, res.n, tojson(res)); } else { assert.eq(1, res.nInserted, tojson(res)); } -var res = assert.writeOK(collA.remove({_id:3}, {writeConcern:{w:1}})); +var res = assert.gleOK(collA.remove({_id:3}, {writeConcern:{w:1}})); if (!db.getMongo().useWriteCommands() ) { assert.eq(0, res.n, tojson(res)); } else { assert.eq(0, res.nRemoved, tojson(res)); } -var res = assert.writeOK(collA.remove({}, {justOne:true, writeConcern:{w:1}})); +var res = assert.gleOK(collA.remove({}, {justOne:true, writeConcern:{w:1}})); if (!db.getMongo().useWriteCommands() ) { assert.eq(1, res.n, tojson(res)); } else { assert.eq(1, res.nRemoved, tojson(res)); } -assert.writeError(collA.insert([{_id:1}, {_id:1}], {ordered:true, writeConcern:{w:1}})); -assert.writeError(collA.insert([{_id:1}, {_id:1}], {ordered:false, writeConcern:{w:1}})); +assert.gleError(collA.insert([{_id:1}, {_id:1}], {ordered:true, writeConcern:{w:1}})); +assert.gleError(collA.insert([{_id:1}, {_id:1}], {ordered:false, writeConcern:{w:1}})); diff --git a/src/mongo/shell/assert.js b/src/mongo/shell/assert.js index 25bc108febd..fcd7b4e59bb 100644 --- a/src/mongo/shell/assert.js +++ b/src/mongo/shell/assert.js @@ -343,70 +343,103 @@ assert.closeWithinMS = function(a, b, msg, deltaMS) { }; assert.writeOK = function(res, msg) { - var errMsg = ""; + + var errMsg = null; - if (!res) - errMsg = "missing first argument, no response to check" - else if (!res.getWriteConcernError) // not BulkWriteResult/SingleWriteResult. - assert.gleOK(res, msg) - else { + if (res instanceof WriteResult) { + if (res.hasWriteError()) { + errMsg = "write failed with error: " + tojson(res); + } + else if(res.hasWriteConcernError()) { + errMsg = "write concern failed with errors: " + tojson(res); + } + } + else if (res instanceof BulkWriteResult) { + // Can only happen with bulk inserts if (res.hasWriteErrors()) { - errMsg = "write failed with errors: " + tojson(res) - } else if (res.getWriteConcernError()) { - errMsg = "write concern failed with errors: " + tojson(res) + errMsg = "write failed with errors: " + tojson(res); + } + else if(res.hasWriteConcernError()) { + errMsg = "write concern failed with errors: " + tojson(res); } - - if (errMsg != "" && msg) - errMsg = errMsg + " : " + msg; - - if (errMsg) - doassert(errMsg); } - + else if (res instanceof WriteCommandError) { + // Can only happen with bulk inserts + errMsg = "write command failed: " + tojson(res); + } + else { + errMsg = "unknown type of write result, cannot check ok: " + + tojson(res); + } + + if (errMsg) { + if (msg) + errMsg = errMsg + ": " + msg; + doassert(errMsg); + } + return res; } -assert.gleOK = function(res, msg) { - var errMsg = ""; - - if (!res) - errMsg = "missing first argument, no response to check" - else if (!res.ok) - errMsg = "command failed: " + tojson(res); - - if ('code' in res || 'errMsg' in res || 'errInfo' in res || 'writeErrors' in res) - errMsg = "write failed: " + tojson(res); - - if (errMsg != "" && msg) - errMsg = errMsg + " : " + msg; +assert.writeError = function(res, msg) { + + var errMsg = null; - if (errMsg) + if (res instanceof WriteResult) { + if (!res.hasWriteError() && !res.hasWriteConcernError()) { + errMsg = "no write error: " + tojson(res); + } + } + 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) { + // Can only happen with bulk inserts + // No-op since we're expecting an error + } + else { + errMsg = "unknown type of write result, cannot check error: " + + tojson(res); + } + + if (errMsg) { + if (msg) + errMsg = errMsg + ": " + msg; doassert(errMsg); - + } + return res; } -assert.writeError = function(res, msg) { - var errMsg = ""; +assert.gleOK = function(res, msg) { + + var errMsg = null; - if (!res) - errMsg = "The response arg was missing or undefined! -- " + res - else if (!res.getWriteConcernError) { - if (!res.err) - errMsg = "no error" + tojson(res); - } else { - if (!(res.hasWriteErrors() || res.getWriteConcernError())) - errMsg = "no write errors : " + tojson(res); + if (!res) { + errMsg = "missing first argument, no response to check" + } + else if (!res.ok) { + errMsg = "getLastError failed: " + tojson(res); } - if (errMsg != "" && msg) - errMsg = errMsg + " : " + msg; - if (errMsg) + 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); + } + return res; } -assert.gleSuccess = function(db, msg) { - var gle = db.getLastErrorObj(); +assert.gleSuccess = function(dbOrGLEDoc, msg) { + var gle = dbOrGLEDoc instanceof DB ? dbOrGLEDoc.getLastErrorObj() : dbOrGLEDoc; if (gle.err) { if (typeof(msg) == "function") msg = msg(gle); @@ -415,8 +448,8 @@ assert.gleSuccess = function(db, msg) { return gle; } -assert.gleError = function(db, msg) { - var gle = db.getLastErrorObj(); +assert.gleError = function(dbOrGLEDoc, msg) { + var gle = dbOrGLEDoc instanceof DB ? dbOrGLEDoc.getLastErrorObj() : dbOrGLEDoc; if (!gle.err) { if (typeof(msg) == "function") msg = msg(gle); @@ -424,8 +457,8 @@ assert.gleError = function(db, msg) { } } -assert.gleErrorCode = function(db, code, msg) { - var gle = db.getLastErrorObj(); +assert.gleErrorCode = function(dbOrGLEDoc, code, msg) { + var gle = dbOrGLEDoc instanceof DB ? dbOrGLEDoc.getLastErrorObj() : dbOrGLEDoc; if (!gle.err || gle.code != code) { if (typeof(msg) == "function") msg = msg(gle); @@ -434,8 +467,8 @@ assert.gleErrorCode = function(db, code, msg) { } } -assert.gleErrorRegex = function(db, regex, msg) { - var gle = db.getLastErrorObj(); +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") msg = msg(gle); diff --git a/src/mongo/shell/bulk_api.js b/src/mongo/shell/bulk_api.js index 2dd2bb94ff2..c99f43e7868 100644 --- a/src/mongo/shell/bulk_api.js +++ b/src/mongo/shell/bulk_api.js @@ -2,6 +2,7 @@ // Scope for the function // var _bulk_api_module = (function() { + // Batch types var NONE = 0; var INSERT = 1; @@ -30,29 +31,84 @@ var _bulk_api_module = (function() { } /** - * getLastErrorMethod that supports all write concerns + * 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 executeGetLastError = function(db, options) { - var cmd = { getlasterror : 1 }; - options = options || {}; - - // Add write concern options to the command - if(typeof(options.w) != 'undefined') cmd.w = options.w; - if(typeof(options.wtimeout) != 'undefined') cmd.wtimeout = options.wtimeout; - if(options.j) cmd.j = options.j; - if(options.fsync) cmd.fsync = options.fsync; - - // Execute the getLastErrorCommand - return db.runCommand( cmd ); - }; + 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 0, not " + opts.wtimeout); + } + + if (typeof opts.j != 'undefined' && typeof opts.j != 'boolean') + throw Error("j value must either true or false if defined, not " + opts.j); + + this._wc = opts; + + this.toJSON = function() { + return Object.merge({}, this._wc); + }; + + /** + * @return {string} + */ + this.tojson = function(indent, nolint) { + return tojson(this.toJSON(), indent, nolint); + }; + + this.toString = function() { + return "WriteConcern(" + this.tojson() + ")"; + }; + + 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). - * singleBatch is passed in on bulk operations consisting of a single batch and - * are used to filter the SingleWriteResult to only include relevant result fields. + * 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 SingleWriteResult = function(bulkResult, singleBatch, writeConcern) { + 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); @@ -75,14 +131,18 @@ var _bulk_api_module = (function() { return bulkResult; }; - this.hasWriteErrors = function() { - return bulkResult.writeErrors.length > 0; - }; - this.getWriteError = function() { - return bulkResult.writeErrors[bulkResult.writeErrors.length - 1]; + if (bulkResult.writeErrors.length == 0) { + return null; + } else { + return bulkResult.writeErrors[bulkResult.writeErrors.length - 1]; + } }; + this.hasWriteError = function() { + return this.getWriteError() != null; + }; + this.getWriteConcernError = function() { if (bulkResult.writeConcernErrors.length == 0) { return null; @@ -90,6 +150,10 @@ var _bulk_api_module = (function() { return bulkResult.writeConcernErrors[0]; } }; + + this.hasWriteConcernError = function() { + return this.getWriteConcernError() != null; + }; /** * @return {string} @@ -97,24 +161,24 @@ var _bulk_api_module = (function() { this.tojson = function(indent, nolint) { var result = {} - if(singleBatch && singleBatch.batchType == INSERT) { + if(singleBatchType == INSERT) { result.nInserted = this.nInserted; } - if(singleBatch && singleBatch.batchType == UPDATE) { + if(singleBatchType == UPDATE) { result.nMatched = this.nMatched; result.nUpserted = this.nUpserted; - + if(this.nModified != undefined) result.nModified = this.nModified; - + if(Array.isArray(bulkResult.upserted) && bulkResult.upserted.length == 1) { result._id = bulkResult.upserted[0]._id; } } - if(singleBatch && singleBatch.batchType == REMOVE) { + if(singleBatchType == REMOVE) { result.nRemoved = bulkResult.nRemoved; } @@ -142,16 +206,16 @@ var _bulk_api_module = (function() { this.shellPrint = function() { return this.toString(); }; - - this.isOK = function() { - return bulkResult.ok == 1; - }; }; /** * Wraps the result for the commands */ - var BulkWriteResult = function(bulkResult, singleBatch, writeConcern) { + 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); @@ -195,6 +259,10 @@ var _bulk_api_module = (function() { return bulkResult.writeErrors; } + this.hasWriteConcernError = function() { + return bulkResult.writeConcernErrors.length > 0; + } + this.getWriteConcernError = function() { if(bulkResult.writeConcernErrors.length == 0) { return null; @@ -237,22 +305,137 @@ var _bulk_api_module = (function() { return this.toString(); } - this.isOK = function() { - return bulkResult.ok == 1; - }; + this.hasErrors = function() { + return this.hasWriteErrors() || this.hasWriteConcernError(); + } + + 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"; + } + + var hasWCError = this.hasWriteConcernError(); + if (numWriteErrors > 0 && hasWCError) { + message += " and "; + } + + 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 {SingleWriteResult} the simplified results condensed into one. + * @return {WriteResult} the simplified results condensed into one. */ this.toSingleResult = function() { - if(singleBatch == null) throw Error( - "Cannot output SingleWriteResult from multiple batch result"); - return new SingleWriteResult(bulkResult, singleBatch, writeConcern); + if(singleBatchType == null) throw Error( + "Cannot output single WriteResult from multiple batch result"); + return new WriteResult(bulkResult, singleBatchType, writeConcern); } }; /** - * Wraps the error + * 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); + + Error.captureStackTrace(this, this.constructor); + 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.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: [] + }; + } + + /** + * Wraps a command error + */ + var WriteCommandError = function(commandError) { + + if(!(this instanceof WriteCommandError)) return new WriteCommandError(commandError); + + // Define properties + defineReadOnlyProperty(this, "code", commandError.code); + defineReadOnlyProperty(this, "errmsg", commandError.errmsg); + + Error.captureStackTrace(this, this.constructor); + this.name = 'WriteCommandError'; + this.message = this.errmsg; + + /** + * @return {string} + */ + this.tojson = function(indent, nolint) { + return tojson(commandError, indent, nolint); + } + + this.toString = function() { + return "WriteCommandError(" + this.tojson() + ")"; + } + + this.shellPrint = function() { + return this.toString(); + } + + 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); @@ -329,18 +512,7 @@ var _bulk_api_module = (function() { this.index = index; this.operation = operation; } - - /*********************************************************** - * Adds the initializers of bulk operations to the db collection - ***********************************************************/ - DBCollection.prototype.initializeUnorderedBulkOp = function() { - return new Bulk(this, false); - } - - DBCollection.prototype.initializeOrderedBulkOp = function() { - return new Bulk(this, true); - } - + /*********************************************************** * Wraps the operations done for the batch ***********************************************************/ @@ -356,16 +528,7 @@ var _bulk_api_module = (function() { var currentOp; // Final results - var bulkResult = { - writeErrors: [] - , writeConcernErrors: [] - , nInserted: 0 - , nUpserted: 0 - , nMatched: 0 - , nModified: 0 - , nRemoved: 0 - , upserted: [] - }; + var bulkResult = getEmptyBulkResult(); // Current batch var currentBatch = null; @@ -584,17 +747,6 @@ var _bulk_api_module = (function() { // // Merge write command result into aggregated results object var mergeBatchResults = function(batch, bulkResult, result) { - // - // NEEDED to pass tests as some write errors are - // returned as write concern errors (j write on non journal mongod) - // also internal error code 75 is still making it out as a write concern error - // - if(ordered && result && result.writeConcernError - && (result.writeConcernError.code == 2 || result.writeConcernError.code == 75)) { - throw Error( - "legacy batch failed, cannot aggregate results: " - + result.writeConcernError.errmsg); - } // If we have an insert Batch type if(batch.batchType == INSERT) { @@ -658,7 +810,7 @@ var _bulk_api_module = (function() { bulkResult.writeConcernErrors.push(new WriteConcernError(result.writeConcernError)); } } - + // // Execute the batch var executeBatch = function(batch) { @@ -696,14 +848,13 @@ var _bulk_api_module = (function() { 0 /* flags */).next(); if(result.ok == 0) { - throw Error( - "batch failed, cannot aggregate results: " + result.errmsg); + 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 @@ -758,7 +909,7 @@ var _bulk_api_module = (function() { var code = gleResponse.code; var timeout = gleResponse.wtimeout? true : false; - var extractedErr = { writeError: null, wcError: null }; + 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. @@ -802,7 +953,11 @@ var _bulk_api_module = (function() { }; } else if (!isOK) { - throw Error('Unexpected error from getLastError: ' + tojson(gleResponse)); + // This is a GLE failure we don't understand + extractedErr.unknownError = { + code: code + , errmsg: errMsg + } } else if (err != '') { extractedErr.writeError = { @@ -821,6 +976,16 @@ var _bulk_api_module = (function() { 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) { @@ -830,7 +995,7 @@ var _bulk_api_module = (function() { , upserted: [] }; - var extractedError = null; + var extractedErr = null; var totalToExecute = batch.operations.length; // Run over all the operations @@ -842,14 +1007,22 @@ var _bulk_api_module = (function() { executeLegacyOp(_legacyOp); var result = executeGetLastError(collection.getDB(), { w: 1 }); - extractedError = extractGLEErrors(result); + extractedErr = extractGLEErrors(result); + + if (extractedErr.unknownError) { + throw new WriteCommandError({ + ok : 0.0 + , code : extractedErr.unknownError.code + , errmsg : extractedErr.unknownError.errmsg + }); + } - if (extractedError.writeError != null) { + if (extractedErr.writeError != null) { // Create the emulated result set var errResult = { index: _legacyOp.index - , code: extractedError.writeError.code - , errmsg: extractedError.writeError.errmsg + , code: extractedErr.writeError.code + , errmsg: extractedErr.writeError.errmsg , op: batch.operations[_legacyOp.index] }; @@ -881,6 +1054,7 @@ var _bulk_api_module = (function() { bsonWoCompare(writeConcern, { w: 1 }) != 0 && bsonWoCompare(writeConcern, { w: 0 }) != 0; + extractedErr = null; if (needToEnforceWC && (batchResult.writeErrors.length == 0 || (!ordered && @@ -897,11 +1071,16 @@ var _bulk_api_module = (function() { } result = executeGetLastError(collection.getDB(), writeConcern); - extractedError = extractGLEErrors(result); + extractedErr = extractGLEErrors(result); + + if (extractedErr.unknownError) { + // Report as a wc failure + extractedErr.wcError = extractedErr.unknownError; + } } - if (extractedError != null && extractedError.wcError != null) { - bulkResult.writeConcernErrors.push(extractedError.wcError); + if (extractedErr != null && extractedErr.wcError != null) { + bulkResult.writeConcernErrors.push(extractedErr.wcError); } // Merge the results @@ -930,7 +1109,7 @@ var _bulk_api_module = (function() { for(var i = 0; i < batches.length; i++) { // Execute the batch - if(collection.getMongo().hasWriteCommands() && + if(collection.getMongo().hasWriteCommands() && collection.getMongo().writeMode() == "commands") { executeBatch(batches[i]); } else { @@ -949,84 +1128,50 @@ var _bulk_api_module = (function() { // Set as executed executed = true; - if(batches.length == 1) { - return new BulkWriteResult(bulkResult, batches[0], writeConcern); + // 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(); } - // Execute the batch and return the final results - return new BulkWriteResult(bulkResult, null, writeConcern); + return typedResult; } } -})(); - -if ( ( typeof WriteConcern ) == 'undefined' ){ - /** - * Shell representation of WriteConcern, possibly includes: - * j: write waits for journal - * w: write waits for 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 - */ - WriteConcern = function(wValue, wTimeout, jValue) { - - var opts = {}; - if (typeof wValue == 'object') { - if (typeof jValue == 'undefined' && typeof wTimeout == 'undefined') - 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; - } + // + // 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); + }; - // 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 0, not " + opts.wtimeout); - } - - if (typeof opts.j != 'undefined' && typeof opts.j != 'boolean') - throw Error("j value must either true or false if defined, not " + opts.j); - - this._wc = opts; - }; + return module; - /** - * @return {object} the object representation of this object. Use tojson (small caps) to get - * the string representation instead. - */ - WriteConcern.prototype.toJSON = function() { - return Object.merge({}, this._wc); - }; +})(); - /** - * @return {string} the string representation of this object. Use toJSON (capitalized) to get - * the object representation instead. - */ - WriteConcern.prototype.tojson = function(indent, nolint) { - return tojson(this.toJSON(), indent, nolint); - }; +// Globals +WriteConcern = _bulk_api_module.WriteConcern; +WriteResult = _bulk_api_module.WriteResult; +BulkWriteResult = _bulk_api_module.BulkWriteResult; +BulkWriteError = _bulk_api_module.BulkWriteError; +WriteCommandError = _bulk_api_module.WriteCommandError; - WriteConcern.prototype.toString = function() { - return "WriteConcern(" + this.tojson() + ")"; - }; +/*********************************************************** + * Adds the initializers of bulk operations to the db collection + ***********************************************************/ +DBCollection.prototype.initializeUnorderedBulkOp = _bulk_api_module.initializeUnorderedBulkOp; +DBCollection.prototype.initializeOrderedBulkOp = _bulk_api_module.initializeOrderedBulkOp; - WriteConcern.prototype.shellPrint = function() { - return this.toString(); - }; -} diff --git a/src/mongo/shell/collection.js b/src/mongo/shell/collection.js index f112325744c..dc6248a5111 100644 --- a/src/mongo/shell/collection.js +++ b/src/mongo/shell/collection.js @@ -229,20 +229,35 @@ DBCollection.prototype.insert = function( obj , options, _allow_dot ){ if ( this.getMongo().writeMode() != "legacy" ) { // Bit 1 of option flag is continueOnError. Bit 0 (stop on error) is the default. - var batch = ordered ? this.initializeOrderedBulkOp() : this.initializeUnorderedBulkOp(); + var bulk = ordered ? this.initializeOrderedBulkOp() : this.initializeUnorderedBulkOp(); + var isMultiInsert = Array.isArray(obj); - if (Array.isArray(obj)) { + if (isMultiInsert) { obj.forEach(function(doc) { - batch.insert(doc); + bulk.insert(doc); }); - // Do not return a SingleWriteResult if inserting an array - result = batch.execute(wc); } else { - batch.insert(obj); - result = batch.execute(wc).toSingleResult(); + bulk.insert(obj); } + try { + result = bulk.execute(wc); + if (!isMultiInsert) + result = result.toSingleResult(); + } + catch( ex ) { + if ( ex instanceof BulkWriteError ) { + result = isMultiInsert ? ex.toResult() : ex.toSingleResult(); + } + else if ( ex instanceof WriteCommandError ) { + result = isMultiInsert ? ex : ex.toSingleResult(); + } + else { + // Other exceptions thrown + throw ex; + } + } } else { if ( ! _allow_dot ) { @@ -292,14 +307,14 @@ DBCollection.prototype.remove = function( t , justOne ){ wc = opts.writeConcern; justOne = opts.justOne; } - + if (!wc) wc = this.getWriteConcern(); if ( this.getMongo().writeMode() != "legacy" ) { var query = (typeof(t) == 'undefined')? {} : this._massageObject(t); - var batch = this.initializeOrderedBulkOp(); - var removeOp = batch.find(query); + var bulk = this.initializeOrderedBulkOp(); + var removeOp = bulk.find(query); if (justOne) { removeOp.removeOne(); @@ -308,7 +323,18 @@ DBCollection.prototype.remove = function( t , justOne ){ removeOp.remove(); } - result = batch.execute(wc).toSingleResult(); + try { + result = bulk.execute(wc).toSingleResult(); + } + catch( ex ) { + if ( ex instanceof BulkWriteError || ex instanceof WriteCommandError ) { + result = ex.toSingleResult(); + } + else { + // Other exceptions thrown + throw ex; + } + } } else { this._validateRemoveDoc(t); @@ -364,8 +390,8 @@ DBCollection.prototype.update = function( query , obj , upsert , multi ){ wc = this.getWriteConcern(); if ( this.getMongo().writeMode() != "legacy" ) { - var batch = this.initializeOrderedBulkOp(); - var updateOp = batch.find(query); + var bulk = this.initializeOrderedBulkOp(); + var updateOp = bulk.find(query); if (upsert) { updateOp = updateOp.upsert(); @@ -378,7 +404,18 @@ DBCollection.prototype.update = function( query , obj , upsert , multi ){ updateOp.updateOne(obj); } - result = batch.execute(wc).toSingleResult(); + try { + result = bulk.execute(wc).toSingleResult(); + } + catch( ex ) { + if ( ex instanceof BulkWriteError || ex instanceof WriteCommandError ) { + result = ex.toSingleResult(); + } + else { + // Other exceptions thrown + throw ex; + } + } } else { this._validateUpdateDoc(obj); |