/** * 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 . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the GNU Affero General Public License in all respects for * all of the code used other than as permitted herein. If you modify file(s) * with this exception, you may extend this exception to your version of the * file(s), but you are not obligated to do so. If you do not wish to do so, * delete this exception statement from your version. If you delete this * exception statement from all source files in the program, then also delete * it in the license file. */ #include "mongo/platform/basic.h" #include "mongo/db/ops/parsed_update.h" #include "mongo/db/ops/update_request.h" #include "mongo/db/query/canonical_query.h" #include "mongo/db/query/query_planner_common.h" namespace mongo { ParsedUpdate::ParsedUpdate(OperationContext* txn, const UpdateRequest* request) : _txn(txn), _request(request), _driver(UpdateDriver::Options()), _canonicalQuery() { } Status ParsedUpdate::parseRequest() { // It is invalid to request that the UpdateStage return the prior or newly-updated version // of a document during a multi-update. invariant(!(_request->shouldReturnAnyDocs() && _request->isMulti())); // It is invalid to request that a ProjectionStage be applied to the UpdateStage if the // UpdateStage would not return any document. invariant(_request->getProj().isEmpty() || _request->shouldReturnAnyDocs()); // We parse the update portion before the query portion because the dispostion of the update // may determine whether or not we need to produce a CanonicalQuery at all. For example, if // the update involves the positional-dollar operator, we must have a CanonicalQuery even if // it isn't required for query execution. Status status = parseUpdate(); if (!status.isOK()) return status; status = parseQuery(); if (!status.isOK()) return status; return Status::OK(); } Status ParsedUpdate::parseQuery() { dassert(!_canonicalQuery.get()); if (!_driver.needMatchDetails() && CanonicalQuery::isSimpleIdQuery(_request->getQuery())) { return Status::OK(); } return parseQueryToCQ(); } Status ParsedUpdate::parseQueryToCQ() { dassert(!_canonicalQuery.get()); CanonicalQuery* cqRaw; const WhereCallbackReal whereCallback(_txn, _request->getNamespaceString().db()); // Limit should only used for the findAndModify command when a sort is specified. If a sort // is requested, we want to use a top-k sort for efficiency reasons, so should pass the // limit through. Generally, a update stage expects to be able to skip documents that were // deleted/modified under it, but a limit could inhibit that and give an EOF when the update // has not actually updated a document. This behavior is fine for findAndModify, but should // not apply to update in general. long long limit = (!_request->isMulti() && !_request->getSort().isEmpty()) ? -1 : 0; // The projection needs to be applied after the update operation, so we specify an empty // BSONObj as the projection during canonicalization. const BSONObj emptyObj; Status status = CanonicalQuery::canonicalize(_request->getNamespaceString().ns(), _request->getQuery(), _request->getSort(), emptyObj, // projection 0, // skip limit, emptyObj, // hint emptyObj, // min emptyObj, // max false, // snapshot _request->isExplain(), &cqRaw, whereCallback); if (status.isOK()) { _canonicalQuery.reset(cqRaw); } return status; } Status ParsedUpdate::parseUpdate() { const NamespaceString& ns(_request->getNamespaceString()); // Should the modifiers validate their embedded docs via okForStorage // Only user updates should be checked. Any system or replication stuff should pass through. // Config db docs shouldn't get checked for valid field names since the shard key can have // a dot (".") in it. const bool shouldValidate = !(!_txn->writesAreReplicated() || ns.isConfigDB() || _request->isFromMigration()); _driver.setLogOp(true); _driver.setModOptions(ModifierInterface::Options(!_txn->writesAreReplicated(), shouldValidate)); return _driver.parse(_request->getUpdates(), _request->isMulti()); } bool ParsedUpdate::canYield() const { return !_request->isGod() && PlanExecutor::YIELD_AUTO == _request->getYieldPolicy() && !isIsolated(); } bool ParsedUpdate::isIsolated() const { return _canonicalQuery.get() ? QueryPlannerCommon::hasNode(_canonicalQuery->root(), MatchExpression::ATOMIC) : LiteParsedQuery::isQueryIsolated(_request->getQuery()); } bool ParsedUpdate::hasParsedQuery() const { return _canonicalQuery.get() != NULL; } CanonicalQuery* ParsedUpdate::releaseParsedQuery() { invariant(_canonicalQuery.get() != NULL); return _canonicalQuery.release(); } const UpdateRequest* ParsedUpdate::getRequest() const { return _request; } UpdateDriver* ParsedUpdate::getDriver() { return &_driver; } } // namespace mongo