sqltmplx
sqltmplx
sqltmplx is a SQL-first conditional template renderer for Go.
It is provided as the dbx/sqltmplx subpackage (a part of dbx), so dbx is typically the package you start with.
It keeps control flow inside SQL comments, preserves sample literals for tooling, and renders bind variables plus arguments at runtime.
Package Layout
- Core renderer:
github.com/DaiYuANg/arcgo/dbx/sqltmplx - Dialect contracts:
github.com/DaiYuANg/arcgo/dbx/dialect - Built-in dialects:
github.com/DaiYuANg/arcgo/dbx/dialect/mysqlgithub.com/DaiYuANg/arcgo/dbx/dialect/postgresgithub.com/DaiYuANg/arcgo/dbx/dialect/sqlite
- Validator contract and registry:
github.com/DaiYuANg/arcgo/dbx/sqltmplx/validate - Optional parser-backed validators:
github.com/DaiYuANg/arcgo/dbx/sqltmplx/validate/mysqlparsergithub.com/DaiYuANg/arcgo/dbx/sqltmplx/validate/postgresparsergithub.com/DaiYuANg/arcgo/dbx/sqltmplx/validate/sqliteparser
This split keeps the core package lightweight.
Importing sqltmplx no longer forces all database parser dependencies into the same module graph.
Supported Template Features
/*%if expr */ ... /*%end *//*%where */ ... /*%end *//*%set */ ... /*%end */- Doma-style placeholders:
/* Name */'alice' - Slice expansion:
/* IDs */(1, 2, 3) - Expression helpers:
empty(x),blank(x),present(x) - Struct binding by field name first, then
sqltmpl,db,jsonaliases
Quick Start
package main
import (
"fmt"
"github.com/DaiYuANg/arcgo/dbx/sqltmplx"
"github.com/DaiYuANg/arcgo/dbx/dialect/postgres"
"github.com/DaiYuANg/arcgo/dbx/sqltmplx/validate"
_ "github.com/DaiYuANg/arcgo/dbx/sqltmplx/validate/postgresparser"
)
type Query struct {
Tenant string `db:"tenant"`
Name string `json:"name"`
IDs []int `json:"ids"`
}
func main() {
engine := sqltmplx.New(
postgres.New(),
sqltmplx.WithValidator(validate.NewSQLParser(postgres.New())),
)
tpl := `
SELECT id, tenant, name
FROM users
/*%where */
/*%if present(Tenant) */
AND tenant = /* Tenant */'acme'
/*%end */
/*%if present(Name) */
AND name = /* Name */'alice'
/*%end */
/*%if !empty(IDs) */
AND id IN (/* IDs */(1, 2, 3))
/*%end */
/*%end */
ORDER BY id DESC
`
bound, err := engine.Render(tpl, Query{
Tenant: "acme",
Name: "alice",
IDs: []int{1, 2, 3},
})
if err != nil {
panic(err)
}
fmt.Println(bound.Query)
fmt.Println(bound.Args)
}Validation Model
Use validate.NewSQLParser(dialect) when you want validator selection by dialect name.
This works through registration, so the specific backend package must be imported somewhere in your program.
Example:
import (
"github.com/DaiYuANg/arcgo/dbx/sqltmplx/validate"
_ "github.com/DaiYuANg/arcgo/dbx/sqltmplx/validate/mysqlparser"
)You can also bypass the registry and pass a validator implementation directly:
sqltmplx.WithValidator(mysqlparser.New())Statement Reuse with Registry
Reuse statements via MustStatement or Statement to avoid repeated parsing. The Registry caches compiled templates by name. Obtain the statement once (at init or first use), then pass it to dbx.SQLList, dbx.SQLGet, session.SQL().Exec, etc. in hot paths:
// Good: build once, execute many
registry := sqltmplx.NewRegistry(sqlFS, core.Dialect())
stmt := registry.MustStatement("sql/user/find_active.sql")
for range batches {
items, _ := dbx.SQLList(ctx, session, stmt, params, mapper)
// ...
}
// Avoid: parsing on every call
for range batches {
items, _ := dbx.SQLList(ctx, session, registry.MustStatement("sql/user/find_active.sql"), params, mapper)
}Compile Once, Render Many
engine := sqltmplx.New(mysql.New())
tpl, err := engine.Compile(queryText)
if err != nil {
panic(err)
}
bound1, _ := tpl.Render(map[string]any{"Tenant": "acme", "Status": "PAID"})
bound2, _ := tpl.Render(map[string]any{"Tenant": "acme", "Status": "SHIPPED"})
fmt.Println(bound1.Query)
fmt.Println(bound2.Query)Examples
- Example guide: sqltmplx examples
- Runnable examples:
Related dbx Docs
- dbx getting started: /docs/dbx/getting-started/
- dbx schema declaration: /docs/dbx/schema-design/
- dbx ID strategy: /docs/dbx/id-generation/
- dbx index configuration: /docs/dbx/indexes/
- dbx + sqltmplx integration: /docs/dbx/sqltmplx/
Testing and Benchmarks
go test ./dbx/sqltmplx/...
go test ./dbx/sqltmplx -run ^$ -bench . -benchmem
go test ./dbx/sqltmplx/render -run ^$ -bench . -benchmem
go test ./dbx/sqltmplx/validate/mysqlparser -run ^$ -bench . -benchmem