feat(codegen/golang): Allow exporting models to a different package - #3874
feat(codegen/golang): Allow exporting models to a different package#3874berk-karaal wants to merge 3 commits into
Conversation
|
Any plans to merge this PR? It’s a really useful feature for large projects using sqlc. |
|
Would love to see some movement on this one, pretty handy feature. What's keeping this from being integrated? |
|
@kyleconroy hey! I’m working on a big project using SQLC, and it keeps growing. This PR would have a huge positive impact on my project as well as many others. From my perspective, this is the only weak spot in SQLC, and it could be resolved with this PR. I already tested this PR on our codebase, and it works like a charm. Please consider reviewing it when you have a chance, and please let me know if I can help in any way. Thank you! |
|
I need this feature. Can't use without. Please. |
|
patiently waiting as well :) |
|
@kyleconroy any plans to merge this? |
|
@kyleconroy it seems like a great feature. Would be grateful if this PR was merged! |
|
Fixed here #4421 |
internal/store held its SQL as string literals, its column order as a const per aggregate (authorColumns, sessionColumns, apiTokenColumns), and its scanning as a hand-written scan* function matching that const positionally. Nothing checked the three against each other or against the schema. At four columns that is fine. The schema ARCHITECTURE §2 already describes has releases at eighteen, where a positional Scan drifting from a select list fails silently — which is the failure those consts exist to make unlikely without being able to make it impossible. sqlc makes it impossible: queries are checked against the migrations at generation time, and CI fails on drift. Adopted now, at three tables rather than at eleven, because every phase from 2 onward adds schema and this is the cheapest the migration will ever be. internal/domain is now generated. sqlc emits row structs and query methods into one package, and the upstream option to split them was proposed and closed unmerged (sqlc-dev/sqlc#3874), so the package depends on pgx and ARCHITECTURE §1's "domain depends on nothing" rule is gone. The alternative was a parallel struct set and a converter per table — the duplication this change exists to remove — and paying it to keep a layering rule would have been paying twice. The generated Author, Session and APIToken came out field-for-field identical to the hand-written types they replace, so nothing above the store moved. What sqlc does not do stays in store: ErrNotFound and ErrUsernameTaken, the constraint-name unique-violation mapping, and the ownership scoping that makes the §5 authorization rules true. requireRow now takes a count from :execrows rather than a CommandTag, which has a pleasant side effect — the zero-rows policy is stated in the query file's annotation. DeleteAPIToken is :execrows because another author's id must read as ErrNotFound; DeleteSessionsForAuthor is :exec because an author with no live session has nothing to revoke. That distinction used to live only in a comment. Four things the config has to carry, all found by running sqlc rather than by reading its documentation. citext without an override generates as interface{} — silently, not as an error. emit_pointers_for_null_types does not cover timestamps: without an explicit override every timestamp column, nullable or not, is pgtype.Timestamptz, and that type spreads to every caller. Nullable enums are *CommentState as hoped. And sqlc's initialisms need rename:, keyed on the singular table name, because the plural silently does nothing. Each override is keyed on both spellings of its type, bare and canonical, since an override matching nothing fails that same silent way. internal/db/schema.sql and the just db-schema target that produced it are deleted. sqlc parses the goose migrations directly and produces byte-identical output, so the snapshot had no consumer — and it was the one drift CI could not catch: a migration merged without re-running db-schema would leave sqlc diff passing against a stale snapshot, with every select * expansion quietly omitting the new column. The migrations are now the only schema artifact, and just generate needs neither Docker nor Postgres. The store and HTTP suites pass with no test file touched, which is the whole claim a behaviour-preserving retrofit can make. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Fixes #835
This PR adds exporting models to a different package feature to Go code generator.
I also added a how-to page for this feature, you can check the
docs/howto/separate-models-file.mdfile.Main code changes made in this PR
output_models_package,models_package_import_pathandoutput_query_files_directoryoptions to gen/go configuration. (internal/codegen/golang/opts/options.go)Packagefield toStructtype to specify the correct type of the Struct since they can be in different package now. (internal/codegen/golang/struct.goandinternal/codegen/golang/result.go)Type()method ofQueryValuewill return{Package}.{Name}forStructtypes if thePackagefield of theStructis not empty. (internal/codegen/golang/query.go){Package}.prefix to enum types if configuration specifies separate models package. (changed onlyinternal/codegen/golang/postgresql_type.gosince only postgresql implementation supports typed enum values)internal/codegen/golang/imports.go)Note for sqlc users
You can simply try this feature using my sqlc-gen-go plugin fork. I will try to keep that plugin and this PR in sync. Surely sqlc-gen-go plugin configuration is not same as the sqlc gen configuration but the feature implementation code is the same as this PR.