summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Abrahams <jonathan@mongodb.com>2016-09-12 09:29:08 -0400
committerJonathan Abrahams <jonathan@mongodb.com>2016-09-12 09:30:58 -0400
commit58b888b983ba0b07ff7a6e70710206fbd114a58a (patch)
tree458702522a5f780edd922252e568073fdff42f1a
parent0365ec133c8261cd0ef361475d77e6ac1bcb7962 (diff)
downloadmongo-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.js142
-rw-r--r--src/mongo/scripting/mozjs/bson.cpp22
-rw-r--r--src/mongo/scripting/mozjs/bson.h3
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(