diff options
Diffstat (limited to 'src/mongo/db/query/parsed_projection_test.cpp')
-rw-r--r-- | src/mongo/db/query/parsed_projection_test.cpp | 354 |
1 files changed, 176 insertions, 178 deletions
diff --git a/src/mongo/db/query/parsed_projection_test.cpp b/src/mongo/db/query/parsed_projection_test.cpp index c669490f35a..9128575ea95 100644 --- a/src/mongo/db/query/parsed_projection_test.cpp +++ b/src/mongo/db/query/parsed_projection_test.cpp @@ -35,182 +35,180 @@ namespace { - using std::unique_ptr; - using std::string; - using std::vector; - - using namespace mongo; - - // - // creation function - // - - unique_ptr<ParsedProjection> createParsedProjection(const BSONObj& query, const BSONObj& projObj) { - StatusWithMatchExpression swme = MatchExpressionParser::parse(query); - ASSERT(swme.isOK()); - std::unique_ptr<MatchExpression> queryMatchExpr(swme.getValue()); - ParsedProjection* out = NULL; - Status status = ParsedProjection::make(projObj, queryMatchExpr.get(), &out); - if (!status.isOK()) { - FAIL(mongoutils::str::stream() << "failed to parse projection " << projObj - << " (query: " << query << "): " << status.toString()); - } - ASSERT(out); - return unique_ptr<ParsedProjection>(out); +using std::unique_ptr; +using std::string; +using std::vector; + +using namespace mongo; + +// +// creation function +// + +unique_ptr<ParsedProjection> createParsedProjection(const BSONObj& query, const BSONObj& projObj) { + StatusWithMatchExpression swme = MatchExpressionParser::parse(query); + ASSERT(swme.isOK()); + std::unique_ptr<MatchExpression> queryMatchExpr(swme.getValue()); + ParsedProjection* out = NULL; + Status status = ParsedProjection::make(projObj, queryMatchExpr.get(), &out); + if (!status.isOK()) { + FAIL(mongoutils::str::stream() << "failed to parse projection " << projObj + << " (query: " << query << "): " << status.toString()); } - - unique_ptr<ParsedProjection> createParsedProjection(const char* queryStr, const char* projStr) { - BSONObj query = fromjson(queryStr); - BSONObj projObj = fromjson(projStr); - return createParsedProjection(query, projObj); - } - - // - // Failure to create a parsed projection is expected - // - - void assertInvalidProjection(const char* queryStr, const char* projStr) { - BSONObj query = fromjson(queryStr); - BSONObj projObj = fromjson(projStr); - StatusWithMatchExpression swme = MatchExpressionParser::parse(query); - ASSERT(swme.isOK()); - std::unique_ptr<MatchExpression> queryMatchExpr(swme.getValue()); - ParsedProjection* out = NULL; - Status status = ParsedProjection::make(projObj, queryMatchExpr.get(), &out); - std::unique_ptr<ParsedProjection> destroy(out); - ASSERT(!status.isOK()); - } - - // canonical_query.cpp will invoke ParsedProjection::make only when - // the projection spec is non-empty. This test case is included for - // completeness and do not reflect actual usage. - TEST(ParsedProjectionTest, MakeId) { - unique_ptr<ParsedProjection> parsedProj(createParsedProjection("{}", "{}")); - ASSERT(parsedProj->requiresDocument()); - } - - TEST(ParsedProjectionTest, MakeEmpty) { - unique_ptr<ParsedProjection> parsedProj(createParsedProjection("{}", "{_id: 0}")); - ASSERT(parsedProj->requiresDocument()); - } - - TEST(ParsedProjectionTest, MakeSingleField) { - unique_ptr<ParsedProjection> parsedProj(createParsedProjection("{}", "{a: 1}")); - ASSERT(!parsedProj->requiresDocument()); - const vector<string>& fields = parsedProj->getRequiredFields(); - ASSERT_EQUALS(fields.size(), 2U); - ASSERT_EQUALS(fields[0], "_id"); - ASSERT_EQUALS(fields[1], "a"); - } - - TEST(ParsedProjectionTest, MakeSingleFieldCovered) { - unique_ptr<ParsedProjection> parsedProj(createParsedProjection("{}", "{_id: 0, a: 1}")); - ASSERT(!parsedProj->requiresDocument()); - const vector<string>& fields = parsedProj->getRequiredFields(); - ASSERT_EQUALS(fields.size(), 1U); - ASSERT_EQUALS(fields[0], "a"); - } - - TEST(ParsedProjectionTest, MakeSingleFieldIDCovered) { - unique_ptr<ParsedProjection> parsedProj(createParsedProjection("{}", "{_id: 1}")); - ASSERT(!parsedProj->requiresDocument()); - const vector<string>& fields = parsedProj->getRequiredFields(); - ASSERT_EQUALS(fields.size(), 1U); - ASSERT_EQUALS(fields[0], "_id"); - } - - // boolean support is undocumented - TEST(ParsedProjectionTest, MakeSingleFieldCoveredBoolean) { - unique_ptr<ParsedProjection> parsedProj(createParsedProjection("{}", "{_id: 0, a: true}")); - ASSERT(!parsedProj->requiresDocument()); - const vector<string>& fields = parsedProj->getRequiredFields(); - ASSERT_EQUALS(fields.size(), 1U); - ASSERT_EQUALS(fields[0], "a"); - } - - // boolean support is undocumented - TEST(ParsedProjectionTest, MakeSingleFieldCoveredIdBoolean) { - unique_ptr<ParsedProjection> parsedProj(createParsedProjection("{}", "{_id: false, a: 1}")); - ASSERT(!parsedProj->requiresDocument()); - const vector<string>& fields = parsedProj->getRequiredFields(); - ASSERT_EQUALS(fields.size(), 1U); - ASSERT_EQUALS(fields[0], "a"); - } - - // - // Positional operator validation - // - - TEST(ParsedProjectionTest, InvalidPositionalOperatorProjections) { - assertInvalidProjection("{}", "{'a.$': 1}"); - assertInvalidProjection("{a: 1}", "{'b.$': 1}"); - assertInvalidProjection("{a: 1}", "{'a.$': 0}"); - assertInvalidProjection("{a: 1}", "{'a.$.d.$': 1}"); - assertInvalidProjection("{a: 1}", "{'a.$.$': 1}"); - assertInvalidProjection("{a: 1}", "{'a.$.$': 1}"); - assertInvalidProjection("{a: 1, b: 1, c: 1}", "{'abc.$': 1}"); - assertInvalidProjection("{$or: [{a: 1}, {$or: [{b: 1}, {c: 1}]}]}", "{'d.$': 1}"); - assertInvalidProjection("{a: [1, 2, 3]}", "{'.$': 1}"); - } - - TEST(ParsedProjectionTest, ValidPositionalOperatorProjections) { - createParsedProjection("{a: 1}", "{'a.$': 1}"); - createParsedProjection("{a: 1}", "{'a.foo.bar.$': 1}"); - createParsedProjection("{a: 1}", "{'a.foo.bar.$.x.y': 1}"); - createParsedProjection("{'a.b.c': 1}", "{'a.b.c.$': 1}"); - createParsedProjection("{'a.b.c': 1}", "{'a.e.f.$': 1}"); - createParsedProjection("{a: {b: 1}}", "{'a.$': 1}"); - createParsedProjection("{a: 1, b: 1}}", "{'a.$': 1}"); - createParsedProjection("{a: 1, b: 1}}", "{'b.$': 1}"); - createParsedProjection("{$and: [{a: 1}, {b: 1}]}", "{'a.$': 1}"); - createParsedProjection("{$and: [{a: 1}, {b: 1}]}", "{'b.$': 1}"); - createParsedProjection("{$or: [{a: 1}, {b: 1}]}", "{'a.$': 1}"); - createParsedProjection("{$or: [{a: 1}, {b: 1}]}", "{'b.$': 1}"); - createParsedProjection("{$and: [{$or: [{a: 1}, {$and: [{b: 1}, {c: 1}]}]}]}", - "{'c.d.f.$': 1}"); - // Fields with empty name can be projected using the positional $ operator. - createParsedProjection("{'': [1, 2, 3]}", "{'.$': 1}"); - } - - // Some match expressions (eg. $where) do not override MatchExpression::path() - // In this test case, we use an internal match expression implementation ALWAYS_FALSE - // to achieve the same effect. - // Projection parser should handle this the same way as an empty path. - TEST(ParsedProjectionTest, InvalidPositionalProjectionDefaultPathMatchExpression) { - unique_ptr<MatchExpression> queryMatchExpr(new FalseMatchExpression()); - ASSERT(NULL == queryMatchExpr->path().rawData()); - - ParsedProjection* out = NULL; - BSONObj projObj = fromjson("{'a.$': 1}"); - Status status = ParsedProjection::make(projObj, queryMatchExpr.get(), &out); - ASSERT(!status.isOK()); - std::unique_ptr<ParsedProjection> destroy(out); - - // Projecting onto empty field should fail. - BSONObj emptyFieldProjObj = fromjson("{'.$': 1}"); - status = ParsedProjection::make(emptyFieldProjObj, queryMatchExpr.get(), &out); - ASSERT(!status.isOK()); - } - - // - // DBRef projections - // - - TEST(ParsedProjectionTest, DBRefProjections) { - // non-dotted - createParsedProjection(BSONObj(), BSON( "$ref" << 1)); - createParsedProjection(BSONObj(), BSON( "$id" << 1)); - createParsedProjection(BSONObj(), BSON( "$ref" << 1)); - // dotted before - createParsedProjection("{}", "{'a.$ref': 1}"); - createParsedProjection("{}", "{'a.$id': 1}"); - createParsedProjection("{}", "{'a.$db': 1}"); - // dotted after - createParsedProjection("{}", "{'$id.a': 1}"); - // position operator on $id - // $ref and $db hold the collection and database names respectively, - // so these fields cannot be arrays. - createParsedProjection("{'a.$id': {$elemMatch: {x: 1}}}", "{'a.$id.$': 1}"); - - } -} // unnamed namespace + ASSERT(out); + return unique_ptr<ParsedProjection>(out); +} + +unique_ptr<ParsedProjection> createParsedProjection(const char* queryStr, const char* projStr) { + BSONObj query = fromjson(queryStr); + BSONObj projObj = fromjson(projStr); + return createParsedProjection(query, projObj); +} + +// +// Failure to create a parsed projection is expected +// + +void assertInvalidProjection(const char* queryStr, const char* projStr) { + BSONObj query = fromjson(queryStr); + BSONObj projObj = fromjson(projStr); + StatusWithMatchExpression swme = MatchExpressionParser::parse(query); + ASSERT(swme.isOK()); + std::unique_ptr<MatchExpression> queryMatchExpr(swme.getValue()); + ParsedProjection* out = NULL; + Status status = ParsedProjection::make(projObj, queryMatchExpr.get(), &out); + std::unique_ptr<ParsedProjection> destroy(out); + ASSERT(!status.isOK()); +} + +// canonical_query.cpp will invoke ParsedProjection::make only when +// the projection spec is non-empty. This test case is included for +// completeness and do not reflect actual usage. +TEST(ParsedProjectionTest, MakeId) { + unique_ptr<ParsedProjection> parsedProj(createParsedProjection("{}", "{}")); + ASSERT(parsedProj->requiresDocument()); +} + +TEST(ParsedProjectionTest, MakeEmpty) { + unique_ptr<ParsedProjection> parsedProj(createParsedProjection("{}", "{_id: 0}")); + ASSERT(parsedProj->requiresDocument()); +} + +TEST(ParsedProjectionTest, MakeSingleField) { + unique_ptr<ParsedProjection> parsedProj(createParsedProjection("{}", "{a: 1}")); + ASSERT(!parsedProj->requiresDocument()); + const vector<string>& fields = parsedProj->getRequiredFields(); + ASSERT_EQUALS(fields.size(), 2U); + ASSERT_EQUALS(fields[0], "_id"); + ASSERT_EQUALS(fields[1], "a"); +} + +TEST(ParsedProjectionTest, MakeSingleFieldCovered) { + unique_ptr<ParsedProjection> parsedProj(createParsedProjection("{}", "{_id: 0, a: 1}")); + ASSERT(!parsedProj->requiresDocument()); + const vector<string>& fields = parsedProj->getRequiredFields(); + ASSERT_EQUALS(fields.size(), 1U); + ASSERT_EQUALS(fields[0], "a"); +} + +TEST(ParsedProjectionTest, MakeSingleFieldIDCovered) { + unique_ptr<ParsedProjection> parsedProj(createParsedProjection("{}", "{_id: 1}")); + ASSERT(!parsedProj->requiresDocument()); + const vector<string>& fields = parsedProj->getRequiredFields(); + ASSERT_EQUALS(fields.size(), 1U); + ASSERT_EQUALS(fields[0], "_id"); +} + +// boolean support is undocumented +TEST(ParsedProjectionTest, MakeSingleFieldCoveredBoolean) { + unique_ptr<ParsedProjection> parsedProj(createParsedProjection("{}", "{_id: 0, a: true}")); + ASSERT(!parsedProj->requiresDocument()); + const vector<string>& fields = parsedProj->getRequiredFields(); + ASSERT_EQUALS(fields.size(), 1U); + ASSERT_EQUALS(fields[0], "a"); +} + +// boolean support is undocumented +TEST(ParsedProjectionTest, MakeSingleFieldCoveredIdBoolean) { + unique_ptr<ParsedProjection> parsedProj(createParsedProjection("{}", "{_id: false, a: 1}")); + ASSERT(!parsedProj->requiresDocument()); + const vector<string>& fields = parsedProj->getRequiredFields(); + ASSERT_EQUALS(fields.size(), 1U); + ASSERT_EQUALS(fields[0], "a"); +} + +// +// Positional operator validation +// + +TEST(ParsedProjectionTest, InvalidPositionalOperatorProjections) { + assertInvalidProjection("{}", "{'a.$': 1}"); + assertInvalidProjection("{a: 1}", "{'b.$': 1}"); + assertInvalidProjection("{a: 1}", "{'a.$': 0}"); + assertInvalidProjection("{a: 1}", "{'a.$.d.$': 1}"); + assertInvalidProjection("{a: 1}", "{'a.$.$': 1}"); + assertInvalidProjection("{a: 1}", "{'a.$.$': 1}"); + assertInvalidProjection("{a: 1, b: 1, c: 1}", "{'abc.$': 1}"); + assertInvalidProjection("{$or: [{a: 1}, {$or: [{b: 1}, {c: 1}]}]}", "{'d.$': 1}"); + assertInvalidProjection("{a: [1, 2, 3]}", "{'.$': 1}"); +} + +TEST(ParsedProjectionTest, ValidPositionalOperatorProjections) { + createParsedProjection("{a: 1}", "{'a.$': 1}"); + createParsedProjection("{a: 1}", "{'a.foo.bar.$': 1}"); + createParsedProjection("{a: 1}", "{'a.foo.bar.$.x.y': 1}"); + createParsedProjection("{'a.b.c': 1}", "{'a.b.c.$': 1}"); + createParsedProjection("{'a.b.c': 1}", "{'a.e.f.$': 1}"); + createParsedProjection("{a: {b: 1}}", "{'a.$': 1}"); + createParsedProjection("{a: 1, b: 1}}", "{'a.$': 1}"); + createParsedProjection("{a: 1, b: 1}}", "{'b.$': 1}"); + createParsedProjection("{$and: [{a: 1}, {b: 1}]}", "{'a.$': 1}"); + createParsedProjection("{$and: [{a: 1}, {b: 1}]}", "{'b.$': 1}"); + createParsedProjection("{$or: [{a: 1}, {b: 1}]}", "{'a.$': 1}"); + createParsedProjection("{$or: [{a: 1}, {b: 1}]}", "{'b.$': 1}"); + createParsedProjection("{$and: [{$or: [{a: 1}, {$and: [{b: 1}, {c: 1}]}]}]}", "{'c.d.f.$': 1}"); + // Fields with empty name can be projected using the positional $ operator. + createParsedProjection("{'': [1, 2, 3]}", "{'.$': 1}"); +} + +// Some match expressions (eg. $where) do not override MatchExpression::path() +// In this test case, we use an internal match expression implementation ALWAYS_FALSE +// to achieve the same effect. +// Projection parser should handle this the same way as an empty path. +TEST(ParsedProjectionTest, InvalidPositionalProjectionDefaultPathMatchExpression) { + unique_ptr<MatchExpression> queryMatchExpr(new FalseMatchExpression()); + ASSERT(NULL == queryMatchExpr->path().rawData()); + + ParsedProjection* out = NULL; + BSONObj projObj = fromjson("{'a.$': 1}"); + Status status = ParsedProjection::make(projObj, queryMatchExpr.get(), &out); + ASSERT(!status.isOK()); + std::unique_ptr<ParsedProjection> destroy(out); + + // Projecting onto empty field should fail. + BSONObj emptyFieldProjObj = fromjson("{'.$': 1}"); + status = ParsedProjection::make(emptyFieldProjObj, queryMatchExpr.get(), &out); + ASSERT(!status.isOK()); +} + +// +// DBRef projections +// + +TEST(ParsedProjectionTest, DBRefProjections) { + // non-dotted + createParsedProjection(BSONObj(), BSON("$ref" << 1)); + createParsedProjection(BSONObj(), BSON("$id" << 1)); + createParsedProjection(BSONObj(), BSON("$ref" << 1)); + // dotted before + createParsedProjection("{}", "{'a.$ref': 1}"); + createParsedProjection("{}", "{'a.$id': 1}"); + createParsedProjection("{}", "{'a.$db': 1}"); + // dotted after + createParsedProjection("{}", "{'$id.a': 1}"); + // position operator on $id + // $ref and $db hold the collection and database names respectively, + // so these fields cannot be arrays. + createParsedProjection("{'a.$id': {$elemMatch: {x: 1}}}", "{'a.$id.$': 1}"); +} +} // unnamed namespace |