Repository: duckdb/duckdb
File: scripts/settings_scripts/update_settings_header_file.py (Python)

Code before:
from .config import SEPARATOR, SettingsList, find_start_end_indexes, write_content_to_file

# markers
START_MARKER = (
    f"//===----------------------------------------------------------------------===//\n"
    f"// This code is autogenerated from 'update_settings_header_file.py'.\n"
    f"// Please do not make any changes directly here, as they will be overwritten.\n//\n"
    f"// Start of the auto-generated list of settings structures\n"
    f"//===----------------------------------------------------------------------===//\n"
)
END_MARKER = "// End of the auto-generated list of settings structures"


def extract_declarations(setting) -> str:
    if setting.alternative_sql_type:
        alternative_sql_type = f"\"{setting.alternative_sql_type}\""
    else:
        alternative_sql_type = "nullptr"
    definition = (
        f"struct {setting.struct_name} {{\n"
        f"    using RETURN_TYPE = {setting.return_type};\n"
        f"    static constexpr const char *Name = \"{setting.name}\";\n"
        f"    static constexpr const char *Description = \"{setting.description}\";\n"
        f"    static constexpr const char *InputType = \"{setting.sql_type}\";\n"
        f"    static constexpr const char *AlternativeInputType = {alternative_sql_type};\n"
    )
    if setting.scope == "GLOBAL" or setting.scope == "GLOBAL_LOCAL":
        definition += f"    static void SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &parameter);\n"
        definition += f"    static void ResetGlobal(DatabaseInstance *db, DBConfig &config);\n"
        if setting.on_set:
            definition += f"static bool OnGlobalSet(DatabaseInstance *db, DBConfig &config, const Value &input);\n"
        if setting.on_reset:
            definition += f"static bool OnGlobalReset(DatabaseInstance *db, DBConfig &config);\n"
    if setting.scope == "LOCAL" or setting.scope == "GLOBAL_LOCAL":
        definition += f"    static void SetLocal(ClientContext &context, const Value &parameter);\n"
        definition += f"    static void ResetLocal(ClientContext &context);\n"
        if setting.on_set:
            definition += f"static bool OnLocalSet(ClientContext &context, const Value &input);\n"
        if setting.on_reset:
            definition += f"static bool OnLocalReset(ClientContext &context);\n"
    if setting.scope is not None:
        definition += f"    static Value GetSetting(const ClientContext &context);\n"
    if setting.is_generic_setting:
        definition += f"    static constexpr const char *DefaultValue = \"{setting.default_value}\";\n"
        definition += f"    static constexpr SetScope DefaultScope = SetScope::{setting.default_scope};\n"
        if setting.on_set:
            definition += f"    static void OnSet(SettingCallbackInfo &info, Value &input);\n"

    definition += f"}};\n\n"
    return definition

Review comment (bug):
I'm not such a big fan of this - it seems like this doesn't generalize particularly nicely.

My proposed solution here would be to add a second setting - `extension_directories` - that takes a `VARCHAR[]`. The original `extension_directory` setting can then only be used to set a single directory (i.e. maintains the old behavior). They just control the same underlying setting.

We actually already have a setting that works like this - `schema` and `search_path` (albeit not as nice since this predates nested types):

```sql
attach ':memory:' as db1;
attach ':memory:' as db2;
-- schema can only set a single path
set schema='db1.main';
set schema='db1.main,db2.main';
-- Parser Error: Failed to convert entry "db1.main,db2.main" to CatalogSearchEntry - expected a single entry
-- search path can set multiple
set search_path='db1.main,db2.main';
```

I think if we go down this route we can revert most of the special casing added to the config / settings layer.

Code after:
from .config import SEPARATOR, SettingsList, find_start_end_indexes, write_content_to_file

# markers
START_MARKER = (
    f"//===----------------------------------------------------------------------===//\n"
    f"// This code is autogenerated from 'update_settings_header_file.py'.\n"
    f"// Please do not make any changes directly here, as they will be overwritten.\n//\n"
    f"// Start of the auto-generated list of settings structures\n"
    f"//===----------------------------------------------------------------------===//\n"
)
END_MARKER = "// End of the auto-generated list of settings structures"


def extract_declarations(setting) -> str:
    definition = (
        f"struct {setting.struct_name} {{\n"
        f"    using RETURN_TYPE = {setting.return_type};\n"
        f"    static constexpr const char *Name = \"{setting.name}\";\n"
        f"    static constexpr const char *Description = \"{setting.description}\";\n"
        f"    static constexpr const char *InputType = \"{setting.sql_type}\";\n"
    )
    if setting.scope == "GLOBAL" or setting.scope == "GLOBAL_LOCAL":
        definition += f"    static void SetGlobal(DatabaseInstance *db, DBConfig &config, const Value &parameter);\n"
        definition += f"    static void ResetGlobal(DatabaseInstance *db, DBConfig &config);\n"
        if setting.on_set:
            definition += f"static bool OnGlobalSet(DatabaseInstance *db, DBConfig &config, const Value &input);\n"
        if setting.on_reset:
            definition += f"static bool OnGlobalReset(DatabaseInstance *db, DBConfig &config);\n"
    if setting.scope == "LOCAL" or setting.scope == "GLOBAL_LOCAL":
        definition += f"    static void SetLocal(ClientContext &context, const Value &parameter);\n"
        definition += f"    static void ResetLocal(ClientContext &context);\n"
        if setting.on_set:
            definition += f"static bool OnLocalSet(ClientContext &context, const Value &input);\n"
        if setting.on_reset:
            definition += f"static bool OnLocalReset(ClientContext &context);\n"
    if setting.scope is not None:
        definition += f"    static Value GetSetting(const ClientContext &context);\n"
    if setting.is_generic_setting:
        definition += f"    static constexpr const char *DefaultValue = \"{setting.default_value}\";\n"
        definition += f"    static constexpr SetScope DefaultScope = SetScope::{setting.default_scope};\n"
        if setting.on_set:
            definition += f"    static void OnSet(SettingCallbackInfo &info, Value &input);\n"

    definition += f"}};\n\n"
    return definition


# generate code for all the settings for the the header file
def generate_content(header_file_path):
    with open(header_file_path, 'r') as source_file: