diff options
Diffstat (limited to 'src/mongo/db/catalog/rename_collection.cpp')
-rw-r--r-- | src/mongo/db/catalog/rename_collection.cpp | 326 |
1 files changed, 159 insertions, 167 deletions
diff --git a/src/mongo/db/catalog/rename_collection.cpp b/src/mongo/db/catalog/rename_collection.cpp index 9c73d968e37..a077a452fb8 100644 --- a/src/mongo/db/catalog/rename_collection.cpp +++ b/src/mongo/db/catalog/rename_collection.cpp @@ -51,208 +51,200 @@ namespace mongo { namespace { - static void dropCollection(OperationContext* txn, Database* db, StringData collName) { - WriteUnitOfWork wunit(txn); - if (db->dropCollection(txn, collName).isOK()) { - // ignoring failure case - wunit.commit(); - } +static void dropCollection(OperationContext* txn, Database* db, StringData collName) { + WriteUnitOfWork wunit(txn); + if (db->dropCollection(txn, collName).isOK()) { + // ignoring failure case + wunit.commit(); + } +} +} // namespace + +Status renameCollection(OperationContext* txn, + const NamespaceString& source, + const NamespaceString& target, + bool dropTarget, + bool stayTemp) { + DisableDocumentValidation validationDisabler(txn); + + ScopedTransaction transaction(txn, MODE_X); + Lock::GlobalWrite globalWriteLock(txn->lockState()); + // We stay in source context the whole time. This is mostly to set the CurOp namespace. + OldClientContext ctx(txn, source); + + bool userInitiatedWritesAndNotPrimary = txn->writesAreReplicated() && + !repl::getGlobalReplicationCoordinator()->canAcceptWritesFor(source); + + if (userInitiatedWritesAndNotPrimary) { + return Status(ErrorCodes::NotMaster, + str::stream() << "Not primary while renaming collection " << source.ns() + << " to " << target.ns()); } -} // namespace - - Status renameCollection(OperationContext* txn, - const NamespaceString& source, - const NamespaceString& target, - bool dropTarget, - bool stayTemp) { - DisableDocumentValidation validationDisabler(txn); - - ScopedTransaction transaction(txn, MODE_X); - Lock::GlobalWrite globalWriteLock(txn->lockState()); - // We stay in source context the whole time. This is mostly to set the CurOp namespace. - OldClientContext ctx(txn, source); - - bool userInitiatedWritesAndNotPrimary = txn->writesAreReplicated() && - !repl::getGlobalReplicationCoordinator()->canAcceptWritesFor(source); - - if (userInitiatedWritesAndNotPrimary) { - return Status(ErrorCodes::NotMaster, str::stream() - << "Not primary while renaming collection " << source.ns() - << " to " << target.ns()); - } - Database* const sourceDB = dbHolder().get(txn, source.db()); - Collection* const sourceColl = sourceDB ? sourceDB->getCollection(source.ns()) : nullptr; - if (!sourceColl) { - return Status(ErrorCodes::NamespaceNotFound, "source namespace does not exist"); - } + Database* const sourceDB = dbHolder().get(txn, source.db()); + Collection* const sourceColl = sourceDB ? sourceDB->getCollection(source.ns()) : nullptr; + if (!sourceColl) { + return Status(ErrorCodes::NamespaceNotFound, "source namespace does not exist"); + } - { - // Ensure that collection name does not exceed maximum length. - // Ensure that index names do not push the length over the max. - // Iterator includes unfinished indexes. - IndexCatalog::IndexIterator sourceIndIt = - sourceColl->getIndexCatalog()->getIndexIterator(txn, true); - int longestIndexNameLength = 0; - while (sourceIndIt.more()) { - int thisLength = sourceIndIt.next()->indexName().length(); - if (thisLength > longestIndexNameLength) - longestIndexNameLength = thisLength; - } + { + // Ensure that collection name does not exceed maximum length. + // Ensure that index names do not push the length over the max. + // Iterator includes unfinished indexes. + IndexCatalog::IndexIterator sourceIndIt = + sourceColl->getIndexCatalog()->getIndexIterator(txn, true); + int longestIndexNameLength = 0; + while (sourceIndIt.more()) { + int thisLength = sourceIndIt.next()->indexName().length(); + if (thisLength > longestIndexNameLength) + longestIndexNameLength = thisLength; + } - unsigned int longestAllowed = - std::min(int(NamespaceString::MaxNsCollectionLen), - int(NamespaceString::MaxNsLen) - 2/*strlen(".$")*/ - longestIndexNameLength); - if (target.size() > longestAllowed) { - StringBuilder sb; - sb << "collection name length of " << target.size() - << " exceeds maximum length of " << longestAllowed - << ", allowing for index names"; - return Status(ErrorCodes::InvalidLength, sb.str()); - } + unsigned int longestAllowed = + std::min(int(NamespaceString::MaxNsCollectionLen), + int(NamespaceString::MaxNsLen) - 2 /*strlen(".$")*/ - longestIndexNameLength); + if (target.size() > longestAllowed) { + StringBuilder sb; + sb << "collection name length of " << target.size() << " exceeds maximum length of " + << longestAllowed << ", allowing for index names"; + return Status(ErrorCodes::InvalidLength, sb.str()); } + } - BackgroundOperation::assertNoBgOpInProgForNs(source.ns()); + BackgroundOperation::assertNoBgOpInProgForNs(source.ns()); - Database* const targetDB = dbHolder().openDb(txn, target.db()); + Database* const targetDB = dbHolder().openDb(txn, target.db()); - { - WriteUnitOfWork wunit(txn); + { + WriteUnitOfWork wunit(txn); - // Check if the target namespace exists and if dropTarget is true. - // If target exists and dropTarget is not true, return false. - if (targetDB->getCollection(target)) { - if (!dropTarget) { - printStackTrace(); - return Status(ErrorCodes::NamespaceExists, "target namespace exists"); - } - - Status s = targetDB->dropCollection(txn, target.ns()); - if (!s.isOK()) { - return s; - } + // Check if the target namespace exists and if dropTarget is true. + // If target exists and dropTarget is not true, return false. + if (targetDB->getCollection(target)) { + if (!dropTarget) { + printStackTrace(); + return Status(ErrorCodes::NamespaceExists, "target namespace exists"); } - // If we are renaming in the same database, just - // rename the namespace and we're done. - if (sourceDB == targetDB) { - Status s = targetDB->renameCollection(txn, source.ns(), target.ns(), stayTemp); - if (!s.isOK()) { - return s; - } - - getGlobalServiceContext()->getOpObserver()->onRenameCollection( - txn, - NamespaceString(source), - NamespaceString(target), - dropTarget, - stayTemp); - - wunit.commit(); - return Status::OK(); + Status s = targetDB->dropCollection(txn, target.ns()); + if (!s.isOK()) { + return s; + } + } + + // If we are renaming in the same database, just + // rename the namespace and we're done. + if (sourceDB == targetDB) { + Status s = targetDB->renameCollection(txn, source.ns(), target.ns(), stayTemp); + if (!s.isOK()) { + return s; } + getGlobalServiceContext()->getOpObserver()->onRenameCollection( + txn, NamespaceString(source), NamespaceString(target), dropTarget, stayTemp); + wunit.commit(); + return Status::OK(); } - // If we get here, we are renaming across databases, so we must copy all the data and - // indexes, then remove the source collection. + wunit.commit(); + } - // Create the target collection. It will be removed if we fail to copy the collection. - // TODO use a temp collection and unset the temp flag on success. - Collection* targetColl = nullptr; - { - CollectionOptions options = sourceColl->getCatalogEntry()->getCollectionOptions(txn); + // If we get here, we are renaming across databases, so we must copy all the data and + // indexes, then remove the source collection. - WriteUnitOfWork wunit(txn); + // Create the target collection. It will be removed if we fail to copy the collection. + // TODO use a temp collection and unset the temp flag on success. + Collection* targetColl = nullptr; + { + CollectionOptions options = sourceColl->getCatalogEntry()->getCollectionOptions(txn); - // No logOp necessary because the entire renameCollection command is one logOp. - bool shouldReplicateWrites = txn->writesAreReplicated(); - txn->setReplicatedWrites(false); - targetColl = targetDB->createCollection(txn, target.ns(), options, - false); // _id index build with others later. - txn->setReplicatedWrites(shouldReplicateWrites); - if (!targetColl) { - return Status(ErrorCodes::OutOfDiskSpace, "Failed to create target collection."); - } + WriteUnitOfWork wunit(txn); - wunit.commit(); + // No logOp necessary because the entire renameCollection command is one logOp. + bool shouldReplicateWrites = txn->writesAreReplicated(); + txn->setReplicatedWrites(false); + targetColl = targetDB->createCollection(txn, + target.ns(), + options, + false); // _id index build with others later. + txn->setReplicatedWrites(shouldReplicateWrites); + if (!targetColl) { + return Status(ErrorCodes::OutOfDiskSpace, "Failed to create target collection."); } - // Dismissed on success - ScopeGuard targetCollectionDropper = MakeGuard(dropCollection, txn, targetDB, target.ns()); - - MultiIndexBlock indexer(txn, targetColl); - indexer.allowInterruption(); - - // Copy the index descriptions from the source collection, adjusting the ns field. - { - std::vector<BSONObj> indexesToCopy; - IndexCatalog::IndexIterator sourceIndIt = - sourceColl->getIndexCatalog()->getIndexIterator(txn, true); - while (sourceIndIt.more()) { - const BSONObj currIndex = sourceIndIt.next()->infoObj(); - - // Process the source index. - BSONObjBuilder newIndex; - newIndex.append("ns", target); - newIndex.appendElementsUnique(currIndex); - indexesToCopy.push_back(newIndex.obj()); - } - indexer.init(indexesToCopy); - } + wunit.commit(); + } - { - // Copy over all the data from source collection to target collection. - auto cursor = sourceColl->getCursor(txn); - while (auto record = cursor->next()) { - txn->checkForInterrupt(); - - const auto obj = record->data.releaseToBson(); - - WriteUnitOfWork wunit(txn); - // No logOp necessary because the entire renameCollection command is one logOp. - bool shouldReplicateWrites = txn->writesAreReplicated(); - txn->setReplicatedWrites(false); - Status status = - targetColl->insertDocument(txn, obj, &indexer, true).getStatus(); - txn->setReplicatedWrites(shouldReplicateWrites); - if (!status.isOK()) - return status; - wunit.commit(); - } + // Dismissed on success + ScopeGuard targetCollectionDropper = MakeGuard(dropCollection, txn, targetDB, target.ns()); + + MultiIndexBlock indexer(txn, targetColl); + indexer.allowInterruption(); + + // Copy the index descriptions from the source collection, adjusting the ns field. + { + std::vector<BSONObj> indexesToCopy; + IndexCatalog::IndexIterator sourceIndIt = + sourceColl->getIndexCatalog()->getIndexIterator(txn, true); + while (sourceIndIt.more()) { + const BSONObj currIndex = sourceIndIt.next()->infoObj(); + + // Process the source index. + BSONObjBuilder newIndex; + newIndex.append("ns", target); + newIndex.appendElementsUnique(currIndex); + indexesToCopy.push_back(newIndex.obj()); } + indexer.init(indexesToCopy); + } - Status status = indexer.doneInserting(); - if (!status.isOK()) - return status; + { + // Copy over all the data from source collection to target collection. + auto cursor = sourceColl->getCursor(txn); + while (auto record = cursor->next()) { + txn->checkForInterrupt(); - { - // Getting here means we successfully built the target copy. We now remove the - // source collection and finalize the rename. - WriteUnitOfWork wunit(txn); + const auto obj = record->data.releaseToBson(); + WriteUnitOfWork wunit(txn); + // No logOp necessary because the entire renameCollection command is one logOp. bool shouldReplicateWrites = txn->writesAreReplicated(); txn->setReplicatedWrites(false); - Status status = sourceDB->dropCollection(txn, source.ns()); + Status status = targetColl->insertDocument(txn, obj, &indexer, true).getStatus(); txn->setReplicatedWrites(shouldReplicateWrites); if (!status.isOK()) return status; + wunit.commit(); + } + } - indexer.commit(); + Status status = indexer.doneInserting(); + if (!status.isOK()) + return status; - getGlobalServiceContext()->getOpObserver()->onRenameCollection( - txn, - NamespaceString(source), - NamespaceString(target), - dropTarget, - stayTemp); + { + // Getting here means we successfully built the target copy. We now remove the + // source collection and finalize the rename. + WriteUnitOfWork wunit(txn); - wunit.commit(); - } + bool shouldReplicateWrites = txn->writesAreReplicated(); + txn->setReplicatedWrites(false); + Status status = sourceDB->dropCollection(txn, source.ns()); + txn->setReplicatedWrites(shouldReplicateWrites); + if (!status.isOK()) + return status; - targetCollectionDropper.Dismiss(); - return Status::OK(); + indexer.commit(); + + getGlobalServiceContext()->getOpObserver()->onRenameCollection( + txn, NamespaceString(source), NamespaceString(target), dropTarget, stayTemp); + + wunit.commit(); } -} // namespace mongo + targetCollectionDropper.Dismiss(); + return Status::OK(); +} + +} // namespace mongo |