Skip to content

feat: Psycopg 3 support via the psycopg_async driver#217

Open
rayakame wants to merge 11 commits into
mainfrom
feat/psycopg-async
Open

feat: Psycopg 3 support via the psycopg_async driver#217
rayakame wants to merge 11 commits into
mainfrom
feat/psycopg-async

Conversation

@rayakame

@rayakame rayakame commented Jul 22, 2026

Copy link
Copy Markdown
Owner

Summary

Adds Psycopg 3 support: sql_driver: "psycopg_async" generates asyncio code for psycopg with full asyncpg command parity - :one, :many, :exec, :execresult, :execrows, and :copyfrom (streamed through cursor.copy(), with the cursor's rowcount as the return). :execlastid stays unsupported, as on asyncpg.

conn = await psycopg.AsyncConnection.connect("postgresql://...")
user = await queries.get_author(conn, id_=1)

Design

Placeholder rewrite. psycopg rejects PostgreSQL-native $N and scans the whole query text for pyformat placeholders (verified empirically). Parameterized queries are rewritten once at generation time by a scanner aware of string literals (including '' doubling and E'...' escapes), quoted identifiers, dollar quoting, and nested block comments: $N becomes %(pN)s and every literal % doubles - psycopg requires that even inside SQL string literals. Parameters bind as a dict keyed by sqlc's parameter number, which makes $1 reuse and ordering non-issues. Parameterless SQL is untouched, since psycopg leaves it unscanned.

Type contract parity with asyncpg. The shared postgresql.go mapping is reused, so models are identical across both postgres drivers. bytea/inet/cidr convert inline exactly like asyncpg. Returned json/jsonb columns keep their str wire type by registering psycopg's TextLoader on the process-global adapters map per module (the sqlite register_converter precedent) - psycopg would otherwise parse them into dicts that cannot be passed back as parameters. Converters therefore behave identically on both postgres drivers.

pyright strict. psycopg's typed API takes LiteralString query text, so SQL constants emit as typing.Final[typing.LiteralString] for this driver and QueryResults annotates its sql parameter and attribute to match. The connection annotation is psycopg.AsyncConnection[psycopg.rows.TupleRow], so a connection configured with a non-tuple row factory is rejected statically.

Named-params QueryResults. A new class-header variant carries a params dict instead of *args. Overlong dicts hoist into a local sql_params, and that name joins conn/self/sql in the parameter dedup map so a column of the same name cannot be clobbered.

Semantics worth knowing

  • :execrows returns the cursor's rowcount, which is -1 for statements like CREATE TABLE (asyncpg's status parse yields 0).
  • :execresult returns the AsyncCursor; the status tag is cursor.statusmessage.
  • The default cursor buffers the full result set client-side on execute; QueryResults iteration decodes row by row but does not stream from the server (asyncpg remains the streaming option).
  • On Windows, psycopg's async support requires the SelectorEventLoop; the conftest installs the policy for local test runs, and the drivers docs note it.

Test coverage

  • Full fixture matrix test/driver_psycopg_async/ mirroring driver_asyncpg: all four model types x classes/functions, byte-checked by sqlc diff (psycopg_async_check), pyright strict, ruff, and ruff format stability.
  • ~440 runtime tests adapted from the asyncpg suite, all passing against live PostgreSQL (1896 total). The test connection runs autocommit=True to match asyncpg's per-statement semantics; fixed-ID constants are offset so the suites can share the database.
  • Go: 100% statement coverage on every touched package; CI-style Python coverage reports 100%.
  • New psycopg-async CI job wired into ci-done.

Docs

Drivers guide section, feature-support matrix column, configuration reference, README, and landing pages updated. Getting-started's synced driver tabs include psycopg_async, and the feature support reference documents per-driver prepared-statement behavior.

Summary by CodeRabbit

  • New Features
    • Added psycopg_async as an additional PostgreSQL driver (4 total), generating asyncio-based Python for Psycopg 3.
    • Implemented PostgreSQL placeholder rewriting with named parameter binding, async many iteration, COPY/:copyfrom streaming, and :execlastid support.
    • Added json/jsonb return handling and improved generated query typing compatibility.
  • Documentation
    • Updated driver, configuration, command support, and Getting Started guides to cover psycopg_async.
  • Tests
    • Added extensive async integration coverage and driver-generation verification for psycopg_async.
  • CI
    • Added dedicated psycopg-async verification checks and Nox sessions.

Add sql_driver "psycopg_async", generating asyncio code for Psycopg 3
with full asyncpg command parity: one, many, exec, execresult, execrows
and copyfrom, which streams rows through cursor.copy().

psycopg rejects PostgreSQL-native $N placeholders and scans the whole
query text for pyformat ones, so parameterized queries are rewritten at
generation time: a scanner aware of string literals, escape strings,
quoted identifiers, dollar quoting and nested block comments turns $N
into %(pN)s and doubles every literal percent, and parameters bind as a
dict keyed by sqlc's parameter number, which also handles reuse.
Parameterless SQL stays untouched, since psycopg leaves it unscanned.

The type contract matches asyncpg exactly. The shared postgresql mapping
is reused, bytea, inet and cidr convert inline, and returned json/jsonb
columns keep their str wire type by registering psycopg's TextLoader on
the process-global adapters map per module, mirroring how the sqlite
drivers register converters. Converters therefore behave identically on
both postgres drivers.

psycopg's typed API takes LiteralString query text, so the SQL constants
and the QueryResults sql parameter are annotated as such for this
driver. QueryResults carries the params dict through a named-parameters
variant of the class header. Overlong dicts hoist into a local named
sql_params, and that name joins conn/self/sql in the parameter dedup
map. execresult returns the cursor, execrows its rowcount, which is -1
for statements like CREATE TABLE.

The full fixture matrix mirrors driver_asyncpg across all four model
types with classes and functions variants, checked by pyright strict,
ruff, sqlc diff and a runtime pytest suite against PostgreSQL. The test
connection runs autocommit to match asyncpg's per-statement semantics,
and on Windows the conftest installs the selector event loop policy that
psycopg's async support requires.
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2be01417-13f2-482a-b975-2a9aa86a32bb

📥 Commits

Reviewing files that changed from the base of the PR and between 49cb36b and 03bde4f.

⛔ Files ignored due to path filters (4)
  • test/driver_aiosqlite/sqlc-gen-better-python.wasm is excluded by !**/*.wasm, !**/*.wasm
  • test/driver_asyncpg/sqlc-gen-better-python.wasm is excluded by !**/*.wasm, !**/*.wasm
  • test/driver_psycopg_async/sqlc-gen-better-python.wasm is excluded by !**/*.wasm, !**/*.wasm
  • test/driver_sqlite3/sqlc-gen-better-python.wasm is excluded by !**/*.wasm, !**/*.wasm
📒 Files selected for processing (24)
  • docs/content/docs/getting-started.md
  • docs/content/docs/guide/drivers.md
  • docs/content/docs/guide/enums.md
  • docs/content/docs/guide/writing-queries.md
  • docs/content/docs/reference/feature-support.md
  • internal/driver/psycopg.go
  • internal/driver/psycopg_test.go
  • internal/render/renderer.go
  • internal/transform/queries.go
  • internal/transform/queries_test.go
  • sqlc.yaml
  • test/driver_aiosqlite/sqlc.yaml
  • test/driver_asyncpg/sqlc.yaml
  • test/driver_psycopg_async/attrs/test_psycopg_attrs_classes.py
  • test/driver_psycopg_async/attrs/test_psycopg_attrs_functions.py
  • test/driver_psycopg_async/dataclass/test_psycopg_dataclass_classes.py
  • test/driver_psycopg_async/dataclass/test_psycopg_dataclass_functions.py
  • test/driver_psycopg_async/msgspec/test_psycopg_msgspec_classes.py
  • test/driver_psycopg_async/msgspec/test_psycopg_msgspec_functions.py
  • test/driver_psycopg_async/omit_tc/test_omit_typechecking_runtime.py
  • test/driver_psycopg_async/pydantic/test_psycopg_pydantic_classes.py
  • test/driver_psycopg_async/pydantic/test_psycopg_pydantic_functions.py
  • test/driver_psycopg_async/sqlc.yaml
  • test/driver_sqlite3/sqlc.yaml

📝 Walkthrough

Walkthrough

Adds psycopg_async support for Psycopg 3, including SQL rewriting, named parameter binding, COPY streaming, JSON handling, generated-code typing, integration tests, documentation, and CI validation.

Changes

Async Psycopg driver

Layer / File(s) Summary
Driver registration and code generation
internal/config/*, internal/driver/*, internal/model/*
Registers psycopg_async and generates Psycopg-specific execution, iteration, conversion, parameter binding, JSON loader, and COPY code.
SQL rewriting and parameter metadata
internal/transform/*
Rewrites PostgreSQL placeholders to named Psycopg placeholders, preserves SQL lexical regions, tracks parameter numbers, and handles bundled parameters.
Generated imports and QueryResults classes
internal/render/*, internal/writer/*
Adds Psycopg imports, LiteralString query annotations, named-parameter result classes, and matching docstrings.
Psycopg integration assets
test/driver_psycopg_async/*, test/conftest.py
Adds PostgreSQL schemas, queries, sqlc configurations, fixtures, and integration coverage for attrs, dataclasses, msgspec, Pydantic, converters, enums, COPY, and runtime imports.
Tooling and CI integration
noxfile.py, .github/workflows/ci.yml, pyproject.toml, scripts/build/*, sqlc.yaml, test/*/sqlc.yaml
Adds Psycopg dependencies and Nox sessions, propagates generated artifacts, refreshes checksums, and makes Psycopg verification required in CI.
Documentation and changelog
README.md, docs/content/*, .changes/unreleased/*
Documents the fourth driver, Psycopg configuration and command support, COPY behavior, JSON handling, and Psycopg 3 support.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested labels: docs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding Psycopg 3 support through the new psycopg_async driver.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Checkov (3.3.8)
sqlc.yaml

Traceback (most recent call last):
File "/usr/local/bin/checkov", line 2, in
from checkov.main import Checkov
ModuleNotFoundError: No module named 'checkov'

test/driver_aiosqlite/sqlc.yaml

Traceback (most recent call last):
File "/usr/local/bin/checkov", line 2, in
from checkov.main import Checkov
ModuleNotFoundError: No module named 'checkov'

test/driver_asyncpg/sqlc.yaml

Traceback (most recent call last):
File "/usr/local/bin/checkov", line 2, in
from checkov.main import Checkov
ModuleNotFoundError: No module named 'checkov'

  • 2 others

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 22, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://rayakame.github.io/sqlc-gen-better-python/pr-preview/pr-217/

Built to branch gh-pages at 2026-07-22 19:01 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

The SQL rewriter now follows PostgreSQL's lexer rules for identifiers
containing dollar signs (col$2 is a column name, not a parameter, and
a$x$ no longer opens a dollar quote), recognizes multi-byte dollar-quote
tags and identifiers, and ends line comments at a bare carriage return.

The hoisted sql_params local is only annotated with QueryResultsArgsType
in :many bodies, where the alias exists and dict invariance requires it;
other commands bind through psycopg's Mapping parameter and stay
unannotated, so a wide query in a module without :many no longer
references an undefined name. Plain sequence parameters copy through
list() before binding, since psycopg dumps tuples as composite records
rather than arrays while the Sequence annotation invites them. The
:execrows docstring now documents -1 for statements without a row count,
matching cur.rowcount. The cur and row body locals are reserved in the
parameter dedup map like sql_params, and every generated statement wraps
through WriteWrappedCall, including an overlong COPY statement, which
pre-explodes in the exact layout ruff format produces.

Shared cleanup: the identical asyncpg/psycopg __anext__ emission moved
into writeAsyncNextMethod, the dead async flag on psycopgBase and the
unused docstring wrapper pair are gone, scanStringLiteral delegates
quote-doubling to scanQuoted, and both postgres conftest teardowns share
one cleanup constant.

The omit_typechecking_block coverage promised by the psycopg sqlc.yaml
comment now exists: two omit_tc codegen blocks, committed fixtures, and
import plus runtime tests. Docs that still described three drivers or
called :copyfrom asyncpg-only are updated.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Line 314: Update the needs dependency list in the workflow to remove spaces
immediately inside the square brackets, matching the repository’s YAML
flow-sequence style while preserving the existing dependency entries and order.
- Around line 178-196: Add job-level permissions to the psycopg-async job
granting only contents: read, and configure its actions/checkout step with
persist-credentials: false. Update only the psycopg-async job and its checkout
configuration.

In `@test/driver_psycopg_async/__init__.py`:
- Line 20: Correct the package docstrings in
test/driver_psycopg_async/__init__.py:20-20,
test/driver_psycopg_async/attrs/__init__.py:20-20, and
test/driver_psycopg_async/msgspec/__init__.py:20-20 by changing the driver name
from asyncpg to psycopg; no other changes are needed.

In `@test/driver_psycopg_async/dataclass/__init__.py`:
- Line 20: Update the package docstring in
test/driver_psycopg_async/dataclass/__init__.py at line 20 to reference psycopg
dataclass tests instead of asyncpg tests, and make the same wording correction
in test/driver_psycopg_async/pydantic/__init__.py at line 20 for psycopg
pydantic tests.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: b5aa9079-2e9a-4091-8771-a2dfb426e021

📥 Commits

Reviewing files that changed from the base of the PR and between 8dda8f6 and e53d618.

⛔ Files ignored due to path filters (234)
  • test/driver_aiosqlite/attrs/classes/__init__.py is excluded by !test/driver_*/*/classes/**
  • test/driver_aiosqlite/attrs/classes/models.py is excluded by !test/driver_*/*/classes/**
  • test/driver_aiosqlite/attrs/classes/queries.py is excluded by !test/driver_*/*/classes/**
  • test/driver_aiosqlite/attrs/functions/__init__.py is excluded by !test/driver_*/*/functions/**
  • test/driver_aiosqlite/attrs/functions/models.py is excluded by !test/driver_*/*/functions/**
  • test/driver_aiosqlite/attrs/functions/queries.py is excluded by !test/driver_*/*/functions/**
  • test/driver_aiosqlite/dataclass/classes/__init__.py is excluded by !test/driver_*/*/classes/**
  • test/driver_aiosqlite/dataclass/classes/models.py is excluded by !test/driver_*/*/classes/**
  • test/driver_aiosqlite/dataclass/classes/queries.py is excluded by !test/driver_*/*/classes/**
  • test/driver_aiosqlite/dataclass/functions/__init__.py is excluded by !test/driver_*/*/functions/**
  • test/driver_aiosqlite/dataclass/functions/models.py is excluded by !test/driver_*/*/functions/**
  • test/driver_aiosqlite/dataclass/functions/queries.py is excluded by !test/driver_*/*/functions/**
  • test/driver_aiosqlite/dataclass/functions/queries_slice.py is excluded by !test/driver_*/*/functions/**
  • test/driver_aiosqlite/msgspec/classes/__init__.py is excluded by !test/driver_*/*/classes/**
  • test/driver_aiosqlite/msgspec/classes/models.py is excluded by !test/driver_*/*/classes/**
  • test/driver_aiosqlite/msgspec/classes/queries.py is excluded by !test/driver_*/*/classes/**
  • test/driver_aiosqlite/msgspec/functions/__init__.py is excluded by !test/driver_*/*/functions/**
  • test/driver_aiosqlite/msgspec/functions/models.py is excluded by !test/driver_*/*/functions/**
  • test/driver_aiosqlite/msgspec/functions/queries.py is excluded by !test/driver_*/*/functions/**
  • test/driver_aiosqlite/pydantic/classes/__init__.py is excluded by !test/driver_*/*/classes/**
  • test/driver_aiosqlite/pydantic/classes/models.py is excluded by !test/driver_*/*/classes/**
  • test/driver_aiosqlite/pydantic/classes/queries.py is excluded by !test/driver_*/*/classes/**
  • test/driver_aiosqlite/pydantic/functions/__init__.py is excluded by !test/driver_*/*/functions/**
  • test/driver_aiosqlite/pydantic/functions/models.py is excluded by !test/driver_*/*/functions/**
  • test/driver_aiosqlite/pydantic/functions/queries.py is excluded by !test/driver_*/*/functions/**
  • test/driver_aiosqlite/sqlc-gen-better-python.wasm is excluded by !**/*.wasm, !**/*.wasm
  • test/driver_asyncpg/attrs/classes/__init__.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/attrs/classes/enums.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/attrs/classes/models.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/attrs/classes/queries.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/attrs/classes/queries_copy_override.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/attrs/classes/queries_enum_override.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/attrs/classes/queries_field_namings.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/attrs/classes/queries_invalid_identifiers.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/attrs/functions/__init__.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/attrs/functions/enums.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/attrs/functions/models.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/attrs/functions/queries.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/attrs/functions/queries_copy_override.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/attrs/functions/queries_enum_override.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/attrs/functions/queries_field_namings.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/attrs/functions/queries_invalid_identifiers.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/dataclass/classes/__init__.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/dataclass/classes/enums.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/dataclass/classes/models.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/dataclass/classes/queries.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/dataclass/classes/queries_copy_override.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/dataclass/classes/queries_enum_override.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/dataclass/classes/queries_field_namings.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/dataclass/classes/queries_invalid_identifiers.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/dataclass/functions/__init__.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/dataclass/functions/enums.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/dataclass/functions/models.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/dataclass/functions/queries.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/dataclass/functions/queries_converters.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/dataclass/functions/queries_copy_override.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/dataclass/functions/queries_enum_override.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/dataclass/functions/queries_field_namings.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/dataclass/functions/queries_invalid_identifiers.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/msgspec/classes/__init__.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/msgspec/classes/enums.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/msgspec/classes/models.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/msgspec/classes/queries.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/msgspec/classes/queries_copy_override.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/msgspec/classes/queries_enum_override.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/msgspec/classes/queries_field_namings.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/msgspec/classes/queries_invalid_identifiers.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/msgspec/functions/__init__.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/msgspec/functions/enums.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/msgspec/functions/models.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/msgspec/functions/queries.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/msgspec/functions/queries_copy_override.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/msgspec/functions/queries_enum_override.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/msgspec/functions/queries_field_namings.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/msgspec/functions/queries_invalid_identifiers.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/omit_tc/classes/__init__.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/omit_tc/classes/enums.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/omit_tc/classes/models.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/omit_tc/classes/queries_enum_override.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/omit_tc/functions/__init__.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/omit_tc/functions/enums.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/omit_tc/functions/models.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/omit_tc/functions/queries_enum_override.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/pydantic/classes/__init__.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/pydantic/classes/enums.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/pydantic/classes/models.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/pydantic/classes/queries.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/pydantic/classes/queries_copy_override.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/pydantic/classes/queries_enum_override.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/pydantic/classes/queries_field_namings.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/pydantic/classes/queries_invalid_identifiers.py is excluded by !test/driver_*/*/classes/**
  • test/driver_asyncpg/pydantic/functions/__init__.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/pydantic/functions/enums.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/pydantic/functions/models.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/pydantic/functions/queries.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/pydantic/functions/queries_copy_override.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/pydantic/functions/queries_enum_override.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/pydantic/functions/queries_field_namings.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/pydantic/functions/queries_invalid_identifiers.py is excluded by !test/driver_*/*/functions/**
  • test/driver_asyncpg/sqlc-gen-better-python.wasm is excluded by !**/*.wasm, !**/*.wasm
  • test/driver_psycopg_async/attrs/classes/__init__.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/attrs/classes/enums.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/attrs/classes/models.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/attrs/classes/queries.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/attrs/classes/queries_copy_override.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/attrs/classes/queries_enum_override.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/attrs/classes/queries_field_namings.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/attrs/classes/queries_invalid_identifiers.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/attrs/functions/__init__.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/attrs/functions/enums.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/attrs/functions/models.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/attrs/functions/queries.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/attrs/functions/queries_copy_override.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/attrs/functions/queries_enum_override.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/attrs/functions/queries_field_namings.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/attrs/functions/queries_invalid_identifiers.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/dataclass/classes/__init__.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/dataclass/classes/enums.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/dataclass/classes/models.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/dataclass/classes/queries.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/dataclass/classes/queries_copy_override.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/dataclass/classes/queries_enum_override.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/dataclass/classes/queries_field_namings.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/dataclass/classes/queries_invalid_identifiers.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/dataclass/functions/__init__.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/dataclass/functions/enums.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/dataclass/functions/models.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/dataclass/functions/queries.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/dataclass/functions/queries_converters.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/dataclass/functions/queries_copy_override.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/dataclass/functions/queries_enum_override.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/dataclass/functions/queries_field_namings.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/dataclass/functions/queries_invalid_identifiers.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/msgspec/classes/__init__.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/msgspec/classes/enums.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/msgspec/classes/models.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/msgspec/classes/queries.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/msgspec/classes/queries_copy_override.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/msgspec/classes/queries_enum_override.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/msgspec/classes/queries_field_namings.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/msgspec/classes/queries_invalid_identifiers.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/msgspec/functions/__init__.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/msgspec/functions/enums.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/msgspec/functions/models.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/msgspec/functions/queries.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/msgspec/functions/queries_copy_override.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/msgspec/functions/queries_enum_override.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/msgspec/functions/queries_field_namings.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/msgspec/functions/queries_invalid_identifiers.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/omit_tc/classes/__init__.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/omit_tc/classes/enums.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/omit_tc/classes/models.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/omit_tc/classes/queries_enum_override.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/omit_tc/functions/__init__.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/omit_tc/functions/enums.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/omit_tc/functions/models.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/omit_tc/functions/queries_enum_override.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/pydantic/classes/__init__.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/pydantic/classes/enums.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/pydantic/classes/models.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/pydantic/classes/queries.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/pydantic/classes/queries_copy_override.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/pydantic/classes/queries_enum_override.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/pydantic/classes/queries_field_namings.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/pydantic/classes/queries_invalid_identifiers.py is excluded by !test/driver_*/*/classes/**
  • test/driver_psycopg_async/pydantic/functions/__init__.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/pydantic/functions/enums.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/pydantic/functions/models.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/pydantic/functions/queries.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/pydantic/functions/queries_copy_override.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/pydantic/functions/queries_enum_override.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/pydantic/functions/queries_field_namings.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/pydantic/functions/queries_invalid_identifiers.py is excluded by !test/driver_*/*/functions/**
  • test/driver_psycopg_async/sqlc-gen-better-python.wasm is excluded by !**/*.wasm, !**/*.wasm
  • test/driver_sqlite3/attrs/classes/__init__.py is excluded by !test/driver_*/*/classes/**
  • test/driver_sqlite3/attrs/classes/models.py is excluded by !test/driver_*/*/classes/**
  • test/driver_sqlite3/attrs/classes/queries.py is excluded by !test/driver_*/*/classes/**
  • test/driver_sqlite3/attrs/classes/queries_case.py is excluded by !test/driver_*/*/classes/**
  • test/driver_sqlite3/attrs/classes/queries_override_adapter.py is excluded by !test/driver_*/*/classes/**
  • test/driver_sqlite3/attrs/classes/queries_override_converter.py is excluded by !test/driver_*/*/classes/**
  • test/driver_sqlite3/attrs/classes/queries_unknown_override.py is excluded by !test/driver_*/*/classes/**
  • test/driver_sqlite3/attrs/functions/__init__.py is excluded by !test/driver_*/*/functions/**
  • test/driver_sqlite3/attrs/functions/models.py is excluded by !test/driver_*/*/functions/**
  • test/driver_sqlite3/attrs/functions/queries.py is excluded by !test/driver_*/*/functions/**
  • test/driver_sqlite3/attrs/functions/queries_case.py is excluded by !test/driver_*/*/functions/**
  • test/driver_sqlite3/attrs/functions/queries_override_adapter.py is excluded by !test/driver_*/*/functions/**
  • test/driver_sqlite3/attrs/functions/queries_override_converter.py is excluded by !test/driver_*/*/functions/**
  • test/driver_sqlite3/attrs/functions/queries_unknown_override.py is excluded by !test/driver_*/*/functions/**
  • test/driver_sqlite3/dataclass/classes/__init__.py is excluded by !test/driver_*/*/classes/**
  • test/driver_sqlite3/dataclass/classes/models.py is excluded by !test/driver_*/*/classes/**
  • test/driver_sqlite3/dataclass/classes/queries.py is excluded by !test/driver_*/*/classes/**
  • test/driver_sqlite3/dataclass/classes/queries_case.py is excluded by !test/driver_*/*/classes/**
  • test/driver_sqlite3/dataclass/classes/queries_override_adapter.py is excluded by !test/driver_*/*/classes/**
  • test/driver_sqlite3/dataclass/classes/queries_override_converter.py is excluded by !test/driver_*/*/classes/**
  • test/driver_sqlite3/dataclass/classes/queries_unknown_override.py is excluded by !test/driver_*/*/classes/**
  • test/driver_sqlite3/dataclass/functions/__init__.py is excluded by !test/driver_*/*/functions/**
  • test/driver_sqlite3/dataclass/functions/models.py is excluded by !test/driver_*/*/functions/**
  • test/driver_sqlite3/dataclass/functions/queries.py is excluded by !test/driver_*/*/functions/**
  • test/driver_sqlite3/dataclass/functions/queries_any_param.py is excluded by !test/driver_*/*/functions/**
  • test/driver_sqlite3/dataclass/functions/queries_case.py is excluded by !test/driver_*/*/functions/**
  • test/driver_sqlite3/dataclass/functions/queries_override_adapter.py is excluded by !test/driver_*/*/functions/**
  • test/driver_sqlite3/dataclass/functions/queries_override_converter.py is excluded by !test/driver_*/*/functions/**
  • test/driver_sqlite3/dataclass/functions/queries_slice.py is excluded by !test/driver_*/*/functions/**
  • test/driver_sqlite3/dataclass/functions/queries_unknown_override.py is excluded by !test/driver_*/*/functions/**
  • test/driver_sqlite3/msgspec/classes/__init__.py is excluded by !test/driver_*/*/classes/**
  • test/driver_sqlite3/msgspec/classes/models.py is excluded by !test/driver_*/*/classes/**
  • test/driver_sqlite3/msgspec/classes/queries.py is excluded by !test/driver_*/*/classes/**
  • test/driver_sqlite3/msgspec/classes/queries_case.py is excluded by !test/driver_*/*/classes/**
  • test/driver_sqlite3/msgspec/classes/queries_override_adapter.py is excluded by !test/driver_*/*/classes/**
  • test/driver_sqlite3/msgspec/classes/queries_override_converter.py is excluded by !test/driver_*/*/classes/**
  • test/driver_sqlite3/msgspec/classes/queries_unknown_override.py is excluded by !test/driver_*/*/classes/**
  • test/driver_sqlite3/msgspec/functions/__init__.py is excluded by !test/driver_*/*/functions/**
  • test/driver_sqlite3/msgspec/functions/models.py is excluded by !test/driver_*/*/functions/**
  • test/driver_sqlite3/msgspec/functions/queries.py is excluded by !test/driver_*/*/functions/**
  • test/driver_sqlite3/msgspec/functions/queries_case.py is excluded by !test/driver_*/*/functions/**
  • test/driver_sqlite3/msgspec/functions/queries_override_adapter.py is excluded by !test/driver_*/*/functions/**
  • test/driver_sqlite3/msgspec/functions/queries_override_converter.py is excluded by !test/driver_*/*/functions/**
  • test/driver_sqlite3/msgspec/functions/queries_unknown_override.py is excluded by !test/driver_*/*/functions/**
  • test/driver_sqlite3/pydantic/classes/__init__.py is excluded by !test/driver_*/*/classes/**
  • test/driver_sqlite3/pydantic/classes/models.py is excluded by !test/driver_*/*/classes/**
  • test/driver_sqlite3/pydantic/classes/queries.py is excluded by !test/driver_*/*/classes/**
  • test/driver_sqlite3/pydantic/classes/queries_case.py is excluded by !test/driver_*/*/classes/**
  • test/driver_sqlite3/pydantic/classes/queries_override_adapter.py is excluded by !test/driver_*/*/classes/**
  • test/driver_sqlite3/pydantic/classes/queries_override_converter.py is excluded by !test/driver_*/*/classes/**
  • test/driver_sqlite3/pydantic/classes/queries_unknown_override.py is excluded by !test/driver_*/*/classes/**
  • test/driver_sqlite3/pydantic/functions/__init__.py is excluded by !test/driver_*/*/functions/**
  • test/driver_sqlite3/pydantic/functions/models.py is excluded by !test/driver_*/*/functions/**
  • test/driver_sqlite3/pydantic/functions/queries.py is excluded by !test/driver_*/*/functions/**
  • test/driver_sqlite3/pydantic/functions/queries_case.py is excluded by !test/driver_*/*/functions/**
  • test/driver_sqlite3/pydantic/functions/queries_override_adapter.py is excluded by !test/driver_*/*/functions/**
  • test/driver_sqlite3/pydantic/functions/queries_override_converter.py is excluded by !test/driver_*/*/functions/**
  • test/driver_sqlite3/pydantic/functions/queries_unknown_override.py is excluded by !test/driver_*/*/functions/**
  • test/driver_sqlite3/sqlc-gen-better-python.wasm is excluded by !**/*.wasm, !**/*.wasm
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (70)
  • .changes/unreleased/Added-20260722-120000.yaml
  • .github/workflows/ci.yml
  • README.md
  • docs/content/_index.md
  • docs/content/docs/_index.md
  • docs/content/docs/guide/_index.md
  • docs/content/docs/guide/configuration.md
  • docs/content/docs/guide/drivers.md
  • docs/content/docs/guide/writing-queries.md
  • docs/content/docs/reference/configuration-options.md
  • docs/content/docs/reference/feature-support.md
  • internal/config/constants.go
  • internal/driver/asyncpg.go
  • internal/driver/common.go
  • internal/driver/common_test.go
  • internal/driver/driver.go
  • internal/driver/psycopg.go
  • internal/driver/psycopg_test.go
  • internal/driver/sqlite_base.go
  • internal/model/types.go
  • internal/render/imports.go
  • internal/render/imports_test.go
  • internal/render/queries.go
  • internal/render/render_queries_test.go
  • internal/transform/psycopg_sql.go
  • internal/transform/psycopg_sql_test.go
  • internal/transform/queries.go
  • internal/transform/queries_test.go
  • internal/writer/docstrings.go
  • internal/writer/docstrings_test.go
  • internal/writer/queryresults.go
  • internal/writer/queryresults_test.go
  • noxfile.py
  • pyproject.toml
  • scripts/build/build.bat
  • scripts/build/build.sh
  • sqlc.yaml
  • test/conftest.py
  • test/driver_aiosqlite/sqlc.yaml
  • test/driver_asyncpg/sqlc.yaml
  • test/driver_psycopg_async/__init__.py
  • test/driver_psycopg_async/attrs/__init__.py
  • test/driver_psycopg_async/attrs/ruff.toml
  • test/driver_psycopg_async/attrs/test_psycopg_attrs_classes.py
  • test/driver_psycopg_async/attrs/test_psycopg_attrs_functions.py
  • test/driver_psycopg_async/dataclass/__init__.py
  • test/driver_psycopg_async/dataclass/ruff.toml
  • test/driver_psycopg_async/dataclass/test_psycopg_dataclass_classes.py
  • test/driver_psycopg_async/dataclass/test_psycopg_dataclass_functions.py
  • test/driver_psycopg_async/msgspec/__init__.py
  • test/driver_psycopg_async/msgspec/ruff.toml
  • test/driver_psycopg_async/msgspec/test_psycopg_msgspec_classes.py
  • test/driver_psycopg_async/msgspec/test_psycopg_msgspec_functions.py
  • test/driver_psycopg_async/omit_tc/__init__.py
  • test/driver_psycopg_async/omit_tc/ruff.toml
  • test/driver_psycopg_async/omit_tc/test_omit_typechecking_import.py
  • test/driver_psycopg_async/omit_tc/test_omit_typechecking_runtime.py
  • test/driver_psycopg_async/pydantic/__init__.py
  • test/driver_psycopg_async/pydantic/ruff.toml
  • test/driver_psycopg_async/pydantic/test_psycopg_pydantic_classes.py
  • test/driver_psycopg_async/pydantic/test_psycopg_pydantic_functions.py
  • test/driver_psycopg_async/queries.sql
  • test/driver_psycopg_async/queries_converters.sql
  • test/driver_psycopg_async/queries_copy_override.sql
  • test/driver_psycopg_async/queries_enum_override.sql
  • test/driver_psycopg_async/queries_field_namings.sql
  • test/driver_psycopg_async/queries_invalid_identifiers.sql
  • test/driver_psycopg_async/schema.sql
  • test/driver_psycopg_async/sqlc.yaml
  • test/driver_sqlite3/sqlc.yaml

Comment thread .github/workflows/ci.yml
Comment thread .github/workflows/ci.yml
Comment thread test/driver_psycopg_async/__init__.py Outdated
Comment thread test/driver_psycopg_async/dataclass/__init__.py Outdated
rayakame added 5 commits July 22, 2026 14:26
Harden the psycopg-async CI job with read-only permissions and a
credential-free checkout like the go jobs, and correct the copied
psycopg test package docstrings that still said asyncpg.
Add the psycopg_async tab to all six synced driver tab groups in the
quickstart, note that the $N placeholders are rewritten to psycopg's
format at generation time, and give the drivers guide a sentence on
choosing between the two PostgreSQL drivers.
Readers coming from sqlc's Go workflow look for emit_prepared_queries; a new section under Query commands explains that every supported driver already prepares statements automatically and shows the per-driver knobs, including the PgBouncer caveat.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/content/docs/guide/drivers.md (1)

44-77: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Remove the stale asyncpg-only :copyfrom claim.

This new section documents psycopg_async support for :copyfrom, but the command-support text at Lines 121-122 still says :copyfrom is asyncpg-only. Update that sentence to say that PostgreSQL drivers support :copyfrom; otherwise the page gives contradictory guidance.

Proposed fix
- for example `:copyfrom` is asyncpg-only and `:execlastid` is SQLite-only.
+ for example `:copyfrom` is supported by PostgreSQL drivers and `:execlastid` is SQLite-only.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/content/docs/guide/drivers.md` around lines 44 - 77, Update the
command-support statement in the drivers documentation to say that PostgreSQL
drivers support :copyfrom, removing the asyncpg-only claim. Keep the existing
explanation of bulk insertion behavior intact and ensure it aligns with the
psycopg_async section.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/content/docs/getting-started.md`:
- Around line 470-471: Update the main example around AsyncConnection.connect to
manage the connection lifecycle with an async context manager or try/finally,
ensuring conn is closed on both normal and exceptional exits while preserving
the existing connection usage.

In `@docs/content/docs/guide/writing-queries.md`:
- Around line 162-164: Update the prepare_threshold explanation in the psycopg
documentation to state that server-side preparation occurs only after more than
the configured threshold executions, making the default 5 mean the sixth
execution. Preserve the documented special cases: 0 prepares on the first
execution and None disables preparation.

---

Outside diff comments:
In `@docs/content/docs/guide/drivers.md`:
- Around line 44-77: Update the command-support statement in the drivers
documentation to say that PostgreSQL drivers support :copyfrom, removing the
asyncpg-only claim. Keep the existing explanation of bulk insertion behavior
intact and ensure it aligns with the psycopg_async section.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 555c3db7-8c05-4389-bafb-24910e6fa33f

📥 Commits

Reviewing files that changed from the base of the PR and between e53d618 and 49cb36b.

📒 Files selected for processing (9)
  • .github/workflows/ci.yml
  • docs/content/docs/getting-started.md
  • docs/content/docs/guide/drivers.md
  • docs/content/docs/guide/writing-queries.md
  • test/driver_psycopg_async/__init__.py
  • test/driver_psycopg_async/attrs/__init__.py
  • test/driver_psycopg_async/dataclass/__init__.py
  • test/driver_psycopg_async/msgspec/__init__.py
  • test/driver_psycopg_async/pydantic/__init__.py

Comment thread docs/content/docs/getting-started.md Outdated
Comment thread docs/content/docs/guide/writing-queries.md Outdated
rayakame added 3 commits July 22, 2026 20:06
The section now lives under Query commands in the feature support reference, replacing the stale not-planned bullet; the writing queries guide keeps a short note linking to it.
Manage the quickstart psycopg connection with a context manager, state precisely that prepare_threshold prepares after more than N executions (the sixth with the default of 5), and correct the copyfrom claim in the drivers guide to PostgreSQL-only.
Two psycopg emission shapes were not ruff format fixed points: an
exploded :copyfrom row tuple now uses ruff's nested layout (one-line
one-element tuples while they fit, exploded past that), and :one gets a
dedicated emitter whose overlong tier is the nested-await layout, since
its double-parenthesis tail is the one shape WriteWrappedCall cannot
explode stably. With :one split out, writePsycopgCall's tail parameter
became constant and is gone. Both layouts are verified against ruff
format, and no committed fixture changes.

The parameter dedup map additionally reserves _decode_hook for psycopg
:many bodies, and the unsupported-command error names the sql_driver
config value instead of driver.Name(), which differ for psycopg.

Docs: the feature-support copyfrom callout no longer explains the
command through asyncpg's API alone, the enums guide credits both
PostgreSQL drivers, the drivers guide states the psycopg 3.2 floor, and
the psycopg test suites drop a comment claiming cursor iteration
requires a transaction - that constraint is asyncpg's.
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