# v0.9.1 — Connection-lifecycle & catalog-context fixes

Two related bugs that both stem from the extension owning long-lived
connections separate from the caller's:

1. **RW→RO reopen hang** — the OverrideContext catalog connection
   prevents reopening the DB in a different access mode in the same
   process.
2. **ADBC / cross-connection expansion fails** — `FROM
   semantic_view(...)` executes the inner expanded SQL on the
   extension's `query_conn`, whose catalog/schema context may not
   match the caller's. Unqualified table refs surface as
   `Catalog Error: Table with name X does not exist`.

# Item 1: In-process RW→RO reopen hang

## Downstream bug report (2026-05-21)

```python
# duckdb 1.5.2, semantic_views v0.9.0
w = duckdb.connect(path)
w.execute("INSTALL semantic_views FROM community")
w.execute("LOAD semantic_views")
w.execute("CREATE TABLE sales_data (...)")
w.execute("CREATE SEMANTIC VIEW sales_view AS ...")
w.close()

r = duckdb.connect(path, read_only=True)   # hangs >45s
```

Without `CREATE SEMANTIC VIEW`, RO open returns instantly. So the
blocker isn't `LOAD` on a read-only DB (Phase 63's actual fix) — it's
opening a DB that has a persisted semantic view in read-only mode
*in the same process that bootstrapped it*.

## Known issue — deferred during v0.9.0

Documented in `.planning/phases/63-readonly-database-load-support/deferred-items.md`
under "In-process RW→RO reopen of the same DB hangs (Phase 62
OverrideContext leak)". Phase 63 tests worked around it by bootstrapping
in `subprocess.run(...)` so the OS reclaims `DBConfig` at child exit.

Rationale for deferral at the time: "production deployments separate
bootstrap (CI/build) from read-only query (analytics worker) across
process boundaries; the hang only manifests in scripts that do both
in one process."

A real downstream user just hit it. The classification was wrong.

## Root cause hypothesis

Phase 62 attaches `OverrideContext` (which owns a `duckdb_connection`
opened via `duckdb_connect` in `init_extension`) to
`SemanticViewsParserInfo` on `DBConfig`. RESEARCH §Q2 documented this
as an intentional bounded leak. The catalog connection keeps the
underlying `Database` alive past the user's `conn.close()`. DuckDB's
in-process `DatabaseManager` then refuses to reopen the same path in a
different access mode while a writable handle is still referenced and
blocks indefinitely.

## Candidate fixes (from deferred-items.md)

- **(a) Real fix.** Drop the `OverrideContext` catalog connection
  deterministically at extension teardown via a DuckDB extension-unload
  hook (if available) — or via `DBConfig` destructor coupling.
- **(b) Safety net.** Detect access-mode mismatch on reopen inside
  `init_extension` and return a clear error instead of hanging.

Phase plan for v0.9.1 should investigate (a) first; (b) is the cheap
fallback if no unload hook surface exists in DuckDB 1.5.x.

## Test requirements

The current `test/integration/test_readonly_load.py` uses subprocess
bootstrap precisely *because* of this bug. After the fix, add a
companion test that does same-process bootstrap-then-RO and asserts
the second `connect(..., read_only=True)` returns in under N seconds
(watchdog). The subprocess version should stay as the deployment-style
smoke; the in-process version is the regression guard for v0.9.1.

---

# Item 2: ADBC / cross-connection expansion fails

## Symptom (from `_notes/error_with_adbc.md`)

```
adbc_driver_manager.ProgrammingError: Catalog Error: Table with name sales_data does not exist\!
Did you mean "memory.sales_data"?

FROM "sales_data" AS "s"
```

Raw SQL through ADBC works. Only `FROM semantic_view(...)` fails.

## Root cause

`src/lib.rs:493-508` — at extension load time, the extension creates a
dedicated `query_conn` via `duckdb_connect(db_handle)` and stores it in
the `semantic_view` table function's `QueryState`. When the user runs
`SELECT … FROM semantic_view(...)` on their own connection (Python,
ADBC, dbt, whatever), the inner expanded SQL executes on **that
extension-owned `query_conn`**, not the caller's. The two connections
have independent catalog/schema search paths. Through Python the
defaults usually coincide (`memory.main`) so the bug stays latent.
Through ADBC they diverge and the unqualified `FROM "sales_data"`
fails.

## Current state of mitigation

Phase 64 (v0.9.0) added `qualify_and_quote_table_ref` in
`src/expand/resolution.rs:70`, which prepends `def.database_name` and
`def.schema_name` (captured at CREATE time via
`current_database()` / `current_schema()` in
`src/ddl/define.rs:138`). This is wired into the main expansion path
at `src/expand/sql_gen.rs:499, 530, 550`.

But it is **NOT** wired into:

- Fact-query expansion — `src/expand/sql_gen.rs:181, 224, 244`
- Semi-additive expansion — `src/expand/semi_additive.rs:195, 220, 238`
- Window-metric expansion — `src/expand/window.rs:156, 181, 199`
- Materialization routing — `src/expand/materialization.rs:157`

A view that uses any of FACTS / semi-additive / window metrics /
materialization routing will still emit unqualified table refs on the
inner SQL and fail under ADBC (and any other client with a divergent
search path).

There is also a subtler risk: `current_database()` / `current_schema()`
are queried on the **catalog connection** at CREATE time, not the
caller's. If the caller's session has a different active database
attached (e.g. `ATTACH 'other.db' AS other; USE other;`), the captured
qualifier will be wrong.

## Fix strategy

- Port `qualify_and_quote_table_ref` into every remaining expansion
  site that emits a physical table reference (fact / semi-additive /
  window / materialization).
- Audit whether `current_database()` / `current_schema()` capture at
  CREATE time is reliable across connection contexts; if not, fall
  back to qualifying at expand time using a connection-aware lookup
  (or accept that catalog context is captured-at-CREATE and document
  the limitation).
- Add an ADBC query regression test: `SELECT … FROM semantic_view(...)`
  through `adbc_driver_duckdb` against a multi-database scenario
  (`ATTACH … AS db2; CREATE SEMANTIC VIEW db2.main.v AS …; FROM
  semantic_view('db2.main.v', …)`).
- Tests must cover at least one fact-query, one semi-additive metric,
  and one window-metric so the broken paths are exercised.

## Existing note (predates v0.9.1 scoping)

See `_notes/error_with_adbc.md` for the original downstream report
(dated 2026-05-02). That note's "this is upstream" conclusion is
wrong — the catalog mismatch is something we can fix at expansion
time by emitting fully-qualified `db.schema.table` references.
