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"]); }
TEST(CollectionOptions, ParseEngineField) { CollectionOptions opts; ASSERT_OK(opts.parse(fromjson( "{unknownField: 1, " "storageEngine: {storageEngine1: {x: 1, y: 2}, storageEngine2: {a: 1, b:2}}}"))); checkRoundTrip(opts); // 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")); 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")); }
TEST(CollectionOptions, CollationFieldNotDumpedToBSONWhenOmitted) { CollectionOptions options; ASSERT_OK(options.parse(fromjson("{validator: {a: 1}}"))); ASSERT_TRUE(options.collation.isEmpty()); BSONObj asBSON = options.toBSON(); ASSERT_FALSE(asBSON["collation"]); }
bool run(OperationContext* txn, const string& dbname, BSONObj& jsobj, int, string& errmsg, BSONObjBuilder& result, bool /*fromRepl*/) { ScopedTransaction scopedXact(txn, MODE_IS); AutoGetDb autoDb(txn, dbname, MODE_S); const Database* d = autoDb.getDb(); const DatabaseCatalogEntry* dbEntry = NULL; list<string> names; if ( d ) { dbEntry = d->getDatabaseCatalogEntry(); dbEntry->getCollectionNamespaces( &names ); names.sort(); } scoped_ptr<MatchExpression> matcher; if ( jsobj["filter"].isABSONObj() ) { StatusWithMatchExpression parsed = MatchExpressionParser::parse( jsobj["filter"].Obj() ); if ( !parsed.isOK() ) { return appendCommandStatus( result, parsed.getStatus() ); } matcher.reset( parsed.getValue() ); } 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() ); BSONObj maybe = b.obj(); if ( matcher && !matcher->matchesBSON( maybe ) ) { continue; } arr.append( maybe ); } result.append( "collections", arr.arr() ); return true; }
bool run(OperationContext* txn, const string& dbname, BSONObj& jsobj, int, string& errmsg, BSONObjBuilder& result, bool /*fromRepl*/) { Client::ReadContext ctx( txn, dbname ); const Database* d = ctx.ctx().db(); const DatabaseCatalogEntry* dbEntry = d->getDatabaseCatalogEntry(); list<string> names; dbEntry->getCollectionNamespaces( &names ); BSONArrayBuilder arr; for ( list<string>::const_iterator i = names.begin(); i != names.end(); ++i ) { string ns = *i; BSONObjBuilder b; b.append( "name", nsToCollectionSubstring( ns ) ); CollectionOptions options = dbEntry->getCollectionCatalogEntry( txn, ns )->getCollectionOptions(txn); b.append( "options", options.toBSON() ); arr.append( b.obj() ); } result.append( "collections", arr.arr() ); return true; }
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(); }
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()); }
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; }
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")); }
void checkRoundTrip(const CollectionOptions& options1) { CollectionOptions options2; options2.parse(options1.toBSON()); ASSERT_BSONOBJ_EQ(options1.toBSON(), options2.toBSON()); }
bool run(OperationContext* txn, const string& dbname, BSONObj& jsobj, int, string& errmsg, BSONObjBuilder& result) { unique_ptr<MatchExpression> matcher; BSONElement filterElt = jsobj["filter"]; if (!filterElt.eoo()) { if (filterElt.type() != mongo::Object) { return appendCommandStatus( result, Status(ErrorCodes::BadValue, "\"filter\" must be an object")); } StatusWithMatchExpression statusWithMatcher = MatchExpressionParser::parse(filterElt.Obj()); if (!statusWithMatcher.isOK()) { return appendCommandStatus(result, statusWithMatcher.getStatus()); } matcher = std::move(statusWithMatcher.getValue()); } const long long defaultBatchSize = std::numeric_limits<long long>::max(); long long batchSize; Status parseCursorStatus = parseCommandCursorOptions(jsobj, defaultBatchSize, &batchSize); if (!parseCursorStatus.isOK()) { return appendCommandStatus(result, parseCursorStatus); } ScopedTransaction scopedXact(txn, MODE_IS); AutoGetDb autoDb(txn, dbname, MODE_S); const Database* d = autoDb.getDb(); const DatabaseCatalogEntry* dbEntry = NULL; list<string> names; if (d) { dbEntry = d->getDatabaseCatalogEntry(); dbEntry->getCollectionNamespaces(&names); names.sort(); } auto ws = make_unique<WorkingSet>(); auto root = make_unique<QueuedDataStage>(txn, ws.get()); for (std::list<std::string>::const_iterator i = names.begin(); i != names.end(); ++i) { const std::string& ns = *i; StringData collection = nsToCollectionSubstring(ns); if (collection == "system.namespaces") { continue; } BSONObjBuilder b; b.append("name", collection); CollectionOptions options = dbEntry->getCollectionCatalogEntry(ns)->getCollectionOptions(txn); b.append("options", options.toBSON()); BSONObj maybe = b.obj(); if (matcher && !matcher->matchesBSON(maybe)) { continue; } WorkingSetID id = ws->allocate(); WorkingSetMember* member = ws->get(id); member->keyData.clear(); member->loc = RecordId(); member->obj = Snapshotted<BSONObj>(SnapshotId(), maybe); member->transitionToOwnedObj(); root->pushBack(id); } std::string cursorNamespace = str::stream() << dbname << ".$cmd." << name; dassert(NamespaceString(cursorNamespace).isValid()); dassert(NamespaceString(cursorNamespace).isListCollectionsCursorNS()); auto statusWithPlanExecutor = PlanExecutor::make( txn, std::move(ws), std::move(root), cursorNamespace, PlanExecutor::YIELD_MANUAL); if (!statusWithPlanExecutor.isOK()) { return appendCommandStatus(result, statusWithPlanExecutor.getStatus()); } unique_ptr<PlanExecutor> exec = std::move(statusWithPlanExecutor.getValue()); BSONArrayBuilder firstBatch; const int byteLimit = FindCommon::kMaxBytesToReturnToClientAtOnce; for (long long objCount = 0; objCount < batchSize && firstBatch.len() < byteLimit; objCount++) { BSONObj next; PlanExecutor::ExecState state = exec->getNext(&next, NULL); if (state == PlanExecutor::IS_EOF) { break; } invariant(state == PlanExecutor::ADVANCED); firstBatch.append(next); } CursorId cursorId = 0LL; if (!exec->isEOF()) { exec->saveState(); exec->detachFromOperationContext(); ClientCursor* cursor = new ClientCursor(CursorManager::getGlobalCursorManager(), exec.release(), cursorNamespace, txn->recoveryUnit()->isReadingFromMajorityCommittedSnapshot()); cursorId = cursor->cursorid(); } appendCursorResponseObject(cursorId, cursorNamespace, firstBatch.arr(), &result); return true; }
Status MMAPV1DatabaseCatalogEntry::createCollection( OperationContext* txn, const StringData& ns, const CollectionOptions& options, bool allocateDefaultSpace ) { _namespaceIndex.init( txn ); if ( _namespaceIndex.details( ns ) ) { return Status( ErrorCodes::NamespaceExists, str::stream() << "namespace already exists: " << ns ); } BSONObj optionsAsBSON = options.toBSON(); _addNamespaceToNamespaceCollection( txn, ns, &optionsAsBSON ); _namespaceIndex.add_ns( txn, ns, DiskLoc(), options.capped ); // allocation strategy set explicitly in flags or by server-wide default if ( !options.capped ) { NamespaceDetailsRSV1MetaData md( ns, _namespaceIndex.details( ns ), _getNamespaceRecordStore( txn, ns ) ); if ( options.flagsSet ) { md.setUserFlag( txn, options.flags ); } else if ( newCollectionsUsePowerOf2Sizes ) { md.setUserFlag( txn, NamespaceDetails::Flag_UsePowerOf2Sizes ); } } else if ( options.cappedMaxDocs > 0 ) { txn->recoveryUnit()->writingInt( _namespaceIndex.details( ns )->maxDocsInCapped ) = options.cappedMaxDocs; } if ( allocateDefaultSpace ) { scoped_ptr<RecordStoreV1Base> rs( _getRecordStore( txn, ns ) ); if ( options.initialNumExtents > 0 ) { int size = _massageExtentSize( &_extentManager, options.cappedSize ); for ( int i = 0; i < options.initialNumExtents; i++ ) { rs->increaseStorageSize( txn, size, -1 ); } } 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, -1 ); } } 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() ); sz &= 0xffffff00; rs->increaseStorageSize( txn, sz, -1 ); } while( rs->storageSize() < options.cappedSize ); } else { rs->increaseStorageSize( txn, _extentManager.initialSize( 128 ), -1 ); } } return Status::OK(); }
void check( const CollectionOptions& options1 ) { CollectionOptions options2; options2.parse( options1.toBSON() ); ASSERT_EQUALS( options1.toBSON(), options2.toBSON() ); }
bool run(OperationContext* txn, const string& dbname, BSONObj& jsobj, int, string& errmsg, BSONObjBuilder& result, bool /*fromRepl*/) { boost::scoped_ptr<MatchExpression> matcher; BSONElement filterElt = jsobj["filter"]; if (!filterElt.eoo()) { if (filterElt.type() != mongo::Object) { return appendCommandStatus(result, Status(ErrorCodes::TypeMismatch, str::stream() << "\"filter\" must be of type Object, not " << typeName(filterElt.type()))); } StatusWithMatchExpression statusWithMatcher = MatchExpressionParser::parse(filterElt.Obj()); if (!statusWithMatcher.isOK()) { return appendCommandStatus(result, statusWithMatcher.getStatus()); } matcher.reset(statusWithMatcher.getValue()); } const long long defaultBatchSize = std::numeric_limits<long long>::max(); long long batchSize; Status parseCursorStatus = parseCommandCursorOptions(jsobj, defaultBatchSize, &batchSize); if (!parseCursorStatus.isOK()) { return appendCommandStatus(result, parseCursorStatus); } ScopedTransaction scopedXact(txn, MODE_IS); AutoGetDb autoDb(txn, dbname, MODE_S); const Database* d = autoDb.getDb(); const DatabaseCatalogEntry* dbEntry = NULL; list<string> names; if (d) { dbEntry = d->getDatabaseCatalogEntry(); dbEntry->getCollectionNamespaces(&names); names.sort(); } std::auto_ptr<WorkingSet> ws(new WorkingSet()); std::auto_ptr<QueuedDataStage> root(new QueuedDataStage(ws.get())); for (std::list<std::string>::const_iterator i = names.begin(); i != names.end(); ++i) { const std::string& ns = *i; StringData collection = nsToCollectionSubstring(ns); if (collection == "system.namespaces") { continue; } BSONObjBuilder b; b.append("name", collection); CollectionOptions options = dbEntry->getCollectionCatalogEntry(ns)->getCollectionOptions(txn); b.append("options", options.toBSON()); BSONObj maybe = b.obj(); if (matcher && !matcher->matchesBSON(maybe)) { continue; } WorkingSetMember member; member.state = WorkingSetMember::OWNED_OBJ; member.keyData.clear(); member.loc = RecordId(); member.obj = Snapshotted<BSONObj>(SnapshotId(), maybe); root->pushBack(member); } std::string cursorNamespace = str::stream() << dbname << ".$cmd." << name; dassert(NamespaceString(cursorNamespace).isValid()); dassert(NamespaceString(cursorNamespace).isListCollectionsGetMore()); PlanExecutor* rawExec; Status makeStatus = PlanExecutor::make(txn, ws.release(), root.release(), cursorNamespace, PlanExecutor::YIELD_MANUAL, &rawExec); std::auto_ptr<PlanExecutor> exec(rawExec); if (!makeStatus.isOK()) { return appendCommandStatus(result, makeStatus); } BSONArrayBuilder firstBatch; const int byteLimit = MaxBytesToReturnToClientAtOnce; for (long long objCount = 0; objCount < batchSize && firstBatch.len() < byteLimit; objCount++) { BSONObj next; PlanExecutor::ExecState state = exec->getNext(&next, NULL); if (state == PlanExecutor::IS_EOF) { break; } invariant(state == PlanExecutor::ADVANCED); firstBatch.append(next); } CursorId cursorId = 0LL; if (!exec->isEOF()) { exec->saveState(); ClientCursor* cursor = new ClientCursor( CursorManager::getGlobalCursorManager(), exec.release(), cursorNamespace); cursorId = cursor->cursorid(); } Command::appendCursorResponseObject(cursorId, cursorNamespace, firstBatch.arr(), &result); return true; }