Skip to content

fix(duckdb): apply extension settings from connector_config before INSTALL/LOAD - #6123

Open
Tanakpek wants to merge 1 commit into
SQLMesh:mainfrom
Tanakpek:fix/duckdb-extension-settings-order
Open

Tanakpek wants to merge 1 commit into
SQLMesh:mainfrom
Tanakpek:fix/duckdb-extension-settings-order

Conversation

@Tanakpek

@Tanakpek Tanakpek commented Oct 9, 2026

Copy link
Copy Markdown

Summary

This PR fixes a bug in the DuckDB connection's dependency bootstrap process. When a new cursor is initialized, SQLMesh bootstraps the connection's dependencies (extensions, settings, secrets, filesystems, catalogs) in a fixed order, and the step that installs extensions ran before the step that configured where extensions are fetched from. The bootstrap therefore reached out to the default public repository even when the user had configured an internal mirror or a local directory, which cannot be worked around from configuration. This PR reorders the bootstrap so extension-management settings are applied first.

Problem

BaseDuckDBConnectionConfig._cursor_init currently runs in this order on every new cursor:

  1. INSTALL <ext> + LOAD <ext> for every entry in extensions
  2. SET <k> = '<v>' for every entry in connector_config

That means settings which control where extensions come from (custom_extension_repository, extension_directory, autoinstall_known_extensions, http_proxy, ...) are applied after DuckDB has already tried to fetch the extensions from the default extensions.duckdb.org repository.

In a restricted network (internal Artifactory mirror, air-gapped host, pre-downloaded extension directory) this is a chicken-and-egg problem: there is no way to make INSTALL httpfs look anywhere other than the default repo, so the connection hangs/fails before the configured repository is ever set. Even a plain local folder cannot be used as a repository because the install runs first.

A second, related bug: the per-extension repository value is interpolated unquoted into INSTALL <name> FROM <repository>. That only works for bare aliases (community, core_nightly); a local path or https:// URL produces a parse error.

Fix

  • Split connector_config into two phases around the extension loop:
    • settings in a new DUCKDB_EXTENSION_SETTINGS set (custom_extension_repository, autoinstall_extension_repository, extension_directory, autoinstall_known_extensions, autoload_known_extensions, allow_community_extensions, allow_extensions_metadata_mismatch, http_proxy, http_proxy_username, http_proxy_password) are applied before any INSTALL/LOAD;
    • all remaining settings are applied after, exactly as today, since some of them (e.g. s3_region) only exist once the corresponding extension is loaded.
  • The existing "read duckdb_settings() and only SET when the value differs" logic (Fix(duckdb): Only SET connector_config values on cursor init if they are different #4981) is kept and factored into a small apply_settings helper used by both phases.
  • INSTALL ... FROM <repository> now quotes anything that is not a bare identifier alias, so local directories and URLs work (INSTALL httpfs FROM '/opt/duckdb/extensions').

New order:

duckdb.connect
  -> SET extension-management settings
  -> INSTALL / LOAD extensions
  -> SET remaining connector_config
  -> CREATE SECRET ...
  -> register fsspec filesystems
  -> ATTACH catalogs

All settings in DUCKDB_EXTENSION_SETTINGS were checked against duckdb_settings() on DuckDB 1.4.5 and are core (not extension-provided) and SET-able on a live connection. allow_unsigned_extensions is deliberately excluded because it is startup-only and cannot be SET; the docs call this out.

Reproduction (before / after)

connection:
  type: duckdb
  connector_config:
    custom_extension_repository: /tmp/empty-dir   # or an internal https:// mirror
    autoinstall_known_extensions: false
  extensions:
    - httpfs
  • Before: INSTALL httpfs runs first and reaches out to extensions.duckdb.org (hangs or fails in a locked-down network); the custom repository is only set afterwards.
  • After: DuckDB looks in the configured repository (.../v1.4.5/osx_arm64/httpfs.duckdb_extension) and never contacts the default one.

Tests

  • test_duckdb_extension_settings_applied_before_install asserts the relative order of SET custom_extension_repository / SET autoinstall_known_extensions < INSTALL httpfs < LOAD httpfs < SET memory_limit.
  • test_duckdb_connector_config_without_extensions covers the no-extensions path.
  • test_duckdb_extension_repository_quoting (parametrized) covers aliases, local paths, URLs and quote escaping; test_duckdb_extension_force_install_with_repository covers FORCE INSTALL ... FROM '...'.
  • Existing tests/core/test_connection_config.py -k duckdb, tests/core/engine_adapter/test_duckdb.py and the DuckDB integration tests (test_connector_config_from_multiple_connections, test_secret_registration_from_multiple_connections) pass; make style is clean.

Docs

docs/integrations/engines/duckdb.md: expanded the extensions / connector_config rows, added an "Extensions" section documenting the dict form (name, repository, force_install), and a sub-section on installing from a custom or offline repository that describes the ordering guarantee.

Out of scope

  • Passing startup-only options such as allow_unsigned_extensions via duckdb.connect(config=...) (this was removed in Fix: configure duckdb with connector_config settings. #2240; reintroducing it is a separate discussion).
  • Adapter reuse via _data_file_to_adapter still ignores the configuration of a second connection pointing at the same database file.

…STALL/LOAD

Settings that control where DuckDB fetches extensions from
(custom_extension_repository, extension_directory,
autoinstall_known_extensions, http_proxy, ...) were applied after the
extensions had already been installed, so in restricted networks
INSTALL reached out to the default repository before the configured
one was ever set. These settings are now applied first; all other
connector_config settings are still applied after the extensions are
loaded.

Also quote non-alias `repository` values in `INSTALL ... FROM` so local
directories and URLs work, not only aliases such as `community`.

Signed-off-by: Tan Akpek <akpektan@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant