Status processRoles(OperationContext* opCtx,
                        AuthorizationManager* authzManager,
                        StringData rolesCollName,
                        StringData db,
                        bool drop) {
        // When the "drop" argument has been provided, we use this set to store the roles
        // that are currently in the system, and remove from it as we encounter
        // same-named roles in the collection we are restoring from.  Once we've fully
        // moved over the temp roles collection into its final location, we drop
        // any roles that previously existed there but weren't in the temp collection.
        // This is so that we can completely replace the system.roles
        // collection with the roles from the temp collection, without removing all
        // roles at the beginning and thus potentially locking ourselves out.
        unordered_set<RoleName> rolesToDrop;

        if (drop) {
            // Create map of the roles currently in the DB
            BSONObj query =
                db.empty() ? BSONObj() : BSON(AuthorizationManager::ROLE_DB_FIELD_NAME << db);
            BSONObj fields = BSON(AuthorizationManager::ROLE_NAME_FIELD_NAME
                                  << 1
                                  << AuthorizationManager::ROLE_DB_FIELD_NAME
                                  << 1);

            Status status =
                queryAuthzDocument(opCtx,
                                   AuthorizationManager::rolesCollectionNamespace,
                                   query,
                                   fields,
                                   stdx::bind(&CmdMergeAuthzCollections::extractAndInsertRoleName,
                                              &rolesToDrop,
                                              stdx::placeholders::_1));
            if (!status.isOK()) {
                return status;
            }
        }

        Status status = queryAuthzDocument(
            opCtx,
            NamespaceString(rolesCollName),
            db.empty() ? BSONObj() : BSON(AuthorizationManager::ROLE_DB_FIELD_NAME << db),
            BSONObj(),
            stdx::bind(&CmdMergeAuthzCollections::addRole,
                       opCtx,
                       authzManager,
                       db,
                       drop,
                       &rolesToDrop,
                       stdx::placeholders::_1));
        if (!status.isOK()) {
            return status;
        }

        if (drop) {
            long long numRemoved;
            for (unordered_set<RoleName>::iterator it = rolesToDrop.begin();
                 it != rolesToDrop.end();
                 ++it) {
                const RoleName& roleName = *it;
                audit::logDropRole(Client::getCurrent(), roleName);
                status = removeRoleDocuments(opCtx,
                                             BSON(AuthorizationManager::ROLE_NAME_FIELD_NAME
                                                  << roleName.getRole().toString()
                                                  << AuthorizationManager::ROLE_DB_FIELD_NAME
                                                  << roleName.getDB().toString()),
                                             &numRemoved);
                if (!status.isOK()) {
                    return status;
                }
                dassert(numRemoved == 1);
            }
        }

        return Status::OK();
    }