summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorBenety Goh <benety@mongodb.com>2013-12-09 16:00:09 -0500
committerBenety Goh <benety@mongodb.com>2013-12-10 11:04:22 -0500
commit1772eef330e7ed06700b2d1560f40147412fb685 (patch)
treeee0b64344b27dcda772d0958eaae181e2c3ebd37 /src/mongo
parent976bbf60bfa42d412cfdd59d791a3d5ef36e5d4e (diff)
downloadmongo-1772eef330e7ed06700b2d1560f40147412fb685.tar.gz
SERVER-11986 return failure on invalid positional $ projection
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/db/exec/SConscript15
-rw-r--r--src/mongo/db/exec/projection_exec.cpp5
-rw-r--r--src/mongo/db/exec/projection_exec_test.cpp141
3 files changed, 160 insertions, 1 deletions
diff --git a/src/mongo/db/exec/SConscript b/src/mongo/db/exec/SConscript
index f3eadfe0b10..33c3487642e 100644
--- a/src/mongo/db/exec/SConscript
+++ b/src/mongo/db/exec/SConscript
@@ -77,3 +77,18 @@ env.CppUnitTest(
],
NO_CRUTCH = True,
)
+
+env.CppUnitTest(
+ target = "projection_exec_test",
+ source = [
+ "projection_exec_test.cpp",
+ ],
+ LIBDEPS = [
+ "exec",
+ "mock_stage",
+ "$BUILD_DIR/mongo/serveronly",
+ "$BUILD_DIR/mongo/coreserver",
+ "$BUILD_DIR/mongo/coredb",
+ ],
+ NO_CRUTCH = True,
+)
diff --git a/src/mongo/db/exec/projection_exec.cpp b/src/mongo/db/exec/projection_exec.cpp
index 269a1104fb0..862c6461184 100644
--- a/src/mongo/db/exec/projection_exec.cpp
+++ b/src/mongo/db/exec/projection_exec.cpp
@@ -343,7 +343,10 @@ namespace mongo {
// Case 2: no array projection for this field.
Matchers::const_iterator matcher = _matchers.find(elt.fieldName());
if (_matchers.end() == matcher) {
- append(bob, elt, details, arrayOpType);
+ Status s = append(bob, elt, details, arrayOpType);
+ if (!s.isOK()) {
+ return s;
+ }
continue;
}
diff --git a/src/mongo/db/exec/projection_exec_test.cpp b/src/mongo/db/exec/projection_exec_test.cpp
new file mode 100644
index 00000000000..bf2309a37af
--- /dev/null
+++ b/src/mongo/db/exec/projection_exec_test.cpp
@@ -0,0 +1,141 @@
+/**
+ * Copyright (C) 2013 mongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+/**
+ * This file contains tests for mongo/db/exec/projection_exec.cpp
+ */
+
+#include "mongo/db/exec/projection_exec.h"
+
+#include <sstream>
+#include <memory>
+#include "mongo/db/json.h"
+#include "mongo/db/matcher/expression_parser.h"
+#include "mongo/unittest/unittest.h"
+
+using namespace mongo;
+
+namespace {
+
+ using std::auto_ptr;
+ using std::stringstream;
+
+ /**
+ * Utility function to create MatchExpression
+ */
+ MatchExpression* parseMatchExpression(const BSONObj& obj) {
+ StatusWithMatchExpression status = MatchExpressionParser::parse(obj);
+ ASSERT_TRUE(status.isOK());
+ MatchExpression* expr(status.getValue());
+ return expr;
+ }
+
+ //
+ // transform tests
+ //
+
+ /**
+ * test function to verify results of transform()
+ * on a working set member.
+ *
+ * specStr - projection specification
+ * queryStr - query
+ * objStr - object to run projection on
+ * expectedStatusOK - expected status of transformation
+ * expectedObjStr - expected object after successful projection.
+ * Ignored if expectedStatusOK is false.
+ */
+
+ void testTransform(const char* specStr, const char* queryStr, const char* objStr,
+ bool expectedStatusOK, const char* expectedObjStr) {
+ // Create projection exec object.
+ BSONObj spec = fromjson(specStr);
+ BSONObj query = fromjson(queryStr);
+ auto_ptr<MatchExpression> queryExpression(parseMatchExpression(query));
+ ProjectionExec exec(spec, queryExpression.get());
+
+ // Create working set member.
+ WorkingSetMember wsm;
+ wsm.state = WorkingSetMember::OWNED_OBJ;
+ wsm.obj = fromjson(objStr);
+
+ // Transform object
+ Status status = exec.transform(&wsm);
+
+ // There are fewer checks to perform if we are expected a failed status.
+ if (!expectedStatusOK) {
+ if (status.isOK()) {
+ stringstream ss;
+ ss << "expected transform() to fail but got success instead."
+ << "\nprojection spec: " << specStr
+ << "\nquery: " << queryStr
+ << "\nobject before projection: " << objStr;
+ FAIL(ss.str());
+ }
+ return;
+ }
+
+ // If we are expecting a successful transformation but got a failed status instead,
+ // print out status message in assertion message.
+ if (!status.isOK()) {
+ stringstream ss;
+ ss << "transform() test failed: unexpected failed status: " << status.toString()
+ << "\nprojection spec: " << specStr
+ << "\nquery: " << queryStr
+ << "\nobject before projection: " << objStr
+ << "\nexpected object after projection: " << expectedObjStr;
+ FAIL(ss.str());
+ }
+
+ // Finally, we compare the projected object.
+ const BSONObj& obj = wsm.obj;
+ BSONObj expectedObj = fromjson(expectedObjStr);
+ if (obj != expectedObj) {
+ stringstream ss;
+ ss << "transform() test failed: unexpected projected object."
+ << "\nprojection spec: " << specStr
+ << "\nquery: " << queryStr
+ << "\nobject before projection: " << objStr
+ << "\nexpected object after projection: " << expectedObjStr
+ << "\nactual object after projection: " << obj.toString();
+ FAIL(ss.str());
+ }
+ }
+
+ TEST(ProjectionExecTest, TransformPositionalDollar) {
+ // Valid position $ projections.
+ testTransform("{'a.$': 1}", "{a: 10}", "{a: [10, 20, 30]}", true, "{a: [10]}");
+ testTransform("{'a.$': 1}", "{a: 20}", "{a: [10, 20, 30]}", true, "{a: [20]}");
+ testTransform("{'a.$': 1}", "{a: 30}", "{a: [10, 20, 30]}", true, "{a: [30]}");
+ testTransform("{'a.$': 1}", "{a: {$gt: 4}}", "{a: [5]}", true, "{a: [5]}");
+
+ // Invalid position $ projections.
+ testTransform("{'a.$': 1}", "{a: {$size: 1}}", "{a: [5]}", false, "");
+ }
+
+} // namespace