diff options
author | Benety Goh <benety@mongodb.com> | 2013-12-09 16:00:09 -0500 |
---|---|---|
committer | Benety Goh <benety@mongodb.com> | 2013-12-10 11:04:22 -0500 |
commit | 1772eef330e7ed06700b2d1560f40147412fb685 (patch) | |
tree | ee0b64344b27dcda772d0958eaae181e2c3ebd37 /src/mongo | |
parent | 976bbf60bfa42d412cfdd59d791a3d5ef36e5d4e (diff) | |
download | mongo-1772eef330e7ed06700b2d1560f40147412fb685.tar.gz |
SERVER-11986 return failure on invalid positional $ projection
Diffstat (limited to 'src/mongo')
-rw-r--r-- | src/mongo/db/exec/SConscript | 15 | ||||
-rw-r--r-- | src/mongo/db/exec/projection_exec.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/exec/projection_exec_test.cpp | 141 |
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 |