Quarry Docs
GitHub
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.

Quarry logo

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",
})