CollectionOptions MMAPV1DatabaseCatalogEntry::getCollectionOptions( OperationContext* txn,
                                                                        const StringData& ns ) const {
        if ( nsToCollectionSubstring( ns ) == "system.namespaces" ) {
            return CollectionOptions();
        }

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

        scoped_ptr<RecordIterator> it( rs->getIterator(txn) );
        while ( !it->isEOF() ) {
            DiskLoc loc = it->getNext();
            BSONObj entry = it->dataFor( loc ).toBson();
            BSONElement name = entry["name"];
            if ( name.type() == String && name.String() == ns ) {
                CollectionOptions options;
                if ( entry["options"].isABSONObj() ) {
                    Status status = options.parse( entry["options"].Obj() );
                    fassert( 18523, status );
                }
                return options;
            }
        }

        return CollectionOptions();
    }
    RecordStoreV1Base* MMAPV1DatabaseCatalogEntry::_getRecordStore( OperationContext* txn,
                                                                   const StringData& ns ) {

        // XXX TODO - CACHE

        NamespaceString nss( ns );
        NamespaceDetails* details = _namespaceIndex.details( ns );
        if ( !details ) {
            return NULL;
        }

        auto_ptr<NamespaceDetailsRSV1MetaData> md( new NamespaceDetailsRSV1MetaData( ns,
                                                                                     details,
                                                                                     _getNamespaceRecordStore( txn, ns ) ) );

        if ( details->isCapped ) {
            return new CappedRecordStoreV1( txn,
                                            NULL, //TOD(ERH) this will blow up :)
                                            ns,
                                            md.release(),
                                            &_extentManager,
                                            nss.coll() == "system.indexes" );
        }

        return new SimpleRecordStoreV1( txn,
                                        ns,
                                        md.release(),
                                        &_extentManager,
                                        nss.coll() == "system.indexes" );
    }
    IndexAccessMethod* MMAPV1DatabaseCatalogEntry::getIndex( OperationContext* txn,
                                                            const CollectionCatalogEntry* collection,
                                                            IndexCatalogEntry* entry ) {
        const string& type = entry->descriptor()->getAccessMethodName();

        string ns = collection->ns().ns();

        if ( IndexNames::TEXT == type ||
             entry->descriptor()->getInfoElement("expireAfterSeconds").isNumber() ) {
            NamespaceDetailsRSV1MetaData md( ns,
                                             _namespaceIndex.details( ns ),
                                             _getNamespaceRecordStore() );
            md.setUserFlag( txn, NamespaceDetails::Flag_UsePowerOf2Sizes );
        }

        RecordStore* rs = NULL;
        {
            boost::mutex::scoped_lock lk( _collectionsLock );
            string ns = entry->descriptor()->indexNamespace();
            Entry*& entry = _collections[ns];
            if ( !entry ) {
                entry = new Entry();
                _fillInEntry_inlock( txn, ns, entry );
            }
            rs = entry->recordStore.get();
        }

        std::auto_ptr<BtreeInterface> btree(
            BtreeInterface::getInterface(entry->headManager(),
                                         rs,
                                         entry->ordering(),
                                         entry->descriptor()->indexNamespace(),
                                         entry->descriptor()->version(),
                                         &BtreeBasedAccessMethod::invalidateCursors));

        if (IndexNames::HASHED == type)
            return new HashAccessMethod( entry, btree.release() );

        if (IndexNames::GEO_2DSPHERE == type)
            return new S2AccessMethod( entry, btree.release() );

        if (IndexNames::TEXT == type)
            return new FTSAccessMethod( entry, btree.release() );

        if (IndexNames::GEO_HAYSTACK == type)
            return new HaystackAccessMethod( entry, btree.release() );

        if ("" == type)
            return new BtreeAccessMethod( entry, btree.release() );

        if (IndexNames::GEO_2D == type)
            return new TwoDAccessMethod( entry, btree.release() );

        log() << "Can't find index for keyPattern " << entry->descriptor()->keyPattern();
        fassertFailed(17489);
    }
    void MMAPV1DatabaseCatalogEntry::_addNamespaceToNamespaceCollection( OperationContext* txn,
                                                                        const StringData& ns,
                                                                        const BSONObj* options ) {
        if ( nsToCollectionSubstring( ns ) == "system.namespaces" ) {
            // system.namespaces holds all the others, so it is not explicitly listed in the catalog.
            return;
        }

        BSONObjBuilder b;
        b.append("name", ns);
        if ( options && !options->isEmpty() )
            b.append("options", *options);
        BSONObj obj = b.done();

        RecordStoreV1Base* rs = _getNamespaceRecordStore( txn, ns );
        invariant( rs );
        StatusWith<DiskLoc> loc = rs->insertRecord( txn, obj.objdata(), obj.objsize(), -1 );
        massertStatusOK( loc.getStatus() );
    }
    void MMAPV1DatabaseCatalogEntry::_removeNamespaceFromNamespaceCollection( OperationContext* txn,
                                                                             const StringData& ns ) {
        if ( nsToCollectionSubstring( ns ) == "system.namespaces" ) {
            // system.namespaces holds all the others, so it is not explicitly listed in the catalog.
            return;
        }

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

        scoped_ptr<RecordIterator> it( rs->getIterator(txn) );
        while ( !it->isEOF() ) {
            DiskLoc loc = it->getNext();
            BSONObj entry = it->dataFor( loc ).toBson();
            BSONElement name = entry["name"];
            if ( name.type() == String && name.String() == ns ) {
                rs->deleteRecord( txn, loc );
                break;
            }
        }
    }
    IndexAccessMethod* MMAPV1DatabaseCatalogEntry::getIndex( OperationContext* txn,
                                                            const CollectionCatalogEntry* collection,
                                                            IndexCatalogEntry* entry ) {
        const string& type = entry->descriptor()->getAccessMethodName();

        string ns = collection->ns().ns();

        if ( IndexNames::TEXT == type ||
             entry->descriptor()->getInfoElement("expireAfterSeconds").isNumber() ) {
            NamespaceDetailsRSV1MetaData md( ns,
                                             _namespaceIndex.details( ns ),
                                             _getNamespaceRecordStore( txn, ns ) );
            md.setUserFlag( txn, NamespaceDetails::Flag_UsePowerOf2Sizes );
        }

        RecordStore* rs = _getRecordStore( txn, entry->descriptor()->indexNamespace() );
        invariant( rs );

        if (IndexNames::HASHED == type)
            return new HashAccessMethod( entry, rs );

        if (IndexNames::GEO_2DSPHERE == type)
            return new S2AccessMethod( entry, rs );

        if (IndexNames::TEXT == type)
            return new FTSAccessMethod( entry, rs );

        if (IndexNames::GEO_HAYSTACK == type)
            return new HaystackAccessMethod( entry, rs );

        if ("" == type)
            return new BtreeAccessMethod( entry, rs );

        if (IndexNames::GEO_2D == type)
            return new TwoDAccessMethod( entry, rs );

        log() << "Can't find index for keyPattern " << entry->descriptor()->keyPattern();
        fassertFailed(17489);
    }
    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();
    }
    Status MMAPV1DatabaseCatalogEntry::_renameSingleNamespace( OperationContext* txn,
                                                              const StringData& fromNS,
                                                              const StringData& toNS,
                                                              bool stayTemp ) {
        // some sanity checking
        NamespaceDetails* fromDetails = _namespaceIndex.details( fromNS );
        if ( !fromDetails )
            return Status( ErrorCodes::BadValue, "from namespace doesn't exist" );

        if ( _namespaceIndex.details( toNS ) )
            return Status( ErrorCodes::BadValue, "to namespace already exists" );

        // at this point, we haven't done anything destructive yet

        // ----
        // actually start moving
        // ----

        // this could throw, but if it does we're ok
        _namespaceIndex.add_ns( txn, toNS, fromDetails );
        NamespaceDetails* toDetails = _namespaceIndex.details( toNS );

        try {
            toDetails->copyingFrom(txn,
                                   toNS,
                                   _namespaceIndex,
                                   fromDetails); // fixes extraOffset
        }
        catch( DBException& ) {
            // could end up here if .ns is full - if so try to clean up / roll back a little
            _namespaceIndex.kill_ns( txn, toNS );
            throw;
        }

        // at this point, code .ns stuff moved

        _namespaceIndex.kill_ns( txn, fromNS );
        fromDetails = NULL;

        // fix system.namespaces
        BSONObj newSpec;
        DiskLoc oldSpecLocation;
        {

            BSONObj oldSpec;
            {
                RecordStoreV1Base* rs = _getNamespaceRecordStore( txn, fromNS );
                scoped_ptr<RecordIterator> it( rs->getIterator() );
                while ( !it->isEOF() ) {
                    DiskLoc loc = it->getNext();
                    const Record* rec = it->recordFor( loc );
                    BSONObj entry( rec->data() );
                    if ( fromNS == entry["name"].String() ) {
                        oldSpecLocation = loc;
                        oldSpec = entry.getOwned();
                        break;
                    }
                }
            }
            invariant( !oldSpec.isEmpty() );
            invariant( !oldSpecLocation.isNull() );

            BSONObjBuilder b;
            BSONObjIterator i( oldSpec.getObjectField( "options" ) );
            while( i.more() ) {
                BSONElement e = i.next();
                if ( strcmp( e.fieldName(), "create" ) != 0 ) {
                    if (stayTemp || (strcmp(e.fieldName(), "temp") != 0))
                        b.append( e );
                }
                else {
                    b << "create" << toNS;
                }
            }
            newSpec = b.obj();
        }

        _addNamespaceToNamespaceCollection( txn, toNS, newSpec.isEmpty() ? 0 : &newSpec );

        _getNamespaceRecordStore( txn, fromNS )->deleteRecord( txn, oldSpecLocation );

        return Status::OK();
    }