feat: selectable database driver (psycopg or asyncpg) - #62
Draft
Apakottur wants to merge 6 commits into
Draft
Conversation
Replace synchronous psycopg3 with asyncpg for the database introspection path (src/pgmig/_build/*). build_db_info keeps its sync signature via asyncio.run, so api.py, the CLI, and tests are untouched. The 10 loaders and _run_query become async; connect uses server_settings for the read-only transaction. Bad-DSN/connection failures are surfaced as PgmigError (catching OSError, PostgresError, InterfaceError). asyncpg uses the binary protocol, which decodes the pg internal "char" type as bytes. A non-identity attidentity is byte \x00, which pydantic coerced to the non-empty string '\x00' and broke the identity check. Casting the four returned "char" columns (attidentity, contype, prokind, relkind) to ::text restores the old text-protocol values. Test fixtures stay on psycopg (DDL setup, orthogonal to the read path). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Resolve conflicts from the _build->_introspect rename (#112) and the async-psycopg introspection refactor (#124): - Drop the obsolete _build/* edits; re-apply the asyncpg driver swap onto the current _introspect read path (_core, _engine, _context). - _connect: asyncpg.connect with session read-only + REPEATABLE READ, and a json/jsonb text codec so nested jsonb parses into models. - run_introspection_query: fetch via asyncpg and build models from records. - Cast attidentity::text to match the other char columns. - Regenerate uv.lock for asyncpg.
# Conflicts: # src/pgmig/_introspect/_context.py # src/pgmig/_introspect/_core.py # src/pgmig/_introspect/_engine.py
match driver in _db.py, thread driver arg through api/cli/engine, --driver pytest flag, both drivers in dev deps, cast char catalog cols to text. Both driver suites pass (316 each). Lint/type-check + packaging default TBD.
Add a driver argument to generate/agenerate (and --driver / PGMIG_DRIVER on the CLI) selecting psycopg (default) or asyncpg behind the _db abstraction. Package psycopg as the base dependency with binary/c/asyncpg install extras. Cast char catalog columns to text so both drivers agree. Run one asyncpg cell (py3.14/pg18) in CI. Document the driver/build tradeoffs in the README FAQ.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014h6yDcdivduaRr31BviN1C
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Make the database driver selectable rather than hard-swapping psycopg for asyncpg. Both drivers sit behind the
_dbabstraction; adriverargument picks one.drivertogenerate/agenerate("psycopg"default, or"asyncpg"), and--driver/PGMIG_DRIVERon the CLI. Threaded down tointrospect_dband the connection._db.pygrows an asyncpg implementation alongside psycopg, chosen withmatch driver:inconnect/execute/introspect.pgmig[binary](psycopg prebuilt wheel),pgmig[c](psycopg compiled against system libpq),pgmig[asyncpg]."char"catalog columns (attgenerated, matviewrelkind) totextso asyncpg and psycopg return identical values.--driverpytest flag; CI runs one asyncpg cell (py3.14 / pg18).Behavior
No change to emitted SQL. Both drivers produce identical migrations — the full suite (316 tests) passes under each. Default is
driver="psycopg", so existing users are unaffected; asyncpg is opt-in via the extra plus the argument.Notes
Benchmarked psycopg vs asyncpg on introspection: psycopg is ~1.2–1.3x faster (introspection is a handful of one-shot catalog queries, where asyncpg's per-statement prepare cost outweighs its throughput edge; psycopg's
binary/cbuilds widen the gap). Hence psycopg is the default.pgmig[binary]is offered but not the default — its bundled libpq/OpenSSL only update with the wheel, which psycopg discourages for production;pgmig[c]is the production-fast option.