1#include "duckdb/main/database.hpp"
2
3#include "duckdb/catalog/catalog.hpp"
4#include "duckdb/common/virtual_file_system.hpp"
5#include "duckdb/execution/operator/helper/physical_set.hpp"
6#include "duckdb/function/cast/cast_function_set.hpp"
7#include "duckdb/function/compression_function.hpp"
8#include "duckdb/main/attached_database.hpp"
9#include "duckdb/main/client_context.hpp"
10#include "duckdb/main/connection_manager.hpp"
11#include "duckdb/main/database_manager.hpp"
12#include "duckdb/main/error_manager.hpp"
13#include "duckdb/main/extension_helper.hpp"
14#include "duckdb/parallel/task_scheduler.hpp"
15#include "duckdb/parser/parsed_data/attach_info.hpp"
16#include "duckdb/storage/object_cache.hpp"
17#include "duckdb/storage/standard_buffer_manager.hpp"
18#include "duckdb/main/database_path_and_type.hpp"
19#include "duckdb/storage/storage_extension.hpp"
20#include "duckdb/storage/storage_manager.hpp"
21#include "duckdb/transaction/transaction_manager.hpp"
22
23#ifndef DUCKDB_NO_THREADS
24#include "duckdb/common/thread.hpp"
25#endif
26
27namespace duckdb {
28
29DBConfig::DBConfig() {
30 compression_functions = make_uniq<CompressionFunctionSet>();
31 cast_functions = make_uniq<CastFunctionSet>();
32 error_manager = make_uniq<ErrorManager>();
33}
34
35DBConfig::DBConfig(std::unordered_map<string, string> &config_dict, bool read_only) : DBConfig::DBConfig() {
36 if (read_only) {
37 options.access_mode = AccessMode::READ_ONLY;
38 }
39 for (auto &kv : config_dict) {
40 string key = kv.first;
41 string val = kv.second;
42 auto opt_val = Value(val);
43 DBConfig::SetOptionByName(name: key, value: opt_val);
44 }
45}
46
47DBConfig::~DBConfig() {
48}
49
50DatabaseInstance::DatabaseInstance() {
51}
52
53DatabaseInstance::~DatabaseInstance() {
54}
55
56BufferManager &BufferManager::GetBufferManager(DatabaseInstance &db) {
57 return db.GetBufferManager();
58}
59
60BufferManager &BufferManager::GetBufferManager(AttachedDatabase &db) {
61 return BufferManager::GetBufferManager(db&: db.GetDatabase());
62}
63
64DatabaseInstance &DatabaseInstance::GetDatabase(ClientContext &context) {
65 return *context.db;
66}
67
68DatabaseManager &DatabaseInstance::GetDatabaseManager() {
69 if (!db_manager) {
70 throw InternalException("Missing DB manager");
71 }
72 return *db_manager;
73}
74
75Catalog &Catalog::GetSystemCatalog(DatabaseInstance &db) {
76 return db.GetDatabaseManager().GetSystemCatalog();
77}
78
79Catalog &Catalog::GetCatalog(AttachedDatabase &db) {
80 return db.GetCatalog();
81}
82
83FileSystem &FileSystem::GetFileSystem(DatabaseInstance &db) {
84 return db.GetFileSystem();
85}
86
87FileSystem &FileSystem::Get(AttachedDatabase &db) {
88 return FileSystem::GetFileSystem(db&: db.GetDatabase());
89}
90
91DBConfig &DBConfig::GetConfig(DatabaseInstance &db) {
92 return db.config;
93}
94
95ClientConfig &ClientConfig::GetConfig(ClientContext &context) {
96 return context.config;
97}
98
99DBConfig &DBConfig::Get(AttachedDatabase &db) {
100 return DBConfig::GetConfig(db&: db.GetDatabase());
101}
102
103const DBConfig &DBConfig::GetConfig(const DatabaseInstance &db) {
104 return db.config;
105}
106
107const ClientConfig &ClientConfig::GetConfig(const ClientContext &context) {
108 return context.config;
109}
110
111TransactionManager &TransactionManager::Get(AttachedDatabase &db) {
112 return db.GetTransactionManager();
113}
114
115ConnectionManager &ConnectionManager::Get(DatabaseInstance &db) {
116 return db.GetConnectionManager();
117}
118
119ClientContext *ConnectionManager::GetConnection(DatabaseInstance *db) {
120 for (auto &conn : connections) {
121 if (conn.first->db.get() == db) {
122 return conn.first;
123 }
124 }
125 return nullptr;
126}
127
128ConnectionManager &ConnectionManager::Get(ClientContext &context) {
129 return ConnectionManager::Get(db&: DatabaseInstance::GetDatabase(context));
130}
131
132duckdb::unique_ptr<AttachedDatabase> DatabaseInstance::CreateAttachedDatabase(AttachInfo &info, const string &type,
133 AccessMode access_mode) {
134 duckdb::unique_ptr<AttachedDatabase> attached_database;
135 if (!type.empty()) {
136 // find the storage extension
137 auto extension_name = ExtensionHelper::ApplyExtensionAlias(extension_name: type);
138 auto entry = config.storage_extensions.find(x: extension_name);
139 if (entry == config.storage_extensions.end()) {
140 throw BinderException("Unrecognized storage type \"%s\"", type);
141 }
142
143 if (entry->second->attach != nullptr && entry->second->create_transaction_manager != nullptr) {
144 // use storage extension to create the initial database
145 attached_database = make_uniq<AttachedDatabase>(args&: *this, args&: Catalog::GetSystemCatalog(db&: *this), args&: *entry->second,
146 args&: info.name, args&: info, args&: access_mode);
147 } else {
148 attached_database =
149 make_uniq<AttachedDatabase>(args&: *this, args&: Catalog::GetSystemCatalog(db&: *this), args&: info.name, args&: info.path, args&: access_mode);
150 }
151 } else {
152 // check if this is an in-memory database or not
153 attached_database =
154 make_uniq<AttachedDatabase>(args&: *this, args&: Catalog::GetSystemCatalog(db&: *this), args&: info.name, args&: info.path, args&: access_mode);
155 }
156 return attached_database;
157}
158
159void DatabaseInstance::CreateMainDatabase() {
160 AttachInfo info;
161 info.name = AttachedDatabase::ExtractDatabaseName(dbpath: config.options.database_path);
162 info.path = config.options.database_path;
163
164 auto attached_database = CreateAttachedDatabase(info, type: config.options.database_type, access_mode: config.options.access_mode);
165 auto initial_database = attached_database.get();
166 {
167 Connection con(*this);
168 con.BeginTransaction();
169 db_manager->AddDatabase(context&: *con.context, db: std::move(attached_database));
170 con.Commit();
171 }
172
173 // initialize the database
174 initial_database->SetInitialDatabase();
175 initial_database->Initialize();
176}
177
178void ThrowExtensionSetUnrecognizedOptions(const unordered_map<string, Value> &unrecognized_options) {
179 auto unrecognized_options_iter = unrecognized_options.begin();
180 string unrecognized_option_keys = unrecognized_options_iter->first;
181 for (; unrecognized_options_iter == unrecognized_options.end(); ++unrecognized_options_iter) {
182 unrecognized_option_keys = "," + unrecognized_options_iter->first;
183 }
184 throw InvalidInputException("Unrecognized configuration property \"%s\"", unrecognized_option_keys);
185}
186
187void DatabaseInstance::Initialize(const char *database_path, DBConfig *user_config) {
188 DBConfig default_config;
189 DBConfig *config_ptr = &default_config;
190 if (user_config) {
191 config_ptr = user_config;
192 }
193
194 if (config_ptr->options.temporary_directory.empty() && database_path) {
195 // no directory specified: use default temp path
196 config_ptr->options.temporary_directory = string(database_path) + ".tmp";
197
198 // special treatment for in-memory mode
199 if (strcmp(s1: database_path, s2: ":memory:") == 0) {
200 config_ptr->options.temporary_directory = ".tmp";
201 }
202 }
203
204 if (database_path) {
205 config_ptr->options.database_path = database_path;
206 } else {
207 config_ptr->options.database_path.clear();
208 }
209 Configure(config&: *config_ptr);
210
211 if (user_config && !user_config->options.use_temporary_directory) {
212 // temporary directories explicitly disabled
213 config.options.temporary_directory = string();
214 }
215
216 db_manager = make_uniq<DatabaseManager>(args&: *this);
217 buffer_manager = make_uniq<StandardBufferManager>(args&: *this, args&: config.options.temporary_directory);
218 scheduler = make_uniq<TaskScheduler>(args&: *this);
219 object_cache = make_uniq<ObjectCache>();
220 connection_manager = make_uniq<ConnectionManager>();
221
222 // check if we are opening a standard DuckDB database or an extension database
223 if (config.options.database_type.empty()) {
224 auto path_and_type = DBPathAndType::Parse(combined_path: config.options.database_path, config);
225 config.options.database_type = path_and_type.type;
226 config.options.database_path = path_and_type.path;
227 }
228
229 // initialize the system catalog
230 db_manager->InitializeSystemCatalog();
231
232 if (!config.options.database_type.empty()) {
233 // if we are opening an extension database - load the extension
234 if (!config.file_system) {
235 throw InternalException("No file system!?");
236 }
237 ExtensionHelper::LoadExternalExtension(db&: *this, fs&: *config.file_system, extension: config.options.database_type);
238 }
239
240 if (!config.options.unrecognized_options.empty()) {
241 ThrowExtensionSetUnrecognizedOptions(unrecognized_options: config.options.unrecognized_options);
242 }
243
244 if (!db_manager->HasDefaultDatabase()) {
245 CreateMainDatabase();
246 }
247
248 // only increase thread count after storage init because we get races on catalog otherwise
249 scheduler->SetThreads(config.options.maximum_threads);
250}
251
252DuckDB::DuckDB(const char *path, DBConfig *new_config) : instance(make_shared<DatabaseInstance>()) {
253 instance->Initialize(database_path: path, user_config: new_config);
254 if (instance->config.options.load_extensions) {
255 ExtensionHelper::LoadAllExtensions(db&: *this);
256 }
257}
258
259DuckDB::DuckDB(const string &path, DBConfig *config) : DuckDB(path.c_str(), config) {
260}
261
262DuckDB::DuckDB(DatabaseInstance &instance_p) : instance(instance_p.shared_from_this()) {
263}
264
265DuckDB::~DuckDB() {
266}
267
268BufferManager &DatabaseInstance::GetBufferManager() {
269 return *buffer_manager;
270}
271
272BufferPool &DatabaseInstance::GetBufferPool() {
273 return *config.buffer_pool;
274}
275
276DatabaseManager &DatabaseManager::Get(DatabaseInstance &db) {
277 return db.GetDatabaseManager();
278}
279
280DatabaseManager &DatabaseManager::Get(ClientContext &db) {
281 return DatabaseManager::Get(db&: *db.db);
282}
283
284TaskScheduler &DatabaseInstance::GetScheduler() {
285 return *scheduler;
286}
287
288ObjectCache &DatabaseInstance::GetObjectCache() {
289 return *object_cache;
290}
291
292FileSystem &DatabaseInstance::GetFileSystem() {
293 return *config.file_system;
294}
295
296ConnectionManager &DatabaseInstance::GetConnectionManager() {
297 return *connection_manager;
298}
299
300FileSystem &DuckDB::GetFileSystem() {
301 return instance->GetFileSystem();
302}
303
304Allocator &Allocator::Get(ClientContext &context) {
305 return Allocator::Get(db&: *context.db);
306}
307
308Allocator &Allocator::Get(DatabaseInstance &db) {
309 return *db.config.allocator;
310}
311
312Allocator &Allocator::Get(AttachedDatabase &db) {
313 return Allocator::Get(db&: db.GetDatabase());
314}
315
316void DatabaseInstance::Configure(DBConfig &new_config) {
317 config.options = new_config.options;
318 if (config.options.access_mode == AccessMode::UNDEFINED) {
319 config.options.access_mode = AccessMode::READ_WRITE;
320 }
321 if (new_config.file_system) {
322 config.file_system = std::move(new_config.file_system);
323 } else {
324 config.file_system = make_uniq<VirtualFileSystem>();
325 }
326 if (config.options.maximum_memory == (idx_t)-1) {
327 config.SetDefaultMaxMemory();
328 }
329 if (new_config.options.maximum_threads == (idx_t)-1) {
330 config.SetDefaultMaxThreads();
331 }
332 config.allocator = std::move(new_config.allocator);
333 if (!config.allocator) {
334 config.allocator = make_uniq<Allocator>();
335 }
336 config.replacement_scans = std::move(new_config.replacement_scans);
337 config.parser_extensions = std::move(new_config.parser_extensions);
338 config.error_manager = std::move(new_config.error_manager);
339 if (!config.error_manager) {
340 config.error_manager = make_uniq<ErrorManager>();
341 }
342 if (!config.default_allocator) {
343 config.default_allocator = Allocator::DefaultAllocatorReference();
344 }
345 if (new_config.buffer_pool) {
346 config.buffer_pool = std::move(new_config.buffer_pool);
347 } else {
348 config.buffer_pool = make_shared<BufferPool>(args&: config.options.maximum_memory);
349 }
350}
351
352DBConfig &DBConfig::GetConfig(ClientContext &context) {
353 return context.db->config;
354}
355
356const DBConfig &DBConfig::GetConfig(const ClientContext &context) {
357 return context.db->config;
358}
359
360idx_t DatabaseInstance::NumberOfThreads() {
361 return scheduler->NumberOfThreads();
362}
363
364const unordered_set<std::string> &DatabaseInstance::LoadedExtensions() {
365 return loaded_extensions;
366}
367
368idx_t DuckDB::NumberOfThreads() {
369 return instance->NumberOfThreads();
370}
371
372bool DatabaseInstance::ExtensionIsLoaded(const std::string &name) {
373 auto extension_name = ExtensionHelper::GetExtensionName(extension: name);
374 return loaded_extensions.find(x: extension_name) != loaded_extensions.end();
375}
376
377bool DuckDB::ExtensionIsLoaded(const std::string &name) {
378 return instance->ExtensionIsLoaded(name);
379}
380
381void DatabaseInstance::SetExtensionLoaded(const std::string &name) {
382 auto extension_name = ExtensionHelper::GetExtensionName(extension: name);
383 loaded_extensions.insert(x: extension_name);
384}
385
386bool DatabaseInstance::TryGetCurrentSetting(const std::string &key, Value &result) {
387 // check the session values
388 auto &db_config = DBConfig::GetConfig(db&: *this);
389 const auto &global_config_map = db_config.options.set_variables;
390
391 auto global_value = global_config_map.find(x: key);
392 bool found_global_value = global_value != global_config_map.end();
393 if (!found_global_value) {
394 return false;
395 }
396 result = global_value->second;
397 return true;
398}
399
400string ClientConfig::ExtractTimezone() const {
401 auto entry = set_variables.find(x: "TimeZone");
402 if (entry == set_variables.end()) {
403 return "UTC";
404 } else {
405 return entry->second.GetValue<std::string>();
406 }
407}
408
409ValidChecker &DatabaseInstance::GetValidChecker() {
410 return db_validity;
411}
412
413ValidChecker &ValidChecker::Get(DatabaseInstance &db) {
414 return db.GetValidChecker();
415}
416
417} // namespace duckdb
418