diff options
39 files changed, 276 insertions, 181 deletions
diff --git a/src/mongo/db/commands/count.cpp b/src/mongo/db/commands/count.cpp index e630ff21dc9..f68f7488785 100644 --- a/src/mongo/db/commands/count.cpp +++ b/src/mongo/db/commands/count.cpp @@ -209,7 +209,7 @@ namespace mongo { const BSONObj hintObj = hint.empty() ? BSONObj() : BSON("$hint" << hint); StringData dbnameData(dbname); - const WhereCallbackReal whereCallback(dbnameData); + const WhereCallbackReal whereCallback(txn, dbnameData); CanonicalQuery* cq; uassertStatusOK(CanonicalQuery::canonicalize(ns, diff --git a/src/mongo/db/commands/find_and_modify.cpp b/src/mongo/db/commands/find_and_modify.cpp index f242327f935..9df4c363b62 100644 --- a/src/mongo/db/commands/find_and_modify.cpp +++ b/src/mongo/db/commands/find_and_modify.cpp @@ -142,7 +142,7 @@ namespace mongo { Collection* collection = cx.db()->getCollection( txn, ns ); - const WhereCallbackReal whereCallback = WhereCallbackReal(StringData(ns)); + const WhereCallbackReal whereCallback = WhereCallbackReal(txn, StringData(ns)); BSONObj doc; bool found = false; @@ -247,7 +247,7 @@ namespace mongo { } const NamespaceString requestNs(ns); - UpdateRequest request(requestNs); + UpdateRequest request(txn, requestNs); request.setQuery(queryModified); request.setUpdates(update); @@ -257,8 +257,7 @@ namespace mongo { // the shard version below, but for now no UpdateLifecycleImpl updateLifecycle(false, requestNs); request.setLifecycle(&updateLifecycle); - UpdateResult res = mongo::update(txn, - cx.db(), + UpdateResult res = mongo::update(cx.db(), request, &txn->getCurOp()->debug()); @@ -328,7 +327,7 @@ namespace mongo { Projection projection; if (fields) { - projection.init(fieldsHolder, WhereCallbackReal(StringData(dbname))); + projection.init(fieldsHolder, WhereCallbackReal(txn, StringData(dbname))); if (!projection.includeID()) { fields = NULL; // do projection in post-processing } diff --git a/src/mongo/db/commands/geo_near_cmd.cpp b/src/mongo/db/commands/geo_near_cmd.cpp index f994004e2b2..7fb6b972efd 100644 --- a/src/mongo/db/commands/geo_near_cmd.cpp +++ b/src/mongo/db/commands/geo_near_cmd.cpp @@ -171,7 +171,7 @@ namespace mongo { CanonicalQuery* cq; const NamespaceString nss(dbname); - const WhereCallbackReal whereCallback(nss.db()); + const WhereCallbackReal whereCallback(txn, nss.db()); if (!CanonicalQuery::canonicalize(ns, rewritten, diff --git a/src/mongo/db/commands/group.cpp b/src/mongo/db/commands/group.cpp index 519a5989dd1..aed8e699e83 100644 --- a/src/mongo/db/commands/group.cpp +++ b/src/mongo/db/commands/group.cpp @@ -134,7 +134,7 @@ namespace mongo { Collection* collection = db->getCollection( txn, ns ); - const WhereCallbackReal whereCallback(StringData(db->name())); + const WhereCallbackReal whereCallback(txn, StringData(db->name())); map<BSONObj,int,BSONObjCmp> map; list<BSONObj> blah; diff --git a/src/mongo/db/commands/index_filter_commands.cpp b/src/mongo/db/commands/index_filter_commands.cpp index e0d5be5255a..139350b3002 100644 --- a/src/mongo/db/commands/index_filter_commands.cpp +++ b/src/mongo/db/commands/index_filter_commands.cpp @@ -67,9 +67,11 @@ namespace { /** * Retrieves a collection's query settings and plan cache from the database. */ - Status getQuerySettingsAndPlanCache(OperationContext* txn, Database* db, const string& ns, - QuerySettings** querySettingsOut, - PlanCache** planCacheOut) { + static Status getQuerySettingsAndPlanCache(OperationContext* txn, + Database* db, + const string& ns, + QuerySettings** querySettingsOut, + PlanCache** planCacheOut) { invariant(db); Collection* collection = db->getCollection(txn, ns); @@ -238,12 +240,15 @@ namespace mongo { // No collection - do nothing. return Status::OK(); } - return clear(querySettings, planCache, ns, cmdObj); + return clear(txn, querySettings, planCache, ns, cmdObj); } // static - Status ClearFilters::clear(QuerySettings* querySettings, PlanCache* planCache, - const std::string& ns, const BSONObj& cmdObj) { + Status ClearFilters::clear(OperationContext* txn, + QuerySettings* querySettings, + PlanCache* planCache, + const std::string& ns, + const BSONObj& cmdObj) { invariant(querySettings); // According to the specification, the planCacheClearFilters command runs in two modes: @@ -252,7 +257,7 @@ namespace mongo { // command arguments. if (cmdObj.hasField("query")) { CanonicalQuery* cqRaw; - Status status = PlanCacheCommand::canonicalize(ns, cmdObj, &cqRaw); + Status status = PlanCacheCommand::canonicalize(txn, ns, cmdObj, &cqRaw); if (!status.isOK()) { return status; } @@ -281,7 +286,7 @@ namespace mongo { querySettings->clearAllowedIndices(); const NamespaceString nss(ns); - const WhereCallbackReal whereCallback(nss.db()); + const WhereCallbackReal whereCallback(txn, nss.db()); // Remove corresponding entries from plan cache. // Admin hints affect the planning process directly. If there were @@ -328,12 +333,15 @@ namespace mongo { if (!status.isOK()) { return status; } - return set(querySettings, planCache, ns, cmdObj); + return set(txn, querySettings, planCache, ns, cmdObj); } // static - Status SetFilter::set(QuerySettings* querySettings, PlanCache* planCache, - const string& ns, const BSONObj& cmdObj) { + Status SetFilter::set(OperationContext* txn, + QuerySettings* querySettings, + PlanCache* planCache, + const string& ns, + const BSONObj& cmdObj) { // indexes - required BSONElement indexesElt = cmdObj.getField("indexes"); if (indexesElt.eoo()) { @@ -362,7 +370,7 @@ namespace mongo { } CanonicalQuery* cqRaw; - Status status = PlanCacheCommand::canonicalize(ns, cmdObj, &cqRaw); + Status status = PlanCacheCommand::canonicalize(txn, ns, cmdObj, &cqRaw); if (!status.isOK()) { return status; } diff --git a/src/mongo/db/commands/index_filter_commands.h b/src/mongo/db/commands/index_filter_commands.h index f6ba8fa9efb..2f106b16f36 100644 --- a/src/mongo/db/commands/index_filter_commands.h +++ b/src/mongo/db/commands/index_filter_commands.h @@ -138,7 +138,10 @@ namespace mongo { * Namespace argument ns is ignored if we are clearing the entire cache. * Removes corresponding entries from plan cache. */ - static Status clear(QuerySettings* querySettings, PlanCache* planCache, const std::string& ns, + static Status clear(OperationContext* txn, + QuerySettings* querySettings, + PlanCache* planCache, + const std::string& ns, const BSONObj& cmdObj); }; @@ -167,7 +170,10 @@ namespace mongo { * Sets index filter for a query shape. * Removes entry for query shape from plan cache. */ - static Status set(QuerySettings* querySettings, PlanCache* planCache, const std::string& ns, + static Status set(OperationContext* txn, + QuerySettings* querySettings, + PlanCache* planCache, + const std::string& ns, const BSONObj& cmdObj); }; diff --git a/src/mongo/db/commands/index_filter_commands_test.cpp b/src/mongo/db/commands/index_filter_commands_test.cpp index c3a5fc1d6a5..e892bcee7a8 100644 --- a/src/mongo/db/commands/index_filter_commands_test.cpp +++ b/src/mongo/db/commands/index_filter_commands_test.cpp @@ -33,6 +33,7 @@ #include "mongo/db/commands/index_filter_commands.h" #include "mongo/db/json.h" +#include "mongo/db/operation_context_noop.h" #include "mongo/db/query/plan_ranker.h" #include "mongo/db/query/query_solution.h" #include "mongo/unittest/unittest.h" @@ -181,35 +182,39 @@ namespace { TEST(IndexFilterCommandsTest, ClearFiltersInvalidParameter) { QuerySettings empty; PlanCache planCache; + OperationContextNoop txn; + // If present, query has to be an object. - ASSERT_NOT_OK(ClearFilters::clear(&empty, &planCache, ns, fromjson("{query: 1234}"))); + ASSERT_NOT_OK(ClearFilters::clear(&txn, &empty, &planCache, ns, fromjson("{query: 1234}"))); // If present, sort must be an object. - ASSERT_NOT_OK(ClearFilters::clear(&empty, &planCache, ns, + ASSERT_NOT_OK(ClearFilters::clear(&txn, &empty, &planCache, ns, fromjson("{query: {a: 1}, sort: 1234}"))); // If present, projection must be an object. - ASSERT_NOT_OK(ClearFilters::clear(&empty, &planCache, ns, + ASSERT_NOT_OK(ClearFilters::clear(&txn, &empty, &planCache, ns, fromjson("{query: {a: 1}, projection: 1234}"))); // Query must pass canonicalization. - ASSERT_NOT_OK(ClearFilters::clear(&empty, &planCache, ns, + ASSERT_NOT_OK(ClearFilters::clear(&txn, &empty, &planCache, ns, fromjson("{query: {a: {$no_such_op: 1}}}"))); // Sort present without query is an error. - ASSERT_NOT_OK(ClearFilters::clear(&empty, &planCache, ns, fromjson("{sort: {a: 1}}"))); + ASSERT_NOT_OK(ClearFilters::clear(&txn, &empty, &planCache, ns, fromjson("{sort: {a: 1}}"))); // Projection present without query is an error. - ASSERT_NOT_OK(ClearFilters::clear(&empty, &planCache, ns, + ASSERT_NOT_OK(ClearFilters::clear(&txn, &empty, &planCache, ns, fromjson("{projection: {_id: 0, a: 1}}"))); } TEST(IndexFilterCommandsTest, ClearNonexistentHint) { QuerySettings querySettings; PlanCache planCache; - ASSERT_OK(SetFilter::set(&querySettings, &planCache, ns, + OperationContextNoop txn; + + ASSERT_OK(SetFilter::set(&txn, &querySettings, &planCache, ns, fromjson("{query: {a: 1}, indexes: [{a: 1}]}"))); vector<BSONObj> filters = getFilters(querySettings); ASSERT_EQUALS(filters.size(), 1U); // Clear nonexistent hint. // Command should succeed and cache should remain unchanged. - ASSERT_OK(ClearFilters::clear(&querySettings, &planCache, ns, fromjson("{query: {b: 1}}"))); + ASSERT_OK(ClearFilters::clear(&txn, &querySettings, &planCache, ns, fromjson("{query: {b: 1}}"))); filters = getFilters(querySettings); ASSERT_EQUALS(filters.size(), 1U); } @@ -221,46 +226,49 @@ namespace { TEST(IndexFilterCommandsTest, SetFilterInvalidParameter) { QuerySettings empty; PlanCache planCache; - ASSERT_NOT_OK(SetFilter::set(&empty, &planCache, ns, fromjson("{}"))); + OperationContextNoop txn; + + ASSERT_NOT_OK(SetFilter::set(&txn, &empty, &planCache, ns, fromjson("{}"))); // Missing required query field. - ASSERT_NOT_OK(SetFilter::set(&empty, &planCache, ns, fromjson("{indexes: [{a: 1}]}"))); + ASSERT_NOT_OK(SetFilter::set(&txn, &empty, &planCache, ns, fromjson("{indexes: [{a: 1}]}"))); // Missing required indexes field. - ASSERT_NOT_OK(SetFilter::set(&empty, &planCache, ns, fromjson("{query: {a: 1}}"))); + ASSERT_NOT_OK(SetFilter::set(&txn, &empty, &planCache, ns, fromjson("{query: {a: 1}}"))); // Query has to be an object. - ASSERT_NOT_OK(SetFilter::set(&empty, &planCache, ns, + ASSERT_NOT_OK(SetFilter::set(&txn, &empty, &planCache, ns, fromjson("{query: 1234, indexes: [{a: 1}, {b: 1}]}"))); // Indexes field has to be an array. - ASSERT_NOT_OK(SetFilter::set(&empty, &planCache, ns, + ASSERT_NOT_OK(SetFilter::set(&txn, &empty, &planCache, ns, fromjson("{query: {a: 1}, indexes: 1234}"))); // Array indexes field cannot empty. - ASSERT_NOT_OK(SetFilter::set(&empty, &planCache, ns, + ASSERT_NOT_OK(SetFilter::set(&txn, &empty, &planCache, ns, fromjson("{query: {a: 1}, indexes: []}"))); // Elements in indexes have to be objects. - ASSERT_NOT_OK(SetFilter::set(&empty, &planCache, ns, + ASSERT_NOT_OK(SetFilter::set(&txn, &empty, &planCache, ns, fromjson("{query: {a: 1}, indexes: [{a: 1}, 99]}"))); // Objects in indexes cannot be empty. - ASSERT_NOT_OK(SetFilter::set(&empty, &planCache, ns, + ASSERT_NOT_OK(SetFilter::set(&txn, &empty, &planCache, ns, fromjson("{query: {a: 1}, indexes: [{a: 1}, {}]}"))); // If present, sort must be an object. - ASSERT_NOT_OK(SetFilter::set(&empty, &planCache, ns, + ASSERT_NOT_OK(SetFilter::set(&txn, &empty, &planCache, ns, fromjson("{query: {a: 1}, sort: 1234, indexes: [{a: 1}, {b: 1}]}"))); // If present, projection must be an object. - ASSERT_NOT_OK(SetFilter::set(&empty, &planCache, ns, + ASSERT_NOT_OK(SetFilter::set(&txn, &empty, &planCache, ns, fromjson("{query: {a: 1}, projection: 1234, indexes: [{a: 1}, {b: 1}]}"))); // Query must pass canonicalization. - ASSERT_NOT_OK(SetFilter::set(&empty, &planCache, ns, + ASSERT_NOT_OK(SetFilter::set(&txn, &empty, &planCache, ns, fromjson("{query: {a: {$no_such_op: 1}}, indexes: [{a: 1}, {b: 1}]}"))); } TEST(IndexFilterCommandsTest, SetAndClearFilters) { QuerySettings querySettings; PlanCache planCache; + OperationContextNoop txn; // Inject query shape into plan cache. addQueryShapeToPlanCache(&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(&querySettings, &planCache, ns, + ASSERT_OK(SetFilter::set(&txn, &querySettings, &planCache, ns, fromjson("{query: {a: 1, b: 1}, sort: {a: -1}, projection: {_id: 0, a: 1}, " "indexes: [{a: 1}]}"))); vector<BSONObj> filters = getFilters(querySettings); @@ -276,20 +284,20 @@ namespace { // 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(&querySettings, &planCache, ns, + ASSERT_OK(SetFilter::set(&txn, &querySettings, &planCache, ns, fromjson("{query: {b: 2, a: 3}, sort: {a: -1}, projection: {_id: 0, a: 1}, " "indexes: [{a: 1, b: 1}]}"))); filters = getFilters(querySettings); ASSERT_EQUALS(filters.size(), 1U); // Add hint for different query shape. - ASSERT_OK(SetFilter::set(&querySettings, &planCache, ns, + ASSERT_OK(SetFilter::set(&txn, &querySettings, &planCache, ns, fromjson("{query: {b: 1}, indexes: [{b: 1}]}"))); filters = getFilters(querySettings); ASSERT_EQUALS(filters.size(), 2U); // Add hint for 3rd query shape. This is to prepare for ClearHint tests. - ASSERT_OK(SetFilter::set(&querySettings, &planCache, ns, + ASSERT_OK(SetFilter::set(&txn, &querySettings, &planCache, ns, fromjson("{query: {a: 1}, indexes: [{a: 1}]}"))); filters = getFilters(querySettings); ASSERT_EQUALS(filters.size(), 3U); @@ -299,7 +307,7 @@ namespace { addQueryShapeToPlanCache(&planCache, "{b: 1}", "{}", "{}"); // Clear single hint. - ASSERT_OK(ClearFilters::clear(&querySettings, &planCache, ns, + ASSERT_OK(ClearFilters::clear(&txn, &querySettings, &planCache, ns, fromjson("{query: {a: 1}}"))); filters = getFilters(querySettings); ASSERT_EQUALS(filters.size(), 2U); @@ -309,7 +317,7 @@ namespace { ASSERT_TRUE(planCacheContains(planCache, "{b: 1}", "{}", "{}")); // Clear all filters - ASSERT_OK(ClearFilters::clear(&querySettings, &planCache, ns, fromjson("{}"))); + ASSERT_OK(ClearFilters::clear(&txn, &querySettings, &planCache, 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 58360d4cdd0..df0a6e7b9e3 100644 --- a/src/mongo/db/commands/mr.cpp +++ b/src/mongo/db/commands/mr.cpp @@ -977,7 +977,7 @@ namespace mongo { _db.count(_config.incLong, BSONObj(), QueryOption_SlaveOk))); const NamespaceString nss(_config.incLong); - const WhereCallbackReal whereCallback(nss.db()); + const WhereCallbackReal whereCallback(_txn, nss.db()); CanonicalQuery* cq; verify(CanonicalQuery::canonicalize(_config.incLong, @@ -1317,7 +1317,7 @@ namespace mongo { scoped_ptr<Client::Context> ctx(new Client::Context(txn, config.ns, false)); const NamespaceString nss(config.ns); - const WhereCallbackReal whereCallback(nss.db()); + const WhereCallbackReal whereCallback(txn, nss.db()); CanonicalQuery* cq; if (!CanonicalQuery::canonicalize(config.ns, diff --git a/src/mongo/db/commands/plan_cache_commands.cpp b/src/mongo/db/commands/plan_cache_commands.cpp index 7436ecc9eea..2a8bb83ef8c 100644 --- a/src/mongo/db/commands/plan_cache_commands.cpp +++ b/src/mongo/db/commands/plan_cache_commands.cpp @@ -157,7 +157,9 @@ namespace mongo { } // static - Status PlanCacheCommand::canonicalize(const string& ns, const BSONObj& cmdObj, + Status PlanCacheCommand::canonicalize(OperationContext* txn, + const string& ns, + const BSONObj& cmdObj, CanonicalQuery** canonicalQueryOut) { // query - required BSONElement queryElt = cmdObj.getField("query"); @@ -196,7 +198,7 @@ namespace mongo { CanonicalQuery* cqRaw; const NamespaceString nss(ns); - const WhereCallbackReal whereCallback(nss.db()); + const WhereCallbackReal whereCallback(txn, nss.db()); Status result = CanonicalQuery::canonicalize( ns, queryObj, sortObj, projObj, &cqRaw, whereCallback); @@ -273,11 +275,14 @@ namespace mongo { // No collection - nothing to do. Return OK status. return Status::OK(); } - return clear(planCache, ns, cmdObj); + return clear(txn, planCache, ns, cmdObj); } // static - Status PlanCacheClear::clear(PlanCache* planCache, const string& ns, const BSONObj& cmdObj) { + Status PlanCacheClear::clear(OperationContext* txn, + PlanCache* planCache, + const string& ns, + const BSONObj& cmdObj) { invariant(planCache); // According to the specification, the planCacheClear command runs in two modes: @@ -286,7 +291,7 @@ namespace mongo { // command arguments. if (cmdObj.hasField("query")) { CanonicalQuery* cqRaw; - Status status = PlanCacheCommand::canonicalize(ns, cmdObj, &cqRaw); + Status status = PlanCacheCommand::canonicalize(txn, ns, cmdObj, &cqRaw); if (!status.isOK()) { return status; } @@ -346,14 +351,17 @@ namespace mongo { plansBuilder.doneFast(); return Status::OK(); } - return list(*planCache, ns, cmdObj, bob); + return list(txn, *planCache, ns, cmdObj, bob); } // static - Status PlanCacheListPlans::list(const PlanCache& planCache, const std::string& ns, - const BSONObj& cmdObj, BSONObjBuilder* bob) { + Status PlanCacheListPlans::list(OperationContext* txn, + const PlanCache& planCache, + const std::string& ns, + const BSONObj& cmdObj, + BSONObjBuilder* bob) { CanonicalQuery* cqRaw; - Status status = canonicalize(ns, cmdObj, &cqRaw); + Status status = canonicalize(txn, ns, cmdObj, &cqRaw); if (!status.isOK()) { return status; } diff --git a/src/mongo/db/commands/plan_cache_commands.h b/src/mongo/db/commands/plan_cache_commands.h index 507d4fe4927..1a4a49a3c4f 100644 --- a/src/mongo/db/commands/plan_cache_commands.h +++ b/src/mongo/db/commands/plan_cache_commands.h @@ -89,7 +89,9 @@ namespace mongo { /** * Validatess query shape from command object and returns canonical query. */ - static Status canonicalize(const std::string& ns, const BSONObj& cmdObj, + static Status canonicalize(OperationContext* txn, + const std::string& ns, + const BSONObj& cmdObj, CanonicalQuery** canonicalQueryOut); private: @@ -141,7 +143,10 @@ namespace mongo { * Clears collection's plan cache. * If query shape is provided, clears plans for that single query shape only. */ - static Status clear(PlanCache* planCache, const std::string& ns, const BSONObj& cmdObj); + static Status clear(OperationContext* txn, + PlanCache* planCache, + const std::string& ns, + const BSONObj& cmdObj); }; /** @@ -166,8 +171,11 @@ namespace mongo { /** * Displays the cached plans for a query shape. */ - static Status list(const PlanCache& planCache, const std::string& ns, - const BSONObj& cmdObj, BSONObjBuilder* bob); + static Status list(OperationContext* txn, + const PlanCache& planCache, + const std::string& ns, + const BSONObj& cmdObj, + BSONObjBuilder* bob); }; } // namespace mongo diff --git a/src/mongo/db/commands/plan_cache_commands_test.cpp b/src/mongo/db/commands/plan_cache_commands_test.cpp index 5a6775cdb74..d2cae74ac12 100644 --- a/src/mongo/db/commands/plan_cache_commands_test.cpp +++ b/src/mongo/db/commands/plan_cache_commands_test.cpp @@ -34,6 +34,7 @@ #include <algorithm> #include "mongo/db/json.h" +#include "mongo/db/operation_context_noop.h" #include "mongo/db/query/plan_ranker.h" #include "mongo/db/query/query_solution.h" #include "mongo/unittest/unittest.h" @@ -154,6 +155,8 @@ namespace { // Plan cache with one entry PlanCache planCache; QuerySolution qs; + OperationContextNoop txn; + qs.cacheData.reset(createSolutionCacheData()); std::vector<QuerySolution*> solns; solns.push_back(&qs); @@ -161,7 +164,7 @@ namespace { ASSERT_EQUALS(getShapes(planCache).size(), 1U); // Clear cache and confirm number of keys afterwards. - ASSERT_OK(PlanCacheClear::clear(&planCache, ns, BSONObj())); + ASSERT_OK(PlanCacheClear::clear(&txn, &planCache, ns, BSONObj())); ASSERT_EQUALS(getShapes(planCache).size(), 0U); } @@ -173,35 +176,37 @@ namespace { TEST(PlanCacheCommandsTest, Canonicalize) { // Invalid parameters CanonicalQuery* cqRaw; + OperationContextNoop txn; + // Missing query field - ASSERT_NOT_OK(PlanCacheCommand::canonicalize(ns, fromjson("{}"), &cqRaw)); + ASSERT_NOT_OK(PlanCacheCommand::canonicalize(&txn, ns, fromjson("{}"), &cqRaw)); // Query needs to be an object - ASSERT_NOT_OK(PlanCacheCommand::canonicalize(ns, fromjson("{query: 1}"), &cqRaw)); + ASSERT_NOT_OK(PlanCacheCommand::canonicalize(&txn, ns, fromjson("{query: 1}"), &cqRaw)); // Sort needs to be an object - ASSERT_NOT_OK(PlanCacheCommand::canonicalize(ns, fromjson("{query: {}, sort: 1}"), + ASSERT_NOT_OK(PlanCacheCommand::canonicalize(&txn, ns, fromjson("{query: {}, sort: 1}"), &cqRaw)); // Bad query (invalid sort order) - ASSERT_NOT_OK(PlanCacheCommand::canonicalize(ns, fromjson("{query: {}, sort: {a: 0}}"), + ASSERT_NOT_OK(PlanCacheCommand::canonicalize(&txn, ns, fromjson("{query: {}, sort: {a: 0}}"), &cqRaw)); // Valid parameters - ASSERT_OK(PlanCacheCommand::canonicalize(ns, fromjson("{query: {a: 1, b: 1}}"), &cqRaw)); + ASSERT_OK(PlanCacheCommand::canonicalize(&txn, ns, fromjson("{query: {a: 1, b: 1}}"), &cqRaw)); scoped_ptr<CanonicalQuery> query(cqRaw); // Equivalent query should generate same key. - ASSERT_OK(PlanCacheCommand::canonicalize(ns, fromjson("{query: {b: 1, a: 1}}"), &cqRaw)); + ASSERT_OK(PlanCacheCommand::canonicalize(&txn, ns, fromjson("{query: {b: 1, a: 1}}"), &cqRaw)); scoped_ptr<CanonicalQuery> equivQuery(cqRaw); ASSERT_EQUALS(query->getPlanCacheKey(), equivQuery->getPlanCacheKey()); // Sort query should generate different key from unsorted query. - ASSERT_OK(PlanCacheCommand::canonicalize(ns, + ASSERT_OK(PlanCacheCommand::canonicalize(&txn, ns, fromjson("{query: {a: 1, b: 1}, sort: {a: 1}}"), &cqRaw)); scoped_ptr<CanonicalQuery> sortQuery(cqRaw); ASSERT_NOT_EQUALS(query->getPlanCacheKey(), sortQuery->getPlanCacheKey()); // Projected query should generate different key from unprojected query. - ASSERT_OK(PlanCacheCommand::canonicalize(ns, + ASSERT_OK(PlanCacheCommand::canonicalize(&txn, ns, fromjson("{query: {a: 1, b: 1}, projection: {_id: 0, a: 1}}"), &cqRaw)); scoped_ptr<CanonicalQuery> projectionQuery(cqRaw); ASSERT_NOT_EQUALS(query->getPlanCacheKey(), projectionQuery->getPlanCacheKey()); @@ -213,22 +218,26 @@ namespace { TEST(PlanCacheCommandsTest, planCacheClearInvalidParameter) { PlanCache planCache; + OperationContextNoop txn; + // Query field type must be BSON object. - ASSERT_NOT_OK(PlanCacheClear::clear(&planCache, ns, fromjson("{query: 12345}"))); - ASSERT_NOT_OK(PlanCacheClear::clear(&planCache, ns, fromjson("{query: /keyisnotregex/}"))); + ASSERT_NOT_OK(PlanCacheClear::clear(&txn, &planCache, ns, fromjson("{query: 12345}"))); + ASSERT_NOT_OK(PlanCacheClear::clear(&txn, &planCache, ns, fromjson("{query: /keyisnotregex/}"))); // Query must pass canonicalization. - ASSERT_NOT_OK(PlanCacheClear::clear(&planCache, ns, + ASSERT_NOT_OK(PlanCacheClear::clear(&txn, &planCache, ns, fromjson("{query: {a: {$no_such_op: 1}}}"))); // Sort present without query is an error. - ASSERT_NOT_OK(PlanCacheClear::clear(&planCache, ns, fromjson("{sort: {a: 1}}"))); + ASSERT_NOT_OK(PlanCacheClear::clear(&txn, &planCache, ns, fromjson("{sort: {a: 1}}"))); // Projection present without query is an error. - ASSERT_NOT_OK(PlanCacheClear::clear(&planCache, ns, + ASSERT_NOT_OK(PlanCacheClear::clear(&txn, &planCache, ns, fromjson("{projection: {_id: 0, a: 1}}"))); } TEST(PlanCacheCommandsTest, planCacheClearUnknownKey) { PlanCache planCache; - ASSERT_OK(PlanCacheClear::clear(&planCache, ns, fromjson("{query: {a: 1}}"))); + OperationContextNoop txn; + + ASSERT_OK(PlanCacheClear::clear(&txn, &planCache, ns, fromjson("{query: {a: 1}}"))); } TEST(PlanCacheCommandsTest, planCacheClearOneKey) { @@ -260,7 +269,9 @@ namespace { // Drop {b: 1} from cache. Make sure {a: 1} is still in cache afterwards. BSONObjBuilder bob; - ASSERT_OK(PlanCacheClear::clear(&planCache, ns, BSON("query" << cqB->getQueryObj()))); + OperationContextNoop txn; + + ASSERT_OK(PlanCacheClear::clear(&txn, &planCache, ns, BSON("query" << cqB->getQueryObj()))); vector<BSONObj> shapesAfter = getShapes(planCache); ASSERT_EQUALS(shapesAfter.size(), 1U); ASSERT_EQUALS(shapesAfter[0], shapeA); @@ -308,9 +319,11 @@ namespace { */ vector<BSONObj> getPlans(const PlanCache& planCache, const BSONObj& query, const BSONObj& sort, const BSONObj& projection) { + OperationContextNoop txn; + BSONObjBuilder bob; BSONObj cmdObj = BSON("query" << query << "sort" << sort << "projection" << projection); - ASSERT_OK(PlanCacheListPlans::list(planCache, ns, cmdObj, &bob)); + ASSERT_OK(PlanCacheListPlans::list(&txn, planCache, ns, cmdObj, &bob)); BSONObj resultObj = bob.obj(); BSONElement plansElt = resultObj.getField("plans"); ASSERT_EQUALS(plansElt.type(), mongo::Array); @@ -324,21 +337,24 @@ namespace { TEST(PlanCacheCommandsTest, planCacheListPlansInvalidParameter) { PlanCache planCache; BSONObjBuilder ignored; + OperationContextNoop txn; + // Missing query field is not ok. - ASSERT_NOT_OK(PlanCacheListPlans::list(planCache, ns, BSONObj(), &ignored)); + ASSERT_NOT_OK(PlanCacheListPlans::list(&txn, planCache, ns, BSONObj(), &ignored)); // Query field type must be BSON object. - ASSERT_NOT_OK(PlanCacheListPlans::list(planCache, ns, fromjson("{query: 12345}"), + ASSERT_NOT_OK(PlanCacheListPlans::list(&txn, planCache, ns, fromjson("{query: 12345}"), &ignored)); - ASSERT_NOT_OK(PlanCacheListPlans::list(planCache, ns, fromjson("{query: /keyisnotregex/}"), + ASSERT_NOT_OK(PlanCacheListPlans::list(&txn, planCache, ns, fromjson("{query: /keyisnotregex/}"), &ignored)); } TEST(PlanCacheCommandsTest, planCacheListPlansUnknownKey) { // Leave the plan cache empty. PlanCache planCache; + OperationContextNoop txn; BSONObjBuilder ignored; - ASSERT_OK(PlanCacheListPlans::list(planCache, ns, fromjson("{query: {a: 1}}"), &ignored)); + ASSERT_OK(PlanCacheListPlans::list(&txn, planCache, ns, fromjson("{query: {a: 1}}"), &ignored)); } TEST(PlanCacheCommandsTest, planCacheListPlansOnlyOneSolutionTrue) { diff --git a/src/mongo/db/commands/write_commands/batch_executor.cpp b/src/mongo/db/commands/write_commands/batch_executor.cpp index 2f3a39fa3d7..419c01be9c6 100644 --- a/src/mongo/db/commands/write_commands/batch_executor.cpp +++ b/src/mongo/db/commands/write_commands/batch_executor.cpp @@ -1088,7 +1088,7 @@ namespace mongo { WriteOpResult* result ) { const NamespaceString nsString(updateItem.getRequest()->getNS()); - UpdateRequest request(nsString); + UpdateRequest request(txn, nsString); request.setQuery(updateItem.getUpdate()->getQuery()); request.setUpdates(updateItem.getUpdate()->getUpdateExpr()); request.setMulti(updateItem.getUpdate()->getMulti()); @@ -1115,7 +1115,7 @@ namespace mongo { Client::Context ctx(txn, nsString.ns(), false /* don't check version */); try { - UpdateResult res = executor.execute(txn, ctx.db()); + UpdateResult res = executor.execute(ctx.db()); const long long numDocsModified = res.numDocsModified; const long long numMatched = res.numMatched; @@ -1149,7 +1149,7 @@ namespace mongo { WriteOpResult* result ) { const NamespaceString nss( removeItem.getRequest()->getNS() ); - DeleteRequest request( nss ); + DeleteRequest request(txn, nss); request.setQuery( removeItem.getDelete()->getQuery() ); request.setMulti( removeItem.getDelete()->getLimit() != 1 ); request.setUpdateOpLog(true); @@ -1178,7 +1178,7 @@ namespace mongo { Client::Context writeContext(txn, nss.ns(), false /* don't check version */); try { - result->getStats().n = executor.execute(txn, writeContext.db()); + result->getStats().n = executor.execute(writeContext.db()); } catch ( const DBException& ex ) { status = ex.toStatus(); diff --git a/src/mongo/db/dbhelpers.cpp b/src/mongo/db/dbhelpers.cpp index 8cc0eb0bd1c..ae6b560dbad 100644 --- a/src/mongo/db/dbhelpers.cpp +++ b/src/mongo/db/dbhelpers.cpp @@ -107,7 +107,7 @@ namespace mongo { return DiskLoc(); CanonicalQuery* cq; - const WhereCallbackReal whereCallback(collection->ns().db()); + const WhereCallbackReal whereCallback(txn, collection->ns().db()); massert(17244, "Could not canonicalize " + query.toString(), CanonicalQuery::canonicalize(collection->ns(), query, &cq, whereCallback).isOK()); @@ -216,7 +216,7 @@ namespace mongo { Client::Context context(txn, ns); const NamespaceString requestNs(ns); - UpdateRequest request(requestNs); + UpdateRequest request(txn, requestNs); request.setQuery(id); request.setUpdates(o); @@ -226,7 +226,7 @@ namespace mongo { UpdateLifecycleImpl updateLifecycle(true, requestNs); request.setLifecycle(&updateLifecycle); - update(txn, context.db(), request, &debug); + update(context.db(), request, &debug); } void Helpers::putSingleton(OperationContext* txn, const char *ns, BSONObj obj) { @@ -234,7 +234,7 @@ namespace mongo { Client::Context context(txn, ns); const NamespaceString requestNs(ns); - UpdateRequest request(requestNs); + UpdateRequest request(txn, requestNs); request.setUpdates(obj); request.setUpsert(); @@ -242,7 +242,7 @@ namespace mongo { UpdateLifecycleImpl updateLifecycle(true, requestNs); request.setLifecycle(&updateLifecycle); - update(txn, context.db(), request, &debug); + update(context.db(), request, &debug); context.getClient()->curop()->done(); } @@ -252,14 +252,14 @@ namespace mongo { Client::Context context(txn, ns); const NamespaceString requestNs(ns); - UpdateRequest request(requestNs); + UpdateRequest request(txn, requestNs); request.setGod(); request.setUpdates(obj); request.setUpsert(); request.setUpdateOpLog(logTheOp); - update(txn, context.db(), request, &debug); + update(context.db(), request, &debug); context.getClient()->curop()->done(); } diff --git a/src/mongo/db/exec/stagedebug_cmd.cpp b/src/mongo/db/exec/stagedebug_cmd.cpp index 346d481d8be..952b3b1e104 100644 --- a/src/mongo/db/exec/stagedebug_cmd.cpp +++ b/src/mongo/db/exec/stagedebug_cmd.cpp @@ -177,7 +177,7 @@ namespace mongo { BSONObj argObj = e.Obj(); if (filterTag == e.fieldName()) { StatusWithMatchExpression swme = MatchExpressionParser::parse( - argObj, WhereCallbackReal(collection->ns().db())); + argObj, WhereCallbackReal(txn, collection->ns().db())); if (!swme.isOK()) { return NULL; } // exprs is what will wind up deleting this. matcher = swme.getValue(); diff --git a/src/mongo/db/exec/subplan.cpp b/src/mongo/db/exec/subplan.cpp index 245450998b3..4019aae418b 100644 --- a/src/mongo/db/exec/subplan.cpp +++ b/src/mongo/db/exec/subplan.cpp @@ -156,7 +156,7 @@ namespace mongo { QLOG() << "Subplanner: index " << i << " is " << ie.toString() << endl; } - const WhereCallbackReal whereCallback(_collection->ns().db()); + const WhereCallbackReal whereCallback(_txn, _collection->ns().db()); for (size_t i = 0; i < theOr->numChildren(); ++i) { // Turn the i-th child into its own query. diff --git a/src/mongo/db/fts/fts_command_mongod.cpp b/src/mongo/db/fts/fts_command_mongod.cpp index a2c4b35e165..bebfa07cdfc 100644 --- a/src/mongo/db/fts/fts_command_mongod.cpp +++ b/src/mongo/db/fts/fts_command_mongod.cpp @@ -106,7 +106,7 @@ namespace mongo { limit, BSONObj(), &cq, - WhereCallbackReal(StringData(dbname))); + WhereCallbackReal(txn, StringData(dbname))); if (!canonicalizeStatus.isOK()) { errmsg = canonicalizeStatus.reason(); return false; diff --git a/src/mongo/db/instance.cpp b/src/mongo/db/instance.cpp index da48a427e23..fe90ef4b588 100644 --- a/src/mongo/db/instance.cpp +++ b/src/mongo/db/instance.cpp @@ -104,7 +104,7 @@ namespace mongo { MONGO_FP_DECLARE(rsStopGetMore); - static void inProgCmd( Message &m, DbResponse &dbresponse ) { + static void inProgCmd(OperationContext* txn, Message &m, DbResponse &dbresponse) { DbMessage d(m); QueryMessage q(d); BSONObjBuilder b; @@ -139,7 +139,7 @@ namespace mongo { Client& me = cc(); scoped_lock bl(Client::clientsMutex); - Matcher m(filter, WhereCallbackReal(nss.db())); + Matcher m(filter, WhereCallbackReal(txn, nss.db())); for( set<Client*>::iterator i = Client::clients.begin(); i != Client::clients.end(); i++ ) { Client *c = *i; verify( c ); @@ -346,7 +346,7 @@ namespace mongo { opwrite(m); if( strstr(ns, ".$cmd.sys.") ) { if( strstr(ns, "$cmd.sys.inprog") ) { - inProgCmd(m, dbresponse); + inProgCmd(txn, m, dbresponse); return; } if( strstr(ns, "$cmd.sys.killop") ) { @@ -587,7 +587,7 @@ namespace mongo { op.debug().query = query; op.setQuery(query); - UpdateRequest request(ns); + UpdateRequest request(txn, ns); request.setUpsert(upsert); request.setMulti(multi); @@ -609,7 +609,7 @@ namespace mongo { Client::Context ctx(txn, ns ); - UpdateResult res = executor.execute(txn, ctx.db()); + UpdateResult res = executor.execute(ctx.db()); // for getlasterror lastError.getSafe()->recordUpdate( res.existing , res.numMatched , res.upserted ); @@ -635,7 +635,7 @@ namespace mongo { op.debug().query = pattern; op.setQuery(pattern); - DeleteRequest request(ns); + DeleteRequest request(txn, ns); request.setQuery(pattern); request.setMulti(!justOne); request.setUpdateOpLog(true); @@ -650,7 +650,7 @@ namespace mongo { Client::Context ctx(txn, ns); - long long n = executor.execute(txn, ctx.db()); + long long n = executor.execute(ctx.db()); lastError.getSafe()->recordDelete( n ); op.debug().ndeleted = n; wunit.commit(); diff --git a/src/mongo/db/matcher/expression_parser.h b/src/mongo/db/matcher/expression_parser.h index b463d84f64a..0ba4013aa80 100644 --- a/src/mongo/db/matcher/expression_parser.h +++ b/src/mongo/db/matcher/expression_parser.h @@ -39,6 +39,8 @@ namespace mongo { + class OperationContext; + typedef StatusWith<MatchExpression*> StatusWithMatchExpression; class MatchExpressionParser { @@ -174,11 +176,19 @@ namespace mongo { */ class WhereCallbackReal : public MatchExpressionParser::WhereCallback { public: - WhereCallbackReal(const StringData& dbName); + + /** + * The OperationContext passed here is not owned, but just referenced. It gets assigned to + * any $where parsers, which this callback generates. Therefore, the op context must only + * be destroyed after these parsers and their clones (shallowClone) have been destroyed. + */ + WhereCallbackReal(OperationContext* txn, const StringData& dbName); virtual StatusWithMatchExpression parseWhere(const BSONElement& where) const; private: + // + OperationContext* _txn; const StringData _dbName; }; diff --git a/src/mongo/db/matcher/expression_where.cpp b/src/mongo/db/matcher/expression_where.cpp index 909c648063c..cef554357dc 100644 --- a/src/mongo/db/matcher/expression_where.cpp +++ b/src/mongo/db/matcher/expression_where.cpp @@ -43,7 +43,15 @@ namespace mongo { class WhereMatchExpression : public MatchExpression { public: - WhereMatchExpression() : MatchExpression( WHERE ){ _func = 0; } + WhereMatchExpression(OperationContext* txn) + : MatchExpression(WHERE), + _txn(txn) { + + invariant(_txn != NULL); + + _func = 0; + } + virtual ~WhereMatchExpression(){} Status init(const StringData& dbName, const StringData& theCode, const BSONObj& scope); @@ -55,9 +63,9 @@ namespace mongo { } virtual MatchExpression* shallowClone() const { - WhereMatchExpression* e = new WhereMatchExpression(); + WhereMatchExpression* e = new WhereMatchExpression(_txn); e->init(_dbName, _code, _userScope); - if ( getTag() ) { + if (getTag()) { e->setTag(getTag()->clone()); } return e; @@ -72,12 +80,16 @@ namespace mongo { virtual void resetTag() { setTag(NULL); } private: + string _dbName; string _code; BSONObj _userScope; auto_ptr<Scope> _scope; ScriptingFunction _func; + + // Not owned. See comments insde WhereCallbackReal for the lifetime of this pointer. + OperationContext* _txn; }; Status WhereMatchExpression::init( const StringData& dbName, @@ -98,6 +110,7 @@ namespace mongo { const string userToken = ClientBasic::getCurrent()->getAuthorizationSession() ->getAuthenticatedUserNamesToken(); + _scope = globalScriptEngine->getPooledScope(_dbName, "where" + userToken); _func = _scope->createFunction( _code.c_str() ); @@ -159,8 +172,9 @@ namespace mongo { _userScope == realOther->_userScope; } - WhereCallbackReal::WhereCallbackReal(const StringData& dbName) - : _dbName(dbName) { + WhereCallbackReal::WhereCallbackReal(OperationContext* txn, const StringData& dbName) + : _txn(txn), + _dbName(dbName) { } @@ -169,7 +183,7 @@ namespace mongo { return StatusWithMatchExpression(ErrorCodes::BadValue, "no globalScriptEngine in $where parsing"); - auto_ptr<WhereMatchExpression> exp(new WhereMatchExpression()); + auto_ptr<WhereMatchExpression> exp(new WhereMatchExpression(_txn)); if (where.type() == String || where.type() == Code) { Status s = exp->init(_dbName, where.valuestr(), BSONObj()); if (!s.isOK()) diff --git a/src/mongo/db/ops/delete.cpp b/src/mongo/db/ops/delete.cpp index c78ca1692f1..38b1b2aefe3 100644 --- a/src/mongo/db/ops/delete.cpp +++ b/src/mongo/db/ops/delete.cpp @@ -46,13 +46,13 @@ namespace mongo { bool logop, bool god) { NamespaceString nsString(ns); - DeleteRequest request(nsString); + DeleteRequest request(txn, nsString); request.setQuery(pattern); request.setMulti(!justOne); request.setUpdateOpLog(logop); request.setGod(god); DeleteExecutor executor(&request); - return executor.execute(txn, db); + return executor.execute(db); } } // namespace mongo diff --git a/src/mongo/db/ops/delete_executor.cpp b/src/mongo/db/ops/delete_executor.cpp index e7eb2a9546c..d2c36f3ba3c 100644 --- a/src/mongo/db/ops/delete_executor.cpp +++ b/src/mongo/db/ops/delete_executor.cpp @@ -66,7 +66,8 @@ namespace mongo { } CanonicalQuery* cqRaw; - const WhereCallbackReal whereCallback(_request->getNamespaceString().db()); + const WhereCallbackReal whereCallback( + _request->getOpCtx(), _request->getNamespaceString().db()); Status status = CanonicalQuery::canonicalize(_request->getNamespaceString().ns(), _request->getQuery(), @@ -80,7 +81,7 @@ namespace mongo { return status; } - long long DeleteExecutor::execute(OperationContext* txn, Database* db) { + long long DeleteExecutor::execute(Database* db) { uassertStatusOK(prepare()); uassert(17417, mongoutils::str::stream() << @@ -100,7 +101,7 @@ namespace mongo { } } - Collection* collection = db->getCollection(txn, ns.ns()); + Collection* collection = db->getCollection(_request->getOpCtx(), ns.ns()); if (NULL == collection) { return 0; } @@ -118,11 +119,14 @@ namespace mongo { Runner* rawRunner; if (_canonicalQuery.get()) { - uassertStatusOK(getRunner(txn, collection, _canonicalQuery.release(), &rawRunner)); + uassertStatusOK(getRunner(_request->getOpCtx(), + collection, + _canonicalQuery.release(), + &rawRunner)); } else { CanonicalQuery* ignored; - uassertStatusOK(getRunner(txn, + uassertStatusOK(getRunner(_request->getOpCtx(), collection, ns.ns(), _request->getQuery(), @@ -135,7 +139,7 @@ namespace mongo { DiskLoc rloc; Runner::RunnerState state; - CurOp* curOp = txn->getCurOp(); + CurOp* curOp = _request->getOpCtx()->getCurOp(); int oldYieldCount = curOp->numYields(); while (Runner::RUNNER_ADVANCED == (state = runner->getNext(NULL, &rloc))) { if (oldYieldCount != curOp->numYields()) { @@ -151,8 +155,9 @@ namespace mongo { // TODO: do we want to buffer docs and delete them in a group rather than // saving/restoring state repeatedly? runner->saveState(); - collection->deleteDocument(txn, rloc, false, false, logop ? &toDelete : NULL ); - runner->restoreState(txn); + collection->deleteDocument( + _request->getOpCtx(), rloc, false, false, logop ? &toDelete : NULL); + runner->restoreState(_request->getOpCtx()); nDeleted++; @@ -163,7 +168,8 @@ namespace mongo { } else { bool replJustOne = true; - repl::logOp(txn, "d", ns.ns().c_str(), toDelete, 0, &replJustOne); + repl::logOp( + _request->getOpCtx(), "d", ns.ns().c_str(), toDelete, 0, &replJustOne); } } @@ -172,7 +178,7 @@ namespace mongo { } if (!_request->isGod()) { - txn->recoveryUnit()->commitIfNeeded(); + _request->getOpCtx()->recoveryUnit()->commitIfNeeded(); } if (debug && _request->isGod() && nDeleted == 100) { diff --git a/src/mongo/db/ops/delete_executor.h b/src/mongo/db/ops/delete_executor.h index 475df92a105..c0324b14901 100644 --- a/src/mongo/db/ops/delete_executor.h +++ b/src/mongo/db/ops/delete_executor.h @@ -92,7 +92,7 @@ namespace mongo { * * Returns the number of documents deleted. */ - long long execute(OperationContext* txn, Database* db); + long long execute(Database* db); private: /// Unowned pointer to the request object that this executor will process. diff --git a/src/mongo/db/ops/delete_request.h b/src/mongo/db/ops/delete_request.h index 4e10cb409c2..3ce0a0bb9fd 100644 --- a/src/mongo/db/ops/delete_request.h +++ b/src/mongo/db/ops/delete_request.h @@ -39,7 +39,8 @@ namespace mongo { class DeleteRequest { MONGO_DISALLOW_COPYING(DeleteRequest); public: - explicit DeleteRequest(const NamespaceString& nsString) : + explicit DeleteRequest(OperationContext* txn, const NamespaceString& nsString) : + _txn(txn), _nsString(nsString), _multi(false), _logop(false), @@ -55,10 +56,12 @@ namespace mongo { bool isMulti() const { return _multi; } bool shouldCallLogOp() const { return _logop; } bool isGod() const { return _god; } + OperationContext* getOpCtx() const { return _txn; } std::string toString() const; private: + OperationContext* _txn; const NamespaceString& _nsString; BSONObj _query; bool _multi; diff --git a/src/mongo/db/ops/update.cpp b/src/mongo/db/ops/update.cpp index 53e67a9baf9..f872f39a97d 100644 --- a/src/mongo/db/ops/update.cpp +++ b/src/mongo/db/ops/update.cpp @@ -421,22 +421,19 @@ namespace mongo { } } // namespace - UpdateResult update(OperationContext* txn, - Database* db, + UpdateResult update(Database* db, const UpdateRequest& request, OpDebug* opDebug) { UpdateExecutor executor(&request, opDebug); - return executor.execute(txn, db); + return executor.execute(db); } - UpdateResult update( - OperationContext* txn, - Database* db, - const UpdateRequest& request, - OpDebug* opDebug, - UpdateDriver* driver, - CanonicalQuery* cq) { + UpdateResult update(Database* db, + const UpdateRequest& request, + OpDebug* opDebug, + UpdateDriver* driver, + CanonicalQuery* cq) { LOG(3) << "processing update : " << request; @@ -444,7 +441,7 @@ namespace mongo { const NamespaceString& nsString = request.getNamespaceString(); UpdateLifecycle* lifecycle = request.getLifecycle(); - Collection* collection = db->getCollection(txn, nsString.ns()); + Collection* collection = db->getCollection(request.getOpCtx(), nsString.ns()); validateUpdate(nsString.ns().c_str(), request.getUpdates(), request.getQuery()); @@ -459,8 +456,9 @@ namespace mongo { PlanExecutor* rawExec; Status status = cq ? - getExecutor(txn, collection, cqHolder.release(), &rawExec) : - getExecutor(txn, collection, nsString.ns(), request.getQuery(), &rawExec); + getExecutor(request.getOpCtx(), collection, cqHolder.release(), &rawExec) : + getExecutor(request.getOpCtx(), collection, nsString.ns(), request.getQuery(), &rawExec); + uassert(17243, "could not get executor" + request.getQuery().toString() + "; " + causedBy(status), status.isOK()); @@ -628,7 +626,7 @@ namespace mongo { // If a set of modifiers were all no-ops, we are still 'in place', but there is // no work to do, in which case we want to consider the object unchanged. if (!damages.empty() ) { - collection->updateDocumentWithDamages( txn, loc, source, damages ); + collection->updateDocumentWithDamages(request.getOpCtx(), loc, source, damages); docWasModified = true; opDebug->fastmod = true; } @@ -647,7 +645,7 @@ namespace mongo { str::stream() << "Resulting document after update is larger than " << BSONObjMaxUserSize, newObj.objsize() <= BSONObjMaxUserSize); - StatusWith<DiskLoc> res = collection->updateDocument(txn, + StatusWith<DiskLoc> res = collection->updateDocument(request.getOpCtx(), loc, newObj, true, @@ -674,7 +672,7 @@ namespace mongo { // Call logOp if requested. if (request.shouldCallLogOp() && !logObj.isEmpty()) { BSONObj idQuery = driver->makeOplogEntryQuery(newObj, request.isMulti()); - repl::logOp(txn, "u", nsString.ns().c_str(), logObj , &idQuery, + repl::logOp(request.getOpCtx(), "u", nsString.ns().c_str(), logObj, &idQuery, NULL, request.isFromMigration()); } @@ -687,7 +685,7 @@ namespace mongo { } // Opportunity for journaling to write during the update. - txn->recoveryUnit()->commitIfNeeded(); + request.getOpCtx()->recoveryUnit()->commitIfNeeded(); } // Get summary information about the plan. @@ -775,9 +773,9 @@ namespace mongo { // Only create the collection if the doc will be inserted. if (!collection) { - collection = db->getCollection(txn, request.getNamespaceString().ns()); + collection = db->getCollection(request.getOpCtx(), request.getNamespaceString().ns()); if (!collection) { - collection = db->createCollection(txn, request.getNamespaceString().ns()); + collection = db->createCollection(request.getOpCtx(), request.getNamespaceString().ns()); } } @@ -787,13 +785,18 @@ namespace mongo { str::stream() << "Document to upsert is larger than " << BSONObjMaxUserSize, newObj.objsize() <= BSONObjMaxUserSize); - StatusWith<DiskLoc> newLoc = collection->insertDocument(txn, + StatusWith<DiskLoc> newLoc = collection->insertDocument(request.getOpCtx(), newObj, !request.isGod() /*enforceQuota*/); uassertStatusOK(newLoc.getStatus()); if (request.shouldCallLogOp()) { - repl::logOp(txn, "i", nsString.ns().c_str(), newObj, - NULL, NULL, request.isFromMigration()); + repl::logOp(request.getOpCtx(), + "i", + nsString.ns().c_str(), + newObj, + NULL, + NULL, + request.isFromMigration()); } opDebug->nMatched = 1; diff --git a/src/mongo/db/ops/update.h b/src/mongo/db/ops/update.h index f950fbaf08c..cff79a0d90b 100644 --- a/src/mongo/db/ops/update.h +++ b/src/mongo/db/ops/update.h @@ -46,8 +46,7 @@ namespace mongo { * * Caller must hold the appropriate database locks. */ - UpdateResult update(OperationContext* txn, - Database* db, + UpdateResult update(Database* db, const UpdateRequest& request, OpDebug* opDebug); @@ -59,8 +58,7 @@ namespace mongo { * * TODO: Move this into a private method of UpdateExecutor. */ - UpdateResult update(OperationContext* txn, - Database* db, + UpdateResult update(Database* db, const UpdateRequest& request, OpDebug* opDebug, UpdateDriver* driver, diff --git a/src/mongo/db/ops/update_executor.cpp b/src/mongo/db/ops/update_executor.cpp index fd2f11701a7..268e7616d3c 100644 --- a/src/mongo/db/ops/update_executor.cpp +++ b/src/mongo/db/ops/update_executor.cpp @@ -62,10 +62,9 @@ namespace mongo { return Status::OK(); } - UpdateResult UpdateExecutor::execute(OperationContext* txn, Database* db) { + UpdateResult UpdateExecutor::execute(Database* db) { uassertStatusOK(prepare()); - return update(txn, - db, + return update(db, *_request, _opDebug, &_driver, @@ -85,7 +84,8 @@ namespace mongo { } CanonicalQuery* cqRaw; - const WhereCallbackReal whereCallback(_request->getNamespaceString().db()); + const WhereCallbackReal whereCallback( + _request->getOpCtx(), _request->getNamespaceString().db()); Status status = CanonicalQuery::canonicalize(_request->getNamespaceString().ns(), _request->getQuery(), diff --git a/src/mongo/db/ops/update_executor.h b/src/mongo/db/ops/update_executor.h index 881c0cc0d36..f4a62224c59 100644 --- a/src/mongo/db/ops/update_executor.h +++ b/src/mongo/db/ops/update_executor.h @@ -91,7 +91,7 @@ namespace mongo { * Execute an update. Requires the caller to hold the database lock on the * appropriate resources for the request. */ - UpdateResult execute(OperationContext* txn, Database* db); + UpdateResult execute(Database* db); private: /** diff --git a/src/mongo/db/ops/update_request.h b/src/mongo/db/ops/update_request.h index db0dcbf1e5e..5ed6eca209a 100644 --- a/src/mongo/db/ops/update_request.h +++ b/src/mongo/db/ops/update_request.h @@ -42,8 +42,9 @@ namespace mongo { class UpdateRequest { public: - inline UpdateRequest(const NamespaceString& nsString) - : _nsString(nsString) + inline UpdateRequest(OperationContext* txn, const NamespaceString& nsString) + : _txn(txn) + , _nsString(nsString) , _god(false) , _upsert(false) , _multi(false) @@ -131,6 +132,10 @@ namespace mongo { return _lifecycle; } + inline OperationContext* getOpCtx() const { + return _txn; + } + const std::string toString() const { return str::stream() << " query: " << _query @@ -144,6 +149,9 @@ namespace mongo { } private: + // Not owned. Must live as long as the request lives. + OperationContext* _txn; + const NamespaceString& _nsString; // Contains the query that selects documents to update. diff --git a/src/mongo/db/pipeline/pipeline_d.cpp b/src/mongo/db/pipeline/pipeline_d.cpp index 1f6f93f1274..9259a2a8072 100644 --- a/src/mongo/db/pipeline/pipeline_d.cpp +++ b/src/mongo/db/pipeline/pipeline_d.cpp @@ -167,7 +167,7 @@ namespace { boost::shared_ptr<Runner> runner; bool sortInRunner = false; - const WhereCallbackReal whereCallback(pExpCtx->ns.db()); + const WhereCallbackReal whereCallback(pExpCtx->opCtx, pExpCtx->ns.db()); if (sortStage) { CanonicalQuery* cq; diff --git a/src/mongo/db/query/get_executor.cpp b/src/mongo/db/query/get_executor.cpp index f153434e3b0..4b56863eeaa 100644 --- a/src/mongo/db/query/get_executor.cpp +++ b/src/mongo/db/query/get_executor.cpp @@ -181,7 +181,7 @@ namespace mongo { if (!CanonicalQuery::isSimpleIdQuery(unparsedQuery) || !collection->getIndexCatalog()->findIdIndex()) { - const WhereCallbackReal whereCallback(collection->ns().db()); + const WhereCallbackReal whereCallback(txn, collection->ns().db()); CanonicalQuery* cq; Status status = CanonicalQuery::canonicalize( collection->ns(), unparsedQuery, &cq, whereCallback); @@ -230,7 +230,7 @@ namespace mongo { // so we don't support covered projections. However, we might use the simple inclusion // fast path. if (NULL != canonicalQuery->getProj()) { - ProjectionStageParams params(WhereCallbackReal(collection->ns().db())); + ProjectionStageParams params(WhereCallbackReal(txn, collection->ns().db())); params.projObj = canonicalQuery->getProj()->getProjObj(); // Stuff the right data into the params depending on what proj impl we use. @@ -652,7 +652,7 @@ namespace mongo { PlanExecutor** execOut) { invariant(collection); - const WhereCallbackReal whereCallback(collection->ns().db()); + const WhereCallbackReal whereCallback(txn, collection->ns().db()); CanonicalQuery* cq; uassertStatusOK(CanonicalQuery::canonicalize(collection->ns().ns(), @@ -759,7 +759,7 @@ namespace mongo { } } - const WhereCallbackReal whereCallback(collection->ns().db()); + const WhereCallbackReal whereCallback(txn, collection->ns().db()); // If there are no suitable indices for the distinct hack bail out now into regular planning // with no projection. diff --git a/src/mongo/db/query/get_runner.cpp b/src/mongo/db/query/get_runner.cpp index 429cbc15d33..e93d670a898 100644 --- a/src/mongo/db/query/get_runner.cpp +++ b/src/mongo/db/query/get_runner.cpp @@ -88,7 +88,7 @@ namespace mongo { if (!CanonicalQuery::isSimpleIdQuery(unparsedQuery) || !collection->getIndexCatalog()->findIdIndex()) { - const WhereCallbackReal whereCallback(collection->ns().db()); + const WhereCallbackReal whereCallback(txn, collection->ns().db()); Status status = CanonicalQuery::canonicalize( collection->ns(), unparsedQuery, outCanonicalQuery, whereCallback); if (!status.isOK()) @@ -516,7 +516,7 @@ namespace mongo { Runner** out) { verify(collection); - const WhereCallbackReal whereCallback(collection->ns().db()); + const WhereCallbackReal whereCallback(txn, collection->ns().db()); CanonicalQuery* cq; uassertStatusOK(CanonicalQuery::canonicalize(collection->ns().ns(), @@ -573,7 +573,7 @@ namespace mongo { } } - const WhereCallbackReal whereCallback(collection->ns().db()); + const WhereCallbackReal whereCallback(txn, collection->ns().db()); // If there are no suitable indices for the distinct hack bail out now into regular planning // with no projection. diff --git a/src/mongo/db/query/idhack_runner.cpp b/src/mongo/db/query/idhack_runner.cpp index 5382a4e4bfa..cf8f8dc78f5 100644 --- a/src/mongo/db/query/idhack_runner.cpp +++ b/src/mongo/db/query/idhack_runner.cpp @@ -174,7 +174,7 @@ namespace mongo { BSONObj projectedObj; ProjectionExec projExec(projObj, _query->root(), - WhereCallbackReal(_collection->ns().db())); + WhereCallbackReal(_txn, _collection->ns().db())); projExec.transform(docObj, &projectedObj); return projectedObj; } diff --git a/src/mongo/db/query/new_find.cpp b/src/mongo/db/query/new_find.cpp index 09ab4104d15..3cc4a035a6e 100644 --- a/src/mongo/db/query/new_find.cpp +++ b/src/mongo/db/query/new_find.cpp @@ -496,7 +496,7 @@ namespace mongo { // Parse the qm into a CanonicalQuery. CanonicalQuery* cq; Status canonStatus = CanonicalQuery::canonicalize( - q, &cq, WhereCallbackReal(StringData(ctx.ctx().db()->name()))); + q, &cq, WhereCallbackReal(txn, StringData(ctx.ctx().db()->name()))); if (!canonStatus.isOK()) { uasserted(17287, str::stream() << "Can't canonicalize query: " << canonStatus.toString()); } diff --git a/src/mongo/db/query/stage_builder.cpp b/src/mongo/db/query/stage_builder.cpp index b0441eb0a25..bec04c13fba 100644 --- a/src/mongo/db/query/stage_builder.cpp +++ b/src/mongo/db/query/stage_builder.cpp @@ -113,7 +113,7 @@ namespace mongo { PlanStage* childStage = buildStages(txn, collection, qsol, pn->children[0], ws); if (NULL == childStage) { return NULL; } - ProjectionStageParams params(WhereCallbackReal(collection->ns().db())); + ProjectionStageParams params(WhereCallbackReal(txn, collection->ns().db())); params.projObj = pn->projection; // Stuff the right data into the params depending on what proj impl we use. diff --git a/src/mongo/db/query/subplan_runner.cpp b/src/mongo/db/query/subplan_runner.cpp index c107f829c77..4682c29b2b4 100644 --- a/src/mongo/db/query/subplan_runner.cpp +++ b/src/mongo/db/query/subplan_runner.cpp @@ -203,7 +203,7 @@ namespace mongo { QLOG() << "Subplanner: index " << i << " is " << ie.toString() << endl; } - const WhereCallbackReal whereCallback(_collection->ns().db()); + const WhereCallbackReal whereCallback(_txn, _collection->ns().db()); for (size_t i = 0; i < theOr->numChildren(); ++i) { // Turn the i-th child into its own query. diff --git a/src/mongo/db/repl/master_slave.cpp b/src/mongo/db/repl/master_slave.cpp index 0e216cdd05b..5e591b41f4f 100644 --- a/src/mongo/db/repl/master_slave.cpp +++ b/src/mongo/db/repl/master_slave.cpp @@ -207,13 +207,13 @@ namespace repl { Client::Context ctx(txn, "local.sources"); const NamespaceString requestNs("local.sources"); - UpdateRequest request(requestNs); + UpdateRequest request(txn, requestNs); request.setQuery(pattern); request.setUpdates(o); request.setUpsert(); - UpdateResult res = update(txn, ctx.db(), request, &debug); + UpdateResult res = update(ctx.db(), request, &debug); verify( ! res.modifiers ); verify( res.numMatched == 1 ); diff --git a/src/mongo/db/repl/oplog.cpp b/src/mongo/db/repl/oplog.cpp index e911c19a852..36d7d7810da 100644 --- a/src/mongo/db/repl/oplog.cpp +++ b/src/mongo/db/repl/oplog.cpp @@ -619,7 +619,7 @@ namespace repl { Timer t; const NamespaceString requestNs(ns); - UpdateRequest request(requestNs); + UpdateRequest request(txn, requestNs); request.setQuery(o); request.setUpdates(o); @@ -628,7 +628,7 @@ namespace repl { UpdateLifecycleImpl updateLifecycle(true, requestNs); request.setLifecycle(&updateLifecycle); - update(txn, db, request, &debug); + update(db, request, &debug); if( t.millis() >= 2 ) { RARELY OCCASIONALLY log() << "warning, repl doing slow updates (no _id field) for " << ns << endl; @@ -648,7 +648,7 @@ namespace repl { b.append(_id); const NamespaceString requestNs(ns); - UpdateRequest request(requestNs); + UpdateRequest request(txn, requestNs); request.setQuery(b.done()); request.setUpdates(o); @@ -657,7 +657,7 @@ namespace repl { UpdateLifecycleImpl updateLifecycle(true, requestNs); request.setLifecycle(&updateLifecycle); - update(txn, db, request, &debug); + update(db, request, &debug); } } } @@ -675,7 +675,7 @@ namespace repl { const bool upsert = valueB || convertUpdateToUpsert; const NamespaceString requestNs(ns); - UpdateRequest request(requestNs); + UpdateRequest request(txn, requestNs); request.setQuery(updateCriteria); request.setUpdates(o); @@ -684,7 +684,7 @@ namespace repl { UpdateLifecycleImpl updateLifecycle(true, requestNs); request.setLifecycle(&updateLifecycle); - UpdateResult ur = update(txn, db, request, &debug); + UpdateResult ur = update(db, request, &debug); if( ur.numMatched == 0 ) { if( ur.modifiers ) { diff --git a/src/mongo/db/repl/rs_rollback.cpp b/src/mongo/db/repl/rs_rollback.cpp index 445183fc5c9..b65dbb324bb 100644 --- a/src/mongo/db/repl/rs_rollback.cpp +++ b/src/mongo/db/repl/rs_rollback.cpp @@ -632,7 +632,7 @@ namespace repl { updates++; const NamespaceString requestNs(doc.ns); - UpdateRequest request(requestNs); + UpdateRequest request(txn, requestNs); request.setQuery(pattern); request.setUpdates(it->second); @@ -641,7 +641,7 @@ namespace repl { UpdateLifecycleImpl updateLifecycle(true, requestNs); request.setLifecycle(&updateLifecycle); - update(txn, ctx.db(), request, &debug); + update(ctx.db(), request, &debug); } } diff --git a/src/mongo/dbtests/matchertests.cpp b/src/mongo/dbtests/matchertests.cpp index cee3995395c..efe21b9cb88 100644 --- a/src/mongo/dbtests/matchertests.cpp +++ b/src/mongo/dbtests/matchertests.cpp @@ -222,7 +222,7 @@ namespace MatcherTests { Client::ReadContext ctx(&txn, "unittests.matchertests"); M m(BSON("$where" << "function(){ return this.a == 1; }"), - WhereCallbackReal(StringData("unittests"))); + WhereCallbackReal(&txn, StringData("unittests"))); ASSERT( m.matches( BSON( "a" << 1 ) ) ); ASSERT( !m.matches( BSON( "a" << 2 ) ) ); } |