mongo::BSONArray MockRemoteDBServer::query(MockRemoteDBServer::InstanceID id, const string& ns, mongo::Query query, int nToReturn, int nToSkip, const BSONObj* fieldsToReturn, int queryOptions, int batchSize) { checkIfUp(id); if (_delayMilliSec > 0) { mongo::sleepmillis(_delayMilliSec); } checkIfUp(id); scoped_spinlock sLock(_lock); _queryCount++; const vector<BSONObj>& coll = _dataMgr[ns]; BSONArrayBuilder result; for (vector<BSONObj>::const_iterator iter = coll.begin(); iter != coll.end(); ++iter) { result.append(iter->copy()); } return BSONArray(result.obj()); }
virtual bool run(OperationContext* txn, const string& dbname, BSONObj& cmdObj, int options, string& errmsg, BSONObjBuilder& result, bool fromRepl = false ) { NamespaceString ns( dbname, cmdObj[name].String() ); AutoGetCollectionForRead ctx(txn, ns.ns()); Collection* collection = ctx.getCollection(); if ( !collection ) return appendCommandStatus( result, Status( ErrorCodes::NamespaceNotFound, str::stream() << "ns does not exist: " << ns.ns() ) ); size_t numCursors = static_cast<size_t>( cmdObj["numCursors"].numberInt() ); if ( numCursors == 0 || numCursors > 10000 ) return appendCommandStatus( result, Status( ErrorCodes::BadValue, str::stream() << "numCursors has to be between 1 and 10000" << " was: " << numCursors ) ); OwnedPointerVector<RecordIterator> iterators(collection->getManyIterators(txn)); if (iterators.size() < numCursors) { numCursors = iterators.size(); } OwnedPointerVector<PlanExecutor> execs; for ( size_t i = 0; i < numCursors; i++ ) { WorkingSet* ws = new WorkingSet(); MultiIteratorStage* mis = new MultiIteratorStage(txn, ws, collection); PlanExecutor* rawExec; // Takes ownership of 'ws' and 'mis'. Status execStatus = PlanExecutor::make(txn, ws, mis, collection, PlanExecutor::YIELD_AUTO, &rawExec); invariant(execStatus.isOK()); auto_ptr<PlanExecutor> curExec(rawExec); // The PlanExecutor was registered on construction due to the YIELD_AUTO policy. // We have to deregister it, as it will be registered with ClientCursor. curExec->deregisterExec(); // Need to save state while yielding locks between now and getMore(). curExec->saveState(); execs.push_back(curExec.release()); } // transfer iterators to executors using a round-robin distribution. // TODO consider using a common work queue once invalidation issues go away. for (size_t i = 0; i < iterators.size(); i++) { PlanExecutor* theExec = execs[i % execs.size()]; MultiIteratorStage* mis = static_cast<MultiIteratorStage*>(theExec->getRootStage()); // This wasn't called above as they weren't assigned yet iterators[i]->saveState(); mis->addIterator(iterators.releaseAt(i)); } { BSONArrayBuilder bucketsBuilder; for (size_t i = 0; i < execs.size(); i++) { // transfer ownership of an executor to the ClientCursor (which manages its own // lifetime). ClientCursor* cc = new ClientCursor( collection->getCursorManager(), execs.releaseAt(i), ns.ns() ); BSONObjBuilder threadResult; appendCursorResponseObject( cc->cursorid(), ns.ns(), BSONArray(), &threadResult ); threadResult.appendBool( "ok", 1 ); bucketsBuilder.append( threadResult.obj() ); } result.appendArray( "cursors", bucketsBuilder.obj() ); } return true; }
virtual bool run(OperationContext* txn, const string& dbname, BSONObj& cmdObj, int options, string& errmsg, BSONObjBuilder& result, bool fromRepl = false ) { NamespaceString ns( dbname, cmdObj[name].String() ); AutoGetCollectionForRead ctx(txn, ns.ns()); Collection* collection = ctx.getCollection(); if ( !collection ) return appendCommandStatus( result, Status( ErrorCodes::NamespaceNotFound, str::stream() << "ns does not exist: " << ns.ns() ) ); size_t numCursors = static_cast<size_t>( cmdObj["numCursors"].numberInt() ); if ( numCursors == 0 || numCursors > 10000 ) return appendCommandStatus( result, Status( ErrorCodes::BadValue, str::stream() << "numCursors has to be between 1 and 10000" << " was: " << numCursors ) ); OwnedPointerVector<RecordIterator> iterators(collection->getManyIterators(txn)); if (iterators.size() < numCursors) { numCursors = iterators.size(); } OwnedPointerVector<PlanExecutor> execs; for ( size_t i = 0; i < numCursors; i++ ) { WorkingSet* ws = new WorkingSet(); MultiIteratorStage* mis = new MultiIteratorStage(txn, ws, collection); // Takes ownership of 'ws' and 'mis'. auto_ptr<PlanExecutor> curExec(new PlanExecutor(txn, ws, mis, collection)); // Each of the plan executors should yield automatically. We pass "false" to // indicate that 'curExec' should not register itself, as it will get registered // by ClientCursor instead. curExec->setYieldPolicy(PlanExecutor::YIELD_AUTO, false); // Need to save state while yielding locks between now and newGetMore. curExec->saveState(); execs.push_back(curExec.release()); } // transfer iterators to executors using a round-robin distribution. // TODO consider using a common work queue once invalidation issues go away. for (size_t i = 0; i < iterators.size(); i++) { PlanExecutor* theExec = execs[i % execs.size()]; MultiIteratorStage* mis = static_cast<MultiIteratorStage*>(theExec->getRootStage()); mis->addIterator(iterators.releaseAt(i)); } { BSONArrayBuilder bucketsBuilder; for (size_t i = 0; i < execs.size(); i++) { // transfer ownership of an executor to the ClientCursor (which manages its own // lifetime). ClientCursor* cc = new ClientCursor( collection, execs.releaseAt(i) ); // we are mimicking the aggregation cursor output here // that is why there are ns, ok and empty firstBatch BSONObjBuilder threadResult; { BSONObjBuilder cursor; cursor.appendArray( "firstBatch", BSONObj() ); cursor.append( "ns", ns ); cursor.append( "id", cc->cursorid() ); threadResult.append( "cursor", cursor.obj() ); } threadResult.appendBool( "ok", 1 ); bucketsBuilder.append( threadResult.obj() ); } result.appendArray( "cursors", bucketsBuilder.obj() ); } return true; }
virtual bool run( const string& dbname, BSONObj& cmdObj, int options, string& errmsg, BSONObjBuilder& result, bool fromRepl = false ) { NamespaceString ns( dbname, cmdObj[name].String() ); Client::ReadContext ctx(ns.ns()); Database* db = ctx.ctx().db(); Collection* collection = db->getCollection( ns ); if ( !collection ) return appendCommandStatus( result, Status( ErrorCodes::NamespaceNotFound, str::stream() << "ns does not exist: " << ns.ns() ) ); size_t numCursors = static_cast<size_t>( cmdObj["numCursors"].numberInt() ); if ( numCursors == 0 || numCursors > 10000 ) return appendCommandStatus( result, Status( ErrorCodes::BadValue, str::stream() << "numCursors has to be between 1 and 10000" << " was: " << numCursors ) ); OwnedPointerVector<RecordIterator> iterators(collection->getManyIterators()); if (iterators.size() < numCursors) { numCursors = iterators.size(); } OwnedPointerVector<MultiIteratorRunner> runners; for ( size_t i = 0; i < numCursors; i++ ) { runners.push_back(new MultiIteratorRunner(ns.ns(), collection)); } // transfer iterators to runners using a round-robin distribution. // TODO consider using a common work queue once invalidation issues go away. for (size_t i = 0; i < iterators.size(); i++) { runners[i % runners.size()]->addIterator(iterators.releaseAt(i)); } { BSONArrayBuilder bucketsBuilder; for (size_t i = 0; i < runners.size(); i++) { // transfer ownership of a runner to the ClientCursor (which manages its own // lifetime). ClientCursor* cc = new ClientCursor( collection, runners.releaseAt(i) ); // we are mimicking the aggregation cursor output here // that is why there are ns, ok and empty firstBatch BSONObjBuilder threadResult; { BSONObjBuilder cursor; cursor.appendArray( "firstBatch", BSONObj() ); cursor.append( "ns", ns ); cursor.append( "id", cc->cursorid() ); threadResult.append( "cursor", cursor.obj() ); } threadResult.appendBool( "ok", 1 ); bucketsBuilder.append( threadResult.obj() ); } result.appendArray( "cursors", bucketsBuilder.obj() ); } return true; }
/** * If uuid is specified, add it to the collection specified by nss. This will error if the * collection already has a UUID. */ Status _collModInternal(OperationContext* opCtx, const NamespaceString& nss, const BSONObj& cmdObj, BSONObjBuilder* result, bool upgradeUniqueIndexes) { StringData dbName = nss.db(); AutoGetDb autoDb(opCtx, dbName, MODE_X); Database* const db = autoDb.getDb(); Collection* coll = db ? db->getCollection(opCtx, nss) : nullptr; // May also modify a view instead of a collection. boost::optional<ViewDefinition> view; if (db && !coll) { const auto sharedView = db->getViewCatalog()->lookup(opCtx, nss.ns()); if (sharedView) { // We copy the ViewDefinition as it is modified below to represent the requested state. view = {*sharedView}; } } // This can kill all cursors so don't allow running it while a background operation is in // progress. BackgroundOperation::assertNoBgOpInProgForNs(nss); // If db/collection/view does not exist, short circuit and return. if (!db || (!coll && !view)) { return Status(ErrorCodes::NamespaceNotFound, "ns does not exist"); } // This is necessary to set up CurOp and update the Top stats. OldClientContext ctx(opCtx, nss.ns()); bool userInitiatedWritesAndNotPrimary = opCtx->writesAreReplicated() && !repl::ReplicationCoordinator::get(opCtx)->canAcceptWritesFor(opCtx, nss); if (userInitiatedWritesAndNotPrimary) { return Status(ErrorCodes::NotMaster, str::stream() << "Not primary while setting collection options on " << nss.ns()); } BSONObjBuilder oplogEntryBuilder; auto statusW = parseCollModRequest(opCtx, nss, coll, cmdObj, &oplogEntryBuilder); if (!statusW.isOK()) { return statusW.getStatus(); } CollModRequest cmr = statusW.getValue(); WriteUnitOfWork wunit(opCtx); // Handle collMod on a view and return early. The View Catalog handles the creation of oplog // entries for modifications on a view. if (view) { if (!cmr.viewPipeLine.eoo()) view->setPipeline(cmr.viewPipeLine); if (!cmr.viewOn.empty()) view->setViewOn(NamespaceString(dbName, cmr.viewOn)); ViewCatalog* catalog = db->getViewCatalog(); BSONArrayBuilder pipeline; for (auto& item : view->pipeline()) { pipeline.append(item); } auto errorStatus = catalog->modifyView(opCtx, nss, view->viewOn(), BSONArray(pipeline.obj())); if (!errorStatus.isOK()) { return errorStatus; } wunit.commit(); return Status::OK(); } // In order to facilitate the replication rollback process, which makes a best effort attempt to // "undo" a set of oplog operations, we store a snapshot of the old collection options to // provide to the OpObserver. TTL index updates aren't a part of collection options so we // save the relevant TTL index data in a separate object. CollectionOptions oldCollOptions = coll->getCatalogEntry()->getCollectionOptions(opCtx); boost::optional<TTLCollModInfo> ttlInfo; // Handle collMod operation type appropriately. // TTLIndex if (!cmr.indexExpireAfterSeconds.eoo()) { BSONElement& newExpireSecs = cmr.indexExpireAfterSeconds; BSONElement oldExpireSecs = cmr.idx->infoObj().getField("expireAfterSeconds"); if (SimpleBSONElementComparator::kInstance.evaluate(oldExpireSecs != newExpireSecs)) { result->appendAs(oldExpireSecs, "expireAfterSeconds_old"); // Change the value of "expireAfterSeconds" on disk. coll->getCatalogEntry()->updateTTLSetting( opCtx, cmr.idx->indexName(), newExpireSecs.safeNumberLong()); // Notify the index catalog that the definition of this index changed. cmr.idx = coll->getIndexCatalog()->refreshEntry(opCtx, cmr.idx); result->appendAs(newExpireSecs, "expireAfterSeconds_new"); opCtx->recoveryUnit()->onRollback([ opCtx, idx = cmr.idx, coll ]() { coll->getIndexCatalog()->refreshEntry(opCtx, idx); }); } // Save previous TTL index expiration. ttlInfo = TTLCollModInfo{Seconds(newExpireSecs.safeNumberLong()), Seconds(oldExpireSecs.safeNumberLong()), cmr.idx->indexName()}; } // The Validator, ValidationAction and ValidationLevel are already parsed and must be OK. if (!cmr.collValidator.eoo()) invariant(coll->setValidator(opCtx, cmr.collValidator.Obj())); if (!cmr.collValidationAction.empty()) invariant(coll->setValidationAction(opCtx, cmr.collValidationAction)); if (!cmr.collValidationLevel.empty()) invariant(coll->setValidationLevel(opCtx, cmr.collValidationLevel)); // UsePowerof2Sizes if (!cmr.usePowerOf2Sizes.eoo()) setCollectionOptionFlag(opCtx, coll, cmr.usePowerOf2Sizes, result); // NoPadding if (!cmr.noPadding.eoo()) setCollectionOptionFlag(opCtx, coll, cmr.noPadding, result); // Upgrade unique indexes if (upgradeUniqueIndexes) { // A cmdObj with an empty collMod, i.e. nFields = 1, implies that it is a Unique Index // upgrade collMod. invariant(cmdObj.nFields() == 1); std::vector<std::string> indexNames; coll->getCatalogEntry()->getAllUniqueIndexes(opCtx, &indexNames); for (size_t i = 0; i < indexNames.size(); i++) { const IndexDescriptor* desc = coll->getIndexCatalog()->findIndexByName(opCtx, indexNames[i]); invariant(desc); // Update index metadata in storage engine. coll->getCatalogEntry()->updateIndexMetadata(opCtx, desc); // Refresh the in-memory instance of the index. desc = coll->getIndexCatalog()->refreshEntry(opCtx, desc); opCtx->recoveryUnit()->onRollback( [opCtx, desc, coll]() { coll->getIndexCatalog()->refreshEntry(opCtx, desc); }); } } // Only observe non-view collMods, as view operations are observed as operations on the // system.views collection. getGlobalServiceContext()->getOpObserver()->onCollMod( opCtx, nss, coll->uuid(), oplogEntryBuilder.obj(), oldCollOptions, ttlInfo); wunit.commit(); return Status::OK(); }
virtual bool run(OperationContext* txn, const string& dbname, BSONObj& cmdObj, int options, string& errmsg, BSONObjBuilder& result) { NamespaceString ns(dbname, cmdObj[name].String()); AutoGetCollectionForRead ctx(txn, ns.ns()); Collection* collection = ctx.getCollection(); if (!collection) return appendCommandStatus(result, Status(ErrorCodes::NamespaceNotFound, str::stream() << "ns does not exist: " << ns.ns())); size_t numCursors = static_cast<size_t>(cmdObj["numCursors"].numberInt()); if (numCursors == 0 || numCursors > 10000) return appendCommandStatus(result, Status(ErrorCodes::BadValue, str::stream() << "numCursors has to be between 1 and 10000" << " was: " << numCursors)); auto iterators = collection->getManyCursors(txn); if (iterators.size() < numCursors) { numCursors = iterators.size(); } std::vector<std::unique_ptr<PlanExecutor>> execs; for (size_t i = 0; i < numCursors; i++) { unique_ptr<WorkingSet> ws = make_unique<WorkingSet>(); unique_ptr<MultiIteratorStage> mis = make_unique<MultiIteratorStage>(txn, ws.get(), collection); // Takes ownership of 'ws' and 'mis'. auto statusWithPlanExecutor = PlanExecutor::make( txn, std::move(ws), std::move(mis), collection, PlanExecutor::YIELD_AUTO); invariant(statusWithPlanExecutor.isOK()); execs.push_back(std::move(statusWithPlanExecutor.getValue())); } // transfer iterators to executors using a round-robin distribution. // TODO consider using a common work queue once invalidation issues go away. for (size_t i = 0; i < iterators.size(); i++) { auto& planExec = execs[i % execs.size()]; MultiIteratorStage* mis = checked_cast<MultiIteratorStage*>(planExec->getRootStage()); mis->addIterator(std::move(iterators[i])); } { BSONArrayBuilder bucketsBuilder; for (auto&& exec : execs) { // The PlanExecutor was registered on construction due to the YIELD_AUTO policy. // We have to deregister it, as it will be registered with ClientCursor. exec->deregisterExec(); // Need to save state while yielding locks between now and getMore(). exec->saveState(); exec->detachFromOperationContext(); // transfer ownership of an executor to the ClientCursor (which manages its own // lifetime). ClientCursor* cc = new ClientCursor(collection->getCursorManager(), exec.release(), ns.ns(), txn->recoveryUnit()->isReadingFromMajorityCommittedSnapshot()); BSONObjBuilder threadResult; appendCursorResponseObject(cc->cursorid(), ns.ns(), BSONArray(), &threadResult); threadResult.appendBool("ok", 1); bucketsBuilder.append(threadResult.obj()); } result.appendArray("cursors", bucketsBuilder.obj()); } return true; }