Skip to content

Commit 806571a

Browse files
authored
feat: increase consistency between driver configurations (#224)
Refactor the parameter style configuration to be more consistent across all adapters.
1 parent c869b92 commit 806571a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+3637
-2539
lines changed

AGENTS.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,19 @@ SQLSpec is a type-safe SQL query mapper designed for minimal abstraction between
185185
- **Single-Pass Processing**: Parse once → transform once → validate once - SQL object is single source of truth
186186
- **Abstract Methods with Concrete Implementations**: Protocol defines abstract methods, base classes provide concrete sync/async implementations
187187

188+
### Driver Parameter Profile Registry
189+
190+
- All adapter parameter defaults live in `DriverParameterProfile` entries inside `sqlspec/core/parameters.py`.
191+
- Use lowercase adapter keys (e.g., `"asyncpg"`, `"duckdb"`) and populate every required field: default style, supported styles, execution style, native list expansion flags, JSON strategy, and optional extras.
192+
- JSON behaviour is controlled through `json_serializer_strategy`:
193+
- `"helper"`: call `ParameterStyleConfig.with_json_serializers()` (dict/list/tuple auto-encode)
194+
- `"driver"`: defer to driver codecs while surfacing serializer references for later registration
195+
- `"none"`: skip JSON helpers entirely (reserve for adapters that must not touch JSON)
196+
- Extras should encapsulate adapter-specific tweaks (e.g., `type_coercion_overrides`, `json_tuple_strategy`). Document new extras inline and keep them immutable.
197+
- Always build `StatementConfig` via `build_statement_config_from_profile()` and pass adapter-specific overrides through the helper instead of instantiating configs manually in drivers.
198+
- When introducing a new adapter, add its profile, update relevant guides, and extend unit coverage so each JSON strategy path is exercised.
199+
- Record the canonical adapter key, JSON strategy, and extras in the corresponding adapter guide so contributors can verify behaviour without reading the registry source.
200+
188201
### Protocol Abstract Methods Pattern
189202

190203
When adding methods that need to support both sync and async configurations, use this pattern:
@@ -335,6 +348,13 @@ def test_starlette_autocommit_mode() -> None:
335348
- Disabling pooling - Tests don't reflect production configuration
336349
- Running tests serially - Slows down CI significantly
337350

351+
### CLI Config Loader Isolation Pattern
352+
353+
- When exercising CLI migration commands, generate a unique module namespace for each test (for example `cli_test_config_<uuid>`).
354+
- Place temporary config modules inside `tmp_path` and register them via `sys.modules` within the test, then delete them during teardown to prevent bleed-through.
355+
- Always patch `Path.cwd()` or provide explicit path arguments so helper functions resolve the test-local module rather than cached global fixtures.
356+
- Add regression tests ensuring the helper cleaning logic runs even if CLI commands raise exceptions to avoid polluting later suites.
357+
338358
### Performance Optimizations
339359

340360
- **Mypyc Compilation**: Core modules can be compiled with mypyc for performance
@@ -2667,6 +2687,7 @@ def _extract_starlette_settings(self, config):
26672687
4. **Conditionally Skip DI Setup**:
26682688

26692689
**Middleware-based (Starlette/FastAPI)**:
2690+
26702691
```python
26712692
def init_app(self, app):
26722693
# ... lifespan setup ...
@@ -2677,6 +2698,7 @@ def init_app(self, app):
26772698
```
26782699

26792700
**Provider-based (Litestar)**:
2701+
26802702
```python
26812703
def on_app_init(self, app_config):
26822704
for state in self._plugin_configs:
@@ -2693,6 +2715,7 @@ def on_app_init(self, app_config):
26932715
```
26942716

26952717
**Hook-based (Flask)**:
2718+
26962719
```python
26972720
def init_app(self, app):
26982721
# ... pool setup ...

docs/guides/adapters/adbc.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ This guide provides specific instructions for the `adbc` adapter.
1111
- **Driver:** Arrow Database Connectivity (ADBC) drivers (e.g., `adbc_driver_postgresql`, `adbc_driver_sqlite`).
1212
- **Parameter Style:** Varies by underlying database (e.g., `numeric` for PostgreSQL, `qmark` for SQLite).
1313

14+
## Parameter Profile
15+
16+
- **Registry Key:** `"adbc"`
17+
- **JSON Strategy:** `helper` (shared serializers wrap dict/list/tuple values)
18+
- **Extras:** `type_coercion_overrides` ensure Arrow arrays map to Python lists; PostgreSQL dialects attach a NULL-handling AST transformer
19+
1420
## Best Practices
1521

1622
- **Arrow-Native:** The primary benefit of ADBC is its direct integration with Apache Arrow. Use it when you need to move large amounts of data efficiently between the database and data science tools like Pandas or Polars.

docs/guides/adapters/aiosqlite.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ This guide provides specific instructions for the `aiosqlite` adapter.
1111
- **Driver:** `aiosqlite`
1212
- **Parameter Style:** `qmark` (e.g., `?`)
1313

14+
## Parameter Profile
15+
16+
- **Registry Key:** `"aiosqlite"`
17+
- **JSON Strategy:** `helper` (shared serializer handles dict/list/tuple inputs)
18+
- **Extras:** None (profile applies bool→int and ISO datetime coercions automatically)
19+
1420
## Best Practices
1521

1622
- **Async Only:** This is an asynchronous driver for SQLite. Use it in `asyncio` applications.

docs/guides/adapters/asyncmy.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ This guide covers `asyncmy`.
1111
- **Driver:** `asyncmy`
1212
- **Parameter Style:** `pyformat` (e.g., `%s`)
1313

14+
## Parameter Profile
15+
16+
- **Registry Key:** `"asyncmy"`
17+
- **JSON Strategy:** `helper` (uses shared JSON serializers for dict/list/tuple)
18+
- **Extras:** None (native list expansion remains disabled)
19+
1420
## Best Practices
1521

1622
- **Character Set:** Always ensure the connection character set is `utf8mb4` to support a full range of Unicode characters, including emojis.

docs/guides/adapters/asyncpg.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ This guide provides specific instructions and best practices for working with th
1111
- **Driver:** `asyncpg`
1212
- **Parameter Style:** `numeric` (e.g., `$1, $2`)
1313

14+
## Parameter Profile
15+
16+
- **Registry Key:** `"asyncpg"`
17+
- **JSON Strategy:** `driver` (delegates JSON binding to asyncpg codecs)
18+
- **Extras:** None (codecs registered through config init hook)
19+
1420
## Best Practices
1521

1622
- **High-Performance:** `asyncpg` is often chosen for high-performance applications due to its speed. It's a good choice for applications with a high volume of database traffic.

docs/guides/adapters/bigquery.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ This guide provides specific instructions for the `bigquery` adapter.
1111
- **Driver:** `google-cloud-bigquery`
1212
- **Parameter Style:** `named` (e.g., `@name`)
1313

14+
## Parameter Profile
15+
16+
- **Registry Key:** `"bigquery"`
17+
- **JSON Strategy:** `helper` with `json_tuple_strategy="tuple"`
18+
- **Extras:** `type_coercion_overrides` keep list values intact while converting tuples to lists during binding
19+
1420
## Best Practices
1521

1622
- **Authentication:** BigQuery requires authentication with Google Cloud. For local development, the easiest way is to use the Google Cloud CLI and run `gcloud auth application-default login`.

docs/guides/adapters/duckdb.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ This guide provides specific instructions for the `duckdb` adapter.
1111
- **Driver:** `duckdb`
1212
- **Parameter Style:** `qmark` (e.g., `?`)
1313

14+
## Parameter Profile
15+
16+
- **Registry Key:** `"duckdb"`
17+
- **JSON Strategy:** `helper` (shared serializer covers dict/list/tuple)
18+
- **Extras:** None (profile preserves existing `allow_mixed_parameter_styles=False`)
19+
1420
## Best Practices
1521

1622
- **In-Memory vs. File:** DuckDB can run entirely in-memory (`:memory:`) or with a file-based database. In-memory is great for fast, temporary analytics. File-based is for persistence.

docs/guides/adapters/oracledb.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ This guide provides specific instructions and best practices for working with th
1111
- **Driver:** `oracledb`
1212
- **Parameter Style:** `named` (e.g., `:name`)
1313

14+
## Parameter Profile
15+
16+
- **Registry Key:** `"oracledb"`
17+
- **JSON Strategy:** `helper` (shared JSON serializer applied through the profile)
18+
- **Extras:** None (uses defaults with native list expansion disabled)
19+
1420
## Thick vs. Thin Client
1521

1622
The `oracledb` driver supports two modes:
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Driver Parameter Profile Registry
2+
3+
The table below summarizes the canonical `DriverParameterProfile` entries defined in `sqlspec/core/parameters/_registry.py`. Use it as a quick reference when updating adapters or adding new ones.
4+
5+
| Adapter | Registry Key | JSON Strategy | Extras | Default Dialect | Notes |
6+
| --- | --- | --- | --- | --- | --- |
7+
| ADBC | `"adbc"` | `helper` | `type_coercion_overrides` (list/tuple array coercion) | dynamic (per detected dialect) | Shares AST transformer metadata with BigQuery dialect helpers. |
8+
| AioSQLite | `"aiosqlite"` | `helper` | None | `sqlite` | Mirrors SQLite defaults; bools coerced to ints for driver parity. |
9+
| AsyncMy | `"asyncmy"` | `helper` | None | `mysql` | Native list expansion currently disabled until connector parity confirmed. |
10+
| AsyncPG | `"asyncpg"` | `driver` | None | `postgres` | Relies on asyncpg codecs; JSON serializers referenced for later registration. |
11+
| BigQuery | `"bigquery"` | `helper` | `json_tuple_strategy="tuple"`, `type_coercion_overrides` | `bigquery` | Enforces named parameters; tuple JSON payloads preserved as tuples. |
12+
| DuckDB | `"duckdb"` | `helper` | None | `duckdb` | Mixed-style binding disabled; aligns bool/datetime coercion with SQLite. |
13+
| OracleDB | `"oracledb"` | `helper` | None | `oracle` | List expansion disabled; LOB handling delegated to adapter converters. |
14+
| PSQLPy | `"psqlpy"` | `helper` | None | `postgres` | Decimal values currently downcast to float for driver compatibility. |
15+
| Psycopg | `"psycopg"` | `helper` | None | `postgres` | Array coercion delegated to psycopg adapters; JSON handled by shared converters. |
16+
| SQLite | `"sqlite"` | `helper` | None | `sqlite` | Shares bool/datetime handling with DuckDB and CLI defaults. |
17+
18+
## Adding or Updating Profiles
19+
20+
1. Define the profile in `_registry.py` using lowercase key naming.
21+
2. Pick the JSON strategy that matches driver capabilities (`helper`, `driver`, or `none`).
22+
3. Declare extras as an immutable mapping; document each addition in this file and the relevant adapter guide.
23+
4. Add or update regression coverage (see `specs/archive/driver-quality-review/research/testing_deliverables.md`).
24+
5. If behaviour changes, update changelog entries and adapter guides accordingly.
25+
26+
Refer to [AGENTS.md](../../AGENTS.md) for the full checklist when touching the registry.
27+
28+
## Example Usage
29+
30+
```python
31+
from sqlspec.core.parameters import get_driver_profile, build_statement_config_from_profile
32+
33+
profile = get_driver_profile("duckdb")
34+
config = build_statement_config_from_profile(profile)
35+
36+
print(config.parameter_config.default_parameter_style)
37+
```
38+
39+
The snippet above retrieves the DuckDB profile, builds a `StatementConfig`, and prints the default parameter style (`?`). Use the same pattern for new adapters after defining their profiles.

docs/guides/adapters/psqlpy.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ This guide provides specific instructions for the `psqlpy` adapter for PostgreSQ
1212
- **Parameter Style:** `numeric` (e.g., `$1, $2`)
1313
- **Type System:** Rust-level type conversion (not Python-level)
1414

15+
## Parameter Profile
16+
17+
- **Registry Key:** `"psqlpy"`
18+
- **JSON Strategy:** `helper` (shared JSON serializer applied before Rust-side codecs)
19+
- **Extras:** Decimal writes coerce through `_decimal_to_float` to match Rust numeric expectations
20+
1521
## Architecture
1622

1723
Psqlpy handles type conversion differently than other PostgreSQL drivers:

0 commit comments

Comments
 (0)