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 | |
27 | namespace duckdb { |
28 | |
29 | DBConfig::DBConfig() { |
30 | compression_functions = make_uniq<CompressionFunctionSet>(); |
31 | cast_functions = make_uniq<CastFunctionSet>(); |
32 | error_manager = make_uniq<ErrorManager>(); |
33 | } |
34 | |
35 | DBConfig::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 | |
47 | DBConfig::~DBConfig() { |
48 | } |
49 | |
50 | DatabaseInstance::DatabaseInstance() { |
51 | } |
52 | |
53 | DatabaseInstance::~DatabaseInstance() { |
54 | } |
55 | |
56 | BufferManager &BufferManager::GetBufferManager(DatabaseInstance &db) { |
57 | return db.GetBufferManager(); |
58 | } |
59 | |
60 | BufferManager &BufferManager::GetBufferManager(AttachedDatabase &db) { |
61 | return BufferManager::GetBufferManager(db&: db.GetDatabase()); |
62 | } |
63 | |
64 | DatabaseInstance &DatabaseInstance::GetDatabase(ClientContext &context) { |
65 | return *context.db; |
66 | } |
67 | |
68 | DatabaseManager &DatabaseInstance::GetDatabaseManager() { |
69 | if (!db_manager) { |
70 | throw InternalException("Missing DB manager" ); |
71 | } |
72 | return *db_manager; |
73 | } |
74 | |
75 | Catalog &Catalog::GetSystemCatalog(DatabaseInstance &db) { |
76 | return db.GetDatabaseManager().GetSystemCatalog(); |
77 | } |
78 | |
79 | Catalog &Catalog::GetCatalog(AttachedDatabase &db) { |
80 | return db.GetCatalog(); |
81 | } |
82 | |
83 | FileSystem &FileSystem::GetFileSystem(DatabaseInstance &db) { |
84 | return db.GetFileSystem(); |
85 | } |
86 | |
87 | FileSystem &FileSystem::Get(AttachedDatabase &db) { |
88 | return FileSystem::GetFileSystem(db&: db.GetDatabase()); |
89 | } |
90 | |
91 | DBConfig &DBConfig::GetConfig(DatabaseInstance &db) { |
92 | return db.config; |
93 | } |
94 | |
95 | ClientConfig &ClientConfig::GetConfig(ClientContext &context) { |
96 | return context.config; |
97 | } |
98 | |
99 | DBConfig &DBConfig::Get(AttachedDatabase &db) { |
100 | return DBConfig::GetConfig(db&: db.GetDatabase()); |
101 | } |
102 | |
103 | const DBConfig &DBConfig::GetConfig(const DatabaseInstance &db) { |
104 | return db.config; |
105 | } |
106 | |
107 | const ClientConfig &ClientConfig::GetConfig(const ClientContext &context) { |
108 | return context.config; |
109 | } |
110 | |
111 | TransactionManager &TransactionManager::Get(AttachedDatabase &db) { |
112 | return db.GetTransactionManager(); |
113 | } |
114 | |
115 | ConnectionManager &ConnectionManager::Get(DatabaseInstance &db) { |
116 | return db.GetConnectionManager(); |
117 | } |
118 | |
119 | ClientContext *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 | |
128 | ConnectionManager &ConnectionManager::Get(ClientContext &context) { |
129 | return ConnectionManager::Get(db&: DatabaseInstance::GetDatabase(context)); |
130 | } |
131 | |
132 | duckdb::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 | |
159 | void 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 | |
178 | void 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 | |
187 | void 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 | |
252 | DuckDB::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 | |
259 | DuckDB::DuckDB(const string &path, DBConfig *config) : DuckDB(path.c_str(), config) { |
260 | } |
261 | |
262 | DuckDB::DuckDB(DatabaseInstance &instance_p) : instance(instance_p.shared_from_this()) { |
263 | } |
264 | |
265 | DuckDB::~DuckDB() { |
266 | } |
267 | |
268 | BufferManager &DatabaseInstance::GetBufferManager() { |
269 | return *buffer_manager; |
270 | } |
271 | |
272 | BufferPool &DatabaseInstance::GetBufferPool() { |
273 | return *config.buffer_pool; |
274 | } |
275 | |
276 | DatabaseManager &DatabaseManager::Get(DatabaseInstance &db) { |
277 | return db.GetDatabaseManager(); |
278 | } |
279 | |
280 | DatabaseManager &DatabaseManager::Get(ClientContext &db) { |
281 | return DatabaseManager::Get(db&: *db.db); |
282 | } |
283 | |
284 | TaskScheduler &DatabaseInstance::GetScheduler() { |
285 | return *scheduler; |
286 | } |
287 | |
288 | ObjectCache &DatabaseInstance::GetObjectCache() { |
289 | return *object_cache; |
290 | } |
291 | |
292 | FileSystem &DatabaseInstance::GetFileSystem() { |
293 | return *config.file_system; |
294 | } |
295 | |
296 | ConnectionManager &DatabaseInstance::GetConnectionManager() { |
297 | return *connection_manager; |
298 | } |
299 | |
300 | FileSystem &DuckDB::GetFileSystem() { |
301 | return instance->GetFileSystem(); |
302 | } |
303 | |
304 | Allocator &Allocator::Get(ClientContext &context) { |
305 | return Allocator::Get(db&: *context.db); |
306 | } |
307 | |
308 | Allocator &Allocator::Get(DatabaseInstance &db) { |
309 | return *db.config.allocator; |
310 | } |
311 | |
312 | Allocator &Allocator::Get(AttachedDatabase &db) { |
313 | return Allocator::Get(db&: db.GetDatabase()); |
314 | } |
315 | |
316 | void 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 | |
352 | DBConfig &DBConfig::GetConfig(ClientContext &context) { |
353 | return context.db->config; |
354 | } |
355 | |
356 | const DBConfig &DBConfig::GetConfig(const ClientContext &context) { |
357 | return context.db->config; |
358 | } |
359 | |
360 | idx_t DatabaseInstance::NumberOfThreads() { |
361 | return scheduler->NumberOfThreads(); |
362 | } |
363 | |
364 | const unordered_set<std::string> &DatabaseInstance::LoadedExtensions() { |
365 | return loaded_extensions; |
366 | } |
367 | |
368 | idx_t DuckDB::NumberOfThreads() { |
369 | return instance->NumberOfThreads(); |
370 | } |
371 | |
372 | bool 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 | |
377 | bool DuckDB::ExtensionIsLoaded(const std::string &name) { |
378 | return instance->ExtensionIsLoaded(name); |
379 | } |
380 | |
381 | void DatabaseInstance::SetExtensionLoaded(const std::string &name) { |
382 | auto extension_name = ExtensionHelper::GetExtensionName(extension: name); |
383 | loaded_extensions.insert(x: extension_name); |
384 | } |
385 | |
386 | bool 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 | |
400 | string ClientConfig::() 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 | |
409 | ValidChecker &DatabaseInstance::GetValidChecker() { |
410 | return db_validity; |
411 | } |
412 | |
413 | ValidChecker &ValidChecker::Get(DatabaseInstance &db) { |
414 | return db.GetValidChecker(); |
415 | } |
416 | |
417 | } // namespace duckdb |
418 | |