summaryrefslogtreecommitdiff
path: root/src/mongo/db/commands/write_commands/write_commands.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/commands/write_commands/write_commands.cpp')
-rw-r--r--src/mongo/db/commands/write_commands/write_commands.cpp447
1 files changed, 221 insertions, 226 deletions
diff --git a/src/mongo/db/commands/write_commands/write_commands.cpp b/src/mongo/db/commands/write_commands/write_commands.cpp
index 4bf374778fb..fe6bb9a2ff9 100644
--- a/src/mongo/db/commands/write_commands/write_commands.cpp
+++ b/src/mongo/db/commands/write_commands/write_commands.cpp
@@ -53,270 +53,265 @@
namespace mongo {
- using std::string;
- using std::stringstream;
+using std::string;
+using std::stringstream;
- namespace {
+namespace {
- MONGO_INITIALIZER(RegisterWriteCommands)(InitializerContext* context) {
- // Leaked intentionally: a Command registers itself when constructed.
- new CmdInsert();
- new CmdUpdate();
- new CmdDelete();
- return Status::OK();
- }
-
- } // namespace
+MONGO_INITIALIZER(RegisterWriteCommands)(InitializerContext* context) {
+ // Leaked intentionally: a Command registers itself when constructed.
+ new CmdInsert();
+ new CmdUpdate();
+ new CmdDelete();
+ return Status::OK();
+}
- WriteCmd::WriteCmd( StringData name, BatchedCommandRequest::BatchType writeType ) :
- Command( name ), _writeType( writeType ) {
- }
+} // namespace
- void WriteCmd::redactTooLongLog( mutablebson::Document* cmdObj, StringData fieldName ) {
- namespace mmb = mutablebson;
- mmb::Element root = cmdObj->root();
- mmb::Element field = root.findFirstChildNamed( fieldName );
+WriteCmd::WriteCmd(StringData name, BatchedCommandRequest::BatchType writeType)
+ : Command(name), _writeType(writeType) {}
- // If the cmdObj is too large, it will be a "too big" message given by CachedBSONObj.get()
- if ( !field.ok() ) {
- return;
- }
+void WriteCmd::redactTooLongLog(mutablebson::Document* cmdObj, StringData fieldName) {
+ namespace mmb = mutablebson;
+ mmb::Element root = cmdObj->root();
+ mmb::Element field = root.findFirstChildNamed(fieldName);
- // Redact the log if there are more than one documents or operations.
- if ( field.countChildren() > 1 ) {
- field.setValueInt( field.countChildren() );
- }
+ // If the cmdObj is too large, it will be a "too big" message given by CachedBSONObj.get()
+ if (!field.ok()) {
+ return;
}
- // Slaves can't perform writes.
- bool WriteCmd::slaveOk() const { return false; }
+ // Redact the log if there are more than one documents or operations.
+ if (field.countChildren() > 1) {
+ field.setValueInt(field.countChildren());
+ }
+}
+
+// Slaves can't perform writes.
+bool WriteCmd::slaveOk() const {
+ return false;
+}
+
+bool WriteCmd::isWriteCommandForConfigServer() const {
+ return false;
+}
+
+Status WriteCmd::checkAuthForCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ Status status(auth::checkAuthForWriteCommand(AuthorizationSession::get(client),
+ _writeType,
+ NamespaceString(parseNs(dbname, cmdObj)),
+ cmdObj));
+
+ // TODO: Remove this when we standardize GLE reporting from commands
+ if (!status.isOK()) {
+ LastError::get(client).setLastError(status.code(), status.reason());
+ }
- bool WriteCmd::isWriteCommandForConfigServer() const { return false; }
+ return status;
+}
+
+// Write commands are counted towards their corresponding opcounters, not command opcounters.
+bool WriteCmd::shouldAffectCommandCounter() const {
+ return false;
+}
+
+bool WriteCmd::run(OperationContext* txn,
+ const string& dbName,
+ BSONObj& cmdObj,
+ int options,
+ string& errMsg,
+ BSONObjBuilder& result) {
+ // Can't be run on secondaries.
+ dassert(txn->writesAreReplicated());
+ BatchedCommandRequest request(_writeType);
+ BatchedCommandResponse response;
+
+ if (!request.parseBSON(cmdObj, &errMsg) || !request.isValid(&errMsg)) {
+ return appendCommandStatus(result, Status(ErrorCodes::FailedToParse, errMsg));
+ }
- Status WriteCmd::checkAuthForCommand( ClientBasic* client,
- const std::string& dbname,
- const BSONObj& cmdObj ) {
+ // Note that this is a runCommmand, and therefore, the database and the collection name
+ // are in different parts of the grammar for the command. But it's more convenient to
+ // work with a NamespaceString. We built it here and replace it in the parsed command.
+ // Internally, everything work with the namespace string as opposed to just the
+ // collection name.
+ NamespaceString nss(dbName, request.getNS());
+ request.setNSS(nss);
- Status status( auth::checkAuthForWriteCommand( AuthorizationSession::get(client),
- _writeType,
- NamespaceString( parseNs( dbname, cmdObj ) ),
- cmdObj ));
+ StatusWith<WriteConcernOptions> wcStatus = extractWriteConcern(cmdObj);
- // TODO: Remove this when we standardize GLE reporting from commands
- if ( !status.isOK() ) {
- LastError::get(client).setLastError(status.code(), status.reason());
- }
+ if (!wcStatus.isOK()) {
+ return appendCommandStatus(result, wcStatus.getStatus());
+ }
+ txn->setWriteConcern(wcStatus.getValue());
+
+ WriteBatchExecutor writeBatchExecutor(
+ txn, &globalOpCounters, &LastError::get(txn->getClient()));
+
+ writeBatchExecutor.executeBatch(request, &response);
+
+ result.appendElements(response.toBSON());
+ return response.getOk();
+}
+
+Status WriteCmd::explain(OperationContext* txn,
+ const std::string& dbname,
+ const BSONObj& cmdObj,
+ ExplainCommon::Verbosity verbosity,
+ BSONObjBuilder* out) const {
+ // For now we only explain update and delete write commands.
+ if (BatchedCommandRequest::BatchType_Update != _writeType &&
+ BatchedCommandRequest::BatchType_Delete != _writeType) {
+ return Status(ErrorCodes::IllegalOperation,
+ "Only update and delete write ops can be explained");
+ }
- return status;
+ // Parse the batch request.
+ BatchedCommandRequest request(_writeType);
+ std::string errMsg;
+ if (!request.parseBSON(cmdObj, &errMsg) || !request.isValid(&errMsg)) {
+ return Status(ErrorCodes::FailedToParse, errMsg);
}
- // Write commands are counted towards their corresponding opcounters, not command opcounters.
- bool WriteCmd::shouldAffectCommandCounter() const { return false; }
-
- bool WriteCmd::run(OperationContext* txn,
- const string& dbName,
- BSONObj& cmdObj,
- int options,
- string& errMsg,
- BSONObjBuilder& result) {
- // Can't be run on secondaries.
- dassert(txn->writesAreReplicated());
- BatchedCommandRequest request( _writeType );
- BatchedCommandResponse response;
-
- if ( !request.parseBSON( cmdObj, &errMsg ) || !request.isValid( &errMsg ) ) {
- return appendCommandStatus( result, Status( ErrorCodes::FailedToParse, errMsg ) );
- }
+ // Note that this is a runCommmand, and therefore, the database and the collection name
+ // are in different parts of the grammar for the command. But it's more convenient to
+ // work with a NamespaceString. We built it here and replace it in the parsed command.
+ // Internally, everything work with the namespace string as opposed to just the
+ // collection name.
+ NamespaceString nsString(dbname, request.getNS());
+ request.setNSS(nsString);
+
+ // Do the validation of the batch that is shared with non-explained write batches.
+ Status isValid = WriteBatchExecutor::validateBatch(request);
+ if (!isValid.isOK()) {
+ return isValid;
+ }
- // Note that this is a runCommmand, and therefore, the database and the collection name
- // are in different parts of the grammar for the command. But it's more convenient to
- // work with a NamespaceString. We built it here and replace it in the parsed command.
- // Internally, everything work with the namespace string as opposed to just the
- // collection name.
- NamespaceString nss(dbName, request.getNS());
- request.setNSS(nss);
+ // Explain must do one additional piece of validation: For now we only explain
+ // singleton batches.
+ if (request.sizeWriteOps() != 1u) {
+ return Status(ErrorCodes::InvalidLength, "explained write batches must be of size 1");
+ }
- StatusWith<WriteConcernOptions> wcStatus = extractWriteConcern(cmdObj);
+ ScopedTransaction scopedXact(txn, MODE_IX);
- if (!wcStatus.isOK()) {
- return appendCommandStatus(result, wcStatus.getStatus());
- }
- txn->setWriteConcern(wcStatus.getValue());
+ // Get a reference to the singleton batch item (it's the 0th item in the batch).
+ BatchItemRef batchItem(&request, 0);
- WriteBatchExecutor writeBatchExecutor(txn,
- &globalOpCounters,
- &LastError::get(txn->getClient()));
+ if (BatchedCommandRequest::BatchType_Update == _writeType) {
+ // Create the update request.
+ UpdateRequest updateRequest(nsString);
+ updateRequest.setQuery(batchItem.getUpdate()->getQuery());
+ updateRequest.setUpdates(batchItem.getUpdate()->getUpdateExpr());
+ updateRequest.setMulti(batchItem.getUpdate()->getMulti());
+ updateRequest.setUpsert(batchItem.getUpdate()->getUpsert());
+ UpdateLifecycleImpl updateLifecycle(true, updateRequest.getNamespaceString());
+ updateRequest.setLifecycle(&updateLifecycle);
+ updateRequest.setExplain();
- writeBatchExecutor.executeBatch( request, &response );
+ // Explained updates can yield.
+ updateRequest.setYieldPolicy(PlanExecutor::YIELD_AUTO);
- result.appendElements( response.toBSON() );
- return response.getOk();
- }
+ OpDebug* debug = &CurOp::get(txn)->debug();
- Status WriteCmd::explain(OperationContext* txn,
- const std::string& dbname,
- const BSONObj& cmdObj,
- ExplainCommon::Verbosity verbosity,
- BSONObjBuilder* out) const {
- // For now we only explain update and delete write commands.
- if ( BatchedCommandRequest::BatchType_Update != _writeType &&
- BatchedCommandRequest::BatchType_Delete != _writeType ) {
- return Status( ErrorCodes::IllegalOperation,
- "Only update and delete write ops can be explained" );
+ ParsedUpdate parsedUpdate(txn, &updateRequest);
+ Status parseStatus = parsedUpdate.parseRequest();
+ if (!parseStatus.isOK()) {
+ return parseStatus;
}
- // Parse the batch request.
- BatchedCommandRequest request( _writeType );
- std::string errMsg;
- if ( !request.parseBSON( cmdObj, &errMsg ) || !request.isValid( &errMsg ) ) {
- return Status( ErrorCodes::FailedToParse, errMsg );
- }
+ // Explains of write commands are read-only, but we take write locks so
+ // that timing info is more accurate.
+ AutoGetDb autoDb(txn, nsString.db(), MODE_IX);
+ Lock::CollectionLock colLock(txn->lockState(), nsString.ns(), MODE_IX);
- // Note that this is a runCommmand, and therefore, the database and the collection name
- // are in different parts of the grammar for the command. But it's more convenient to
- // work with a NamespaceString. We built it here and replace it in the parsed command.
- // Internally, everything work with the namespace string as opposed to just the
- // collection name.
- NamespaceString nsString(dbname, request.getNS());
- request.setNSS(nsString);
-
- // Do the validation of the batch that is shared with non-explained write batches.
- Status isValid = WriteBatchExecutor::validateBatch( request );
- if (!isValid.isOK()) {
- return isValid;
- }
+ ensureShardVersionOKOrThrow(txn->getClient(), nsString.ns());
- // Explain must do one additional piece of validation: For now we only explain
- // singleton batches.
- if ( request.sizeWriteOps() != 1u ) {
- return Status( ErrorCodes::InvalidLength,
- "explained write batches must be of size 1" );
+ // Get a pointer to the (possibly NULL) collection.
+ Collection* collection = NULL;
+ if (autoDb.getDb()) {
+ collection = autoDb.getDb()->getCollection(nsString.ns());
}
- ScopedTransaction scopedXact(txn, MODE_IX);
-
- // Get a reference to the singleton batch item (it's the 0th item in the batch).
- BatchItemRef batchItem( &request, 0 );
-
- if ( BatchedCommandRequest::BatchType_Update == _writeType ) {
- // Create the update request.
- UpdateRequest updateRequest( nsString );
- updateRequest.setQuery( batchItem.getUpdate()->getQuery() );
- updateRequest.setUpdates( batchItem.getUpdate()->getUpdateExpr() );
- updateRequest.setMulti( batchItem.getUpdate()->getMulti() );
- updateRequest.setUpsert( batchItem.getUpdate()->getUpsert() );
- UpdateLifecycleImpl updateLifecycle( true, updateRequest.getNamespaceString() );
- updateRequest.setLifecycle( &updateLifecycle );
- updateRequest.setExplain();
-
- // Explained updates can yield.
- updateRequest.setYieldPolicy(PlanExecutor::YIELD_AUTO);
-
- OpDebug* debug = &CurOp::get(txn)->debug();
-
- ParsedUpdate parsedUpdate( txn, &updateRequest );
- Status parseStatus = parsedUpdate.parseRequest();
- if ( !parseStatus.isOK() ) {
- return parseStatus;
- }
-
- // Explains of write commands are read-only, but we take write locks so
- // that timing info is more accurate.
- AutoGetDb autoDb( txn, nsString.db(), MODE_IX );
- Lock::CollectionLock colLock( txn->lockState(), nsString.ns(), MODE_IX );
-
- ensureShardVersionOKOrThrow( txn->getClient(), nsString.ns() );
-
- // Get a pointer to the (possibly NULL) collection.
- Collection* collection = NULL;
- if ( autoDb.getDb() ) {
- collection = autoDb.getDb()->getCollection( nsString.ns() );
- }
-
- PlanExecutor* rawExec;
- uassertStatusOK(getExecutorUpdate(txn, collection, &parsedUpdate, debug, &rawExec));
- std::unique_ptr<PlanExecutor> exec(rawExec);
-
- // Explain the plan tree.
- Explain::explainStages( exec.get(), verbosity, out );
- return Status::OK();
+ PlanExecutor* rawExec;
+ uassertStatusOK(getExecutorUpdate(txn, collection, &parsedUpdate, debug, &rawExec));
+ std::unique_ptr<PlanExecutor> exec(rawExec);
+
+ // Explain the plan tree.
+ Explain::explainStages(exec.get(), verbosity, out);
+ return Status::OK();
+ } else {
+ invariant(BatchedCommandRequest::BatchType_Delete == _writeType);
+
+ // Create the delete request.
+ DeleteRequest deleteRequest(nsString);
+ deleteRequest.setQuery(batchItem.getDelete()->getQuery());
+ deleteRequest.setMulti(batchItem.getDelete()->getLimit() != 1);
+ deleteRequest.setGod(false);
+ deleteRequest.setExplain();
+
+ // Explained deletes can yield.
+ deleteRequest.setYieldPolicy(PlanExecutor::YIELD_AUTO);
+
+ ParsedDelete parsedDelete(txn, &deleteRequest);
+ Status parseStatus = parsedDelete.parseRequest();
+ if (!parseStatus.isOK()) {
+ return parseStatus;
}
- else {
- invariant( BatchedCommandRequest::BatchType_Delete == _writeType );
-
- // Create the delete request.
- DeleteRequest deleteRequest( nsString );
- deleteRequest.setQuery( batchItem.getDelete()->getQuery() );
- deleteRequest.setMulti( batchItem.getDelete()->getLimit() != 1 );
- deleteRequest.setGod( false );
- deleteRequest.setExplain();
-
- // Explained deletes can yield.
- deleteRequest.setYieldPolicy(PlanExecutor::YIELD_AUTO);
-
- 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
- // info is more accurate.
- AutoGetDb autoDb(txn, nsString.db(), MODE_IX);
- Lock::CollectionLock colLock(txn->lockState(), nsString.ns(), MODE_IX);
-
- ensureShardVersionOKOrThrow( txn->getClient(), nsString.ns() );
-
- // Get a pointer to the (possibly NULL) collection.
- Collection* collection = NULL;
- if (autoDb.getDb()) {
- collection = autoDb.getDb()->getCollection(nsString.ns());
- }
-
- PlanExecutor* rawExec;
- uassertStatusOK(getExecutorDelete(txn, collection, &parsedDelete, &rawExec));
- std::unique_ptr<PlanExecutor> exec(rawExec);
-
- // Explain the plan tree.
- Explain::explainStages(exec.get(), verbosity, out);
- return Status::OK();
+
+ // Explains of write commands are read-only, but we take write locks so that timing
+ // info is more accurate.
+ AutoGetDb autoDb(txn, nsString.db(), MODE_IX);
+ Lock::CollectionLock colLock(txn->lockState(), nsString.ns(), MODE_IX);
+
+ ensureShardVersionOKOrThrow(txn->getClient(), nsString.ns());
+
+ // Get a pointer to the (possibly NULL) collection.
+ Collection* collection = NULL;
+ if (autoDb.getDb()) {
+ collection = autoDb.getDb()->getCollection(nsString.ns());
}
- }
- CmdInsert::CmdInsert() :
- WriteCmd( "insert", BatchedCommandRequest::BatchType_Insert ) {
- }
+ PlanExecutor* rawExec;
+ uassertStatusOK(getExecutorDelete(txn, collection, &parsedDelete, &rawExec));
+ std::unique_ptr<PlanExecutor> exec(rawExec);
- void CmdInsert::redactForLogging( mutablebson::Document* cmdObj ) {
- redactTooLongLog( cmdObj, StringData( "documents", StringData::LiteralTag() ) );
+ // Explain the plan tree.
+ Explain::explainStages(exec.get(), verbosity, out);
+ return Status::OK();
}
+}
- void CmdInsert::help( stringstream& help ) const {
- help << "insert documents";
- }
+CmdInsert::CmdInsert() : WriteCmd("insert", BatchedCommandRequest::BatchType_Insert) {}
- CmdUpdate::CmdUpdate() :
- WriteCmd( "update", BatchedCommandRequest::BatchType_Update ) {
- }
+void CmdInsert::redactForLogging(mutablebson::Document* cmdObj) {
+ redactTooLongLog(cmdObj, StringData("documents", StringData::LiteralTag()));
+}
- void CmdUpdate::redactForLogging( mutablebson::Document* cmdObj ) {
- redactTooLongLog( cmdObj, StringData( "updates", StringData::LiteralTag() ) );
- }
+void CmdInsert::help(stringstream& help) const {
+ help << "insert documents";
+}
- void CmdUpdate::help( stringstream& help ) const {
- help << "update documents";
- }
+CmdUpdate::CmdUpdate() : WriteCmd("update", BatchedCommandRequest::BatchType_Update) {}
- CmdDelete::CmdDelete() :
- WriteCmd( "delete", BatchedCommandRequest::BatchType_Delete ) {
- }
+void CmdUpdate::redactForLogging(mutablebson::Document* cmdObj) {
+ redactTooLongLog(cmdObj, StringData("updates", StringData::LiteralTag()));
+}
- void CmdDelete::redactForLogging( mutablebson::Document* cmdObj ) {
- redactTooLongLog( cmdObj, StringData( "deletes", StringData::LiteralTag() ) );
- }
+void CmdUpdate::help(stringstream& help) const {
+ help << "update documents";
+}
- void CmdDelete::help( stringstream& help ) const {
- help << "delete documents";
- }
+CmdDelete::CmdDelete() : WriteCmd("delete", BatchedCommandRequest::BatchType_Delete) {}
+
+void CmdDelete::redactForLogging(mutablebson::Document* cmdObj) {
+ redactTooLongLog(cmdObj, StringData("deletes", StringData::LiteralTag()));
+}
+
+void CmdDelete::help(stringstream& help) const {
+ help << "delete documents";
+}
-} // namespace mongo
+} // namespace mongo