Repository navigation
feat: sqlc v1.31.1 feature parity - #1
Merged
Merged
Conversation
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Example deps move to SQLAlchemy 2, psycopg 3, pydantic 2 and mypy for the upcoming fixture checks. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Examples now build against the local plugin (file://../bin) so that sqlc diff checks this tree rather than the released 1.2.0 WASM. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…params Needed for executemany param lists, error wrapping, X | None annotations, Union[...] slices, protocol stubs and PEP 695 queriers. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
String constants escape backslashes and quotes, so SQL text is now emitted as runtime text and the printer doubles its backslashes. Class and function docstrings share one triple-quoted helper (multi-line docstrings used to print five quotes), empty class bodies print pass, multi-line field comments stay inside comments, and If prints else. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Field, parameter, method and class names go through one pyIdent helper (rename, then a trailing underscore for hard keywords). Enum members that sanitize to an empty, digit-leading or duplicate name get VALUE_<n>, a VALUE_ prefix, or a _<k> suffix. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…uilder Each generated file now records the imports its nodes use (typing and collections.abc names, dotted type modules, py_import names), replacing the importer's parallel prediction. Sync and async querier methods come from one builder with an async flag. pyType carries ArrayDims for nested List wrappers, and the PostgreSQL map gains the varchar/bpchar/name/time/ timestamp spellings sqlc's Go codegen knows. Existing fixtures are unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ersion overrides map a db_type or column (with * globs) to a py_type, given as a dotted path or as py_type plus py_import. Overrides and rename are also read from sqlc's global options for the plugin; codegen entries are checked first, while global rename values win. Invalid entries fail generation and name the entry. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Querier and AsyncQuerier take Connection | Session and AsyncConnection | AsyncSession and declare a typed _conn. Row values are wrapped in typing.cast so mypy --strict accepts the generated code. rowcount reads and the :execresult return use string annotations, so no subscripted SQLAlchemy type is evaluated at runtime. Annotation-only: behaviour is unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
emit_generic_querier makes Querier/AsyncQuerier PEP 695 generics bounded by the connection-or-session union, so Querier(session)._conn is typed Session. Query comments become method docstrings. sqlc.embed() fields are built as nested models from their run of row columns. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
All four take one positional Sequence of params objects. :copyfrom and :batchexec run a single executemany (returning rowcount and None); :batchone and :batchmany are generators that run the statement per item and yield lazily. Generation used to fail on :copyfrom and panic on the batch commands. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Behind emit_querier_protocol. Protocols come from the same method list as the queriers; async generator methods are declared with plain def so the implementation matches structurally. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Generates errors.py with alt-sqlc-gen-python's QueryError hierarchy plus constraint_name, and wraps each querier method body in errors._wrap_errors(), which re-raises IntegrityError/OperationalError as the class for the SQLSTATE. The with block encloses generator loops, so errors while iterating are wrapped too. A query file mapping to errors.py fails generation. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Two kitchen-sink fixtures combine every new feature with the classic and the modern option sets. The db job moves to Python 3.12 (PEP 695 fixtures), compiles every fixture, and type-checks both matrices. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A library example is generated with the classic and the modern option sets. pytest covers lazy batch results, single executemany calls, typed errors (also while iterating) through psycopg and asyncpg, Session and generic queriers, a protocol fake, and aware datetimes. The harness moves to SQLAlchemy 2, psycopg 3 and pytest-asyncio 1.x. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
README gains sections for every new option and command plus a note for alt-sqlc-gen-python users; design.md records where the implementation differs from the original plan. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
kvdomingo
marked this pull request as ready for review
October 4, 2026 07:19
go.mod moves from go 1.19 to 1.27 and mise pins 1.27.1; CI reads the version from go.mod so the two cannot drift. Fixture sha256s follow the rebuilt WASM. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
kvdomingo
force-pushed
the
feat/sqlc-feature-parity
branch
from
October 4, 2026 07:46
90e3ba9 to
14313ce
Compare
The README described the original fork's always-on modern output, try/except error wrapping and a keyword-only :batchexec. Document the opt-in options as implemented, the batch commands, the published alt-sqlc-gen-python.wasm asset and the PR-title-driven release workflow. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The fork drops upstream's tags. With none present, the first releasable merge is tagged v1.0.0 instead of counting up from v0.0.0. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
kvdomingo
force-pushed
the
feat/sqlc-feature-parity
branch
from
October 4, 2026 07:56
f6977d1 to
7dba9d6
Compare
…edence and embed nulls Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ry py_type module, accept db_type aliases A keyword parameter named errors, models, cast or an imported module shadowed the module-level name the method body reads. Repeated parameter names produced a SyntaxError. A generic py_type such as dict[str, decimal.Decimal] was cut at its last dot into an invalid import. db_type: bigint never matched because sqlc reports pg_catalog.int8. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Params named after a builtin or py_type name used inside cast() (list, int, str, dict, ...) shadowed it in the method body; escape them like errors/cast. Dedup suffixes for params and struct fields now skip names already taken, so id, id, id_2 no longer yields two id_2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…iles Method names are now escaped against the same shadowed-name set as params, so a query named List/Int/Models no longer hides those types in later annotations of the class body. A query file named models.sql now errors instead of silently overwriting models.py. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…meter Two queries that map to the same Python method (e.g. List and List_) now fail generation instead of emitting duplicate defs and constants. The generic querier type parameter is now _ConnT, so it no longer shadows the SQL constant of a query named T. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ant and NFKC collisions Each query file is its own module, so same-named methods across files are valid again. Also reject duplicate constants and methods that collide after Python's NFKC identifier normalisation. Update docs to the _ConnT type parameter name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kvdomingo
added a commit
that referenced
this pull request
Oct 4, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kvdomingo
force-pushed
the
feat/sqlc-feature-parity
branch
from
October 4, 2026 09:01
c4f8768 to
4ca45c8
Compare
kvdomingo
added a commit
that referenced
this pull request
Oct 4, 2026
* chore: mise & openspec tooling setup * feat: sqlc v1.31.1 feature parity (#1) * docs(openspec): propose sqlc-feature-parity change Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * chore(tooling): move CI to sqlc 1.31.1 and examples to SQLAlchemy 2 Example deps move to SQLAlchemy 2, psycopg 3, pydantic 2 and mypy for the upcoming fixture checks. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * chore(testdata): regenerate fixtures and examples with sqlc 1.31.1 Examples now build against the local plugin (file://../bin) so that sqlc diff checks this tree rather than the released 1.2.0 WASM. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat(ast): add ListComp, With, BinOp, Tuple, ellipsis and class type params Needed for executemany param lists, error wrapping, X | None annotations, Union[...] slices, protocol stubs and PEP 695 queriers. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(printer): escape string literals, print docstrings, pass and else String constants escape backslashes and quotes, so SQL text is now emitted as runtime text and the printer doubles its backslashes. Class and function docstrings share one triple-quoted helper (multi-line docstrings used to print five quotes), empty class bodies print pass, multi-line field comments stay inside comments, and If prints else. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(codegen): escape Python keywords and sanitize enum member names Field, parameter, method and class names go through one pyIdent helper (rename, then a trailing underscore for hard keywords). Enum members that sanitize to an empty, digit-leading or duplicate name get VALUE_<n>, a VALUE_ prefix, or a _<k> suffix. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * refactor(codegen): record imports while building, share one querier builder Each generated file now records the imports its nodes use (typing and collections.abc names, dotted type modules, py_import names), replacing the importer's parallel prediction. Sync and async querier methods come from one builder with an async flag. pyType carries ArrayDims for nested List wrappers, and the PostgreSQL map gains the varchar/bpchar/name/time/ timestamp spellings sqlc's Go codegen knows. Existing fixtures are unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(testdata): cover emit_modern_types and emit_aware_datetime Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat(config): add overrides, rename, omit_unused_structs, omit_sqlc_version overrides map a db_type or column (with * globs) to a py_type, given as a dotted path or as py_type plus py_import. Overrides and rename are also read from sqlc's global options for the plugin; codegen entries are checked first, while global rename values win. Invalid entries fail generation and name the entry. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat(querier): accept sessions and cast row values Querier and AsyncQuerier take Connection | Session and AsyncConnection | AsyncSession and declare a typed _conn. Row values are wrapped in typing.cast so mypy --strict accepts the generated code. rowcount reads and the :execresult return use string annotations, so no subscripted SQLAlchemy type is evaluated at runtime. Annotation-only: behaviour is unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat(querier): generic queriers, query docstrings and sqlc.embed() emit_generic_querier makes Querier/AsyncQuerier PEP 695 generics bounded by the connection-or-session union, so Querier(session)._conn is typed Session. Query comments become method docstrings. sqlc.embed() fields are built as nested models from their run of row columns. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat(querier): support :copyfrom, :batchexec, :batchone and :batchmany All four take one positional Sequence of params objects. :copyfrom and :batchexec run a single executemany (returning rowcount and None); :batchone and :batchmany are generators that run the statement per item and yield lazily. Generation used to fail on :copyfrom and panic on the batch commands. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat(querier): emit QuerierProtocol and AsyncQuerierProtocol Behind emit_querier_protocol. Protocols come from the same method list as the queriers; async generator methods are declared with plain def so the implementation matches structurally. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat(querier): typed query errors behind emit_query_errors Generates errors.py with alt-sqlc-gen-python's QueryError hierarchy plus constraint_name, and wraps each querier method body in errors._wrap_errors(), which re-raises IntegrityError/OperationalError as the class for the SQLSTATE. The with block encloses generator loops, so errors while iterating are wrapped too. A query file mapping to errors.py fails generation. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(ci): feature matrix fixtures, compile and mypy --strict in CI Two kitchen-sink fixtures combine every new feature with the classic and the modern option sets. The db job moves to Python 3.12 (PEP 695 fixtures), compiles every fixture, and type-checks both matrices. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(examples): exercise new features against Postgres A library example is generated with the classic and the modern option sets. pytest covers lazy batch results, single executemany calls, typed errors (also while iterating) through psycopg and asyncpg, Session and generic queriers, a protocol fake, and aware datetimes. The harness moves to SQLAlchemy 2, psycopg 3 and pytest-asyncio 1.x. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * docs: document new options, commands and fork migration README gains sections for every new option and command plus a note for alt-sqlc-gen-python users; design.md records where the implementation differs from the original plan. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * ci: update postgres * ci: install sqlalchemy asyncio extras * chore(go): bump to Go 1.27 go.mod moves from go 1.19 to 1.27 and mise pins 1.27.1; CI reads the version from go.mod so the two cannot drift. Fixture sha256s follow the rebuilt WASM. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * ci: add zizmor, pre-commit hooks * ci: autogenerate release version * docs(readme): match the implementation and the automated release The README described the original fork's always-on modern output, try/except error wrapping and a keyword-only :batchexec. Document the opt-in options as implemented, the batch commands, the published alt-sqlc-gen-python.wasm asset and the PR-title-driven release workflow. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * ci(release): start versioning at v1.0.0 The fork drops upstream's tags. With none present, the first releasable merge is tagged v1.0.0 instead of counting up from v0.0.0. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * docs: update readme * docs(readme): document override aliases, generic py_type, rename precedence and embed nulls Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(codegen): escape shadowing params, dedupe param names, import every py_type module, accept db_type aliases A keyword parameter named errors, models, cast or an imported module shadowed the module-level name the method body reads. Repeated parameter names produced a SyntaxError. A generic py_type such as dict[str, decimal.Decimal] was cut at its last dot into an invalid import. db_type: bigint never matched because sqlc reports pg_catalog.int8. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(codegen): escape builtin-named params, keep deduped names unique Params named after a builtin or py_type name used inside cast() (list, int, str, dict, ...) shadowed it in the method body; escape them like errors/cast. Dedup suffixes for params and struct fields now skip names already taken, so id, id, id_2 no longer yields two id_2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(codegen): escape builtin-named methods, reject models.sql query files Method names are now escaped against the same shadowed-name set as params, so a query named List/Int/Models no longer hides those types in later annotations of the class body. A query file named models.sql now errors instead of silently overwriting models.py. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(codegen): reject duplicate method names, rename generic type parameter Two queries that map to the same Python method (e.g. List and List_) now fail generation instead of emitting duplicate defs and constants. The generic querier type parameter is now _ConnT, so it no longer shadows the SQL constant of a query named T. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(codegen): scope duplicate-name checks per query file, catch constant and NFKC collisions Each query file is its own module, so same-named methods across files are valid again. Also reject duplicate constants and methods that collide after Python's NFKC identifier normalisation. Update docs to the _ConnT type parameter name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.
Brings the plugin up to parity with sqlc v1.31.1 (OpenSpec change
sqlc-feature-parity). PostgreSQL only; every new behaviour is opt-in except the correctness fixes and annotation-only typing changes.New options
overrides(db_type/columnwith*globs, dotted or genericpy_typeorpy_type+py_import,nullable),rename, both also read from sqlc's top-leveloptionsomit_unused_structs,omit_sqlc_versionemit_modern_types(X | None,list[X],collections.abc)emit_aware_datetime(pydantic.AwareDatetimefortimestamptz; requiresemit_pydantic_models)emit_generic_querier(PEP 695class Querier[_ConnT: Connection | Session])emit_querier_protocol(QuerierProtocol/AsyncQuerierProtocol)emit_query_errors(generatederrors.py, same classes as alt-sqlc-gen-python plusconstraint_name)Query features
:copyfrom,:batchexec(singleexecutemany),:batchone,:batchmany(lazy generators)sqlc.embed(), query comments as method docstrings, multi-dimensional arraysSession/AsyncSession; row values wrapped intyping.castso output passesmypy --strictFixes
from→from_),passfor empty classes, valid unique enum member names, escaped string literals, multi-line comments, missing PostgreSQL type aliases (varchar,timestamp, …)errors,models,cast, imported modules) get a trailing_; repeated parameter names get a numeric suffix (id,id_2)db_typeoverrides accept SQL spellings (bigint,timestamp with time zone)py_typenames used in the body (list,int,dict, …) are escaped too; dedup suffixes skip names already taken (id,id_2,id_2_2)list,int,models, …) get a trailing_models.py/errors.pyNotable decisions
renamereplaces a codegen one (as in sqlc's Go codegen). Both documented in the README.renamealso applies to enum values (one flat map); documented.examples/builds against the local WASM sosqlc diffin CI tests this tree.rowcountand:execresultuse string annotations so SQLAlchemy 1.4 still imports the code.Also in this PR
go.mod,mise.toml; CI readsgo.mod)main, the PR title picks the bump (feat→ minor,fix/hotfix→ patch,!/BREAKING CHANGE→ major); first release isv1.0.0. Publishesalt-sqlc-gen-python.wasm+.sha256.Testing
make test,go vet ./...,sqlc diffinexamples/mypy --strictin CI (Python 3.14)examples/pytest against Postgres 18 (psycopg 3 + asyncpg)🤖 Generated with Claude Code