summaryrefslogtreecommitdiff
path: root/db/database.h
blob: 23bca7ce87f1fbc9ea5e12f506d5ebb6dcde74d6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
// database.h

/**
*    Copyright (C) 2008 10gen 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 <http://www.gnu.org/licenses/>.
*/

#pragma once

#include "cmdline.h"

namespace mongo {


    /**
     * Database represents a database database
     * Each database database has its own set of files -- dbname.ns, dbname.0, dbname.1, ...
     * NOT memory mapped
    */
    class Database {
    public:
        static bool _openAllFiles;
        
        Database(const char *nm, bool& newDb, const string& _path = dbpath)
            : name(nm), path(_path), namespaceIndex( path, name ) {
            
            { // check db name is valid
                size_t L = strlen(nm);
                uassert( 10028 ,  "db name is empty", L > 0 );
                uassert( 10029 ,  "bad db name [1]", *nm != '.' );
                uassert( 10030 ,  "bad db name [2]", nm[L-1] != '.' );
                uassert( 10031 ,  "bad char(s) in db name", strchr(nm, ' ') == 0 );
                uassert( 10032 ,  "db name too long", L < 64 );
            }

            newDb = namespaceIndex.exists();
            profile = 0;
            profileName = name + ".system.profile";

            // If already exists, open.  Otherwise behave as if empty until
            // there's a write, then open.
            if ( ! newDb || cmdLine.defaultProfile ) {
                namespaceIndex.init();
                if( _openAllFiles )
                    openAllFiles();

            }
            
            magic = 781231;
        }
        
        ~Database() {
            magic = 0;
            btreeStore->closeFiles(name, path);
            size_t n = files.size();
            for ( size_t i = 0; i < n; i++ )
                delete files[i];
        }
        
        /**
         * tries to make sure that this hasn't been deleted
         */
        bool isOk(){
            return magic == 781231;
        }

        bool isEmpty(){
            return ! namespaceIndex.allocated();
        }

        boost::filesystem::path fileName( int n ) {
            stringstream ss;
            ss << name << '.' << n;
            boost::filesystem::path fullName;
            fullName = boost::filesystem::path(path);
            if ( directoryperdb )
                fullName /= name;
            fullName /= ss.str();
            return fullName;
        }
        
        bool exists(int n) { 
            return boost::filesystem::exists( fileName( n ) );
        }

        void openAllFiles() { 
            int n = 0;
            while( exists(n) ) { 
                getFile(n);
                n++;
            }
            // If last file is empty, consider it preallocated and make sure it's not mapped
            // until a write is requested
            if ( n > 1 && getFile( n - 1 )->getHeader()->isEmpty() ) {
                delete files[ n - 1 ];
                files.pop_back();
            }
        }

        MongoDataFile* getFile( int n, int sizeNeeded = 0, bool preallocateOnly = false ) {
            assert(this);

            namespaceIndex.init();
            if ( n < 0 || n >= DiskLoc::MaxFiles ) {
                out() << "getFile(): n=" << n << endl;
#if !defined(_RECSTORE)
                if( n >= RecCache::Base && n <= RecCache::Base+1000 )
                    massert( 10294 , "getFile(): bad file number - using recstore db w/nonrecstore db build?", false);
#endif
                massert( 10295 , "getFile(): bad file number value (corrupt db?): run repair", false);
            }
            DEV {
                if ( n > 100 )
                    out() << "getFile(): n=" << n << "?" << endl;
            }
            MongoDataFile* p = 0;
            if ( !preallocateOnly ) {
                while ( n >= (int) files.size() )
                    files.push_back(0);
                p = files[n];
            }
            if ( p == 0 ) {
                boost::filesystem::path fullName = fileName( n );
                string fullNameString = fullName.string();
                p = new MongoDataFile(n);
                int minSize = 0;
                if ( n != 0 && files[ n - 1 ] )
                    minSize = files[ n - 1 ]->getHeader()->fileLength;
                if ( sizeNeeded + MDFHeader::headerSize() > minSize )
                    minSize = sizeNeeded + MDFHeader::headerSize();
                try {
                    p->open( fullNameString.c_str(), minSize, preallocateOnly );
                }
                catch ( AssertionException& ) {
                    delete p;
                    throw;
                }
                if ( preallocateOnly )
                    delete p;
                else
                    files[n] = p;
            }
            return preallocateOnly ? 0 : p;
        }

        MongoDataFile* addAFile( int sizeNeeded, bool preallocateNextFile ) {
            int n = (int) files.size();
            MongoDataFile *ret = getFile( n, sizeNeeded );
            if ( preallocateNextFile )
                preallocateAFile();
            return ret;
        }
        
        // safe to call this multiple times - the implementation will only preallocate one file
        void preallocateAFile() {
            int n = (int) files.size();
            getFile( n, 0, true );
        }

        MongoDataFile* suitableFile( int sizeNeeded, bool preallocate ) {
            MongoDataFile* f = newestFile();
            if ( !f ) {
                f = addAFile( sizeNeeded, preallocate );                
            }
            for ( int i = 0; i < 8; i++ ) {
                if ( f->getHeader()->unusedLength >= sizeNeeded )
                    break;
                f = addAFile( sizeNeeded, preallocate );
                if ( f->getHeader()->fileLength >= MongoDataFile::maxSize() ) // this is as big as they get so might as well stop
                    break;
            }
            return f;
        }

        Extent* allocExtent( const char *ns, int size, bool capped ) { 
            Extent *e = DataFileMgr::allocFromFreeList( ns, size, capped );
            if( e ) return e;
            return suitableFile( size, !capped )->createExtent( ns, size, capped );
        }
        
        MongoDataFile* newestFile() {
            int n = (int) files.size();
            if ( n > 0 ) {
                n--;
            } else {
                return 0;   
            }
            return getFile(n);
        }
        
        /**
         * @return true if success, false otherwise
         */
        bool setProfilingLevel( int newLevel , string& errmsg );

        void finishInit();
        
        vector<MongoDataFile*> files;
        string name; // "alleyinsider"
        string path;
        NamespaceIndex namespaceIndex;
        int profile; // 0=off.
        string profileName; // "alleyinsider.system.profile"
        int magic; // used for making sure the object is still loaded in memory 
    };

} // namespace mongo