Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
592c1d6
feat: add overrides config option
oliverlambson Mar 11, 2025
19e8d56
feat: added invalid test for invalid name generation when using pytho…
QuentinN42 Jul 25, 2025
f928244
fix: added reserved python keywords
QuentinN42 Jul 25, 2025
24fcd39
refactor: use poet.Node
QuentinN42 Jul 25, 2025
a5d1ee8
test: added more tests to the poet pkg and generated the pydantic.Con…
QuentinN42 Jul 25, 2025
068fd25
feat: added dataclasses tests
QuentinN42 Jul 25, 2025
6cdbccb
Merge pull request #2 from oliverlambson/feat/overrides-config
MerlinVeritas Feb 19, 2026
e1e3865
Merge branch 'main' into Escape-Technologies-main
MerlinVeritas Feb 19, 2026
440baa6
Merge pull request #4 from enveritas/Escape-Technologies-main
MerlinVeritas Feb 19, 2026
62ab2e7
fix: escape double quotes if they appear in constant str
MerlinVeritas Nov 29, 2025
692ef0c
more fixes to docstring and comments in general
MerlinVeritas Nov 29, 2025
1967bfa
add more python keywords
MerlinVeritas Nov 29, 2025
5bd1108
Merge pull request #5 from enveritas/fix/escape-constants
MerlinVeritas Feb 19, 2026
eeed5d4
strings.Replace->strings.ReplaceAll
MerlinVeritas Nov 30, 2025
95289ea
params and return type should be CamelCased
MerlinVeritas Dec 1, 2025
1b98158
Merge pull request #6 from enveritas/fix/struct-format
MerlinVeritas Feb 19, 2026
266fab8
fix generating enum for db enums
MerlinVeritas Dec 5, 2025
0806323
add config option to control if have schema prefix for types or not
MerlinVeritas Dec 5, 2025
38c7e0d
Merge pull request #7 from enveritas/fix/support-enum
MerlinVeritas Feb 20, 2026
0a9dcb7
Support type overrides with test coveragfixtures
MerlinVeritas Feb 24, 2026
86a9e4e
fail for unknown yaml fields
MerlinVeritas Feb 24, 2026
619f15b
fix ci
MerlinVeritas Feb 24, 2026
dba6e76
Merge pull request #8 from enveritas/feature/type-overrides
MerlinVeritas Feb 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
add config option to control if have schema prefix for types or not
  • Loading branch information
MerlinVeritas committed Feb 19, 2026
commit 0806323c92e6873d47c9e323cf4f2fd77bec4843
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,29 @@ sql:
emit_async_querier: true
```

### Configuration Options

These are the supported `options` for the `py` plugin. Add them under the `codegen[].options` section of your `sqlc.yaml`.

- package: Module path used for imports in generated query files (e.g., `from <package> import models`).
- emit_sync_querier: Emit a synchronous `Querier` class using `sqlalchemy.engine.Connection`.
- emit_async_querier: Emit an asynchronous `AsyncQuerier` class using `sqlalchemy.ext.asyncio.AsyncConnection`.
- emit_pydantic_models: Emit Pydantic models instead of `dataclasses` for models.py. See the section below.
- emit_str_enum: Emit enums as `enum.StrEnum` (Python >=3.11). When false, emit `(str, enum.Enum)`. See the section below.
- emit_schema_name_prefix: When true, prefix non-default schema to generated types to avoid name collisions. Examples:
- false (default): `Book`, `BookStatus`
- true: `MySchemaBook`, `MySchemaBookStatus` when the objects live in schema `my_schema`.
- emit_exact_table_names: When true, do not singularize table names for model class names.
- query_parameter_limit: Integer controlling when query params are grouped into a single struct argument.
- If the number of parameters exceeds this value, a single `Params` struct is emitted.
- Set to 0 to always emit a struct; omit or set to a large value to keep separate parameters.
- inflection_exclude_table_names: A list of table names to exclude from singularization when `emit_exact_table_names` is false.
- overrides: Column type overrides; see the section below.

Notes
- out: Controlled by `codegen[].out` at the sqlc level. The plugin’s `out` option is not used; prefer the top-level `out` value.


### Emit Pydantic Models instead of `dataclasses`

Option: `emit_pydantic_models`
Expand Down
26 changes: 17 additions & 9 deletions internal/config.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
package python

type OverrideColumn struct {
Column string `json:"column"`
PyType string `json:"py_type"`
PyImport string `json:"py_import"`
}

type Config struct {
EmitExactTableNames bool `json:"emit_exact_table_names"`
EmitSyncQuerier bool `json:"emit_sync_querier"`
EmitAsyncQuerier bool `json:"emit_async_querier"`
Package string `json:"package"`
Out string `json:"out"`
EmitPydanticModels bool `json:"emit_pydantic_models"`
EmitStrEnum bool `json:"emit_str_enum"`
QueryParameterLimit *int32 `json:"query_parameter_limit"`
InflectionExcludeTableNames []string `json:"inflection_exclude_table_names"`
EmitExactTableNames bool `json:"emit_exact_table_names"`
EmitSyncQuerier bool `json:"emit_sync_querier"`
EmitAsyncQuerier bool `json:"emit_async_querier"`
Package string `json:"package"`
Out string `json:"out"`
EmitPydanticModels bool `json:"emit_pydantic_models"`
EmitStrEnum bool `json:"emit_str_enum"`
EmitSchemaNamePrefix bool `json:"emit_schema_name_prefix"`
QueryParameterLimit *int32 `json:"query_parameter_limit"`
InflectionExcludeTableNames []string `json:"inflection_exclude_table_names"`
Overrides []OverrideColumn `json:"overrides"`
}
56 changes: 45 additions & 11 deletions internal/gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,18 +181,52 @@ func (q Query) ArgDictNode() *pyast.Node {
}

func makePyType(req *plugin.GenerateRequest, col *plugin.Column) pyType {
typ := pyInnerType(req, col)
// Parse the configuration
var conf Config
if len(req.PluginOptions) > 0 {
if err := json.Unmarshal(req.PluginOptions, &conf); err != nil {
log.Printf("failed to parse plugin options: %s", err)
}
}

// Check for overrides
if len(conf.Overrides) > 0 && col.Table != nil {
tableName := col.Table.Name
if col.Table.Schema != "" && col.Table.Schema != req.Catalog.DefaultSchema {
tableName = col.Table.Schema + "." + tableName
}

// Look for a matching override
for _, override := range conf.Overrides {
overrideKey := tableName + "." + col.Name
if override.Column == overrideKey {
// Found a match, use the override
typeStr := override.PyType
if override.PyImport != "" && !strings.Contains(typeStr, ".") {
typeStr = override.PyImport + "." + override.PyType
}
return pyType{
InnerType: typeStr,
IsArray: col.IsArray,
IsNull: !col.NotNull,
}
}
}
}

// No override found, use the standard type mapping
typ := pyInnerType(conf, req, col)
return pyType{
InnerType: typ,
IsArray: col.IsArray,
IsNull: !col.NotNull,
}
}

func pyInnerType(req *plugin.GenerateRequest, col *plugin.Column) string {
func pyInnerType(conf Config, req *plugin.GenerateRequest, col *plugin.Column) string {
switch req.Settings.Engine {
case "postgresql":
return postgresType(req, col)
return postgresType(conf, req, col)
default:
log.Println("unsupported engine type")
return "Any"
Expand Down Expand Up @@ -226,18 +260,18 @@ func pyEnumValueName(value string) string {
return strings.ToUpper(id)
}

func buildEnums(req *plugin.GenerateRequest) []Enum {
func buildEnums(conf Config, req *plugin.GenerateRequest) []Enum {
var enums []Enum
for _, schema := range req.Catalog.Schemas {
if schema.Name == "pg_catalog" || schema.Name == "information_schema" {
continue
}
for _, enum := range schema.Enums {
var enumName string
if schema.Name == req.Catalog.DefaultSchema {
enumName = enum.Name
} else {
if conf.EmitSchemaNamePrefix && schema.Name != req.Catalog.DefaultSchema {
enumName = schema.Name + "_" + enum.Name
} else {
enumName = enum.Name
}
e := Enum{
Name: modelName(enumName, req.Settings),
Expand Down Expand Up @@ -267,10 +301,10 @@ func buildModels(conf Config, req *plugin.GenerateRequest) []Struct {
}
for _, table := range schema.Tables {
var tableName string
if schema.Name == req.Catalog.DefaultSchema {
tableName = table.Rel.Name
} else {
if conf.EmitSchemaNamePrefix && schema.Name != req.Catalog.DefaultSchema {
tableName = schema.Name + "_" + table.Rel.Name
} else {
tableName = table.Rel.Name
}
structName := tableName
if !conf.EmitExactTableNames {
Expand Down Expand Up @@ -1185,7 +1219,7 @@ func Generate(_ context.Context, req *plugin.GenerateRequest) (*plugin.GenerateR
}
}

enums := buildEnums(req)
enums := buildEnums(conf, req)
models := buildModels(conf, req)
queries, err := buildQueries(conf, req, models)
if err != nil {
Expand Down
9 changes: 5 additions & 4 deletions internal/postgresql_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"github.com/sqlc-dev/plugin-sdk-go/sdk"
)

func postgresType(req *plugin.GenerateRequest, col *plugin.Column) string {
func postgresType(conf Config, req *plugin.GenerateRequest, col *plugin.Column) string {
columnType := sdk.DataType(col.Type)

switch columnType {
Expand Down Expand Up @@ -50,10 +50,11 @@ func postgresType(req *plugin.GenerateRequest, col *plugin.Column) string {
for _, enum := range schema.Enums {
// Match both unqualified and schema-qualified enum type names
if columnType == enum.Name || columnType == schema.Name+"."+enum.Name {
if schema.Name == req.Catalog.DefaultSchema {
return "models." + modelName(enum.Name, req.Settings)
name := enum.Name
if conf.EmitSchemaNamePrefix && schema.Name != req.Catalog.DefaultSchema {
name = schema.Name + "_" + enum.Name
}
return "models." + modelName(schema.Name+"_"+enum.Name, req.Settings)
return "models." + modelName(name, req.Settings)
}
}
}
Expand Down