---
phase: 61-bounded-isolation-quality-docs
plan: 01
type: retroactive-reconstruction
wave: 1
depends_on: [60-race-guards-validation-hardening]
autonomous: false
requirements: [LRU-01, RAII-01, ESC-LIFTOUT-01, FUZZ-FFI-01, DOCS-01]
source_commits:
  - 2fa8ec2  # feat(v0.8.1): close milestone — race guards, FALLBACK error workaround, full test/doc coverage  (partial — LRU/RAII/tests/docs portions)
  - c27a170  # fix(v0.8.1): address PR #29 review — sound FFI buffer free, YAML FILE eviction error
  - b5285a2  # 0.8.0 docs review edits
  - 4851e43  # docs(v0.8.1): document CREATE IF NOT EXISTS race + caret-position behaviour change
files_modified:
  - src/parse.rs                                  # bounded LRU, escape-helper extraction
  - src/catalog.rs                                # CatalogReader RAII (PreparedStmt, QueryResult)
  - cpp/src/shim.cpp                              # FFI buffer free correctness
  - fuzz/Cargo.toml
  - fuzz/fuzz_targets/fuzz_parser_override_ffi.rs
  - examples/race_guards_and_unification.py
  - test/integration/test_caret_position.py       # text-only assertions for v0.8.0
  - test/integration/test_concurrent_ddl.py
  - test/integration/test_type_inference.py       # type inference under txn
  - test/sql/v080_transactional_ddl.test          # additional D-series cases
  - CHANGELOG.md
  - TECH-DEBT.md
  - MAINTAINER.md
  - docs/explanation/transactional-ddl-and-limitations.rst
  - docs/explanation/index.rst
  - docs/how-to/yaml-definitions.rst
  - docs/reference/{create,alter,describe,drop,error-messages,show}-semantic-view*.rst
note: Phase 61 shares commit 2fa8ec2 with Phase 60. The split groups commit content by intent (race/validation in 60; bounded isolation, RAII, tests, docs in 61), not by commit boundary.
---

# Phase 61: Bounded Multi-DB Isolation, RAII, Tests & Docs

> Reconstructed retroactively on 2026-05-05.

## Goal

Cap the per-DB token→catalog map (Phase 58 left it unbounded), bring `CatalogReader` resource handling under RAII, lift the SQL-escape helper out of the rewrite path, expand integration test coverage to `BEGIN/COMMIT` corners, add an FFI fuzz target, and refresh user-facing documentation (CHANGELOG, TECH-DEBT, MAINTAINER, Sphinx site).

## Approach

### Bounded LRU for multi-DB processes

The Phase 58 multi-DB isolation fix introduced a global `HashMap<u64, CatalogReader>` keyed by `db_token`. Long-lived processes that opened more than 16 DuckDB instances (test harnesses, ADBC servers, long-running notebooks) leaked `CatalogReader` handles indefinitely. Cap the map at **16 entries** with insertion-order eviction. When an evicted token is later reused, the rewrite path surfaces the actionable error `catalog context for this database has been evicted` rather than silently routing CREATE to the wrong database (TECH-DEBT item 20).

Note: this is a known limitation, not a clean fix. The structural fix (attach `CatalogReader` directly to `SemanticViewsParserInfo`, lifetime tied to `DBConfig`) is **slated for Phase 62** alongside the caret-restoration work — the two share the same `parser_info` reshape.

### `CatalogReader` RAII

`prepared_lookup` and `execute_list_all` previously made paired `duckdb_prepare` / `duckdb_destroy_prepare` calls along every error path manually — easy to leak under early-return. Internal `PreparedStmt` and `QueryResult` guards now own these handles; manual destroy calls are gone (commit 1e7e92b foreshadowed this; the bulk of the RAII conversion lands here).

### Escape helper lift-out

The single SQL-string escape helper used in 3+ rewrite paths is moved out of `parse.rs` into a shared utility location. Pure refactor, no behaviour change.

### FFI buffer free correctness (c27a170)

The C++ side of the FFI now frees the heap-owned SQL buffer along **every** return path from `parser_override` (success, error-with-message, defer). Prior to c27a170 the defer path leaked.

### Test additions

- `test/integration/test_concurrent_ddl.py` (Justfile `test-concurrent`) — multiprocess CREATE under contention, asserting documented PK-violation shape for `IF NOT EXISTS` race.
- `test/integration/test_type_inference.py` — type inference `LIMIT 0` probe runs successfully inside `BEGIN/COMMIT`.
- `test/sql/v080_transactional_ddl.test` D-series cases — `INSERT OR REPLACE` row-count, byte-identical rollback (MD5), same-txn `list_semantic_views` visibility (documents the limitation).
- `fuzz/fuzz_targets/fuzz_parser_override_ffi.rs` — arbitrary-bytes target hitting `sv_parser_override_rust` with malformed UTF-8, oversized input, embedded NULs, and DDL-prefix near-misses.

### Documentation refresh

- `CHANGELOG.md` — final v0.8.x entry.
- `TECH-DEBT.md` — items 19, 20, 21, 22, 23 added with full context, links to commits, and resolution plan.
- `MAINTAINER.md` — `just test-concurrent`, `just test-adbc`, fuzz-target run instructions, parser_override architecture overview.
- `examples/race_guards_and_unification.py` — narrated end-to-end example showing race guards + transactional DDL.
- Sphinx docs (commit b5285a2) — refreshed reference pages for CREATE/ALTER/DROP/DESCRIBE/SHOW + new explanation page `docs/explanation/transactional-ddl-and-limitations.rst`.

## Tests

All test additions listed above are exercised by `just test-all` and `just ci` runs at phase close.
