summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDavid Storch <david.storch@10gen.com>2014-12-02 20:10:01 -0500
committerDavid Storch <david.storch@10gen.com>2014-12-04 17:41:11 -0500
commit367810995073e01ee58159deb1bb5b878882632f (patch)
tree3074960c42a7b63e4e3be337e44f33a036a6679f /src
parent5080932a79ce0152535169ec3b42edde2951bdcd (diff)
downloadmongo-367810995073e01ee58159deb1bb5b878882632f.tar.gz
SERVER-16101 replace DeleteExecutor with ParsedDelete
Diffstat (limited to 'src')
-rw-r--r--src/mongo/SConscript2
-rw-r--r--src/mongo/db/commands/write_commands/batch_executor.cpp20
-rw-r--r--src/mongo/db/commands/write_commands/write_commands.cpp26
-rw-r--r--src/mongo/db/exec/delete.cpp10
-rw-r--r--src/mongo/db/exec/delete.h8
-rw-r--r--src/mongo/db/instance.cpp20
-rw-r--r--src/mongo/db/ops/delete.cpp21
-rw-r--r--src/mongo/db/ops/delete_executor.cpp199
-rw-r--r--src/mongo/db/ops/delete_executor.h132
-rw-r--r--src/mongo/db/ops/parsed_delete.cpp104
-rw-r--r--src/mongo/db/ops/parsed_delete.h109
-rw-r--r--src/mongo/db/query/get_executor.cpp152
-rw-r--r--src/mongo/db/query/get_executor.h50
13 files changed, 385 insertions, 468 deletions
diff --git a/src/mongo/SConscript b/src/mongo/SConscript
index da637d789d1..a1979673f24 100644
--- a/src/mongo/SConscript
+++ b/src/mongo/SConscript
@@ -649,8 +649,8 @@ serverOnlyFiles = [ "db/background.cpp",
"db/matcher/expression_where.cpp",
"db/operation_context_impl.cpp",
"db/ops/delete.cpp",
- "db/ops/delete_executor.cpp",
"db/ops/insert.cpp",
+ "db/ops/parsed_delete.cpp",
"db/ops/parsed_update.cpp",
"db/ops/update.cpp",
"db/ops/update_lifecycle_impl.cpp",
diff --git a/src/mongo/db/commands/write_commands/batch_executor.cpp b/src/mongo/db/commands/write_commands/batch_executor.cpp
index a5801823f0d..cf356b848c3 100644
--- a/src/mongo/db/commands/write_commands/batch_executor.cpp
+++ b/src/mongo/db/commands/write_commands/batch_executor.cpp
@@ -46,9 +46,10 @@
#include "mongo/db/catalog/database_holder.h"
#include "mongo/db/catalog/index_create.h"
#include "mongo/db/concurrency/write_conflict_exception.h"
+#include "mongo/db/exec/delete.h"
#include "mongo/db/exec/update.h"
-#include "mongo/db/ops/delete_executor.h"
#include "mongo/db/ops/delete_request.h"
+#include "mongo/db/ops/parsed_delete.h"
#include "mongo/db/ops/parsed_update.h"
#include "mongo/db/ops/insert.h"
#include "mongo/db/ops/update_lifecycle_impl.h"
@@ -1345,9 +1346,9 @@ namespace mongo {
while ( 1 ) {
try {
- DeleteExecutor executor( txn, &request );
- Status status = executor.prepare();
- if ( !status.isOK() ) {
+ ParsedDelete parsedDelete(txn, &request);
+ Status status = parsedDelete.parseRequest();
+ if (!status.isOK()) {
result->setError(toWriteError(status));
return;
}
@@ -1368,7 +1369,16 @@ namespace mongo {
// TODO: better constructor?
Client::Context ctx(txn, nss.ns(), false /* don't check version */);
- result->getStats().n = executor.execute(autoDb.getDb());
+ PlanExecutor* rawExec;
+ uassertStatusOK(getExecutorDelete(txn,
+ ctx.db()->getCollection(txn, nss),
+ &parsedDelete,
+ &rawExec));
+ boost::scoped_ptr<PlanExecutor> exec(rawExec);
+
+ // Execute the delete and retrieve the number deleted.
+ uassertStatusOK(exec->executePlan());
+ result->getStats().n = DeleteStage::getNumDeleted(exec.get());
break;
}
diff --git a/src/mongo/db/commands/write_commands/write_commands.cpp b/src/mongo/db/commands/write_commands/write_commands.cpp
index 2b7f523636f..b6a71b468cb 100644
--- a/src/mongo/db/commands/write_commands/write_commands.cpp
+++ b/src/mongo/db/commands/write_commands/write_commands.cpp
@@ -38,8 +38,8 @@
#include "mongo/db/curop.h"
#include "mongo/db/json.h"
#include "mongo/db/lasterror.h"
-#include "mongo/db/ops/delete_executor.h"
#include "mongo/db/ops/delete_request.h"
+#include "mongo/db/ops/parsed_delete.h"
#include "mongo/db/ops/parsed_update.h"
#include "mongo/db/ops/update_lifecycle_impl.h"
#include "mongo/db/query/explain.h"
@@ -252,12 +252,10 @@ namespace mongo {
// Explained deletes can yield.
deleteRequest.setYieldPolicy(PlanExecutor::YIELD_AUTO);
- // Use the request to create a DeleteExecutor, and from it extract the
- // plan tree which will be used to execute this update.
- DeleteExecutor deleteExecutor( txn, &deleteRequest );
- Status prepStatus = deleteExecutor.prepare();
- if ( !prepStatus.isOK() ) {
- return prepStatus;
+ ParsedDelete parsedDelete(txn, &deleteRequest);
+ Status parseStatus = parsedDelete.parseRequest();
+ if (!parseStatus.isOK()) {
+ return parseStatus;
}
// Explains of write commands are read-only, but we take write locks so that timing
@@ -271,16 +269,18 @@ namespace mongo {
// or collections.
ensureShardVersionOKOrThrow( nsString.ns() );
- Status prepInLockStatus = deleteExecutor.prepareInLock( autoDb.getDb() );
- if ( !prepInLockStatus.isOK()) {
- return prepInLockStatus;
+ // Get a pointer to the (possibly NULL) collection.
+ Collection* collection = NULL;
+ if (autoDb.getDb()) {
+ collection = autoDb.getDb()->getCollection(txn, nsString.ns());
}
- // Executor registration and yield policy is handled internally by the delete executor.
- PlanExecutor* exec = deleteExecutor.getPlanExecutor();
+ PlanExecutor* rawExec;
+ uassertStatusOK(getExecutorDelete(txn, collection, &parsedDelete, &rawExec));
+ boost::scoped_ptr<PlanExecutor> exec(rawExec);
// Explain the plan tree.
- Explain::explainStages( exec, verbosity, out );
+ Explain::explainStages(exec.get(), verbosity, out);
return Status::OK();
}
}
diff --git a/src/mongo/db/exec/delete.cpp b/src/mongo/db/exec/delete.cpp
index 6d9e2829cbf..1765a698a60 100644
--- a/src/mongo/db/exec/delete.cpp
+++ b/src/mongo/db/exec/delete.cpp
@@ -204,4 +204,14 @@ namespace mongo {
return &_specificStats;
}
+ // static
+ long long DeleteStage::getNumDeleted(PlanExecutor* exec) {
+ invariant(exec->getRootStage()->isEOF());
+ invariant(exec->getRootStage()->stageType() == STAGE_DELETE);
+ DeleteStage* deleteStage = static_cast<DeleteStage*>(exec->getRootStage());
+ const DeleteStats* deleteStats =
+ static_cast<const DeleteStats*>(deleteStage->getSpecificStats());
+ return deleteStats->docsDeleted;
+ }
+
} // namespace mongo
diff --git a/src/mongo/db/exec/delete.h b/src/mongo/db/exec/delete.h
index aa7512eb1e8..4c64679fde9 100644
--- a/src/mongo/db/exec/delete.h
+++ b/src/mongo/db/exec/delete.h
@@ -34,6 +34,7 @@
namespace mongo {
class OperationContext;
+ class PlanExecutor;
struct DeleteStageParams {
DeleteStageParams() :
@@ -93,6 +94,13 @@ namespace mongo {
static const char* kStageType;
+ /**
+ * Extracts the number of documents deleted by the update plan 'exec'.
+ *
+ * Should only be called if the root plan stage of 'exec' is UPDATE and if 'exec' is EOF.
+ */
+ static long long getNumDeleted(PlanExecutor* exec);
+
private:
// Transactional context. Not owned by us.
OperationContext* _txn;
diff --git a/src/mongo/db/instance.cpp b/src/mongo/db/instance.cpp
index 64a8e14451a..74ebd47def0 100644
--- a/src/mongo/db/instance.cpp
+++ b/src/mongo/db/instance.cpp
@@ -64,10 +64,11 @@
#include "mongo/db/mongod_options.h"
#include "mongo/db/namespace_string.h"
#include "mongo/db/catalog/index_create.h"
+#include "mongo/db/exec/delete.h"
#include "mongo/db/exec/update.h"
-#include "mongo/db/ops/delete_executor.h"
#include "mongo/db/ops/delete_request.h"
#include "mongo/db/ops/insert.h"
+#include "mongo/db/ops/parsed_delete.h"
#include "mongo/db/ops/parsed_update.h"
#include "mongo/db/ops/update_lifecycle_impl.h"
#include "mongo/db/ops/update_driver.h"
@@ -681,8 +682,8 @@ namespace {
int attempt = 1;
while ( 1 ) {
try {
- DeleteExecutor executor(txn, &request);
- uassertStatusOK(executor.prepare());
+ ParsedDelete parsedDelete(txn, &request);
+ uassertStatusOK(parsedDelete.parseRequest());
AutoGetDb autoDb(txn, ns.db(), MODE_IX);
if (!autoDb.getDb()) break;
@@ -690,8 +691,17 @@ namespace {
Lock::CollectionLock colLock(txn->lockState(), ns.ns(), MODE_IX);
Client::Context ctx(txn, ns);
- long long n = executor.execute(ctx.db());
- lastError.getSafe()->recordDelete( n );
+ PlanExecutor* rawExec;
+ uassertStatusOK(getExecutorDelete(txn,
+ ctx.db()->getCollection(txn, ns),
+ &parsedDelete,
+ &rawExec));
+ boost::scoped_ptr<PlanExecutor> exec(rawExec);
+
+ // Run the plan and get the number of docs deleted.
+ uassertStatusOK(exec->executePlan());
+ long long n = DeleteStage::getNumDeleted(exec.get());
+ lastError.getSafe()->recordDelete(n);
op.debug().ndeleted = n;
break;
diff --git a/src/mongo/db/ops/delete.cpp b/src/mongo/db/ops/delete.cpp
index 8fc12448a21..fcaa2b6d02b 100644
--- a/src/mongo/db/ops/delete.cpp
+++ b/src/mongo/db/ops/delete.cpp
@@ -28,8 +28,10 @@
#include "mongo/db/ops/delete.h"
-#include "mongo/db/ops/delete_executor.h"
+#include "mongo/db/exec/delete.h"
#include "mongo/db/ops/delete_request.h"
+#include "mongo/db/ops/parsed_delete.h"
+#include "mongo/db/query/get_executor.h"
namespace mongo {
@@ -55,8 +57,21 @@ namespace mongo {
request.setGod(god);
request.setFromMigrate(fromMigrate);
request.setYieldPolicy(policy);
- DeleteExecutor executor(txn, &request);
- return executor.execute(db);
+
+ Collection* collection = NULL;
+ if (db) {
+ collection = db->getCollection(txn, nsString.ns());
+ }
+
+ ParsedDelete parsedDelete(txn, &request);
+ uassertStatusOK(parsedDelete.parseRequest());
+
+ PlanExecutor* rawExec;
+ uassertStatusOK(getExecutorDelete(txn, collection, &parsedDelete, &rawExec));
+ boost::scoped_ptr<PlanExecutor> exec(rawExec);
+
+ uassertStatusOK(exec->executePlan());
+ return DeleteStage::getNumDeleted(exec.get());
}
} // namespace mongo
diff --git a/src/mongo/db/ops/delete_executor.cpp b/src/mongo/db/ops/delete_executor.cpp
deleted file mode 100644
index 01b08e6c56d..00000000000
--- a/src/mongo/db/ops/delete_executor.cpp
+++ /dev/null
@@ -1,199 +0,0 @@
-/**
- * Copyright (C) 2014 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.
- */
-
-#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kWrite
-
-#include "mongo/platform/basic.h"
-
-#include "mongo/db/ops/delete_executor.h"
-
-#include "mongo/db/catalog/collection.h"
-#include "mongo/db/catalog/database.h"
-#include "mongo/db/exec/delete.h"
-#include "mongo/db/ops/delete_request.h"
-#include "mongo/db/query/canonical_query.h"
-#include "mongo/db/query/get_executor.h"
-#include "mongo/db/query/query_planner_common.h"
-#include "mongo/db/repl/repl_coordinator_global.h"
-#include "mongo/util/assert_util.h"
-#include "mongo/util/log.h"
-#include "mongo/util/mongoutils/str.h"
-
-namespace mongo {
-
- DeleteExecutor::DeleteExecutor(OperationContext* txn, const DeleteRequest* request) :
- _txn(txn),
- _request(request),
- _canonicalQuery(),
- _isQueryParsed(false) {
- }
-
- DeleteExecutor::~DeleteExecutor() {}
-
- Status DeleteExecutor::prepare() {
- if (_isQueryParsed)
- return Status::OK();
-
- dassert(!_canonicalQuery.get());
-
- if (CanonicalQuery::isSimpleIdQuery(_request->getQuery())) {
- _isQueryParsed = true;
- return Status::OK();
- }
-
- CanonicalQuery* cqRaw;
- const WhereCallbackReal whereCallback(_txn, _request->getNamespaceString().db());
-
- Status status = CanonicalQuery::canonicalize(_request->getNamespaceString().ns(),
- _request->getQuery(),
- _request->isExplain(),
- &cqRaw,
- whereCallback);
- if (status.isOK()) {
- _canonicalQuery.reset(cqRaw);
- _isQueryParsed = true;
- }
-
- return status;
- }
-
- PlanExecutor* DeleteExecutor::getPlanExecutor() {
- return _exec.get();
- }
-
- Status DeleteExecutor::prepareInLock(Database* db) {
- // If we have a non-NULL PlanExecutor, then we've already done the in-lock preparation.
- if (_exec.get()) {
- return Status::OK();
- }
-
- uassert(17417,
- mongoutils::str::stream() <<
- "DeleteExecutor::prepare() failed to parse query " << _request->getQuery(),
- _isQueryParsed);
-
- const NamespaceString& ns(_request->getNamespaceString());
- if (!_request->isGod()) {
- if (ns.isSystem()) {
- uassert(12050,
- "cannot delete from system namespace",
- legalClientSystemNS(ns.ns(), true));
- }
- if (ns.ns().find('$') != string::npos) {
- log() << "cannot delete from collection with reserved $ in name: " << ns << endl;
- uasserted(10100, "cannot delete from collection with reserved $ in name");
- }
- }
-
- // Note that 'collection' may by NULL in the case that the collection or database we are
- // trying to delete from does not exist. NULL 'collection' is handled by
- // getExecutorDelete(); we expect to get back a plan executor whose plan is a DeleteStage
- // on top of an EOFStage.
- Collection* collection = NULL;
- if (db) {
- collection = db->getCollection(_txn, ns.ns());
- }
-
- if (collection && collection->isCapped()) {
- return Status(ErrorCodes::IllegalOperation,
- str::stream() << "cannot remove from a capped collection: " << ns.ns());
- }
-
- if (_request->shouldCallLogOp() &&
- !repl::getGlobalReplicationCoordinator()->canAcceptWritesForDatabase(ns.db())) {
- return Status(ErrorCodes::NotMaster,
- str::stream() << "Not primary while removing from " << ns.ns());
- }
-
- // If yielding is allowed for this plan, then set an auto yield policy. Otherwise set
- // a manual yield policy.
- const bool canYield = !_request->isGod() &&
- PlanExecutor::YIELD_AUTO == _request->getYieldPolicy() && (
- _canonicalQuery.get() ?
- !QueryPlannerCommon::hasNode(_canonicalQuery->root(), MatchExpression::ATOMIC) :
- !LiteParsedQuery::isQueryIsolated(_request->getQuery()));
-
- PlanExecutor::YieldPolicy policy = canYield ? PlanExecutor::YIELD_AUTO :
- PlanExecutor::YIELD_MANUAL;
-
- PlanExecutor* rawExec;
- Status getExecStatus = Status::OK();
- if (_canonicalQuery.get()) {
- // This is the non-idhack branch.
- getExecStatus = getExecutorDelete(_txn,
- collection,
- _canonicalQuery.release(),
- _request->isMulti(),
- _request->shouldCallLogOp(),
- _request->isFromMigrate(),
- _request->isExplain(),
- policy,
- &rawExec);
- }
- else {
- // This is the idhack branch.
- getExecStatus = getExecutorDelete(_txn,
- collection,
- ns.ns(),
- _request->getQuery(),
- _request->isMulti(),
- _request->shouldCallLogOp(),
- _request->isFromMigrate(),
- _request->isExplain(),
- policy,
- &rawExec);
- }
-
- if (!getExecStatus.isOK()) {
- return getExecStatus;
- }
-
- invariant(rawExec);
- _exec.reset(rawExec);
-
- return Status::OK();
- }
-
- long long DeleteExecutor::execute(Database* db) {
- uassertStatusOK(prepare());
-
- // If we've already done the in-lock preparation, this is a no-op.
- uassertStatusOK(prepareInLock(db));
- invariant(_exec.get());
-
- uassertStatusOK(_exec->executePlan());
-
- // Extract the number of documents deleted from the DeleteStage stats.
- invariant(_exec->getRootStage()->stageType() == STAGE_DELETE);
- DeleteStage* deleteStage = static_cast<DeleteStage*>(_exec->getRootStage());
- const DeleteStats* deleteStats =
- static_cast<const DeleteStats*>(deleteStage->getSpecificStats());
- return deleteStats->docsDeleted;
- }
-
-} // namespace mongo
diff --git a/src/mongo/db/ops/delete_executor.h b/src/mongo/db/ops/delete_executor.h
deleted file mode 100644
index cc984af1c95..00000000000
--- a/src/mongo/db/ops/delete_executor.h
+++ /dev/null
@@ -1,132 +0,0 @@
-/**
- * Copyright (C) 2014 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 <memory>
-
-#include "mongo/base/disallow_copying.h"
-#include "mongo/base/status.h"
-#include "mongo/db/query/plan_executor.h"
-
-
-namespace mongo {
-
- class CanonicalQuery;
- class Database;
- class DeleteRequest;
- class OperationContext;
-
- /**
- * Implementation of the processing of a delete operation in a mongod.
- *
- * The executor has two important methods, prepare() and execute(). The prepare() method can
- * run without locks, and does whatever parsing and precomputation can be done without access to
- * database data. The execute method performs the delete, but the caller must already hold the
- * appropriate database lock.
- *
- * Expected usage is approximately:
- * DeleteRequest request(...);
- * // configure request
- * DeleteExecutor executor(txn, &request);
- * uassertStatusOK(executor.prepare());
- * // Get locks, get ready to execute.
- * try {
- * long long nDeleted = executor.execute();
- * }
- * catch (const DBException& ex) {
- * // Error handling.
- * }
- */
- class DeleteExecutor {
- MONGO_DISALLOW_COPYING(DeleteExecutor);
- public:
- /**
- * Constructs a delete executor.
- *
- * The object pointed to by "request" must stay in scope for the life of the constructed
- * executor.
- */
- DeleteExecutor(OperationContext* txn, const DeleteRequest* request);
-
- ~DeleteExecutor();
-
- /**
- * Performs preparatory work that does not require database locks.
- *
- * Returns Status::OK() on success. Other results indicate that the executor will not run
- * correctly, and should be abandoned.
- *
- * Calling prepare() is optional. It is available for situations in which the user
- * wishes to do as much work as possible before acquiring database locks.
- */
- Status prepare();
-
- /**
- * Performs preparatory work that *does* require the appropriate database lock. This
- * preparation involves construction of a PlanExecutor. Construction of a PlanExecutor
- * requires the database lock because it goes through query planning and optimization,
- * which may involve partial execution of the delete plan tree.
- *
- * On success, a non-NULL PlanExecutor will be available via getPlanExecutor().
- */
- Status prepareInLock(Database* db);
-
- /**
- * Retrieve the PlanExecutor that will be used to execute this delete upon calling
- * execute(). Returns NULL if no PlanExecutor has been created.
- */
- PlanExecutor* getPlanExecutor();
-
- /**
- * Execute a delete. Requires the caller to hold the database lock on the
- * appropriate resources for the request.
- *
- * Returns the number of documents deleted.
- */
- long long execute(Database* db);
-
- private:
- // Transactional context. Not owned by us.
- OperationContext* _txn;
-
- // Unowned pointer to the request object that this executor will process.
- const DeleteRequest* const _request;
-
- // Parsed query object, or NULL if the query proves to be an id hack query.
- std::auto_ptr<CanonicalQuery> _canonicalQuery;
-
- // The tree of execution stages which will be used to execute the update.
- boost::scoped_ptr<PlanExecutor> _exec;
-
- // Flag indicating if the query has been successfully parsed.
- bool _isQueryParsed;
-
- };
-
-} // namespace mongo
diff --git a/src/mongo/db/ops/parsed_delete.cpp b/src/mongo/db/ops/parsed_delete.cpp
new file mode 100644
index 00000000000..da1b2c77a25
--- /dev/null
+++ b/src/mongo/db/ops/parsed_delete.cpp
@@ -0,0 +1,104 @@
+/**
+ * Copyright (C) 2014 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.
+ */
+
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kWrite
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/ops/parsed_delete.h"
+
+#include "mongo/db/catalog/collection.h"
+#include "mongo/db/catalog/database.h"
+#include "mongo/db/exec/delete.h"
+#include "mongo/db/ops/delete_request.h"
+#include "mongo/db/query/canonical_query.h"
+#include "mongo/db/query/get_executor.h"
+#include "mongo/db/query/query_planner_common.h"
+#include "mongo/db/repl/repl_coordinator_global.h"
+#include "mongo/util/assert_util.h"
+#include "mongo/util/log.h"
+#include "mongo/util/mongoutils/str.h"
+
+namespace mongo {
+
+ ParsedDelete::ParsedDelete(OperationContext* txn, const DeleteRequest* request) :
+ _txn(txn),
+ _request(request) { }
+
+ Status ParsedDelete::parseRequest() {
+ dassert(!_canonicalQuery.get());
+
+ if (CanonicalQuery::isSimpleIdQuery(_request->getQuery())) {
+ return Status::OK();
+ }
+
+ return parseQueryToCQ();
+ }
+
+ Status ParsedDelete::parseQueryToCQ() {
+ dassert(!_canonicalQuery.get());
+
+ CanonicalQuery* cqRaw;
+ const WhereCallbackReal whereCallback(_txn, _request->getNamespaceString().db());
+
+ Status status = CanonicalQuery::canonicalize(_request->getNamespaceString().ns(),
+ _request->getQuery(),
+ _request->isExplain(),
+ &cqRaw,
+ whereCallback);
+
+ if (status.isOK()) {
+ cqRaw->setIsForWrite(true);
+ _canonicalQuery.reset(cqRaw);
+ }
+
+ return status;
+ }
+
+ const DeleteRequest* ParsedDelete::getRequest() const {
+ return _request;
+ }
+
+ bool ParsedDelete::canYield() const {
+ return !_request->isGod() &&
+ PlanExecutor::YIELD_AUTO == _request->getYieldPolicy() && (
+ _canonicalQuery.get() ?
+ !QueryPlannerCommon::hasNode(_canonicalQuery->root(), MatchExpression::ATOMIC) :
+ !LiteParsedQuery::isQueryIsolated(_request->getQuery()));
+ }
+
+ bool ParsedDelete::hasParsedQuery() const {
+ return _canonicalQuery.get() != NULL;
+ }
+
+ CanonicalQuery* ParsedDelete::releaseParsedQuery() {
+ invariant(_canonicalQuery.get() != NULL);
+ return _canonicalQuery.release();
+ }
+
+} // namespace mongo
diff --git a/src/mongo/db/ops/parsed_delete.h b/src/mongo/db/ops/parsed_delete.h
new file mode 100644
index 00000000000..c117db28cb7
--- /dev/null
+++ b/src/mongo/db/ops/parsed_delete.h
@@ -0,0 +1,109 @@
+/**
+ * Copyright (C) 2014 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/base/disallow_copying.h"
+#include "mongo/base/status.h"
+#include "mongo/db/query/plan_executor.h"
+
+namespace mongo {
+
+ class CanonicalQuery;
+ class Database;
+ class DeleteRequest;
+ class OperationContext;
+
+ /**
+ * This class takes a pointer to a DeleteRequest, and converts that request into a parsed form
+ * via the parseRequest() method. A ParsedDelete can then be used to retrieve a PlanExecutor
+ * capable of executing the delete.
+ *
+ * A delete request is parsed to a CanonicalQuery, so this class is a thin, delete-specific
+ * wrapper around canonicalization.
+ *
+ * No locks need to be held during parsing.
+ */
+ class ParsedDelete {
+ MONGO_DISALLOW_COPYING(ParsedDelete);
+ public:
+ /**
+ * Constructs a parsed delete.
+ *
+ * The object pointed to by "request" must stay in scope for the life of the constructed
+ * ParsedDelete.
+ */
+ ParsedDelete(OperationContext* txn, const DeleteRequest* request);
+
+ /**
+ * Parses the delete request to a canonical query. On success, the parsed delete can be
+ * used to create a PlanExecutor capable of executing this delete.
+ */
+ Status parseRequest();
+
+ /**
+ * As an optimization, we do not create a canonical query if the predicate is a simple
+ * _id equality. This method can be used to force full parsing to a canonical query,
+ * as a fallback if the idhack path is not available (e.g. no _id index).
+ */
+ Status parseQueryToCQ();
+
+ /**
+ * Get the raw request.
+ */
+ const DeleteRequest* getRequest() const;
+
+ /**
+ * Is this delete allowed to yield?
+ */
+ bool canYield() const;
+
+ /**
+ * As an optimization, we don't create a canonical query for updates with simple _id
+ * queries. Use this method to determine whether or not we actually parsed the query.
+ */
+ bool hasParsedQuery() const;
+
+ /**
+ * Releases ownership of the canonical query to the caller.
+ */
+ CanonicalQuery* releaseParsedQuery();
+
+ private:
+ // Transactional context. Not owned by us.
+ OperationContext* _txn;
+
+ // Unowned pointer to the request object that this executor will process.
+ const DeleteRequest* const _request;
+
+ // Parsed query object, or NULL if the query proves to be an id hack query.
+ std::auto_ptr<CanonicalQuery> _canonicalQuery;
+
+ };
+
+} // namespace mongo
diff --git a/src/mongo/db/query/get_executor.cpp b/src/mongo/db/query/get_executor.cpp
index 290ec20adf4..cdf665fc0b1 100644
--- a/src/mongo/db/query/get_executor.cpp
+++ b/src/mongo/db/query/get_executor.cpp
@@ -460,87 +460,101 @@ namespace mongo {
Status getExecutorDelete(OperationContext* txn,
Collection* collection,
- CanonicalQuery* rawCanonicalQuery,
- bool isMulti,
- bool shouldCallLogOp,
- bool fromMigrate,
- bool isExplain,
- PlanExecutor::YieldPolicy yieldPolicy,
- PlanExecutor** out) {
- auto_ptr<CanonicalQuery> canonicalQuery(rawCanonicalQuery);
+ ParsedDelete* parsedDelete,
+ PlanExecutor** execOut) {
+ const DeleteRequest* request = parsedDelete->getRequest();
+
+ const NamespaceString& nss(request->getNamespaceString());
+ if (!request->isGod()) {
+ if (nss.isSystem()) {
+ uassert(12050,
+ "cannot delete from system namespace",
+ legalClientSystemNS(nss.ns(), true));
+ }
+ if (nss.ns().find('$') != string::npos) {
+ log() << "cannot delete from collection with reserved $ in name: " << nss << endl;
+ uasserted(10100, "cannot delete from collection with reserved $ in name");
+ }
+ }
+
+ if (collection && collection->isCapped()) {
+ return Status(ErrorCodes::IllegalOperation,
+ str::stream() << "cannot remove from a capped collection: " << nss.ns());
+ }
+
+ if (request->shouldCallLogOp() &&
+ !repl::getGlobalReplicationCoordinator()->canAcceptWritesForDatabase(nss.db())) {
+ return Status(ErrorCodes::NotMaster,
+ str::stream() << "Not primary while removing from " << nss.ns());
+ }
+
+ DeleteStageParams deleteStageParams;
+ deleteStageParams.isMulti = request->isMulti();
+ deleteStageParams.shouldCallLogOp = request->shouldCallLogOp();
+ deleteStageParams.fromMigrate = request->isFromMigrate();
+ deleteStageParams.isExplain = request->isExplain();
+
auto_ptr<WorkingSet> ws(new WorkingSet());
+ PlanExecutor::YieldPolicy policy = parsedDelete->canYield() ? PlanExecutor::YIELD_AUTO :
+ PlanExecutor::YIELD_MANUAL;
+
+ if (!parsedDelete->hasParsedQuery()) {
+ // This is the idhack fast-path for getting a PlanExecutor without doing the work
+ // to create a CanonicalQuery.
+ const BSONObj& unparsedQuery = request->getQuery();
+
+ if (!collection) {
+ // Treat collections that do not exist as empty collections. Note that the explain
+ // reporting machinery always assumes that the root stage for a delete operation is
+ // a DeleteStage, so in this case we put a DeleteStage on top of an EOFStage.
+ LOG(2) << "Collection " << nss.ns() << " does not exist."
+ << " Using EOF stage: " << unparsedQuery.toString();
+ DeleteStage* deleteStage = new DeleteStage(txn, deleteStageParams, ws.get(), NULL,
+ new EOFStage());
+ return PlanExecutor::make(txn, ws.release(), deleteStage, nss.ns(), policy,
+ execOut);
+
+ }
+
+ if (CanonicalQuery::isSimpleIdQuery(unparsedQuery) &&
+ collection->getIndexCatalog()->findIdIndex(txn)) {
+ LOG(2) << "Using idhack: " << unparsedQuery.toString();
+
+ PlanStage* idHackStage = new IDHackStage(txn,
+ collection,
+ unparsedQuery["_id"].wrap(),
+ ws.get());
+ DeleteStage* root = new DeleteStage(txn, deleteStageParams, ws.get(), collection,
+ idHackStage);
+ return PlanExecutor::make(txn, ws.release(), root, collection, policy, execOut);
+ }
+
+ // If we're here then we don't have a parsed query, but we're also not eligible for
+ // the idhack fast path. We need to force canonicalization now.
+ Status cqStatus = parsedDelete->parseQueryToCQ();
+ if (!cqStatus.isOK()) {
+ return cqStatus;
+ }
+ }
+
+ // This is the regular path for when we have a CanonicalQuery.
+ std::auto_ptr<CanonicalQuery> cq(parsedDelete->releaseParsedQuery());
+
PlanStage* root;
QuerySolution* querySolution;
const size_t defaultPlannerOptions = 0;
- Status status = prepareExecution(txn, collection, ws.get(), canonicalQuery.get(),
+ Status status = prepareExecution(txn, collection, ws.get(), cq.get(),
defaultPlannerOptions, &root, &querySolution);
if (!status.isOK()) {
return status;
}
invariant(root);
- DeleteStageParams deleteStageParams;
- deleteStageParams.isMulti = isMulti;
- deleteStageParams.shouldCallLogOp = shouldCallLogOp;
- deleteStageParams.fromMigrate = fromMigrate;
- deleteStageParams.isExplain = isExplain;
+
root = new DeleteStage(txn, deleteStageParams, ws.get(), collection, root);
// We must have a tree of stages in order to have a valid plan executor, but the query
// solution may be null.
- return PlanExecutor::make(txn, ws.release(), root, querySolution, canonicalQuery.release(),
- collection, yieldPolicy, out);
- }
-
- Status getExecutorDelete(OperationContext* txn,
- Collection* collection,
- const std::string& ns,
- const BSONObj& unparsedQuery,
- bool isMulti,
- bool shouldCallLogOp,
- bool fromMigrate,
- bool isExplain,
- PlanExecutor::YieldPolicy yieldPolicy,
- PlanExecutor** out) {
- auto_ptr<WorkingSet> ws(new WorkingSet());
- DeleteStageParams deleteStageParams;
- deleteStageParams.isMulti = isMulti;
- deleteStageParams.shouldCallLogOp = shouldCallLogOp;
- deleteStageParams.fromMigrate = fromMigrate;
- deleteStageParams.isExplain = isExplain;
- if (!collection) {
- // Treat collections that do not exist as empty collections. Note that the explain
- // reporting machinery always assumes that the root stage for a delete operation is a
- // DeleteStage, so in this case we put a DeleteStage on top of an EOFStage.
- LOG(2) << "Collection " << ns << " does not exist."
- << " Using EOF stage: " << unparsedQuery.toString();
- DeleteStage* deleteStage = new DeleteStage(txn, deleteStageParams, ws.get(), NULL,
- new EOFStage());
- return PlanExecutor::make(txn, ws.release(), deleteStage, ns, yieldPolicy, out);
- }
-
- if (CanonicalQuery::isSimpleIdQuery(unparsedQuery) &&
- collection->getIndexCatalog()->findIdIndex(txn)) {
- LOG(2) << "Using idhack: " << unparsedQuery.toString();
-
- PlanStage* idHackStage = new IDHackStage(txn, collection, unparsedQuery["_id"].wrap(),
- ws.get());
- DeleteStage* root = new DeleteStage(txn, deleteStageParams, ws.get(), collection,
- idHackStage);
- return PlanExecutor::make(txn, ws.release(), root, collection, yieldPolicy, out);
- }
-
- const WhereCallbackReal whereCallback(txn, collection->ns().db());
- CanonicalQuery* cq;
- Status status = CanonicalQuery::canonicalize(collection->ns(),
- unparsedQuery,
- deleteStageParams.isExplain,
- &cq,
- whereCallback);
- if (!status.isOK())
- return status;
-
- // Takes ownership of 'cq'.
- return getExecutorDelete(txn, collection, cq, isMulti, shouldCallLogOp,
- fromMigrate, isExplain, yieldPolicy, out);
+ return PlanExecutor::make(txn, ws.release(), root, querySolution, cq.release(),
+ collection, policy, execOut);
}
//
diff --git a/src/mongo/db/query/get_executor.h b/src/mongo/db/query/get_executor.h
index ab100a9e00e..f298851c3cf 100644
--- a/src/mongo/db/query/get_executor.h
+++ b/src/mongo/db/query/get_executor.h
@@ -31,6 +31,8 @@
#include "mongo/db/query/query_planner_params.h"
#include "mongo/db/query/query_settings.h"
#include "mongo/db/query/query_solution.h"
+#include "mongo/db/ops/delete_request.h"
+#include "mongo/db/ops/parsed_delete.h"
#include "mongo/db/ops/parsed_update.h"
#include "mongo/db/ops/update_driver.h"
#include "mongo/db/ops/update_request.h"
@@ -131,55 +133,25 @@ namespace mongo {
PlanExecutor::YieldPolicy yieldPolicy,
PlanExecutor** execOut);
- //
- // Delete
- //
-
/**
- * Get a PlanExecutor for a delete operation. 'rawCanonicalQuery' describes the predicate for
- * the documents to be deleted. A write lock is required to execute the returned plan.
- *
- * Takes ownership of 'rawCanonicalQuery'.
+ * Get a PlanExecutor for a delete operation. 'parsedDelete' describes the query predicate
+ * and delete flags like 'isMulti'. The caller must hold the appropriate MODE_X or MODE_IX
+ * locks, and must not release these locks until after the returned PlanExecutor is deleted.
*
- * If the query is valid and an executor could be created, returns Status::OK() and populates
- * *out with the PlanExecutor.
+ * The returned PlanExecutor will yield if and only if parsedDelete->canYield().
*
- * If the query cannot be executed, returns a Status indicating why.
- */
- Status getExecutorDelete(OperationContext* txn,
- Collection* collection,
- CanonicalQuery* rawCanonicalQuery,
- bool isMulti,
- bool shouldCallLogOp,
- bool fromMigrate,
- bool isExplain,
- PlanExecutor::YieldPolicy yieldPolicy,
- PlanExecutor** execOut);
-
- /**
- * Overload of getExecutorDelete() above, for when a canonicalQuery is not available. Used to
- * support idhack-powered deletes.
+ * Does not take ownership of its arguments.
*
* If the query is valid and an executor could be created, returns Status::OK() and populates
- * *out with the PlanExecutor.
+ * *execOut with the PlanExecutor. The caller takes ownership of *execOut.
*
* If the query cannot be executed, returns a Status indicating why.
*/
Status getExecutorDelete(OperationContext* txn,
Collection* collection,
- const std::string& ns,
- const BSONObj& unparsedQuery,
- bool isMulti,
- bool shouldCallLogOp,
- bool fromMigrate,
- bool isExplain,
- PlanExecutor::YieldPolicy yieldPolicy,
+ ParsedDelete* parsedDelete,
PlanExecutor** execOut);
- //
- // Update
- //
-
/**
* Get a PlanExecutor for an update operation. 'parsedUpdate' describes the query predicate
* and update modifiers. The caller must hold the appropriate MODE_X or MODE_IX locks prior
@@ -201,10 +173,6 @@ namespace mongo {
OpDebug* opDebug,
PlanExecutor** execOut);
- //
- // Group
- //
-
/**
* Get a PlanExecutor for a group operation. 'rawCanonicalQuery' describes the predicate for
* the documents to be grouped.