diff options
Diffstat (limited to 'src/mongo/db/commands/create_indexes.cpp')
-rw-r--r-- | src/mongo/db/commands/create_indexes.cpp | 414 |
1 files changed, 212 insertions, 202 deletions
diff --git a/src/mongo/db/commands/create_indexes.cpp b/src/mongo/db/commands/create_indexes.cpp index 83dc5e72177..c18c33b7ce4 100644 --- a/src/mongo/db/commands/create_indexes.cpp +++ b/src/mongo/db/commands/create_indexes.cpp @@ -52,251 +52,261 @@ namespace mongo { - using std::string; - - /** - * { createIndexes : "bar", indexes : [ { ns : "test.bar", key : { x : 1 }, name: "x_1" } ] } - */ - class CmdCreateIndex : public Command { - public: - CmdCreateIndex() : Command( "createIndexes" ){} - - virtual bool isWriteCommandForConfigServer() const { return false; } - virtual bool slaveOk() const { return false; } // TODO: this could be made true... - - virtual Status checkAuthForCommand(ClientBasic* client, - const std::string& dbname, - const BSONObj& cmdObj) { - ActionSet actions; - actions.addAction(ActionType::createIndex); - Privilege p(parseResourcePattern(dbname, cmdObj), actions); - if (AuthorizationSession::get(client)->isAuthorizedForPrivilege(p)) - return Status::OK(); - return Status(ErrorCodes::Unauthorized, "Unauthorized"); - } - +using std::string; - BSONObj _addNsToSpec( const NamespaceString& ns, const BSONObj& obj ) { - BSONObjBuilder b; - b.append( "ns", ns ); - b.appendElements( obj ); - return b.obj(); +/** + * { createIndexes : "bar", indexes : [ { ns : "test.bar", key : { x : 1 }, name: "x_1" } ] } + */ +class CmdCreateIndex : public Command { +public: + CmdCreateIndex() : Command("createIndexes") {} + + virtual bool isWriteCommandForConfigServer() const { + return false; + } + virtual bool slaveOk() const { + return false; + } // TODO: this could be made true... + + virtual Status checkAuthForCommand(ClientBasic* client, + const std::string& dbname, + const BSONObj& cmdObj) { + ActionSet actions; + actions.addAction(ActionType::createIndex); + Privilege p(parseResourcePattern(dbname, cmdObj), actions); + if (AuthorizationSession::get(client)->isAuthorizedForPrivilege(p)) + return Status::OK(); + return Status(ErrorCodes::Unauthorized, "Unauthorized"); + } + + + BSONObj _addNsToSpec(const NamespaceString& ns, const BSONObj& obj) { + BSONObjBuilder b; + b.append("ns", ns); + b.appendElements(obj); + return b.obj(); + } + + virtual bool run(OperationContext* txn, + const string& dbname, + BSONObj& cmdObj, + int options, + string& errmsg, + BSONObjBuilder& result) { + // --- parse + + NamespaceString ns(dbname, cmdObj[name].String()); + Status status = userAllowedWriteNS(ns); + if (!status.isOK()) + return appendCommandStatus(result, status); + + if (cmdObj["indexes"].type() != Array) { + errmsg = "indexes has to be an array"; + result.append("cmdObj", cmdObj); + return false; } - virtual bool run(OperationContext* txn, const string& dbname, BSONObj& cmdObj, int options, - string& errmsg, BSONObjBuilder& result) { - // --- parse - - NamespaceString ns( dbname, cmdObj[name].String() ); - Status status = userAllowedWriteNS( ns ); - if ( !status.isOK() ) - return appendCommandStatus( result, status ); - - if ( cmdObj["indexes"].type() != Array ) { - errmsg = "indexes has to be an array"; - result.append( "cmdObj", cmdObj ); - return false; + std::vector<BSONObj> specs; + { + BSONObjIterator i(cmdObj["indexes"].Obj()); + while (i.more()) { + BSONElement e = i.next(); + if (e.type() != Object) { + errmsg = "everything in indexes has to be an Object"; + result.append("cmdObj", cmdObj); + return false; + } + specs.push_back(e.Obj()); } + } - std::vector<BSONObj> specs; - { - BSONObjIterator i( cmdObj["indexes"].Obj() ); - while ( i.more() ) { - BSONElement e = i.next(); - if ( e.type() != Object ) { - errmsg = "everything in indexes has to be an Object"; - result.append( "cmdObj", cmdObj ); - return false; - } - specs.push_back( e.Obj() ); - } + if (specs.size() == 0) { + errmsg = "no indexes to add"; + return false; + } + + // check specs + for (size_t i = 0; i < specs.size(); i++) { + BSONObj spec = specs[i]; + if (spec["ns"].eoo()) { + spec = _addNsToSpec(ns, spec); + specs[i] = spec; } - if ( specs.size() == 0 ) { - errmsg = "no indexes to add"; + if (spec["ns"].type() != String) { + errmsg = "spec has no ns"; + result.append("spec", spec); return false; } - - // check specs - for ( size_t i = 0; i < specs.size(); i++ ) { - BSONObj spec = specs[i]; - if ( spec["ns"].eoo() ) { - spec = _addNsToSpec( ns, spec ); - specs[i] = spec; - } - - if ( spec["ns"].type() != String ) { - errmsg = "spec has no ns"; - result.append( "spec", spec ); - return false; - } - if ( ns != spec["ns"].String() ) { - errmsg = "namespace mismatch"; - result.append( "spec", spec ); - return false; - } + if (ns != spec["ns"].String()) { + errmsg = "namespace mismatch"; + result.append("spec", spec); + return false; } + } - // now we know we have to create index(es) - // Note: createIndexes command does not currently respect shard versioning. - ScopedTransaction transaction(txn, MODE_IX); - Lock::DBLock dbLock(txn->lockState(), ns.db(), MODE_X); - if (!repl::getGlobalReplicationCoordinator()->canAcceptWritesFor(ns)) { - return appendCommandStatus(result, Status(ErrorCodes::NotMaster, str::stream() - << "Not primary while creating indexes in " << ns.ns())); - } + // now we know we have to create index(es) + // Note: createIndexes command does not currently respect shard versioning. + ScopedTransaction transaction(txn, MODE_IX); + Lock::DBLock dbLock(txn->lockState(), ns.db(), MODE_X); + if (!repl::getGlobalReplicationCoordinator()->canAcceptWritesFor(ns)) { + return appendCommandStatus( + result, + Status(ErrorCodes::NotMaster, + str::stream() << "Not primary while creating indexes in " << ns.ns())); + } - Database* db = dbHolder().get(txn, ns.db()); - if (!db) { - db = dbHolder().openDb(txn, ns.db()); - } + Database* db = dbHolder().get(txn, ns.db()); + if (!db) { + db = dbHolder().openDb(txn, ns.db()); + } - Collection* collection = db->getCollection( ns.ns() ); - result.appendBool( "createdCollectionAutomatically", collection == NULL ); - if ( !collection ) { - MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN { - WriteUnitOfWork wunit(txn); - collection = db->createCollection(txn, ns.ns(), CollectionOptions()); - invariant( collection ); - wunit.commit(); - } MONGO_WRITE_CONFLICT_RETRY_LOOP_END(txn, "createIndexes", ns.ns()); + Collection* collection = db->getCollection(ns.ns()); + result.appendBool("createdCollectionAutomatically", collection == NULL); + if (!collection) { + MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN { + WriteUnitOfWork wunit(txn); + collection = db->createCollection(txn, ns.ns(), CollectionOptions()); + invariant(collection); + wunit.commit(); } + MONGO_WRITE_CONFLICT_RETRY_LOOP_END(txn, "createIndexes", ns.ns()); + } - const int numIndexesBefore = collection->getIndexCatalog()->numIndexesTotal(txn); - result.append("numIndexesBefore", numIndexesBefore); + const int numIndexesBefore = collection->getIndexCatalog()->numIndexesTotal(txn); + result.append("numIndexesBefore", numIndexesBefore); - MultiIndexBlock indexer(txn, collection); - indexer.allowBackgroundBuilding(); - indexer.allowInterruption(); + MultiIndexBlock indexer(txn, collection); + indexer.allowBackgroundBuilding(); + indexer.allowInterruption(); - const size_t origSpecsSize = specs.size(); - indexer.removeExistingIndexes(&specs); + const size_t origSpecsSize = specs.size(); + indexer.removeExistingIndexes(&specs); - if (specs.size() == 0) { - result.append("numIndexesAfter", numIndexesBefore); - result.append( "note", "all indexes already exist" ); - return true; - } + if (specs.size() == 0) { + result.append("numIndexesAfter", numIndexesBefore); + result.append("note", "all indexes already exist"); + return true; + } - if (specs.size() != origSpecsSize) { - result.append( "note", "index already exists" ); - } + if (specs.size() != origSpecsSize) { + result.append("note", "index already exists"); + } - for ( size_t i = 0; i < specs.size(); i++ ) { - const BSONObj& spec = specs[i]; - if ( spec["unique"].trueValue() ) { - status = checkUniqueIndexConstraints(txn, ns.ns(), spec["key"].Obj()); + for (size_t i = 0; i < specs.size(); i++) { + const BSONObj& spec = specs[i]; + if (spec["unique"].trueValue()) { + status = checkUniqueIndexConstraints(txn, ns.ns(), spec["key"].Obj()); - if ( !status.isOK() ) { - appendCommandStatus( result, status ); - return false; - } + if (!status.isOK()) { + appendCommandStatus(result, status); + return false; } } + } - MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN { - uassertStatusOK(indexer.init(specs)); - } MONGO_WRITE_CONFLICT_RETRY_LOOP_END(txn, "createIndexes", ns.ns()); + MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN { + uassertStatusOK(indexer.init(specs)); + } + MONGO_WRITE_CONFLICT_RETRY_LOOP_END(txn, "createIndexes", ns.ns()); - // If we're a background index, replace exclusive db lock with an intent lock, so that - // other readers and writers can proceed during this phase. - if (indexer.getBuildInBackground()) { - txn->recoveryUnit()->abandonSnapshot(); - dbLock.relockWithMode(MODE_IX); - if (!repl::getGlobalReplicationCoordinator()->canAcceptWritesFor(ns)) { - return appendCommandStatus(result, Status(ErrorCodes::NotMaster, str::stream() - << "Not primary while creating background indexes in " << ns.ns())); - } + // If we're a background index, replace exclusive db lock with an intent lock, so that + // other readers and writers can proceed during this phase. + if (indexer.getBuildInBackground()) { + txn->recoveryUnit()->abandonSnapshot(); + dbLock.relockWithMode(MODE_IX); + if (!repl::getGlobalReplicationCoordinator()->canAcceptWritesFor(ns)) { + return appendCommandStatus( + result, + Status(ErrorCodes::NotMaster, + str::stream() << "Not primary while creating background indexes in " + << ns.ns())); } + } - try { - Lock::CollectionLock colLock(txn->lockState(), ns.ns(), MODE_IX); - uassertStatusOK(indexer.insertAllDocumentsInCollection()); - } - catch (const DBException& e) { - invariant(e.getCode() != ErrorCodes::WriteConflict); - // Must have exclusive DB lock before we clean up the index build via the - // destructor of 'indexer'. - if (indexer.getBuildInBackground()) { - try { - // This function cannot throw today, but we will preemptively prepare for - // that day, to avoid data corruption due to lack of index cleanup. - txn->recoveryUnit()->abandonSnapshot(); - dbLock.relockWithMode(MODE_X); - if (!repl::getGlobalReplicationCoordinator()->canAcceptWritesFor(ns)) { - return appendCommandStatus( - result, - Status(ErrorCodes::NotMaster, str::stream() - << "Not primary while creating background indexes in " - << ns.ns() << ": cleaning up index build failure due to " - << e.toString())); - } - } - catch (...) { - std::terminate(); + try { + Lock::CollectionLock colLock(txn->lockState(), ns.ns(), MODE_IX); + uassertStatusOK(indexer.insertAllDocumentsInCollection()); + } catch (const DBException& e) { + invariant(e.getCode() != ErrorCodes::WriteConflict); + // Must have exclusive DB lock before we clean up the index build via the + // destructor of 'indexer'. + if (indexer.getBuildInBackground()) { + try { + // This function cannot throw today, but we will preemptively prepare for + // that day, to avoid data corruption due to lack of index cleanup. + txn->recoveryUnit()->abandonSnapshot(); + dbLock.relockWithMode(MODE_X); + if (!repl::getGlobalReplicationCoordinator()->canAcceptWritesFor(ns)) { + return appendCommandStatus( + result, + Status(ErrorCodes::NotMaster, + str::stream() + << "Not primary while creating background indexes in " + << ns.ns() << ": cleaning up index build failure due to " + << e.toString())); } + } catch (...) { + std::terminate(); } - throw; } - // Need to return db lock back to exclusive, to complete the index build. - if (indexer.getBuildInBackground()) { - txn->recoveryUnit()->abandonSnapshot(); - dbLock.relockWithMode(MODE_X); - uassert(ErrorCodes::NotMaster, - str::stream() << "Not primary while completing index build in " << dbname, - repl::getGlobalReplicationCoordinator()->canAcceptWritesFor(ns)); - - Database* db = dbHolder().get(txn, ns.db()); - uassert(28551, "database dropped during index build", db); - uassert(28552, "collection dropped during index build", - db->getCollection(ns.ns())); - } - - MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN { - WriteUnitOfWork wunit(txn); + throw; + } + // Need to return db lock back to exclusive, to complete the index build. + if (indexer.getBuildInBackground()) { + txn->recoveryUnit()->abandonSnapshot(); + dbLock.relockWithMode(MODE_X); + uassert(ErrorCodes::NotMaster, + str::stream() << "Not primary while completing index build in " << dbname, + repl::getGlobalReplicationCoordinator()->canAcceptWritesFor(ns)); - indexer.commit(); + Database* db = dbHolder().get(txn, ns.db()); + uassert(28551, "database dropped during index build", db); + uassert(28552, "collection dropped during index build", db->getCollection(ns.ns())); + } - for ( size_t i = 0; i < specs.size(); i++ ) { - std::string systemIndexes = ns.getSystemIndexesCollection(); - getGlobalServiceContext()->getOpObserver()->onCreateIndex(txn, - systemIndexes, - specs[i]); - } + MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN { + WriteUnitOfWork wunit(txn); - wunit.commit(); - } MONGO_WRITE_CONFLICT_RETRY_LOOP_END(txn, "createIndexes", ns.ns()); + indexer.commit(); - result.append( "numIndexesAfter", collection->getIndexCatalog()->numIndexesTotal(txn) ); + for (size_t i = 0; i < specs.size(); i++) { + std::string systemIndexes = ns.getSystemIndexesCollection(); + getGlobalServiceContext()->getOpObserver()->onCreateIndex( + txn, systemIndexes, specs[i]); + } - return true; + wunit.commit(); } + MONGO_WRITE_CONFLICT_RETRY_LOOP_END(txn, "createIndexes", ns.ns()); - private: - static Status checkUniqueIndexConstraints(OperationContext* txn, - StringData ns, - const BSONObj& newIdxKey) { + result.append("numIndexesAfter", collection->getIndexCatalog()->numIndexesTotal(txn)); - invariant(txn->lockState()->isCollectionLockedForMode(ns, MODE_X)); + return true; + } - if ( shardingState.enabled() ) { - CollectionMetadataPtr metadata( - shardingState.getCollectionMetadata( ns.toString() )); +private: + static Status checkUniqueIndexConstraints(OperationContext* txn, + StringData ns, + const BSONObj& newIdxKey) { + invariant(txn->lockState()->isCollectionLockedForMode(ns, MODE_X)); - if ( metadata ) { - ShardKeyPattern shardKeyPattern(metadata->getKeyPattern()); - if (!shardKeyPattern.isUniqueIndexCompatible(newIdxKey)) { - return Status(ErrorCodes::CannotCreateIndex, - str::stream() << "cannot create unique index over " << newIdxKey - << " with shard key pattern " - << shardKeyPattern.toBSON()); - } + if (shardingState.enabled()) { + CollectionMetadataPtr metadata(shardingState.getCollectionMetadata(ns.toString())); + + if (metadata) { + ShardKeyPattern shardKeyPattern(metadata->getKeyPattern()); + if (!shardKeyPattern.isUniqueIndexCompatible(newIdxKey)) { + return Status(ErrorCodes::CannotCreateIndex, + str::stream() << "cannot create unique index over " << newIdxKey + << " with shard key pattern " + << shardKeyPattern.toBSON()); } } - - return Status::OK(); } - } cmdCreateIndex; + return Status::OK(); + } +} cmdCreateIndex; } |