Skip to content

Commit af18e1c

Browse files
fix: MySQL parameter binding compatibility with SQLAlchemy
- Convert MySQL '?' placeholders to named parameters (':p1', ':p2', etc.) for proper SQLAlchemy parameter binding - Add MySQL type mapping support for Python type inference - Fix SQL syntax error when using generated code with MySQL + SQLAlchemy The generated code was using '?' placeholders in SQL but passing named parameters as dictionaries, which caused MySQL syntax errors. This fix ensures the SQL uses named parameters that match the parameter binding. Fixes parameter binding issues for MySQL users of sqlc-gen-python.
1 parent 53fa0b2 commit af18e1c

File tree

2 files changed

+64
-1
lines changed

2 files changed

+64
-1
lines changed

internal/gen.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,8 +193,10 @@ func pyInnerType(req *plugin.GenerateRequest, col *plugin.Column) string {
193193
switch req.Settings.Engine {
194194
case "postgresql":
195195
return postgresType(req, col)
196+
case "mysql":
197+
return mysqlType(req, col)
196198
default:
197-
log.Println("unsupported engine type")
199+
log.Printf("unsupported engine type: %s\n", req.Settings.Engine)
198200
return "Any"
199201
}
200202
}
@@ -360,6 +362,14 @@ func sqlalchemySQL(s, engine string) string {
360362
if engine == "postgresql" {
361363
return postgresPlaceholderRegexp.ReplaceAllString(s, ":p$1")
362364
}
365+
if engine == "mysql" {
366+
// Convert MySQL ? placeholders to named parameters for SQLAlchemy compatibility
367+
i := 1
368+
for strings.Contains(s, "?") {
369+
s = strings.Replace(s, "?", fmt.Sprintf(":p%d", i), 1)
370+
i++
371+
}
372+
}
363373
return s
364374
}
365375

internal/mysql_type.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package python
2+
3+
import (
4+
"log"
5+
6+
"github.com/sqlc-dev/plugin-sdk-go/plugin"
7+
"github.com/sqlc-dev/plugin-sdk-go/sdk"
8+
)
9+
10+
func mysqlType(req *plugin.GenerateRequest, col *plugin.Column) string {
11+
columnType := sdk.DataType(col.Type)
12+
13+
switch columnType {
14+
case "tinyint", "smallint", "mediumint", "int", "integer", "bigint":
15+
return "int"
16+
case "float", "double", "real":
17+
return "float"
18+
case "decimal", "numeric":
19+
return "decimal.Decimal"
20+
case "bit", "boolean", "bool":
21+
return "bool"
22+
case "json":
23+
return "Any"
24+
case "binary", "varbinary", "blob", "tinyblob", "mediumblob", "longblob":
25+
return "memoryview"
26+
case "date":
27+
return "datetime.date"
28+
case "time":
29+
return "datetime.time"
30+
case "datetime", "timestamp":
31+
return "datetime.datetime"
32+
case "char", "varchar", "text", "tinytext", "mediumtext", "longtext":
33+
return "str"
34+
case "enum", "set":
35+
return "str"
36+
default:
37+
for _, schema := range req.Catalog.Schemas {
38+
if schema.Name == "information_schema" || schema.Name == "mysql" {
39+
continue
40+
}
41+
for _, enum := range schema.Enums {
42+
if columnType == enum.Name {
43+
if schema.Name == req.Catalog.DefaultSchema {
44+
return "models." + modelName(enum.Name, req.Settings)
45+
}
46+
return "models." + modelName(schema.Name+"_"+enum.Name, req.Settings)
47+
}
48+
}
49+
}
50+
log.Printf("unknown MySQL type: %s\n", columnType)
51+
return "Any"
52+
}
53+
}

0 commit comments

Comments
 (0)