| 1 | #include "duckdb/storage/storage_manager.hpp" | 
|---|
| 2 | #include "duckdb/storage/checkpoint_manager.hpp" | 
|---|
| 3 | #include "duckdb/storage/in_memory_block_manager.hpp" | 
|---|
| 4 | #include "duckdb/storage/single_file_block_manager.hpp" | 
|---|
| 5 |  | 
|---|
| 6 | #include "duckdb/catalog/catalog.hpp" | 
|---|
| 7 | #include "duckdb/common/file_system.hpp" | 
|---|
| 8 | #include "duckdb/main/database.hpp" | 
|---|
| 9 | #include "duckdb/main/client_context.hpp" | 
|---|
| 10 | #include "duckdb/function/function.hpp" | 
|---|
| 11 | #include "duckdb/parser/parsed_data/create_schema_info.hpp" | 
|---|
| 12 | #include "duckdb/transaction/transaction_manager.hpp" | 
|---|
| 13 | #include "duckdb/planner/binder.hpp" | 
|---|
| 14 | #include "duckdb/common/serializer/buffered_file_reader.hpp" | 
|---|
| 15 |  | 
|---|
| 16 | using namespace duckdb; | 
|---|
| 17 | using namespace std; | 
|---|
| 18 |  | 
|---|
| 19 | StorageManager::StorageManager(DuckDB &db, string path, bool read_only) | 
|---|
| 20 | : database(db), path(path), wal(db), read_only(read_only) { | 
|---|
| 21 | } | 
|---|
| 22 |  | 
|---|
| 23 | StorageManager::~StorageManager() { | 
|---|
| 24 | } | 
|---|
| 25 |  | 
|---|
| 26 | BufferManager &BufferManager::GetBufferManager(ClientContext &context) { | 
|---|
| 27 | return *context.db.storage->buffer_manager; | 
|---|
| 28 | } | 
|---|
| 29 |  | 
|---|
| 30 | void StorageManager::Initialize() { | 
|---|
| 31 | bool in_memory = path.empty() || path == ":memory:"; | 
|---|
| 32 |  | 
|---|
| 33 | if (in_memory && read_only) { | 
|---|
| 34 | throw CatalogException( "Cannot launch in-memory database in read-only mode!"); | 
|---|
| 35 | } | 
|---|
| 36 |  | 
|---|
| 37 | // first initialize the base system catalogs | 
|---|
| 38 | // these are never written to the WAL | 
|---|
| 39 | ClientContext context(database); | 
|---|
| 40 | context.transaction.BeginTransaction(); | 
|---|
| 41 |  | 
|---|
| 42 | // create the default schema | 
|---|
| 43 | CreateSchemaInfo info; | 
|---|
| 44 | info.schema = DEFAULT_SCHEMA; | 
|---|
| 45 | database.catalog->CreateSchema(context, &info); | 
|---|
| 46 |  | 
|---|
| 47 | // initialize default functions | 
|---|
| 48 | BuiltinFunctions builtin(context, *database.catalog); | 
|---|
| 49 | builtin.Initialize(); | 
|---|
| 50 |  | 
|---|
| 51 | // commit transactions | 
|---|
| 52 | context.transaction.Commit(); | 
|---|
| 53 |  | 
|---|
| 54 | if (!in_memory) { | 
|---|
| 55 | // create or load the database from disk, if not in-memory mode | 
|---|
| 56 | LoadDatabase(); | 
|---|
| 57 | } else { | 
|---|
| 58 | block_manager = make_unique<InMemoryBlockManager>(); | 
|---|
| 59 | buffer_manager = make_unique<BufferManager>(*database.file_system, *block_manager, database.temporary_directory, | 
|---|
| 60 | database.maximum_memory); | 
|---|
| 61 | } | 
|---|
| 62 | } | 
|---|
| 63 |  | 
|---|
| 64 | void StorageManager::Checkpoint(string wal_path) { | 
|---|
| 65 | if (!database.file_system->FileExists(wal_path)) { | 
|---|
| 66 | // no WAL to checkpoint | 
|---|
| 67 | return; | 
|---|
| 68 | } | 
|---|
| 69 | if (read_only) { | 
|---|
| 70 | // cannot checkpoint in read-only system | 
|---|
| 71 | return; | 
|---|
| 72 | } | 
|---|
| 73 | // check the size of the WAL | 
|---|
| 74 | { | 
|---|
| 75 | BufferedFileReader reader(*database.file_system, wal_path.c_str()); | 
|---|
| 76 | if (reader.FileSize() <= database.checkpoint_wal_size) { | 
|---|
| 77 | // WAL is too small | 
|---|
| 78 | return; | 
|---|
| 79 | } | 
|---|
| 80 | } | 
|---|
| 81 |  | 
|---|
| 82 | // checkpoint the database | 
|---|
| 83 | // FIXME: we do this now by creating a new database and forcing a checkpoint in that database | 
|---|
| 84 | // then reloading the file again | 
|---|
| 85 | // this should be fixed and turned into an incremental checkpoint | 
|---|
| 86 | DBConfig config; | 
|---|
| 87 | config.checkpoint_only = true; | 
|---|
| 88 | DuckDB db(path, &config); | 
|---|
| 89 | } | 
|---|
| 90 |  | 
|---|
| 91 | void StorageManager::LoadDatabase() { | 
|---|
| 92 | string wal_path = path + ".wal"; | 
|---|
| 93 | // first check if the database exists | 
|---|
| 94 | if (!database.file_system->FileExists(path)) { | 
|---|
| 95 | if (read_only) { | 
|---|
| 96 | throw CatalogException( "Cannot open database \"%s\" in read-only mode: database does not exist", | 
|---|
| 97 | path.c_str()); | 
|---|
| 98 | } | 
|---|
| 99 | // check if the WAL exists | 
|---|
| 100 | if (database.file_system->FileExists(wal_path)) { | 
|---|
| 101 | // WAL file exists but database file does not | 
|---|
| 102 | // remove the WAL | 
|---|
| 103 | database.file_system->RemoveFile(wal_path); | 
|---|
| 104 | } | 
|---|
| 105 | // initialize the block manager while creating a new db file | 
|---|
| 106 | block_manager = | 
|---|
| 107 | make_unique<SingleFileBlockManager>(*database.file_system, path, read_only, true, database.use_direct_io); | 
|---|
| 108 | buffer_manager = make_unique<BufferManager>(*database.file_system, *block_manager, database.temporary_directory, | 
|---|
| 109 | database.maximum_memory); | 
|---|
| 110 | } else { | 
|---|
| 111 | if (!database.checkpoint_only) { | 
|---|
| 112 | Checkpoint(wal_path); | 
|---|
| 113 | } | 
|---|
| 114 | // initialize the block manager while loading the current db file | 
|---|
| 115 | auto sf = | 
|---|
| 116 | make_unique<SingleFileBlockManager>(*database.file_system, path, read_only, false, database.use_direct_io); | 
|---|
| 117 | buffer_manager = make_unique<BufferManager>(*database.file_system, *sf, database.temporary_directory, | 
|---|
| 118 | database.maximum_memory); | 
|---|
| 119 | sf->LoadFreeList(*buffer_manager); | 
|---|
| 120 | block_manager = move(sf); | 
|---|
| 121 |  | 
|---|
| 122 | //! Load from storage | 
|---|
| 123 | CheckpointManager checkpointer(*this); | 
|---|
| 124 | checkpointer.LoadFromStorage(); | 
|---|
| 125 | // check if the WAL file exists | 
|---|
| 126 | if (database.file_system->FileExists(wal_path)) { | 
|---|
| 127 | // replay the WAL | 
|---|
| 128 | WriteAheadLog::Replay(database, wal_path); | 
|---|
| 129 | if (database.checkpoint_only) { | 
|---|
| 130 | assert(!read_only); | 
|---|
| 131 | // checkpoint the database | 
|---|
| 132 | checkpointer.CreateCheckpoint(); | 
|---|
| 133 | // remove the WAL | 
|---|
| 134 | database.file_system->RemoveFile(wal_path); | 
|---|
| 135 | } | 
|---|
| 136 | } | 
|---|
| 137 | } | 
|---|
| 138 | // initialize the WAL file | 
|---|
| 139 | if (!database.checkpoint_only && !read_only) { | 
|---|
| 140 | wal.Initialize(wal_path); | 
|---|
| 141 | } | 
|---|
| 142 | } | 
|---|
| 143 |  | 
|---|