summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTess Avitabile <tess.avitabile@mongodb.com>2017-10-10 13:28:31 -0400
committerTess Avitabile <tess.avitabile@mongodb.com>2017-10-11 14:07:30 -0400
commit0287fb01445a7372469b1ac1486f7ede25ab6bf7 (patch)
tree289707075e9cc5584d5c0cfbdf1755ccc6db3d07
parent8c35aac5f109639d6b6d942db100acc0f709cd10 (diff)
downloadmongo-0287fb01445a7372469b1ac1486f7ede25ab6bf7.tar.gz
SERVER-31408 Test that all code paths that support expr support throwing at runtime
-rw-r--r--jstests/aggregation/sources/graphLookup/error.js21
-rw-r--r--jstests/core/expr.js107
-rw-r--r--jstests/core/list_collections_filter.js5
-rw-r--r--jstests/core/list_databases.js3
4 files changed, 108 insertions, 28 deletions
diff --git a/jstests/aggregation/sources/graphLookup/error.js b/jstests/aggregation/sources/graphLookup/error.js
index 62cec4ce87f..d05313200ca 100644
--- a/jstests/aggregation/sources/graphLookup/error.js
+++ b/jstests/aggregation/sources/graphLookup/error.js
@@ -11,7 +11,7 @@ load("jstests/aggregation/extras/utils.js"); // For "assertErrorCode".
var local = db.local;
local.drop();
- local.insert({});
+ assert.writeOK(local.insert({b: 0}));
var pipeline = {$graphLookup: 4};
assertErrorCode(
@@ -295,6 +295,11 @@ load("jstests/aggregation/extras/utils.js"); // For "assertErrorCode".
};
assertErrorCode(local, pipeline, 40186, "cannot use $near inside $graphLookup at any depth");
+ let foreign = db.foreign;
+ foreign.drop();
+ assert.writeOK(foreign.insert({a: 0, x: 0}));
+
+ // Test a restrictSearchWithMatch expression that fails to parse.
pipeline = {
$graphLookup: {
from: 'foreign',
@@ -307,8 +312,20 @@ load("jstests/aggregation/extras/utils.js"); // For "assertErrorCode".
};
assertErrorCode(local, pipeline, 40186, "cannot use $expr with unbound variable");
+ // Test a restrictSearchWithMatchExpression that throws at runtime.
+ pipeline = {
+ $graphLookup: {
+ from: 'foreign',
+ startWith: {$literal: 0},
+ connectToField: "a",
+ connectFromField: "b",
+ as: "output",
+ restrictSearchWithMatch: {$expr: {$divide: [1, "$x"]}}
+ }
+ };
+ assertErrorCode(local, pipeline, 16608, "division by zero in $expr");
+
// $graphLookup can only consume at most 100MB of memory.
- var foreign = db.foreign;
foreign.drop();
// Here, the visited set exceeds 100MB.
diff --git a/jstests/core/expr.js b/jstests/core/expr.js
index 63ffea5e7e4..dbd00d0c948 100644
--- a/jstests/core/expr.js
+++ b/jstests/core/expr.js
@@ -13,33 +13,42 @@
//
coll.drop();
- assert.writeOK(coll.insert({a: 5}));
- assert.eq(1, coll.aggregate([{$match: {$expr: {$eq: ["$a", 5]}}}]).itcount());
+ assert.writeOK(coll.insert({a: 0}));
+ assert.eq(1, coll.aggregate([{$match: {$expr: {$eq: ["$a", 0]}}}]).itcount());
assert.throws(function() {
coll.aggregate([{$match: {$expr: {$eq: ["$a", "$$unbound"]}}}]);
});
+ assert.throws(function() {
+ coll.aggregate([{$match: {$expr: {$divide: [1, "$a"]}}}]);
+ });
//
// $expr in count.
//
coll.drop();
- assert.writeOK(coll.insert({a: 5}));
- assert.eq(1, coll.find({$expr: {$eq: ["$a", 5]}}).count());
+ assert.writeOK(coll.insert({a: 0}));
+ assert.eq(1, coll.find({$expr: {$eq: ["$a", 0]}}).count());
assert.throws(function() {
coll.find({$expr: {$eq: ["$a", "$$unbound"]}}).count();
});
+ assert.throws(function() {
+ coll.find({$expr: {$divide: [1, "$a"]}}).count();
+ });
//
// $expr in distinct.
//
coll.drop();
- assert.writeOK(coll.insert({a: 5}));
- assert.eq(1, coll.distinct("a", {$expr: {$eq: ["$a", 5]}}).length);
+ assert.writeOK(coll.insert({a: 0}));
+ assert.eq(1, coll.distinct("a", {$expr: {$eq: ["$a", 0]}}).length);
assert.throws(function() {
coll.distinct("a", {$expr: {$eq: ["$a", "$$unbound"]}});
});
+ assert.throws(function() {
+ coll.distinct("a", {$expr: {$divide: [1, "$a"]}});
+ });
//
// $expr in find.
@@ -47,22 +56,32 @@
// $expr is allowed in query.
coll.drop();
- assert.writeOK(coll.insert({a: 5}));
- assert.eq(1, coll.find({$expr: {$eq: ["$a", 5]}}).itcount());
+ assert.writeOK(coll.insert({a: 0}));
+ assert.eq(1, coll.find({$expr: {$eq: ["$a", 0]}}).itcount());
// $expr with unbound variable throws.
assert.throws(function() {
coll.find({$expr: {$eq: ["$a", "$$unbound"]}}).itcount();
});
+ // $expr with division by zero throws.
+ assert.throws(function() {
+ coll.find({$expr: {$divide: [1, "$a"]}}).itcount();
+ });
+
// $expr is allowed in find with explain.
- assert.commandWorked(coll.find({$expr: {$eq: ["$a", 5]}}).explain());
+ assert.commandWorked(coll.find({$expr: {$eq: ["$a", 0]}}).explain());
// $expr with unbound variable in find with explain throws.
assert.throws(function() {
coll.find({$expr: {$eq: ["$a", "$$unbound"]}}).explain();
});
+ // $expr with division by zero in find with explain with executionStats throws.
+ assert.throws(function() {
+ coll.find({$expr: {$divide: [1, "$a"]}}).explain("executionStats");
+ });
+
// $expr is not allowed in $elemMatch projection.
coll.drop();
assert.writeOK(coll.insert({a: [{b: 5}]}));
@@ -76,10 +95,10 @@
// $expr is allowed in the query when upsert=false.
coll.drop();
- assert.writeOK(coll.insert({_id: 0, a: 5}));
- assert.eq({_id: 0, a: 5, b: 6},
+ assert.writeOK(coll.insert({_id: 0, a: 0}));
+ assert.eq({_id: 0, a: 0, b: 6},
coll.findAndModify(
- {query: {_id: 0, $expr: {$eq: ["$a", 5]}}, update: {$set: {b: 6}}, new: true}));
+ {query: {_id: 0, $expr: {$eq: ["$a", 0]}}, update: {$set: {b: 6}}, new: true}));
// $expr with unbound variable throws.
assert.throws(function() {
@@ -87,12 +106,17 @@
{query: {_id: 0, $expr: {$eq: ["$a", "$$unbound"]}}, update: {$set: {b: 6}}});
});
+ // $expr with division by zero throws.
+ assert.throws(function() {
+ coll.findAndModify({query: {_id: 0, $expr: {$divide: [1, "$a"]}}, update: {$set: {b: 6}}});
+ });
+
// $expr is not allowed in the query when upsert=true.
coll.drop();
- assert.writeOK(coll.insert({_id: 0, a: 5}));
+ assert.writeOK(coll.insert({_id: 0, a: 0}));
assert.throws(function() {
coll.findAndModify(
- {query: {_id: 0, $expr: {$eq: ["$a", 5]}}, update: {$set: {b: 6}}, upsert: true});
+ {query: {_id: 0, $expr: {$eq: ["$a", 0]}}, update: {$set: {b: 6}}, upsert: true});
});
// $expr is not allowed in $pull filter.
@@ -120,7 +144,7 @@
//
coll.drop();
- assert.writeOK(coll.insert({geo: {type: "Point", coordinates: [0, 0]}, a: 5}));
+ assert.writeOK(coll.insert({geo: {type: "Point", coordinates: [0, 0]}, a: 0}));
assert.commandWorked(coll.ensureIndex({geo: "2dsphere"}));
assert.eq(1,
assert
@@ -128,7 +152,7 @@
geoNear: coll.getName(),
near: {type: "Point", coordinates: [0, 0]},
spherical: true,
- query: {$expr: {$eq: ["$a", 5]}}
+ query: {$expr: {$eq: ["$a", 0]}}
}))
.results.length);
assert.commandFailed(db.runCommand({
@@ -137,6 +161,12 @@
spherical: true,
query: {$expr: {$eq: ["$a", "$$unbound"]}}
}));
+ assert.commandFailed(db.runCommand({
+ geoNear: coll.getName(),
+ near: {type: "Point", coordinates: [0, 0]},
+ spherical: true,
+ query: {$expr: {$divide: [1, "$a"]}}
+ }));
//
// $expr in group.
@@ -145,9 +175,9 @@
// The group command is not permitted in sharded collections.
if (!isMongos) {
coll.drop();
- assert.writeOK(coll.insert({a: 5}));
- assert.eq([{a: 5, count: 1}], coll.group({
- cond: {$expr: {$eq: ["$a", 5]}},
+ assert.writeOK(coll.insert({a: 0}));
+ assert.eq([{a: 0, count: 1}], coll.group({
+ cond: {$expr: {$eq: ["$a", 0]}},
key: {a: 1},
initial: {count: 0},
reduce: function(curr, result) {
@@ -164,6 +194,16 @@
}
});
});
+ assert.throws(function() {
+ coll.group({
+ cond: {$expr: {$divide: [1, "$a"]}},
+ key: {a: 1},
+ initial: {count: 0},
+ reduce: function(curr, result) {
+ result.count += 1;
+ }
+ });
+ });
}
//
@@ -171,7 +211,7 @@
//
coll.drop();
- assert.writeOK(coll.insert({a: 5}));
+ assert.writeOK(coll.insert({a: 0}));
let mapReduceOut = coll.mapReduce(
function() {
emit(this.a, 1);
@@ -179,7 +219,7 @@
function(key, values) {
return Array.sum(values);
},
- {out: {inline: 1}, query: {$expr: {$eq: ["$a", 5]}}});
+ {out: {inline: 1}, query: {$expr: {$eq: ["$a", 0]}}});
assert.commandWorked(mapReduceOut);
assert.eq(mapReduceOut.results.length, 1, tojson(mapReduceOut));
assert.throws(function() {
@@ -192,17 +232,29 @@
},
{out: {inline: 1}, query: {$expr: {$eq: ["$a", "$$unbound"]}}});
});
+ assert.throws(function() {
+ coll.mapReduce(
+ function() {
+ emit(this.a, 1);
+ },
+ function(key, values) {
+ return Array.sum(values);
+ },
+ {out: {inline: 1}, query: {$expr: {$divide: [1, "$a"]}}});
+ });
//
// $expr in remove.
//
coll.drop();
- assert.writeOK(coll.insert({_id: 0, a: 5}));
- let writeRes = coll.remove({_id: 0, $expr: {$eq: ["$a", 5]}});
+ assert.writeOK(coll.insert({_id: 0, a: 0}));
+ let writeRes = coll.remove({_id: 0, $expr: {$eq: ["$a", 0]}});
assert.writeOK(writeRes);
assert.eq(1, writeRes.nRemoved);
assert.writeError(coll.remove({_id: 0, $expr: {$eq: ["$a", "$$unbound"]}}));
+ assert.writeOK(coll.insert({_id: 0, a: 0}));
+ assert.writeError(coll.remove({_id: 0, $expr: {$divide: [1, "$a"]}}));
// Any writes preceding the write that fails to parse are executed.
coll.drop();
@@ -222,13 +274,16 @@
// $expr is allowed in the query when upsert=false.
coll.drop();
- assert.writeOK(coll.insert({_id: 0, a: 5}));
- assert.writeOK(coll.update({_id: 0, $expr: {$eq: ["$a", 5]}}, {$set: {b: 6}}));
- assert.eq({_id: 0, a: 5, b: 6}, coll.findOne({_id: 0}));
+ assert.writeOK(coll.insert({_id: 0, a: 0}));
+ assert.writeOK(coll.update({_id: 0, $expr: {$eq: ["$a", 0]}}, {$set: {b: 6}}));
+ assert.eq({_id: 0, a: 0, b: 6}, coll.findOne({_id: 0}));
// $expr with unbound variable fails.
assert.writeError(coll.update({_id: 0, $expr: {$eq: ["$a", "$$unbound"]}}, {$set: {b: 6}}));
+ // $expr with division by zero fails.
+ assert.writeError(coll.update({_id: 0, $expr: {$divide: [1, "$a"]}}, {$set: {b: 6}}));
+
// $expr is not allowed in the query when upsert=true.
coll.drop();
assert.writeOK(coll.insert({_id: 0, a: 5}));
diff --git a/jstests/core/list_collections_filter.js b/jstests/core/list_collections_filter.js
index defd5c62bc0..76feeb9dd4b 100644
--- a/jstests/core/list_collections_filter.js
+++ b/jstests/core/list_collections_filter.js
@@ -92,6 +92,11 @@
mydb.getCollectionInfos({$expr: {$eq: ["$name", "$$unbound"]}});
});
+ // Filter with $expr with a runtime error.
+ assert.throws(function() {
+ mydb.getCollectionInfos({$expr: {$abs: "$name"}});
+ });
+
// No extensions are allowed in filters.
assert.throws(function() {
mydb.getCollectionInfos({$text: {$search: "str"}});
diff --git a/jstests/core/list_databases.js b/jstests/core/list_databases.js
index a52cfec656b..d98fc8f68c4 100644
--- a/jstests/core/list_databases.js
+++ b/jstests/core/list_databases.js
@@ -72,6 +72,9 @@
assert.commandFailed(
db.adminCommand({listDatabases: 1, filter: {$expr: {$eq: ["$name", "$$unbound"]}}}));
+ // $expr with a filter that throws at runtime.
+ assert.commandFailed(db.adminCommand({listDatabases: 1, filter: {$expr: {$abs: "$name"}}}));
+
// No extensions are allowed in filters.
assert.commandFailed(db.adminCommand({listDatabases: 1, filter: {$text: {$search: "str"}}}));
assert.commandFailed(db.adminCommand({