Summary
Two per-field inefficiencies in the code generation hot path:
1. schema.NamingStrategy created per field
utils.go:153-162 — generateDBName is called for every exported field of every struct:
func generateDBName(fieldName, gormTag string) string {
// ...
ns := schema.NamingStrategy{IdentifierMaxLength: 64}
return ns.ColumnName("", fieldName)
}
NamingStrategy is stateless — a single package-level instance suffices:
var namingStrategy = schema.NamingStrategy{IdentifierMaxLength: 64}
2. Param counting in RenderSQLTemplate is fragile
sqlparser.go:329-343 — After building the AST, the renderer estimates param count by:
- Splitting emitted Go code into lines
- Counting commas in
_params = append(_params, ...) lines
- Detecting
for loops and multiplying by 4
for _, line := range strings.Split(code, "\n") {
if strings.Index(code, "\tfor ") > 0 {
baseCount = 4
}
if strings.Contains(line, "_params = append(_params") {
count += strings.Count(line, ",") * baseCount
}
}
Problems:
- Commas inside string literals (e.g.
CONCAT("a, b")) cause overcounting
- The
for detection (strings.Index(code, "\tfor ") > 0) checks the entire code block, not the current line — once one for is found, all subsequent params are multiplied by 4
- The 4× multiplier is a hardcoded guess
Fix: Track param count during AST construction. Each TextNode.Emit already knows how many params it appends — return that count alongside the code string. ForNode and IfNode can sum their children's counts.
Expected Impact
Minor per-field improvement × thousands of fields = meaningful reduction in generation time. The param counting fix also prevents incorrect _params slice preallocation (which currently causes either overallocation or underallocation depending on the code).
Summary
Two per-field inefficiencies in the code generation hot path:
1.
schema.NamingStrategycreated per fieldutils.go:153-162—generateDBNameis called for every exported field of every struct:NamingStrategyis stateless — a single package-level instance suffices:2. Param counting in
RenderSQLTemplateis fragilesqlparser.go:329-343— After building the AST, the renderer estimates param count by:_params = append(_params, ...)linesforloops and multiplying by 4Problems:
CONCAT("a, b")) cause overcountingfordetection (strings.Index(code, "\tfor ") > 0) checks the entire code block, not the current line — once oneforis found, all subsequent params are multiplied by 4Fix: Track param count during AST construction. Each
TextNode.Emitalready knows how many params it appends — return that count alongside the code string.ForNodeandIfNodecan sum their children's counts.Expected Impact
Minor per-field improvement × thousands of fields = meaningful reduction in generation time. The param counting fix also prevents incorrect
_paramsslice preallocation (which currently causes either overallocation or underallocation depending on the code).