Quarry Docs
GitHub
Example

Patch/Update Examples

A patch endpoint usually needs three things: ignore missing fields, preserve explicit zero values, and return the row that actually changed. Quarry keeps those cases explicit instead of hiding them in string concatenation.

Quarry logo

Profile patch endpoint

type ProfilePatch struct {
    ID          int
    DisplayName *string
    Email       *string
    Bio         *string
    Enabled     *bool
}

q := qq.Update("profiles").
    SetOptional("display_name", patch.DisplayName).
    SetOptional("email", patch.Email).
    SetOptional("bio", patch.Bio).
    SetIf(patch.Enabled != nil, "enabled", *patch.Enabled).
    Where(quarry.Eq("id", patch.ID)).
    Returning("id", "display_name", "email", "bio", "enabled")
Why SetOptional matters A nil pointer means “do not touch this field.” A pointer to an empty string still becomes a real update if that is what the caller asked for.

Map-driven changes

Sometimes the fields come from a map that already has stable keys. SetMap keeps the column order deterministic, so review diffs stay readable.

changes := map[string]any{
    "display_name": "Alice Wu",
    "timezone":     "UTC",
    "theme":        "dark",
}

q := qq.Update("profiles").
    SetMap(changes).
    Where(quarry.Eq("id", 42))
UPDATE profiles
SET display_name = $1, theme = $2, timezone = $3
WHERE id = $4

Returning the changed row

updated, err := scan.One[Profile](ctx, db,
    qq.Update("profiles").
        SetOptional("bio", patch.Bio).
        SetIf(patch.Enabled != nil, "enabled", *patch.Enabled).
        Where(quarry.Eq("id", patch.ID)).
        Returning("id", "display_name", "email", "bio", "enabled"),
)
Dialect note Postgres and SQLite can return rows directly. MySQL does not, so the builder returns a clear unsupported-feature error instead of pretending otherwise.

Bulk writes and soft deletes

Bulk insert

q := qq.InsertInto("audit_log").
    Columns("actor_id", "action", "created_at").
    Rows(
        []any{42, "profile.updated", time.Now()},
        []any{42, "profile.viewed", time.Now()},
    )

Soft delete

q := qq.Update("profiles").
    Set("deleted_at", time.Now()).
    SetOptional("deleted_reason", patch.Reason).
    Where(quarry.Eq("id", patch.ID))