Quarry Docs
GitHub
Example

Raw SQL, Templates, and Recipes

Quarry keeps the escape hatch visible. Codex adds a small registry for reusable raw SQL templates and typed recipe wrappers when the same shape shows up again.

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. Raw fragments are trusted SQL text; they are not a sanitizer for user input.

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)
}
What this section is for It shows how a recipe stays close to raw SQL while still returning a Quarry query that the rest of the code can render or execute.

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