summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenety Goh <benety@mongodb.com>2014-04-10 12:26:03 -0400
committerDan Pasette <dan@mongodb.com>2014-04-16 10:14:15 -0400
commitb39c5ee1eaf1507e7f9149330520e01d5c060340 (patch)
treef588fb92b0482c19db4f1380fdba3126e914b12d
parentf0e41e169c8b38578c774af0b9cf38dd79e5ad23 (diff)
downloadmongo-b39c5ee1eaf1507e7f9149330520e01d5c060340.tar.gz
SERVER-13503 disallow $where under $elemMatch value
(cherry picked from commit 5aac10481a602c2ca3ce1d24a0c97e3602e33814)
-rw-r--r--jstests/core/where1.js11
-rw-r--r--src/mongo/db/matcher/expression_parser.cpp39
2 files changed, 49 insertions, 1 deletions
diff --git a/jstests/core/where1.js b/jstests/core/where1.js
index 7ff20a53620..6e3d693b996 100644
--- a/jstests/core/where1.js
+++ b/jstests/core/where1.js
@@ -26,3 +26,14 @@ assert.throws( function() {
assert.throws( function() {
t.find( { a: 3, "b.c": { $where : "this.a;" } } ).itcount();
} );
+
+// SERVER-13503
+assert.throws( function() {
+ t.find( { a: { $elemMatch : { $where : "this.a;", b : 1 } } } ).itcount();
+} );
+assert.throws( function() {
+ t.find( { a: { $elemMatch : { b : 1, $where : "this.a;" } } } ).itcount();
+} );
+assert.throws( function() {
+ t.find( { a: { $elemMatch : { $and : [ { b : 1 }, { $where : "this.a;" } ] } } } ).itcount();
+} );
diff --git a/src/mongo/db/matcher/expression_parser.cpp b/src/mongo/db/matcher/expression_parser.cpp
index bfc363ceda1..e97165b59fa 100644
--- a/src/mongo/db/matcher/expression_parser.cpp
+++ b/src/mongo/db/matcher/expression_parser.cpp
@@ -41,6 +41,27 @@
#include "mongo/util/log.h"
#include "mongo/util/mongoutils/str.h"
+namespace {
+
+ using namespace mongo;
+
+ /**
+ * Returns true if subtree contains MatchExpression 'type'.
+ */
+ bool hasNode(const MatchExpression* root, MatchExpression::MatchType type) {
+ if (type == root->matchType()) {
+ return true;
+ }
+ for (size_t i = 0; i < root->numChildren(); ++i) {
+ if (hasNode(root->getChild(i), type)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+} // namespace
+
namespace mongo {
StatusWithMatchExpression MatchExpressionParser::_parseComparison( const char* name,
@@ -80,6 +101,12 @@ namespace mongo {
int x = e.getGtLtOp(-1);
switch ( x ) {
case -1:
+ // $where cannot be a sub-expression because it works on top-level documents only.
+ if ( mongoutils::str::equals( "$where", e.fieldName() ) ) {
+ return StatusWithMatchExpression( ErrorCodes::BadValue,
+ "$where cannot be applied to a field" );
+ }
+
return StatusWithMatchExpression( ErrorCodes::BadValue,
mongoutils::str::stream() << "unknown operator: "
<< e.fieldName() );
@@ -619,6 +646,8 @@ namespace mongo {
// 1) the argument is an expression document; and
// 2) expression is not a AND/NOR/OR logical operator. Children of
// these logical operators are initialized with field names.
+ // 3) expression is not a WHERE operator. WHERE works on objects instead
+ // of specific field.
bool isElemMatchValue = false;
if ( _isExpressionDocument( e, true ) ) {
BSONObj o = e.Obj();
@@ -627,7 +656,8 @@ namespace mongo {
isElemMatchValue = !mongoutils::str::equals( "$and", elt.fieldName() ) &&
!mongoutils::str::equals( "$nor", elt.fieldName() ) &&
- !mongoutils::str::equals( "$or", elt.fieldName() );
+ !mongoutils::str::equals( "$or", elt.fieldName() ) &&
+ !mongoutils::str::equals( "$where", elt.fieldName() );
}
if ( isElemMatchValue ) {
@@ -661,6 +691,13 @@ namespace mongo {
if ( !sub.isOK() )
return sub;
+ // $where is not supported under $elemMatch because $where
+ // applies to top-level document, not array elements in a field.
+ if ( hasNode( sub.getValue(), MatchExpression::WHERE ) ) {
+ return StatusWithMatchExpression( ErrorCodes::BadValue,
+ "$elemMatch cannot contain $where expression" );
+ }
+
std::auto_ptr<ElemMatchObjectMatchExpression> temp( new ElemMatchObjectMatchExpression() );
Status status = temp->init( name, sub.getValue() );
if ( !status.isOK() )