Example
Raw SQL, Templates, and Recipes
Quarry does not hide the escape hatch. Codex gives it a registry, named bindings, and recipe wrappers so you can reuse the parts that are worth reusing.
Raw fragments
q := qq.Select(
"id",
"email",
quarry.Raw("COUNT(*) FILTER (WHERE status = ?)", "active"),
).
From("users").
Where(quarry.Raw("created_at >= ?", since))
Placeholder rewriting still happens
Raw fragments keep the raw SQL visible, but
? placeholders still become
dialect tokens and the scanner ignores quoted text, comments, and dollar-quoted bodies.
Named templates
store := codex.NewStore()
store.MustAdd("users.by_email", `
SELECT id, email, status
FROM users
WHERE tenant_id = :tenant_id
AND email = :email
`)
template, ok := store.Get("users.by_email")
if !ok {
panic("missing query")
}
bound := template.With(qq).BindMap(map[string]any{
"tenant_id": 42,
"email": "ada@example.com",
})
Strict mode
If you want unused named arguments rejected, set strict mode on the store before you
add the query.
Recipes
type UserSearchParams struct {
TenantID int
Query string
Status *string
}
recipe := codex.NewRecipe(func(qq *quarry.Quarry, p UserSearchParams) quarry.SQLer {
return qq.Select("id", "email", "status").
From("users").
Where(
quarry.Eq("tenant_id", p.TenantID),
quarry.OptionalILike("email", p.Query),
quarry.OptionalEq("status", p.Status),
)
})
if err := store.AddRecipe("users.search", recipe); err != nil {
panic(err)
}
q, err := store.MustRecipe("users.search").Build(qq, UserSearchParams{
TenantID: 42,
Query: "%bob%",
})
if err != nil {
panic(err)
}
When the raw query is still the right tool
- Window functions that are easier to read directly.
- Vendor-specific operators that Quarry should not pretend to understand.
- Existing SQL that you want to keep verbatim while only changing the bindings.
- Queries that are reused often enough to deserve a name in the registry.
Struct bindings and strict mode
The store can reject unused named arguments, and BindStruct maps fields
using the same tag rules as the scan layer.
store := codex.NewStore().SetStrict(true)
store.MustAdd("users.search", `
SELECT id, email, status
FROM users
WHERE tenant_id = :tenant_id
AND status = :status
`)
template, ok := store.Get("users.search")
if !ok {
panic("missing template")
}
bound := template.With(qq).BindStruct(struct {
TenantID int `db:"tenant_id"`
Status string `db:"status"`
}{
TenantID: 42,
Status: "active",
})