Example
Results Scanning
Quarry's scan layer is intentionally smaller than an ORM, but it still covers the practical cases: structs, pointers, nullables, scalar values, and queries that run through a transaction.
Struct hydration
type UserView struct {
ID int `db:"id"`
Email string `db:"email"`
CreatedAt time.Time `db:"created_at"`
Nickname *string `db:"nickname"`
Status sql.NullString `db:"status"`
}
users, err := scan.All[UserView](ctx, db,
qq.Select(
quarry.C("id"),
quarry.C("email"),
quarry.C("created_at"),
quarry.C("nickname"),
quarry.C("status"),
).
From("users").
Where(quarry.Eq("tenant_id", 42)),
)
Tag precedence
db tags win first, then json, then snake_case matching. Unknown
columns are ignored so you can select a little more than you scan.
One row, maybe one row
One is the strict version. MaybeOne is the zero-or-one version.
Both keep the row-count contract explicit.
One
user, err := scan.One[UserView](ctx, db,
qq.Select("id", "email").
From("users").
Where(quarry.Eq("id", 9)),
)
MaybeOne
user, err := scan.MaybeOne[UserView](ctx, db,
qq.Select("id", "email").
From("users").
Where(quarry.Eq("email", "ada@example.com")),
)
Transactions and writes
tx, err := db.BeginTx(ctx, nil)
if err != nil {
return err
}
defer tx.Rollback()
if _, err := scan.Exec(ctx, tx,
qq.Update("users").
Set("last_seen_at", time.Now()).
Where(quarry.Eq("id", 9)),
); err != nil {
return err
}
The same helpers work with *sql.DB and *sql.Tx. The package
only cares that the handle exposes the right context methods.
Scalar and aggregate scans
count, err := scan.One[int64](ctx, db,
qq.Select(quarry.Raw("COUNT(*)")).
From("users").
Where(quarry.Eq("tenant_id", 42)),
)
emails, err := scan.All[string](ctx, db,
qq.Select("email").
From("users").
Where(quarry.Eq("active", true)),
)