Coverage Report

Created: 2026-07-07 07:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/duckdb/src/main/config.cpp
Line
Count
Source
1
#include "duckdb/main/config.hpp"
2
3
#include "duckdb/common/cgroups.hpp"
4
#include "duckdb/common/file_system.hpp"
5
#include "duckdb/common/operator/cast_operators.hpp"
6
#include "duckdb/common/operator/multiply.hpp"
7
#include "duckdb/common/string_util.hpp"
8
#include "duckdb/main/database.hpp"
9
#include "duckdb/main/settings.hpp"
10
#include "duckdb/main/extension_helper.hpp"
11
#include "duckdb/storage/storage_extension.hpp"
12
#include "duckdb/common/serializer/serializer.hpp"
13
#include "duckdb/common/exception/parser_exception.hpp"
14
#include "duckdb/common/path.hpp"
15
16
#ifndef DUCKDB_NO_THREADS
17
#include "duckdb/common/thread.hpp"
18
#endif
19
20
#include <cinttypes>
21
#include <cstdio>
22
23
namespace duckdb {
24
25
#ifdef DEBUG
26
bool DBConfigOptions::debug_print_bindings = false;
27
#endif
28
DebugVerificationMode DBConfigOptions::global_verification_mode = DebugVerificationMode::NONE;
29
30
#define DUCKDB_SETTING(_PARAM)                                                                                         \
31
  {                                                                                                                  \
32
    _PARAM::Name, _PARAM::Description, _PARAM::InputType, nullptr, nullptr, nullptr, nullptr, nullptr,             \
33
        _PARAM::Scope, _PARAM::DefaultValue, nullptr, _PARAM::SettingIndex                                         \
34
  }
35
#define DUCKDB_SETTING_CALLBACK(_PARAM)                                                                                \
36
  {                                                                                                                  \
37
    _PARAM::Name, _PARAM::Description, _PARAM::InputType, nullptr, nullptr, nullptr, nullptr, nullptr,             \
38
        _PARAM::Scope, _PARAM::DefaultValue, _PARAM::OnSet, _PARAM::SettingIndex                                   \
39
  }
40
#define DUCKDB_GLOBAL(_PARAM)                                                                                          \
41
  {                                                                                                                  \
42
    _PARAM::Name, _PARAM::Description, _PARAM::InputType, _PARAM::SetGlobal, nullptr, _PARAM::ResetGlobal,         \
43
        nullptr, _PARAM::GetSetting, SettingScopeTarget::INVALID, nullptr, nullptr, optional_idx()                 \
44
  }
45
#define DUCKDB_LOCAL(_PARAM)                                                                                           \
46
  {                                                                                                                  \
47
    _PARAM::Name, _PARAM::Description, _PARAM::InputType, nullptr, _PARAM::SetLocal, nullptr, _PARAM::ResetLocal,  \
48
        _PARAM::GetSetting, SettingScopeTarget::INVALID, nullptr, nullptr, optional_idx()                          \
49
  }
50
#define DUCKDB_GLOBAL_LOCAL(_PARAM)                                                                                    \
51
  {                                                                                                                  \
52
    _PARAM::Name, _PARAM::Description, _PARAM::InputType, _PARAM::SetGlobal, _PARAM::SetLocal,                     \
53
        _PARAM::ResetGlobal, _PARAM::ResetLocal, _PARAM::GetSetting, SettingScopeTarget::INVALID, nullptr,         \
54
        nullptr, optional_idx()                                                                                    \
55
  }
56
#define FINAL_SETTING                                                                                                  \
57
  {                                                                                                                  \
58
    nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, SettingScopeTarget::INVALID, nullptr,  \
59
        nullptr, optional_idx()                                                                                    \
60
  }
61
62
#define DUCKDB_SETTING_ALIAS(_ALIAS, _SETTING_INDEX)                                                                   \
63
  { _ALIAS, _SETTING_INDEX }
64
#define FINAL_ALIAS                                                                                                    \
65
  { nullptr, 0 }
66
67
static const ConfigurationOption internal_options[] = {
68
69
    DUCKDB_GLOBAL(DeltaOnlyVariantEncodingEnabledSetting),
70
    DUCKDB_GLOBAL(AccessModeSetting),
71
    DUCKDB_SETTING_CALLBACK(AllocatorBackgroundThreadsSetting),
72
    DUCKDB_GLOBAL(AllocatorBulkDeallocationFlushThresholdSetting),
73
    DUCKDB_SETTING_CALLBACK(AllocatorFlushThresholdSetting),
74
    DUCKDB_SETTING_CALLBACK(AllowCommunityExtensionsSetting),
75
    DUCKDB_SETTING(AllowExtensionsMetadataMismatchSetting),
76
    DUCKDB_SETTING_CALLBACK(AllowParserOverrideExtensionSetting),
77
    DUCKDB_GLOBAL(AllowPersistentSecretsSetting),
78
    DUCKDB_SETTING_CALLBACK(AllowUnredactedSecretsSetting),
79
    DUCKDB_SETTING_CALLBACK(AllowUnsignedExtensionsSetting),
80
    DUCKDB_GLOBAL(AllowedConfigsSetting),
81
    DUCKDB_GLOBAL(AllowedDirectoriesSetting),
82
    DUCKDB_GLOBAL(AllowedPathsSetting),
83
    DUCKDB_SETTING(ApproximateJoinOrderThresholdSetting),
84
    DUCKDB_SETTING(ArrowLargeBufferSizeSetting),
85
    DUCKDB_SETTING(ArrowLosslessConversionSetting),
86
    DUCKDB_SETTING(ArrowOutputListViewSetting),
87
    DUCKDB_SETTING_CALLBACK(ArrowOutputVersionSetting),
88
    DUCKDB_SETTING(AsofLoopJoinThresholdSetting),
89
    DUCKDB_GLOBAL(AsyncThreadsSetting),
90
    DUCKDB_SETTING(AutoCheckpointSkipWalThresholdSetting),
91
    DUCKDB_SETTING(AutoinstallExtensionRepositorySetting),
92
    DUCKDB_SETTING(AutoinstallKnownExtensionsSetting),
93
    DUCKDB_SETTING(AutoloadKnownExtensionsSetting),
94
    DUCKDB_GLOBAL(BlockAllocatorMemorySetting),
95
    DUCKDB_SETTING(CacheLocalFilesSetting),
96
    DUCKDB_SETTING(CatalogErrorMaxSchemasSetting),
97
    DUCKDB_SETTING_CALLBACK(CheckpointOnDetachSetting),
98
    DUCKDB_GLOBAL(CheckpointThresholdSetting),
99
    DUCKDB_LOCAL(ConfigureProfilingSetting),
100
    DUCKDB_SETTING_CALLBACK(CurrentTransactionInvalidationPolicySetting),
101
    DUCKDB_SETTING(CustomExtensionRepositorySetting),
102
    DUCKDB_GLOBAL(CustomUserAgentSetting),
103
    DUCKDB_SETTING(DebugAsofIejoinSetting),
104
    DUCKDB_SETTING_CALLBACK(DebugCheckpointAbortSetting),
105
    DUCKDB_SETTING(DebugCheckpointSleepMsSetting),
106
    DUCKDB_SETTING(DebugDisableOptimizerSetting),
107
    DUCKDB_SETTING(DebugEvictionQueueSleepMicroSecondsSetting),
108
    DUCKDB_SETTING(DebugForceCommitFailureSetting),
109
    DUCKDB_SETTING(DebugForceCommitRevertFailureSetting),
110
    DUCKDB_SETTING(DebugForceExternalSetting),
111
    DUCKDB_SETTING(DebugForceFetchRowSetting),
112
    DUCKDB_SETTING(DebugForceNoCrossProductSetting),
113
    DUCKDB_SETTING(DebugLocalFileSystemDelayMsSetting),
114
    DUCKDB_GLOBAL(DebugOrderVerificationSetting),
115
    DUCKDB_SETTING_CALLBACK(DebugPhysicalTableScanExecutionStrategySetting),
116
    DUCKDB_SETTING(DebugSkipCheckpointOnCommitSetting),
117
    DUCKDB_SETTING(DebugTransformerTrampolineStyleSetting),
118
    DUCKDB_GLOBAL(DebugVerificationModeSetting),
119
    DUCKDB_SETTING(DebugVerificationProjectionSetting),
120
    DUCKDB_SETTING(DebugVerifyAggregateStateExportSetting),
121
    DUCKDB_SETTING(DebugVerifyBlocksSetting),
122
    DUCKDB_SETTING(DebugVerifyColumnBindingsSetting),
123
    DUCKDB_SETTING(DebugVerifySerializerSetting),
124
    DUCKDB_SETTING_CALLBACK(DebugVerifyStatementSetting),
125
    DUCKDB_SETTING(DebugVerifyStatsSetting),
126
    DUCKDB_SETTING_CALLBACK(DebugVerifyVectorSetting),
127
    DUCKDB_SETTING_CALLBACK(DebugWindowModeSetting),
128
    DUCKDB_SETTING_CALLBACK(DefaultBlockSizeSetting),
129
    DUCKDB_SETTING_CALLBACK(DefaultCollationSetting),
130
    DUCKDB_SETTING_CALLBACK(DefaultIoModeSetting),
131
    DUCKDB_SETTING_CALLBACK(DefaultNullOrderSetting),
132
    DUCKDB_SETTING_CALLBACK(DefaultOrderSetting),
133
    DUCKDB_GLOBAL(DefaultSecretStorageSetting),
134
    DUCKDB_SETTING_CALLBACK(DefaultTransactionInvalidationPolicySetting),
135
    DUCKDB_SETTING(DelimJoinAsCteSetting),
136
    DUCKDB_SETTING_CALLBACK(DeprecatedUsingKeySyntaxSetting),
137
    DUCKDB_SETTING_CALLBACK(DialectCompatibilityModeSetting),
138
    DUCKDB_SETTING_CALLBACK(DisableDatabaseInvalidationSetting),
139
    DUCKDB_SETTING(DisableTimestamptzCastsSetting),
140
    DUCKDB_GLOBAL(DisabledCompressionMethodsSetting),
141
    DUCKDB_GLOBAL(DisabledFilesystemsSetting),
142
    DUCKDB_GLOBAL(DisabledLogTypes),
143
    DUCKDB_GLOBAL(DisabledOptimizersSetting),
144
    DUCKDB_SETTING_CALLBACK(DuckDBAPISetting),
145
    DUCKDB_SETTING(DynamicOrFilterThresholdSetting),
146
    DUCKDB_SETTING(EnableCachingOperatorsSetting),
147
    DUCKDB_SETTING_CALLBACK(EnableExternalAccessSetting),
148
    DUCKDB_SETTING_CALLBACK(EnableExternalFileCacheSetting),
149
    DUCKDB_SETTING(EnableFSSTVectorsSetting),
150
    DUCKDB_SETTING(EnableHTTPMetadataCacheSetting),
151
    DUCKDB_GLOBAL(EnableLogging),
152
    DUCKDB_SETTING(EnableMacroDependenciesSetting),
153
    DUCKDB_SETTING(EnableObjectCacheSetting),
154
    DUCKDB_SETTING(EnableOptimizerSetting),
155
    DUCKDB_LOCAL(EnableProfilingSetting),
156
    DUCKDB_LOCAL(EnableProgressBarSetting),
157
    DUCKDB_LOCAL(EnableProgressBarPrintSetting),
158
    DUCKDB_SETTING(EnableViewDependenciesSetting),
159
    DUCKDB_GLOBAL(EnabledLogTypes),
160
    DUCKDB_SETTING(ErrorsAsJSONSetting),
161
    DUCKDB_SETTING(ExperimentalMetadataReuseSetting),
162
    DUCKDB_SETTING_CALLBACK(ExplainOutputSetting),
163
    DUCKDB_GLOBAL(ExtensionDirectoriesSetting),
164
    DUCKDB_SETTING(ExtensionDirectorySetting),
165
    DUCKDB_SETTING_CALLBACK(ExternalFileCacheLocalBlockSizeSetting),
166
    DUCKDB_SETTING_CALLBACK(ExternalFileCacheRemoteBlockSizeSetting),
167
    DUCKDB_SETTING_CALLBACK(ExternalThreadsSetting),
168
    DUCKDB_SETTING(FileSearchPathSetting),
169
    DUCKDB_SETTING_CALLBACK(ForceBitpackingModeSetting),
170
    DUCKDB_SETTING(ForceColumnMetadataReuseSetting),
171
    DUCKDB_SETTING_CALLBACK(ForceCompressionSetting),
172
    DUCKDB_GLOBAL(ForceMbedtlsUnsafeSetting),
173
    DUCKDB_SETTING(ForceUpdateToDelAndInsertSetting),
174
    DUCKDB_GLOBAL(ForceVariantShredding),
175
    DUCKDB_SETTING(GeometryMinimumShreddingSize),
176
    DUCKDB_SETTING_CALLBACK(HomeDirectorySetting),
177
    DUCKDB_GLOBAL(HTTPProxySetting),
178
    DUCKDB_SETTING(HTTPProxyPasswordSetting),
179
    DUCKDB_SETTING(HTTPProxyUsernameSetting),
180
    DUCKDB_SETTING(IeeeFloatingPointOpsSetting),
181
    DUCKDB_SETTING(IgnoreUnknownCrsSetting),
182
    DUCKDB_SETTING(ImmediateTransactionModeSetting),
183
    DUCKDB_SETTING(IndexScanMaxCountSetting),
184
    DUCKDB_SETTING_CALLBACK(IndexScanPercentageSetting),
185
    DUCKDB_SETTING_CALLBACK(InitialColumnSegmentSizeSetting),
186
    DUCKDB_SETTING(IntegerDivisionSetting),
187
    DUCKDB_SETTING_CALLBACK(LambdaSyntaxSetting),
188
    DUCKDB_SETTING(LateMaterializationMaxRowsSetting),
189
    DUCKDB_SETTING(LegacyDisableNullTypeSetting),
190
    DUCKDB_SETTING(LegacyMetricsFormatSetting),
191
    DUCKDB_SETTING(LockConfigurationSetting),
192
    DUCKDB_SETTING_CALLBACK(LogQueryPathSetting),
193
    DUCKDB_GLOBAL(LoggingLevel),
194
    DUCKDB_GLOBAL(LoggingMode),
195
    DUCKDB_GLOBAL(LoggingStorage),
196
    DUCKDB_SETTING(MaxExecutionTimeSetting),
197
    DUCKDB_SETTING(MaxExpressionDepthSetting),
198
    DUCKDB_GLOBAL(MaxMemorySetting),
199
    DUCKDB_GLOBAL(MaxTempDirectorySizeSetting),
200
    DUCKDB_SETTING(MaxVacuumTasksSetting),
201
    DUCKDB_SETTING(MergeJoinThresholdSetting),
202
    DUCKDB_SETTING(NestedLoopJoinThresholdSetting),
203
    DUCKDB_SETTING(OldImplicitCastingSetting),
204
    DUCKDB_LOCAL(OperatorMemoryLimitSetting),
205
    DUCKDB_SETTING(OrderByNonIntegerLiteralSetting),
206
    DUCKDB_SETTING_CALLBACK(OrderedAggregateThresholdSetting),
207
    DUCKDB_SETTING(ParallelizeSequentialSourcesSetting),
208
    DUCKDB_SETTING(PartitionedWriteFlushThresholdSetting),
209
    DUCKDB_SETTING(PartitionedWriteMaxOpenFilesSetting),
210
    DUCKDB_SETTING(PasswordSetting),
211
    DUCKDB_SETTING_CALLBACK(PerfectHtThresholdSetting),
212
    DUCKDB_SETTING_CALLBACK(PinThreadsSetting),
213
    DUCKDB_SETTING(PivotFilterThresholdSetting),
214
    DUCKDB_SETTING(PivotLimitSetting),
215
    DUCKDB_SETTING(PreferRangeJoinsSetting),
216
    DUCKDB_SETTING(PreserveIdentifierCaseSetting),
217
    DUCKDB_SETTING(PreserveInsertionOrderSetting),
218
    DUCKDB_SETTING(ProduceArrowStringViewSetting),
219
    DUCKDB_LOCAL(ProfilingCoverageSetting),
220
    DUCKDB_LOCAL(ProfilingModeSetting),
221
    DUCKDB_LOCAL(ProfilingOutputSetting),
222
    DUCKDB_LOCAL(ProfilingRendererSettingsSetting),
223
    DUCKDB_LOCAL(ProgressBarTimeSetting),
224
    DUCKDB_SETTING_CALLBACK(RegexMatchOperatorSemanticsSetting),
225
    DUCKDB_SETTING(ScalarSubqueryErrorOnMultipleRowsSetting),
226
    DUCKDB_SETTING(SchedulerProcessPartialSetting),
227
    DUCKDB_LOCAL(SchemaSetting),
228
    DUCKDB_LOCAL(SearchPathSetting),
229
    DUCKDB_GLOBAL(SecretDirectorySetting),
230
    DUCKDB_GLOBAL(StandardVectorSizeSetting),
231
    DUCKDB_SETTING_CALLBACK(StorageBlockPrefetchSetting),
232
    DUCKDB_GLOBAL(StorageCompatibilityVersionSetting),
233
    DUCKDB_LOCAL(StreamingBufferSizeSetting),
234
    DUCKDB_SETTING_CALLBACK(TableFunctionIdentifierConversionSetting),
235
    DUCKDB_GLOBAL(TempDirectorySetting),
236
    DUCKDB_SETTING_CALLBACK(TempFileEncryptionSetting),
237
    DUCKDB_GLOBAL(ThreadsSetting),
238
    DUCKDB_LOCAL(TrackedMetricsSetting),
239
    DUCKDB_SETTING(UsernameSetting),
240
    DUCKDB_SETTING_CALLBACK(VacuumRebuildIndexesSetting),
241
    DUCKDB_SETTING_CALLBACK(ValidateExternalFileCacheSetting),
242
    DUCKDB_SETTING(VariantMinimumShreddingSizeSetting),
243
    DUCKDB_SETTING(WalAutocheckpointEntriesSetting),
244
    DUCKDB_SETTING_CALLBACK(WarningsAsErrorsSetting),
245
    DUCKDB_SETTING(WriteBufferRowGroupCountSetting),
246
    DUCKDB_GLOBAL(WriteBufferRowGroupMemoryLimitSetting),
247
    DUCKDB_SETTING(ZstdMinStringLengthSetting),
248
    FINAL_SETTING};
249
250
static const ConfigurationAlias setting_aliases[] = {DUCKDB_SETTING_ALIAS("configure_metrics", 30),
251
                                                     DUCKDB_SETTING_ALIAS("custom_profiling_settings", 30),
252
                                                     DUCKDB_SETTING_ALIAS("memory_limit", 129),
253
                                                     DUCKDB_SETTING_ALIAS("null_order", 62),
254
                                                     DUCKDB_SETTING_ALIAS("profile_output", 152),
255
                                                     DUCKDB_SETTING_ALIAS("user", 170),
256
                                                     DUCKDB_SETTING_ALIAS("wal_autocheckpoint", 29),
257
                                                     DUCKDB_SETTING_ALIAS("worker_threads", 168),
258
                                                     FINAL_ALIAS};
259
260
0
vector<ConfigurationOption> DBConfig::GetOptions() {
261
0
  vector<ConfigurationOption> options;
262
0
  for (idx_t index = 0; internal_options[index].name; index++) {
263
0
    options.push_back(internal_options[index]);
264
0
  }
265
0
  return options;
266
0
}
267
268
0
vector<ConfigurationAlias> DBConfig::GetAliases() {
269
0
  vector<ConfigurationAlias> aliases;
270
0
  for (idx_t index = 0; index < GetAliasCount(); index++) {
271
0
    aliases.push_back(setting_aliases[index]);
272
0
  }
273
0
  return aliases;
274
0
}
275
276
SettingCallbackInfo::SettingCallbackInfo(ClientContext &context_p, SetScope scope)
277
0
    : config(DBConfig::GetConfig(context_p)), db(context_p.db.get()), context(context_p), scope(scope) {
278
0
}
279
280
SettingCallbackInfo::SettingCallbackInfo(DBConfig &config, optional_ptr<DatabaseInstance> db)
281
7.23k
    : config(config), db(db), context(nullptr), scope(SetScope::GLOBAL) {
282
7.23k
}
283
284
0
idx_t DBConfig::GetOptionCount() {
285
0
  return sizeof(internal_options) / sizeof(ConfigurationOption) - 1;
286
0
}
287
288
0
idx_t DBConfig::GetAliasCount() {
289
0
  return sizeof(setting_aliases) / sizeof(ConfigurationAlias) - 1;
290
0
}
291
292
3
vector<string> DBConfig::GetOptionNames() {
293
3
  vector<string> names;
294
540
  for (idx_t index = 0; internal_options[index].name; index++) {
295
537
    names.emplace_back(internal_options[index].name);
296
537
  }
297
27
  for (idx_t index = 0; setting_aliases[index].alias; index++) {
298
24
    names.emplace_back(setting_aliases[index].alias);
299
24
  }
300
3
  return names;
301
3
}
302
303
0
optional_ptr<const ConfigurationOption> DBConfig::GetOptionByIndex(idx_t target_index) {
304
0
  if (target_index >= GetOptionCount()) {
305
0
    return nullptr;
306
0
  }
307
0
  return internal_options + target_index;
308
0
}
309
310
0
optional_ptr<const ConfigurationAlias> DBConfig::GetAliasByIndex(idx_t target_index) {
311
0
  if (target_index >= GetAliasCount()) {
312
0
    return nullptr;
313
0
  }
314
0
  return setting_aliases + target_index;
315
0
}
316
317
62.1k
optional_ptr<const ConfigurationOption> DBConfig::GetOptionByName(const String &name) {
318
62.1k
  auto lname = name.Lower();
319
8.62M
  for (idx_t index = 0; internal_options[index].name; index++) {
320
8.59M
    D_ASSERT(StringUtil::Lower(internal_options[index].name) == string(internal_options[index].name));
321
8.59M
    if (internal_options[index].name == lname) {
322
29.6k
      return internal_options + index;
323
29.6k
    }
324
8.59M
  }
325
293k
  for (idx_t index = 0; setting_aliases[index].alias; index++) {
326
260k
    D_ASSERT(StringUtil::Lower(internal_options[index].name) == string(internal_options[index].name));
327
260k
    if (setting_aliases[index].alias == lname) {
328
0
      return GetOptionByIndex(setting_aliases[index].option_index);
329
0
    }
330
260k
  }
331
32.5k
  return nullptr;
332
32.5k
}
333
334
7.23k
void DBConfig::SetOption(const ConfigurationOption &option, const Value &value) {
335
7.23k
  SetOption(nullptr, option, value);
336
7.23k
}
337
338
7.23k
void DBConfig::SetOptionByName(const string &name, const Value &value) {
339
7.23k
  if (is_user_config) {
340
    // for user config we just set the option in the `user_options`
341
0
    options.user_options[name] = value;
342
0
  }
343
7.23k
  auto option = DBConfig::GetOptionByName(name);
344
7.23k
  if (option) {
345
7.23k
    SetOption(*option, value);
346
7.23k
    return;
347
7.23k
  }
348
349
0
  ExtensionOption extension_option;
350
0
  if (TryGetExtensionOption(name, extension_option)) {
351
0
    Value target_value = value.DefaultCastAs(extension_option.type);
352
0
    SetOption(extension_option.setting_index.GetIndex(), std::move(target_value));
353
0
  } else {
354
0
    options.unrecognized_options[name] = value;
355
0
  }
356
0
}
357
358
0
void DBConfig::SetOptionsByName(const case_insensitive_map_t<Value> &values) {
359
0
  for (auto &kv : values) {
360
0
    auto &name = kv.first;
361
0
    auto &value = kv.second;
362
0
    SetOptionByName(name, value);
363
0
  }
364
0
}
365
366
7.23k
void DBConfig::SetOption(optional_ptr<DatabaseInstance> db, const ConfigurationOption &option, const Value &value) {
367
7.23k
  Value input = value.DefaultCastAs(ParseLogicalType(option.parameter_type));
368
7.23k
  if (option.default_value) {
369
    // generic option
370
7.23k
    if (option.set_callback) {
371
7.23k
      SettingCallbackInfo info(*this, db);
372
7.23k
      option.set_callback(info, input);
373
7.23k
    }
374
7.23k
    user_settings.SetUserSetting(option.setting_idx.GetIndex(), std::move(input));
375
7.23k
    return;
376
7.23k
  }
377
1
  if (!option.set_global) {
378
0
    throw InvalidInputException("Could not set option \"%s\" as a global option", option.name);
379
0
  }
380
1
  lock_guard<mutex> guard(config_lock);
381
1
  D_ASSERT(option.reset_global);
382
1
  option.set_global(db.get(), *this, input);
383
1
}
384
385
0
void DBConfig::ResetOption(optional_ptr<DatabaseInstance> db, const ConfigurationOption &option) {
386
0
  if (option.default_value) {
387
    // generic option
388
0
    user_settings.ClearSetting(option.setting_idx.GetIndex());
389
0
    return;
390
0
  }
391
0
  if (!option.reset_global) {
392
0
    throw InternalException("Could not reset option \"%s\" as a global option", option.name);
393
0
  }
394
0
  lock_guard<mutex> guard(config_lock);
395
0
  D_ASSERT(option.set_global);
396
0
  option.reset_global(db.get(), *this);
397
0
}
398
399
0
void DBConfig::SetOption(idx_t setting_index, Value value) {
400
0
  user_settings.SetUserSetting(setting_index, std::move(value));
401
0
}
402
403
0
void DBConfig::SetOption(const string &name, Value value) {
404
0
  optional_ptr<const ConfigurationOption> option;
405
0
  auto setting_index = TryGetSettingIndex(name, option);
406
0
  if (!setting_index.IsValid()) {
407
0
    throw InternalException("Unrecognized option %s in DBConfig::SetOption", name);
408
0
  }
409
0
  SetOption(setting_index.GetIndex(), std::move(value));
410
0
}
411
412
0
void DBConfig::ResetOption(const ExtensionOption &extension_option) {
413
0
  auto &default_value = extension_option.default_value;
414
0
  auto setting_index = extension_option.setting_index.GetIndex();
415
0
  if (!default_value.IsNull()) {
416
    // Default is not NULL, override the setting
417
0
    user_settings.SetUserSetting(setting_index, default_value);
418
0
  } else {
419
    // Otherwise just remove it from the 'set_variables' map
420
0
    user_settings.ClearSetting(setting_index);
421
0
  }
422
0
}
423
424
0
void DBConfig::ResetGenericOption(idx_t setting_index) {
425
0
  user_settings.ClearSetting(setting_index);
426
0
}
427
428
5.03M
LogicalType DBConfig::ParseLogicalType(const string &type) {
429
5.03M
  if (StringUtil::EndsWith(type, "[]")) {
430
    // list - recurse
431
282k
    auto child_type = ParseLogicalType(type.substr(0, type.size() - 2));
432
282k
    return LogicalType::LIST(child_type);
433
282k
  }
434
435
4.74M
  if (StringUtil::EndsWith(type, "]")) {
436
    // array - recurse
437
0
    auto bracket_open_idx = type.rfind('[');
438
0
    if (bracket_open_idx == string::npos || bracket_open_idx == 0) {
439
0
      throw InternalException("Ill formatted type: '%s'", type);
440
0
    }
441
0
    idx_t array_size = 0;
442
0
    for (auto length_idx = bracket_open_idx + 1; length_idx < type.size() - 1; length_idx++) {
443
0
      if (!isdigit(type[length_idx])) {
444
0
        throw InternalException("Ill formatted array type: '%s'", type);
445
0
      }
446
0
      array_size = array_size * 10 + static_cast<idx_t>(type[length_idx] - '0');
447
0
    }
448
0
    if (array_size == 0 || array_size > ArrayType::MAX_ARRAY_SIZE) {
449
0
      throw InternalException("Invalid array size: '%s'", type);
450
0
    }
451
0
    auto child_type = ParseLogicalType(type.substr(0, bracket_open_idx));
452
0
    return LogicalType::ARRAY(child_type, array_size);
453
0
  }
454
455
4.74M
  if (StringUtil::StartsWith(type, "MAP(") && StringUtil::EndsWith(type, ")")) {
456
    // map - recurse
457
7.23k
    string map_args = type.substr(4, type.size() - 5);
458
7.23k
    vector<string> map_args_vect = StringUtil::SplitWithParentheses(map_args);
459
7.23k
    if (map_args_vect.size() != 2) {
460
0
      throw InternalException("Ill formatted map type: '%s'", type);
461
0
    }
462
7.23k
    StringUtil::Trim(map_args_vect[0]);
463
7.23k
    StringUtil::Trim(map_args_vect[1]);
464
7.23k
    auto key_type = ParseLogicalType(map_args_vect[0]);
465
7.23k
    auto value_type = ParseLogicalType(map_args_vect[1]);
466
7.23k
    return LogicalType::MAP(key_type, value_type);
467
7.23k
  }
468
469
4.74M
  if (StringUtil::StartsWith(type, "UNION(") && StringUtil::EndsWith(type, ")")) {
470
    // union - recurse
471
0
    string union_members_str = type.substr(6, type.size() - 7);
472
0
    vector<string> union_members_vect = StringUtil::SplitWithParentheses(union_members_str);
473
0
    child_list_t<LogicalType> union_members;
474
0
    for (idx_t member_idx = 0; member_idx < union_members_vect.size(); member_idx++) {
475
0
      StringUtil::Trim(union_members_vect[member_idx]);
476
0
      vector<string> union_member_parts = StringUtil::SplitWithParentheses(union_members_vect[member_idx], ' ');
477
0
      if (union_member_parts.size() != 2) {
478
0
        throw InternalException("Ill formatted union type: %s", type);
479
0
      }
480
0
      StringUtil::Trim(union_member_parts[0]);
481
0
      StringUtil::Trim(union_member_parts[1]);
482
0
      auto value_type = ParseLogicalType(union_member_parts[1]);
483
0
      union_members.emplace_back(make_pair(union_member_parts[0], value_type));
484
0
    }
485
0
    if (union_members.empty() || union_members.size() > UnionType::MAX_UNION_MEMBERS) {
486
0
      throw InternalException("Invalid number of union members: '%s'", type);
487
0
    }
488
0
    return LogicalType::UNION(union_members);
489
0
  }
490
491
4.74M
  if (type == "STRUCT") {
492
    // empty struct
493
115k
    return LogicalType::STRUCT({});
494
115k
  }
495
496
4.62M
  if (StringUtil::StartsWith(type, "STRUCT(") && StringUtil::EndsWith(type, ")")) {
497
    // struct - recurse
498
7.23k
    string struct_members_str = type.substr(7, type.size() - 8);
499
7.23k
    vector<string> struct_members_vect = StringUtil::SplitWithParentheses(struct_members_str);
500
7.23k
    child_list_t<LogicalType> struct_members;
501
28.9k
    for (idx_t member_idx = 0; member_idx < struct_members_vect.size(); member_idx++) {
502
21.6k
      StringUtil::Trim(struct_members_vect[member_idx]);
503
21.6k
      vector<string> struct_member_parts = StringUtil::SplitWithParentheses(struct_members_vect[member_idx], ' ');
504
21.6k
      if (struct_member_parts.size() != 2) {
505
0
        throw InternalException("Ill formatted struct type: %s", type);
506
0
      }
507
21.6k
      StringUtil::Trim(struct_member_parts[0]);
508
21.6k
      StringUtil::Trim(struct_member_parts[1]);
509
21.6k
      auto value_type = ParseLogicalType(struct_member_parts[1]);
510
21.6k
      struct_members.emplace_back(make_pair(struct_member_parts[0], value_type));
511
21.6k
    }
512
7.23k
    return LogicalType::STRUCT(struct_members);
513
7.23k
  }
514
515
4.61M
  const auto type_id = StringUtil::CIEquals(type, "ANY") ? LogicalTypeId::ANY : TransformStringToLogicalTypeId(type);
516
4.61M
  if (type_id == LogicalTypeId::UNBOUND) {
517
0
    throw InternalException("Error while generating extension function overloads - unrecognized logical type %s",
518
0
                            type);
519
0
  }
520
4.61M
  return type_id;
521
4.61M
}
522
523
0
bool DBConfig::HasExtensionOption(const string &name) const {
524
0
  return user_settings.HasExtensionOption(name);
525
0
}
526
527
47.4k
bool DBConfig::TryGetExtensionOption(const String &name, ExtensionOption &result) const {
528
47.4k
  return user_settings.TryGetExtensionOption(name, result);
529
47.4k
}
530
531
void DBConfig::AddExtensionOption(const string &name, string description, LogicalType parameter,
532
43.3k
                                  const Value &default_value, set_option_callback_t function, SetScope default_scope) {
533
43.3k
  ExtensionOption extension_option(std::move(description), std::move(parameter), function, default_value,
534
43.3k
                                   default_scope);
535
43.3k
  auto setting_index = user_settings.AddExtensionOption(name, std::move(extension_option));
536
  // copy over unrecognized options, if they match the new extension option
537
43.3k
  auto iter = options.unrecognized_options.find(name);
538
43.3k
  if (iter != options.unrecognized_options.end()) {
539
0
    user_settings.SetUserSetting(setting_index, iter->second);
540
0
    options.unrecognized_options.erase(iter);
541
0
  }
542
43.3k
  if (!default_value.IsNull() && !user_settings.IsSet(setting_index)) {
543
    // Default value is set, insert it into the 'set_variables' list
544
36.1k
    user_settings.SetUserSetting(setting_index, default_value);
545
36.1k
  }
546
43.3k
}
547
548
3
case_insensitive_map_t<ExtensionOption> DBConfig::GetExtensionSettings() const {
549
3
  return user_settings.GetExtensionSettings();
550
3
}
551
552
7.23k
bool DBConfig::IsInMemoryDatabase(const char *database_path) {
553
7.23k
  if (!database_path) {
554
    // Entirely empty
555
0
    return true;
556
0
  }
557
7.23k
  if (strlen(database_path) == 0) {
558
    // '' empty string
559
7.23k
    return true;
560
7.23k
  }
561
0
  if (strcmp(database_path, ":memory:") == 0) {
562
0
    return true;
563
0
  }
564
0
  return false;
565
0
}
566
567
1.65M
CastFunctionSet &DBConfig::GetCastFunctions() {
568
1.65M
  return type_manager->GetCastFunctions();
569
1.65M
}
570
571
1.14k
TypeManager &DBConfig::GetTypeManager() {
572
1.14k
  return *type_manager;
573
1.14k
}
574
575
519
CollationBinding &DBConfig::GetCollationBinding() {
576
519
  return *collation_bindings;
577
519
}
578
579
0
IndexTypeSet &DBConfig::GetIndexTypes() {
580
0
  return *index_types;
581
0
}
582
583
7.23k
void DBConfig::SetDefaultMaxMemory() {
584
7.23k
  auto memory = GetSystemAvailableMemory(*file_system);
585
7.23k
  if (memory == DBConfigOptions().maximum_memory) {
586
    // If GetSystemAvailableMemory returned the default, use it as is
587
0
    options.maximum_memory = memory;
588
7.23k
  } else {
589
    // Otherwise, use 80% of the available memory
590
7.23k
    options.maximum_memory = memory * 8 / 10;
591
7.23k
  }
592
7.23k
}
593
594
7.23k
void DBConfig::SetDefaultTempDirectory() {
595
7.23k
  if (!options.use_temporary_directory) {
596
0
    options.temporary_directory = string();
597
7.23k
  } else if (DBConfig::IsInMemoryDatabase(options.database_path.c_str())) {
598
7.23k
    options.temporary_directory = ".tmp";
599
7.23k
  } else {
600
0
    options.temporary_directory = Path::AddSuffixToPath(options.database_path, ".tmp");
601
0
  }
602
7.23k
}
603
604
13.1k
void DBConfig::CheckLock(const String &name) {
605
13.1k
  if (!Settings::Get<LockConfigurationSetting>(*this)) {
606
    // not locked
607
13.1k
    return;
608
13.1k
  }
609
0
  identifier_set_t allowed_settings {"schema", "search_path"};
610
0
  if (allowed_settings.find(Identifier(name.ToStdString())) != allowed_settings.end()) {
611
    // we are always allowed to change these settings
612
0
    return;
613
0
  }
614
0
  if (!options.allowed_configs.empty()) {
615
0
    auto option = GetOptionByName(name);
616
0
    auto canonical_name = option ? string(option->name) : name.ToStdString();
617
0
    if (options.allowed_configs.find(Identifier(canonical_name)) != options.allowed_configs.end()) {
618
      // settings that are allowed through allowed_configs
619
0
      return;
620
0
    }
621
0
  }
622
  // not allowed!
623
0
  throw InvalidInputException("Cannot change configuration option \"%s\" - the configuration has been locked", name);
624
0
}
625
626
14.4k
idx_t DBConfig::GetSystemMaxThreads(FileSystem &fs) {
627
#ifdef DUCKDB_NO_THREADS
628
  return 1;
629
#else
630
14.4k
  idx_t physical_cores = std::thread::hardware_concurrency();
631
14.4k
#ifdef __linux__
632
14.4k
  if (const char *slurm_cpus = getenv("SLURM_CPUS_ON_NODE")) {
633
0
    idx_t slurm_threads;
634
0
    if (TryCast::Operation<string_t, idx_t>(string_t(slurm_cpus), slurm_threads)) {
635
0
      return MaxValue<idx_t>(slurm_threads, 1);
636
0
    }
637
0
  }
638
14.4k
  return MaxValue<idx_t>(CGroups::GetCPULimit(fs, physical_cores), 1);
639
#else
640
  return MaxValue<idx_t>(physical_cores, 1);
641
#endif
642
14.4k
#endif
643
14.4k
}
644
645
7.23k
idx_t DBConfig::GetSystemMaxAsyncThreads(FileSystem &fs) {
646
#ifdef DUCKDB_NO_THREADS
647
  return 0;
648
#else
649
  // via benchmark, it seems that ~2x system threads is the I/O-concurrency sweet spot for remote (S3) scans
650
7.23k
  return 2 * GetSystemMaxThreads(fs);
651
7.23k
#endif
652
7.23k
}
653
654
14.4k
idx_t DBConfig::GetSystemAvailableMemory(FileSystem &fs) {
655
  // System memory detection
656
14.4k
  auto memory = FileSystem::GetAvailableMemory();
657
14.4k
  auto available_memory = memory.IsValid() ? memory.GetIndex() : DBConfigOptions().maximum_memory;
658
659
14.4k
#ifdef __linux__
660
  // Check SLURM environment variables first
661
14.4k
  const char *slurm_mem_per_node = getenv("SLURM_MEM_PER_NODE");
662
14.4k
  const char *slurm_mem_per_cpu = getenv("SLURM_MEM_PER_CPU");
663
664
14.4k
  if (slurm_mem_per_node) {
665
0
    auto limit = ParseMemoryLimitSlurm(slurm_mem_per_node);
666
0
    if (limit.IsValid()) {
667
0
      return limit.GetIndex();
668
0
    }
669
14.4k
  } else if (slurm_mem_per_cpu) {
670
0
    auto mem_per_cpu = ParseMemoryLimitSlurm(slurm_mem_per_cpu);
671
0
    if (mem_per_cpu.IsValid()) {
672
0
      idx_t num_threads = GetSystemMaxThreads(fs);
673
0
      return mem_per_cpu.GetIndex() * num_threads;
674
0
    }
675
0
  }
676
677
  // Check cgroup memory limit
678
14.4k
  auto cgroup_memory_limit = CGroups::GetMemoryLimit(fs);
679
14.4k
  if (cgroup_memory_limit.IsValid()) {
680
14.4k
    auto cgroup_memory_limit_value = cgroup_memory_limit.GetIndex();
681
14.4k
    return std::min(cgroup_memory_limit_value, available_memory);
682
14.4k
  }
683
0
#endif
684
685
0
  return available_memory;
686
14.4k
}
687
688
0
idx_t DBConfig::ParseMemoryLimit(const string &arg) {
689
0
  if (arg[0] == '-' || arg == "null" || arg == "none") {
690
    // infinite
691
0
    return NumericLimits<idx_t>::Maximum();
692
0
  }
693
694
0
  idx_t result;
695
0
  string error = StringUtil::TryParseFormattedBytes(arg, result);
696
697
0
  if (!error.empty()) {
698
0
    if (error == "Memory cannot be negative") {
699
0
      result = DConstants::INVALID_INDEX;
700
0
    } else {
701
0
      throw ParserException(error);
702
0
    }
703
0
  }
704
705
0
  return result;
706
0
}
707
708
0
optional_idx DBConfig::ParseMemoryLimitSlurm(const string &arg) {
709
0
  if (arg.empty()) {
710
0
    return optional_idx();
711
0
  }
712
713
0
  string number_str = arg;
714
0
  idx_t multiplier = 1000LL * 1000LL; // Default to MB if no unit specified
715
716
  // Check for SLURM-style suffixes
717
0
  if (arg.back() == 'K' || arg.back() == 'k') {
718
0
    number_str = arg.substr(0, arg.size() - 1);
719
0
    multiplier = 1000LL;
720
0
  } else if (arg.back() == 'M' || arg.back() == 'm') {
721
0
    number_str = arg.substr(0, arg.size() - 1);
722
0
    multiplier = 1000LL * 1000LL;
723
0
  } else if (arg.back() == 'G' || arg.back() == 'g') {
724
0
    number_str = arg.substr(0, arg.size() - 1);
725
0
    multiplier = 1000LL * 1000LL * 1000LL;
726
0
  } else if (arg.back() == 'T' || arg.back() == 't') {
727
0
    number_str = arg.substr(0, arg.size() - 1);
728
0
    multiplier = 1000LL * 1000LL * 1000LL * 1000LL;
729
0
  }
730
731
  // Parse the number
732
0
  double limit;
733
0
  if (!TryCast::Operation<string_t, double>(string_t(number_str), limit)) {
734
0
    return optional_idx();
735
0
  }
736
737
0
  if (limit < 0) {
738
0
    return static_cast<idx_t>(NumericLimits<int64_t>::Maximum());
739
0
  }
740
0
  idx_t actual_limit = LossyNumericCast<idx_t>(static_cast<double>(multiplier) * limit);
741
0
  if (actual_limit == NumericLimits<idx_t>::Maximum()) {
742
0
    return static_cast<idx_t>(NumericLimits<int64_t>::Maximum());
743
0
  }
744
0
  return actual_limit;
745
0
}
746
747
// Right now we only really care about access mode when comparing DBConfigs
748
0
bool DBConfigOptions::operator==(const DBConfigOptions &other) const {
749
0
  return other.access_mode == access_mode && other.user_options == user_options;
750
0
}
751
752
0
bool DBConfig::operator==(const DBConfig &other) {
753
0
  return other.options == options;
754
0
}
755
756
0
bool DBConfig::operator!=(const DBConfig &other) {
757
0
  return !(other.options == options);
758
0
}
759
760
9
OrderType DBConfig::ResolveOrder(ClientContext &context, OrderType order_type) const {
761
9
  if (order_type != OrderType::ORDER_DEFAULT) {
762
0
    return order_type;
763
0
  }
764
9
  return Settings::Get<DefaultOrderSetting>(context);
765
9
}
766
767
1.23M
SettingLookupResult DBConfig::TryGetCurrentUserSetting(idx_t setting_index, Value &result) const {
768
1.23M
  return user_settings.TryGetSetting(setting_index, result);
769
1.23M
}
770
771
39.8k
SettingLookupResult DBConfig::TryGetDefaultValue(optional_ptr<const ConfigurationOption> option, Value &result) {
772
39.8k
  if (!option || !option->default_value) {
773
32.5k
    return SettingLookupResult();
774
32.5k
  }
775
7.23k
  auto input_type = ParseLogicalType(option->parameter_type);
776
7.23k
  result = Value(option->default_value).DefaultCastAs(input_type);
777
7.23k
  return SettingLookupResult(SettingScope::GLOBAL);
778
39.8k
}
779
780
7.23k
SettingLookupResult DBConfig::TryGetCurrentSetting(const string &key, Value &result) const {
781
7.23k
  optional_ptr<const ConfigurationOption> option;
782
7.23k
  auto setting_index = TryGetSettingIndex(key, option);
783
7.23k
  if (setting_index.IsValid()) {
784
7.23k
    auto lookup_result = TryGetCurrentUserSetting(setting_index.GetIndex(), result);
785
7.23k
    if (lookup_result) {
786
0
      return lookup_result;
787
0
    }
788
7.23k
  }
789
7.23k
  return TryGetDefaultValue(option, result);
790
7.23k
}
791
792
47.4k
optional_idx DBConfig::TryGetSettingIndex(const String &name, optional_ptr<const ConfigurationOption> &option) const {
793
47.4k
  ExtensionOption extension_option;
794
47.4k
  if (TryGetExtensionOption(name, extension_option)) {
795
    // extension setting
796
5.69k
    return extension_option.setting_index;
797
5.69k
  }
798
41.7k
  option = GetOptionByName(name);
799
41.7k
  if (option) {
800
    // built-in setting
801
9.17k
    return option->setting_idx;
802
9.17k
  }
803
  // unknown setting
804
32.5k
  return optional_idx();
805
41.7k
}
806
807
OrderByNullType DBConfig::ResolveNullOrder(ClientContext &context, OrderType order_type,
808
9
                                           OrderByNullType null_type) const {
809
9
  if (null_type != OrderByNullType::ORDER_DEFAULT) {
810
0
    return null_type;
811
0
  }
812
9
  auto null_order = Settings::Get<DefaultNullOrderSetting>(context);
813
9
  switch (null_order) {
814
0
  case DefaultOrderByNullType::NULLS_FIRST:
815
0
    return OrderByNullType::NULLS_FIRST;
816
9
  case DefaultOrderByNullType::NULLS_LAST:
817
9
    return OrderByNullType::NULLS_LAST;
818
0
  case DefaultOrderByNullType::NULLS_FIRST_ON_ASC_LAST_ON_DESC:
819
0
    return order_type == OrderType::ASCENDING ? OrderByNullType::NULLS_FIRST : OrderByNullType::NULLS_LAST;
820
0
  case DefaultOrderByNullType::NULLS_LAST_ON_ASC_FIRST_ON_DESC:
821
0
    return order_type == OrderType::ASCENDING ? OrderByNullType::NULLS_LAST : OrderByNullType::NULLS_FIRST;
822
0
  default:
823
0
    throw InternalException("Unknown null order setting");
824
9
  }
825
9
}
826
827
0
string GetDefaultUserAgent() {
828
0
  return StringUtil::Format("duckdb/%s(%s)", DuckDB::LibraryVersion(), DuckDB::Platform());
829
0
}
830
831
0
const string DBConfig::UserAgent() const {
832
0
  auto user_agent = GetDefaultUserAgent();
833
834
0
  auto duckdb_api = Settings::Get<DuckDBAPISetting>(*this);
835
0
  if (!duckdb_api.empty()) {
836
0
    user_agent += " " + duckdb_api;
837
0
  }
838
839
0
  if (!options.custom_user_agent.empty()) {
840
0
    user_agent += " " + options.custom_user_agent;
841
0
  }
842
0
  return user_agent;
843
0
}
844
845
194k
ExtensionCallbackManager &DBConfig::GetCallbackManager() {
846
194k
  return *callback_manager;
847
194k
}
848
849
37.7k
const ExtensionCallbackManager &DBConfig::GetCallbackManager() const {
850
37.7k
  return *callback_manager;
851
37.7k
}
852
853
0
string DBConfig::SanitizeAllowedPath(const string &path_p) const {
854
0
  auto result = file_system->CanonicalizePath(path_p);
855
  // allowed_directories/allowed_path always uses forward slashes regardless of the OS
856
0
  auto path_sep = file_system->PathSeparator(path_p);
857
0
  if (path_sep != "/") {
858
0
    result = StringUtil::Replace(result, path_sep, "/");
859
0
  }
860
0
  return result;
861
0
}
862
863
0
void DBConfig::AddAllowedConfig(const string &config_name) {
864
0
  if (config_name.empty()) {
865
0
    throw InvalidInputException("Cannot provide an empty string for allowed_configs");
866
0
  }
867
0
  duckdb::identifier_set_t always_disallowed_config {"allowed_configs", "lock_configuration"};
868
0
  if (always_disallowed_config.find(Identifier(config_name)) != always_disallowed_config.end()) {
869
0
    throw InvalidInputException("Cannot include '%s' in allowed_configs", config_name);
870
0
  }
871
  // Validate that the config name refers to a known setting (built-in or extension)
872
  // and resolve aliases to canonical names
873
0
  auto option = GetOptionByName(config_name);
874
0
  if (option) {
875
    // Store the canonical name so alias lookups work in CheckLock
876
0
    options.allowed_configs.insert(option->name);
877
0
    return;
878
0
  }
879
0
  ExtensionOption extension_option;
880
0
  if (TryGetExtensionOption(config_name, extension_option)) {
881
0
    options.allowed_configs.insert(Identifier(config_name));
882
0
    return;
883
0
  }
884
  // Check if the setting belongs to a known extension (even if not yet loaded)
885
0
  auto extension_name = ExtensionHelper::FindExtensionInEntries(config_name, EXTENSION_SETTINGS);
886
0
  if (!extension_name.empty()) {
887
    // Accept the setting - the extension may be autoloaded later when the setting is used
888
0
    options.allowed_configs.insert(Identifier(config_name));
889
0
    return;
890
0
  }
891
0
  throw InvalidInputException("Unknown configuration option '%s' in allowed_configs", config_name);
892
0
}
893
894
0
void DBConfig::AddAllowedDirectory(const string &path) {
895
0
  auto allowed_directory = SanitizeAllowedPath(path);
896
0
  if (allowed_directory.empty()) {
897
0
    throw InvalidInputException("Cannot provide an empty string for allowed_directory");
898
0
  }
899
  // ensure the directory ends with a path separator
900
0
  if (!StringUtil::EndsWith(allowed_directory, "/")) {
901
0
    allowed_directory += "/";
902
0
  }
903
0
  options.allowed_directories.insert(allowed_directory);
904
0
}
905
906
0
void DBConfig::AddAllowedPath(const string &path) {
907
0
  auto allowed_path = SanitizeAllowedPath(path);
908
0
  options.allowed_paths.insert(allowed_path);
909
0
}
910
911
15.1k
bool DBConfig::CanAccessFile(const string &input_path, FileType type) {
912
15.1k
  if (Settings::Get<EnableExternalAccessSetting>(*this)) {
913
    // all external access is allowed
914
15.1k
    return true;
915
15.1k
  }
916
0
  string path = SanitizeAllowedPath(input_path);
917
918
0
  if (options.allowed_paths.count(path) > 0) {
919
    // path is explicitly allowed
920
0
    return true;
921
0
  }
922
923
0
  if (options.allowed_directories.empty()) {
924
    // no prefix directories specified
925
0
    return false;
926
0
  }
927
0
  if (type == FileType::FILE_TYPE_DIR) {
928
    // make sure directories end with a /
929
0
    if (!StringUtil::EndsWith(path, "/")) {
930
0
      path += "/";
931
0
    }
932
0
  }
933
934
0
  string prefix;
935
0
  for (const auto &allowed_directory : options.allowed_directories) {
936
0
    if (StringUtil::StartsWith(path, allowed_directory)) {
937
0
      prefix = allowed_directory;
938
0
      break;
939
0
    }
940
0
  }
941
942
0
  if (prefix.empty()) {
943
    // no common prefix found - path is not inside an allowed directory
944
0
    return false;
945
0
  }
946
0
  D_ASSERT(StringUtil::EndsWith(prefix, "/"));
947
0
  return true;
948
0
}
949
950
0
SerializationOptions::SerializationOptions(AttachedDatabase &db) {
951
0
  storage_compatibility = StorageCompatibility::FromDatabase(db);
952
0
}
953
954
0
void DBConfig::SetHTTPUtil(const shared_ptr<HTTPUtil> &new_http_util) {
955
0
  lock_guard<mutex> guard(http_util_lock);
956
0
  old_http_utils.push_back(http_util);
957
0
  http_util.atomic_store(new_http_util);
958
0
}
959
960
0
HTTPUtil &DBConfig::GetHTTPUtil() const {
961
0
  return *http_util.atomic_load();
962
0
}
963
964
} // namespace duckdb