// kv_catalog.cpp /** * Copyright (C) 2014 MongoDB Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the GNU Affero General Public License in all respects for * all of the code used other than as permitted herein. If you modify file(s) * with this exception, you may extend this exception to your version of the * file(s), but you are not obligated to do so. If you do not wish to do so, * delete this exception statement from your version. If you delete this * exception statement from all source files in the program, then also delete * it in the license file. */ #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kStorage #include "mongo/db/storage/kv/kv_catalog.h" #include #include "mongo/db/storage/record_store.h" #include "mongo/platform/random.h" #include "mongo/util/log.h" namespace mongo { KVCatalog::KVCatalog( RecordStore* rs ) : _rs( rs ) { boost::scoped_ptr r( SecureRandom::create() ); _rand = r->nextInt64(); } KVCatalog::~KVCatalog() { _rs = NULL; } void KVCatalog::init( OperationContext* opCtx ) { scoped_ptr it( _rs->getIterator( opCtx ) ); while ( !it->isEOF() ) { DiskLoc loc = it->getNext(); RecordData data = it->dataFor( loc ); BSONObj obj( data.data() ); // no locking needed since can only be one string ns = obj["ns"].String(); string ident = obj["ident"].String(); _idents[ns] = Entry( ident, loc ); } } void KVCatalog::getAllCollections( std::vector* out ) const { boost::mutex::scoped_lock lk( _identsLock ); for ( NSToIdentMap::const_iterator it = _idents.begin(); it != _idents.end(); ++it ) { out->push_back( it->first ); } } Status KVCatalog::newCollection( OperationContext* opCtx, const StringData& ns, const CollectionOptions& options ) { std::stringstream ss; ss << "collection-" << _rand << "-" << _next.fetchAndAdd( 1 ); string ident = ss.str(); boost::mutex::scoped_lock lk( _identsLock ); Entry& old = _idents[ns.toString()]; if ( !old.ident.empty() ) { return Status( ErrorCodes::NamespaceExists, "collection already exists" ); } BSONObj obj; { BSONObjBuilder b; b.append( "ns", ns ); b.append( "ident", ident ); BSONCollectionCatalogEntry::MetaData md; md.ns = ns.toString(); md.options = options; b.append( "md", md.toBSON() ); obj = b.obj(); } StatusWith res = _rs->insertRecord( opCtx, obj.objdata(), obj.objsize(), false ); if ( !res.isOK() ) return res.getStatus(); old = Entry( ident, res.getValue() ); LOG(1) << "stored meta data for " << ns << " @ " << res.getValue(); return Status::OK(); } std::string KVCatalog::getCollectionIdent( const StringData& ns ) const { boost::mutex::scoped_lock lk( _identsLock ); NSToIdentMap::const_iterator it = _idents.find( ns.toString() ); invariant( it != _idents.end() ); return it->second.ident; } std::string KVCatalog::getIndexIdent( OperationContext* opCtx, const StringData& ns, const StringData& idxName ) const { DiskLoc loc; BSONObj obj = _findEntry( opCtx, ns, &loc ); BSONObj idxIdent = obj["idxIdent"].Obj(); return idxIdent[idxName].String(); } BSONObj KVCatalog::_findEntry( OperationContext* opCtx, const StringData& ns, DiskLoc* out ) const { { boost::mutex::scoped_lock lk( _identsLock ); NSToIdentMap::const_iterator it = _idents.find( ns.toString() ); invariant( it != _idents.end() ); *out = it->second.storedLoc; } LOG(1) << "looking up metadata for: " << ns << " @ " << *out; RecordData data = _rs->dataFor( opCtx, *out ); return data.toBson().getOwned(); } const BSONCollectionCatalogEntry::MetaData KVCatalog::getMetaData( OperationContext* opCtx, const StringData& ns ) { DiskLoc loc; BSONObj obj = _findEntry( opCtx, ns, &loc ); LOG(1) << " got: " << obj; BSONCollectionCatalogEntry::MetaData md; if ( obj["md"].isABSONObj() ) md.parse( obj["md"].Obj() ); return md; } void KVCatalog::putMetaData( OperationContext* opCtx, const StringData& ns, BSONCollectionCatalogEntry::MetaData& md ) { DiskLoc loc; BSONObj obj = _findEntry( opCtx, ns, &loc ); { // rebuilt doc BSONObjBuilder b; b.append( "md", md.toBSON() ); BSONObjBuilder newIdentMap; BSONObj oldIdentMap; if ( obj["idxIdent"].isABSONObj() ) oldIdentMap = obj["idxIdent"].Obj(); // fix ident map for ( size_t i = 0; i < md.indexes.size(); i++ ) { string name = md.indexes[i].name(); BSONElement e = oldIdentMap[name]; if ( e.type() == String ) { newIdentMap.append( e ); continue; } // missing, create new std::stringstream ss; ss << getCollectionIdent( ns ) << '$' << name << '-' << _rand << '-' << _next.fetchAndAdd( 1 ); newIdentMap.append( name, ss.str() ); } b.append( "idxIdent", newIdentMap.obj() ); // add whatever is left b.appendElementsUnique( obj ); obj = b.obj(); } StatusWith status = _rs->updateRecord( opCtx, loc, obj.objdata(), obj.objsize(), false, NULL ); fassert( 28521, status.getStatus() ); invariant( status.getValue() == loc ); } Status KVCatalog::renameCollection( OperationContext* opCtx, const StringData& fromNS, const StringData& toNS, bool stayTemp ) { DiskLoc loc; BSONObj old = _findEntry( opCtx, fromNS, &loc ).getOwned(); { BSONObjBuilder b; b.append( "ns", toNS ); BSONCollectionCatalogEntry::MetaData md; md.parse( old["md"].Obj() ); md.rename( toNS ); if ( !stayTemp ) md.options.temp = false; b.append( "md", md.toBSON() ); b.appendElementsUnique( old ); BSONObj obj = b.obj(); StatusWith status = _rs->updateRecord( opCtx, loc, obj.objdata(), obj.objsize(), false, NULL ); fassert( 28522, status.getStatus() ); invariant( status.getValue() == loc ); } boost::mutex::scoped_lock lk( _identsLock ); _idents.erase( fromNS.toString() ); _idents[toNS.toString()] = Entry( old["ident"].String(), loc ); return Status::OK(); } Status KVCatalog::dropCollection( OperationContext* opCtx, const StringData& ns ) { boost::mutex::scoped_lock lk( _identsLock ); Entry old = _idents[ns.toString()]; if ( old.ident.empty() ) { return Status( ErrorCodes::NamespaceNotFound, "collection not found" ); } LOG(1) << "deleting metadat for " << ns << " @ " << old.storedLoc; _rs->deleteRecord( opCtx, old.storedLoc ); _idents.erase( ns.toString() ); return Status::OK(); } }