TEST(CollectionOptions, NExtentsNumberLimits) {
    CollectionOptions options;
    ASSERT_OK(options.parse(fromjson("{$nExtents: 'a'}")));
    ASSERT_EQ(options.initialNumExtents, 0);

    ASSERT_OK(options.parse(fromjson("{$nExtents: '-1'}")));
    ASSERT_EQ(options.initialNumExtents, 0);

    ASSERT_OK(options.parse(fromjson("{$nExtents: '-9999999999999999999999999999999999'}")));
    ASSERT_EQ(options.initialNumExtents, 0);

    ASSERT_OK(options.parse(fromjson("{$nExtents: 9999999999999999999999999999999}")));
    ASSERT_EQ(options.initialNumExtents, LLONG_MAX);
}
TEST(CollectionOptions, MaxNumberLimits) {
    CollectionOptions options;
    ASSERT_OK(options.parse(fromjson("{max: 'a'}")));
    ASSERT_EQ(options.cappedMaxDocs, 0);

    ASSERT_OK(options.parse(fromjson("{max: '-1'}")));
    ASSERT_EQ(options.cappedMaxDocs, 0);

    ASSERT_OK(options.parse(fromjson("{max: '-9999999999999999999999999999999999'}")));
    ASSERT_EQ(options.cappedMaxDocs, 0);

    ASSERT_OK(options.parse(fromjson("{max: 99999999999999999999999999999}")));
    ASSERT_EQ(options.cappedMaxDocs, 0);
}
TEST(CollectionOptions, SizeNumberLimits) {
    CollectionOptions options;
    ASSERT_OK(options.parse(fromjson("{size: 'a'}")));
    ASSERT_EQ(options.cappedSize, 0);


    ASSERT_OK(options.parse(fromjson("{size: '-1'}")));
    ASSERT_EQ(options.cappedSize, 0);

    ASSERT_OK(options.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}")));
}
Example #4
0
    /** { ..., capped: true, size: ..., max: ... }
     * @param createDefaultIndexes - if false, defers id (and other) index creation.
     * @return true if successful
    */
    Status userCreateNS( OperationContext* txn,
                         Database* db,
                         StringData ns,
                         BSONObj options,
                         bool logForReplication,
                         bool createDefaultIndexes ) {

        invariant( db );

        LOG(1) << "create collection " << ns << ' ' << options;

        if ( !NamespaceString::validCollectionComponent(ns) )
            return Status( ErrorCodes::InvalidNamespace,
                           str::stream() << "invalid ns: " << ns );

        Collection* collection = db->getCollection( ns );

        if ( collection )
            return Status( ErrorCodes::NamespaceExists,
                           "collection already exists" );

        CollectionOptions collectionOptions;
        Status status = collectionOptions.parse(options);
        if ( !status.isOK() )
            return status;

        status = validateStorageOptions(collectionOptions.storageEngine,
                                        &StorageEngine::Factory::validateCollectionStorageOptions);
        if ( !status.isOK() )
            return status;

        invariant( db->createCollection( txn, ns, collectionOptions, true, createDefaultIndexes ) );

        if ( logForReplication ) {
            if ( options.getField( "create" ).eoo() ) {
                BSONObjBuilder b;
                b << "create" << nsToCollectionSubstring( ns );
                b.appendElements( options );
                options = b.obj();
            }
            string logNs = nsToDatabase(ns) + ".$cmd";
            repl::logOp(txn, "c", logNs.c_str(), options);
        }

        return Status::OK();
    }
Status MMAPV1DatabaseCatalogEntry::createCollection(OperationContext* txn,
                                                    StringData ns,
                                                    const CollectionOptions& options,
                                                    bool allocateDefaultSpace) {
    if (_namespaceIndex.details(ns)) {
        return Status(ErrorCodes::NamespaceExists,
                      str::stream() << "namespace already exists: " << ns);
    }

    BSONObj optionsAsBSON = options.toBSON();
    RecordId rid = _addNamespaceToNamespaceCollection(txn, ns, &optionsAsBSON);

    _namespaceIndex.add_ns(txn, ns, DiskLoc(), options.capped);
    NamespaceDetails* details = _namespaceIndex.details(ns);

    // Set the flags.
    NamespaceDetailsRSV1MetaData(ns, details).replaceUserFlags(txn, options.flags);

    if (options.capped && options.cappedMaxDocs > 0) {
        txn->recoveryUnit()->writingInt(details->maxDocsInCapped) = options.cappedMaxDocs;
    }

    Entry*& entry = _collections[ns.toString()];
    invariant(!entry);
    txn->recoveryUnit()->registerChange(new EntryInsertion(ns, this));
    entry = new Entry();
    _insertInCache(txn, ns, rid, entry);

    if (allocateDefaultSpace) {
        RecordStoreV1Base* rs = _getRecordStore(ns);
        if (options.initialNumExtents > 0) {
            int size = _massageExtentSize(&_extentManager, options.cappedSize);
            for (int i = 0; i < options.initialNumExtents; i++) {
                rs->increaseStorageSize(txn, size, false);
            }
        } else if (!options.initialExtentSizes.empty()) {
            for (size_t i = 0; i < options.initialExtentSizes.size(); i++) {
                int size = options.initialExtentSizes[i];
                size = _massageExtentSize(&_extentManager, size);
                rs->increaseStorageSize(txn, size, false);
            }
        } else if (options.capped) {
            // normal
            do {
                // Must do this at least once, otherwise we leave the collection with no
                // extents, which is invalid.
                int sz =
                    _massageExtentSize(&_extentManager, options.cappedSize - rs->storageSize(txn));
                sz &= 0xffffff00;
                rs->increaseStorageSize(txn, sz, false);
            } while (rs->storageSize(txn) < options.cappedSize);
        } else {
            rs->increaseStorageSize(txn, _extentManager.initialSize(128), false);
        }
    }

    return Status::OK();
}
Example #6
0
        bool run(OperationContext* txn,
                 const string& dbname,
                 BSONObj& jsobj,
                 int,
                 string& errmsg,
                 BSONObjBuilder& result,
                 bool /*fromRepl*/) {

            Lock::DBRead lk( txn->lockState(), dbname );

            const Database* d = dbHolder().get( txn, dbname );
            const DatabaseCatalogEntry* dbEntry = NULL;

            list<string> names;
            if ( d ) {
                dbEntry = d->getDatabaseCatalogEntry();
                dbEntry->getCollectionNamespaces( &names );
                names.sort();
            }

            BSONArrayBuilder arr;

            for ( list<string>::const_iterator i = names.begin(); i != names.end(); ++i ) {
                string ns = *i;

                StringData collection = nsToCollectionSubstring( ns );
                if ( collection == "system.namespaces" ) {
                    continue;
                }

                BSONObjBuilder b;
                b.append( "name", collection );

                CollectionOptions options =
                    dbEntry->getCollectionCatalogEntry( txn, ns )->getCollectionOptions(txn);
                b.append( "options", options.toBSON() );

                arr.append( b.obj() );
            }

            result.append( "collections", arr.arr() );

            return true;
        }
Example #7
0
TEST(CollectionOptions, ModifyStorageEngineField) {
    CollectionOptions opts;

    // Directly modify storageEngine field in collection options.
    opts.storageEngine = fromjson("{storageEngine1: {x: 1}}");

    // Unrecognized field should not be present in BSON representation.
    BSONObj obj = opts.toBSON();
    ASSERT_FALSE(obj.hasField("unknownField"));

    // Check "storageEngine" field.
    ASSERT_TRUE(obj.hasField("storageEngine"));
    ASSERT_TRUE(obj.getField("storageEngine").isABSONObj());
    BSONObj storageEngine = obj.getObjectField("storageEngine");

    // Check individual storage storageEngine fields.
    ASSERT_TRUE(storageEngine.getField("storageEngine1").isABSONObj());
    BSONObj storageEngine1 = storageEngine.getObjectField("storageEngine1");
    ASSERT_EQUALS(1, storageEngine1.getIntField("x"));
}
CollectionOptions MMAPV1DatabaseCatalogEntry::getCollectionOptions(OperationContext* txn,
                                                                   RecordId rid) const {
    CollectionOptions options;

    if (rid.isNull()) {
        return options;
    }

    RecordStoreV1Base* rs = _getNamespaceRecordStore();
    invariant(rs);

    RecordData data;
    invariant(rs->findRecord(txn, rid, &data));

    if (data.releaseToBson()["options"].isABSONObj()) {
        Status status = options.parse(data.releaseToBson()["options"].Obj());
        fassert(18523, status);
    }
    return options;
}
Example #9
0
TEST(CollectionOptions, ParseEngineField) {
    CollectionOptions opts;
    ASSERT_OK(opts.parse(
        fromjson("{storageEngine: {storageEngine1: {x: 1, y: 2}, storageEngine2: {a: 1, b:2}}}")));
    checkRoundTrip(opts);

    BSONObj obj = opts.toBSON();

    // Check "storageEngine" field.
    ASSERT_TRUE(obj.hasField("storageEngine"));
    ASSERT_TRUE(obj.getField("storageEngine").isABSONObj());
    BSONObj storageEngine = obj.getObjectField("storageEngine");

    // Check individual storage storageEngine fields.
    ASSERT_TRUE(storageEngine.getField("storageEngine1").isABSONObj());
    BSONObj storageEngine1 = storageEngine.getObjectField("storageEngine1");
    ASSERT_EQUALS(1, storageEngine1.getIntField("x"));
    ASSERT_EQUALS(2, storageEngine1.getIntField("y"));

    ASSERT_TRUE(storageEngine.getField("storageEngine2").isABSONObj());
    BSONObj storageEngine2 = storageEngine.getObjectField("storageEngine2");
    ASSERT_EQUALS(1, storageEngine2.getIntField("a"));
    ASSERT_EQUALS(2, storageEngine2.getIntField("b"));
}
Example #10
0
    CollectionCloner::CollectionCloner(ReplicationExecutor* executor,
                                       const HostAndPort& source,
                                       const NamespaceString& sourceNss,
                                       const CollectionOptions& options,
                                       const CallbackFn& work,
                                       StorageInterface* storageInterface)
        : _executor(executor),
          _source(source),
          _sourceNss(sourceNss),
          _destNss(_sourceNss),
          _options(options),
          _work(work),
          _storageInterface(storageInterface),
          _active(false),
          _listIndexesFetcher(_executor,
                              _source,
                              _sourceNss.db().toString(),
                              BSON("listIndexes" << _sourceNss.coll()),
                              stdx::bind(&CollectionCloner::_listIndexesCallback,
                                         this,
                                         stdx::placeholders::_1,
                                         stdx::placeholders::_2,
                                         stdx::placeholders::_3)),
         _findFetcher(_executor,
                      _source,
                      _sourceNss.db().toString(),
                      BSON("find" << _sourceNss.coll() <<
                           "noCursorTimeout" << true), // SERVER-1387
                      stdx::bind(&CollectionCloner::_findCallback,
                                 this,
                                 stdx::placeholders::_1,
                                 stdx::placeholders::_2,
                                 stdx::placeholders::_3)),
          _indexSpecs(),
          _documents(),
          _dbWorkCallbackHandle(),
          // TODO: replace with executor database worker when it is available.
          _scheduleDbWorkFn(stdx::bind(&ReplicationExecutor::scheduleWorkWithGlobalExclusiveLock,
                                       _executor,
                                       stdx::placeholders::_1)) {

        uassert(ErrorCodes::BadValue, "null replication executor", executor);
        uassert(ErrorCodes::BadValue, "invalid collection namespace: " + sourceNss.ns(),
                sourceNss.isValid());
        uassertStatusOK(options.validate());
        uassert(ErrorCodes::BadValue, "callback function cannot be null", work);
        uassert(ErrorCodes::BadValue, "null storage interface", storageInterface);
    }
Example #11
0
Status Database::createView(OperationContext* txn,
                            StringData ns,
                            const CollectionOptions& options) {
    invariant(txn->lockState()->isDbLockedForMode(name(), MODE_X));
    invariant(options.isView());

    NamespaceString nss(ns);
    NamespaceString viewOnNss(nss.db(), options.viewOn);
    _checkCanCreateCollection(nss, options);
    audit::logCreateCollection(&cc(), ns);

    if (nss.isOplog())
        return Status(ErrorCodes::InvalidNamespace,
                      str::stream() << "invalid namespace name for a view: " + nss.toString());

    return _views.createView(txn, nss, viewOnNss, BSONArray(options.pipeline), options.collation);
}
Example #12
0
void OpObserver::onCreateCollection(OperationContext* txn,
                                    const NamespaceString& collectionName,
                                    const CollectionOptions& options) {
    std::string dbName = collectionName.db().toString() + ".$cmd";
    BSONObjBuilder b;
    b.append("create", collectionName.coll().toString());
    b.appendElements(options.toBSON());
    BSONObj cmdObj = b.obj();

    if (!collectionName.isSystemDotProfile()) {
        // do not replicate system.profile modifications
        repl::logOp(txn, "c", dbName.c_str(), cmdObj, nullptr, false);
    }

    getGlobalAuthorizationManager()->logOp(txn, "c", dbName.c_str(), cmdObj, nullptr);
    logOpForDbHash(txn, dbName.c_str());
}
Example #13
0
TEST(CollectionOptions, Validator) {
    CollectionOptions options;

    ASSERT_NOT_OK(options.parse(fromjson("{validator: 'notAnObject'}")));

    ASSERT_OK(options.parse(fromjson("{validator: {a: 1}}")));
    ASSERT_BSONOBJ_EQ(options.validator, fromjson("{a: 1}"));

    options.validator = fromjson("{b: 1}");
    ASSERT_BSONOBJ_EQ(options.toBSON()["validator"].Obj(), fromjson("{b: 1}"));

    CollectionOptions defaultOptions;
    ASSERT_BSONOBJ_EQ(defaultOptions.validator, BSONObj());
    ASSERT(!defaultOptions.toBSON()["validator"]);
}
void ShardingCatalogManager::createCollection(OperationContext* opCtx,
                                              const NamespaceString& ns,
                                              const CollectionOptions& collOptions) {
    const auto catalogClient = Grid::get(opCtx)->catalogClient();
    auto shardRegistry = Grid::get(opCtx)->shardRegistry();

    auto dbEntry =
        uassertStatusOK(catalogClient->getDatabase(
                            opCtx, ns.db().toString(), repl::ReadConcernLevel::kLocalReadConcern))
            .value;
    const auto& primaryShardId = dbEntry.getPrimary();
    auto primaryShard = uassertStatusOK(shardRegistry->getShard(opCtx, primaryShardId));

    BSONObjBuilder createCmdBuilder;
    createCmdBuilder.append("create", ns.coll());
    collOptions.appendBSON(&createCmdBuilder);
    createCmdBuilder.append(kWriteConcernField, opCtx->getWriteConcern().toBSON());
    auto swResponse = primaryShard->runCommandWithFixedRetryAttempts(
        opCtx,
        ReadPreferenceSetting{ReadPreference::PrimaryOnly},
        ns.db().toString(),
        createCmdBuilder.obj(),
        Shard::RetryPolicy::kIdempotent);

    auto createStatus = Shard::CommandResponse::getEffectiveStatus(swResponse);
    if (!createStatus.isOK() && createStatus != ErrorCodes::NamespaceExists) {
        uassertStatusOK(createStatus);
    }

    checkCollectionOptions(opCtx, primaryShard.get(), ns, collOptions);

    // TODO: SERVER-33094 use UUID returned to write config.collections entries.

    // Make sure to advance the opTime if writes didn't occur during the execution of this
    // command. This is to ensure that this request will wait for the opTime that at least
    // reflects the current state (that this command observed) while waiting for replication
    // to satisfy the write concern.
    repl::ReplClientInfo::forClient(opCtx->getClient()).setLastOpToSystemLastOpTime(opCtx);
}
Example #15
0
TEST(CollectionOptions, ParseUUID) {
    CollectionOptions options;
    CollectionUUID uuid = CollectionUUID::gen();

    // 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);

    // Check that a collection options containing a UUID passes validation.
    ASSERT_OK(options.validateForStorage());
}
Example #16
0
void checkRoundTrip(const CollectionOptions& options1) {
    CollectionOptions options2;
    options2.parse(options1.toBSON());
    ASSERT_BSONOBJ_EQ(options1.toBSON(), options2.toBSON());
}
Example #17
0
TEST(CollectionOptions, WriteConcernWhitelistedOptionIgnored) {
    CollectionOptions options;
    auto status = options.parse(fromjson("{writeConcern: 1}"));
    ASSERT_OK(status);
}
Example #18
0
TEST(CollectionOptions, MaxTimeMSWhitelistedOptionIgnored) {
    CollectionOptions options;
    auto status = options.parse(fromjson("{maxTimeMS: 1}"));
    ASSERT_OK(status);
}
Example #19
0
TEST(CollectionOptions, DuplicateCreateOptionIgnoredIfCreateOptionNotFirst) {
    CollectionOptions options;
    auto status =
        options.parse(BSON("capped" << true << "create" << 1 << "create" << 1 << "size" << 1024));
    ASSERT_OK(status);
}
Example #20
0
TEST(CollectionOptions, CollationFieldLeftEmptyWhenOmitted) {
    CollectionOptions options;
    ASSERT_OK(options.parse(fromjson("{validator: {a: 1}}")));
    ASSERT_TRUE(options.collation.isEmpty());
}
Example #21
0
TEST(CollectionOptions, UnknownOptionRejectedIfCreateOptionNotPresent) {
    CollectionOptions options;
    auto status = options.parse(fromjson("{invalidOption: 1}"));
    ASSERT_NOT_OK(status);
    ASSERT_EQ(status.code(), ErrorCodes::InvalidOptions);
}
Example #22
0
TEST(CollectionOptions, UnknownOptionIgnoredIfCreateOptionPresent) {
    CollectionOptions options;
    ASSERT_OK(options.parse(fromjson("{invalidOption: 1, create: 1}")));
}
Example #23
0
TEST(CollectionOptions, CreateOptionIgnoredIfFirst) {
    CollectionOptions options;
    auto status = options.parse(fromjson("{create: 1}"));
    ASSERT_OK(status);
}
Example #24
0
TEST(CollectionOptions, UnknownTopLevelOptionFailsToParse) {
    CollectionOptions options;
    auto status = options.parse(fromjson("{invalidOption: 1}"));
    ASSERT_NOT_OK(status);
    ASSERT_EQ(status.code(), ErrorCodes::InvalidOptions);
}
Example #25
0
TEST(CollectionOptions, PipelineFieldRequiresViewOn) {
    CollectionOptions options;
    ASSERT_NOT_OK(options.parse(fromjson("{pipeline: [{$match: {}}]}")));
}
Example #26
0
TEST(CollectionOptions, ParsedCollationObjShouldBeOwned) {
    CollectionOptions options;
    ASSERT_OK(options.parse(fromjson("{collation: {locale: 'en'}}")));
    ASSERT_BSONOBJ_EQ(options.collation, fromjson("{locale: 'en'}"));
    ASSERT_TRUE(options.collation.isOwned());
}
Example #27
0
TEST(CollectionOptions, ViewParsesCorrectly) {
    CollectionOptions options;
    ASSERT_OK(options.parse(fromjson("{viewOn: 'c', pipeline: [{$match: {}}]}")));
    ASSERT_EQ(options.viewOn, "c");
    ASSERT_BSONOBJ_EQ(options.pipeline, fromjson("[{$match: {}}]"));
}
Example #28
0
TEST(CollectionOptions, DuplicateCreateOptionIgnoredIfCreateOptionFirst) {
    CollectionOptions options;
    auto status = options.parse(BSON("create" << 1 << "create" << 1));
    ASSERT_OK(status);
}
Example #29
0
TEST(CollectionOptions, ViewParsesCorrectlyWithoutPipeline) {
    CollectionOptions options;
    ASSERT_OK(options.parse(fromjson("{viewOn: 'c'}")));
    ASSERT_EQ(options.viewOn, "c");
    ASSERT_BSONOBJ_EQ(options.pipeline, BSONObj());
}
Example #30
0
TEST(CollectionOptions, ResetClearsCollationField) {
    CollectionOptions options;
    ASSERT_TRUE(options.collation.isEmpty());
    ASSERT_OK(options.parse(fromjson("{collation: {locale: 'en'}}")));
    ASSERT_FALSE(options.collation.isEmpty());
}