diff options
author | Jonathan Abrahams <jonathan@mongodb.com> | 2016-09-12 09:29:08 -0400 |
---|---|---|
committer | Jonathan Abrahams <jonathan@mongodb.com> | 2016-09-12 09:30:58 -0400 |
commit | 58b888b983ba0b07ff7a6e70710206fbd114a58a (patch) | |
tree | 458702522a5f780edd922252e568073fdff42f1a | |
parent | 0365ec133c8261cd0ef361475d77e6ac1bcb7962 (diff) | |
download | mongo-58b888b983ba0b07ff7a6e70710206fbd114a58a.tar.gz |
SERVER-25324 Expose a function in the shell to compare BSON at a byte level
(cherry picked from commit 01bfdcb8f35f6cf81b9b471be21500c84d5cb660)
-rw-r--r-- | jstests/core/bson.js | 142 | ||||
-rw-r--r-- | src/mongo/scripting/mozjs/bson.cpp | 22 | ||||
-rw-r--r-- | src/mongo/scripting/mozjs/bson.h | 3 |
3 files changed, 163 insertions, 4 deletions
diff --git a/jstests/core/bson.js b/jstests/core/bson.js new file mode 100644 index 00000000000..3e8c53be20d --- /dev/null +++ b/jstests/core/bson.js @@ -0,0 +1,142 @@ +/** + * This tests mongo shell functions bsonWoCompare & bsonBinaryEqual. + */ +(function() { + 'use strict'; + + function testObjectsAreEqual(obj1, obj2, equalityFunc, func_name) { + var assert_msg = func_name + " " + tojson(obj1) + " " + tojson(obj2); + assert(equalityFunc(obj1, obj2), assert_msg); + } + + function testObjectsAreNotEqual(obj1, obj2, equalityFunc, func_name) { + var assert_msg = func_name + " " + tojson(obj1) + " " + tojson(obj2); + assert(!equalityFunc(obj1, obj2), assert_msg); + } + + function runTests(func, testFunc) { + // Tests on numbers. + testObjectsAreEqual({a: 0}, {a: 0}, func, testFunc); + testObjectsAreEqual({a: -5}, {a: -5}, func, testFunc); + testObjectsAreEqual({a: 1}, {a: 1.0}, func, testFunc); + testObjectsAreEqual({a: 1.1}, {a: 1.1}, func, testFunc); + testObjectsAreEqual({a: 1.1}, {a: 1.10}, func, testFunc); + var nl0 = new NumberLong("18014398509481984"); + var nl1 = new NumberLong("18014398509481985"); + testObjectsAreEqual({a: nl0}, {a: nl0}, func, testFunc); + testObjectsAreNotEqual({a: nl0}, {a: nl1}, func, testFunc); + + // Test on key name. + testObjectsAreNotEqual({a: 0}, {A: 0}, func, testFunc); + + // Tests on strings. + testObjectsAreEqual({a: "abc"}, {a: "abc"}, func, testFunc); + testObjectsAreNotEqual({a: "abc"}, {a: "aBc"}, func, testFunc); + + // Tests on boolean. + testObjectsAreEqual({a: true}, {a: true}, func, testFunc); + testObjectsAreNotEqual({a: true}, {a: false}, func, testFunc); + testObjectsAreEqual({a: false}, {a: false}, func, testFunc); + + // Tests on date & timestamp. + var d0 = new Date(0); + var d1 = new Date(1); + var ts0 = new Timestamp(0, 1); + var ts1 = new Timestamp(1, 1); + testObjectsAreEqual({a: d0}, {a: d0}, func, testFunc); + testObjectsAreNotEqual({a: d1}, {a: d0}, func, testFunc); + testObjectsAreNotEqual({a: d1}, {a: ts1}, func, testFunc); + testObjectsAreEqual({a: ts0}, {a: ts0}, func, testFunc); + testObjectsAreNotEqual({a: ts0}, {a: ts1}, func, testFunc); + + // Tests on regex. + testObjectsAreEqual({a: /3/}, {a: /3/}, func, testFunc); + testObjectsAreNotEqual({a: /3/}, {a: /3/i}, func, testFunc); + + // Tests on DBPointer. + var dbp0 = new DBPointer("test", new ObjectId()); + var dbp1 = new DBPointer("test", new ObjectId()); + testObjectsAreEqual({a: dbp0}, {a: dbp0}, func, testFunc); + testObjectsAreNotEqual({a: dbp0}, {a: dbp1}, func, testFunc); + + // Tests on JavaScript. + var js0 = Function.prototype; + var js1 = function() {}; + testObjectsAreEqual({a: js0}, {a: Function.prototype}, func, testFunc); + testObjectsAreNotEqual({a: js0}, {a: js1}, func, testFunc); + + // Tests on arrays. + testObjectsAreEqual({a: [0, 1]}, {a: [0, 1]}, func, testFunc); + testObjectsAreNotEqual({a: [0, 1]}, {a: [0]}, func, testFunc); + testObjectsAreNotEqual({a: [1, 0]}, {a: [0, 1]}, func, testFunc); + + // Tests on BinData & HexData. + testObjectsAreEqual({a: new BinData(0, "JANgqwetkqwklEWRbWERKKJREtbq")}, + {a: new BinData(0, "JANgqwetkqwklEWRbWERKKJREtbq")}, + func, + testFunc); + testObjectsAreEqual( + {a: new BinData(0, "AAaa")}, {a: new BinData(0, "AAaa")}, func, testFunc); + testObjectsAreNotEqual( + {a: new BinData(0, "AAaa")}, {a: new BinData(0, "aaAA")}, func, testFunc); + testObjectsAreEqual( + {a: new HexData(0, "AAaa")}, {a: new HexData(0, "AAaa")}, func, testFunc); + testObjectsAreEqual( + {a: new HexData(0, "AAaa")}, {a: new HexData(0, "aaAA")}, func, testFunc); + testObjectsAreNotEqual( + {a: new HexData(0, "AAaa")}, {a: new BinData(0, "AAaa")}, func, testFunc); + + // Tests on ObjectId + testObjectsAreEqual({a: new ObjectId("57d1b31cd311a43091fe592f")}, + {a: new ObjectId("57d1b31cd311a43091fe592f")}, + func, + testFunc); + testObjectsAreNotEqual({a: new ObjectId("57d1b31cd311a43091fe592f")}, + {a: new ObjectId("57d1b31ed311a43091fe5930")}, + func, + testFunc); + + // Tests on miscellaneous types. + testObjectsAreEqual({a: NaN}, {a: NaN}, func, testFunc); + testObjectsAreEqual({a: null}, {a: null}, func, testFunc); + testObjectsAreNotEqual({a: null}, {a: -null}, func, testFunc); + testObjectsAreEqual({a: undefined}, {a: undefined}, func, testFunc); + testObjectsAreNotEqual({a: undefined}, {a: null}, func, testFunc); + testObjectsAreEqual({a: MinKey}, {a: MinKey}, func, testFunc); + testObjectsAreEqual({a: MaxKey}, {a: MaxKey}, func, testFunc); + testObjectsAreNotEqual({a: MinKey}, {a: MaxKey}, func, testFunc); + + // Test on object ordering. + testObjectsAreNotEqual({a: 1, b: 2}, {b: 2, a: 1}, func, testFunc); + } + + // Create wrapper function for bsonWoCompare, such that it returns boolean result. + var bsonWoCompareWrapper = function(obj1, obj2) { + return bsonWoCompare(obj1, obj2) === 0; + }; + + // Run the tests which work the same for both comparators. + runTests(bsonWoCompareWrapper, "bsonWoCompare"); + runTests(bsonBinaryEqual, "bsonBinaryEqual"); + + // Run the tests which differ between comparators. + testObjectsAreEqual({a: NaN}, {a: -NaN}, bsonWoCompareWrapper, "bsonWoCompare"); + testObjectsAreNotEqual({a: NaN}, {a: -NaN}, bsonBinaryEqual, "bsonBinaryEqual"); + testObjectsAreEqual({a: 1}, {a: NumberLong("1")}, bsonWoCompareWrapper, "bsonWoCompare"); + testObjectsAreNotEqual({a: 1}, {a: NumberLong("1")}, bsonBinaryEqual, "bsonBinaryEqual"); + testObjectsAreEqual({a: 1.0}, {a: NumberLong("1")}, bsonWoCompareWrapper, "bsonWoCompare"); + testObjectsAreNotEqual({a: 1.0}, {a: NumberLong("1")}, bsonBinaryEqual, "bsonBinaryEqual"); + testObjectsAreEqual( + {a: NumberInt("1")}, {a: NumberLong("1")}, bsonWoCompareWrapper, "bsonWoCompare"); + testObjectsAreNotEqual( + {a: NumberInt("1")}, {a: NumberLong("1")}, bsonBinaryEqual, "bsonBinaryEqual"); + testObjectsAreEqual( + {a: NumberInt("1")}, {a: NumberDecimal("1.0")}, bsonWoCompareWrapper, "bsonWoCompare"); + testObjectsAreNotEqual( + {a: NumberInt("1")}, {a: NumberDecimal("1.0")}, bsonBinaryEqual, "bsonBinaryEqual"); + testObjectsAreEqual( + {a: NumberLong("1")}, {a: NumberDecimal("1.0")}, bsonWoCompareWrapper, "bsonWoCompare"); + testObjectsAreNotEqual( + {a: NumberLong("1")}, {a: NumberDecimal("1.0")}, bsonBinaryEqual, "bsonBinaryEqual"); + +})();
\ No newline at end of file diff --git a/src/mongo/scripting/mozjs/bson.cpp b/src/mongo/scripting/mozjs/bson.cpp index 0cb60fb6117..1e7e4b7c190 100644 --- a/src/mongo/scripting/mozjs/bson.cpp +++ b/src/mongo/scripting/mozjs/bson.cpp @@ -46,8 +46,8 @@ namespace mozjs { const char* const BSONInfo::className = "BSON"; -const JSFunctionSpec BSONInfo::freeFunctions[2] = { - MONGO_ATTACH_JS_FUNCTION(bsonWoCompare), JS_FS_END, +const JSFunctionSpec BSONInfo::freeFunctions[3] = { + MONGO_ATTACH_JS_FUNCTION(bsonWoCompare), MONGO_ATTACH_JS_FUNCTION(bsonBinaryEqual), JS_FS_END, }; namespace { @@ -237,7 +237,7 @@ std::tuple<BSONObj*, bool> BSONInfo::originalBSON(JSContext* cx, JS::HandleObjec void BSONInfo::Functions::bsonWoCompare::call(JSContext* cx, JS::CallArgs args) { if (args.length() != 2) - uasserted(ErrorCodes::BadValue, "bsonWoCompare needs 2 argument"); + uasserted(ErrorCodes::BadValue, "bsonWoCompare needs 2 arguments"); if (!args.get(0).isObject()) uasserted(ErrorCodes::BadValue, "first argument to bsonWoCompare must be an object"); @@ -251,6 +251,22 @@ void BSONInfo::Functions::bsonWoCompare::call(JSContext* cx, JS::CallArgs args) args.rval().setInt32(firstObject.woCompare(secondObject)); } +void BSONInfo::Functions::bsonBinaryEqual::call(JSContext* cx, JS::CallArgs args) { + if (args.length() != 2) + uasserted(ErrorCodes::BadValue, "bsonBinaryEqual needs 2 arguments"); + + if (!args.get(0).isObject()) + uasserted(ErrorCodes::BadValue, "first argument to bsonBinaryEqual must be an object"); + + if (!args.get(1).isObject()) + uasserted(ErrorCodes::BadValue, "second argument to bsonBinaryEqual must be an object"); + + BSONObj firstObject = ValueWriter(cx, args.get(0)).toBSON(); + BSONObj secondObject = ValueWriter(cx, args.get(1)).toBSON(); + + args.rval().setBoolean(firstObject.binaryEqual(secondObject)); +} + void BSONInfo::postInstall(JSContext* cx, JS::HandleObject global, JS::HandleObject proto) { JS::RootedValue value(cx); value.setBoolean(true); diff --git a/src/mongo/scripting/mozjs/bson.h b/src/mongo/scripting/mozjs/bson.h index d341f0a856b..4c9ffcdf6bb 100644 --- a/src/mongo/scripting/mozjs/bson.h +++ b/src/mongo/scripting/mozjs/bson.h @@ -64,9 +64,10 @@ struct BSONInfo : public BaseInfo { struct Functions { MONGO_DECLARE_JS_FUNCTION(bsonWoCompare); + MONGO_DECLARE_JS_FUNCTION(bsonBinaryEqual); }; - static const JSFunctionSpec freeFunctions[2]; + static const JSFunctionSpec freeFunctions[3]; static std::tuple<BSONObj*, bool> originalBSON(JSContext* cx, JS::HandleObject obj); static void make( |