Skip to main content

rudb_functions/
settingcatalog.rs

1//! What `duckdb_settings()` says about each setting this engine has.
2//!
3//! A hundred and ninety two rows for a hundred and eighty five settings, because seven of them have a second spelling and each spelling has a row.
4//! The names, descriptions, input types, scopes, alias lists and defaults were read from the pinned binary because clients may compare them with the values they already know.
5//!
6//! # A setting rudb does not read still has to answer
7//!
8//! rudb acts on twenty three of these names and the other hundred and sixty nine name parts of DuckDB it has no counterpart for.
9//! The obvious thing to do with a name the engine does not read is to refuse it, and that was what this table did until the corpus was measured.
10//! Refusing them costs 7079 records over 584 files, more files than any other single cause in the run, because a test file sets a knob in its preamble and everything after the refusal goes down with it.
11//! So the answer is not one rule but three, and [`Behaviour`] is which of the three a setting gets.
12//! A knob cannot change what a query returns, so rudb takes it, keeps it and hands it back, and the query underneath it runs the same either way.
13//! Everything else can change an answer, so rudb takes it only at the value it already behaves as and refuses the rest, which keeps `SET preserve_insertion_order = false` an error rather than a promise rudb does not keep.
14//!
15//! # The alias direction is the opposite way round from the obvious one
16//!
17//! `max_memory` carries `[memory_limit]` in its alias list and `memory_limit` carries an empty one,
18//! and the same for `threads` and `worker_threads`. So the name the documentation uses is the alias
19//! and the name nobody types is the entry that points at it. That reads backwards and it is what the
20//! binary returns, so it is what is here. Both spellings set the same thing either way, which is the
21//! part that matters to a client, and which of the two rows is the one with the list in it only
22//! matters to a test.
23//!
24//! # `typed_value` is a `VARCHAR` here and a `VARIANT` there
25//!
26//! The pin's last column is a `VARIANT`, which is a type rudb has no [`LogicalType`] for at all, and
27//! it holds the same text as `value` on 191 of the pin's 192 rows. So this reports it as a `VARCHAR`
28//! with the value in it. Adding a `VARIANT` to the type system for one column of one catalog table
29//! would be adding a type no expression can produce, no cast can reach and no file format can store,
30//! and the day rudb has a real one this column changes with the rest of them.
31//!
32//! # What is not here
33//!
34//! The seam settings, and that is decided in `rudb`'s own settings module rather than in this one.
35//! There are twenty seven of them, none is a DuckDB setting, and this table is the answer to "what
36//! can I turn that DuckDB also has". `rudb_strategies()` is the table that answers the other
37//! question.
38
39use rudb_common::{Field, LogicalType};
40
41/// One setting, and everything `duckdb_settings()` says about it that is not its value.
42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
43pub struct SettingEntry {
44    /// The name, as `SET` spells it.
45    pub name: &'static str,
46    /// The sentence the pin prints, word for word.
47    pub description: &'static str,
48    /// The type a value for it is read as, which is the pin's spelling and not a [`LogicalType`].
49    pub input_type: &'static str,
50    /// `GLOBAL` or `LOCAL`, as the pin reports it.
51    pub scope: &'static str,
52    /// The other spellings of this setting, which the pin fills in on one of the pair and not both.
53    pub aliases: &'static [&'static str],
54    /// What the engine does with a value handed to this setting.
55    pub behaviour: Behaviour,
56}
57
58/// What the engine does with a value handed to a setting.
59///
60/// The three cases are not three degrees of the same thing. The first is a setting rudb reads, the
61/// second is one nothing could read because there is nothing for it to change, and the third is one
62/// that would need reading and has nowhere yet to be read.
63#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64pub enum Behaviour {
65    /// rudb acts on it. The value lives in the engine rather than in this table, which is why this
66    /// case carries none.
67    Honoured,
68    /// A knob, with the value a database that has set nothing reports for it.
69    ///
70    /// Nothing it can be set to changes what a query returns, so rudb takes it, keeps it and hands
71    /// it back, and the statement after it runs the way it would have anyway. `SET
72    /// enable_http_metadata_cache = true` is this: there is no HTTP metadata cache here, there is
73    /// nothing a query could notice about whether one is on, and a script that sets it wanted to
74    /// keep going rather than to be told rudb has never heard the name.
75    Knob(&'static str),
76    /// Something rudb does not do, with the one value it already behaves as.
77    ///
78    /// Setting it to anything else is refused. `SET preserve_insertion_order = false` is this: it is
79    /// a real change to what a query returns, rudb cannot make it, and taking the value and not
80    /// acting on it would turn one clear error into a wrong answer a statement later.
81    DefaultOnly(&'static str),
82}
83
84/// The default of a setting the pin answers `NULL` for rather than a value.
85///
86/// Three of the hundred and ninety two are unset on a fresh connection rather than empty, and the
87/// difference is visible twice: `duckdb_settings()` prints null for them where it prints the empty
88/// string for the twenty two that really are empty, and `current_setting('parquet_prefetch_column_gap')`
89/// is null rather than a number. The three are `enable_profiling`, `operator_memory_limit` and
90/// `parquet_prefetch_column_gap`. A NUL byte is what stands for it because it is the one string no
91/// `SET` statement can write, so a setting can only be unset by being at its default or by
92/// `PRAGMA disable_profiling`, which is the one statement that puts a setting back to nothing.
93pub const UNSET: &str = "\0";
94
95/// The scope of a setting that is one per database.
96pub const GLOBAL: &str = "GLOBAL";
97
98/// The scope of a setting the pin keeps per connection.
99///
100/// Fifteen of these rows say `LOCAL` and rudb honours none of the fifteen, so the scope is reported
101/// because the pin reports it and not because two connections here can differ.
102pub const LOCAL: &str = "LOCAL";
103
104/// Every setting, in the order the pin lists them, which is by name.
105pub static SETTINGS: &[SettingEntry] = &[
106    SettingEntry {
107        name: "Calendar",
108        description: "The current calendar",
109        input_type: "VARCHAR",
110        scope: GLOBAL,
111        aliases: &[],
112        behaviour: Behaviour::DefaultOnly("gregorian"),
113    },
114    SettingEntry {
115        name: "TimeZone",
116        description: "The current time zone",
117        input_type: "VARCHAR",
118        scope: GLOBAL,
119        aliases: &[],
120        behaviour: Behaviour::Honoured,
121    },
122    SettingEntry {
123        name: "__delta_only_variant_encoding_enabled",
124        description: "Enables the Parquet reader to identify a Variant structurally.",
125        input_type: "BOOLEAN",
126        scope: GLOBAL,
127        aliases: &[],
128        behaviour: Behaviour::Knob("false"),
129    },
130    SettingEntry {
131        name: "access_mode",
132        description: "Access mode of the database (AUTOMATIC, READ_ONLY or READ_WRITE)",
133        input_type: "VARCHAR",
134        scope: GLOBAL,
135        aliases: &[],
136        behaviour: Behaviour::DefaultOnly("automatic"),
137    },
138    SettingEntry {
139        name: "active_grammar_extensions",
140        description: "The grammar extensions used by the parser",
141        input_type: "VARCHAR[]",
142        scope: LOCAL,
143        aliases: &[],
144        behaviour: Behaviour::DefaultOnly("[]"),
145    },
146    SettingEntry {
147        name: "allocator_background_threads",
148        description: "Whether to enable the allocator background thread.",
149        input_type: "BOOLEAN",
150        scope: GLOBAL,
151        aliases: &[],
152        behaviour: Behaviour::Knob("false"),
153    },
154    SettingEntry {
155        name: "allocator_bulk_deallocation_flush_threshold",
156        description: "If a bulk deallocation larger than this occurs, flush outstanding allocations.",
157        input_type: "VARCHAR",
158        scope: GLOBAL,
159        aliases: &[],
160        behaviour: Behaviour::Knob("512.0 MiB"),
161    },
162    SettingEntry {
163        name: "allocator_flush_threshold",
164        description: "Peak allocation threshold at which to flush the allocator after completing a task.",
165        input_type: "VARCHAR",
166        scope: GLOBAL,
167        aliases: &[],
168        behaviour: Behaviour::Knob("134217728B"),
169    },
170    SettingEntry {
171        name: "allow_community_extensions",
172        description: "Allow to load community built extensions",
173        input_type: "BOOLEAN",
174        scope: GLOBAL,
175        aliases: &[],
176        behaviour: Behaviour::Knob("true"),
177    },
178    SettingEntry {
179        name: "allow_extension_repositories",
180        description: "Whether custom trusted extension repositories are 'allowed', 'forbidden' (which also distrusts existing repositories) or 'undecided' (the default: blocks adding new repositories, but keeps trusting existing ones). While the database is running the setting can only move from 'undecided' to 'allowed' or 'forbidden', or from 'allowed' to 'forbidden'",
181        input_type: "VARCHAR",
182        scope: GLOBAL,
183        aliases: &[],
184        behaviour: Behaviour::Knob("undecided"),
185    },
186    SettingEntry {
187        name: "allow_extensions_metadata_mismatch",
188        description: "Allow to load extensions with not compatible metadata",
189        input_type: "BOOLEAN",
190        scope: GLOBAL,
191        aliases: &[],
192        behaviour: Behaviour::Knob("false"),
193    },
194    SettingEntry {
195        name: "allow_parser_override_extension",
196        description: "Allow extensions to override the current parser",
197        input_type: "VARCHAR",
198        scope: GLOBAL,
199        aliases: &[],
200        behaviour: Behaviour::Honoured,
201    },
202    SettingEntry {
203        name: "allow_persistent_secrets",
204        description: "Allow the creation of persistent secrets, that are stored and loaded on restarts",
205        input_type: "BOOLEAN",
206        scope: GLOBAL,
207        aliases: &[],
208        behaviour: Behaviour::Knob("true"),
209    },
210    SettingEntry {
211        name: "allow_unredacted_secrets",
212        description: "Allow printing unredacted secrets",
213        input_type: "BOOLEAN",
214        scope: GLOBAL,
215        aliases: &[],
216        behaviour: Behaviour::Knob("false"),
217    },
218    SettingEntry {
219        name: "allow_unsigned_extensions",
220        description: "Allow to load extensions with invalid or missing signatures",
221        input_type: "BOOLEAN",
222        scope: GLOBAL,
223        aliases: &[],
224        behaviour: Behaviour::Knob("false"),
225    },
226    SettingEntry {
227        name: "allowed_configs",
228        description: "List of configuration options that are ALWAYS allowed to be changed - even when lock_configuration is true",
229        input_type: "VARCHAR[]",
230        scope: GLOBAL,
231        aliases: &[],
232        behaviour: Behaviour::DefaultOnly("[]"),
233    },
234    SettingEntry {
235        name: "allowed_directories",
236        description: "List of directories/prefixes that are ALWAYS allowed to be queried - even when enable_external_access is false",
237        input_type: "VARCHAR[]",
238        scope: GLOBAL,
239        aliases: &[],
240        behaviour: Behaviour::DefaultOnly("[]"),
241    },
242    SettingEntry {
243        name: "allowed_paths",
244        description: "List of files that are ALWAYS allowed to be queried - even when enable_external_access is false",
245        input_type: "VARCHAR[]",
246        scope: GLOBAL,
247        aliases: &[],
248        behaviour: Behaviour::DefaultOnly("[]"),
249    },
250    SettingEntry {
251        name: "approximate_join_order_threshold",
252        description: "The minimum number of tables in a join to determine the optimal join order approximately instead of exactly.",
253        input_type: "UBIGINT",
254        scope: GLOBAL,
255        aliases: &[],
256        behaviour: Behaviour::Knob("12"),
257    },
258    SettingEntry {
259        name: "arrow_large_buffer_size",
260        description: "Whether Arrow buffers for strings, blobs, uuids and bits should be exported using large buffers",
261        input_type: "BOOLEAN",
262        scope: GLOBAL,
263        aliases: &[],
264        behaviour: Behaviour::DefaultOnly("false"),
265    },
266    SettingEntry {
267        name: "arrow_lossless_conversion",
268        description: "Whenever a DuckDB type does not have a clear native or canonical extension match in Arrow, export the types with a duckdb.type_name extension name.",
269        input_type: "BOOLEAN",
270        scope: GLOBAL,
271        aliases: &[],
272        behaviour: Behaviour::DefaultOnly("false"),
273    },
274    SettingEntry {
275        name: "arrow_output_list_view",
276        description: "Whether export to Arrow format should use ListView as the physical layout for LIST columns",
277        input_type: "BOOLEAN",
278        scope: GLOBAL,
279        aliases: &[],
280        behaviour: Behaviour::DefaultOnly("false"),
281    },
282    SettingEntry {
283        name: "arrow_output_version",
284        description: "Whether strings should be produced by DuckDB in Utf8View format instead of Utf8",
285        input_type: "VARCHAR",
286        scope: GLOBAL,
287        aliases: &[],
288        behaviour: Behaviour::DefaultOnly("1.0"),
289    },
290    SettingEntry {
291        name: "asof_loop_join_threshold",
292        description: "The maximum number of rows we need on the left side of an ASOF join to use a nested loop join",
293        input_type: "UBIGINT",
294        scope: GLOBAL,
295        aliases: &[],
296        behaviour: Behaviour::Knob("64"),
297    },
298    SettingEntry {
299        name: "async_threads",
300        description: "The number of total async threads used by the system for tasks like I/O.",
301        input_type: "BIGINT",
302        scope: GLOBAL,
303        aliases: &[],
304        behaviour: Behaviour::Knob("128"),
305    },
306    SettingEntry {
307        name: "auto_checkpoint_skip_wal_threshold",
308        description: "The estimated WAL write size at which point we will skip writing to the WAL and only checkpoint. Skipping writing to the WAL means concurrent commits are blocked while the checkpoint is happening.",
309        input_type: "UBIGINT",
310        scope: GLOBAL,
311        aliases: &[],
312        behaviour: Behaviour::Knob("100000"),
313    },
314    SettingEntry {
315        name: "autoinstall_extension_repository",
316        description: "Overrides the custom endpoint for extension installation on autoloading",
317        input_type: "VARCHAR",
318        scope: GLOBAL,
319        aliases: &[],
320        behaviour: Behaviour::Knob(""),
321    },
322    SettingEntry {
323        name: "autoinstall_known_extensions",
324        description: "Whether known extensions are allowed to be automatically installed when a query depends on them",
325        input_type: "BOOLEAN",
326        scope: GLOBAL,
327        aliases: &[],
328        behaviour: Behaviour::Knob("true"),
329    },
330    SettingEntry {
331        name: "autoload_known_extensions",
332        description: "Whether known extensions are allowed to be automatically loaded when a query depends on them",
333        input_type: "BOOLEAN",
334        scope: GLOBAL,
335        aliases: &[],
336        behaviour: Behaviour::Knob("true"),
337    },
338    SettingEntry {
339        name: "binary_as_string",
340        description: "In Parquet files, interpret binary data as a string.",
341        input_type: "BOOLEAN",
342        scope: GLOBAL,
343        aliases: &[],
344        behaviour: Behaviour::DefaultOnly("false"),
345    },
346    SettingEntry {
347        name: "block_allocator_memory",
348        description: "Physical memory that the block allocator is allowed to use (this memory is never freed and cannot be reduced).",
349        input_type: "VARCHAR",
350        scope: GLOBAL,
351        aliases: &[],
352        behaviour: Behaviour::Knob("0 bytes"),
353    },
354    SettingEntry {
355        name: "cache_local_files",
356        description: "Whether the external file cache also caches local files (remote files are always cached)",
357        input_type: "BOOLEAN",
358        scope: GLOBAL,
359        aliases: &[],
360        behaviour: Behaviour::Knob("false"),
361    },
362    SettingEntry {
363        name: "catalog_error_max_schemas",
364        description: "The maximum number of schemas the system will scan for \"did you mean...\" style errors in the catalog",
365        input_type: "UBIGINT",
366        scope: GLOBAL,
367        aliases: &[],
368        behaviour: Behaviour::DefaultOnly("100"),
369    },
370    SettingEntry {
371        name: "checkpoint_on_detach",
372        description: "Override checkpoint behavior when detaching a database. ENABLED requests a checkpoint, but the checkpoint does not occur if another connection still references the database. DISABLED never checkpoints, DEFAULT defers to the global checkpoint_on_shutdown setting.",
373        input_type: "VARCHAR",
374        scope: GLOBAL,
375        aliases: &[],
376        behaviour: Behaviour::Knob("DEFAULT"),
377    },
378    SettingEntry {
379        name: "checkpoint_threshold",
380        description: "The WAL size threshold at which to automatically trigger a checkpoint (e.g. 1GB)",
381        input_type: "VARCHAR",
382        scope: GLOBAL,
383        aliases: &["wal_autocheckpoint"],
384        behaviour: Behaviour::Knob("16.0 MiB"),
385    },
386    SettingEntry {
387        name: "current_dialect",
388        description: "The SQL dialect used by the parser",
389        input_type: "VARCHAR",
390        scope: GLOBAL,
391        aliases: &[],
392        behaviour: Behaviour::Honoured,
393    },
394    SettingEntry {
395        name: "current_transaction_invalidation_policy",
396        description: "Which types of exceptions invalidate the database for the current transaction",
397        input_type: "VARCHAR",
398        scope: GLOBAL,
399        aliases: &[],
400        behaviour: Behaviour::DefaultOnly("STANDARD_POLICY"),
401    },
402    SettingEntry {
403        name: "custom_extension_repository",
404        description: "Overrides the custom endpoint for remote extension installation",
405        input_type: "VARCHAR",
406        scope: GLOBAL,
407        aliases: &[],
408        behaviour: Behaviour::Knob(""),
409    },
410    SettingEntry {
411        name: "custom_user_agent",
412        description: "Metadata from DuckDB callers",
413        input_type: "VARCHAR",
414        scope: GLOBAL,
415        aliases: &[],
416        behaviour: Behaviour::Knob(""),
417    },
418    SettingEntry {
419        name: "debug_asof_iejoin",
420        description: "DEBUG SETTING: force use of IEJoin to implement AsOf joins",
421        input_type: "BOOLEAN",
422        scope: GLOBAL,
423        aliases: &[],
424        behaviour: Behaviour::Knob("false"),
425    },
426    SettingEntry {
427        name: "debug_checkpoint_abort",
428        description: "DEBUG SETTING: trigger an abort while checkpointing for testing purposes",
429        input_type: "VARCHAR",
430        scope: GLOBAL,
431        aliases: &[],
432        behaviour: Behaviour::Knob("NONE"),
433    },
434    SettingEntry {
435        name: "debug_checkpoint_sleep_ms",
436        description: "DEBUG SETTING: time to sleep before a checkpoint",
437        input_type: "UBIGINT",
438        scope: GLOBAL,
439        aliases: &[],
440        behaviour: Behaviour::Knob("0"),
441    },
442    SettingEntry {
443        name: "debug_disable_optimizer",
444        description: "DEBUG SETTING: disable optimizer for most queries",
445        input_type: "BOOLEAN",
446        scope: GLOBAL,
447        aliases: &[],
448        behaviour: Behaviour::DefaultOnly("false"),
449    },
450    SettingEntry {
451        name: "debug_eviction_queue_sleep_micro_seconds",
452        description: "DEBUG SETTING: time for the eviction queue to sleep before acquiring shared ownership of block memory",
453        input_type: "UBIGINT",
454        scope: GLOBAL,
455        aliases: &[],
456        behaviour: Behaviour::Knob("0"),
457    },
458    SettingEntry {
459        name: "debug_force_commit_failure",
460        description: "DEBUG SETTING: force transaction commit to fail after the undo buffer has been committed, used for testing commit error recovery",
461        input_type: "BOOLEAN",
462        scope: GLOBAL,
463        aliases: &[],
464        behaviour: Behaviour::DefaultOnly("false"),
465    },
466    SettingEntry {
467        name: "debug_force_commit_revert_failure",
468        description: "DEBUG SETTING: force RevertCommit to fail while recovering from a commit failure, used for testing",
469        input_type: "BOOLEAN",
470        scope: GLOBAL,
471        aliases: &[],
472        behaviour: Behaviour::DefaultOnly("false"),
473    },
474    SettingEntry {
475        name: "debug_force_external",
476        description: "DEBUG SETTING: force out-of-core computation for operators that support it, used for testing",
477        input_type: "BOOLEAN",
478        scope: GLOBAL,
479        aliases: &[],
480        behaviour: Behaviour::Knob("false"),
481    },
482    SettingEntry {
483        name: "debug_force_fetch_row",
484        description: "DEBUG SETTING: force per-row fetching during scans, used for testing",
485        input_type: "BOOLEAN",
486        scope: GLOBAL,
487        aliases: &[],
488        behaviour: Behaviour::Knob("false"),
489    },
490    SettingEntry {
491        name: "debug_force_no_cross_product",
492        description: "DEBUG SETTING: Force disable cross product generation when hyper graph isn't connected, used for testing",
493        input_type: "BOOLEAN",
494        scope: GLOBAL,
495        aliases: &[],
496        behaviour: Behaviour::Knob("false"),
497    },
498    SettingEntry {
499        name: "debug_local_file_system_delay_ms",
500        description: "DEBUG SETTING: time to sleep before local file system open/read/write operations",
501        input_type: "UBIGINT",
502        scope: GLOBAL,
503        aliases: &[],
504        behaviour: Behaviour::Knob("0"),
505    },
506    SettingEntry {
507        name: "debug_order_verification",
508        description: "DEBUG SETTING: verify ORDER BY results by rewriting the ordering (NONE, CREATE_SORT_KEY or VARIANT)",
509        input_type: "VARCHAR",
510        scope: GLOBAL,
511        aliases: &[],
512        behaviour: Behaviour::Knob("none"),
513    },
514    SettingEntry {
515        name: "debug_physical_table_scan_execution_strategy",
516        description: "DEBUG SETTING: force use of given strategy for executing physical table scans",
517        input_type: "VARCHAR",
518        scope: GLOBAL,
519        aliases: &[],
520        behaviour: Behaviour::Knob("DEFAULT"),
521    },
522    SettingEntry {
523        name: "debug_skip_checkpoint_on_commit",
524        description: "DEBUG SETTING: skip checkpointing on commit",
525        input_type: "BOOLEAN",
526        scope: GLOBAL,
527        aliases: &[],
528        behaviour: Behaviour::DefaultOnly("false"),
529    },
530    SettingEntry {
531        name: "debug_transformer_trampoline_style",
532        description: "Use the experimental trampoline-style parser transformer",
533        input_type: "BOOLEAN",
534        scope: GLOBAL,
535        aliases: &[],
536        behaviour: Behaviour::Knob("false"),
537    },
538    SettingEntry {
539        name: "debug_verification_mode",
540        description: "DEBUG SETTING: toggle the verification mode.",
541        input_type: "VARCHAR",
542        scope: GLOBAL,
543        aliases: &[],
544        behaviour: Behaviour::Knob("NONE"),
545    },
546    SettingEntry {
547        name: "debug_verification_projection",
548        description: "DEBUG SETTING: add internal verification projections to stress optimizers",
549        input_type: "BOOLEAN",
550        scope: GLOBAL,
551        aliases: &[],
552        behaviour: Behaviour::Knob("false"),
553    },
554    SettingEntry {
555        name: "debug_verify_aggregate_state_export",
556        description: "DEBUG SETTING: enable verification of aggregate state export",
557        input_type: "BOOLEAN",
558        scope: GLOBAL,
559        aliases: &[],
560        behaviour: Behaviour::Knob("false"),
561    },
562    SettingEntry {
563        name: "debug_verify_blocks",
564        description: "DEBUG SETTING: verify block metadata during checkpointing",
565        input_type: "BOOLEAN",
566        scope: GLOBAL,
567        aliases: &[],
568        behaviour: Behaviour::Knob("false"),
569    },
570    SettingEntry {
571        name: "debug_verify_column_bindings",
572        description: "DEBUG SETTING: run extra internal verification of column bindings",
573        input_type: "BOOLEAN",
574        scope: GLOBAL,
575        aliases: &[],
576        behaviour: Behaviour::Knob("false"),
577    },
578    SettingEntry {
579        name: "debug_verify_serializer",
580        description: "DEBUG SETTING: verify logical plan serializer",
581        input_type: "BOOLEAN",
582        scope: GLOBAL,
583        aliases: &[],
584        behaviour: Behaviour::Knob("false"),
585    },
586    SettingEntry {
587        name: "debug_verify_statement",
588        description: "DEBUG SETTING: the type of statement verification to perform",
589        input_type: "VARCHAR",
590        scope: GLOBAL,
591        aliases: &[],
592        behaviour: Behaviour::Knob("NONE"),
593    },
594    SettingEntry {
595        name: "debug_verify_stats",
596        description: "DEBUG SETTING: verify statistics are correct during execution, instead of assuming",
597        input_type: "BOOLEAN",
598        scope: GLOBAL,
599        aliases: &[],
600        behaviour: Behaviour::Knob("false"),
601    },
602    SettingEntry {
603        name: "debug_verify_vector",
604        description: "DEBUG SETTING: enable vector verification",
605        input_type: "VARCHAR",
606        scope: GLOBAL,
607        aliases: &[],
608        behaviour: Behaviour::Knob("NONE"),
609    },
610    SettingEntry {
611        name: "debug_window_mode",
612        description: "DEBUG SETTING: switch window mode to use",
613        input_type: "VARCHAR",
614        scope: GLOBAL,
615        aliases: &[],
616        behaviour: Behaviour::Knob("WINDOW"),
617    },
618    SettingEntry {
619        name: "default_block_size",
620        description: "The default block size for new duckdb database files (new as-in, they do not yet exist).",
621        input_type: "UBIGINT",
622        scope: GLOBAL,
623        aliases: &[],
624        behaviour: Behaviour::Knob("262144"),
625    },
626    SettingEntry {
627        name: "default_collation",
628        description: "The collation setting used when none is specified",
629        input_type: "VARCHAR",
630        scope: GLOBAL,
631        aliases: &[],
632        behaviour: Behaviour::DefaultOnly(""),
633    },
634    SettingEntry {
635        name: "default_null_order",
636        description: "NULL ordering used when none is specified (NULLS_FIRST or NULLS_LAST)",
637        input_type: "VARCHAR",
638        scope: GLOBAL,
639        aliases: &["null_order"],
640        behaviour: Behaviour::Honoured,
641    },
642    SettingEntry {
643        name: "default_order",
644        description: "The order type used when none is specified (ASC or DESC)",
645        input_type: "VARCHAR",
646        scope: GLOBAL,
647        aliases: &[],
648        behaviour: Behaviour::Honoured,
649    },
650    SettingEntry {
651        name: "default_secret_storage",
652        description: "Allows switching the default storage for secrets",
653        input_type: "VARCHAR",
654        scope: GLOBAL,
655        aliases: &[],
656        behaviour: Behaviour::Knob(""),
657    },
658    SettingEntry {
659        name: "default_transaction_invalidation_policy",
660        description: "When to invalidate transactions when errors occur (SYNTACTIC_ERRORS_DO_NOT_INVALIDATE, i.e. parser and binder exceptions do not invalidate, or ALL_ERRORS_INVALIDATE_TRANSACTION)",
661        input_type: "VARCHAR",
662        scope: GLOBAL,
663        aliases: &[],
664        behaviour: Behaviour::DefaultOnly("ALL_ERRORS_INVALIDATE_TRANSACTION"),
665    },
666    SettingEntry {
667        name: "delim_join_as_cte",
668        description: "Rewrite delim joins to materialized CTEs during dependent join flattening",
669        input_type: "BOOLEAN",
670        scope: GLOBAL,
671        aliases: &[],
672        behaviour: Behaviour::Knob("true"),
673    },
674    SettingEntry {
675        name: "dialect_compatibility_mode",
676        description: "Enable SQL dialect compatibility for a certain engine (e.g. `SET dialect_compatibility_mode='spark'`)",
677        input_type: "VARCHAR",
678        scope: GLOBAL,
679        aliases: &[],
680        behaviour: Behaviour::Honoured,
681    },
682    SettingEntry {
683        name: "disable_database_invalidation",
684        description: "Disables invalidating the database instance when encountering a fatal error. Should be used with great care, as DuckDB cannot guarantee correct behavior after a fatal error.",
685        input_type: "BOOLEAN",
686        scope: GLOBAL,
687        aliases: &[],
688        behaviour: Behaviour::DefaultOnly("false"),
689    },
690    SettingEntry {
691        name: "disable_parquet_prefetching",
692        description: "Disable the prefetching mechanism in Parquet",
693        input_type: "BOOLEAN",
694        scope: GLOBAL,
695        aliases: &[],
696        behaviour: Behaviour::Knob("false"),
697    },
698    SettingEntry {
699        name: "disable_timestamptz_casts",
700        description: "Disable casting from timestamp to timestamptz ",
701        input_type: "BOOLEAN",
702        scope: GLOBAL,
703        aliases: &[],
704        behaviour: Behaviour::Honoured,
705    },
706    SettingEntry {
707        name: "disabled_compression_methods",
708        description: "Disable a specific set of compression methods (comma separated)",
709        input_type: "VARCHAR",
710        scope: GLOBAL,
711        aliases: &[],
712        behaviour: Behaviour::Knob(""),
713    },
714    SettingEntry {
715        name: "disabled_filesystems",
716        description: "Disable specific file systems preventing access (e.g. LocalFileSystem)",
717        input_type: "VARCHAR",
718        scope: GLOBAL,
719        aliases: &[],
720        behaviour: Behaviour::DefaultOnly(""),
721    },
722    SettingEntry {
723        name: "disabled_log_types",
724        description: "Sets the list of disabled loggers",
725        input_type: "VARCHAR",
726        scope: GLOBAL,
727        aliases: &[],
728        behaviour: Behaviour::Knob(""),
729    },
730    SettingEntry {
731        name: "disabled_optimizers",
732        description: "DEBUG SETTING: disable a specific set of optimizers (comma separated)",
733        input_type: "VARCHAR",
734        scope: GLOBAL,
735        aliases: &[],
736        behaviour: Behaviour::Honoured,
737    },
738    SettingEntry {
739        name: "duckdb_api",
740        description: "DuckDB API surface",
741        input_type: "VARCHAR",
742        scope: GLOBAL,
743        aliases: &[],
744        behaviour: Behaviour::Knob("rudb"),
745    },
746    SettingEntry {
747        name: "dynamic_or_filter_threshold",
748        description: "The maximum amount of OR filters we generate dynamically from a hash join",
749        input_type: "UBIGINT",
750        scope: GLOBAL,
751        aliases: &[],
752        behaviour: Behaviour::Knob("50"),
753    },
754    SettingEntry {
755        name: "enable_external_access",
756        description: "Allow the database to access external state (through e.g. loading/installing modules, COPY TO/FROM, CSV readers, pandas replacement scans, etc)",
757        input_type: "BOOLEAN",
758        scope: GLOBAL,
759        aliases: &[],
760        behaviour: Behaviour::DefaultOnly("true"),
761    },
762    SettingEntry {
763        name: "enable_external_file_cache",
764        description: "Allow the database to cache external files (e.g., Parquet) in memory.",
765        input_type: "BOOLEAN",
766        scope: GLOBAL,
767        aliases: &[],
768        behaviour: Behaviour::Knob("true"),
769    },
770    SettingEntry {
771        name: "enable_fsst_vectors",
772        description: "Allow scans on FSST compressed segments to emit compressed vectors to utilize late decompression",
773        input_type: "BOOLEAN",
774        scope: GLOBAL,
775        aliases: &[],
776        behaviour: Behaviour::Knob("false"),
777    },
778    SettingEntry {
779        name: "enable_geoparquet_conversion",
780        description: "Attempt to decode/encode geometry data in/as GeoParquet files if the spatial extension is present.",
781        input_type: "BOOLEAN",
782        scope: GLOBAL,
783        aliases: &[],
784        behaviour: Behaviour::Knob("true"),
785    },
786    SettingEntry {
787        name: "enable_http_metadata_cache",
788        description: "Whether or not the global http metadata is used to cache HTTP metadata",
789        input_type: "BOOLEAN",
790        scope: GLOBAL,
791        aliases: &[],
792        behaviour: Behaviour::Knob("false"),
793    },
794    SettingEntry {
795        name: "enable_logging",
796        description: "Enables the logger",
797        input_type: "BOOLEAN",
798        scope: GLOBAL,
799        aliases: &[],
800        behaviour: Behaviour::Knob("1"),
801    },
802    SettingEntry {
803        name: "enable_macro_dependencies",
804        description: "Enable created MACROs to create dependencies on the referenced objects (such as tables)",
805        input_type: "BOOLEAN",
806        scope: GLOBAL,
807        aliases: &[],
808        behaviour: Behaviour::DefaultOnly("false"),
809    },
810    SettingEntry {
811        name: "enable_object_cache",
812        description: "[PLACEHOLDER] Legacy setting - does nothing",
813        input_type: "BOOLEAN",
814        scope: GLOBAL,
815        aliases: &[],
816        behaviour: Behaviour::Knob("false"),
817    },
818    SettingEntry {
819        name: "enable_optimistic_write",
820        description: "Whether or not to optimistically write large appends to disk before committing. Disable this to keep bulk appends in memory (e.g. for in-memory benchmarks).",
821        input_type: "BOOLEAN",
822        scope: GLOBAL,
823        aliases: &[],
824        behaviour: Behaviour::Knob("true"),
825    },
826    SettingEntry {
827        name: "enable_optimizer",
828        description: "Whether or not query optimization is enabled",
829        input_type: "BOOLEAN",
830        scope: GLOBAL,
831        aliases: &[],
832        behaviour: Behaviour::Knob("true"),
833    },
834    SettingEntry {
835        name: "enable_profiling",
836        description: "Enables profiling, and sets the output format (JSON, QUERY_TREE, QUERY_TREE_OPTIMIZER)",
837        input_type: "VARCHAR",
838        scope: LOCAL,
839        aliases: &[],
840        behaviour: Behaviour::Knob(UNSET),
841    },
842    SettingEntry {
843        name: "enable_progress_bar",
844        description: "Enables the progress bar, printing progress to the terminal for long queries",
845        input_type: "BOOLEAN",
846        scope: LOCAL,
847        aliases: &[],
848        behaviour: Behaviour::Knob("false"),
849    },
850    SettingEntry {
851        name: "enable_progress_bar_print",
852        description: "Controls the printing of the progress bar, when 'enable_progress_bar' is true",
853        input_type: "BOOLEAN",
854        scope: LOCAL,
855        aliases: &[],
856        behaviour: Behaviour::Knob("true"),
857    },
858    SettingEntry {
859        name: "enable_view_dependencies",
860        description: "Enable created VIEWs to create dependencies on the referenced objects (such as tables)",
861        input_type: "BOOLEAN",
862        scope: GLOBAL,
863        aliases: &[],
864        behaviour: Behaviour::DefaultOnly("false"),
865    },
866    SettingEntry {
867        name: "enabled_log_types",
868        description: "Sets the list of enabled loggers",
869        input_type: "VARCHAR",
870        scope: GLOBAL,
871        aliases: &[],
872        behaviour: Behaviour::Knob(""),
873    },
874    SettingEntry {
875        name: "errors_as_json",
876        description: "Output error messages as structured JSON instead of as a raw string",
877        input_type: "BOOLEAN",
878        scope: GLOBAL,
879        aliases: &[],
880        behaviour: Behaviour::Honoured,
881    },
882    SettingEntry {
883        name: "experimental_metadata_reuse",
884        description: "EXPERIMENTAL: Re-use row group and table metadata when checkpointing.",
885        input_type: "BOOLEAN",
886        scope: GLOBAL,
887        aliases: &[],
888        behaviour: Behaviour::Knob("true"),
889    },
890    SettingEntry {
891        name: "explain_output",
892        description: "Output of EXPLAIN statements (ALL, OPTIMIZED_ONLY, PHYSICAL_ONLY)",
893        input_type: "VARCHAR",
894        scope: GLOBAL,
895        aliases: &[],
896        behaviour: Behaviour::DefaultOnly("PHYSICAL_ONLY"),
897    },
898    SettingEntry {
899        name: "extension_directories",
900        description: "Set the directories to store extensions in",
901        input_type: "VARCHAR[]",
902        scope: GLOBAL,
903        aliases: &[],
904        behaviour: Behaviour::Knob("[]"),
905    },
906    SettingEntry {
907        name: "extension_repository_directory",
908        description: "Set the directory in which trusted extension repositories are stored. This is the trust anchor for user-provided repositories, so while signature checking is enabled (allow_unsigned_extensions=false) it can only be set at startup, not while the database is running",
909        input_type: "VARCHAR",
910        scope: GLOBAL,
911        aliases: &[],
912        behaviour: Behaviour::Knob(""),
913    },
914    SettingEntry {
915        name: "external_file_cache_local_block_size",
916        description: "Block size in bytes for the external file cache when reading local (non-remote) files.",
917        input_type: "UBIGINT",
918        scope: GLOBAL,
919        aliases: &[],
920        behaviour: Behaviour::Knob("16384"),
921    },
922    SettingEntry {
923        name: "external_file_cache_remote_block_size",
924        description: "Block size in bytes for the external file cache when reading remote files (e.g. HTTP/S3).",
925        input_type: "UBIGINT",
926        scope: GLOBAL,
927        aliases: &[],
928        behaviour: Behaviour::Knob("2097152"),
929    },
930    SettingEntry {
931        name: "external_file_cache_spill",
932        description: "Whether evicted external file cache blocks of remote files spill to the temporary directory instead of being dropped, so that they are re-read from there rather than re-fetched from the source",
933        input_type: "BOOLEAN",
934        scope: GLOBAL,
935        aliases: &[],
936        behaviour: Behaviour::Knob("false"),
937    },
938    SettingEntry {
939        name: "external_threads",
940        description: "The number of external threads that work on DuckDB tasks.",
941        input_type: "UBIGINT",
942        scope: GLOBAL,
943        aliases: &[],
944        behaviour: Behaviour::Knob("1"),
945    },
946    SettingEntry {
947        name: "file_search_path",
948        description: "A comma separated list of directories to search for input files",
949        input_type: "VARCHAR",
950        scope: GLOBAL,
951        aliases: &[],
952        behaviour: Behaviour::Knob(""),
953    },
954    SettingEntry {
955        name: "force_column_metadata_reuse",
956        description: "Force re-use of row group metadata on a column-level when checkpointing on older storage versions 6 and 7. This breaks storage backward-compatibility with older DuckDB versions.",
957        input_type: "BOOLEAN",
958        scope: GLOBAL,
959        aliases: &[],
960        behaviour: Behaviour::Knob("false"),
961    },
962    SettingEntry {
963        name: "force_compression",
964        description: "DEBUG SETTING: forces a specific compression method to be used",
965        input_type: "VARCHAR",
966        scope: GLOBAL,
967        aliases: &[],
968        behaviour: Behaviour::Knob("auto"),
969    },
970    SettingEntry {
971        name: "geometry_minimum_shredding_size",
972        description: "Minimum size of a rowgroup to enable GEOMETRY shredding, or set to -1 to disable entirely. Defaults to 1/4th of a rowgroup",
973        input_type: "BIGINT",
974        scope: GLOBAL,
975        aliases: &[],
976        behaviour: Behaviour::Knob("30000"),
977    },
978    SettingEntry {
979        name: "heap_based_parser",
980        description: "Use the heap-based PEG parser",
981        input_type: "BOOLEAN",
982        scope: GLOBAL,
983        aliases: &[],
984        behaviour: Behaviour::Knob("true"),
985    },
986    SettingEntry {
987        name: "home_directory",
988        description: "Sets the home directory used by the system",
989        input_type: "VARCHAR",
990        scope: GLOBAL,
991        aliases: &[],
992        behaviour: Behaviour::Knob(""),
993    },
994    SettingEntry {
995        name: "http_proxy",
996        description: "HTTP proxy host (defaults to the HTTP_PROXY environment variable when unset)",
997        input_type: "VARCHAR",
998        scope: GLOBAL,
999        aliases: &[],
1000        behaviour: Behaviour::Knob(""),
1001    },
1002    SettingEntry {
1003        name: "http_proxy_password",
1004        description: "Password for HTTP proxy",
1005        input_type: "VARCHAR",
1006        scope: GLOBAL,
1007        aliases: &[],
1008        behaviour: Behaviour::Knob(""),
1009    },
1010    SettingEntry {
1011        name: "http_proxy_username",
1012        description: "Username for HTTP proxy",
1013        input_type: "VARCHAR",
1014        scope: GLOBAL,
1015        aliases: &[],
1016        behaviour: Behaviour::Knob(""),
1017    },
1018    SettingEntry {
1019        name: "ieee_floating_point_ops",
1020        description: "Use IEEE 754 behavior for supported floating point operations, returning NAN/INF instead of errors/NULL.",
1021        input_type: "BOOLEAN",
1022        scope: GLOBAL,
1023        aliases: &[],
1024        behaviour: Behaviour::Honoured,
1025    },
1026    SettingEntry {
1027        name: "ignore_unknown_crs",
1028        description: "Ignore unknown Coordinate Reference Systems (CRS) when creating geometry types or importing geospatial data.",
1029        input_type: "BOOLEAN",
1030        scope: GLOBAL,
1031        aliases: &[],
1032        behaviour: Behaviour::Knob("false"),
1033    },
1034    SettingEntry {
1035        name: "immediate_transaction_mode",
1036        description: "Whether transactions should be started lazily when needed, or immediately when BEGIN TRANSACTION is called",
1037        input_type: "BOOLEAN",
1038        scope: GLOBAL,
1039        aliases: &[],
1040        behaviour: Behaviour::Knob("false"),
1041    },
1042    SettingEntry {
1043        name: "index_scan_max_count",
1044        description: "The maximum index scan count sets a threshold for index scans. If fewer than MAX(index_scan_max_count, index_scan_percentage * total_row_count) rows match, we perform an index scan instead of a table scan.",
1045        input_type: "UBIGINT",
1046        scope: GLOBAL,
1047        aliases: &[],
1048        behaviour: Behaviour::Knob("2048"),
1049    },
1050    SettingEntry {
1051        name: "index_scan_percentage",
1052        description: "The index scan percentage sets a threshold for index scans. If fewer than MAX(index_scan_max_count, index_scan_percentage * total_row_count) rows match, we perform an index scan instead of a table scan.",
1053        input_type: "DOUBLE",
1054        scope: GLOBAL,
1055        aliases: &[],
1056        behaviour: Behaviour::Knob("0.001"),
1057    },
1058    SettingEntry {
1059        name: "initial_column_segment_size",
1060        description: "The initial memory (in bytes) reserved for the first transient column segment. Must be a power of two. Internally, we subtract the block header size (typically 8 bytes) for segments with or exceeding 1024 bytes. Subsequent segments double in size until reaching the block size.",
1061        input_type: "UBIGINT",
1062        scope: GLOBAL,
1063        aliases: &[],
1064        behaviour: Behaviour::Knob("2048"),
1065    },
1066    SettingEntry {
1067        name: "integer_division",
1068        description: "Whether or not the / operator defaults to integer division, or to floating point division",
1069        input_type: "BOOLEAN",
1070        scope: GLOBAL,
1071        aliases: &[],
1072        behaviour: Behaviour::Honoured,
1073    },
1074    SettingEntry {
1075        name: "json_geometry_format",
1076        description: "How GEOMETRY values are written to JSON: 'wkt' for Well-Known Text, or 'geojson' for GeoJSON geometry objects. COPY ... TO ... (FORMAT GEOJSON) always writes GeoJSON regardless of this setting.",
1077        input_type: "VARCHAR",
1078        scope: GLOBAL,
1079        aliases: &[],
1080        behaviour: Behaviour::Knob("wkt"),
1081    },
1082    SettingEntry {
1083        name: "lambda_syntax",
1084        description: "Configures the use of the deprecated single arrow operator (->) for lambda functions.",
1085        input_type: "VARCHAR",
1086        scope: GLOBAL,
1087        aliases: &[],
1088        behaviour: Behaviour::DefaultOnly("DEFAULT"),
1089    },
1090    SettingEntry {
1091        name: "late_materialization_max_rows",
1092        description: "The maximum amount of rows in the LIMIT/SAMPLE for which we trigger late materialization",
1093        input_type: "UBIGINT",
1094        scope: GLOBAL,
1095        aliases: &[],
1096        behaviour: Behaviour::Knob("50"),
1097    },
1098    SettingEntry {
1099        name: "legacy_disable_null_type",
1100        description: "When enabled, prevent the NULL type from leaving the binder (< v2.0 default behavior)",
1101        input_type: "BOOLEAN",
1102        scope: GLOBAL,
1103        aliases: &[],
1104        behaviour: Behaviour::DefaultOnly("false"),
1105    },
1106    SettingEntry {
1107        name: "legacy_metrics_format",
1108        description: "When enabled, profiling output uses the legacy flat format instead of the current grouped format",
1109        input_type: "BOOLEAN",
1110        scope: GLOBAL,
1111        aliases: &[],
1112        behaviour: Behaviour::Knob("false"),
1113    },
1114    SettingEntry {
1115        name: "lock_configuration",
1116        description: "Whether or not configurations can be altered",
1117        input_type: "BOOLEAN",
1118        scope: GLOBAL,
1119        aliases: &[],
1120        behaviour: Behaviour::DefaultOnly("false"),
1121    },
1122    SettingEntry {
1123        name: "log_query_path",
1124        description: "Specifies the path to which queries should be logged (default: NULL, queries are not logged)",
1125        input_type: "VARCHAR",
1126        scope: GLOBAL,
1127        aliases: &[],
1128        behaviour: Behaviour::Knob(""),
1129    },
1130    SettingEntry {
1131        name: "logging_level",
1132        description: "The log level which will be recorded in the log",
1133        input_type: "VARCHAR",
1134        scope: GLOBAL,
1135        aliases: &[],
1136        behaviour: Behaviour::Knob("WARNING"),
1137    },
1138    SettingEntry {
1139        name: "logging_mode",
1140        description: "Determines which types of log messages are logged",
1141        input_type: "VARCHAR",
1142        scope: GLOBAL,
1143        aliases: &[],
1144        behaviour: Behaviour::Knob("LEVEL_ONLY"),
1145    },
1146    SettingEntry {
1147        name: "logging_storage",
1148        description: "Set the logging storage (memory/stdout/file/<custom>)",
1149        input_type: "VARCHAR",
1150        scope: GLOBAL,
1151        aliases: &[],
1152        behaviour: Behaviour::Knob("shell_log_storage"),
1153    },
1154    SettingEntry {
1155        name: "max_execution_time",
1156        description: "The maximum execution time per query in milliseconds (0 = no limit)",
1157        input_type: "BIGINT",
1158        scope: GLOBAL,
1159        aliases: &[],
1160        behaviour: Behaviour::DefaultOnly("0"),
1161    },
1162    SettingEntry {
1163        name: "max_expression_depth",
1164        description: "The maximum expression depth limit in the parser. WARNING: increasing this setting and using very deep expressions might lead to stack overflow errors.",
1165        input_type: "UBIGINT",
1166        scope: GLOBAL,
1167        aliases: &[],
1168        behaviour: Behaviour::DefaultOnly("1000"),
1169    },
1170    SettingEntry {
1171        name: "max_memory",
1172        description: "The maximum memory of the system (e.g. 1GB)",
1173        input_type: "VARCHAR",
1174        scope: GLOBAL,
1175        aliases: &["memory_limit"],
1176        behaviour: Behaviour::Honoured,
1177    },
1178    SettingEntry {
1179        name: "max_streaming_buffer_size",
1180        description: "The maximum number of bytes a streaming query result buffers (e.g. 1GB). Buffered bytes stay under this cap plus at most one chunk: an oversized chunk is only admitted into an empty queue",
1181        input_type: "VARCHAR",
1182        scope: LOCAL,
1183        aliases: &["streaming_buffer_size"],
1184        behaviour: Behaviour::Knob("10.0 MiB"),
1185    },
1186    SettingEntry {
1187        name: "max_temp_directory_size",
1188        description: "The maximum amount of data stored inside the 'temp_directory' (when set) (e.g. 1GB)",
1189        input_type: "VARCHAR",
1190        scope: GLOBAL,
1191        aliases: &[],
1192        behaviour: Behaviour::Knob("unlimited"),
1193    },
1194    SettingEntry {
1195        name: "max_vacuum_tasks",
1196        description: "The maximum vacuum tasks to schedule during a checkpoint.",
1197        input_type: "UBIGINT",
1198        scope: GLOBAL,
1199        aliases: &[],
1200        behaviour: Behaviour::Knob("100"),
1201    },
1202    SettingEntry {
1203        name: "memory_limit",
1204        description: "The maximum memory of the system (e.g. 1GB)",
1205        input_type: "VARCHAR",
1206        scope: GLOBAL,
1207        aliases: &[],
1208        behaviour: Behaviour::Honoured,
1209    },
1210    SettingEntry {
1211        name: "merge_join_threshold",
1212        description: "The maximum number of rows on either table to choose a merge join",
1213        input_type: "UBIGINT",
1214        scope: GLOBAL,
1215        aliases: &[],
1216        behaviour: Behaviour::Knob("1000"),
1217    },
1218    SettingEntry {
1219        name: "nested_loop_join_threshold",
1220        description: "The maximum number of rows on either table to choose a nested loop join",
1221        input_type: "UBIGINT",
1222        scope: GLOBAL,
1223        aliases: &[],
1224        behaviour: Behaviour::Knob("5"),
1225    },
1226    SettingEntry {
1227        name: "null_on_division_by_zero",
1228        description: "Return NULL instead of throwing an error when dividing by zero.",
1229        input_type: "BOOLEAN",
1230        scope: GLOBAL,
1231        aliases: &[],
1232        behaviour: Behaviour::Honoured,
1233    },
1234    SettingEntry {
1235        name: "null_order",
1236        description: "NULL ordering used when none is specified (NULLS_FIRST or NULLS_LAST)",
1237        input_type: "VARCHAR",
1238        scope: GLOBAL,
1239        aliases: &[],
1240        behaviour: Behaviour::Honoured,
1241    },
1242    SettingEntry {
1243        name: "operator_memory_limit",
1244        description: "The maximum memory for query intermediates (sorts, hash tables) per connection (e.g. 256MB)",
1245        input_type: "VARCHAR",
1246        scope: LOCAL,
1247        aliases: &[],
1248        behaviour: Behaviour::Knob(UNSET),
1249    },
1250    SettingEntry {
1251        name: "order_by_non_integer_literal",
1252        description: "Allow ordering by non-integer literals - ordering by such literals has no effect.",
1253        input_type: "BOOLEAN",
1254        scope: GLOBAL,
1255        aliases: &[],
1256        behaviour: Behaviour::Honoured,
1257    },
1258    SettingEntry {
1259        name: "ordered_aggregate_threshold",
1260        description: "The number of rows to accumulate before sorting, used for tuning",
1261        input_type: "UBIGINT",
1262        scope: GLOBAL,
1263        aliases: &[],
1264        behaviour: Behaviour::Knob("262144"),
1265    },
1266    SettingEntry {
1267        name: "parquet_metadata_cache",
1268        description: "Cache Parquet metadata - useful when reading the same files multiple times",
1269        input_type: "BOOLEAN",
1270        scope: GLOBAL,
1271        aliases: &[],
1272        behaviour: Behaviour::Knob("false"),
1273    },
1274    SettingEntry {
1275        name: "parquet_prefetch_column_gap",
1276        description: "Byte gap under which Parquet prefetch I/O ranges are coalesced (NULL lets the cost model adapt it)",
1277        input_type: "UBIGINT",
1278        scope: GLOBAL,
1279        aliases: &[],
1280        behaviour: Behaviour::Knob(UNSET),
1281    },
1282    SettingEntry {
1283        name: "partitioned_write_flush_threshold",
1284        description: "The threshold in number of rows after which we flush a thread state when writing using PARTITION_BY",
1285        input_type: "UBIGINT",
1286        scope: GLOBAL,
1287        aliases: &[],
1288        behaviour: Behaviour::Knob("524288"),
1289    },
1290    SettingEntry {
1291        name: "partitioned_write_max_open_files",
1292        description: "The maximum amount of files the system can keep open before flushing to disk when writing using PARTITION_BY",
1293        input_type: "UBIGINT",
1294        scope: GLOBAL,
1295        aliases: &[],
1296        behaviour: Behaviour::Knob("100"),
1297    },
1298    SettingEntry {
1299        name: "password",
1300        description: "The password to use. Ignored for legacy compatibility.",
1301        input_type: "VARCHAR",
1302        scope: GLOBAL,
1303        aliases: &[],
1304        behaviour: Behaviour::Knob(""),
1305    },
1306    SettingEntry {
1307        name: "perfect_ht_threshold",
1308        description: "Threshold in bytes for when to use a perfect hash table",
1309        input_type: "UBIGINT",
1310        scope: GLOBAL,
1311        aliases: &[],
1312        behaviour: Behaviour::Knob("12"),
1313    },
1314    SettingEntry {
1315        name: "pin_threads",
1316        description: "Whether to pin threads to cores (Linux only, default AUTO: on when there are more than 64 cores)",
1317        input_type: "VARCHAR",
1318        scope: GLOBAL,
1319        aliases: &[],
1320        behaviour: Behaviour::Knob("auto"),
1321    },
1322    SettingEntry {
1323        name: "pivot_filter_threshold",
1324        description: "The threshold to switch from using filtered aggregates to LIST with a dedicated pivot operator",
1325        input_type: "UBIGINT",
1326        scope: GLOBAL,
1327        aliases: &[],
1328        behaviour: Behaviour::Knob("20"),
1329    },
1330    SettingEntry {
1331        name: "pivot_limit",
1332        description: "The maximum number of pivot columns in a pivot statement",
1333        input_type: "UBIGINT",
1334        scope: GLOBAL,
1335        aliases: &[],
1336        behaviour: Behaviour::DefaultOnly("100000"),
1337    },
1338    SettingEntry {
1339        name: "prefer_range_joins",
1340        description: "Force use of range joins with mixed predicates",
1341        input_type: "BOOLEAN",
1342        scope: GLOBAL,
1343        aliases: &[],
1344        behaviour: Behaviour::Knob("false"),
1345    },
1346    SettingEntry {
1347        name: "prefetch_all_parquet_files",
1348        description: "(deprecated) Parquet files are now always prefetched, this setting has no effect",
1349        input_type: "BOOLEAN",
1350        scope: GLOBAL,
1351        aliases: &[],
1352        behaviour: Behaviour::Knob("false"),
1353    },
1354    SettingEntry {
1355        name: "preserve_identifier_case",
1356        description: "How to fold non-quoted identifiers: 'preserve_case' keeps the case as written, 'lowercase' lowercases them, 'uppercase' uppercases them",
1357        input_type: "VARCHAR",
1358        scope: GLOBAL,
1359        aliases: &[],
1360        behaviour: Behaviour::Honoured,
1361    },
1362    SettingEntry {
1363        name: "preserve_insertion_order",
1364        description: "Whether or not to preserve insertion order. If set to false the system is allowed to re-order any results that do not contain ORDER BY clauses.",
1365        input_type: "BOOLEAN",
1366        scope: GLOBAL,
1367        aliases: &[],
1368        behaviour: Behaviour::DefaultOnly("true"),
1369    },
1370    SettingEntry {
1371        name: "profile_output",
1372        description: "The file to which profile output should be saved, or empty to print to the terminal",
1373        input_type: "VARCHAR",
1374        scope: LOCAL,
1375        aliases: &[],
1376        behaviour: Behaviour::Knob(""),
1377    },
1378    SettingEntry {
1379        name: "profiling_coverage",
1380        description: "The profiling coverage (SELECT or ALL)",
1381        input_type: "VARCHAR",
1382        scope: LOCAL,
1383        aliases: &[],
1384        behaviour: Behaviour::Knob("SELECT"),
1385    },
1386    SettingEntry {
1387        name: "profiling_output",
1388        description: "The file to which profile output should be saved, or empty to print to the terminal",
1389        input_type: "VARCHAR",
1390        scope: LOCAL,
1391        aliases: &["profile_output"],
1392        behaviour: Behaviour::Knob(""),
1393    },
1394    SettingEntry {
1395        name: "profiling_renderer_settings",
1396        description: "A map of settings passed to the renderer of the profiler output (e.g. {'max_extra_lines': 100}) - settings not recognized by the active renderer are ignored",
1397        input_type: "MAP(VARCHAR, VARCHAR)",
1398        scope: LOCAL,
1399        aliases: &[],
1400        behaviour: Behaviour::Knob("{}"),
1401    },
1402    SettingEntry {
1403        name: "progress_bar_time",
1404        description: "Sets the time (in milliseconds) how long a query needs to take before we start printing a progress bar",
1405        input_type: "BIGINT",
1406        scope: LOCAL,
1407        aliases: &[],
1408        behaviour: Behaviour::Knob("2000"),
1409    },
1410    SettingEntry {
1411        name: "read_ahead_depth",
1412        description: "Number of scan jobs prefetched ahead of decoding. -1 = automatic (backlog bounded by a memory budget), 0 = disabled.",
1413        input_type: "BIGINT",
1414        scope: GLOBAL,
1415        aliases: &[],
1416        behaviour: Behaviour::Knob("-1"),
1417    },
1418    SettingEntry {
1419        name: "regex_match_operator_semantics",
1420        description: "Configures whether regex match operators use partial or full string matching",
1421        input_type: "VARCHAR",
1422        scope: GLOBAL,
1423        aliases: &[],
1424        behaviour: Behaviour::Honoured,
1425    },
1426    SettingEntry {
1427        name: "scalar_subquery_error_on_multiple_rows",
1428        description: "Throw an error when a scalar subquery returns more than one row. When disabled, an arbitrary row is returned instead.",
1429        input_type: "BOOLEAN",
1430        scope: GLOBAL,
1431        aliases: &[],
1432        behaviour: Behaviour::Honoured,
1433    },
1434    SettingEntry {
1435        name: "scheduler_process_partial",
1436        description: "Partially process tasks before rescheduling - allows for more scheduler fairness between separate queries",
1437        input_type: "BOOLEAN",
1438        scope: GLOBAL,
1439        aliases: &[],
1440        behaviour: Behaviour::Knob("false"),
1441    },
1442    SettingEntry {
1443        name: "schema",
1444        description: "Sets the default search schema. Equivalent to setting search_path to a single value.",
1445        input_type: "VARCHAR",
1446        scope: LOCAL,
1447        aliases: &[],
1448        behaviour: Behaviour::DefaultOnly("main"),
1449    },
1450    SettingEntry {
1451        name: "search_path",
1452        description: "Sets the default catalog search path as a comma-separated list of values",
1453        input_type: "VARCHAR",
1454        scope: LOCAL,
1455        aliases: &[],
1456        behaviour: Behaviour::DefaultOnly(""),
1457    },
1458    SettingEntry {
1459        name: "secret_directory",
1460        description: "Set the directory to which persistent secrets are stored",
1461        input_type: "VARCHAR",
1462        scope: GLOBAL,
1463        aliases: &[],
1464        behaviour: Behaviour::Knob(""),
1465    },
1466    SettingEntry {
1467        name: "show_behavior",
1468        description: "How SHOW resolves a bare identifier: 'auto' (describe a table if one exists, else a setting; deprecated), 'table' (always a table), or 'setting' (always a setting)",
1469        input_type: "VARCHAR",
1470        scope: GLOBAL,
1471        aliases: &[],
1472        behaviour: Behaviour::Honoured,
1473    },
1474    SettingEntry {
1475        name: "standard_vector_size",
1476        description: "The compiled-in STANDARD_VECTOR_SIZE (read-only)",
1477        input_type: "UBIGINT",
1478        scope: GLOBAL,
1479        aliases: &[],
1480        behaviour: Behaviour::DefaultOnly("2048"),
1481    },
1482    SettingEntry {
1483        name: "storage_block_prefetch",
1484        description: "In which scenarios to use storage block prefetching",
1485        input_type: "VARCHAR",
1486        scope: GLOBAL,
1487        aliases: &[],
1488        behaviour: Behaviour::Knob("REMOTE_ONLY"),
1489    },
1490    SettingEntry {
1491        name: "storage_compatibility_version",
1492        description: "Serialize on checkpoint with compatibility for a given duckdb version",
1493        input_type: "VARCHAR",
1494        scope: GLOBAL,
1495        aliases: &[],
1496        behaviour: Behaviour::Knob("latest"),
1497    },
1498    SettingEntry {
1499        name: "streaming_buffer_size",
1500        description: "The maximum number of bytes a streaming query result buffers (e.g. 1GB). Buffered bytes stay under this cap plus at most one chunk: an oversized chunk is only admitted into an empty queue",
1501        input_type: "VARCHAR",
1502        scope: LOCAL,
1503        aliases: &[],
1504        behaviour: Behaviour::Knob("10.0 MiB"),
1505    },
1506    SettingEntry {
1507        name: "table_function_identifier_conversion",
1508        description: "Configures the use of deprecated implicit conversion of unbound identifiers to strings in table function arguments.",
1509        input_type: "VARCHAR",
1510        scope: GLOBAL,
1511        aliases: &[],
1512        behaviour: Behaviour::DefaultOnly("DEFAULT"),
1513    },
1514    SettingEntry {
1515        name: "temp_directory",
1516        description: "Set the directory to which to write temp files",
1517        input_type: "VARCHAR",
1518        scope: GLOBAL,
1519        aliases: &[],
1520        behaviour: Behaviour::Knob(""),
1521    },
1522    SettingEntry {
1523        name: "temp_file_encryption",
1524        description: "Encrypt all temporary files if database is encrypted",
1525        input_type: "BOOLEAN",
1526        scope: GLOBAL,
1527        aliases: &[],
1528        behaviour: Behaviour::Knob("false"),
1529    },
1530    SettingEntry {
1531        name: "threads",
1532        description: "The number of total threads used by the system.",
1533        input_type: "BIGINT",
1534        scope: GLOBAL,
1535        aliases: &["worker_threads"],
1536        behaviour: Behaviour::Honoured,
1537    },
1538    SettingEntry {
1539        name: "tracked_metrics",
1540        description: "A list of metric glob patterns to enable for collection (e.g. ['query.*', 'optimizer.*'])",
1541        input_type: "VARCHAR[]",
1542        scope: LOCAL,
1543        aliases: &[],
1544        behaviour: Behaviour::Knob("[*]"),
1545    },
1546    SettingEntry {
1547        name: "user",
1548        description: "The username to use. Ignored for legacy compatibility.",
1549        input_type: "VARCHAR",
1550        scope: GLOBAL,
1551        aliases: &[],
1552        behaviour: Behaviour::Knob(""),
1553    },
1554    SettingEntry {
1555        name: "username",
1556        description: "The username to use. Ignored for legacy compatibility.",
1557        input_type: "VARCHAR",
1558        scope: GLOBAL,
1559        aliases: &["user"],
1560        behaviour: Behaviour::Knob(""),
1561    },
1562    SettingEntry {
1563        name: "vacuum_rebuild_indexes",
1564        description: "(Experimental) Allow vacuum to compact row groups on tables with bound ART indexes, rebuilding the indexes afterward. Tables with a row count exceeding this threshold are skipped. 0 = disabled. Can also be set per-database via the 'vacuum_rebuild_indexes' ATTACH option, which overrides this default.",
1565        input_type: "UBIGINT",
1566        scope: GLOBAL,
1567        aliases: &[],
1568        behaviour: Behaviour::Knob("0"),
1569    },
1570    SettingEntry {
1571        name: "validate_external_file_cache",
1572        description: "Cache validation mode: VALIDATE_ALL (default, validate all cache entries), VALIDATE_REMOTE (validate only remote cache entries), or NO_VALIDATION (disable cache validation).",
1573        input_type: "VARCHAR",
1574        scope: GLOBAL,
1575        aliases: &[],
1576        behaviour: Behaviour::Knob("VALIDATE_ALL"),
1577    },
1578    SettingEntry {
1579        name: "variant_minimum_shredding_size",
1580        description: "Minimum size of a rowgroup to enable VARIANT shredding, or set to -1 to disable entirely. Defaults to 1/4th of a rowgroup",
1581        input_type: "BIGINT",
1582        scope: GLOBAL,
1583        aliases: &[],
1584        behaviour: Behaviour::Knob("30000"),
1585    },
1586    SettingEntry {
1587        name: "wal_autocheckpoint",
1588        description: "The WAL size threshold at which to automatically trigger a checkpoint (e.g. 1GB)",
1589        input_type: "VARCHAR",
1590        scope: GLOBAL,
1591        aliases: &[],
1592        behaviour: Behaviour::Knob("16.0 MiB"),
1593    },
1594    SettingEntry {
1595        name: "wal_autocheckpoint_entries",
1596        description: "Trigger automatic checkpoint when WAL entry count reaches or exceeds N (0 = disabled)",
1597        input_type: "UBIGINT",
1598        scope: GLOBAL,
1599        aliases: &[],
1600        behaviour: Behaviour::Knob("0"),
1601    },
1602    SettingEntry {
1603        name: "warnings_as_errors",
1604        description: "Escalate all warnings to errors.",
1605        input_type: "BOOLEAN",
1606        scope: GLOBAL,
1607        aliases: &[],
1608        behaviour: Behaviour::Honoured,
1609    },
1610    SettingEntry {
1611        name: "worker_threads",
1612        description: "The number of total threads used by the system.",
1613        input_type: "BIGINT",
1614        scope: GLOBAL,
1615        aliases: &[],
1616        behaviour: Behaviour::Honoured,
1617    },
1618    SettingEntry {
1619        name: "write_buffer_row_group_count",
1620        description: "The amount of row groups to buffer in bulk ingestion prior to flushing them together. Reducing this setting can reduce memory consumption.",
1621        input_type: "UBIGINT",
1622        scope: GLOBAL,
1623        aliases: &[],
1624        behaviour: Behaviour::Knob("5"),
1625    },
1626    SettingEntry {
1627        name: "write_buffer_row_group_memory_limit",
1628        description: "The maximum data to buffer in row groups (in bytes) to buffer prior to flushing them together. When either this limit is reached, or write_buffer_row_group_count is reached, we flush the data to disk. Defaults to 20% of memory limit divided by thread count.",
1629        input_type: "VARCHAR",
1630        scope: GLOBAL,
1631        aliases: &[],
1632        behaviour: Behaviour::Knob("155.5 MiB"),
1633    },
1634    SettingEntry {
1635        name: "zstd_min_string_length",
1636        description: "The (average) length at which to enable ZSTD compression, defaults to 4096",
1637        input_type: "UBIGINT",
1638        scope: GLOBAL,
1639        aliases: &[],
1640        behaviour: Behaviour::Knob("4096"),
1641    },
1642];
1643
1644/// The columns `duckdb_settings()` returns, in the pin's order.
1645#[must_use]
1646pub fn setting_fields() -> Vec<Field> {
1647    vec![
1648        Field::new("name", LogicalType::Varchar),
1649        Field::new("value", LogicalType::Varchar),
1650        Field::new("description", LogicalType::Varchar),
1651        Field::new("input_type", LogicalType::Varchar),
1652        Field::new("scope", LogicalType::Varchar),
1653        Field::new("aliases", LogicalType::list(LogicalType::Varchar)),
1654        Field::new("typed_value", LogicalType::Varchar),
1655    ]
1656}
1657
1658/// The entry for a setting with this name, and `None` for a name that is not a setting.
1659///
1660/// The comparison ignores case, which is the pin's rule rather than a convenience here.
1661/// `SELECT current_setting('THREADS')` answers with the thread count there and `SET THREADS = 4`
1662/// turns it, so a setting name is matched the way an identifier is and not the way a string is.
1663#[must_use]
1664pub fn setting_named(name: &str) -> Option<&'static SettingEntry> {
1665    SETTINGS.iter().find(|entry| entry.name.eq_ignore_ascii_case(name))
1666}
1667
1668/// What the engine says when it is handed a name that is not a setting.
1669///
1670/// Here rather than where each caller is, because there are three of them and they are in two
1671/// crates. `SET nope = 1`, `RESET nope` and `current_setting('nope')` all say this, and on the pin
1672/// they say the same sentence as each other, so one sentence is what they share.
1673///
1674/// The list after it is the five nearest names, the way the pin prints at most five, because with a
1675/// hundred and ninety two of them printing the lot buries the one the writer meant. The pin scores
1676/// by its own string similarity and this scores by edit distance, so the two lists agree on the
1677/// obvious misspellings and may differ on the rest.
1678#[must_use]
1679pub fn unknown_setting(name: &str) -> String {
1680    let mut scored: Vec<(usize, &'static str)> =
1681        SETTINGS.iter().map(|entry| (distance(name, entry.name), entry.name)).collect();
1682    // Ties go to the name that sorts first, so the list is the same one twice for the same input.
1683    scored.sort_unstable();
1684    let near: Vec<String> = scored
1685        .iter()
1686        .take(SUGGESTIONS)
1687        .filter(|(score, candidate)| *score <= cutoff(name, candidate))
1688        .map(|(_, candidate)| format!("\"{candidate}\""))
1689        .collect();
1690    let message = format!("unrecognized configuration parameter \"{name}\"");
1691    if near.is_empty() {
1692        return message;
1693    }
1694    format!("{message}\n\nDid you mean: {}", near.join(", "))
1695}
1696
1697/// How many names the suggestion list holds at most, which is what the pin prints.
1698const SUGGESTIONS: usize = 5;
1699
1700/// How far a name may be from what was written and still be worth suggesting.
1701///
1702/// A third of the longer of the two, so a short name has to be nearly right and a long one may be
1703/// off by several letters. Without a cutoff a name like `x` would drag in whichever five settings
1704/// happen to be shortest, which is a list about the table rather than about the mistake. A third
1705/// rather than a half because at a half `memory_limitt` suggests `pivot_limit`, which shares a
1706/// suffix and nothing else.
1707fn cutoff(written: &str, candidate: &str) -> usize {
1708    written.chars().count().max(candidate.chars().count()).div_ceil(3).max(1)
1709}
1710
1711/// The number of single character edits between two names, ignoring case.
1712///
1713/// The ordinary two row Levenshtein. Both strings are names a person typed or a table holds, so
1714/// this walks characters rather than bytes and a multi byte letter counts once.
1715fn distance(written: &str, candidate: &str) -> usize {
1716    let left: Vec<char> = written.chars().flat_map(char::to_lowercase).collect();
1717    let right: Vec<char> = candidate.chars().flat_map(char::to_lowercase).collect();
1718    let mut previous: Vec<usize> = (0..=right.len()).collect();
1719    let mut current = vec![0; right.len() + 1];
1720    for (row, from) in left.iter().enumerate() {
1721        current[0] = row + 1;
1722        for (column, to) in right.iter().enumerate() {
1723            let substitute = previous[column] + usize::from(from != to);
1724            current[column + 1] = substitute.min(previous[column + 1] + 1).min(current[column] + 1);
1725        }
1726        std::mem::swap(&mut previous, &mut current);
1727    }
1728    previous[right.len()]
1729}
1730
1731#[cfg(test)]
1732mod tests {
1733    use super::{
1734        Behaviour, GLOBAL, LOCAL, SETTINGS, setting_fields, setting_named, unknown_setting,
1735    };
1736
1737    #[test]
1738    fn the_table_is_the_shape_the_pin_returns() {
1739        assert_eq!(SETTINGS.len(), 192, "a hundred and eighty five settings, seven of them twice");
1740        assert_eq!(setting_fields().len(), 7);
1741    }
1742
1743    #[test]
1744    fn the_names_are_sorted_because_the_pin_returns_them_that_way() {
1745        let names: Vec<&str> = SETTINGS.iter().map(|entry| entry.name).collect();
1746        let mut sorted = names.clone();
1747        sorted.sort_unstable();
1748        assert_eq!(names, sorted);
1749    }
1750
1751    /// The alias reads backwards, so it gets a test rather than a comment nobody checks against the
1752    /// binary again.
1753    #[test]
1754    fn an_alias_is_a_row_of_its_own_and_the_list_sits_on_the_other_one() {
1755        let memory = setting_named("max_memory").expect("a setting");
1756        assert_eq!(memory.aliases, ["memory_limit"]);
1757        assert_eq!(setting_named("memory_limit").expect("a setting").aliases, [] as [&str; 0]);
1758        let threads = setting_named("threads").expect("a setting");
1759        assert_eq!(threads.aliases, ["worker_threads"]);
1760        assert_eq!(setting_named("worker_threads").expect("a setting").aliases, [] as [&str; 0]);
1761        // Both halves of a pair say the same thing, since they are one setting with two names.
1762        assert_eq!(
1763            memory.description,
1764            setting_named("memory_limit").expect("a setting").description
1765        );
1766        assert_eq!(
1767            threads.input_type,
1768            setting_named("worker_threads").expect("a setting").input_type
1769        );
1770        // Seven pairs, and every name in a list is a row of its own.
1771        let pairs: Vec<&str> =
1772            SETTINGS.iter().filter(|entry| !entry.aliases.is_empty()).map(|e| e.name).collect();
1773        assert_eq!(pairs.len(), 7, "{pairs:?}");
1774        for entry in SETTINGS {
1775            for alias in entry.aliases {
1776                let other = setting_named(alias).expect("an alias has a row");
1777                assert_eq!(other.behaviour, entry.behaviour, "{}", entry.name);
1778                assert_eq!(other.scope, entry.scope, "{}", entry.name);
1779            }
1780        }
1781    }
1782
1783    #[test]
1784    fn every_scope_is_one_of_the_two_the_pin_prints() {
1785        let local = SETTINGS.iter().filter(|entry| entry.scope == LOCAL).count();
1786        assert_eq!(local, 15);
1787        for entry in SETTINGS {
1788            assert!(entry.scope == GLOBAL || entry.scope == LOCAL, "{}", entry.name);
1789        }
1790        assert_eq!(setting_named("nothing_called_this"), None);
1791    }
1792
1793    /// Twenty three names are read by the engine and the rest are taken and kept, or taken at one
1794    /// value and refused at the others. The counts are here so that moving a setting from one case
1795    /// to another is a line in a diff rather than something nobody notices.
1796    #[test]
1797    fn every_setting_is_read_or_carried_or_held_at_its_default() {
1798        let count = |wanted: fn(&Behaviour) -> bool| {
1799            SETTINGS.iter().filter(|entry| wanted(&entry.behaviour)).count()
1800        };
1801        assert_eq!(count(|b| matches!(b, Behaviour::Honoured)), 23);
1802        assert_eq!(count(|b| matches!(b, Behaviour::Knob(_))), 133);
1803        assert_eq!(count(|b| matches!(b, Behaviour::DefaultOnly(_))), 36);
1804        assert_eq!(
1805            setting_named("memory_limit").expect("a setting").behaviour,
1806            Behaviour::Honoured
1807        );
1808        assert_eq!(
1809            setting_named("enable_http_metadata_cache").expect("a setting").behaviour,
1810            Behaviour::Knob("false")
1811        );
1812        assert_eq!(
1813            setting_named("preserve_insertion_order").expect("a setting").behaviour,
1814            Behaviour::DefaultOnly("true")
1815        );
1816    }
1817
1818    /// The pin answers `current_setting('THREADS')` and turns `SET THREADS`, so case is ignored.
1819    #[test]
1820    fn a_setting_is_found_whichever_way_the_name_is_cased() {
1821        assert_eq!(setting_named("THREADS").expect("a setting").name, "threads");
1822        assert_eq!(setting_named("Memory_Limit").expect("a setting").name, "memory_limit");
1823    }
1824
1825    /// The sentence three callers in two crates share, with the pin's blank line in the middle.
1826    #[test]
1827    fn an_unknown_setting_is_named_and_then_the_nearest_ones_are_listed() {
1828        // Word for word what the pin says to the same mistake.
1829        let message = unknown_setting("memory_limitt");
1830        assert_eq!(
1831            message,
1832            "unrecognized configuration parameter \"memory_limitt\"\n\nDid you mean: \"memory_limit\""
1833        );
1834        // At most five, the way the pin prints at most five.
1835        let many = unknown_setting("enable_");
1836        assert!(many.matches('"').count() <= 2 + 5 * 2, "{many}");
1837        // A name nothing is near gets the sentence and no list, rather than five names picked for
1838        // being short.
1839        assert_eq!(
1840            unknown_setting("zzzzzzzzzzzzzzzzzzzzzzzz"),
1841            "unrecognized configuration parameter \"zzzzzzzzzzzzzzzzzzzzzzzz\""
1842        );
1843    }
1844}