diff options
author | Tess Avitabile <tess.avitabile@mongodb.com> | 2016-04-28 16:36:11 -0400 |
---|---|---|
committer | Tess Avitabile <tess.avitabile@mongodb.com> | 2016-05-05 10:29:06 -0400 |
commit | f7a46a118288ba0ce45c7664777ea0e89c2eb845 (patch) | |
tree | d9ba6c6981072ce4b836617ebb1889ac17e4fcc2 /src/mongo/db | |
parent | f49018bca41048d8a4f729ccc0489ea6be073a20 (diff) | |
download | mongo-f7a46a118288ba0ce45c7664777ea0e89c2eb845.tar.gz |
SERVER-23610 CanonicalQuery should own a CollatorInterface
Diffstat (limited to 'src/mongo/db')
42 files changed, 460 insertions, 175 deletions
diff --git a/src/mongo/db/auth/authz_manager_external_state_local.cpp b/src/mongo/db/auth/authz_manager_external_state_local.cpp index 7525464f6bb..5d76027fc22 100644 --- a/src/mongo/db/auth/authz_manager_external_state_local.cpp +++ b/src/mongo/db/auth/authz_manager_external_state_local.cpp @@ -370,14 +370,16 @@ Status AuthzManagerExternalStateLocal::_initializeRoleGraph(OperationContext* tx class AuthzManagerExternalStateLocal::AuthzManagerLogOpHandler : public RecoveryUnit::Change { public: - // None of the parameters below (except externalState) need to live longer than - // the instantiations of this class - AuthzManagerLogOpHandler(AuthzManagerExternalStateLocal* externalState, + // None of the parameters below (except txn and externalState) need to live longer than the + // instantiations of this class + AuthzManagerLogOpHandler(OperationContext* txn, + AuthzManagerExternalStateLocal* externalState, const char* op, const char* ns, const BSONObj& o, const BSONObj* o2) - : _externalState(externalState), + : _txn(txn), + _externalState(externalState), _op(op), _ns(ns), _o(o.getOwned()), @@ -388,7 +390,7 @@ public: virtual void commit() { stdx::lock_guard<stdx::mutex> lk(_externalState->_roleGraphMutex); Status status = _externalState->_roleGraph.handleLogOp( - _op.c_str(), NamespaceString(_ns.c_str()), _o, _isO2Set ? &_o2 : NULL); + _txn, _op.c_str(), NamespaceString(_ns.c_str()), _o, _isO2Set ? &_o2 : NULL); if (status == ErrorCodes::OplogOperationUnsupported) { _externalState->_roleGraph = RoleGraph(); @@ -419,6 +421,7 @@ public: virtual void rollback() {} private: + OperationContext* _txn; AuthzManagerExternalStateLocal* _externalState; const std::string _op; const std::string _ns; @@ -432,7 +435,7 @@ void AuthzManagerExternalStateLocal::logOp( OperationContext* txn, const char* op, const char* ns, const BSONObj& o, const BSONObj* o2) { if (ns == AuthorizationManager::rolesCollectionNamespace.ns() || ns == AuthorizationManager::adminCommandNamespace.ns()) { - txn->recoveryUnit()->registerChange(new AuthzManagerLogOpHandler(this, op, ns, o, o2)); + txn->recoveryUnit()->registerChange(new AuthzManagerLogOpHandler(txn, this, op, ns, o, o2)); } } diff --git a/src/mongo/db/auth/authz_manager_external_state_mock.cpp b/src/mongo/db/auth/authz_manager_external_state_mock.cpp index 47f65d888c4..6bab48f91e9 100644 --- a/src/mongo/db/auth/authz_manager_external_state_mock.cpp +++ b/src/mongo/db/auth/authz_manager_external_state_mock.cpp @@ -204,7 +204,7 @@ Status AuthzManagerExternalStateMock::updateOne(OperationContext* txn, if (query.hasField("_id")) { document.root().appendElement(query["_id"]); } - status = driver.populateDocumentWithQueryFields(query, NULL, document); + status = driver.populateDocumentWithQueryFields(txn, query, NULL, document); if (!status.isOK()) { return status; } diff --git a/src/mongo/db/auth/role_graph.h b/src/mongo/db/auth/role_graph.h index 50a0c47a857..42512312b64 100644 --- a/src/mongo/db/auth/role_graph.h +++ b/src/mongo/db/auth/role_graph.h @@ -41,6 +41,8 @@ namespace mongo { +class OperationContext; + /** * A graph of role and privilege relationships. * @@ -240,7 +242,8 @@ public: * operation is not supported, and other codes (typically BadValue) if the oplog operation * is ill-described. */ - Status handleLogOp(const char* op, + Status handleLogOp(OperationContext* txn, + const char* op, const NamespaceString& ns, const BSONObj& o, const BSONObj* o2); diff --git a/src/mongo/db/auth/role_graph_update.cpp b/src/mongo/db/auth/role_graph_update.cpp index 62df4a33a4b..6d44385b8f8 100644 --- a/src/mongo/db/auth/role_graph_update.cpp +++ b/src/mongo/db/auth/role_graph_update.cpp @@ -160,7 +160,8 @@ Status handleOplogInsert(RoleGraph* roleGraph, const BSONObj& insertedObj) { * * Treats all updates as upserts. */ -Status handleOplogUpdate(RoleGraph* roleGraph, +Status handleOplogUpdate(OperationContext* txn, + RoleGraph* roleGraph, const BSONObj& updatePattern, const BSONObj& queryPattern) { RoleName roleToUpdate; @@ -178,7 +179,7 @@ Status handleOplogUpdate(RoleGraph* roleGraph, status = AuthorizationManager::getBSONForRole(roleGraph, roleToUpdate, roleDocument.root()); if (status == ErrorCodes::RoleNotFound) { // The query pattern will only contain _id, no other immutable fields are present - status = driver.populateDocumentWithQueryFields(queryPattern, NULL, roleDocument); + status = driver.populateDocumentWithQueryFields(txn, queryPattern, NULL, roleDocument); } if (!status.isOK()) return status; @@ -273,7 +274,8 @@ Status RoleGraph::addRoleFromDocument(const BSONObj& doc) { return status; } -Status RoleGraph::handleLogOp(const char* op, +Status RoleGraph::handleLogOp(OperationContext* txn, + const char* op, const NamespaceString& ns, const BSONObj& o, const BSONObj* o2) { @@ -307,7 +309,7 @@ Status RoleGraph::handleLogOp(const char* op, return Status(ErrorCodes::InternalError, "Missing query pattern in update oplog entry."); } - return handleOplogUpdate(this, o, *o2); + return handleOplogUpdate(txn, this, o, *o2); case 'd': return handleOplogDelete(this, o); case 'n': diff --git a/src/mongo/db/commands/SConscript b/src/mongo/db/commands/SConscript index 74935b95a60..54dbbc405fa 100644 --- a/src/mongo/db/commands/SConscript +++ b/src/mongo/db/commands/SConscript @@ -98,6 +98,7 @@ env.CppUnitTest( "index_filter_commands_test.cpp", ], LIBDEPS=[ + "$BUILD_DIR/mongo/db/query/query_test_service_context", "$BUILD_DIR/mongo/db/serveronly", "$BUILD_DIR/mongo/util/ntservice_mock", ], @@ -122,6 +123,7 @@ env.CppUnitTest( "plan_cache_commands_test.cpp", ], LIBDEPS=[ + "$BUILD_DIR/mongo/db/query/query_test_service_context", "$BUILD_DIR/mongo/db/serveronly", "$BUILD_DIR/mongo/util/ntservice_mock", ], diff --git a/src/mongo/db/commands/find_cmd.cpp b/src/mongo/db/commands/find_cmd.cpp index 8741623dd8c..13ba25eb033 100644 --- a/src/mongo/db/commands/find_cmd.cpp +++ b/src/mongo/db/commands/find_cmd.cpp @@ -147,7 +147,7 @@ public: ExtensionsCallbackReal extensionsCallback(txn, &nss); auto statusWithCQ = - CanonicalQuery::canonicalize(lpqStatus.getValue().release(), extensionsCallback); + CanonicalQuery::canonicalize(txn, lpqStatus.getValue().release(), extensionsCallback); if (!statusWithCQ.isOK()) { return statusWithCQ.getStatus(); } @@ -232,7 +232,7 @@ public: // Finish the parsing step by using the LiteParsedQuery to create a CanonicalQuery. ExtensionsCallbackReal extensionsCallback(txn, &nss); - auto statusWithCQ = CanonicalQuery::canonicalize(lpq.release(), extensionsCallback); + auto statusWithCQ = CanonicalQuery::canonicalize(txn, lpq.release(), extensionsCallback); if (!statusWithCQ.isOK()) { return appendCommandStatus(result, statusWithCQ.getStatus()); } diff --git a/src/mongo/db/commands/geo_near_cmd.cpp b/src/mongo/db/commands/geo_near_cmd.cpp index aaef5f68171..392c0f58993 100644 --- a/src/mongo/db/commands/geo_near_cmd.cpp +++ b/src/mongo/db/commands/geo_near_cmd.cpp @@ -209,7 +209,7 @@ public: const ExtensionsCallbackReal extensionsCallback(txn, &nss); auto statusWithCQ = CanonicalQuery::canonicalize( - nss, rewritten, BSONObj(), projObj, 0, numWanted, BSONObj(), extensionsCallback); + txn, nss, rewritten, BSONObj(), projObj, 0, numWanted, BSONObj(), extensionsCallback); if (!statusWithCQ.isOK()) { errmsg = "Can't parse filter / create query"; return false; diff --git a/src/mongo/db/commands/index_filter_commands.cpp b/src/mongo/db/commands/index_filter_commands.cpp index 287e7e5950f..961e34e4ccd 100644 --- a/src/mongo/db/commands/index_filter_commands.cpp +++ b/src/mongo/db/commands/index_filter_commands.cpp @@ -323,7 +323,7 @@ Status ClearFilters::clear(OperationContext* txn, // Create canonical query. auto statusWithCQ = CanonicalQuery::canonicalize( - nss, entry->query, entry->sort, entry->projection, extensionsCallback); + txn, nss, entry->query, entry->sort, entry->projection, extensionsCallback); invariant(statusWithCQ.isOK()); std::unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); diff --git a/src/mongo/db/commands/index_filter_commands_test.cpp b/src/mongo/db/commands/index_filter_commands_test.cpp index 463f1bb117e..7015d32ec56 100644 --- a/src/mongo/db/commands/index_filter_commands_test.cpp +++ b/src/mongo/db/commands/index_filter_commands_test.cpp @@ -38,6 +38,7 @@ #include "mongo/db/operation_context_noop.h" #include "mongo/db/query/plan_ranker.h" #include "mongo/db/query/query_solution.h" +#include "mongo/db/query/query_test_service_context.h" #include "mongo/unittest/unittest.h" using namespace mongo; @@ -112,7 +113,8 @@ PlanRankingDecision* createDecision(size_t numPlans) { /** * Injects an entry into plan cache for query shape. */ -void addQueryShapeToPlanCache(PlanCache* planCache, +void addQueryShapeToPlanCache(OperationContext* txn, + PlanCache* planCache, const char* queryStr, const char* sortStr, const char* projectionStr) { @@ -122,7 +124,7 @@ void addQueryShapeToPlanCache(PlanCache* planCache, // Create canonical query. auto statusWithCQ = CanonicalQuery::canonicalize( - nss, queryObj, sortObj, projectionObj, ExtensionsCallbackDisallowExtensions()); + txn, nss, queryObj, sortObj, projectionObj, ExtensionsCallbackDisallowExtensions()); ASSERT_OK(statusWithCQ.getStatus()); std::unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); @@ -141,13 +143,16 @@ bool planCacheContains(const PlanCache& planCache, const char* queryStr, const char* sortStr, const char* projectionStr) { + QueryTestServiceContext serviceContext; + auto txn = serviceContext.makeOperationContext(); + BSONObj queryObj = fromjson(queryStr); BSONObj sortObj = fromjson(sortStr); BSONObj projectionObj = fromjson(projectionStr); // Create canonical query. auto statusWithInputQuery = CanonicalQuery::canonicalize( - nss, queryObj, sortObj, projectionObj, ExtensionsCallbackDisallowExtensions()); + txn.get(), nss, queryObj, sortObj, projectionObj, ExtensionsCallbackDisallowExtensions()); ASSERT_OK(statusWithInputQuery.getStatus()); unique_ptr<CanonicalQuery> inputQuery = std::move(statusWithInputQuery.getValue()); @@ -163,7 +168,8 @@ bool planCacheContains(const PlanCache& planCache, // Alternatively, we could add key to PlanCacheEntry but that would be used in one place // only. auto statusWithCurrentQuery = - CanonicalQuery::canonicalize(nss, + CanonicalQuery::canonicalize(txn.get(), + nss, entry->query, entry->sort, entry->projection, @@ -296,13 +302,14 @@ TEST(IndexFilterCommandsTest, SetFilterInvalidParameter) { TEST(IndexFilterCommandsTest, SetAndClearFilters) { QuerySettings querySettings; PlanCache planCache; - OperationContextNoop txn; + QueryTestServiceContext serviceContext; + auto txn = serviceContext.makeOperationContext(); // Inject query shape into plan cache. - addQueryShapeToPlanCache(&planCache, "{a: 1, b: 1}", "{a: -1}", "{_id: 0, a: 1}"); + addQueryShapeToPlanCache(txn.get(), &planCache, "{a: 1, b: 1}", "{a: -1}", "{_id: 0, a: 1}"); ASSERT_TRUE(planCacheContains(planCache, "{a: 1, b: 1}", "{a: -1}", "{_id: 0, a: 1}")); - ASSERT_OK(SetFilter::set(&txn, + ASSERT_OK(SetFilter::set(txn.get(), &querySettings, &planCache, nss.ns(), @@ -322,7 +329,7 @@ TEST(IndexFilterCommandsTest, SetAndClearFilters) { // Replacing the hint for the same query shape ({a: 1, b: 1} and {b: 2, a: 3} // share same shape) should not change the query settings size. - ASSERT_OK(SetFilter::set(&txn, + ASSERT_OK(SetFilter::set(txn.get(), &querySettings, &planCache, nss.ns(), @@ -333,7 +340,7 @@ TEST(IndexFilterCommandsTest, SetAndClearFilters) { ASSERT_EQUALS(filters.size(), 1U); // Add hint for different query shape. - ASSERT_OK(SetFilter::set(&txn, + ASSERT_OK(SetFilter::set(txn.get(), &querySettings, &planCache, nss.ns(), @@ -342,7 +349,7 @@ TEST(IndexFilterCommandsTest, SetAndClearFilters) { ASSERT_EQUALS(filters.size(), 2U); // Add hint for 3rd query shape. This is to prepare for ClearHint tests. - ASSERT_OK(SetFilter::set(&txn, + ASSERT_OK(SetFilter::set(txn.get(), &querySettings, &planCache, nss.ns(), @@ -351,12 +358,12 @@ TEST(IndexFilterCommandsTest, SetAndClearFilters) { ASSERT_EQUALS(filters.size(), 3U); // Add 2 entries to plan cache and check plan cache after clearing one/all filters. - addQueryShapeToPlanCache(&planCache, "{a: 1}", "{}", "{}"); - addQueryShapeToPlanCache(&planCache, "{b: 1}", "{}", "{}"); + addQueryShapeToPlanCache(txn.get(), &planCache, "{a: 1}", "{}", "{}"); + addQueryShapeToPlanCache(txn.get(), &planCache, "{b: 1}", "{}", "{}"); // Clear single hint. ASSERT_OK(ClearFilters::clear( - &txn, &querySettings, &planCache, nss.ns(), fromjson("{query: {a: 1}}"))); + txn.get(), &querySettings, &planCache, nss.ns(), fromjson("{query: {a: 1}}"))); filters = getFilters(querySettings); ASSERT_EQUALS(filters.size(), 2U); @@ -365,7 +372,7 @@ TEST(IndexFilterCommandsTest, SetAndClearFilters) { ASSERT_TRUE(planCacheContains(planCache, "{b: 1}", "{}", "{}")); // Clear all filters - ASSERT_OK(ClearFilters::clear(&txn, &querySettings, &planCache, nss.ns(), fromjson("{}"))); + ASSERT_OK(ClearFilters::clear(txn.get(), &querySettings, &planCache, nss.ns(), fromjson("{}"))); filters = getFilters(querySettings); ASSERT_TRUE(filters.empty()); diff --git a/src/mongo/db/commands/mr.cpp b/src/mongo/db/commands/mr.cpp index e8c1651aa8a..3f0b2643af8 100644 --- a/src/mongo/db/commands/mr.cpp +++ b/src/mongo/db/commands/mr.cpp @@ -983,7 +983,7 @@ BSONObj _nativeToTemp(const BSONObj& args, void* data) { * After calling this method, the temp collection will be completed. * If inline, the results will be in the in memory map */ -void State::finalReduce(CurOp* curOp, ProgressMeterHolder& pm) { +void State::finalReduce(OperationContext* txn, CurOp* curOp, ProgressMeterHolder& pm) { if (_jsMode) { // apply the reduce within JS if (_onDisk) { @@ -1061,7 +1061,7 @@ void State::finalReduce(CurOp* curOp, ProgressMeterHolder& pm) { const ExtensionsCallbackReal extensionsCallback(_txn, &nss); auto statusWithCQ = - CanonicalQuery::canonicalize(nss, BSONObj(), sortKey, BSONObj(), extensionsCallback); + CanonicalQuery::canonicalize(txn, nss, BSONObj(), sortKey, BSONObj(), extensionsCallback); verify(statusWithCQ.isOK()); std::unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); @@ -1430,7 +1430,7 @@ public: const ExtensionsCallbackReal extensionsCallback(txn, &nss); auto statusWithCQ = CanonicalQuery::canonicalize( - nss, config.filter, config.sort, BSONObj(), extensionsCallback); + txn, nss, config.filter, config.sort, BSONObj(), extensionsCallback); if (!statusWithCQ.isOK()) { uasserted(17238, "Can't canonicalize query " + config.filter.toString()); return 0; @@ -1572,7 +1572,7 @@ public: // if not inline: dump the in memory map to inc collection, all data is on disk state.dumpToInc(); // final reduce - state.finalReduce(curOp, pm); + state.finalReduce(txn, curOp, pm); reduceTime += rt.micros(); countsBuilder.appendNumber("reduce", state.numReduces()); timingBuilder.appendNumber("reduceTime", reduceTime / 1000); diff --git a/src/mongo/db/commands/mr.h b/src/mongo/db/commands/mr.h index 3237e6f7238..0fdec1129a9 100644 --- a/src/mongo/db/commands/mr.h +++ b/src/mongo/db/commands/mr.h @@ -306,7 +306,7 @@ public: void finalReduce(BSONList& values); - void finalReduce(CurOp* op, ProgressMeterHolder& pm); + void finalReduce(OperationContext* txn, CurOp* op, ProgressMeterHolder& pm); // ------- cleanup/data positioning ---------- diff --git a/src/mongo/db/commands/plan_cache_commands.cpp b/src/mongo/db/commands/plan_cache_commands.cpp index b7c408f3b3c..f48c16dc268 100644 --- a/src/mongo/db/commands/plan_cache_commands.cpp +++ b/src/mongo/db/commands/plan_cache_commands.cpp @@ -212,7 +212,7 @@ StatusWith<unique_ptr<CanonicalQuery>> PlanCacheCommand::canonicalize(OperationC const ExtensionsCallbackReal extensionsCallback(txn, &nss); auto statusWithCQ = CanonicalQuery::canonicalize( - std::move(nss), queryObj, sortObj, projObj, extensionsCallback); + txn, std::move(nss), queryObj, sortObj, projObj, extensionsCallback); if (!statusWithCQ.isOK()) { return statusWithCQ.getStatus(); } diff --git a/src/mongo/db/commands/plan_cache_commands_test.cpp b/src/mongo/db/commands/plan_cache_commands_test.cpp index 7234588fd37..ab811c7cf92 100644 --- a/src/mongo/db/commands/plan_cache_commands_test.cpp +++ b/src/mongo/db/commands/plan_cache_commands_test.cpp @@ -39,6 +39,7 @@ #include "mongo/db/operation_context_noop.h" #include "mongo/db/query/plan_ranker.h" #include "mongo/db/query/query_solution.h" +#include "mongo/db/query/query_test_service_context.h" #include "mongo/unittest/unittest.h" #include "mongo/util/mongoutils/str.h" @@ -125,9 +126,12 @@ TEST(PlanCacheCommandsTest, planCacheListQueryShapesEmpty) { } TEST(PlanCacheCommandsTest, planCacheListQueryShapesOneKey) { + QueryTestServiceContext serviceContext; + auto txn = serviceContext.makeOperationContext(); + // Create a canonical query auto statusWithCQ = CanonicalQuery::canonicalize( - nss, fromjson("{a: 1}"), ExtensionsCallbackDisallowExtensions()); + txn.get(), nss, fromjson("{a: 1}"), ExtensionsCallbackDisallowExtensions()); ASSERT_OK(statusWithCQ.getStatus()); unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); @@ -151,16 +155,18 @@ TEST(PlanCacheCommandsTest, planCacheListQueryShapesOneKey) { */ TEST(PlanCacheCommandsTest, planCacheClearAllShapes) { + QueryTestServiceContext serviceContext; + auto txn = serviceContext.makeOperationContext(); + // Create a canonical query auto statusWithCQ = CanonicalQuery::canonicalize( - nss, fromjson("{a: 1}"), ExtensionsCallbackDisallowExtensions()); + txn.get(), nss, fromjson("{a: 1}"), ExtensionsCallbackDisallowExtensions()); ASSERT_OK(statusWithCQ.getStatus()); unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); // Plan cache with one entry PlanCache planCache; QuerySolution qs; - OperationContextNoop txn; qs.cacheData.reset(createSolutionCacheData()); std::vector<QuerySolution*> solns; @@ -169,7 +175,7 @@ TEST(PlanCacheCommandsTest, planCacheClearAllShapes) { ASSERT_EQUALS(getShapes(planCache).size(), 1U); // Clear cache and confirm number of keys afterwards. - ASSERT_OK(PlanCacheClear::clear(&txn, &planCache, nss.ns(), BSONObj())); + ASSERT_OK(PlanCacheClear::clear(txn.get(), &planCache, nss.ns(), BSONObj())); ASSERT_EQUALS(getShapes(planCache).size(), 0U); } @@ -268,13 +274,16 @@ TEST(PlanCacheCommandsTest, planCacheClearUnknownKey) { } TEST(PlanCacheCommandsTest, planCacheClearOneKey) { + QueryTestServiceContext serviceContext; + auto txn = serviceContext.makeOperationContext(); + // Create 2 canonical queries. auto statusWithCQA = CanonicalQuery::canonicalize( - nss, fromjson("{a: 1}"), ExtensionsCallbackDisallowExtensions()); + txn.get(), nss, fromjson("{a: 1}"), ExtensionsCallbackDisallowExtensions()); ASSERT_OK(statusWithCQA.getStatus()); unique_ptr<CanonicalQuery> cqA = std::move(statusWithCQA.getValue()); auto statusWithCQB = CanonicalQuery::canonicalize( - nss, fromjson("{b: 1}"), ExtensionsCallbackDisallowExtensions()); + txn.get(), nss, fromjson("{b: 1}"), ExtensionsCallbackDisallowExtensions()); ASSERT_OK(statusWithCQB.getStatus()); unique_ptr<CanonicalQuery> cqB = std::move(statusWithCQB.getValue()); @@ -299,10 +308,9 @@ TEST(PlanCacheCommandsTest, planCacheClearOneKey) { // Drop {b: 1} from cache. Make sure {a: 1} is still in cache afterwards. BSONObjBuilder bob; - OperationContextNoop txn; - ASSERT_OK( - PlanCacheClear::clear(&txn, &planCache, nss.ns(), BSON("query" << cqB->getQueryObj()))); + ASSERT_OK(PlanCacheClear::clear( + txn.get(), &planCache, nss.ns(), BSON("query" << cqB->getQueryObj()))); vector<BSONObj> shapesAfter = getShapes(planCache); ASSERT_EQUALS(shapesAfter.size(), 1U); ASSERT_EQUALS(shapesAfter[0], shapeA); @@ -392,9 +400,12 @@ TEST(PlanCacheCommandsTest, planCacheListPlansUnknownKey) { } TEST(PlanCacheCommandsTest, planCacheListPlansOnlyOneSolutionTrue) { + QueryTestServiceContext serviceContext; + auto txn = serviceContext.makeOperationContext(); + // Create a canonical query auto statusWithCQ = CanonicalQuery::canonicalize( - nss, fromjson("{a: 1}"), ExtensionsCallbackDisallowExtensions()); + txn.get(), nss, fromjson("{a: 1}"), ExtensionsCallbackDisallowExtensions()); ASSERT_OK(statusWithCQ.getStatus()); unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); @@ -412,9 +423,12 @@ TEST(PlanCacheCommandsTest, planCacheListPlansOnlyOneSolutionTrue) { } TEST(PlanCacheCommandsTest, planCacheListPlansOnlyOneSolutionFalse) { + QueryTestServiceContext serviceContext; + auto txn = serviceContext.makeOperationContext(); + // Create a canonical query auto statusWithCQ = CanonicalQuery::canonicalize( - nss, fromjson("{a: 1}"), ExtensionsCallbackDisallowExtensions()); + txn.get(), nss, fromjson("{a: 1}"), ExtensionsCallbackDisallowExtensions()); ASSERT_OK(statusWithCQ.getStatus()); unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); diff --git a/src/mongo/db/dbcommands.cpp b/src/mongo/db/dbcommands.cpp index a23025034f0..b1fbd59940c 100644 --- a/src/mongo/db/dbcommands.cpp +++ b/src/mongo/db/dbcommands.cpp @@ -631,7 +631,8 @@ public: MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN { auto statusWithCQ = - CanonicalQuery::canonicalize(NamespaceString(ns), + CanonicalQuery::canonicalize(txn, + NamespaceString(ns), query, sort, BSONObj(), diff --git a/src/mongo/db/dbhelpers.cpp b/src/mongo/db/dbhelpers.cpp index 623a06b9098..54157d737fe 100644 --- a/src/mongo/db/dbhelpers.cpp +++ b/src/mongo/db/dbhelpers.cpp @@ -134,7 +134,8 @@ RecordId Helpers::findOne(OperationContext* txn, const ExtensionsCallbackReal extensionsCallback(txn, &collection->ns()); - auto statusWithCQ = CanonicalQuery::canonicalize(collection->ns(), query, extensionsCallback); + auto statusWithCQ = + CanonicalQuery::canonicalize(txn, collection->ns(), query, extensionsCallback); massert(17244, "Could not canonicalize " + query.toString(), statusWithCQ.isOK()); unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue()); diff --git a/src/mongo/db/exec/sort_key_generator.cpp b/src/mongo/db/exec/sort_key_generator.cpp index 7ffa4fe3f52..0625e1553fc 100644 --- a/src/mongo/db/exec/sort_key_generator.cpp +++ b/src/mongo/db/exec/sort_key_generator.cpp @@ -50,7 +50,9 @@ namespace mongo { // SortKeyGenerator // -SortKeyGenerator::SortKeyGenerator(const BSONObj& sortSpec, const BSONObj& queryObj) { +SortKeyGenerator::SortKeyGenerator(OperationContext* txn, + const BSONObj& sortSpec, + const BSONObj& queryObj) { _hasBounds = false; _sortHasMeta = false; _rawSortSpec = sortSpec; @@ -100,7 +102,7 @@ SortKeyGenerator::SortKeyGenerator(const BSONObj& sortSpec, const BSONObj& query _keyGen.reset(new BtreeKeyGeneratorV1(fieldNames, fixed, false /* not sparse */, nullptr)); // The bounds checker only works on the Btree part of the sort key. - getBoundsForSort(queryObj, _btreeObj); + getBoundsForSort(txn, queryObj, _btreeObj); if (_hasBounds) { _boundsChecker.reset(new IndexBoundsChecker(&_bounds, _btreeObj, 1 /* == order */)); @@ -220,7 +222,9 @@ StatusWith<BSONObj> SortKeyGenerator::getSortKeyFromObject(const WorkingSetMembe return *keys.begin(); } -void SortKeyGenerator::getBoundsForSort(const BSONObj& queryObj, const BSONObj& sortObj) { +void SortKeyGenerator::getBoundsForSort(OperationContext* txn, + const BSONObj& queryObj, + const BSONObj& sortObj) { QueryPlannerParams params; params.options = QueryPlannerParams::NO_TABLE_SCAN; @@ -230,7 +234,7 @@ void SortKeyGenerator::getBoundsForSort(const BSONObj& queryObj, const BSONObj& params.indices.push_back(sortOrder); auto statusWithQueryForSort = CanonicalQuery::canonicalize( - NamespaceString("fake.ns"), queryObj, ExtensionsCallbackNoop()); + txn, NamespaceString("fake.ns"), queryObj, ExtensionsCallbackNoop()); verify(statusWithQueryForSort.isOK()); std::unique_ptr<CanonicalQuery> queryForSort = std::move(statusWithQueryForSort.getValue()); @@ -287,7 +291,7 @@ bool SortKeyGeneratorStage::isEOF() { PlanStage::StageState SortKeyGeneratorStage::doWork(WorkingSetID* out) { if (!_sortKeyGen) { - _sortKeyGen = stdx::make_unique<SortKeyGenerator>(_sortSpec, _query); + _sortKeyGen = stdx::make_unique<SortKeyGenerator>(getOpCtx(), _sortSpec, _query); return PlanStage::NEED_TIME; } diff --git a/src/mongo/db/exec/sort_key_generator.h b/src/mongo/db/exec/sort_key_generator.h index ea035c5db5e..b63b56b0c4d 100644 --- a/src/mongo/db/exec/sort_key_generator.h +++ b/src/mongo/db/exec/sort_key_generator.h @@ -52,8 +52,11 @@ public: * 'queryObj' is the BSONObj in the .find(...) clause. For multikey arrays we have to * ensure that the value we select to sort by is within bounds generated by * executing 'queryObj' using the virtual index with key pattern 'sortSpec'. + * + * 'txn' must point to a valid OperationContext, but 'txn' does not need to outlive the + * constructed SortKeyGenerator. */ - SortKeyGenerator(const BSONObj& sortSpec, const BSONObj& queryObj); + SortKeyGenerator(OperationContext* txn, const BSONObj& sortSpec, const BSONObj& queryObj); /** * Returns the key used to sort 'member'. If the member is in LOC_AND_IDX state, it must not @@ -76,7 +79,7 @@ private: * * Populates _hasBounds and _bounds. */ - void getBoundsForSort(const BSONObj& queryObj, const BSONObj& sortObj); + void getBoundsForSort(OperationContext* txn, const BSONObj& queryObj, const BSONObj& sortObj); // The raw object in .sort() BSONObj _rawSortSpec; diff --git a/src/mongo/db/exec/subplan.cpp b/src/mongo/db/exec/subplan.cpp index 320e3d58502..b694a4cc902 100644 --- a/src/mongo/db/exec/subplan.cpp +++ b/src/mongo/db/exec/subplan.cpp @@ -186,7 +186,8 @@ Status SubplanStage::planSubqueries() { MatchExpression* orChild = _orExpression->getChild(i); // Turn the i-th child into its own query. - auto statusWithCQ = CanonicalQuery::canonicalize(*_query, orChild, extensionsCallback); + auto statusWithCQ = + CanonicalQuery::canonicalize(getOpCtx(), *_query, orChild, extensionsCallback); if (!statusWithCQ.isOK()) { mongoutils::str::stream ss; ss << "Can't canonicalize subchild " << orChild->toString() << " " diff --git a/src/mongo/db/ops/SConscript b/src/mongo/db/ops/SConscript index a6ac8dff253..e91110656a4 100644 --- a/src/mongo/db/ops/SConscript +++ b/src/mongo/db/ops/SConscript @@ -231,6 +231,7 @@ env.CppUnitTest( LIBDEPS=[ '$BUILD_DIR/mongo/bson/mutable/mutable_bson_test_utils', '$BUILD_DIR/mongo/db/query/query_planner', + '$BUILD_DIR/mongo/db/query/query_test_service_context', 'update_driver', ], ) diff --git a/src/mongo/db/ops/parsed_delete.cpp b/src/mongo/db/ops/parsed_delete.cpp index 5b488412827..80cc3b44d8b 100644 --- a/src/mongo/db/ops/parsed_delete.cpp +++ b/src/mongo/db/ops/parsed_delete.cpp @@ -83,7 +83,8 @@ Status ParsedDelete::parseQueryToCQ() { // The projection needs to be applied after the delete operation, so we specify an empty // BSONObj as the projection during canonicalization. const BSONObj emptyObj; - auto statusWithCQ = CanonicalQuery::canonicalize(_request->getNamespaceString(), + auto statusWithCQ = CanonicalQuery::canonicalize(_txn, + _request->getNamespaceString(), _request->getQuery(), _request->getSort(), emptyObj, // projection diff --git a/src/mongo/db/ops/parsed_update.cpp b/src/mongo/db/ops/parsed_update.cpp index ce4dc8d79ce..1de16adeff5 100644 --- a/src/mongo/db/ops/parsed_update.cpp +++ b/src/mongo/db/ops/parsed_update.cpp @@ -88,7 +88,8 @@ Status ParsedUpdate::parseQueryToCQ() { // The projection needs to be applied after the update operation, so we specify an empty // BSONObj as the projection during canonicalization. const BSONObj emptyObj; - auto statusWithCQ = CanonicalQuery::canonicalize(_request->getNamespaceString(), + auto statusWithCQ = CanonicalQuery::canonicalize(_txn, + _request->getNamespaceString(), _request->getQuery(), _request->getSort(), emptyObj, // projection diff --git a/src/mongo/db/ops/update_driver.cpp b/src/mongo/db/ops/update_driver.cpp index d3b5f32c16a..885759df77b 100644 --- a/src/mongo/db/ops/update_driver.cpp +++ b/src/mongo/db/ops/update_driver.cpp @@ -167,14 +167,15 @@ inline Status UpdateDriver::addAndParse(const modifiertable::ModifierType type, return Status::OK(); } -Status UpdateDriver::populateDocumentWithQueryFields(const BSONObj& query, +Status UpdateDriver::populateDocumentWithQueryFields(OperationContext* txn, + const BSONObj& query, const vector<FieldRef*>* immutablePaths, mutablebson::Document& doc) const { // We canonicalize the query to collapse $and/$or, and the first arg (ns) is not needed. Also, // because this is for the upsert case, where we insert a new document if one was not found, the // $where/$text clauses do not make sense, hence empty ExtensionsCallback. auto statusWithCQ = - CanonicalQuery::canonicalize(NamespaceString(""), query, ExtensionsCallbackNoop()); + CanonicalQuery::canonicalize(txn, NamespaceString(""), query, ExtensionsCallbackNoop()); if (!statusWithCQ.isOK()) { return statusWithCQ.getStatus(); } diff --git a/src/mongo/db/ops/update_driver.h b/src/mongo/db/ops/update_driver.h index bb11ee42bb4..dd1c940586e 100644 --- a/src/mongo/db/ops/update_driver.h +++ b/src/mongo/db/ops/update_driver.h @@ -43,6 +43,8 @@ namespace mongo { +class OperationContext; + class UpdateDriver { public: struct Options; @@ -67,7 +69,8 @@ public: * Returns Status::OK() if the document can be used. If there are any error or * conflicts along the way then those errors will be returned. */ - Status populateDocumentWithQueryFields(const BSONObj& query, + Status populateDocumentWithQueryFields(OperationContext* txn, + const BSONObj& query, const std::vector<FieldRef*>* immutablePaths, mutablebson::Document& doc) const; diff --git a/src/mongo/db/ops/update_driver_test.cpp b/src/mongo/db/ops/update_driver_test.cpp index 1a778b231a9..a2347b1048f 100644 --- a/src/mongo/db/ops/update_driver_test.cpp +++ b/src/mongo/db/ops/update_driver_test.cpp @@ -37,6 +37,7 @@ #include "mongo/bson/mutable/mutable_bson_test_utils.h" #include "mongo/db/field_ref.h" #include "mongo/db/json.h" +#include "mongo/db/query/query_test_service_context.h" #include "mongo/db/update_index_data.h" #include "mongo/unittest/unittest.h" @@ -47,11 +48,14 @@ using mongo::BSONElement; using mongo::BSONObjIterator; using mongo::FieldRef; using mongo::fromjson; -using mongo::OwnedPointerVector; -using mongo::UpdateIndexData; using mongo::mutablebson::Document; +using mongo::OperationContext; +using mongo::OwnedPointerVector; +using mongo::QueryTestServiceContext; +using mongo::ServiceContext; using mongo::StringData; using mongo::UpdateDriver; +using mongo::UpdateIndexData; using mongoutils::str::stream; TEST(Parse, Normal) { @@ -139,6 +143,7 @@ public: _driverRepl(new UpdateDriver(UpdateDriver::Options())) { _driverOps->parse(fromjson("{$set:{'_':1}}")); _driverRepl->parse(fromjson("{}")); + _opCtx = _serviceContext.makeOperationContext(); } Document& doc() { @@ -153,7 +158,13 @@ public: return *_driverRepl; } + OperationContext* txn() { + return _opCtx.get(); + } + private: + QueryTestServiceContext _serviceContext; + ServiceContext::UniqueOperationContext _opCtx; std::unique_ptr<UpdateDriver> _driverOps; std::unique_ptr<UpdateDriver> _driverRepl; Document _doc; @@ -218,139 +229,139 @@ static void assertSameFields(const BSONObj& docA, const BSONObj& docB) { TEST_F(CreateFromQuery, BasicOp) { BSONObj query = fromjson("{a:1,b:2}"); - ASSERT_OK(driverOps().populateDocumentWithQueryFields(query, NULL, doc())); + ASSERT_OK(driverOps().populateDocumentWithQueryFields(txn(), query, NULL, doc())); assertSameFields(query, doc().getObject()); } TEST_F(CreateFromQuery, BasicOpEq) { BSONObj query = fromjson("{a:{$eq:1}}"); - ASSERT_OK(driverOps().populateDocumentWithQueryFields(query, NULL, doc())); + ASSERT_OK(driverOps().populateDocumentWithQueryFields(txn(), query, NULL, doc())); assertSameFields(fromjson("{a:1}"), doc().getObject()); } TEST_F(CreateFromQuery, BasicOpWithId) { BSONObj query = fromjson("{_id:1,a:1,b:2}"); - ASSERT_OK(driverOps().populateDocumentWithQueryFields(query, NULL, doc())); + ASSERT_OK(driverOps().populateDocumentWithQueryFields(txn(), query, NULL, doc())); assertSameFields(query, doc().getObject()); } TEST_F(CreateFromQuery, BasicRepl) { BSONObj query = fromjson("{a:1,b:2}"); - ASSERT_OK(driverRepl().populateDocumentWithQueryFields(query, NULL, doc())); + ASSERT_OK(driverRepl().populateDocumentWithQueryFields(txn(), query, NULL, doc())); assertSameFields(fromjson("{}"), doc().getObject()); } TEST_F(CreateFromQuery, BasicReplWithId) { BSONObj query = fromjson("{_id:1,a:1,b:2}"); - ASSERT_OK(driverRepl().populateDocumentWithQueryFields(query, NULL, doc())); + ASSERT_OK(driverRepl().populateDocumentWithQueryFields(txn(), query, NULL, doc())); assertSameFields(fromjson("{_id:1}"), doc().getObject()); } TEST_F(CreateFromQuery, BasicReplWithIdEq) { BSONObj query = fromjson("{_id:{$eq:1},a:1,b:2}"); - ASSERT_OK(driverRepl().populateDocumentWithQueryFields(query, NULL, doc())); + ASSERT_OK(driverRepl().populateDocumentWithQueryFields(txn(), query, NULL, doc())); assertSameFields(fromjson("{_id:1}"), doc().getObject()); } TEST_F(CreateFromQuery, NoRootIdOp) { BSONObj query = fromjson("{'_id.a':1,'_id.b':2}"); - ASSERT_OK(driverOps().populateDocumentWithQueryFields(query, NULL, doc())); + ASSERT_OK(driverOps().populateDocumentWithQueryFields(txn(), query, NULL, doc())); assertSameFields(fromjson("{_id:{a:1,b:2}}"), doc().getObject()); } TEST_F(CreateFromQuery, NoRootIdRepl) { BSONObj query = fromjson("{'_id.a':1,'_id.b':2}"); - ASSERT_NOT_OK(driverRepl().populateDocumentWithQueryFields(query, NULL, doc())); + ASSERT_NOT_OK(driverRepl().populateDocumentWithQueryFields(txn(), query, NULL, doc())); } TEST_F(CreateFromQuery, NestedSharedRootOp) { BSONObj query = fromjson("{'a.c':1,'a.b':{$eq:2}}"); - ASSERT_OK(driverOps().populateDocumentWithQueryFields(query, NULL, doc())); + ASSERT_OK(driverOps().populateDocumentWithQueryFields(txn(), query, NULL, doc())); assertSameFields(fromjson("{a:{c:1,b:2}}"), doc().getObject()); } TEST_F(CreateFromQuery, OrQueryOp) { BSONObj query = fromjson("{$or:[{a:1}]}"); - ASSERT_OK(driverOps().populateDocumentWithQueryFields(query, NULL, doc())); + ASSERT_OK(driverOps().populateDocumentWithQueryFields(txn(), query, NULL, doc())); assertSameFields(fromjson("{a:1}"), doc().getObject()); } TEST_F(CreateFromQuery, OrQueryIdRepl) { BSONObj query = fromjson("{$or:[{_id:1}]}"); - ASSERT_OK(driverRepl().populateDocumentWithQueryFields(query, NULL, doc())); + ASSERT_OK(driverRepl().populateDocumentWithQueryFields(txn(), query, NULL, doc())); assertSameFields(fromjson("{_id:1}"), doc().getObject()); } TEST_F(CreateFromQuery, OrQueryNoExtractOps) { BSONObj query = fromjson("{$or:[{a:1}, {b:2}]}"); - ASSERT_OK(driverOps().populateDocumentWithQueryFields(query, NULL, doc())); + ASSERT_OK(driverOps().populateDocumentWithQueryFields(txn(), query, NULL, doc())); assertSameFields(BSONObj(), doc().getObject()); } TEST_F(CreateFromQuery, OrQueryNoExtractIdRepl) { BSONObj query = fromjson("{$or:[{_id:1}, {_id:2}]}"); - ASSERT_OK(driverRepl().populateDocumentWithQueryFields(query, NULL, doc())); + ASSERT_OK(driverRepl().populateDocumentWithQueryFields(txn(), query, NULL, doc())); assertSameFields(BSONObj(), doc().getObject()); } TEST_F(CreateFromQuery, AndQueryOp) { BSONObj query = fromjson("{$and:[{'a.c':1},{'a.b':{$eq:2}}]}"); - ASSERT_OK(driverOps().populateDocumentWithQueryFields(query, NULL, doc())); + ASSERT_OK(driverOps().populateDocumentWithQueryFields(txn(), query, NULL, doc())); assertSameFields(fromjson("{a:{c:1,b:2}}"), doc().getObject()); } TEST_F(CreateFromQuery, AndQueryIdRepl) { BSONObj query = fromjson("{$and:[{_id:1},{a:{$eq:2}}]}"); - ASSERT_OK(driverRepl().populateDocumentWithQueryFields(query, NULL, doc())); + ASSERT_OK(driverRepl().populateDocumentWithQueryFields(txn(), query, NULL, doc())); assertSameFields(fromjson("{_id:1}"), doc().getObject()); } TEST_F(CreateFromQuery, AllArrayOp) { BSONObj query = fromjson("{a:{$all:[1]}}"); - ASSERT_OK(driverOps().populateDocumentWithQueryFields(query, NULL, doc())); + ASSERT_OK(driverOps().populateDocumentWithQueryFields(txn(), query, NULL, doc())); assertSameFields(fromjson("{a:1}"), doc().getObject()); } TEST_F(CreateFromQuery, AllArrayIdRepl) { BSONObj query = fromjson("{_id:{$all:[1]}, b:2}"); - ASSERT_OK(driverRepl().populateDocumentWithQueryFields(query, NULL, doc())); + ASSERT_OK(driverRepl().populateDocumentWithQueryFields(txn(), query, NULL, doc())); assertSameFields(fromjson("{_id:1}"), doc().getObject()); } TEST_F(CreateFromQuery, ConflictFieldsFailOp) { BSONObj query = fromjson("{a:1,'a.b':1}"); - ASSERT_NOT_OK(driverOps().populateDocumentWithQueryFields(query, NULL, doc())); + ASSERT_NOT_OK(driverOps().populateDocumentWithQueryFields(txn(), query, NULL, doc())); } TEST_F(CreateFromQuery, ConflictFieldsFailSameValueOp) { BSONObj query = fromjson("{a:{b:1},'a.b':1}"); - ASSERT_NOT_OK(driverOps().populateDocumentWithQueryFields(query, NULL, doc())); + ASSERT_NOT_OK(driverOps().populateDocumentWithQueryFields(txn(), query, NULL, doc())); } TEST_F(CreateFromQuery, ConflictWithIdRepl) { BSONObj query = fromjson("{_id:1,'_id.a':1}"); - ASSERT_NOT_OK(driverRepl().populateDocumentWithQueryFields(query, NULL, doc())); + ASSERT_NOT_OK(driverRepl().populateDocumentWithQueryFields(txn(), query, NULL, doc())); } TEST_F(CreateFromQuery, ConflictAndQueryOp) { BSONObj query = fromjson("{$and:[{a:{b:1}},{'a.b':{$eq:1}}]}"); - ASSERT_NOT_OK(driverOps().populateDocumentWithQueryFields(query, NULL, doc())); + ASSERT_NOT_OK(driverOps().populateDocumentWithQueryFields(txn(), query, NULL, doc())); } TEST_F(CreateFromQuery, ConflictAllMultipleValsOp) { BSONObj query = fromjson("{a:{$all:[1, 2]}}"); - ASSERT_NOT_OK(driverOps().populateDocumentWithQueryFields(query, NULL, doc())); + ASSERT_NOT_OK(driverOps().populateDocumentWithQueryFields(txn(), query, NULL, doc())); } TEST_F(CreateFromQuery, NoConflictOrQueryOp) { BSONObj query = fromjson("{$or:[{a:{b:1}},{'a.b':{$eq:1}}]}"); - ASSERT_OK(driverOps().populateDocumentWithQueryFields(query, NULL, doc())); + ASSERT_OK(driverOps().populateDocumentWithQueryFields(txn(), query, NULL, doc())); assertSameFields(BSONObj(), doc().getObject()); } TEST_F(CreateFromQuery, ImmutableFieldsOp) { BSONObj query = fromjson("{$or:[{a:{b:1}},{'a.b':{$eq:1}}]}"); - ASSERT_OK(driverOps().populateDocumentWithQueryFields(query, NULL, doc())); + ASSERT_OK(driverOps().populateDocumentWithQueryFields(txn(), query, NULL, doc())); assertSameFields(BSONObj(), doc().getObject()); } @@ -358,7 +369,8 @@ TEST_F(CreateFromQuery, ShardKeyRepl) { BSONObj query = fromjson("{a:{$eq:1}}, b:2}"); OwnedPointerVector<FieldRef> immutablePaths; immutablePaths.push_back(new FieldRef("a")); - ASSERT_OK(driverRepl().populateDocumentWithQueryFields(query, &immutablePaths.vector(), doc())); + ASSERT_OK(driverRepl().populateDocumentWithQueryFields( + txn(), query, &immutablePaths.vector(), doc())); assertSameFields(fromjson("{a:1}"), doc().getObject()); } @@ -367,7 +379,8 @@ TEST_F(CreateFromQuery, NestedShardKeyRepl) { OwnedPointerVector<FieldRef> immutablePaths; immutablePaths.push_back(new FieldRef("a")); immutablePaths.push_back(new FieldRef("b.c")); - ASSERT_OK(driverRepl().populateDocumentWithQueryFields(query, &immutablePaths.vector(), doc())); + ASSERT_OK(driverRepl().populateDocumentWithQueryFields( + txn(), query, &immutablePaths.vector(), doc())); assertSameFields(fromjson("{a:1,b:{c:2}}"), doc().getObject()); } @@ -376,7 +389,8 @@ TEST_F(CreateFromQuery, NestedShardKeyOp) { OwnedPointerVector<FieldRef> immutablePaths; immutablePaths.push_back(new FieldRef("a")); immutablePaths.push_back(new FieldRef("b.c")); - ASSERT_OK(driverOps().populateDocumentWithQueryFields(query, &immutablePaths.vector(), doc())); + ASSERT_OK( + driverOps().populateDocumentWithQueryFields(txn(), query, &immutablePaths.vector(), doc())); assertSameFields(fromjson("{a:1,b:{c:2},d:3}"), doc().getObject()); } @@ -385,8 +399,8 @@ TEST_F(CreateFromQuery, NotFullShardKeyRepl) { OwnedPointerVector<FieldRef> immutablePaths; immutablePaths.push_back(new FieldRef("a")); immutablePaths.push_back(new FieldRef("b")); - ASSERT_NOT_OK( - driverRepl().populateDocumentWithQueryFields(query, &immutablePaths.vector(), doc())); + ASSERT_NOT_OK(driverRepl().populateDocumentWithQueryFields( + txn(), query, &immutablePaths.vector(), doc())); } } // unnamed namespace diff --git a/src/mongo/db/pipeline/pipeline_d.cpp b/src/mongo/db/pipeline/pipeline_d.cpp index 874c7f5f070..e254603af3b 100644 --- a/src/mongo/db/pipeline/pipeline_d.cpp +++ b/src/mongo/db/pipeline/pipeline_d.cpp @@ -209,7 +209,7 @@ StatusWith<std::unique_ptr<PlanExecutor>> attemptToGetExecutor( const ExtensionsCallbackReal extensionsCallback(pExpCtx->opCtx, &pExpCtx->ns); auto cq = CanonicalQuery::canonicalize( - pExpCtx->ns, queryObj, sortObj, projectionObj, extensionsCallback); + txn, pExpCtx->ns, queryObj, sortObj, projectionObj, extensionsCallback); if (!cq.isOK()) { // Return an error instead of uasserting, since there are cases where the combination of diff --git a/src/mongo/db/query/SConscript b/src/mongo/db/query/SConscript index 80afbbf0893..1818879820e 100644 --- a/src/mongo/db/query/SConscript +++ b/src/mongo/db/query/SConscript @@ -44,6 +44,7 @@ env.Library( env.Library( target='query', source=[ + "collation/collator_factory_mock_decoration.cpp", "explain.cpp", "get_executor.cpp", "find.cpp", @@ -54,8 +55,6 @@ env.Library( "stage_builder.cpp", ], LIBDEPS=[ - "collation/collator_factory_interface", - # TODO SERVER-22371: Replace collator_factory_mock with collator_factory_icu. "collation/collator_factory_mock", "internal_plans", "query_common", @@ -93,6 +92,7 @@ env.CppUnitTest( ], LIBDEPS=[ "query", + "query_test_service_context", "$BUILD_DIR/mongo/db/serveronly", "$BUILD_DIR/mongo/dbtests/mocklib", "$BUILD_DIR/mongo/util/ntservice_mock", @@ -185,12 +185,24 @@ env.Library( ) env.Library( + target="query_test_service_context", + source=[ + "query_test_service_context.cpp", + ], + LIBDEPS=[ + "collation/collator_factory_mock", + "$BUILD_DIR/mongo/db/service_context", + ], +) + +env.Library( target="query_planner_test_fixture", source=[ "query_planner_test_fixture.cpp", ], LIBDEPS=[ "query_planner_test_lib", + "query_test_service_context", "$BUILD_DIR/mongo/unittest/unittest", ], ) @@ -213,6 +225,7 @@ env.CppUnitTest( LIBDEPS=[ "collation/collator_interface_mock", "query_planner", + "query_test_service_context", ], ) @@ -283,6 +296,7 @@ env.CppUnitTest( ], LIBDEPS=[ "query_planner_test_fixture", + "query_test_service_context", ], ) diff --git a/src/mongo/db/query/canonical_query.cpp b/src/mongo/db/query/canonical_query.cpp index 5cc41126c79..7fe801443f2 100644 --- a/src/mongo/db/query/canonical_query.cpp +++ b/src/mongo/db/query/canonical_query.cpp @@ -102,20 +102,25 @@ bool matchExpressionLessThan(const MatchExpression* lhs, const MatchExpression* // static StatusWith<std::unique_ptr<CanonicalQuery>> CanonicalQuery::canonicalize( - NamespaceString nss, const BSONObj& query, const ExtensionsCallback& extensionsCallback) { + OperationContext* txn, + NamespaceString nss, + const BSONObj& query, + const ExtensionsCallback& extensionsCallback) { const BSONObj emptyObj; return CanonicalQuery::canonicalize( - std::move(nss), query, emptyObj, emptyObj, 0, 0, extensionsCallback); + txn, std::move(nss), query, emptyObj, emptyObj, 0, 0, extensionsCallback); } // static StatusWith<std::unique_ptr<CanonicalQuery>> CanonicalQuery::canonicalize( + OperationContext* txn, NamespaceString nss, const BSONObj& query, bool explain, const ExtensionsCallback& extensionsCallback) { const BSONObj emptyObj; - return CanonicalQuery::canonicalize(std::move(nss), + return CanonicalQuery::canonicalize(txn, + std::move(nss), query, emptyObj, // sort emptyObj, // projection @@ -131,6 +136,7 @@ StatusWith<std::unique_ptr<CanonicalQuery>> CanonicalQuery::canonicalize( // static StatusWith<std::unique_ptr<CanonicalQuery>> CanonicalQuery::canonicalize( + OperationContext* txn, NamespaceString nss, const BSONObj& query, long long skip, @@ -138,22 +144,24 @@ StatusWith<std::unique_ptr<CanonicalQuery>> CanonicalQuery::canonicalize( const ExtensionsCallback& extensionsCallback) { const BSONObj emptyObj; return CanonicalQuery::canonicalize( - std::move(nss), query, emptyObj, emptyObj, skip, limit, extensionsCallback); + txn, std::move(nss), query, emptyObj, emptyObj, skip, limit, extensionsCallback); } // static StatusWith<std::unique_ptr<CanonicalQuery>> CanonicalQuery::canonicalize( + OperationContext* txn, NamespaceString nss, const BSONObj& query, const BSONObj& sort, const BSONObj& proj, const ExtensionsCallback& extensionsCallback) { return CanonicalQuery::canonicalize( - std::move(nss), query, sort, proj, 0, 0, extensionsCallback); + txn, std::move(nss), query, sort, proj, 0, 0, extensionsCallback); } // static StatusWith<std::unique_ptr<CanonicalQuery>> CanonicalQuery::canonicalize( + OperationContext* txn, NamespaceString nss, const BSONObj& query, const BSONObj& sort, @@ -163,11 +171,12 @@ StatusWith<std::unique_ptr<CanonicalQuery>> CanonicalQuery::canonicalize( const ExtensionsCallback& extensionsCallback) { const BSONObj emptyObj; return CanonicalQuery::canonicalize( - std::move(nss), query, sort, proj, skip, limit, emptyObj, extensionsCallback); + txn, std::move(nss), query, sort, proj, skip, limit, emptyObj, extensionsCallback); } // static StatusWith<std::unique_ptr<CanonicalQuery>> CanonicalQuery::canonicalize( + OperationContext* txn, NamespaceString nss, const BSONObj& query, const BSONObj& sort, @@ -177,7 +186,8 @@ StatusWith<std::unique_ptr<CanonicalQuery>> CanonicalQuery::canonicalize( const BSONObj& hint, const ExtensionsCallback& extensionsCallback) { const BSONObj emptyObj; - return CanonicalQuery::canonicalize(std::move(nss), + return CanonicalQuery::canonicalize(txn, + std::move(nss), query, sort, proj, @@ -197,19 +207,19 @@ StatusWith<std::unique_ptr<CanonicalQuery>> CanonicalQuery::canonicalize( // static StatusWith<std::unique_ptr<CanonicalQuery>> CanonicalQuery::canonicalize( - const QueryMessage& qm, const ExtensionsCallback& extensionsCallback) { + OperationContext* txn, const QueryMessage& qm, const ExtensionsCallback& extensionsCallback) { // Make LiteParsedQuery. auto lpqStatus = LiteParsedQuery::fromLegacyQueryMessage(qm); if (!lpqStatus.isOK()) { return lpqStatus.getStatus(); } - return CanonicalQuery::canonicalize(lpqStatus.getValue().release(), extensionsCallback); + return CanonicalQuery::canonicalize(txn, lpqStatus.getValue().release(), extensionsCallback); } // static StatusWith<std::unique_ptr<CanonicalQuery>> CanonicalQuery::canonicalize( - LiteParsedQuery* lpq, const ExtensionsCallback& extensionsCallback) { + OperationContext* txn, LiteParsedQuery* lpq, const ExtensionsCallback& extensionsCallback) { std::unique_ptr<LiteParsedQuery> autoLpq(lpq); // Make MatchExpression. @@ -234,6 +244,7 @@ StatusWith<std::unique_ptr<CanonicalQuery>> CanonicalQuery::canonicalize( // static StatusWith<std::unique_ptr<CanonicalQuery>> CanonicalQuery::canonicalize( + OperationContext* txn, const CanonicalQuery& baseQuery, MatchExpression* root, const ExtensionsCallback& extensionsCallback) { @@ -269,6 +280,7 @@ StatusWith<std::unique_ptr<CanonicalQuery>> CanonicalQuery::canonicalize( // static StatusWith<std::unique_ptr<CanonicalQuery>> CanonicalQuery::canonicalize( + OperationContext* txn, NamespaceString nss, const BSONObj& query, const BSONObj& sort, diff --git a/src/mongo/db/query/canonical_query.h b/src/mongo/db/query/canonical_query.h index 65741b8f204..e6288ff9d1a 100644 --- a/src/mongo/db/query/canonical_query.h +++ b/src/mongo/db/query/canonical_query.h @@ -38,16 +38,23 @@ namespace mongo { +class OperationContext; + class CanonicalQuery { public: /** * If parsing succeeds, returns a std::unique_ptr<CanonicalQuery> representing the parsed * query (which will never be NULL). If parsing fails, returns an error Status. * + * 'txn' must point to a valid OperationContext, but 'txn' does not need to outlive the returned + * CanonicalQuery. + * * Used for legacy find through the OP_QUERY message. */ static StatusWith<std::unique_ptr<CanonicalQuery>> canonicalize( - const QueryMessage& qm, const ExtensionsCallback& extensionsCallback); + OperationContext* txn, + const QueryMessage& qm, + const ExtensionsCallback& extensionsCallback); /** * Takes ownership of 'lpq'. @@ -55,9 +62,13 @@ public: * If parsing succeeds, returns a std::unique_ptr<CanonicalQuery> representing the parsed * query (which will never be NULL). If parsing fails, returns an error Status. * + * 'txn' must point to a valid OperationContext, but 'txn' does not need to outlive the returned + * CanonicalQuery. + * * Used for finds using the find command path. */ - static StatusWith<std::unique_ptr<CanonicalQuery>> canonicalize(LiteParsedQuery* lpq, + static StatusWith<std::unique_ptr<CanonicalQuery>> canonicalize(OperationContext* txn, + LiteParsedQuery* lpq, const ExtensionsCallback&); /** @@ -72,20 +83,26 @@ public: * Does not take ownership of 'root'. */ static StatusWith<std::unique_ptr<CanonicalQuery>> canonicalize( + OperationContext* txn, const CanonicalQuery& baseQuery, MatchExpression* root, const ExtensionsCallback& extensionsCallback); static StatusWith<std::unique_ptr<CanonicalQuery>> canonicalize( - NamespaceString nss, const BSONObj& query, const ExtensionsCallback& extensionsCallback); + OperationContext* txn, + NamespaceString nss, + const BSONObj& query, + const ExtensionsCallback& extensionsCallback); static StatusWith<std::unique_ptr<CanonicalQuery>> canonicalize( + OperationContext* txn, NamespaceString nss, const BSONObj& query, bool explain, const ExtensionsCallback& extensionsCallback); static StatusWith<std::unique_ptr<CanonicalQuery>> canonicalize( + OperationContext* txn, NamespaceString nss, const BSONObj& query, long long skip, @@ -93,6 +110,7 @@ public: const ExtensionsCallback& extensionsCallback); static StatusWith<std::unique_ptr<CanonicalQuery>> canonicalize( + OperationContext* txn, NamespaceString nss, const BSONObj& query, const BSONObj& sort, @@ -100,6 +118,7 @@ public: const ExtensionsCallback& extensionsCallback); static StatusWith<std::unique_ptr<CanonicalQuery>> canonicalize( + OperationContext* txn, NamespaceString nss, const BSONObj& query, const BSONObj& sort, @@ -109,6 +128,7 @@ public: const ExtensionsCallback& extensionsCallback); static StatusWith<std::unique_ptr<CanonicalQuery>> canonicalize( + OperationContext* txn, NamespaceString nss, const BSONObj& query, const BSONObj& sort, @@ -119,6 +139,7 @@ public: const ExtensionsCallback& extensionsCallback); static StatusWith<std::unique_ptr<CanonicalQuery>> canonicalize( + OperationContext* txn, NamespaceString nss, const BSONObj& query, const BSONObj& sort, diff --git a/src/mongo/db/query/canonical_query_test.cpp b/src/mongo/db/query/canonical_query_test.cpp index 05c95409f7e..11fada4de05 100644 --- a/src/mongo/db/query/canonical_query_test.cpp +++ b/src/mongo/db/query/canonical_query_test.cpp @@ -35,6 +35,7 @@ #include "mongo/db/namespace_string.h" #include "mongo/db/query/collation/collator_interface_mock.h" #include "mongo/db/query/index_tag.h" +#include "mongo/db/query/query_test_service_context.h" #include "mongo/unittest/unittest.h" namespace mongo { @@ -428,13 +429,16 @@ TEST(CanonicalQueryTest, IsValidTextAndSnapshot) { } TEST(CanonicalQueryTest, IsValidSortKeyMetaProjection) { + QueryTestServiceContext serviceContext; + auto txn = serviceContext.makeOperationContext(); + // Passing a sortKey meta-projection without a sort is an error. { const bool isExplain = false; auto lpq = assertGet(LiteParsedQuery::makeFromFindCommand( nss, fromjson("{find: 'testcoll', projection: {foo: {$meta: 'sortKey'}}}"), isExplain)); - auto cq = - CanonicalQuery::canonicalize(lpq.release(), ExtensionsCallbackDisallowExtensions()); + auto cq = CanonicalQuery::canonicalize( + txn.get(), lpq.release(), ExtensionsCallbackDisallowExtensions()); ASSERT_NOT_OK(cq.getStatus()); } @@ -445,8 +449,8 @@ TEST(CanonicalQueryTest, IsValidSortKeyMetaProjection) { nss, fromjson("{find: 'testcoll', projection: {foo: {$meta: 'sortKey'}}, sort: {bar: 1}}"), isExplain)); - auto cq = - CanonicalQuery::canonicalize(lpq.release(), ExtensionsCallbackDisallowExtensions()); + auto cq = CanonicalQuery::canonicalize( + txn.get(), lpq.release(), ExtensionsCallbackDisallowExtensions()); ASSERT_OK(cq.getStatus()); } } @@ -538,9 +542,12 @@ TEST(CanonicalQueryTest, SortTreeNumChildrenComparison) { * Utility function to create a CanonicalQuery */ unique_ptr<CanonicalQuery> canonicalize(const char* queryStr) { + QueryTestServiceContext serviceContext; + auto txn = serviceContext.makeOperationContext(); + BSONObj queryObj = fromjson(queryStr); - auto statusWithCQ = - CanonicalQuery::canonicalize(nss, queryObj, ExtensionsCallbackDisallowExtensions()); + auto statusWithCQ = CanonicalQuery::canonicalize( + txn.get(), nss, queryObj, ExtensionsCallbackDisallowExtensions()); ASSERT_OK(statusWithCQ.getStatus()); return std::move(statusWithCQ.getValue()); } @@ -548,11 +555,14 @@ unique_ptr<CanonicalQuery> canonicalize(const char* queryStr) { std::unique_ptr<CanonicalQuery> canonicalize(const char* queryStr, const char* sortStr, const char* projStr) { + QueryTestServiceContext serviceContext; + auto txn = serviceContext.makeOperationContext(); + BSONObj queryObj = fromjson(queryStr); BSONObj sortObj = fromjson(sortStr); BSONObj projObj = fromjson(projStr); auto statusWithCQ = CanonicalQuery::canonicalize( - nss, queryObj, sortObj, projObj, ExtensionsCallbackDisallowExtensions()); + txn.get(), nss, queryObj, sortObj, projObj, ExtensionsCallbackDisallowExtensions()); ASSERT_OK(statusWithCQ.getStatus()); return std::move(statusWithCQ.getValue()); } @@ -653,16 +663,19 @@ TEST(CanonicalQueryTest, NormalizeWithInPreservesCollator) { } TEST(CanonicalQueryTest, CanonicalizeFromBaseQuery) { + QueryTestServiceContext serviceContext; + auto txn = serviceContext.makeOperationContext(); + const bool isExplain = true; const std::string cmdStr = "{find:'bogusns', filter:{$or:[{a:1,b:1},{a:1,c:1}]}, projection:{a:1}, sort:{b:1}}"; auto lpq = assertGet(LiteParsedQuery::makeFromFindCommand(nss, fromjson(cmdStr), isExplain)); - auto baseCq = assertGet( - CanonicalQuery::canonicalize(lpq.release(), ExtensionsCallbackDisallowExtensions())); + auto baseCq = assertGet(CanonicalQuery::canonicalize( + txn.get(), lpq.release(), ExtensionsCallbackDisallowExtensions())); MatchExpression* firstClauseExpr = baseCq->root()->getChild(0); auto childCq = assertGet(CanonicalQuery::canonicalize( - *baseCq, firstClauseExpr, ExtensionsCallbackDisallowExtensions())); + txn.get(), *baseCq, firstClauseExpr, ExtensionsCallbackDisallowExtensions())); // Descriptive test. The childCq's filter should be the relevant $or clause, rather than the // entire query predicate. diff --git a/src/mongo/db/query/collation/collator_factory_mock.cpp b/src/mongo/db/query/collation/collator_factory_mock.cpp index 0233cbcf262..e1d1f8f7d2d 100644 --- a/src/mongo/db/query/collation/collator_factory_mock.cpp +++ b/src/mongo/db/query/collation/collator_factory_mock.cpp @@ -31,24 +31,12 @@ #include "mongo/db/query/collation/collator_factory_mock.h" #include "mongo/base/init.h" +#include "mongo/bson/bsonobj.h" #include "mongo/db/query/collation/collator_interface_mock.h" -#include "mongo/db/service_context.h" #include "mongo/stdx/memory.h" namespace mongo { -namespace { - -// TODO SERVER-22371: We should decorate with a CollatorFactoryICU instead. -MONGO_INITIALIZER_WITH_PREREQUISITES(CreateCollatorFactory, - ("SetGlobalEnvironment"))(InitializerContext* context) { - CollatorFactoryInterface::set(getGlobalServiceContext(), - stdx::make_unique<CollatorFactoryMock>()); - return Status::OK(); -} - -} // namespace - StatusWith<std::unique_ptr<CollatorInterface>> CollatorFactoryMock::makeFromBSON( const BSONObj& spec) { auto collator = diff --git a/src/mongo/db/query/collation/collator_factory_mock_decoration.cpp b/src/mongo/db/query/collation/collator_factory_mock_decoration.cpp new file mode 100644 index 00000000000..3c8ef8d7cba --- /dev/null +++ b/src/mongo/db/query/collation/collator_factory_mock_decoration.cpp @@ -0,0 +1,50 @@ +/** + * Copyright (C) 2016 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. + */ + +#include "mongo/platform/basic.h" + +#include "mongo/base/init.h" +#include "mongo/db/query/collation/collator_factory_mock.h" +#include "mongo/db/service_context.h" +#include "mongo/stdx/memory.h" + +namespace mongo { + +namespace { + +// TODO SERVER-22371: We should decorate with a CollatorFactoryICU instead. +MONGO_INITIALIZER_WITH_PREREQUISITES(CreateCollatorFactory, + ("SetGlobalEnvironment"))(InitializerContext* context) { + CollatorFactoryInterface::set(getGlobalServiceContext(), + stdx::make_unique<CollatorFactoryMock>()); + return Status::OK(); +} + +} // namespace + +} // namespace mongo diff --git a/src/mongo/db/query/collation/collator_factory_mock_test.cpp b/src/mongo/db/query/collation/collator_factory_mock_test.cpp index f97a6360185..461909bda15 100644 --- a/src/mongo/db/query/collation/collator_factory_mock_test.cpp +++ b/src/mongo/db/query/collation/collator_factory_mock_test.cpp @@ -28,23 +28,14 @@ #include "mongo/platform/basic.h" -#include "mongo/base/init.h" #include "mongo/bson/bsonobj.h" #include "mongo/db/query/collation/collator_factory_mock.h" -#include "mongo/db/service_context_noop.h" -#include "mongo/stdx/memory.h" #include "mongo/unittest/unittest.h" namespace { using namespace mongo; -// Stub to avoid including the server environment library. -MONGO_INITIALIZER(SetGlobalEnvironment)(InitializerContext* context) { - setGlobalServiceContext(stdx::make_unique<ServiceContextNoop>()); - return Status::OK(); -} - TEST(CollatorFactoryMockTest, CollatorFactoryMockConstructsReverseStringCollator) { CollatorFactoryMock factory; auto collator = factory.makeFromBSON(BSONObj()); @@ -52,12 +43,4 @@ TEST(CollatorFactoryMockTest, CollatorFactoryMockConstructsReverseStringCollator ASSERT_GT(collator.getValue()->compare("abc", "cba"), 0); } -// TODO SERVER-22371: Remove this test. We will decorate with a CollatorFactoryICU instead. -TEST(CollatorFactoryMockTest, ServiceContextDecoratedWithCollatorFactoryMock) { - auto factory = CollatorFactoryInterface::get(getGlobalServiceContext()); - auto collator = factory->makeFromBSON(BSONObj()); - ASSERT_OK(collator.getStatus()); - ASSERT_GT(collator.getValue()->compare("abc", "cba"), 0); -} - } // namespace diff --git a/src/mongo/db/query/find.cpp b/src/mongo/db/query/find.cpp index ca5ec06a5b6..bd58ccf4a2d 100644 --- a/src/mongo/db/query/find.cpp +++ b/src/mongo/db/query/find.cpp @@ -499,7 +499,7 @@ std::string runQuery(OperationContext* txn, // Parse the qm into a CanonicalQuery. - auto statusWithCQ = CanonicalQuery::canonicalize(q, ExtensionsCallbackReal(txn, &nss)); + auto statusWithCQ = CanonicalQuery::canonicalize(txn, q, ExtensionsCallbackReal(txn, &nss)); if (!statusWithCQ.isOK()) { uasserted( 17287, diff --git a/src/mongo/db/query/get_executor.cpp b/src/mongo/db/query/get_executor.cpp index cf5fa10d047..33e6f533fb3 100644 --- a/src/mongo/db/query/get_executor.cpp +++ b/src/mongo/db/query/get_executor.cpp @@ -937,7 +937,7 @@ StatusWith<unique_ptr<PlanExecutor>> getExecutorGroup(OperationContext* txn, const ExtensionsCallbackReal extensionsCallback(txn, &nss); auto statusWithCQ = - CanonicalQuery::canonicalize(nss, request.query, request.explain, extensionsCallback); + CanonicalQuery::canonicalize(txn, nss, request.query, request.explain, extensionsCallback); if (!statusWithCQ.isOK()) { return statusWithCQ.getStatus(); } @@ -1155,6 +1155,7 @@ StatusWith<unique_ptr<PlanExecutor>> getExecutorCount(OperationContext* txn, unique_ptr<WorkingSet> ws = make_unique<WorkingSet>(); auto cq = CanonicalQuery::canonicalize( + txn, request.getNs(), request.getQuery(), BSONObj(), // sort @@ -1330,8 +1331,8 @@ StatusWith<unique_ptr<PlanExecutor>> getExecutorDistinct(OperationContext* txn, // If there are no suitable indices for the distinct hack bail out now into regular planning // with no projection. if (plannerParams.indices.empty()) { - auto statusWithCQ = - CanonicalQuery::canonicalize(collection->ns(), query, isExplain, extensionsCallback); + auto statusWithCQ = CanonicalQuery::canonicalize( + txn, collection->ns(), query, isExplain, extensionsCallback); if (!statusWithCQ.isOK()) { return statusWithCQ.getStatus(); } @@ -1349,7 +1350,8 @@ StatusWith<unique_ptr<PlanExecutor>> getExecutorDistinct(OperationContext* txn, BSONObj projection = getDistinctProjection(field); // Apply a projection of the key. Empty BSONObj() is for the sort. - auto statusWithCQ = CanonicalQuery::canonicalize(collection->ns(), + auto statusWithCQ = CanonicalQuery::canonicalize(txn, + collection->ns(), query, BSONObj(), // sort projection, @@ -1447,7 +1449,7 @@ StatusWith<unique_ptr<PlanExecutor>> getExecutorDistinct(OperationContext* txn, // We drop the projection from the 'cq'. Unfortunately this is not trivial. statusWithCQ = - CanonicalQuery::canonicalize(collection->ns(), query, isExplain, extensionsCallback); + CanonicalQuery::canonicalize(txn, collection->ns(), query, isExplain, extensionsCallback); if (!statusWithCQ.isOK()) { return statusWithCQ.getStatus(); } diff --git a/src/mongo/db/query/get_executor_test.cpp b/src/mongo/db/query/get_executor_test.cpp index 29c73f6c8b2..630eb00ca40 100644 --- a/src/mongo/db/query/get_executor_test.cpp +++ b/src/mongo/db/query/get_executor_test.cpp @@ -35,6 +35,7 @@ #include "mongo/db/json.h" #include "mongo/db/matcher/extensions_callback_disallow_extensions.h" #include "mongo/db/query/query_settings.h" +#include "mongo/db/query/query_test_service_context.h" #include "mongo/unittest/unittest.h" #include "mongo/util/mongoutils/str.h" @@ -52,11 +53,14 @@ static const NamespaceString nss("test.collection"); unique_ptr<CanonicalQuery> canonicalize(const char* queryStr, const char* sortStr, const char* projStr) { + QueryTestServiceContext serviceContext; + auto txn = serviceContext.makeOperationContext(); + BSONObj queryObj = fromjson(queryStr); BSONObj sortObj = fromjson(sortStr); BSONObj projObj = fromjson(projStr); auto statusWithCQ = CanonicalQuery::canonicalize( - nss, queryObj, sortObj, projObj, ExtensionsCallbackDisallowExtensions()); + txn.get(), nss, queryObj, sortObj, projObj, ExtensionsCallbackDisallowExtensions()); ASSERT_OK(statusWithCQ.getStatus()); return std::move(statusWithCQ.getValue()); } diff --git a/src/mongo/db/query/plan_cache_test.cpp b/src/mongo/db/query/plan_cache_test.cpp index 61e803af8af..2b146d9c83a 100644 --- a/src/mongo/db/query/plan_cache_test.cpp +++ b/src/mongo/db/query/plan_cache_test.cpp @@ -44,6 +44,7 @@ #include "mongo/db/query/query_planner.h" #include "mongo/db/query/query_planner_test_lib.h" #include "mongo/db/query/query_solution.h" +#include "mongo/db/query/query_test_service_context.h" #include "mongo/unittest/unittest.h" #include "mongo/util/assert_util.h" #include "mongo/util/scopeguard.h" @@ -62,8 +63,11 @@ static const NamespaceString nss("test.collection"); * Utility functions to create a CanonicalQuery */ unique_ptr<CanonicalQuery> canonicalize(const BSONObj& queryObj) { - auto statusWithCQ = - CanonicalQuery::canonicalize(nss, queryObj, ExtensionsCallbackDisallowExtensions()); + QueryTestServiceContext serviceContext; + auto txn = serviceContext.makeOperationContext(); + + auto statusWithCQ = CanonicalQuery::canonicalize( + txn.get(), nss, queryObj, ExtensionsCallbackDisallowExtensions()); ASSERT_OK(statusWithCQ.getStatus()); return std::move(statusWithCQ.getValue()); } @@ -76,11 +80,14 @@ unique_ptr<CanonicalQuery> canonicalize(const char* queryStr) { unique_ptr<CanonicalQuery> canonicalize(const char* queryStr, const char* sortStr, const char* projStr) { + QueryTestServiceContext serviceContext; + auto txn = serviceContext.makeOperationContext(); + BSONObj queryObj = fromjson(queryStr); BSONObj sortObj = fromjson(sortStr); BSONObj projObj = fromjson(projStr); auto statusWithCQ = CanonicalQuery::canonicalize( - nss, queryObj, sortObj, projObj, ExtensionsCallbackDisallowExtensions()); + txn.get(), nss, queryObj, sortObj, projObj, ExtensionsCallbackDisallowExtensions()); ASSERT_OK(statusWithCQ.getStatus()); return std::move(statusWithCQ.getValue()); } @@ -93,13 +100,17 @@ unique_ptr<CanonicalQuery> canonicalize(const char* queryStr, const char* hintStr, const char* minStr, const char* maxStr) { + QueryTestServiceContext serviceContext; + auto txn = serviceContext.makeOperationContext(); + BSONObj queryObj = fromjson(queryStr); BSONObj sortObj = fromjson(sortStr); BSONObj projObj = fromjson(projStr); BSONObj hintObj = fromjson(hintStr); BSONObj minObj = fromjson(minStr); BSONObj maxObj = fromjson(maxStr); - auto statusWithCQ = CanonicalQuery::canonicalize(nss, + auto statusWithCQ = CanonicalQuery::canonicalize(txn.get(), + nss, queryObj, sortObj, projObj, @@ -125,13 +136,17 @@ unique_ptr<CanonicalQuery> canonicalize(const char* queryStr, const char* maxStr, bool snapshot, bool explain) { + QueryTestServiceContext serviceContext; + auto txn = serviceContext.makeOperationContext(); + BSONObj queryObj = fromjson(queryStr); BSONObj sortObj = fromjson(sortStr); BSONObj projObj = fromjson(projStr); BSONObj hintObj = fromjson(hintStr); BSONObj minObj = fromjson(minStr); BSONObj maxObj = fromjson(maxStr); - auto statusWithCQ = CanonicalQuery::canonicalize(nss, + auto statusWithCQ = CanonicalQuery::canonicalize(txn.get(), + nss, queryObj, sortObj, projObj, @@ -516,6 +531,9 @@ protected: const BSONObj& minObj, const BSONObj& maxObj, bool snapshot) { + QueryTestServiceContext serviceContext; + auto txn = serviceContext.makeOperationContext(); + // Clean up any previous state from a call to runQueryFull for (vector<QuerySolution*>::iterator it = solns.begin(); it != solns.end(); ++it) { delete *it; @@ -523,7 +541,8 @@ protected: solns.clear(); - auto statusWithCQ = CanonicalQuery::canonicalize(nss, + auto statusWithCQ = CanonicalQuery::canonicalize(txn.get(), + nss, query, sort, proj, @@ -605,8 +624,11 @@ protected: const BSONObj& sort, const BSONObj& proj, const QuerySolution& soln) const { + QueryTestServiceContext serviceContext; + auto txn = serviceContext.makeOperationContext(); + auto statusWithCQ = CanonicalQuery::canonicalize( - nss, query, sort, proj, ExtensionsCallbackDisallowExtensions()); + txn.get(), nss, query, sort, proj, ExtensionsCallbackDisallowExtensions()); ASSERT_OK(statusWithCQ.getStatus()); unique_ptr<CanonicalQuery> scopedCq = std::move(statusWithCQ.getValue()); diff --git a/src/mongo/db/query/query_planner_test.cpp b/src/mongo/db/query/query_planner_test.cpp index ea5dcf3f4b1..77898adad35 100644 --- a/src/mongo/db/query/query_planner_test.cpp +++ b/src/mongo/db/query/query_planner_test.cpp @@ -4138,7 +4138,7 @@ TEST_F(QueryPlannerTest, KeyPatternOverflowsInt) { // Test bad input to query planner helpers. // -TEST(BadInputTest, CacheDataFromTaggedTree) { +TEST_F(QueryPlannerTest, CacheDataFromTaggedTreeFailsOnBadInput) { PlanCacheIndexTree* indexTree; // Null match expression. @@ -4150,8 +4150,10 @@ TEST(BadInputTest, CacheDataFromTaggedTree) { // No relevant index matching the index tag. relevantIndices.push_back(IndexEntry(BSON("a" << 1))); - auto statusWithCQ = CanonicalQuery::canonicalize( - NamespaceString("test.collection"), BSON("a" << 3), ExtensionsCallbackDisallowExtensions()); + auto statusWithCQ = CanonicalQuery::canonicalize(txn(), + NamespaceString("test.collection"), + BSON("a" << 3), + ExtensionsCallbackDisallowExtensions()); ASSERT_OK(statusWithCQ.getStatus()); std::unique_ptr<CanonicalQuery> scopedCq = std::move(statusWithCQ.getValue()); scopedCq->root()->setTag(new IndexTag(1)); @@ -4161,11 +4163,11 @@ TEST(BadInputTest, CacheDataFromTaggedTree) { ASSERT(NULL == indexTree); } -TEST(BadInputTest, TagAccordingToCache) { +TEST_F(QueryPlannerTest, TagAccordingToCacheFailsOnBadInput) { const NamespaceString nss("test.collection"); - auto statusWithCQ = - CanonicalQuery::canonicalize(nss, BSON("a" << 3), ExtensionsCallbackDisallowExtensions()); + auto statusWithCQ = CanonicalQuery::canonicalize( + txn(), nss, BSON("a" << 3), ExtensionsCallbackDisallowExtensions()); ASSERT_OK(statusWithCQ.getStatus()); std::unique_ptr<CanonicalQuery> scopedCq = std::move(statusWithCQ.getValue()); @@ -4192,8 +4194,8 @@ TEST(BadInputTest, TagAccordingToCache) { ASSERT_OK(s); // Regenerate canonical query in order to clear tags. - statusWithCQ = - CanonicalQuery::canonicalize(nss, BSON("a" << 3), ExtensionsCallbackDisallowExtensions()); + statusWithCQ = CanonicalQuery::canonicalize( + txn(), nss, BSON("a" << 3), ExtensionsCallbackDisallowExtensions()); ASSERT_OK(statusWithCQ.getStatus()); scopedCq = std::move(statusWithCQ.getValue()); diff --git a/src/mongo/db/query/query_planner_test_fixture.cpp b/src/mongo/db/query/query_planner_test_fixture.cpp index 0e336528b45..5f89d1ee35c 100644 --- a/src/mongo/db/query/query_planner_test_fixture.cpp +++ b/src/mongo/db/query/query_planner_test_fixture.cpp @@ -50,11 +50,16 @@ using unittest::assertGet; const NamespaceString QueryPlannerTest::nss("test.collection"); void QueryPlannerTest::setUp() { + opCtx = serviceContext.makeOperationContext(); internalQueryPlannerEnableHashIntersection = true; params.options = QueryPlannerParams::INCLUDE_COLLSCAN; addIndex(BSON("_id" << 1)); } +OperationContext* QueryPlannerTest::txn() { + return opCtx.get(); +} + void QueryPlannerTest::addIndex(BSONObj keyPattern, bool multikey) { params.indices.push_back(IndexEntry(keyPattern, multikey, @@ -199,7 +204,8 @@ void QueryPlannerTest::runQueryFull(const BSONObj& query, solns.clear(); cq.reset(); - auto statusWithCQ = CanonicalQuery::canonicalize(nss, + auto statusWithCQ = CanonicalQuery::canonicalize(txn(), + nss, query, sort, proj, @@ -267,7 +273,8 @@ void QueryPlannerTest::runInvalidQueryFull(const BSONObj& query, solns.clear(); cq.reset(); - auto statusWithCQ = CanonicalQuery::canonicalize(nss, + auto statusWithCQ = CanonicalQuery::canonicalize(txn(), + nss, query, sort, proj, @@ -296,7 +303,8 @@ void QueryPlannerTest::runQueryAsCommand(const BSONObj& cmdObj) { std::unique_ptr<LiteParsedQuery> lpq( assertGet(LiteParsedQuery::makeFromFindCommand(nss, cmdObj, isExplain))); - auto statusWithCQ = CanonicalQuery::canonicalize(lpq.release(), ExtensionsCallbackNoop()); + auto statusWithCQ = + CanonicalQuery::canonicalize(txn(), lpq.release(), ExtensionsCallbackNoop()); ASSERT_OK(statusWithCQ.getStatus()); cq = std::move(statusWithCQ.getValue()); diff --git a/src/mongo/db/query/query_planner_test_fixture.h b/src/mongo/db/query/query_planner_test_fixture.h index 0a2bfcc8b1a..98f4916ff6f 100644 --- a/src/mongo/db/query/query_planner_test_fixture.h +++ b/src/mongo/db/query/query_planner_test_fixture.h @@ -39,6 +39,7 @@ #include "mongo/db/json.h" #include "mongo/db/query/collation/collator_interface.h" #include "mongo/db/query/query_solution.h" +#include "mongo/db/query/query_test_service_context.h" #include "mongo/unittest/unittest.h" namespace mongo { @@ -47,6 +48,8 @@ class QueryPlannerTest : public mongo::unittest::Test { protected: void setUp(); + OperationContext* txn(); + // // Build up test. // @@ -197,6 +200,8 @@ protected: static const NamespaceString nss; + QueryTestServiceContext serviceContext; + ServiceContext::UniqueOperationContext opCtx; BSONObj queryObj; std::unique_ptr<CanonicalQuery> cq; QueryPlannerParams params; diff --git a/src/mongo/db/query/query_test_service_context.cpp b/src/mongo/db/query/query_test_service_context.cpp new file mode 100644 index 00000000000..e2682fe44d4 --- /dev/null +++ b/src/mongo/db/query/query_test_service_context.cpp @@ -0,0 +1,47 @@ +/** + * Copyright (C) 2016 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. + */ + +#include "mongo/platform/basic.h" + +#include "mongo/db/query/query_test_service_context.h" + +#include "mongo/db/query/collation/collator_factory_mock.h" +#include "mongo/stdx/memory.h" + +namespace mongo { + +QueryTestServiceContext::QueryTestServiceContext() { + CollatorFactoryInterface::set(&_serviceContext, stdx::make_unique<CollatorFactoryMock>()); + _uniqueClient = _serviceContext.makeClient("QueryTest"); +} + +ServiceContext::UniqueOperationContext QueryTestServiceContext::makeOperationContext() { + return _uniqueClient->makeOperationContext(); +} + +} // namespace mongo diff --git a/src/mongo/db/query/query_test_service_context.h b/src/mongo/db/query/query_test_service_context.h new file mode 100644 index 00000000000..7466116d354 --- /dev/null +++ b/src/mongo/db/query/query_test_service_context.h @@ -0,0 +1,52 @@ +/** + * Copyright (C) 2016 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. + */ + +#pragma once + +#include "mongo/db/client.h" +#include "mongo/db/service_context_noop.h" + +namespace mongo { + +/** + * QueryTestServiceContext is a helper class for tests that require only a single Client under a + * single ServiceContext for their execution context. The owned ServiceContext is decorated with a + * CollatorFactoryMock. + */ +class QueryTestServiceContext { +public: + QueryTestServiceContext(); + + ServiceContext::UniqueOperationContext makeOperationContext(); + +private: + ServiceContextNoop _serviceContext; + ServiceContext::UniqueClient _uniqueClient; +}; + +} // namespace mongo diff --git a/src/mongo/db/ttl.cpp b/src/mongo/db/ttl.cpp index 108c86d4e0f..35020609c56 100644 --- a/src/mongo/db/ttl.cpp +++ b/src/mongo/db/ttl.cpp @@ -289,7 +289,7 @@ private: BSONObj query = BSON(keyFieldName << BSON("$gte" << kDawnOfTime << "$lte" << expirationTime)); auto canonicalQuery = - CanonicalQuery::canonicalize(nss, query, ExtensionsCallbackDisallowExtensions()); + CanonicalQuery::canonicalize(txn, nss, query, ExtensionsCallbackDisallowExtensions()); invariantOK(canonicalQuery.getStatus()); DeleteStageParams params; |