summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorGeert Bosch <geert@mongodb.com>2017-03-02 14:07:32 -0500
committerGeert Bosch <geert@mongodb.com>2017-03-08 10:07:17 -0500
commit78052771af75f9c6fdff10796c82a38167274a6f (patch)
tree7fcfa87c45775dc86c86898501b2e06541ecd21b /src/mongo
parent54f3fff857ac66be0cdca5a4ddce9f87a0d80e6a (diff)
downloadmongo-78052771af75f9c6fdff10796c82a38167274a6f.tar.gz
SERVER-27987 Create and persist UUIDs for newly created collections
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/bson/bsonelement.h11
-rw-r--r--src/mongo/db/catalog/SConscript12
-rw-r--r--src/mongo/db/catalog/collection_options.cpp43
-rw-r--r--src/mongo/db/catalog/collection_options.h44
-rw-r--r--src/mongo/db/catalog/collection_options_test.cpp39
-rw-r--r--src/mongo/db/catalog/collection_uuid.cpp62
-rw-r--r--src/mongo/db/catalog/collection_uuid.h83
-rw-r--r--src/mongo/db/catalog/create_collection.cpp8
-rw-r--r--src/mongo/db/catalog/database.cpp6
-rw-r--r--src/mongo/db/catalog/database.h1
-rw-r--r--src/mongo/db/cloner.cpp24
-rw-r--r--src/mongo/db/commands/list_collections.cpp12
-rw-r--r--src/mongo/db/db.h1
-rw-r--r--src/mongo/db/repl/collection_cloner_test.cpp6
-rw-r--r--src/mongo/db/repl/database_cloner.cpp19
-rw-r--r--src/mongo/db/repl/rs_rollback.cpp3
-rw-r--r--src/mongo/db/repl/storage_interface_impl_test.cpp2
-rw-r--r--src/mongo/db/s/migration_destination_manager.cpp9
-rw-r--r--src/mongo/db/storage/bson_collection_catalog_entry.cpp2
-rw-r--r--src/mongo/db/storage/mmap_v1/mmap_v1_database_catalog_entry.cpp3
-rw-r--r--src/mongo/db/storage/mmap_v1/repair_database.cpp3
-rw-r--r--src/mongo/dbtests/querytests.cpp1
-rw-r--r--src/mongo/dbtests/rollbacktests.cpp61
23 files changed, 357 insertions, 98 deletions
diff --git a/src/mongo/bson/bsonelement.h b/src/mongo/bson/bsonelement.h
index 111cbad442f..3120907b589 100644
--- a/src/mongo/bson/bsonelement.h
+++ b/src/mongo/bson/bsonelement.h
@@ -579,6 +579,17 @@ public:
return Timestamp();
}
+ const std::array<unsigned char, 16> uuid() const {
+ int len = 0;
+ const char* data = nullptr;
+ if (type() == BinData && binDataType() == BinDataType::newUUID)
+ data = binData(len);
+ uassert(00000, "uuid must be a 16-byte binary field with UUID (4) subtype", len == 16);
+ std::array<unsigned char, 16> result;
+ memcpy(&result, data, len);
+ return result;
+ }
+
Date_t timestampTime() const {
unsigned long long t = ConstDataView(value() + 4).read<LittleEndian<unsigned int>>();
return Date_t::fromMillisSinceEpoch(t * 1000);
diff --git a/src/mongo/db/catalog/SConscript b/src/mongo/db/catalog/SConscript
index a15aa150388..df7d91124c2 100644
--- a/src/mongo/db/catalog/SConscript
+++ b/src/mongo/db/catalog/SConscript
@@ -4,7 +4,17 @@ Import("env")
env = env.Clone()
-env.Library('collection_options', ['collection_options.cpp'], LIBDEPS=['$BUILD_DIR/mongo/base'])
+env.Library(
+ target='collection_options',
+ source=[
+ 'collection_options.cpp',
+ 'collection_uuid.cpp',
+ ],
+ LIBDEPS=[
+ '$BUILD_DIR/mongo/base',
+ '$BUILD_DIR/mongo/db/server_parameters',
+ ],
+)
env.CppUnitTest('collection_options_test', ['collection_options_test.cpp'],
LIBDEPS=['collection_options'])
diff --git a/src/mongo/db/catalog/collection_options.cpp b/src/mongo/db/catalog/collection_options.cpp
index 0a576d3c5d2..0f5152d138e 100644
--- a/src/mongo/db/catalog/collection_options.cpp
+++ b/src/mongo/db/catalog/collection_options.cpp
@@ -95,32 +95,6 @@ const std::set<StringData> collectionOptionsWhitelist{
} // namespace
-void CollectionOptions::reset() {
- capped = false;
- cappedSize = 0;
- cappedMaxDocs = 0;
- initialNumExtents = 0;
- initialExtentSizes.clear();
- autoIndexId = DEFAULT;
- // For compatibility with previous versions if the user sets no flags,
- // we set Flag_UsePowerOf2Sizes in case the user downgrades.
- flags = Flag_UsePowerOf2Sizes;
- flagsSet = false;
- temp = false;
- storageEngine = BSONObj();
- indexOptionDefaults = BSONObj();
- validator = BSONObj();
- validationLevel = "";
- validationAction = "";
- collation = BSONObj();
- viewOn = "";
- pipeline = BSONObj();
-}
-
-bool CollectionOptions::isValid() const {
- return validate().isOK();
-}
-
bool CollectionOptions::isView() const {
return !viewOn.empty();
}
@@ -129,8 +103,8 @@ Status CollectionOptions::validate() const {
return CollectionOptions().parse(toBSON());
}
-Status CollectionOptions::parse(const BSONObj& options) {
- reset();
+Status CollectionOptions::parse(const BSONObj& options, ParseKind kind) {
+ *this = {};
// Versions 2.4 and earlier of the server store "create" inside the collection metadata when the
// user issues an explicit collection creation command. These versions also wrote any
@@ -151,7 +125,13 @@ Status CollectionOptions::parse(const BSONObj& options) {
BSONElement e = i.next();
StringData fieldName = e.fieldName();
- if (fieldName == "capped") {
+ if (fieldName == "uuid" && kind == parseForStorage) {
+ try {
+ uuid.emplace(e);
+ } catch (const UserException& ex) {
+ return ex.toStatus();
+ }
+ } else if (fieldName == "capped") {
capped = e.trueValue();
} else if (fieldName == "size") {
if (!e.isNumber()) {
@@ -282,6 +262,11 @@ Status CollectionOptions::parse(const BSONObj& options) {
BSONObj CollectionOptions::toBSON() const {
BSONObjBuilder b;
+
+ if (uuid) {
+ b.appendElements(uuid->toBSON());
+ }
+
if (capped) {
b.appendBool("capped", true);
b.appendNumber("size", cappedSize);
diff --git a/src/mongo/db/catalog/collection_options.h b/src/mongo/db/catalog/collection_options.h
index 8f8867bcf27..50703874e22 100644
--- a/src/mongo/db/catalog/collection_options.h
+++ b/src/mongo/db/catalog/collection_options.h
@@ -30,27 +30,26 @@
#include <string>
+#include <boost/optional.hpp>
+
#include "mongo/base/status.h"
+#include "mongo/db/catalog/collection_uuid.h"
#include "mongo/db/jsobj.h"
namespace mongo {
struct CollectionOptions {
- CollectionOptions() {
- reset();
- }
-
- void reset();
-
/**
- * Returns true if collection options validates successfully.
+ * Returns true if the options indicate the namespace is a view.
*/
- bool isValid() const;
+ bool isView() const;
/**
- * Returns true if the options indicate the namespace is a view.
+ * The 'uuid' member is a collection property stored in the catalog with user-settable options,
+ * but is not valid for the user to specify as collection option. So, parsing commands must
+ * reject the 'uuid' property, but parsing stored options must accept it.
*/
- bool isView() const;
+ enum ParseKind { parseForCommand, parseForStorage };
/**
* Confirms that collection options can be converted to BSON and back without errors.
@@ -60,7 +59,7 @@ struct CollectionOptions {
/**
* Parses the "options" subfield of the collection info object.
*/
- Status parse(const BSONObj& obj);
+ Status parse(const BSONObj& obj, ParseKind kind = parseForCommand);
BSONObj toBSON() const;
@@ -72,15 +71,18 @@ struct CollectionOptions {
// ----
- bool capped;
- long long cappedSize;
- long long cappedMaxDocs;
+ // Collection UUID. Will exist if featureCompatibilityVersion >= 3.6.
+ boost::optional<CollectionUUID> uuid;
+
+ bool capped = false;
+ long long cappedSize = 0;
+ long long cappedMaxDocs = 0;
- // following 2 are mutually exclusive, can only have one set
- long long initialNumExtents;
+ // (MMAPv1) The following 2 are mutually exclusive, can only have one set.
+ long long initialNumExtents = 0;
std::vector<long long> initialExtentSizes;
- // behavior of _id index creation when collection created
+ // The behavior of _id index creation when collection created
void setNoIdIndex() {
autoIndexId = NO;
}
@@ -88,17 +90,17 @@ struct CollectionOptions {
DEFAULT, // currently yes for most collections, NO for some system ones
YES, // create _id index
NO // do not create _id index
- } autoIndexId;
+ } autoIndexId = DEFAULT;
// user flags
enum UserFlags {
Flag_UsePowerOf2Sizes = 1 << 0,
Flag_NoPadding = 1 << 1,
};
- int flags; // a bitvector of UserFlags
- bool flagsSet;
+ int flags = Flag_UsePowerOf2Sizes; // a bitvector of UserFlags
+ bool flagsSet = false;
- bool temp;
+ bool temp = false;
// Storage engine collection options. Always owned or empty.
BSONObj storageEngine;
diff --git a/src/mongo/db/catalog/collection_options_test.cpp b/src/mongo/db/catalog/collection_options_test.cpp
index 71f6b28fa29..cb8cef38182 100644
--- a/src/mongo/db/catalog/collection_options_test.cpp
+++ b/src/mongo/db/catalog/collection_options_test.cpp
@@ -57,14 +57,6 @@ TEST(CollectionOptions, SimpleRoundTrip) {
checkRoundTrip(options);
}
-TEST(CollectionOptions, IsValid) {
- CollectionOptions options;
- ASSERT_TRUE(options.isValid());
-
- options.storageEngine = fromjson("{storageEngine1: 1}");
- ASSERT_FALSE(options.isValid());
-}
-
TEST(CollectionOptions, Validate) {
CollectionOptions options;
ASSERT_OK(options.validate());
@@ -84,9 +76,9 @@ TEST(CollectionOptions, Validator) {
options.validator = fromjson("{b: 1}");
ASSERT_BSONOBJ_EQ(options.toBSON()["validator"].Obj(), fromjson("{b: 1}"));
- options.reset();
- ASSERT_BSONOBJ_EQ(options.validator, BSONObj());
- ASSERT(!options.toBSON()["validator"]);
+ CollectionOptions defaultOptions;
+ ASSERT_BSONOBJ_EQ(defaultOptions.validator, BSONObj());
+ ASSERT(!defaultOptions.toBSON()["validator"]);
}
TEST(CollectionOptions, ErrorBadSize) {
@@ -171,9 +163,8 @@ TEST(CollectionOptions, ResetStorageEngineField) {
ASSERT_OK(opts.parse(fromjson("{storageEngine: {storageEngine1: {x: 1}}}")));
checkRoundTrip(opts);
- opts.reset();
-
- ASSERT_TRUE(opts.storageEngine.isEmpty());
+ CollectionOptions defaultOpts;
+ ASSERT_TRUE(defaultOpts.storageEngine.isEmpty());
}
TEST(CollectionOptions, ModifyStorageEngineField) {
@@ -211,7 +202,6 @@ TEST(CollectionOptions, CollationFieldParsesCorrectly) {
CollectionOptions options;
ASSERT_OK(options.parse(fromjson("{collation: {locale: 'en'}}")));
ASSERT_BSONOBJ_EQ(options.collation, fromjson("{locale: 'en'}"));
- ASSERT_TRUE(options.isValid());
ASSERT_OK(options.validate());
}
@@ -224,10 +214,9 @@ TEST(CollectionOptions, ParsedCollationObjShouldBeOwned) {
TEST(CollectionOptions, ResetClearsCollationField) {
CollectionOptions options;
+ ASSERT_TRUE(options.collation.isEmpty());
ASSERT_OK(options.parse(fromjson("{collation: {locale: 'en'}}")));
ASSERT_FALSE(options.collation.isEmpty());
- options.reset();
- ASSERT_TRUE(options.collation.isEmpty());
}
TEST(CollectionOptions, CollationFieldLeftEmptyWhenOmitted) {
@@ -325,4 +314,20 @@ TEST(CollectionOptions, WriteConcernWhitelistedOptionIgnored) {
auto status = options.parse(fromjson("{writeConcern: 1}"));
ASSERT_OK(status);
}
+
+TEST(CollectionOptions, ParseUUID) {
+ CollectionOptions options;
+ CollectionUUID uuid = CollectionUUID::generateSecureRandomUUID();
+
+ // Check required parse failures
+ ASSERT_FALSE(options.uuid);
+ ASSERT_NOT_OK(options.parse(uuid.toBSON()));
+ ASSERT_NOT_OK(options.parse(BSON("uuid" << 1)));
+ ASSERT_NOT_OK(options.parse(BSON("uuid" << 1), CollectionOptions::parseForStorage));
+ ASSERT_FALSE(options.uuid);
+
+ // Check successful parse and roundtrip.
+ ASSERT_OK(options.parse(uuid.toBSON(), CollectionOptions::parseForStorage));
+ ASSERT(options.uuid.get() == uuid);
+}
} // namespace mongo
diff --git a/src/mongo/db/catalog/collection_uuid.cpp b/src/mongo/db/catalog/collection_uuid.cpp
new file mode 100644
index 00000000000..dfe9327705a
--- /dev/null
+++ b/src/mongo/db/catalog/collection_uuid.cpp
@@ -0,0 +1,62 @@
+/**
+ * Copyright (C) 2017 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 <http://www.gnu.org/licenses/>.
+ *
+ * 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 "collection_uuid.h"
+
+#include "mongo/db/server_parameters.h"
+#include "mongo/platform/random.h"
+#include "mongo/stdx/mutex.h"
+
+namespace mongo {
+
+bool enableCollectionUUIDs = false;
+ExportedServerParameter<bool, ServerParameterType::kStartupOnly> enableCollectionUUIDsParameter(
+ ServerParameterSet::getGlobal(), "enableCollectionUUIDs", &enableCollectionUUIDs);
+
+namespace {
+stdx::mutex uuidGenMutex;
+auto uuidGen = SecureRandom::create();
+} // namespace
+
+// static
+CollectionUUID CollectionUUID::generateSecureRandomUUID() {
+ stdx::unique_lock<stdx::mutex> lock(uuidGenMutex);
+ int64_t randomWords[2] = {uuidGen->nextInt64(), uuidGen->nextInt64()};
+ UUID randomBytes;
+ memcpy(&randomBytes, randomWords, sizeof(randomBytes));
+ // Set version in high 4 bits of byte 6 and variant in high 2 bits of byte 8, see RFC 4122,
+ // section 4.1.1, 4.1.2 and 4.1.3.
+ randomBytes[6] &= 0x0f;
+ randomBytes[6] |= 0x40; // v4
+ randomBytes[8] &= 0x3f;
+ randomBytes[8] |= 0x80; // Randomly assigned
+ return CollectionUUID{randomBytes};
+}
+} // namespace mongo
diff --git a/src/mongo/db/catalog/collection_uuid.h b/src/mongo/db/catalog/collection_uuid.h
new file mode 100644
index 00000000000..23c6deba1d9
--- /dev/null
+++ b/src/mongo/db/catalog/collection_uuid.h
@@ -0,0 +1,83 @@
+/**
+ * Copyright (C) 2017 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 <http://www.gnu.org/licenses/>.
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "mongo/db/jsobj.h"
+
+namespace mongo {
+
+extern bool enableCollectionUUIDs; // TODO(SERVER-27993) Replace based on upgrade/downgrade state.
+
+/**
+ * A CollectionUUID is a 128-bit unique identifier, per RFC 4122, v4. for a database collection.
+ * Newly created collections are assigned a new randomly generated CollectionUUID. In a replica-set
+ * or a sharded cluster, all nodes will use the same UUID for a given collection. The UUID stays
+ * with the collection until it is dropped, so even across renames. A copied collection must have
+ * own new new unique UUID though.
+ */
+class CollectionUUID {
+public:
+ using UUID = std::array<unsigned char, 16>;
+ CollectionUUID() = delete;
+ CollectionUUID(const CollectionUUID& other) = default;
+
+ inline bool operator==(const CollectionUUID& rhs) const {
+ return !memcmp(&_uuid, &rhs._uuid, sizeof(_uuid));
+ }
+
+ inline bool operator!=(const CollectionUUID& rhs) const {
+ return !(*this == rhs);
+ }
+
+ /**
+ * Parse a UUID from the given element. Caller must validate the input.
+ */
+ CollectionUUID(BSONElement from) : _uuid(from.uuid()) {}
+
+ /**
+ * Generate a new random v4 UUID per RFC 4122.
+ */
+ static CollectionUUID generateSecureRandomUUID();
+
+ /**
+ * Return a BSON object of the form { uuid: BinData(4, "...") }.
+ */
+ BSONObj toBSON() const {
+ BSONObjBuilder builder;
+ builder.appendBinData("uuid", sizeof(UUID), BinDataType::newUUID, &_uuid);
+ return builder.obj();
+ }
+
+private:
+ CollectionUUID(const UUID& uuid) : _uuid(uuid) {}
+ UUID _uuid; // UUID in network byte order
+};
+}
diff --git a/src/mongo/db/catalog/create_collection.cpp b/src/mongo/db/catalog/create_collection.cpp
index 2007aba76d6..ec963bdf0a8 100644
--- a/src/mongo/db/catalog/create_collection.cpp
+++ b/src/mongo/db/catalog/create_collection.cpp
@@ -86,7 +86,13 @@ Status createCollection(OperationContext* opCtx,
// Create collection.
const bool createDefaultIndexes = true;
- status = userCreateNS(opCtx, ctx.db(), nss.ns(), options, createDefaultIndexes, idIndex);
+ status = userCreateNS(opCtx,
+ ctx.db(),
+ nss.ns(),
+ options,
+ CollectionOptions::parseForCommand,
+ createDefaultIndexes,
+ idIndex);
if (!status.isOK()) {
return status;
}
diff --git a/src/mongo/db/catalog/database.cpp b/src/mongo/db/catalog/database.cpp
index b6c11f4c51f..1f68f3256ee 100644
--- a/src/mongo/db/catalog/database.cpp
+++ b/src/mongo/db/catalog/database.cpp
@@ -640,6 +640,7 @@ Status userCreateNS(OperationContext* opCtx,
Database* db,
StringData ns,
BSONObj options,
+ CollectionOptions::ParseKind parseKind,
bool createDefaultIndexes,
const BSONObj& idIndex) {
invariant(db);
@@ -660,7 +661,7 @@ Status userCreateNS(OperationContext* opCtx,
str::stream() << "a view '" << ns.toString() << "' already exists");
CollectionOptions collectionOptions;
- Status status = collectionOptions.parse(options);
+ Status status = collectionOptions.parse(options, parseKind);
if (!status.isOK())
return status;
@@ -704,8 +705,11 @@ Status userCreateNS(OperationContext* opCtx,
}
if (collectionOptions.isView()) {
+ invariant(parseKind == CollectionOptions::parseForCommand);
uassertStatusOK(db->createView(opCtx, ns, collectionOptions));
} else {
+ if (enableCollectionUUIDs && !collectionOptions.uuid)
+ collectionOptions.uuid.emplace(CollectionUUID::generateSecureRandomUUID());
invariant(
db->createCollection(opCtx, ns, collectionOptions, createDefaultIndexes, idIndex));
}
diff --git a/src/mongo/db/catalog/database.h b/src/mongo/db/catalog/database.h
index de8e65aa3ce..a41d9dcdffb 100644
--- a/src/mongo/db/catalog/database.h
+++ b/src/mongo/db/catalog/database.h
@@ -267,6 +267,7 @@ Status userCreateNS(OperationContext* opCtx,
Database* db,
StringData ns,
BSONObj options,
+ CollectionOptions::ParseKind parseKind = CollectionOptions::parseForCommand,
bool createDefaultIndexes = true,
const BSONObj& idIndex = BSONObj());
diff --git a/src/mongo/db/cloner.cpp b/src/mongo/db/cloner.cpp
index 439d3ac574c..20455ad7c22 100644
--- a/src/mongo/db/cloner.cpp
+++ b/src/mongo/db/cloner.cpp
@@ -170,11 +170,13 @@ struct Cloner::Fun {
opCtx->checkForInterrupt();
WriteUnitOfWork wunit(opCtx);
+ const bool createDefaultIndexes = true;
Status s = userCreateNS(opCtx,
db,
to_collection.toString(),
from_options,
- true,
+ CollectionOptions::parseForCommand,
+ createDefaultIndexes,
fixIndexSpec(to_collection.db().toString(), from_id_index));
verify(s.isOK());
wunit.commit();
@@ -390,12 +392,14 @@ void Cloner::copyIndexes(OperationContext* opCtx,
opCtx->checkForInterrupt();
WriteUnitOfWork wunit(opCtx);
+ const bool createDefaultIndexes = true;
Status s = userCreateNS(
opCtx,
db,
to_collection.toString(),
from_opts,
- true,
+ CollectionOptions::parseForCommand,
+ createDefaultIndexes,
fixIndexSpec(to_collection.db().toString(), getIdIndexSpec(from_indexes)));
invariant(s.isOK());
collection = db->getCollection(to_collection);
@@ -490,7 +494,14 @@ bool Cloner::copyCollection(OperationContext* opCtx,
opCtx->checkForInterrupt();
WriteUnitOfWork wunit(opCtx);
- Status status = userCreateNS(opCtx, db, ns, options, true, idIndexSpec);
+ const bool createDefaultIndexes = true;
+ Status status = userCreateNS(opCtx,
+ db,
+ ns,
+ options,
+ CollectionOptions::parseForCommand,
+ createDefaultIndexes,
+ idIndexSpec);
if (!status.isOK()) {
errmsg = status.toString();
// abort write unit of work
@@ -528,7 +539,8 @@ StatusWith<std::vector<BSONObj>> Cloner::filterCollectionsForClone(
BSONElement collectionOptions = collection["options"];
if (collectionOptions.isABSONObj()) {
- auto parseOptionsStatus = CollectionOptions().parse(collectionOptions.Obj());
+ auto parseOptionsStatus = CollectionOptions().parse(collectionOptions.Obj(),
+ CollectionOptions::parseForCommand);
if (!parseOptionsStatus.isOK()) {
return parseOptionsStatus;
}
@@ -578,12 +590,14 @@ Status Cloner::createCollectionsForDb(
opCtx->checkForInterrupt();
WriteUnitOfWork wunit(opCtx);
+ const bool createDefaultIndexes = true;
Status createStatus =
userCreateNS(opCtx,
db,
nss.ns(),
options,
- true,
+ CollectionOptions::parseForCommand,
+ createDefaultIndexes,
fixIndexSpec(nss.db().toString(), params.idIndexSpec));
if (!createStatus.isOK()) {
return createStatus;
diff --git a/src/mongo/db/commands/list_collections.cpp b/src/mongo/db/commands/list_collections.cpp
index 68a6337f548..a0f998fe346 100644
--- a/src/mongo/db/commands/list_collections.cpp
+++ b/src/mongo/db/commands/list_collections.cpp
@@ -163,10 +163,18 @@ BSONObj buildCollectionBson(OperationContext* opCtx, const Collection* collectio
b.append("type", "collection");
CollectionOptions options = collection->getCatalogEntry()->getCollectionOptions(opCtx);
+
+ // While the UUID is stored as a collection option, from the user's perspective it is an
+ // unsettable read-only property, so put it in the 'info' section.
+ auto uuid = options.uuid;
+ options.uuid.reset();
b.append("options", options.toBSON());
- BSONObj info = BSON("readOnly" << storageGlobalParams.readOnly);
- b.append("info", info);
+ BSONObjBuilder infoBuilder;
+ infoBuilder.append("readOnly", storageGlobalParams.readOnly);
+ if (uuid)
+ infoBuilder.appendElements(uuid->toBSON());
+ b.append("info", infoBuilder.obj());
auto idIndex = collection->getIndexCatalog()->findIdIndex(opCtx);
if (idIndex) {
diff --git a/src/mongo/db/db.h b/src/mongo/db/db.h
index 180ad9847aa..e94bb34ccb2 100644
--- a/src/mongo/db/db.h
+++ b/src/mongo/db/db.h
@@ -1,3 +1,4 @@
+
/**
* Copyright (C) 2008 10gen Inc.
*
diff --git a/src/mongo/db/repl/collection_cloner_test.cpp b/src/mongo/db/repl/collection_cloner_test.cpp
index 440b8dee232..aac4d047372 100644
--- a/src/mongo/db/repl/collection_cloner_test.cpp
+++ b/src/mongo/db/repl/collection_cloner_test.cpp
@@ -73,7 +73,7 @@ protected:
void CollectionClonerTest::setUp() {
BaseClonerTest::setUp();
- options.reset();
+ options = {};
collectionCloner.reset(nullptr);
collectionCloner = stdx::make_unique<CollectionCloner>(
&getExecutor(),
@@ -101,7 +101,7 @@ void CollectionClonerTest::tearDown() {
BaseClonerTest::tearDown();
// Executor may still invoke collection cloner's callback before shutting down.
collectionCloner.reset(nullptr);
- options.reset();
+ options = {};
}
BaseCloner* CollectionClonerTest::getCloner() const {
@@ -320,7 +320,7 @@ TEST_F(CollectionClonerTest,
}
TEST_F(CollectionClonerTest, DoNotCreateIDIndexIfAutoIndexIdUsed) {
- options.reset();
+ options = {};
options.autoIndexId = CollectionOptions::NO;
collectionCloner.reset(new CollectionCloner(
&getExecutor(),
diff --git a/src/mongo/db/repl/database_cloner.cpp b/src/mongo/db/repl/database_cloner.cpp
index ba3f52ed048..f7b4cbbc3f7 100644
--- a/src/mongo/db/repl/database_cloner.cpp
+++ b/src/mongo/db/repl/database_cloner.cpp
@@ -58,6 +58,8 @@ using UniqueLock = stdx::unique_lock<stdx::mutex>;
const char* kNameFieldName = "name";
const char* kOptionsFieldName = "options";
+const char* kInfoFieldName = "info";
+const char* kUUIDFieldName = "uuid";
// The number of attempts for the listCollections commands.
MONGO_EXPORT_SERVER_PARAMETER(numInitialSyncListCollectionsAttempts, int, 3);
@@ -341,11 +343,26 @@ void DatabaseCloner::_listCollectionsCallback(const StatusWith<Fetcher::QueryRes
}
const BSONObj optionsObj = optionsElement.Obj();
CollectionOptions options;
- Status parseStatus = options.parse(optionsObj);
+ Status parseStatus = options.parse(optionsObj, CollectionOptions::parseForCommand);
if (!parseStatus.isOK()) {
_finishCallback_inlock(lk, parseStatus);
return;
}
+
+ BSONElement infoElement = info.getField(kInfoFieldName);
+ if (infoElement.isABSONObj()) {
+ BSONElement uuidElement = infoElement[kUUIDFieldName];
+ if (!uuidElement.eoo()) {
+ try {
+ options.uuid.emplace(uuidElement);
+ } catch (const UserException& ex) {
+ _finishCallback_inlock(lk, ex.toStatus());
+ return;
+ }
+ }
+ }
+ // TODO(SERVER-27994): Ensure UUID present when FCV >= "3.6".
+
seen.insert(collectionName);
_collectionNamespaces.emplace_back(_dbname, collectionName);
diff --git a/src/mongo/db/repl/rs_rollback.cpp b/src/mongo/db/repl/rs_rollback.cpp
index dbfb3a51284..8859ea6fba7 100644
--- a/src/mongo/db/repl/rs_rollback.cpp
+++ b/src/mongo/db/repl/rs_rollback.cpp
@@ -480,12 +480,13 @@ void syncFixUp(OperationContext* opCtx,
<< typeName(optionsField.type()));
}
- auto status = options.parse(optionsField.Obj());
+ auto status = options.parse(optionsField.Obj(), CollectionOptions::parseForCommand);
if (!status.isOK()) {
throw RSFatalException(str::stream() << "Failed to parse options " << info
<< ": "
<< status.toString());
}
+ // TODO(SERVER-27992): Set options.uuid.
} else {
// Use default options.
}
diff --git a/src/mongo/db/repl/storage_interface_impl_test.cpp b/src/mongo/db/repl/storage_interface_impl_test.cpp
index 2ee2cbf2259..cd8bdd98686 100644
--- a/src/mongo/db/repl/storage_interface_impl_test.cpp
+++ b/src/mongo/db/repl/storage_interface_impl_test.cpp
@@ -576,7 +576,7 @@ TEST_F(StorageInterfaceImplWithReplCoordTest, CreateCollectionThatAlreadyExistsF
NamespaceString nss("test.system.indexes");
createCollection(opCtx, nss);
- const CollectionOptions opts;
+ const CollectionOptions opts{};
const std::vector<BSONObj> indexes;
const auto status =
storage.createCollectionForBulkLoading(nss, opts, makeIdIndexSpec(nss), indexes);
diff --git a/src/mongo/db/s/migration_destination_manager.cpp b/src/mongo/db/s/migration_destination_manager.cpp
index cb46f30b056..470afdd6d02 100644
--- a/src/mongo/db/s/migration_destination_manager.cpp
+++ b/src/mongo/db/s/migration_destination_manager.cpp
@@ -509,7 +509,14 @@ void MigrationDestinationManager::_migrateDriver(OperationContext* opCtx,
}
WriteUnitOfWork wuow(opCtx);
- Status status = userCreateNS(opCtx, db, _nss.ns(), options, true, idIndexSpec);
+ const bool createDefaultIndexes = true;
+ Status status = userCreateNS(opCtx,
+ db,
+ _nss.ns(),
+ options,
+ CollectionOptions::parseForCommand,
+ createDefaultIndexes,
+ idIndexSpec);
if (!status.isOK()) {
warning() << "failed to create collection [" << _nss << "] "
<< " with options " << options << ": " << redact(status);
diff --git a/src/mongo/db/storage/bson_collection_catalog_entry.cpp b/src/mongo/db/storage/bson_collection_catalog_entry.cpp
index f7c3eb3d2e0..9bd57ee6941 100644
--- a/src/mongo/db/storage/bson_collection_catalog_entry.cpp
+++ b/src/mongo/db/storage/bson_collection_catalog_entry.cpp
@@ -260,7 +260,7 @@ void BSONCollectionCatalogEntry::MetaData::parse(const BSONObj& obj) {
ns = obj["ns"].valuestrsafe();
if (obj["options"].isABSONObj()) {
- options.parse(obj["options"].Obj());
+ options.parse(obj["options"].Obj(), CollectionOptions::parseForStorage);
}
BSONElement indexList = obj["indexes"];
diff --git a/src/mongo/db/storage/mmap_v1/mmap_v1_database_catalog_entry.cpp b/src/mongo/db/storage/mmap_v1/mmap_v1_database_catalog_entry.cpp
index c88ed7545c4..d92f89ca2e3 100644
--- a/src/mongo/db/storage/mmap_v1/mmap_v1_database_catalog_entry.cpp
+++ b/src/mongo/db/storage/mmap_v1/mmap_v1_database_catalog_entry.cpp
@@ -906,7 +906,8 @@ CollectionOptions MMAPV1DatabaseCatalogEntry::getCollectionOptions(OperationCont
invariant(rs->findRecord(opCtx, rid, &data));
if (data.releaseToBson()["options"].isABSONObj()) {
- Status status = options.parse(data.releaseToBson()["options"].Obj());
+ Status status = options.parse(data.releaseToBson()["options"].Obj(),
+ CollectionOptions::parseForStorage);
fassert(18523, status);
}
return options;
diff --git a/src/mongo/db/storage/mmap_v1/repair_database.cpp b/src/mongo/db/storage/mmap_v1/repair_database.cpp
index c321cd5513c..90198cfd214 100644
--- a/src/mongo/db/storage/mmap_v1/repair_database.cpp
+++ b/src/mongo/db/storage/mmap_v1/repair_database.cpp
@@ -361,7 +361,8 @@ Status MMAPV1Engine::repairDatabase(OperationContext* opCtx,
CollectionOptions options;
if (obj["options"].isABSONObj()) {
- Status status = options.parse(obj["options"].Obj());
+ Status status =
+ options.parse(obj["options"].Obj(), CollectionOptions::parseForCommand);
if (!status.isOK())
return status;
}
diff --git a/src/mongo/dbtests/querytests.cpp b/src/mongo/dbtests/querytests.cpp
index b2b5495573e..83765c6ecd4 100644
--- a/src/mongo/dbtests/querytests.cpp
+++ b/src/mongo/dbtests/querytests.cpp
@@ -1318,6 +1318,7 @@ public:
ctx.db(),
ns(),
fromjson("{ capped : true, size : 2000, max: 10000 }"),
+ CollectionOptions::parseForCommand,
false)
.isOK());
wunit.commit();
diff --git a/src/mongo/dbtests/rollbacktests.cpp b/src/mongo/dbtests/rollbacktests.cpp
index 27982080d36..d9e70651178 100644
--- a/src/mongo/dbtests/rollbacktests.cpp
+++ b/src/mongo/dbtests/rollbacktests.cpp
@@ -72,7 +72,8 @@ void createCollection(OperationContext* opCtx, const NamespaceString& nss) {
{
WriteUnitOfWork uow(opCtx);
ASSERT(!collectionExists(&ctx, nss.ns()));
- ASSERT_OK(userCreateNS(opCtx, ctx.db(), nss.ns(), BSONObj(), false));
+ ASSERT_OK(userCreateNS(
+ opCtx, ctx.db(), nss.ns(), BSONObj(), CollectionOptions::parseForCommand, false));
ASSERT(collectionExists(&ctx, nss.ns()));
uow.commit();
}
@@ -161,7 +162,8 @@ public:
WriteUnitOfWork uow(&opCtx);
ASSERT(!collectionExists(&ctx, ns));
auto options = capped ? BSON("capped" << true << "size" << 1000) : BSONObj();
- ASSERT_OK(userCreateNS(&opCtx, ctx.db(), ns, options, defaultIndexes));
+ ASSERT_OK(userCreateNS(
+ &opCtx, ctx.db(), ns, options, CollectionOptions::parseForCommand, defaultIndexes));
ASSERT(collectionExists(&ctx, ns));
if (!rollback) {
uow.commit();
@@ -192,7 +194,8 @@ public:
WriteUnitOfWork uow(&opCtx);
ASSERT(!collectionExists(&ctx, ns));
auto options = capped ? BSON("capped" << true << "size" << 1000) : BSONObj();
- ASSERT_OK(userCreateNS(&opCtx, ctx.db(), ns, options, defaultIndexes));
+ ASSERT_OK(userCreateNS(
+ &opCtx, ctx.db(), ns, options, CollectionOptions::parseForCommand, defaultIndexes));
uow.commit();
}
ASSERT(collectionExists(&ctx, ns));
@@ -236,7 +239,12 @@ public:
WriteUnitOfWork uow(&opCtx);
ASSERT(!collectionExists(&ctx, source.ns()));
ASSERT(!collectionExists(&ctx, target.ns()));
- ASSERT_OK(userCreateNS(&opCtx, ctx.db(), source.ns(), BSONObj(), defaultIndexes));
+ ASSERT_OK(userCreateNS(&opCtx,
+ ctx.db(),
+ source.ns(),
+ BSONObj(),
+ CollectionOptions::parseForCommand,
+ defaultIndexes));
uow.commit();
}
ASSERT(collectionExists(&ctx, source.ns()));
@@ -288,8 +296,18 @@ public:
WriteUnitOfWork uow(&opCtx);
ASSERT(!collectionExists(&ctx, source.ns()));
ASSERT(!collectionExists(&ctx, target.ns()));
- ASSERT_OK(userCreateNS(&opCtx, ctx.db(), source.ns(), BSONObj(), defaultIndexes));
- ASSERT_OK(userCreateNS(&opCtx, ctx.db(), target.ns(), BSONObj(), defaultIndexes));
+ ASSERT_OK(userCreateNS(&opCtx,
+ ctx.db(),
+ source.ns(),
+ BSONObj(),
+ CollectionOptions::parseForCommand,
+ defaultIndexes));
+ ASSERT_OK(userCreateNS(&opCtx,
+ ctx.db(),
+ target.ns(),
+ BSONObj(),
+ CollectionOptions::parseForCommand,
+ defaultIndexes));
insertRecord(&opCtx, source, sourceDoc);
insertRecord(&opCtx, target, targetDoc);
@@ -348,7 +366,12 @@ public:
{
WriteUnitOfWork uow(&opCtx);
ASSERT(!collectionExists(&ctx, nss.ns()));
- ASSERT_OK(userCreateNS(&opCtx, ctx.db(), nss.ns(), BSONObj(), defaultIndexes));
+ ASSERT_OK(userCreateNS(&opCtx,
+ ctx.db(),
+ nss.ns(),
+ BSONObj(),
+ CollectionOptions::parseForCommand,
+ defaultIndexes));
insertRecord(&opCtx, nss, oldDoc);
uow.commit();
}
@@ -361,7 +384,12 @@ public:
WriteUnitOfWork uow(&opCtx);
ASSERT_OK(ctx.db()->dropCollection(&opCtx, nss.ns()));
ASSERT(!collectionExists(&ctx, nss.ns()));
- ASSERT_OK(userCreateNS(&opCtx, ctx.db(), nss.ns(), BSONObj(), defaultIndexes));
+ ASSERT_OK(userCreateNS(&opCtx,
+ ctx.db(),
+ nss.ns(),
+ BSONObj(),
+ CollectionOptions::parseForCommand,
+ defaultIndexes));
ASSERT(collectionExists(&ctx, nss.ns()));
insertRecord(&opCtx, nss, newDoc);
assertOnlyRecord(&opCtx, nss, newDoc);
@@ -398,7 +426,12 @@ public:
{
WriteUnitOfWork uow(&opCtx);
- ASSERT_OK(userCreateNS(&opCtx, ctx.db(), nss.ns(), BSONObj(), defaultIndexes));
+ ASSERT_OK(userCreateNS(&opCtx,
+ ctx.db(),
+ nss.ns(),
+ BSONObj(),
+ CollectionOptions::parseForCommand,
+ defaultIndexes));
ASSERT(collectionExists(&ctx, nss.ns()));
insertRecord(&opCtx, nss, doc);
assertOnlyRecord(&opCtx, nss, doc);
@@ -434,7 +467,12 @@ public:
{
WriteUnitOfWork uow(&opCtx);
- ASSERT_OK(userCreateNS(&opCtx, ctx.db(), nss.ns(), BSONObj(), defaultIndexes));
+ ASSERT_OK(userCreateNS(&opCtx,
+ ctx.db(),
+ nss.ns(),
+ BSONObj(),
+ CollectionOptions::parseForCommand,
+ defaultIndexes));
ASSERT(collectionExists(&ctx, nss.ns()));
insertRecord(&opCtx, nss, doc);
assertOnlyRecord(&opCtx, nss, doc);
@@ -698,7 +736,8 @@ public:
{
WriteUnitOfWork uow(&opCtx);
ASSERT(!collectionExists(&ctx, nss.ns()));
- ASSERT_OK(userCreateNS(&opCtx, ctx.db(), nss.ns(), BSONObj(), false));
+ ASSERT_OK(userCreateNS(
+ &opCtx, ctx.db(), nss.ns(), BSONObj(), CollectionOptions::parseForCommand, false));
ASSERT(collectionExists(&ctx, nss.ns()));
Collection* coll = ctx.db()->getCollection(ns);
IndexCatalog* catalog = coll->getIndexCatalog();