/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 |