diff options
author | David Storch <david.storch@10gen.com> | 2014-12-02 20:10:01 -0500 |
---|---|---|
committer | David Storch <david.storch@10gen.com> | 2014-12-04 17:41:11 -0500 |
commit | 367810995073e01ee58159deb1bb5b878882632f (patch) | |
tree | 3074960c42a7b63e4e3be337e44f33a036a6679f /src/mongo/db/ops | |
parent | 5080932a79ce0152535169ec3b42edde2951bdcd (diff) | |
download | mongo-367810995073e01ee58159deb1bb5b878882632f.tar.gz |
SERVER-16101 replace DeleteExecutor with ParsedDelete
Diffstat (limited to 'src/mongo/db/ops')
-rw-r--r-- | src/mongo/db/ops/delete.cpp | 21 | ||||
-rw-r--r-- | src/mongo/db/ops/delete_executor.cpp | 199 | ||||
-rw-r--r-- | src/mongo/db/ops/delete_executor.h | 132 | ||||
-rw-r--r-- | src/mongo/db/ops/parsed_delete.cpp | 104 | ||||
-rw-r--r-- | src/mongo/db/ops/parsed_delete.h | 109 |
5 files changed, 231 insertions, 334 deletions
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 |