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
16using namespace duckdb;
17using namespace std;
18
19StorageManager::StorageManager(DuckDB &db, string path, bool read_only)
20 : database(db), path(path), wal(db), read_only(read_only) {
21}
22
23StorageManager::~StorageManager() {
24}
25
26BufferManager &BufferManager::GetBufferManager(ClientContext &context) {
27 return *context.db.storage->buffer_manager;
28}
29
30void 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
64void 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
91void 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