Collection* Database::createCollection( OperationContext* txn, StringData ns, const CollectionOptions& options, bool allocateDefaultSpace, bool createIdIndex ) { massert( 17399, "collection already exists", getCollection( ns ) == NULL ); massertNamespaceNotIndex( ns, "createCollection" ); invariant(txn->lockState()->isDbLockedForMode(name(), MODE_X)); if ( serverGlobalParams.configsvr && !( ns.startsWith( "config." ) || ns.startsWith( "local." ) || ns.startsWith( "admin." ) ) ) { uasserted(14037, "can't create user databases on a --configsvr instance"); } if (NamespaceString::normal(ns)) { // This check only applies for actual collections, not indexes or other types of ns. uassert(17381, str::stream() << "fully qualified namespace " << ns << " is too long " << "(max is " << NamespaceString::MaxNsCollectionLen << " bytes)", ns.size() <= NamespaceString::MaxNsCollectionLen); } NamespaceString nss( ns ); uassert( 17316, "cannot create a blank collection", nss.coll() > 0 ); audit::logCreateCollection( currentClient.get(), ns ); txn->recoveryUnit()->registerChange( new AddCollectionChange(this, ns) ); Status status = _dbEntry->createCollection(txn, ns, options, allocateDefaultSpace); massertNoTraceStatusOK(status); Collection* collection = _getOrCreateCollectionInstance(txn, ns); invariant(collection); _collections[ns] = collection; if ( createIdIndex ) { if ( collection->requiresIdIndex() ) { if ( options.autoIndexId == CollectionOptions::YES || options.autoIndexId == CollectionOptions::DEFAULT ) { IndexCatalog* ic = collection->getIndexCatalog(); uassertStatusOK( ic->createIndexOnEmptyCollection(txn, ic->getDefaultIdIndexSpec())); } } if ( nss.isSystem() ) { authindex::createSystemIndexes( txn, collection ); } } return collection; }
long long runCount( const char *ns, const BSONObj &cmd, string &err, int &errCode ) { Collection *cl = getCollection( ns ); if (cl == NULL) { err = "ns missing"; return -1; } BSONObj query = cmd.getObjectField("query"); long long count = 0; long long skip = cmd["skip"].numberLong(); long long limit = cmd["limit"].numberLong(); if ( limit < 0 ) { limit = -limit; } Client::WithOpSettings wos(OpSettings().setQueryCursorMode(DEFAULT_LOCK_CURSOR).setBulkFetch(true)); Lock::assertAtLeastReadLocked(ns); try { for (shared_ptr<Cursor> cursor = getOptimizedCursor( ns, query, BSONObj(), _countPlanPolicies ); cursor->ok() ; cursor->advance() ) { if ( cursor->currentMatches() && !cursor->getsetdup( cursor->currPK() ) ) { if ( skip > 0 ) { --skip; } else { ++count; if ( limit > 0 && count >= limit ) { break; } } } } return count; } catch ( const DBException &e ) { err = e.toString(); errCode = e.getCode(); count = -2; } catch ( const std::exception &e ) { err = e.what(); errCode = 0; count = -2; } if ( count == -2 ) { // Historically we have returned zero in many count assertion cases - see SERVER-2291. log() << "Count with ns: " << ns << " and query: " << query << " failed with exception: " << err << " code: " << errCode << endl; } return count; }
Status Database::dropCollectionEvenIfSystem(OperationContext* opCtx, const NamespaceString& fullns) { invariant(opCtx->lockState()->isDbLockedForMode(name(), MODE_X)); LOG(1) << "dropCollection: " << fullns; Collection* collection = getCollection(fullns); if (!collection) { return Status::OK(); // Post condition already met. } massertNamespaceNotIndex(fullns.toString(), "dropCollection"); BackgroundOperation::assertNoBgOpInProgForNs(fullns); audit::logDropCollection(&cc(), fullns.toString()); Status s = collection->getIndexCatalog()->dropAllIndexes(opCtx, true); if (!s.isOK()) { warning() << "could not drop collection, trying to drop indexes" << fullns << " because of " << redact(s.toString()); return s; } verify(collection->_details->getTotalIndexCount(opCtx) == 0); LOG(1) << "\t dropIndexes done"; Top::get(opCtx->getClient()->getServiceContext()).collectionDropped(fullns.toString()); // We want to destroy the Collection object before telling the StorageEngine to destroy the // RecordStore. _clearCollectionCache(opCtx, fullns.toString(), "collection dropped"); s = _dbEntry->dropCollection(opCtx, fullns.toString()); if (!s.isOK()) return s; DEV { // check all index collection entries are gone string nstocheck = fullns.toString() + ".$"; for (CollectionMap::const_iterator i = _collections.begin(); i != _collections.end(); ++i) { string temp = i->first; if (temp.find(nstocheck) != 0) continue; log() << "after drop, bad cache entries for: " << fullns << " have " << temp; verify(0); } } getGlobalServiceContext()->getOpObserver()->onDropCollection(opCtx, fullns); return Status::OK(); }
int main(int argc, char *argv[]) { List l = newList(); FILE *f = fopen("collection.txt", "r"); getCollection(l, f); showList(l); getGraph(l, ListLength(l)); return 0; }
bool getLastGTIDinOplog(GTID* gtid) { LOCK_REASON(lockReason, "repl: looking up last GTID in oplog"); Client::ReadContext ctx(rsoplog, lockReason); // TODO: Should this be using rsOplogDetails, verifying non-null? Collection *cl = getCollection(rsoplog); shared_ptr<Cursor> c( Cursor::make(cl, -1) ); if (c->ok()) { *gtid = getGTIDFromOplogEntry(c->current()); return true; } return false; }
TEST(cursor,sdbCursor_get_over_then_close) { INT32 rc = SDB_OK ; rc = initEnv( HOST, SERVER, USER, PASSWD ) ; ASSERT_EQ( SDB_OK, rc ) ; sdbConnectionHandle connection = 0 ; sdbCollectionHandle collection = 0 ; sdbCursorHandle cursor = 0 ; INT32 NUM = 1 ; bson obj ; bson obj1 ; bson obj2 ; rc = sdbConnect ( HOST, SERVER, USER, PASSWD, &connection ) ; ASSERT_EQ( SDB_OK, rc ) ; rc = getCollection ( connection, COLLECTION_FULL_NAME, &collection ) ; ASSERT_EQ( SDB_OK, rc ) ; insertRecords ( collection, NUM ) ; rc = sdbQuery( collection, NULL, NULL, NULL, NULL, 0, -1, &cursor ) ; ASSERT_EQ( SDB_OK, rc ) ; bson_init(&obj); bson_init(&obj1); bson_init(&obj2); rc = sdbNext ( cursor, &obj ) ; CHECK_MSG("%s%d\n","rc = ",rc) ; ASSERT_EQ( SDB_OK, rc ) ; printf( "SdbNext record is:\n" ) ; bson_print( &obj ) ; printf("\n") ; rc = sdbNext ( cursor, &obj1 ) ; CHECK_MSG("%s%d\n","rc = ",rc) ; ASSERT_EQ( SDB_DMS_EOC, rc ) ; rc = sdbNext ( cursor, &obj1 ) ; CHECK_MSG("%s%d\n","rc = ",rc) ; ASSERT_EQ( SDB_DMS_CONTEXT_IS_CLOSE, rc ) ; bson_destroy( &obj ) ; bson_destroy( &obj1 ) ; bson_destroy( &obj2 ) ; rc = sdbCloseCursor( cursor ) ; CHECK_MSG("%s%d\n","rc = ",rc) ; ASSERT_EQ( SDB_OK, rc ) ; sdbDisconnect ( connection ) ; sdbReleaseCursor ( cursor ) ; sdbReleaseCollection ( collection ) ; sdbReleaseConnection ( connection ) ; }
void ActionListEditor::onSideBarSelectionChanged(const QModelIndex &index) { int type = index.data(Zanshin::ItemTypeRole).toInt(); currentPage()->setCollectionSelectorVisible(type == Zanshin::Inbox || type == Zanshin::Context || type == Zanshin::ContextRoot || type == Zanshin::Topic || type == Zanshin::TopicRoot); currentPage()->selectFirstIndex(); currentPage()->setCurrentCollection(getCollection(index)); }
bool gtidExistsInOplog(GTID gtid) { LOCK_REASON(lockReason, "repl: querying for GTID in oplog"); Client::ReadContext ctx(rsoplog, lockReason); // TODO: Should this be using rsOplogDetails, verifying non-null? Collection *cl = getCollection(rsoplog); BSONObjBuilder q; BSONObj result; addGTIDToBSON("_id", gtid, q); const bool found = cl != NULL && cl->findOne( q.done(), result ); return found; }
Collection* Database::_getOrCreateCollectionInstance(OperationContext* txn, StringData fullns) { Collection* collection = getCollection(fullns); if (collection) { return collection; } unique_ptr<CollectionCatalogEntry> cce(_dbEntry->getCollectionCatalogEntry(fullns)); invariant(cce.get()); unique_ptr<RecordStore> rs(_dbEntry->getRecordStore(fullns)); invariant(rs.get()); // if cce exists, so should this // Not registering AddCollectionChange since this is for collections that already exist. Collection* c = new Collection(txn, fullns, cce.release(), rs.release(), _dbEntry); return c; }
bool _tryQueryByPKHack(const char *ns, const BSONObj &query, const ParsedQuery &pq, CurOp &curop, Message &result) { BSONObj resObject; bool found = false; Collection *cl = getCollection(ns); if (cl == NULL) { return false; // ns doesn't exist, fall through to optimizer for legacy reasons } const BSONObj &pk = cl->getSimplePKFromQuery(query); if (pk.isEmpty()) { return false; // unable to query by PK - resort to using the optimizer } found = queryByPKHack(cl, pk, query, resObject); if ( shardingState.needShardChunkManager( ns ) ) { ShardChunkManagerPtr m = shardingState.getShardChunkManager( ns ); if ( m && ! m->belongsToMe( resObject ) ) { // I have something for this _id // but it doesn't belong to me // so return nothing resObject = BSONObj(); found = false; } } BufBuilder bb(sizeof(QueryResult)+resObject.objsize()+32); bb.skip(sizeof(QueryResult)); if ( found ) { fillQueryResultFromObj( bb , pq.getFields() , resObject ); } auto_ptr< QueryResult > qr( (QueryResult *) bb.buf() ); bb.decouple(); qr->setResultFlagsToOk(); qr->len = bb.len(); curop.debug().responseLength = bb.len(); qr->setOperation(opReply); qr->cursorId = 0; qr->startingFrom = 0; qr->nReturned = found ? 1 : 0; result.setData( qr.release(), true ); return true; }
void Database::getStats(OperationContext* opCtx, BSONObjBuilder* output, double scale) { list<string> collections; _dbEntry->getCollectionNamespaces(&collections); long long nCollections = 0; long long nViews = 0; long long objects = 0; long long size = 0; long long storageSize = 0; long long numExtents = 0; long long indexes = 0; long long indexSize = 0; for (list<string>::const_iterator it = collections.begin(); it != collections.end(); ++it) { const string ns = *it; Collection* collection = getCollection(ns); if (!collection) continue; nCollections += 1; objects += collection->numRecords(opCtx); size += collection->dataSize(opCtx); BSONObjBuilder temp; storageSize += collection->getRecordStore()->storageSize(opCtx, &temp); numExtents += temp.obj()["numExtents"].numberInt(); // XXX indexes += collection->getIndexCatalog()->numIndexesTotal(opCtx); indexSize += collection->getIndexSize(opCtx); } getViewCatalog()->iterate(opCtx, [&](const ViewDefinition& view) { nViews += 1; }); output->appendNumber("collections", nCollections); output->appendNumber("views", nViews); output->appendNumber("objects", objects); output->append("avgObjSize", objects == 0 ? 0 : double(size) / double(objects)); output->appendNumber("dataSize", size / scale); output->appendNumber("storageSize", storageSize / scale); output->appendNumber("numExtents", numExtents); output->appendNumber("indexes", indexes); output->appendNumber("indexSize", indexSize / scale); _dbEntry->appendExtraStats(opCtx, output, scale); }
TEST(cursor,sdbNext) { INT32 rc = SDB_OK ; rc = initEnv( HOST, SERVER, USER, PASSWD ) ; ASSERT_EQ( SDB_OK, rc ) ; sdbConnectionHandle connection = 0 ; sdbCollectionHandle collection = 0 ; sdbCursorHandle cursor = 0 ; INT32 NUM = 10 ; SINT64 count = 0 ; bson obj ; rc = sdbConnect ( HOST, SERVER, USER, PASSWD, &connection ) ; ASSERT_EQ( SDB_OK, rc ) ; rc = getCollection ( connection, COLLECTION_FULL_NAME, &collection ) ; ASSERT_EQ( SDB_OK, rc ) ; insertRecords( collection, NUM ) ; rc = sdbQuery ( collection, NULL, NULL, NULL, NULL, 0, -1, &cursor ) ; ASSERT_EQ( SDB_OK, rc ) ; bson_init(&obj); rc = sdbCurrent( cursor, &obj ) ; printf( "Current record is:\n" ) ; bson_print( &obj ) ; printf("\n") ; bson_destroy( &obj ) ; bson_init(&obj); rc = sdbNext( cursor, &obj ) ; ASSERT_EQ( SDB_OK, rc ) ; printf( "Next record is:\n" ) ; bson_print( &obj ) ; printf("\n") ; bson_destroy(&obj); rc = sdbGetCount( collection, NULL, &count ) ; CHECK_MSG("%s%d\n","rc = ",rc) ; CHECK_MSG("%s%d%s%lld\n", "NUM = ", NUM, " count = ", count) ; ASSERT_EQ ( NUM, count ) ; sdbDisconnect ( connection ) ; sdbReleaseCursor ( cursor ) ; sdbReleaseCollection ( collection ) ; sdbReleaseConnection ( connection ) ; }
void Database::_checkCanCreateCollection(const NamespaceString& nss, const CollectionOptions& options) { massert(17399, "collection already exists", getCollection(nss.ns()) == nullptr); massertNamespaceNotIndex(nss.ns(), "createCollection"); uassert(14037, "can't create user databases on a --configsvr instance", serverGlobalParams.clusterRole != ClusterRole::ConfigServer || nss.isOnInternalDb()); // This check only applies for actual collections, not indexes or other types of ns. uassert(17381, str::stream() << "fully qualified namespace " << nss.ns() << " is too long " << "(max is " << NamespaceString::MaxNsCollectionLen << " bytes)", !nss.isNormal() || nss.size() <= NamespaceString::MaxNsCollectionLen); uassert(17316, "cannot create a blank collection", nss.coll() > 0); uassert(28838, "cannot create a non-capped oplog collection", options.capped || !nss.isOplog()); }
// on input, conn is a connection for which the caller has created a multi-statement // mvcc transaction over it. Reads the document from the remote server and // applies it locally void applySnapshotOfDocsMap(shared_ptr<DBClientConnection> conn) { size_t numDocs = 0; log() << "Applying documents to collections for rollback." << rsLog; for (RollbackDocsMapIterator it; it.ok(); it.advance()){ numDocs++; DocID curr = it.current(); LOCK_REASON(lockReason, "repl: appling snapshot of doc during rollback"); Client::ReadContext ctx(curr.ns, lockReason); Collection* cl = getCollection(curr.ns); if (cl->isPKHidden()) { log() << "Collection " << curr.ns << " has a hidden PK, yet it has \ a document for which we want to apply a snapshot of: " << \ curr.pk << rsLog; throw RollbackOplogException("Collection for which we are applying a document has a hidden PK"); } BSONObj pkWithFields = cl->fillPKWithFields(curr.pk); BSONObj remoteImage = findOneFromConn(conn.get(), curr.ns, Query(pkWithFields)); if (!remoteImage.isEmpty()) { const uint64_t flags = Collection::NO_UNIQUE_CHECKS | Collection::NO_LOCKTREE; insertOneObject(cl, remoteImage, flags); } }
void Database::clearTmpCollections(OperationContext* txn) { txn->lockState()->assertWriteLocked( _name ); // Note: we build up a toDelete vector rather than dropping the collection inside the loop // to avoid modifying the system.namespaces collection while iterating over it since that // would corrupt the cursor. vector<string> toDelete; { Collection* coll = getCollection( txn, _namespacesName ); if ( coll ) { scoped_ptr<RecordIterator> it( coll->getIterator() ); DiskLoc next; while ( !( next = it->getNext() ).isNull() ) { BSONObj nsObj = coll->docFor( next ); BSONElement e = nsObj.getFieldDotted( "options.temp" ); if ( !e.trueValue() ) continue; string ns = nsObj["name"].String(); // Do not attempt to drop indexes if ( !NamespaceString::normal(ns.c_str()) ) continue; toDelete.push_back(ns); } } } for (size_t i=0; i < toDelete.size(); i++) { BSONObj info; // using DBDirectClient to ensure this ends up in opLog bool ok = DBDirectClient().dropCollection(toDelete[i], &info); if (!ok) warning() << "could not drop temp collection '" << toDelete[i] << "': " << info; } }
TEST(cursor,sdbCloseCursor) { INT32 rc = SDB_OK ; rc = initEnv( HOST, SERVER, USER, PASSWD ) ; ASSERT_EQ( SDB_OK, rc ) ; sdbConnectionHandle connection = 0 ; sdbCollectionHandle collection = 0 ; sdbCursorHandle cursor = 0 ; INT32 NUM = 10 ; bson obj ; bson obj1 ; rc = sdbConnect ( HOST, SERVER, USER, PASSWD, &connection ) ; ASSERT_EQ( SDB_OK, rc ) ; rc = getCollection ( connection, COLLECTION_FULL_NAME, &collection ) ; ASSERT_EQ( SDB_OK, rc ) ; insertRecords ( collection, NUM ) ; rc = sdbQuery( collection, NULL, NULL, NULL, NULL, 0, -1, &cursor ) ; bson_init(&obj); rc = sdbCurrent( cursor, &obj ) ; ASSERT_EQ( SDB_OK, rc ) ; printf( "Current record is:\n" ) ; bson_print( &obj ) ; printf("\n") ; bson_destroy( &obj ) ; rc = sdbCloseCursor( cursor ) ; ASSERT_EQ( SDB_OK, rc ) ; bson_init( &obj1 ) ; rc = sdbCurrent( cursor, &obj1 ) ; ASSERT_EQ( rc, SDB_DMS_CONTEXT_IS_CLOSE ) ; sdbDisconnect ( connection ) ; sdbReleaseCursor ( cursor ) ; sdbReleaseCollection ( collection ) ; sdbReleaseConnection ( connection ) ; }
bool touch( std::string& ns, std::string& errmsg, bool touch_data, bool touch_indexes, BSONObjBuilder& result ) { Collection *cl = getCollection(ns); if (!cl) { errmsg = "ns not found"; return false; } for (int i = 0; i < cl->nIndexes(); i++) { IndexDetails &idx = cl->idx(i); if ((cl->isPKIndex(idx) && touch_data) || (!cl->isPKIndex(idx) && touch_indexes)) { for (shared_ptr<Cursor> c(Cursor::make(cl, idx, minKey, maxKey, true, 1)); c->ok(); c->advance()) { c->current(); } } } return true; }
bool run( const string& db, BSONObj& cmdObj, int options, string& errmsg, BSONObjBuilder& result, bool fromRepl = false ) { string coll = cmdObj[ "_changePartitionCreateTime" ].valuestrsafe(); uassert( 17263, "_changePartitionCreateTime must specify a collection", !coll.empty() ); string ns = db + "." + coll; Collection *cl = getCollection( ns ); uassert( 17264, "no such collection", cl ); uassert( 17265, "collection must be partitioned", cl->isPartitioned() ); // change the create time for a partition at a certain index PartitionedCollection* pc = cl->as<PartitionedCollection>(); uint64_t index = cmdObj["index"].numberLong(); BSONObj refMeta = pc->getPartitionMetadata(index); BSONObjBuilder bbb; cloneBSONWithFieldChanged(bbb, refMeta, cmdObj["createTime"]); pc->updatePartitionMetadata(index, bbb.done(), false); return true; }
void removeDataFromDocsMap() { Client::Transaction txn(DB_SERIALIZABLE); RollbackDocsMapIterator docsMap; size_t numDocs = 0; log() << "Removing documents from collections for rollback." << rsLog; for (RollbackDocsMapIterator it; it.ok(); it.advance()){ numDocs++; DocID curr = it.current(); LOCK_REASON(lockReason, "repl: deleting a doc during rollback"); Client::ReadContext ctx(curr.ns, lockReason); Collection* cl = getCollection(curr.ns); verify(cl); BSONObj currDoc; LOG(2) << "Finding by pk of " << curr.pk << rsLog; bool found = cl->findByPK(curr.pk, currDoc); if (found) { deleteOneObject(cl, curr.pk, currDoc, Collection::NO_LOCKTREE); } } log() << "Done removing " << numDocs << " documents from collections for rollback." << rsLog; updateRollbackStatus(BSON("_id" << ROLLBACK_ID << "state" << RB_DOCS_REMOVED<< \ "info" << "removed docs from docs map")); txn.commit(DB_TXN_NOSYNC); }
Status Database::dropCollection(OperationContext* opCtx, StringData fullns) { if (!getCollection(fullns)) { // Collection doesn't exist so don't bother validating if it can be dropped. return Status::OK(); } NamespaceString nss(fullns); { verify(nss.db() == _name); if (nss.isSystem()) { if (nss.isSystemDotProfile()) { if (_profile != 0) return Status(ErrorCodes::IllegalOperation, "turn off profiling before dropping system.profile collection"); } else if (!nss.isSystemDotViews()) { return Status(ErrorCodes::IllegalOperation, str::stream() << "can't drop system collection " << fullns); } } } return dropCollectionEvenIfSystem(opCtx, nss); }
Status Database::dropCollection(OperationContext* txn, StringData fullns) { invariant(txn->lockState()->isDbLockedForMode(name(), MODE_X)); LOG(1) << "dropCollection: " << fullns; massertNamespaceNotIndex(fullns, "dropCollection"); Collection* collection = getCollection(fullns); if (!collection) { // collection doesn't exist return Status::OK(); } NamespaceString nss(fullns); { verify(nss.db() == _name); if (nss.isSystem()) { if (nss.isSystemDotProfile()) { if (_profile != 0) return Status(ErrorCodes::IllegalOperation, "turn off profiling before dropping system.profile collection"); } else if (nss.isSystemDotViews()) { if (serverGlobalParams.featureCompatibilityVersion.load() != ServerGlobalParams::FeatureCompatibilityVersion_32) { return Status(ErrorCodes::IllegalOperation, "The featureCompatibilityVersion must be 3.2 to drop the " "system.views collection. See " "http://dochub.mongodb.org/core/3.4-feature-compatibility."); } } else { return Status(ErrorCodes::IllegalOperation, "can't drop system ns"); } } } BackgroundOperation::assertNoBgOpInProgForNs(fullns); audit::logDropCollection(&cc(), fullns); Status s = collection->getIndexCatalog()->dropAllIndexes(txn, true); if (!s.isOK()) { warning() << "could not drop collection, trying to drop indexes" << fullns << " because of " << redact(s.toString()); return s; } verify(collection->_details->getTotalIndexCount(txn) == 0); LOG(1) << "\t dropIndexes done"; Top::get(txn->getClient()->getServiceContext()).collectionDropped(fullns); s = _dbEntry->dropCollection(txn, fullns); // we want to do this always _clearCollectionCache(txn, fullns, "collection dropped"); if (!s.isOK()) return s; DEV { // check all index collection entries are gone string nstocheck = fullns.toString() + ".$"; for (CollectionMap::const_iterator i = _collections.begin(); i != _collections.end(); ++i) { string temp = i->first; if (temp.find(nstocheck) != 0) continue; log() << "after drop, bad cache entries for: " << fullns << " have " << temp; verify(0); } } auto opObserver = getGlobalServiceContext()->getOpObserver(); if (opObserver) opObserver->onDropCollection(txn, nss); return Status::OK(); }
Status Database::dropCollection( const StringData& fullns ) { LOG(1) << "dropCollection: " << fullns << endl; Collection* collection = getCollection( fullns ); if ( !collection ) { // collection doesn't exist return Status::OK(); } _initForWrites(); { NamespaceString s( fullns ); verify( s.db() == _name ); if( s.isSystem() ) { if( s.coll() == "system.profile" ) { if ( _profile != 0 ) return Status( ErrorCodes::IllegalOperation, "turn off profiling before dropping system.profile collection" ); } else { return Status( ErrorCodes::IllegalOperation, "can't drop system ns" ); } } } BackgroundOperation::assertNoBgOpInProgForNs( fullns ); audit::logDropCollection( currentClient.get(), fullns ); try { Status s = collection->getIndexCatalog()->dropAllIndexes( true ); if ( !s.isOK() ) { warning() << "could not drop collection, trying to drop indexes" << fullns << " because of " << s.toString(); return s; } } catch( DBException& e ) { stringstream ss; ss << "drop: dropIndexes for collection failed - consider trying repair "; ss << " cause: " << e.what(); warning() << ss.str() << endl; return Status( ErrorCodes::InternalError, ss.str() ); } verify( collection->_details->getTotalIndexCount() == 0 ); LOG(1) << "\t dropIndexes done" << endl; ClientCursor::invalidate( fullns ); Top::global.collectionDropped( fullns ); Status s = _dropNS( fullns ); _clearCollectionCache( fullns ); // we want to do this always if ( !s.isOK() ) return s; DEV { // check all index collection entries are gone string nstocheck = fullns.toString() + ".$"; scoped_lock lk( _collectionLock ); for ( CollectionMap::iterator i = _collections.begin(); i != _collections.end(); ++i ) { string temp = i->first; if ( temp.find( nstocheck ) != 0 ) continue; log() << "after drop, bad cache entries for: " << fullns << " have " << temp; verify(0); } } return Status::OK(); }
result_t MongoCollection::_named_getter(exlib::string property, obj_ptr<MongoCollection_base>& retVal) { return getCollection(property, retVal); }
bool run(const string& dbname, BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool fromRepl ) { Timer t; string ns = dbname + '.' + cmdObj.firstElement().valuestr(); string key = cmdObj["key"].valuestrsafe(); BSONObj keyPattern = BSON( key << 1 ); BSONObj query = getQuery( cmdObj ); int bufSize = BSONObjMaxUserSize - 4096; BufBuilder bb( bufSize ); char * start = bb.buf(); BSONArrayBuilder arr( bb ); BSONElementSet values; long long nscanned = 0; // locations looked at long long nscannedObjects = 0; // full objects looked at long long n = 0; // matches MatchDetails md; Collection *cl = getCollection( ns ); if ( ! cl ) { result.appendArray( "values" , BSONObj() ); result.append( "stats" , BSON( "n" << 0 << "nscanned" << 0 << "nscannedObjects" << 0 ) ); return true; } shared_ptr<Cursor> cursor; if ( ! query.isEmpty() ) { cursor = getOptimizedCursor(ns.c_str() , query , BSONObj() ); } else { // query is empty, so lets see if we can find an index // with the key so we don't have to hit the raw data for (int i = 0; i < cl->nIndexes(); i++) { IndexDetails &idx = cl->idx(i); if (cl->isMultikey(i)) { continue; } if ( idx.inKeyPattern( key ) ) { cursor = getBestGuessCursor( ns.c_str() , BSONObj() , idx.keyPattern() ); if( cursor.get() ) break; } } if ( ! cursor.get() ) { cursor = getOptimizedCursor(ns.c_str() , query , BSONObj() ); } } verify( cursor ); string cursorName = cursor->toString(); auto_ptr<ClientCursor> cc (new ClientCursor(QueryOption_NoCursorTimeout, cursor, ns)); for ( ; cursor->ok(); cursor->advance() ) { nscanned++; bool loadedRecord = false; if ( cursor->currentMatches( &md ) && !cursor->getsetdup( cursor->currPK() ) ) { n++; BSONObj holder; BSONElementSet temp; loadedRecord = ! cc->getFieldsDotted( key , temp, holder ); for ( BSONElementSet::iterator i=temp.begin(); i!=temp.end(); ++i ) { BSONElement e = *i; if ( values.count( e ) ) continue; int now = bb.len(); uassert(10044, "distinct too big, 16mb cap", ( now + e.size() + 1024 ) < bufSize ); arr.append( e ); BSONElement x( start + now ); values.insert( x ); } } if ( loadedRecord || md.hasLoadedRecord() ) nscannedObjects++; RARELY killCurrentOp.checkForInterrupt(); } verify( start == bb.buf() ); result.appendArray( "values" , arr.done() ); { BSONObjBuilder b; b.appendNumber( "n" , n ); b.appendNumber( "nscanned" , nscanned ); b.appendNumber( "nscannedObjects" , nscannedObjects ); b.appendNumber( "timems" , t.millis() ); b.append( "cursor" , cursorName ); result.append( "stats" , b.obj() ); } return true; }
Status Database::dropCollection( OperationContext* txn, StringData fullns ) { invariant(txn->lockState()->isDbLockedForMode(name(), MODE_X)); LOG(1) << "dropCollection: " << fullns << endl; massertNamespaceNotIndex( fullns, "dropCollection" ); Collection* collection = getCollection( fullns ); if ( !collection ) { // collection doesn't exist return Status::OK(); } { NamespaceString s( fullns ); verify( s.db() == _name ); if( s.isSystem() ) { if( s.coll() == "system.profile" ) { if ( _profile != 0 ) return Status( ErrorCodes::IllegalOperation, "turn off profiling before dropping system.profile collection" ); } else { return Status( ErrorCodes::IllegalOperation, "can't drop system ns" ); } } } BackgroundOperation::assertNoBgOpInProgForNs( fullns ); audit::logDropCollection( currentClient.get(), fullns ); Status s = collection->getIndexCatalog()->dropAllIndexes(txn, true); if ( !s.isOK() ) { warning() << "could not drop collection, trying to drop indexes" << fullns << " because of " << s.toString(); return s; } verify( collection->_details->getTotalIndexCount( txn ) == 0 ); LOG(1) << "\t dropIndexes done" << endl; Top::global.collectionDropped( fullns ); s = _dbEntry->dropCollection( txn, fullns ); _clearCollectionCache( txn, fullns ); // we want to do this always if ( !s.isOK() ) return s; DEV { // check all index collection entries are gone string nstocheck = fullns.toString() + ".$"; for ( CollectionMap::const_iterator i = _collections.begin(); i != _collections.end(); ++i ) { string temp = i->first; if ( temp.find( nstocheck ) != 0 ) continue; log() << "after drop, bad cache entries for: " << fullns << " have " << temp; verify(0); } } return Status::OK(); }
void DBInfo::getProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleValue vp) { // 2nd look into real values, may be cached collection object if (!vp.isUndefined()) { auto scope = getScope(cx); auto opContext = scope->getOpContext(); if (opContext && vp.isObject()) { ObjectWrapper o(cx, vp); if (o.hasOwnField(InternedString::_fullName)) { // need to check every time that the collection did not get sharded if (haveLocalShardingInfo(opContext, o.getString(InternedString::_fullName))) uasserted(ErrorCodes::BadValue, "can't use sharded collection from db.eval"); } } return; } JS::RootedObject parent(cx); if (!JS_GetPrototype(cx, obj, &parent)) uasserted(ErrorCodes::JSInterpreterFailure, "Couldn't get prototype"); ObjectWrapper parentWrapper(cx, parent); if (parentWrapper.hasOwnField(id)) { parentWrapper.getValue(id, vp); return; } IdWrapper idw(cx, id); // if starts with '_' we dont return collection, one must use getCollection() if (idw.isString()) { JSStringWrapper jsstr; auto sname = idw.toStringData(&jsstr); if (sname.size() == 0 || sname[0] == '_') { return; } } // no hit, create new collection JS::RootedValue getCollection(cx); parentWrapper.getValue(InternedString::getCollection, &getCollection); if (!(getCollection.isObject() && JS_ObjectIsFunction(cx, getCollection.toObjectOrNull()))) { uasserted(ErrorCodes::BadValue, "getCollection is not a function"); } JS::AutoValueArray<1> args(cx); idw.toValue(args[0]); JS::RootedValue coll(cx); ObjectWrapper(cx, obj).callMethod(getCollection, args, &coll); uassert(16861, "getCollection returned something other than a collection", getScope(cx)->getProto<DBCollectionInfo>().instanceOf(coll)); // cache collection for reuse, don't enumerate ObjectWrapper(cx, obj).defineProperty(id, coll, 0); vp.set(coll); }
Status createCollectionForApplyOps(OperationContext* opCtx, const std::string& dbName, const BSONElement& ui, const BSONObj& cmdObj, const BSONObj& idIndex) { invariant(opCtx->lockState()->isDbLockedForMode(dbName, MODE_X)); auto db = dbHolder().get(opCtx, dbName); const NamespaceString newCollName(Command::parseNsCollectionRequired(dbName, cmdObj)); auto newCmd = cmdObj; // If a UUID is given, see if we need to rename a collection out of the way, and whether the // collection already exists under a different name. If so, rename it into place. As this is // done during replay of the oplog, the operations do not need to be atomic, just idempotent. // We need to do the renaming part in a separate transaction, as we cannot transactionally // create a database on MMAPv1, which could result in createCollection failing if the database // does not yet exist. if (ui.ok()) { // Return an optional, indicating whether we need to early return (if the collection already // exists, or in case of an error). using Result = boost::optional<Status>; auto result = writeConflictRetry(opCtx, "createCollectionForApplyOps", newCollName.ns(), [&] { WriteUnitOfWork wunit(opCtx); // Options need the field to be named "uuid", so parse/recreate. auto uuid = uassertStatusOK(UUID::parse(ui)); uassert(ErrorCodes::InvalidUUID, "Invalid UUID in applyOps create command: " + uuid.toString(), uuid.isRFC4122v4()); auto& catalog = UUIDCatalog::get(opCtx); auto currentName = catalog.lookupNSSByUUID(uuid); OpObserver* opObserver = getGlobalServiceContext()->getOpObserver(); if (currentName == newCollName) return Result(Status::OK()); // In the case of oplog replay, a future command may have created or renamed a // collection with that same name. In that case, renaming this future collection to // a random temporary name is correct: once all entries are replayed no temporary // names will remain. On MMAPv1 the rename can result in index names that are too // long. However this should only happen for initial sync and "resync collection" // for rollback, so we can let the error propagate resulting in an abort and restart // of the initial sync or result in rollback to fassert, requiring a resync of that // node. const bool stayTemp = true; if (auto futureColl = db ? db->getCollection(opCtx, newCollName) : nullptr) { auto tmpNameResult = db->makeUniqueCollectionNamespace(opCtx, "tmp%%%%%"); if (!tmpNameResult.isOK()) { return Result(Status(tmpNameResult.getStatus().code(), str::stream() << "Cannot generate temporary " "collection namespace for applyOps " "create command: collection: " << newCollName.ns() << ". error: " << tmpNameResult.getStatus().reason())); } const auto& tmpName = tmpNameResult.getValue(); Status status = db->renameCollection(opCtx, newCollName.ns(), tmpName.ns(), stayTemp); if (!status.isOK()) return Result(status); opObserver->onRenameCollection(opCtx, newCollName, tmpName, futureColl->uuid(), /*dropTarget*/ false, /*dropTargetUUID*/ {}, stayTemp); } // If the collection with the requested UUID already exists, but with a different // name, just rename it to 'newCollName'. if (catalog.lookupCollectionByUUID(uuid)) { Status status = db->renameCollection(opCtx, currentName.ns(), newCollName.ns(), stayTemp); if (!status.isOK()) return Result(status); opObserver->onRenameCollection(opCtx, currentName, newCollName, uuid, /*dropTarget*/ false, /*dropTargetUUID*/ {}, stayTemp); wunit.commit(); return Result(Status::OK()); } // A new collection with the specific UUID must be created, so add the UUID to the // creation options. Regular user collection creation commands cannot do this. auto uuidObj = uuid.toBSON(); newCmd = cmdObj.addField(uuidObj.firstElement()); wunit.commit(); return Result(boost::none); }); if (result) { return *result; } } return createCollection( opCtx, newCollName, newCmd, idIndex, CollectionOptions::parseForStorage); }
void Database::getStats( OperationContext* opCtx, BSONObjBuilder* output, double scale ) { bool empty = isEmpty() || getExtentManager()->numFiles() == 0; list<string> collections; if ( !empty ) _dbEntry->getCollectionNamespaces( &collections ); long long ncollections = 0; long long objects = 0; long long size = 0; long long storageSize = 0; long long numExtents = 0; long long indexes = 0; long long indexSize = 0; for (list<string>::const_iterator it = collections.begin(); it != collections.end(); ++it) { const string ns = *it; Collection* collection = getCollection( opCtx, ns ); if ( !collection ) continue; ncollections += 1; objects += collection->numRecords(); size += collection->dataSize(); BSONObjBuilder temp; storageSize += collection->getRecordStore()->storageSize( &temp ); numExtents += temp.obj()["numExtents"].numberInt(); // XXX indexes += collection->getIndexCatalog()->numIndexesTotal(); indexSize += getIndexSizeForCollection(opCtx, collection); } output->append ( "db" , _name ); output->appendNumber( "collections" , ncollections ); output->appendNumber( "objects" , objects ); output->append ( "avgObjSize" , objects == 0 ? 0 : double(size) / double(objects) ); output->appendNumber( "dataSize" , size / scale ); output->appendNumber( "storageSize" , storageSize / scale); output->appendNumber( "numExtents" , numExtents ); output->appendNumber( "indexes" , indexes ); output->appendNumber( "indexSize" , indexSize / scale ); if ( !empty ) { output->appendNumber( "fileSize" , fileSize() / scale ); output->appendNumber( "nsSizeMB", (int)_dbEntry->namespaceIndex().fileLength() / 1024 / 1024 ); } else { output->appendNumber( "fileSize" , 0 ); } BSONObjBuilder dataFileVersion( output->subobjStart( "dataFileVersion" ) ); if ( !empty ) { int major, minor; getFileFormat( opCtx, &major, &minor ); dataFileVersion.append( "major", major ); dataFileVersion.append( "minor", minor ); } dataFileVersion.done(); if ( !empty ){ int freeListSize = 0; int64_t freeListSpace = 0; getExtentManager()->freeListStats( &freeListSize, &freeListSpace ); BSONObjBuilder extentFreeList( output->subobjStart( "extentFreeList" ) ); extentFreeList.append( "num", freeListSize ); extentFreeList.appendNumber( "totalSize", static_cast<long long>( freeListSpace / scale ) ); extentFreeList.done(); } }
Collection* Database::getCollection( const StringData& ns ) { DurTransaction txn; // TODO remove once we require reads to have transactions return getCollection(&txn, ns); }
void syncFixUp(OperationContext* txn, FixUpInfo& fixUpInfo, OplogReader* oplogreader, ReplicationCoordinator* replCoord) { DBClientConnection* them = oplogreader->conn(); // fetch all first so we needn't handle interruption in a fancy way unsigned long long totalSize = 0; list< pair<DocID, BSONObj> > goodVersions; BSONObj newMinValid; // fetch all the goodVersions of each document from current primary DocID doc; unsigned long long numFetched = 0; try { for (set<DocID>::iterator it = fixUpInfo.toRefetch.begin(); it != fixUpInfo.toRefetch.end(); it++) { doc = *it; verify(!doc._id.eoo()); { // TODO : slow. lots of round trips. numFetched++; BSONObj good = them->findOne(doc.ns, doc._id.wrap(), NULL, QueryOption_SlaveOk).getOwned(); totalSize += good.objsize(); uassert(13410, "replSet too much data to roll back", totalSize < 300 * 1024 * 1024); // note good might be eoo, indicating we should delete it goodVersions.push_back(pair<DocID, BSONObj>(doc,good)); } } newMinValid = oplogreader->getLastOp(rsOplogName); if (newMinValid.isEmpty()) { error() << "rollback error newMinValid empty?"; return; } } catch (DBException& e) { LOG(1) << "rollback re-get objects: " << e.toString(); error() << "rollback couldn't re-get ns:" << doc.ns << " _id:" << doc._id << ' ' << numFetched << '/' << fixUpInfo.toRefetch.size(); throw e; } log() << "rollback 3.5"; if (fixUpInfo.rbid != getRBID(oplogreader->conn())) { // our source rolled back itself. so the data we received isn't necessarily consistent. warning() << "rollback rbid on source changed during rollback, cancelling this attempt"; return; } // update them log() << "rollback 4 n:" << goodVersions.size(); bool warn = false; invariant(!fixUpInfo.commonPointOurDiskloc.isNull()); invariant(txn->lockState()->isW()); // we have items we are writing that aren't from a point-in-time. thus best not to come // online until we get to that point in freshness. Timestamp minValid = newMinValid["ts"].timestamp(); log() << "minvalid=" << minValid.toStringLong(); setMinValid(txn, minValid); // any full collection resyncs required? if (!fixUpInfo.collectionsToResyncData.empty() || !fixUpInfo.collectionsToResyncMetadata.empty()) { for (const string& ns : fixUpInfo.collectionsToResyncData) { log() << "rollback 4.1.1 coll resync " << ns; fixUpInfo.collectionsToResyncMetadata.erase(ns); const NamespaceString nss(ns); Database* db = dbHolder().openDb(txn, nss.db().toString()); invariant(db); { WriteUnitOfWork wunit(txn); db->dropCollection(txn, ns); wunit.commit(); } { string errmsg; // This comes as a GlobalWrite lock, so there is no DB to be acquired after // resume, so we can skip the DB stability checks. Also // copyCollectionFromRemote will acquire its own database pointer, under the // appropriate locks, so just releasing and acquiring the lock is safe. invariant(txn->lockState()->isW()); Lock::TempRelease release(txn->lockState()); bool ok = copyCollectionFromRemote(txn, them->getServerAddress(), ns, errmsg); uassert(15909, str::stream() << "replSet rollback error resyncing collection " << ns << ' ' << errmsg, ok); } } for (const string& ns : fixUpInfo.collectionsToResyncMetadata) { log() << "rollback 4.1.2 coll metadata resync " << ns; const NamespaceString nss(ns); auto db = dbHolder().openDb(txn, nss.db().toString()); invariant(db); auto collection = db->getCollection(ns); invariant(collection); auto cce = collection->getCatalogEntry(); const std::list<BSONObj> info = them->getCollectionInfos(nss.db().toString(), BSON("name" << nss.coll())); if (info.empty()) { // Collection dropped by "them" so we should drop it too. log() << ns << " not found on remote host, dropping"; fixUpInfo.toDrop.insert(ns); continue; } invariant(info.size() == 1); CollectionOptions options; auto status = options.parse(info.front()); if (!status.isOK()) { throw RSFatalException(str::stream() << "Failed to parse options " << info.front() << ": " << status.toString()); } WriteUnitOfWork wuow(txn); if (options.flagsSet || cce->getCollectionOptions(txn).flagsSet) { cce->updateFlags(txn, options.flags); } status = collection->setValidator(txn, options.validator); if (!status.isOK()) { throw RSFatalException(str::stream() << "Failed to set validator: " << status.toString()); } wuow.commit(); } // we did more reading from primary, so check it again for a rollback (which would mess // us up), and make minValid newer. log() << "rollback 4.2"; string err; try { newMinValid = oplogreader->getLastOp(rsOplogName); if (newMinValid.isEmpty()) { err = "can't get minvalid from sync source"; } else { Timestamp minValid = newMinValid["ts"].timestamp(); log() << "minvalid=" << minValid.toStringLong(); setMinValid(txn, minValid); } } catch (DBException& e) { err = "can't get/set minvalid: "; err += e.what(); } if (fixUpInfo.rbid != getRBID(oplogreader->conn())) { // our source rolled back itself. so the data we received isn't necessarily // consistent. however, we've now done writes. thus we have a problem. err += "rbid at primary changed during resync/rollback"; } if (!err.empty()) { severe() << "rolling back : " << err << ". A full resync will be necessary."; // TODO: reset minvalid so that we are permanently in fatal state // TODO: don't be fatal, but rather, get all the data first. throw RSFatalException(); } log() << "rollback 4.3"; } map<string,shared_ptr<Helpers::RemoveSaver> > removeSavers; log() << "rollback 4.6"; // drop collections to drop before doing individual fixups - that might make things faster // below actually if there were subsequent inserts to rollback for (set<string>::iterator it = fixUpInfo.toDrop.begin(); it != fixUpInfo.toDrop.end(); it++) { log() << "rollback drop: " << *it; Database* db = dbHolder().get(txn, nsToDatabaseSubstring(*it)); if (db) { WriteUnitOfWork wunit(txn); shared_ptr<Helpers::RemoveSaver>& removeSaver = removeSavers[*it]; if (!removeSaver) removeSaver.reset(new Helpers::RemoveSaver("rollback", "", *it)); // perform a collection scan and write all documents in the collection to disk boost::scoped_ptr<PlanExecutor> exec( InternalPlanner::collectionScan(txn, *it, db->getCollection(*it))); BSONObj curObj; PlanExecutor::ExecState execState; while (PlanExecutor::ADVANCED == (execState = exec->getNext(&curObj, NULL))) { removeSaver->goingToDelete(curObj); } if (execState != PlanExecutor::IS_EOF) { if (execState == PlanExecutor::FAILURE && WorkingSetCommon::isValidStatusMemberObject(curObj)) { Status errorStatus = WorkingSetCommon::getMemberObjectStatus(curObj); severe() << "rolling back createCollection on " << *it << " failed with " << errorStatus << ". A full resync is necessary."; } else { severe() << "rolling back createCollection on " << *it << " failed. A full resync is necessary."; } throw RSFatalException(); } db->dropCollection(txn, *it); wunit.commit(); } } log() << "rollback 4.7"; OldClientContext ctx(txn, rsOplogName); Collection* oplogCollection = ctx.db()->getCollection(rsOplogName); uassert(13423, str::stream() << "replSet error in rollback can't find " << rsOplogName, oplogCollection); unsigned deletes = 0, updates = 0; time_t lastProgressUpdate = time(0); time_t progressUpdateGap = 10; for (list<pair<DocID, BSONObj> >::iterator it = goodVersions.begin(); it != goodVersions.end(); it++) { time_t now = time(0); if (now - lastProgressUpdate > progressUpdateGap) { log() << deletes << " delete and " << updates << " update operations processed out of " << goodVersions.size() << " total operations"; lastProgressUpdate = now; } const DocID& doc = it->first; BSONObj pattern = doc._id.wrap(); // { _id : ... } try { verify(doc.ns && *doc.ns); if (fixUpInfo.collectionsToResyncData.count(doc.ns)) { // we just synced this entire collection continue; } // keep an archive of items rolled back shared_ptr<Helpers::RemoveSaver>& removeSaver = removeSavers[doc.ns]; if (!removeSaver) removeSaver.reset(new Helpers::RemoveSaver("rollback", "", doc.ns)); // todo: lots of overhead in context, this can be faster OldClientContext ctx(txn, doc.ns); // Add the doc to our rollback file BSONObj obj; Collection* collection = ctx.db()->getCollection(doc.ns); // Do not log an error when undoing an insert on a no longer existent collection. // It is likely that the collection was dropped as part of rolling back a // createCollection command and regardless, the document no longer exists. if (collection) { bool found = Helpers::findOne(txn, collection, pattern, obj, false); if (found) { removeSaver->goingToDelete(obj); } else { error() << "rollback cannot find object: " << pattern << " in namespace " << doc.ns; } } if (it->second.isEmpty()) { // wasn't on the primary; delete. // TODO 1.6 : can't delete from a capped collection. need to handle that here. deletes++; if (collection) { if (collection->isCapped()) { // can't delete from a capped collection - so we truncate instead. if // this item must go, so must all successors!!! try { // TODO: IIRC cappedTruncateAfter does not handle completely empty. // this will crazy slow if no _id index. long long start = Listener::getElapsedTimeMillis(); RecordId loc = Helpers::findOne(txn, collection, pattern, false); if (Listener::getElapsedTimeMillis() - start > 200) warning() << "roll back slow no _id index for " << doc.ns << " perhaps?"; // would be faster but requires index: // RecordId loc = Helpers::findById(nsd, pattern); if (!loc.isNull()) { try { collection->temp_cappedTruncateAfter(txn, loc, true); } catch (DBException& e) { if (e.getCode() == 13415) { // hack: need to just make cappedTruncate do this... MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN { WriteUnitOfWork wunit(txn); uassertStatusOK(collection->truncate(txn)); wunit.commit(); } MONGO_WRITE_CONFLICT_RETRY_LOOP_END( txn, "truncate", collection->ns().ns()); } else { throw e; } } } } catch (DBException& e) { error() << "rolling back capped collection rec " << doc.ns << ' ' << e.toString(); } } else {