diff options
author | Daniel Solnik <dan.solnik@Daniels-MacBook-Pro-3.local> | 2019-06-07 13:04:20 -0400 |
---|---|---|
committer | Daniel Solnik <dansolnik@gmail.com> | 2019-06-26 14:06:12 -0400 |
commit | f16da96fdbb9a151cbc9890802c53370298b4fd2 (patch) | |
tree | 265a51f227be275dc2c5bf7449426fa85782cfa3 /src/mongo/db | |
parent | d40952211bc71634435be37855855243154a3571 (diff) | |
download | mongo-f16da96fdbb9a151cbc9890802c53370298b4fd2.tar.gz |
SERVER-38796 Refactor CollectionOptions::parse to be static
Diffstat (limited to 'src/mongo/db')
-rw-r--r-- | src/mongo/db/catalog/collection_options.cpp | 59 | ||||
-rw-r--r-- | src/mongo/db/catalog/collection_options.h | 13 | ||||
-rw-r--r-- | src/mongo/db/catalog/collection_options_test.cpp | 186 | ||||
-rw-r--r-- | src/mongo/db/catalog/collection_test.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/catalog/create_collection.cpp | 7 | ||||
-rw-r--r-- | src/mongo/db/cloner.cpp | 21 | ||||
-rw-r--r-- | src/mongo/db/commands/find_and_modify.cpp | 7 | ||||
-rw-r--r-- | src/mongo/db/db.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/index_build_entry_helpers.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/ops/write_ops_exec.cpp | 6 | ||||
-rw-r--r-- | src/mongo/db/repl/database_cloner.cpp | 8 | ||||
-rw-r--r-- | src/mongo/db/repl/rs_rollback.cpp | 20 | ||||
-rw-r--r-- | src/mongo/db/s/config/configsvr_create_collection_command.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/s/config/sharding_catalog_manager_collection_operations.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/s/migration_destination_manager.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/storage/bson_collection_catalog_entry.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/storage/wiredtiger/wiredtiger_kv_engine_test.cpp | 28 |
17 files changed, 184 insertions, 201 deletions
diff --git a/src/mongo/db/catalog/collection_options.cpp b/src/mongo/db/catalog/collection_options.cpp index a156754bf14..b9ca30d8bc6 100644 --- a/src/mongo/db/catalog/collection_options.cpp +++ b/src/mongo/db/catalog/collection_options.cpp @@ -100,12 +100,11 @@ bool CollectionOptions::isView() const { } Status CollectionOptions::validateForStorage() const { - return CollectionOptions().parse(toBSON(), ParseKind::parseForStorage); + return CollectionOptions::parse(toBSON(), ParseKind::parseForStorage).getStatus(); } -Status CollectionOptions::parse(const BSONObj& options, ParseKind kind) { - *this = {}; - +StatusWith<CollectionOptions> CollectionOptions::parse(const BSONObj& options, ParseKind kind) { + CollectionOptions collectionOptions; // 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 // unrecognized fields into the catalog metadata and allowed the order of these fields to be @@ -130,30 +129,30 @@ Status CollectionOptions::parse(const BSONObj& options, ParseKind kind) { if (!res.isOK()) { return res.getStatus(); } - uuid = res.getValue(); + collectionOptions.uuid = res.getValue(); } else if (fieldName == "capped") { - capped = e.trueValue(); + collectionOptions.capped = e.trueValue(); } else if (fieldName == "size") { if (!e.isNumber()) { // Ignoring for backwards compatibility. continue; } - cappedSize = e.safeNumberLong(); - if (cappedSize < 0) + collectionOptions.cappedSize = e.safeNumberLong(); + if (collectionOptions.cappedSize < 0) return Status(ErrorCodes::BadValue, "size has to be >= 0"); const long long kGB = 1024 * 1024 * 1024; const long long kPB = 1024 * 1024 * kGB; - if (cappedSize > kPB) + if (collectionOptions.cappedSize > kPB) return Status(ErrorCodes::BadValue, "size cannot exceed 1 PB"); - cappedSize += 0xff; - cappedSize &= 0xffffffffffffff00LL; + collectionOptions.cappedSize += 0xff; + collectionOptions.cappedSize &= 0xffffffffffffff00LL; } else if (fieldName == "max") { if (!options["capped"].trueValue() || !e.isNumber()) { // Ignoring for backwards compatibility. continue; } - cappedMaxDocs = e.safeNumberLong(); - if (!validMaxCappedDocs(&cappedMaxDocs)) + collectionOptions.cappedMaxDocs = e.safeNumberLong(); + if (!validMaxCappedDocs(&collectionOptions.cappedMaxDocs)) return Status(ErrorCodes::BadValue, "max in a capped collection has to be < 2^31 or not set"); } else if (fieldName == "$nExtents") { @@ -161,27 +160,27 @@ Status CollectionOptions::parse(const BSONObj& options, ParseKind kind) { BSONObjIterator j(e.Obj()); while (j.more()) { BSONElement inner = j.next(); - initialExtentSizes.push_back(inner.numberInt()); + collectionOptions.initialExtentSizes.push_back(inner.numberInt()); } } else { - initialNumExtents = e.safeNumberLong(); + collectionOptions.initialNumExtents = e.safeNumberLong(); } } else if (fieldName == "autoIndexId") { if (e.trueValue()) - autoIndexId = YES; + collectionOptions.autoIndexId = YES; else - autoIndexId = NO; + collectionOptions.autoIndexId = NO; } else if (fieldName == "flags") { // Ignoring this field as it is deprecated. continue; } else if (fieldName == "temp") { - temp = e.trueValue(); + collectionOptions.temp = e.trueValue(); } else if (fieldName == "storageEngine") { Status status = checkStorageEngineOptions(e); if (!status.isOK()) { return status; } - storageEngine = e.Obj().getOwned(); + collectionOptions.storageEngine = e.Obj().getOwned(); } else if (fieldName == "indexOptionDefaults") { if (e.type() != mongo::Object) { return {ErrorCodes::TypeMismatch, "'indexOptionDefaults' has to be a document."}; @@ -199,25 +198,25 @@ Status CollectionOptions::parse(const BSONObj& options, ParseKind kind) { << " is not a supported option."}; } } - indexOptionDefaults = e.Obj().getOwned(); + collectionOptions.indexOptionDefaults = e.Obj().getOwned(); } else if (fieldName == "validator") { if (e.type() != mongo::Object) { return Status(ErrorCodes::BadValue, "'validator' has to be a document."); } - validator = e.Obj().getOwned(); + collectionOptions.validator = e.Obj().getOwned(); } else if (fieldName == "validationAction") { if (e.type() != mongo::String) { return Status(ErrorCodes::BadValue, "'validationAction' has to be a string."); } - validationAction = e.String(); + collectionOptions.validationAction = e.String(); } else if (fieldName == "validationLevel") { if (e.type() != mongo::String) { return Status(ErrorCodes::BadValue, "'validationLevel' has to be a string."); } - validationLevel = e.String(); + collectionOptions.validationLevel = e.String(); } else if (fieldName == "collation") { if (e.type() != mongo::Object) { return Status(ErrorCodes::BadValue, "'collation' has to be a document."); @@ -227,14 +226,14 @@ Status CollectionOptions::parse(const BSONObj& options, ParseKind kind) { return Status(ErrorCodes::BadValue, "'collation' cannot be an empty document."); } - collation = e.Obj().getOwned(); + collectionOptions.collation = e.Obj().getOwned(); } else if (fieldName == "viewOn") { if (e.type() != mongo::String) { return Status(ErrorCodes::BadValue, "'viewOn' has to be a string."); } - viewOn = e.String(); - if (viewOn.empty()) { + collectionOptions.viewOn = e.String(); + if (collectionOptions.viewOn.empty()) { return Status(ErrorCodes::BadValue, "'viewOn' cannot be empty.'"); } } else if (fieldName == "pipeline") { @@ -242,7 +241,7 @@ Status CollectionOptions::parse(const BSONObj& options, ParseKind kind) { return Status(ErrorCodes::BadValue, "'pipeline' has to be an array."); } - pipeline = e.Obj().getOwned(); + collectionOptions.pipeline = e.Obj().getOwned(); } else if (fieldName == "idIndex" && kind == parseForCommand) { if (e.type() != mongo::Object) { return Status(ErrorCodes::TypeMismatch, "'idIndex' has to be an object."); @@ -253,7 +252,7 @@ Status CollectionOptions::parse(const BSONObj& options, ParseKind kind) { return {ErrorCodes::FailedToParse, "idIndex cannot be empty"}; } - idIndex = std::move(tempIdIndex); + collectionOptions.idIndex = std::move(tempIdIndex); } else if (!createdOn24OrEarlier && !mongo::isGenericArgument(fieldName)) { return Status(ErrorCodes::InvalidOptions, str::stream() << "The field '" << fieldName @@ -262,11 +261,11 @@ Status CollectionOptions::parse(const BSONObj& options, ParseKind kind) { } } - if (viewOn.empty() && !pipeline.isEmpty()) { + if (collectionOptions.viewOn.empty() && !collectionOptions.pipeline.isEmpty()) { return Status(ErrorCodes::BadValue, "'pipeline' cannot be specified without 'viewOn'"); } - return Status::OK(); + return collectionOptions; } BSONObj CollectionOptions::toBSON() const { diff --git a/src/mongo/db/catalog/collection_options.h b/src/mongo/db/catalog/collection_options.h index 2558df0f5ea..ca55a98a5c8 100644 --- a/src/mongo/db/catalog/collection_options.h +++ b/src/mongo/db/catalog/collection_options.h @@ -71,9 +71,16 @@ struct CollectionOptions { Status validateForStorage() const; /** - * Parses the "options" subfield of the collection info object. + * Parses the collection 'options' into the appropriate struct fields. + * + * When 'kind' is set to ParseKind::parseForStorage, the 'uuid' field is parsed, + * otherwise the 'uuid' field is not parsed. + * + * When 'kind' is set to ParseKind::parseForCommand, the 'idIndex' field is parsed, + * otherwise the 'idIndex' field is not parsed. */ - Status parse(const BSONObj& obj, ParseKind kind = parseForCommand); + static StatusWith<CollectionOptions> parse(const BSONObj& options, + ParseKind kind = parseForCommand); void appendBSON(BSONObjBuilder* builder) const; BSONObj toBSON() const; @@ -94,8 +101,6 @@ struct CollectionOptions { bool matchesStorageOptions(const CollectionOptions& other, CollatorFactoryInterface* collatorFactory) const; - // ---- - // Collection UUID. Present for all CollectionOptions parsed for storage. OptionalCollectionUUID uuid; diff --git a/src/mongo/db/catalog/collection_options_test.cpp b/src/mongo/db/catalog/collection_options_test.cpp index 8065c38f207..bd4fd992a02 100644 --- a/src/mongo/db/catalog/collection_options_test.cpp +++ b/src/mongo/db/catalog/collection_options_test.cpp @@ -37,11 +37,11 @@ #include "mongo/unittest/unittest.h" namespace mongo { +using unittest::assertGet; -void checkRoundTrip(const CollectionOptions& options1) { - CollectionOptions options2; - options2.parse(options1.toBSON()).transitional_ignore(); - ASSERT_BSONOBJ_EQ(options1.toBSON(), options2.toBSON()); +void checkRoundTrip(const CollectionOptions& options) { + CollectionOptions parsedOptions = assertGet(CollectionOptions::parse(options.toBSON())); + ASSERT_BSONOBJ_EQ(options.toBSON(), parsedOptions.toBSON()); } TEST(CollectionOptions, SimpleRoundTrip) { @@ -66,11 +66,10 @@ TEST(CollectionOptions, Validate) { } TEST(CollectionOptions, Validator) { - CollectionOptions options; - - ASSERT_NOT_OK(options.parse(fromjson("{validator: 'notAnObject'}"))); - ASSERT_OK(options.parse(fromjson("{validator: {a: 1}}"))); + ASSERT_NOT_OK(CollectionOptions::parse(fromjson("{validator: 'notAnObject'}")).getStatus()); + CollectionOptions options = + assertGet(CollectionOptions::parse(fromjson("{validator: {a: 1}}"))); ASSERT_BSONOBJ_EQ(options.validator, fromjson("{a: 1}")); options.validator = fromjson("{b: 1}"); @@ -82,41 +81,45 @@ TEST(CollectionOptions, Validator) { } TEST(CollectionOptions, ErrorBadSize) { - ASSERT_NOT_OK(CollectionOptions().parse(fromjson("{capped: true, size: -1}"))); - ASSERT_NOT_OK(CollectionOptions().parse(fromjson("{capped: false, size: -1}"))); - ASSERT_NOT_OK(CollectionOptions().parse( - BSON("capped" << true << "size" << std::numeric_limits<long long>::min()))); - ASSERT_NOT_OK(CollectionOptions().parse(BSON("capped" << true << "size" << (1LL << 62)))); - ASSERT_NOT_OK(CollectionOptions().parse( - BSON("capped" << true << "size" << std::numeric_limits<long long>::max()))); + ASSERT_NOT_OK(CollectionOptions::parse(fromjson("{capped: true, size: -1}")).getStatus()); + ASSERT_NOT_OK(CollectionOptions::parse(fromjson("{capped: false, size: -1}")).getStatus()); + ASSERT_NOT_OK(CollectionOptions::parse( + BSON("capped" << true << "size" << std::numeric_limits<long long>::min())) + .getStatus()); + ASSERT_NOT_OK( + CollectionOptions::parse(BSON("capped" << true << "size" << (1LL << 62))).getStatus()); + ASSERT_NOT_OK(CollectionOptions::parse( + BSON("capped" << true << "size" << std::numeric_limits<long long>::max())) + .getStatus()); } TEST(CollectionOptions, ErrorBadMax) { - ASSERT_NOT_OK(CollectionOptions().parse(BSON("capped" << true << "max" << (1LL << 31)))); + ASSERT_NOT_OK( + CollectionOptions::parse(BSON("capped" << true << "max" << (1LL << 31))).getStatus()); } TEST(CollectionOptions, CappedSizeRoundsUpForAlignment) { const long long kUnalignedCappedSize = 1000; const long long kAlignedCappedSize = 1024; - CollectionOptions options; - // Check size rounds up to multiple of alignment. - ASSERT_OK(options.parse(BSON("capped" << true << "size" << kUnalignedCappedSize))); + auto options = assertGet( + CollectionOptions::parse((BSON("capped" << true << "size" << kUnalignedCappedSize)))); + ASSERT_EQUALS(options.capped, true); ASSERT_EQUALS(options.cappedSize, kAlignedCappedSize); ASSERT_EQUALS(options.cappedMaxDocs, 0); } TEST(CollectionOptions, IgnoreSizeWrongType) { - CollectionOptions options; - ASSERT_OK(options.parse(fromjson("{size: undefined, capped: undefined}"))); + auto options = + assertGet(CollectionOptions::parse(fromjson("{size: undefined, capped: undefined}"))); ASSERT_EQUALS(options.capped, false); ASSERT_EQUALS(options.cappedSize, 0); } TEST(CollectionOptions, IgnoreMaxWrongType) { - CollectionOptions options; - ASSERT_OK(options.parse(fromjson("{capped: true, size: 1024, max: ''}"))); + auto options = + assertGet(CollectionOptions::parse(fromjson("{capped: true, size: 1024, max: ''}"))); ASSERT_EQUALS(options.capped, true); ASSERT_EQUALS(options.cappedSize, 1024); ASSERT_EQUALS(options.cappedMaxDocs, 0); @@ -124,18 +127,18 @@ TEST(CollectionOptions, IgnoreMaxWrongType) { TEST(CollectionOptions, InvalidStorageEngineField) { // "storageEngine" field has to be an object if present. - ASSERT_NOT_OK(CollectionOptions().parse(fromjson("{storageEngine: 1}"))); + ASSERT_NOT_OK(CollectionOptions::parse(fromjson("{storageEngine: 1}")).getStatus()); // Every field under "storageEngine" has to be an object. - ASSERT_NOT_OK(CollectionOptions().parse(fromjson("{storageEngine: {storageEngine1: 1}}"))); + ASSERT_NOT_OK( + CollectionOptions::parse(fromjson("{storageEngine: {storageEngine1: 1}}")).getStatus()); // Empty "storageEngine" not allowed - ASSERT_OK(CollectionOptions().parse(fromjson("{storageEngine: {}}"))); + ASSERT_OK(CollectionOptions::parse(fromjson("{storageEngine: {}}")).getStatus()); } TEST(CollectionOptions, ParseEngineField) { - CollectionOptions opts; - ASSERT_OK(opts.parse( + auto opts = assertGet(CollectionOptions::parse( fromjson("{storageEngine: {storageEngine1: {x: 1, y: 2}, storageEngine2: {a: 1, b:2}}}"))); checkRoundTrip(opts); @@ -159,8 +162,9 @@ TEST(CollectionOptions, ParseEngineField) { } TEST(CollectionOptions, ResetStorageEngineField) { - CollectionOptions opts; - ASSERT_OK(opts.parse(fromjson("{storageEngine: {storageEngine1: {x: 1}}}"))); + + auto opts = + assertGet(CollectionOptions::parse(fromjson("{storageEngine: {storageEngine1: {x: 1}}}"))); checkRoundTrip(opts); CollectionOptions defaultOpts; @@ -189,25 +193,21 @@ TEST(CollectionOptions, ModifyStorageEngineField) { } TEST(CollectionOptions, FailToParseCollationThatIsNotAnObject) { - CollectionOptions options; - ASSERT_NOT_OK(options.parse(fromjson("{collation: 'notAnObject'}"))); + ASSERT_NOT_OK(CollectionOptions::parse(fromjson("{collation: 'notAnObject'}")).getStatus()); } TEST(CollectionOptions, FailToParseCollationThatIsAnEmptyObject) { - CollectionOptions options; - ASSERT_NOT_OK(options.parse(fromjson("{collation: {}}"))); + ASSERT_NOT_OK(CollectionOptions::parse(fromjson("{collation: {}}")).getStatus()); } TEST(CollectionOptions, CollationFieldParsesCorrectly) { - CollectionOptions options; - ASSERT_OK(options.parse(fromjson("{collation: {locale: 'en'}}"))); + auto options = assertGet(CollectionOptions::parse(fromjson("{collation: {locale: 'en'}}"))); ASSERT_BSONOBJ_EQ(options.collation, fromjson("{locale: 'en'}")); ASSERT_OK(options.validateForStorage()); } TEST(CollectionOptions, ParsedCollationObjShouldBeOwned) { - CollectionOptions options; - ASSERT_OK(options.parse(fromjson("{collation: {locale: 'en'}}"))); + auto options = assertGet(CollectionOptions::parse(fromjson("{collation: {locale: 'en'}}"))); ASSERT_BSONOBJ_EQ(options.collation, fromjson("{locale: 'en'}")); ASSERT_TRUE(options.collation.isOwned()); } @@ -215,104 +215,87 @@ TEST(CollectionOptions, ParsedCollationObjShouldBeOwned) { TEST(CollectionOptions, ResetClearsCollationField) { CollectionOptions options; ASSERT_TRUE(options.collation.isEmpty()); - ASSERT_OK(options.parse(fromjson("{collation: {locale: 'en'}}"))); + options = assertGet(CollectionOptions::parse(fromjson("{collation: {locale: 'en'}}"))); ASSERT_FALSE(options.collation.isEmpty()); } TEST(CollectionOptions, CollationFieldLeftEmptyWhenOmitted) { - CollectionOptions options; - ASSERT_OK(options.parse(fromjson("{validator: {a: 1}}"))); + auto options = assertGet(CollectionOptions::parse(fromjson("{validator: {a: 1}}"))); ASSERT_TRUE(options.collation.isEmpty()); } TEST(CollectionOptions, CollationFieldNotDumpedToBSONWhenOmitted) { - CollectionOptions options; - ASSERT_OK(options.parse(fromjson("{validator: {a: 1}}"))); + auto options = assertGet(CollectionOptions::parse(fromjson("{validator: {a: 1}}"))); ASSERT_TRUE(options.collation.isEmpty()); BSONObj asBSON = options.toBSON(); ASSERT_FALSE(asBSON["collation"]); } TEST(CollectionOptions, ViewParsesCorrectly) { - CollectionOptions options; - ASSERT_OK(options.parse(fromjson("{viewOn: 'c', pipeline: [{$match: {}}]}"))); + auto options = + assertGet(CollectionOptions::parse(fromjson("{viewOn: 'c', pipeline: [{$match: {}}]}"))); ASSERT_EQ(options.viewOn, "c"); ASSERT_BSONOBJ_EQ(options.pipeline, fromjson("[{$match: {}}]")); } TEST(CollectionOptions, ViewParsesCorrectlyWithoutPipeline) { - CollectionOptions options; - ASSERT_OK(options.parse(fromjson("{viewOn: 'c'}"))); + auto options = assertGet(CollectionOptions::parse(fromjson("{viewOn: 'c'}"))); ASSERT_EQ(options.viewOn, "c"); ASSERT_BSONOBJ_EQ(options.pipeline, BSONObj()); } TEST(CollectionOptions, PipelineFieldRequiresViewOn) { - CollectionOptions options; - ASSERT_NOT_OK(options.parse(fromjson("{pipeline: [{$match: {}}]}"))); + ASSERT_NOT_OK(CollectionOptions::parse(fromjson("{pipeline: [{$match: {}}]}")).getStatus()); } TEST(CollectionOptions, UnknownTopLevelOptionFailsToParse) { - CollectionOptions options; - auto status = options.parse(fromjson("{invalidOption: 1}")); - ASSERT_NOT_OK(status); - ASSERT_EQ(status.code(), ErrorCodes::InvalidOptions); + auto statusWith = CollectionOptions::parse(fromjson("{invalidOption: 1}")); + ASSERT_EQ(statusWith.getStatus().code(), ErrorCodes::InvalidOptions); } TEST(CollectionOptions, CreateOptionIgnoredIfFirst) { - CollectionOptions options; - auto status = options.parse(fromjson("{create: 1}")); - ASSERT_OK(status); + ASSERT_OK(CollectionOptions::parse(fromjson("{create: 1}")).getStatus()); } TEST(CollectionOptions, CreateOptionIgnoredIfNotFirst) { - CollectionOptions options; - auto status = options.parse(fromjson("{capped: true, create: 1, size: 1024}")); - ASSERT_OK(status); + auto options = + assertGet(CollectionOptions::parse(fromjson("{capped: true, create: 1, size: 1024}"))); ASSERT_EQ(options.capped, true); ASSERT_EQ(options.cappedSize, 1024L); } TEST(CollectionOptions, UnknownOptionIgnoredIfCreateOptionFirst) { - CollectionOptions options; - ASSERT_OK(options.parse(fromjson("{create: 1, invalidOption: 1}"))); + ASSERT_OK(CollectionOptions::parse(fromjson("{create: 1, invalidOption: 1}")).getStatus()); } TEST(CollectionOptions, UnknownOptionIgnoredIfCreateOptionPresent) { - CollectionOptions options; - ASSERT_OK(options.parse(fromjson("{invalidOption: 1, create: 1}"))); + ASSERT_OK(CollectionOptions::parse(fromjson("{invalidOption: 1, create: 1}")).getStatus()); } TEST(CollectionOptions, UnknownOptionRejectedIfCreateOptionNotPresent) { - CollectionOptions options; - auto status = options.parse(fromjson("{invalidOption: 1}")); - ASSERT_NOT_OK(status); - ASSERT_EQ(status.code(), ErrorCodes::InvalidOptions); + auto statusWith = CollectionOptions::parse(fromjson("{invalidOption: 1}")); + ASSERT_EQ(statusWith.getStatus().code(), ErrorCodes::InvalidOptions); } TEST(CollectionOptions, DuplicateCreateOptionIgnoredIfCreateOptionFirst) { - CollectionOptions options; - auto status = options.parse(BSON("create" << 1 << "create" << 1)); - ASSERT_OK(status); + auto statusWith = CollectionOptions::parse(BSON("create" << 1 << "create" << 1)); + ASSERT_OK(statusWith.getStatus()); } TEST(CollectionOptions, DuplicateCreateOptionIgnoredIfCreateOptionNotFirst) { - CollectionOptions options; - auto status = - options.parse(BSON("capped" << true << "create" << 1 << "create" << 1 << "size" << 1024)); - ASSERT_OK(status); + auto statusWith = CollectionOptions::parse( + BSON("capped" << true << "create" << 1 << "create" << 1 << "size" << 1024)); + ASSERT_OK(statusWith.getStatus()); } TEST(CollectionOptions, MaxTimeMSWhitelistedOptionIgnored) { - CollectionOptions options; - auto status = options.parse(fromjson("{maxTimeMS: 1}")); - ASSERT_OK(status); + auto statusWith = CollectionOptions::parse(fromjson("{maxTimeMS: 1}")); + ASSERT_OK(statusWith.getStatus()); } TEST(CollectionOptions, WriteConcernWhitelistedOptionIgnored) { - CollectionOptions options; - auto status = options.parse(fromjson("{writeConcern: 1}")); - ASSERT_OK(status); + auto statusWith = CollectionOptions::parse(fromjson("{writeConcern: 1}")); + ASSERT_OK(statusWith.getStatus()); } TEST(CollectionOptions, ParseUUID) { @@ -321,13 +304,14 @@ TEST(CollectionOptions, ParseUUID) { // 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); + ASSERT_NOT_OK(CollectionOptions::parse(uuid.toBSON()).getStatus()); + ASSERT_NOT_OK(CollectionOptions::parse(BSON("uuid" << 1)).getStatus()); + ASSERT_NOT_OK(CollectionOptions::parse(BSON("uuid" << 1), CollectionOptions::parseForStorage) + .getStatus()); // Check successful parse and roundtrip. - ASSERT_OK(options.parse(uuid.toBSON(), CollectionOptions::parseForStorage)); + options = + assertGet(CollectionOptions::parse(uuid.toBSON(), CollectionOptions::parseForStorage)); ASSERT(options.uuid.get() == uuid); // Check that a collection options containing a UUID passes validation. @@ -335,49 +319,49 @@ TEST(CollectionOptions, ParseUUID) { } TEST(CollectionOptions, SizeNumberLimits) { - CollectionOptions options; - ASSERT_OK(options.parse(fromjson("{size: 'a'}"))); + CollectionOptions options = assertGet(CollectionOptions::parse(fromjson("{size: 'a'}"))); ASSERT_EQ(options.cappedSize, 0); - - ASSERT_OK(options.parse(fromjson("{size: '-1'}"))); + options = assertGet(CollectionOptions::parse(fromjson("{size: '-1'}"))); ASSERT_EQ(options.cappedSize, 0); - ASSERT_OK(options.parse(fromjson("{size: '-9999999999999999999999999999999'}"))); + options = + assertGet(CollectionOptions::parse(fromjson("{size: '-9999999999999999999999999999999'}"))); ASSERT_EQ(options.cappedSize, 0); // The test for size is redundant since size returns a status that's not ok if it's larger // than a petabyte, which is smaller than LLONG_MAX anyways. We test that here. - ASSERT_NOT_OK(options.parse(fromjson("{size: 9999999999999999}"))); + ASSERT_NOT_OK(CollectionOptions::parse(fromjson("{size: 9999999999999999}")).getStatus()); } TEST(CollectionOptions, MaxNumberLimits) { - CollectionOptions options; - ASSERT_OK(options.parse(fromjson("{max: 'a'}"))); + CollectionOptions options = assertGet(CollectionOptions::parse(fromjson("{max: 'a'}"))); ASSERT_EQ(options.cappedMaxDocs, 0); - ASSERT_OK(options.parse(fromjson("{max: '-1'}"))); + options = assertGet(CollectionOptions::parse(fromjson("{max: '-1'}"))); ASSERT_EQ(options.cappedMaxDocs, 0); - ASSERT_OK(options.parse(fromjson("{max: '-9999999999999999999999999999999999'}"))); + options = assertGet( + CollectionOptions::parse(fromjson("{max: '-9999999999999999999999999999999999'}"))); ASSERT_EQ(options.cappedMaxDocs, 0); - ASSERT_OK(options.parse(fromjson("{max: 99999999999999999999999999999}"))); + options = assertGet(CollectionOptions::parse(fromjson("{max: 99999999999999999999999999999}"))); ASSERT_EQ(options.cappedMaxDocs, 0); } TEST(CollectionOptions, NExtentsNumberLimits) { - CollectionOptions options; - ASSERT_OK(options.parse(fromjson("{$nExtents: 'a'}"))); + CollectionOptions options = assertGet(CollectionOptions::parse(fromjson("{$nExtents: 'a'}"))); ASSERT_EQ(options.initialNumExtents, 0); - ASSERT_OK(options.parse(fromjson("{$nExtents: '-1'}"))); + options = assertGet(CollectionOptions::parse(fromjson("{$nExtents: '-1'}"))); ASSERT_EQ(options.initialNumExtents, 0); - ASSERT_OK(options.parse(fromjson("{$nExtents: '-9999999999999999999999999999999999'}"))); + options = assertGet( + CollectionOptions::parse(fromjson("{$nExtents: '-9999999999999999999999999999999999'}"))); ASSERT_EQ(options.initialNumExtents, 0); - ASSERT_OK(options.parse(fromjson("{$nExtents: 9999999999999999999999999999999}"))); + options = assertGet( + CollectionOptions::parse(fromjson("{$nExtents: 9999999999999999999999999999999}"))); ASSERT_EQ(options.initialNumExtents, LLONG_MAX); } } // namespace mongo diff --git a/src/mongo/db/catalog/collection_test.cpp b/src/mongo/db/catalog/collection_test.cpp index 0265f2408fb..e55f77b3f7b 100644 --- a/src/mongo/db/catalog/collection_test.cpp +++ b/src/mongo/db/catalog/collection_test.cpp @@ -60,8 +60,9 @@ void CollectionTest::makeCapped(NamespaceString nss, long long cappedSize) { } void CollectionTest::makeUncapped(NamespaceString nss) { - CollectionOptions options; - ASSERT_OK(storageInterface()->createCollection(operationContext(), nss, options)); + CollectionOptions defaultCollectionOptions; + ASSERT_OK( + storageInterface()->createCollection(operationContext(), nss, defaultCollectionOptions)); } // Call validate with different validation levels and verify the results. diff --git a/src/mongo/db/catalog/create_collection.cpp b/src/mongo/db/catalog/create_collection.cpp index 1d15a592b1d..5813440c265 100644 --- a/src/mongo/db/catalog/create_collection.cpp +++ b/src/mongo/db/catalog/create_collection.cpp @@ -166,10 +166,11 @@ Status createCollection(OperationContext* opCtx, CollectionOptions collectionOptions; { - Status status = collectionOptions.parse(options, kind); - if (!status.isOK()) { - return status; + StatusWith<CollectionOptions> statusWith = CollectionOptions::parse(options, kind); + if (!statusWith.isOK()) { + return statusWith.getStatus(); } + collectionOptions = statusWith.getValue(); } if (collectionOptions.isView()) { diff --git a/src/mongo/db/cloner.cpp b/src/mongo/db/cloner.cpp index cf2c445a66b..803bc7108df 100644 --- a/src/mongo/db/cloner.cpp +++ b/src/mongo/db/cloner.cpp @@ -156,8 +156,7 @@ struct Cloner::Fun { WriteUnitOfWork wunit(opCtx); const bool createDefaultIndexes = true; - CollectionOptions collectionOptions; - uassertStatusOK(collectionOptions.parse( + CollectionOptions collectionOptions = uassertStatusOK(CollectionOptions::parse( from_options, CollectionOptions::ParseKind::parseForCommand)); auto indexSpec = fixIndexSpec(to_collection.db().toString(), from_id_index); invariant( @@ -372,9 +371,8 @@ void Cloner::copyIndexes(OperationContext* opCtx, opCtx->checkForInterrupt(); WriteUnitOfWork wunit(opCtx); - CollectionOptions collectionOptions; - uassertStatusOK( - collectionOptions.parse(from_opts, CollectionOptions::ParseKind::parseForCommand)); + CollectionOptions collectionOptions = uassertStatusOK( + CollectionOptions::parse(from_opts, CollectionOptions::ParseKind::parseForCommand)); const bool createDefaultIndexes = true; invariant(db->userCreateNS(opCtx, to_collection, @@ -492,8 +490,8 @@ bool Cloner::copyCollection(OperationContext* opCtx, opCtx->checkForInterrupt(); WriteUnitOfWork wunit(opCtx); - CollectionOptions collectionOptions; - uassertStatusOK(collectionOptions.parse(options, optionsParser)); + CollectionOptions collectionOptions = + uassertStatusOK(CollectionOptions::parse(options, optionsParser)); const bool createDefaultIndexes = true; Status status = db->userCreateNS(opCtx, nss, collectionOptions, createDefaultIndexes, idIndexSpec); @@ -539,10 +537,10 @@ StatusWith<std::vector<BSONObj>> Cloner::filterCollectionsForClone( BSONElement collectionOptions = collection["options"]; if (collectionOptions.isABSONObj()) { - auto parseOptionsStatus = CollectionOptions().parse( + auto statusWithCollectionOptions = CollectionOptions::parse( collectionOptions.Obj(), CollectionOptions::ParseKind::parseForCommand); - if (!parseOptionsStatus.isOK()) { - return parseOptionsStatus; + if (!statusWithCollectionOptions.isOK()) { + return statusWithCollectionOptions.getStatus(); } } @@ -648,8 +646,7 @@ Status Cloner::createCollectionsForDb( const bool createDefaultIndexes = true; auto options = optionsBuilder.obj(); - CollectionOptions collectionOptions; - uassertStatusOK(collectionOptions.parse( + CollectionOptions collectionOptions = uassertStatusOK(CollectionOptions::parse( options, CollectionOptions::ParseKind::parseForStorage)); auto indexSpec = fixIndexSpec(nss.db().toString(), params.idIndexSpec); Status createStatus = db->userCreateNS( diff --git a/src/mongo/db/commands/find_and_modify.cpp b/src/mongo/db/commands/find_and_modify.cpp index 3f6d65e5426..b7c20854f59 100644 --- a/src/mongo/db/commands/find_and_modify.cpp +++ b/src/mongo/db/commands/find_and_modify.cpp @@ -463,11 +463,10 @@ public: if (!collection) { uassertStatusOK(userAllowedCreateNS(nsString.db(), nsString.coll())); WriteUnitOfWork wuow(opCtx); - CollectionOptions collectionOptions; - uassertStatusOK(collectionOptions.parse( - BSONObj(), CollectionOptions::ParseKind::parseForCommand)); + CollectionOptions defaultCollectionOptions; auto db = autoDb->getDb(); - uassertStatusOK(db->userCreateNS(opCtx, nsString, collectionOptions)); + uassertStatusOK( + db->userCreateNS(opCtx, nsString, defaultCollectionOptions)); wuow.commit(); collection = autoDb->getDb()->getCollection(opCtx, nsString); diff --git a/src/mongo/db/db.cpp b/src/mongo/db/db.cpp index 4245498a958..03f6cbd0b69 100644 --- a/src/mongo/db/db.cpp +++ b/src/mongo/db/db.cpp @@ -234,9 +234,8 @@ void logStartup(OperationContext* opCtx) { if (!collection) { BSONObj options = BSON("capped" << true << "size" << 10 * 1024 * 1024); repl::UnreplicatedWritesBlock uwb(opCtx); - CollectionOptions collectionOptions; - uassertStatusOK( - collectionOptions.parse(options, CollectionOptions::ParseKind::parseForCommand)); + CollectionOptions collectionOptions = uassertStatusOK( + CollectionOptions::parse(options, CollectionOptions::ParseKind::parseForCommand)); uassertStatusOK(db->userCreateNS(opCtx, startupLogCollectionName, collectionOptions)); collection = db->getCollection(opCtx, startupLogCollectionName); } diff --git a/src/mongo/db/index_build_entry_helpers.cpp b/src/mongo/db/index_build_entry_helpers.cpp index 5d5048945a1..8ecbc4544c5 100644 --- a/src/mongo/db/index_build_entry_helpers.cpp +++ b/src/mongo/db/index_build_entry_helpers.cpp @@ -106,9 +106,9 @@ void ensureIndexBuildEntriesNamespaceExists(OperationContext* opCtx) { // Create the collection if it doesn't exist. if (!db->getCollection(opCtx, NamespaceString::kIndexBuildEntryNamespace)) { WriteUnitOfWork wuow(opCtx); - CollectionOptions options; + CollectionOptions defaultCollectionOptions; Collection* collection = db->createCollection( - opCtx, NamespaceString::kIndexBuildEntryNamespace, options); + opCtx, NamespaceString::kIndexBuildEntryNamespace, defaultCollectionOptions); // Ensure the collection exists. invariant(collection); diff --git a/src/mongo/db/ops/write_ops_exec.cpp b/src/mongo/db/ops/write_ops_exec.cpp index 3bc99ed80d4..ba1d5d967ab 100644 --- a/src/mongo/db/ops/write_ops_exec.cpp +++ b/src/mongo/db/ops/write_ops_exec.cpp @@ -215,10 +215,8 @@ void makeCollection(OperationContext* opCtx, const NamespaceString& ns) { if (!db.getDb()->getCollection(opCtx, ns)) { // someone else may have beat us to it. uassertStatusOK(userAllowedCreateNS(ns.db(), ns.coll())); WriteUnitOfWork wuow(opCtx); - CollectionOptions collectionOptions; - uassertStatusOK( - collectionOptions.parse(BSONObj(), CollectionOptions::ParseKind::parseForCommand)); - uassertStatusOK(db.getDb()->userCreateNS(opCtx, ns, collectionOptions)); + CollectionOptions defaultCollectionOptions; + uassertStatusOK(db.getDb()->userCreateNS(opCtx, ns, defaultCollectionOptions)); wuow.commit(); } }); diff --git a/src/mongo/db/repl/database_cloner.cpp b/src/mongo/db/repl/database_cloner.cpp index ebcef5ec93d..5991744ccec 100644 --- a/src/mongo/db/repl/database_cloner.cpp +++ b/src/mongo/db/repl/database_cloner.cpp @@ -359,11 +359,13 @@ void DatabaseCloner::_listCollectionsCallback(const StatusWith<Fetcher::QueryRes } const BSONObj optionsObj = optionsElement.Obj(); CollectionOptions options; - auto parseStatus = options.parse(optionsObj, CollectionOptions::parseForStorage); - if (!parseStatus.isOK()) { - _finishCallback_inlock(lk, parseStatus); + auto statusWithCollectionOptions = + CollectionOptions::parse(optionsObj, CollectionOptions::parseForStorage); + if (!statusWithCollectionOptions.isOK()) { + _finishCallback_inlock(lk, statusWithCollectionOptions.getStatus()); return; } + options = statusWithCollectionOptions.getValue(); BSONElement infoElement = info.getField(kInfoFieldName); if (infoElement.isABSONObj()) { diff --git a/src/mongo/db/repl/rs_rollback.cpp b/src/mongo/db/repl/rs_rollback.cpp index c96cc26598d..6f7bd28bedf 100644 --- a/src/mongo/db/repl/rs_rollback.cpp +++ b/src/mongo/db/repl/rs_rollback.cpp @@ -1253,20 +1253,14 @@ void rollback_internal::syncFixUp(OperationContext* opCtx, << typeName(optionsField.type())); } - // Removes the option.uuid field. We do not allow the options.uuid field - // to be parsed while in parseForCommand mode in order to ensure that the - // user cannot set the UUID of a collection. - BSONObj optionsFieldObj = optionsField.Obj(); - optionsFieldObj = optionsFieldObj.removeField("uuid"); - - auto status = options.parse(optionsFieldObj, CollectionOptions::parseForCommand); - if (!status.isOK()) { - throw RSFatalException(str::stream() << "Failed to parse options " << info - << ": " - << status.toString()); + auto statusWithCollectionOptions = CollectionOptions::parse( + optionsField.Obj(), CollectionOptions::parseForCommand); + if (!statusWithCollectionOptions.isOK()) { + throw RSFatalException( + str::stream() << "Failed to parse options " << info << ": " + << statusWithCollectionOptions.getStatus().toString()); } - // TODO(SERVER-27992): Set options.uuid. - + options = statusWithCollectionOptions.getValue(); } else { // Use default options. } diff --git a/src/mongo/db/s/config/configsvr_create_collection_command.cpp b/src/mongo/db/s/config/configsvr_create_collection_command.cpp index 1d32b2bc779..2ac40d3bc23 100644 --- a/src/mongo/db/s/config/configsvr_create_collection_command.cpp +++ b/src/mongo/db/s/config/configsvr_create_collection_command.cpp @@ -77,7 +77,7 @@ public: CollectionOptions options; if (auto requestOptions = request().getOptions()) { - uassertStatusOK(options.parse(*requestOptions)); + options = uassertStatusOK(CollectionOptions::parse(*requestOptions)); } auto const catalogClient = Grid::get(opCtx)->catalogClient(); diff --git a/src/mongo/db/s/config/sharding_catalog_manager_collection_operations.cpp b/src/mongo/db/s/config/sharding_catalog_manager_collection_operations.cpp index 66825b32c47..c205ce2841a 100644 --- a/src/mongo/db/s/config/sharding_catalog_manager_collection_operations.cpp +++ b/src/mongo/db/s/config/sharding_catalog_manager_collection_operations.cpp @@ -108,9 +108,8 @@ boost::optional<UUID> checkCollectionOptions(OperationContext* opCtx, collIter.more()); auto collectionDetails = collIter.next(); - CollectionOptions actualOptions; - - uassertStatusOK(actualOptions.parse(collectionDetails["options"].Obj())); + CollectionOptions actualOptions = + uassertStatusOK(CollectionOptions::parse(collectionDetails["options"].Obj())); // TODO: SERVER-33048 check idIndex field uassert(ErrorCodes::NamespaceExists, diff --git a/src/mongo/db/s/migration_destination_manager.cpp b/src/mongo/db/s/migration_destination_manager.cpp index ce8fb419f08..b5c1a8f6cb5 100644 --- a/src/mongo/db/s/migration_destination_manager.cpp +++ b/src/mongo/db/s/migration_destination_manager.cpp @@ -657,9 +657,8 @@ void MigrationDestinationManager::cloneCollectionIndexesAndOptions(OperationCont // We do not have a collection by this name. Create the collection with the donor's // options. WriteUnitOfWork wuow(opCtx); - CollectionOptions collectionOptions; - uassertStatusOK(collectionOptions.parse(donorOptions, - CollectionOptions::ParseKind::parseForStorage)); + CollectionOptions collectionOptions = uassertStatusOK(CollectionOptions::parse( + donorOptions, CollectionOptions::ParseKind::parseForStorage)); const bool createDefaultIndexes = true; uassertStatusOK(db->userCreateNS( opCtx, nss, collectionOptions, createDefaultIndexes, donorIdIndexSpec)); diff --git a/src/mongo/db/storage/bson_collection_catalog_entry.cpp b/src/mongo/db/storage/bson_collection_catalog_entry.cpp index 8b74fed91b4..d691779de5e 100644 --- a/src/mongo/db/storage/bson_collection_catalog_entry.cpp +++ b/src/mongo/db/storage/bson_collection_catalog_entry.cpp @@ -216,8 +216,8 @@ void BSONCollectionCatalogEntry::MetaData::parse(const BSONObj& obj) { ns = obj["ns"].valuestrsafe(); if (obj["options"].isABSONObj()) { - options.parse(obj["options"].Obj(), CollectionOptions::parseForStorage) - .transitional_ignore(); + options = uassertStatusOK( + CollectionOptions::parse(obj["options"].Obj(), CollectionOptions::parseForStorage)); } BSONElement indexList = obj["indexes"]; diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_kv_engine_test.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_kv_engine_test.cpp index 388c4c577bc..8a1ca32e483 100644 --- a/src/mongo/db/storage/wiredtiger/wiredtiger_kv_engine_test.cpp +++ b/src/mongo/db/storage/wiredtiger/wiredtiger_kv_engine_test.cpp @@ -123,11 +123,12 @@ TEST_F(WiredTigerKVEngineRepairTest, OrphanedDataFilesCanBeRecovered) { NamespaceString nss("a.b"); std::string ident = "collection-1234"; std::string record = "abcd"; - CollectionOptions options; + CollectionOptions defaultCollectionOptions; std::unique_ptr<RecordStore> rs; - ASSERT_OK(_engine->createRecordStore(opCtxPtr.get(), nss.ns(), ident, options)); - rs = _engine->getRecordStore(opCtxPtr.get(), nss.ns(), ident, options); + ASSERT_OK( + _engine->createRecordStore(opCtxPtr.get(), nss.ns(), ident, defaultCollectionOptions)); + rs = _engine->getRecordStore(opCtxPtr.get(), nss.ns(), ident, defaultCollectionOptions); ASSERT(rs); RecordId loc; @@ -150,7 +151,8 @@ TEST_F(WiredTigerKVEngineRepairTest, OrphanedDataFilesCanBeRecovered) { ASSERT(!boost::filesystem::exists(tmpFile)); #ifdef _WIN32 - auto status = _engine->recoverOrphanedIdent(opCtxPtr.get(), nss, ident, options); + auto status = + _engine->recoverOrphanedIdent(opCtxPtr.get(), nss, ident, defaultCollectionOptions); ASSERT_EQ(ErrorCodes::CommandNotSupported, status.code()); #else // Move the data file out of the way so the ident can be dropped. This not permitted on Windows @@ -167,7 +169,8 @@ TEST_F(WiredTigerKVEngineRepairTest, OrphanedDataFilesCanBeRecovered) { boost::filesystem::rename(tmpFile, *dataFilePath, err); ASSERT(!err) << err.message(); - auto status = _engine->recoverOrphanedIdent(opCtxPtr.get(), nss, ident, options); + auto status = + _engine->recoverOrphanedIdent(opCtxPtr.get(), nss, ident, defaultCollectionOptions); ASSERT_EQ(ErrorCodes::DataModifiedByRepair, status.code()); #endif } @@ -178,11 +181,12 @@ TEST_F(WiredTigerKVEngineRepairTest, UnrecoverableOrphanedDataFilesAreRebuilt) { NamespaceString nss("a.b"); std::string ident = "collection-1234"; std::string record = "abcd"; - CollectionOptions options; + CollectionOptions defaultCollectionOptions; std::unique_ptr<RecordStore> rs; - ASSERT_OK(_engine->createRecordStore(opCtxPtr.get(), nss.ns(), ident, options)); - rs = _engine->getRecordStore(opCtxPtr.get(), nss.ns(), ident, options); + ASSERT_OK( + _engine->createRecordStore(opCtxPtr.get(), nss.ns(), ident, defaultCollectionOptions)); + rs = _engine->getRecordStore(opCtxPtr.get(), nss.ns(), ident, defaultCollectionOptions); ASSERT(rs); RecordId loc; @@ -204,7 +208,8 @@ TEST_F(WiredTigerKVEngineRepairTest, UnrecoverableOrphanedDataFilesAreRebuilt) { ASSERT_OK(_engine->dropIdent(opCtxPtr.get(), ident)); #ifdef _WIN32 - auto status = _engine->recoverOrphanedIdent(opCtxPtr.get(), nss, ident, options); + auto status = + _engine->recoverOrphanedIdent(opCtxPtr.get(), nss, ident, defaultCollectionOptions); ASSERT_EQ(ErrorCodes::CommandNotSupported, status.code()); #else // The ident may not get immediately dropped, so ensure it is completely gone. @@ -222,13 +227,14 @@ TEST_F(WiredTigerKVEngineRepairTest, UnrecoverableOrphanedDataFilesAreRebuilt) { // This should recreate an empty data file successfully and move the old one to a name that ends // in ".corrupt". - auto status = _engine->recoverOrphanedIdent(opCtxPtr.get(), nss, ident, options); + auto status = + _engine->recoverOrphanedIdent(opCtxPtr.get(), nss, ident, defaultCollectionOptions); ASSERT_EQ(ErrorCodes::DataModifiedByRepair, status.code()) << status.reason(); boost::filesystem::path corruptFile = (dataFilePath->string() + ".corrupt"); ASSERT(boost::filesystem::exists(corruptFile)); - rs = _engine->getRecordStore(opCtxPtr.get(), nss.ns(), ident, options); + rs = _engine->getRecordStore(opCtxPtr.get(), nss.ns(), ident, defaultCollectionOptions); RecordData data; ASSERT_FALSE(rs->findRecord(opCtxPtr.get(), loc, &data)); #endif |