Repository navigation
Struct Mapping
One set of db tags drives both reading (scanning rows into structs) and writing (Rows, SetRow).
On this page: Tags · Writing structs · Rules for writes · The omitempty trap · Returning rows · db.Row
| Tag | Effect |
|---|---|
db:"name" |
The column name. Default: snake_case of the field name |
db:"name,omitempty" |
Left out of writes while the field holds its zero value |
db:",omitempty" |
Default name, omitempty
|
db:"-" |
Never read or written |
type User struct {
ID int64 `db:"id,omitempty"` // skipped on insert while 0, so the database assigns it
Name string `db:"name"`
Email *string `db:"email"` // nil is written as NULL
CreatedAt time.Time `db:",omitempty"` // column "created_at"
Secret string `db:"-"` // ignored entirely
}id, err := conn.Insert("users").Rows(User{Name: "Ann"}).ExecGetID(ctx)
n, err := conn.Insert("users").Rows(u1, u2).Exec(ctx) // or Rows([]User{u1, u2})
n, err = conn.Insert("users").Rows(u1).Ignore().Exec(ctx)
n, err = conn.Insert("users").Rows(u1).Upsert([]string{"email"}, []string{"name"}).Exec(ctx)
n, err = conn.Update("users").SetRow(User{Name: "Anna"}).Where("id", id).Exec(ctx)
n, err = conn.Update("users").SetMap(map[string]any{"email": nil}).Where("id", id).Exec(ctx) // explicit NULLTake the table name from the type when you do not want to repeat it: name, err := db.TableName[User]().
A pointer to a struct also works: Rows(&u), SetRow(&u).
-
Pointers and
sql.Null*: a nil pointer is written asNULL.sql.Null*types and anydriver.Valuerwork as usual. Adb.Raw(...)field value is inlined. -
Batches: every row of a batch insert must have the same columns, so an
omitemptycolumn is left out only when it is zero in every row of oneRowscall. If any row sets it, all rows write it. -
Embedded structs are flattened. A field behind a nil embedded pointer counts as omitted (or
NULLin a batch where another row sets it). - Unexported fields are ignored.
-
Update(...).SetRow(...)refuses to run without aWHERE(ErrNoWhere) and returnsErrNoValueswhen every field was omitted. See the WHERE guard.
Zero values with
omitemptyare skipped, so the database default or the existing value stays.
This is a common surprise in updates:
// Active is a bool tagged `db:"active,omitempty"`
conn.Update("users").SetRow(User{Active: false}).Where("id", 1).Exec(ctx)
// "active" is NOT written, because false is the zero valueTo write a zero value:
- use a pointer field (
Age *intwith&zero), or - use
Set/SetMap:Set("active", false).
On PostgreSQL and SQLite, scan RETURNING rows into structs with the write builder's Get:
var rows []User
err := conn.Insert("users").Rows(User{Name: "Rae"}).Returning("*").Get(ctx, &rows)
err = conn.Update("users").SetRow(User{Name: "Rae2"}).Where("id", 7).Returning("*").Get(ctx, &rows)
err = conn.Delete("users").Where("id", 7).Returning("*").Get(ctx, &rows)Returning is required before Get. MySQL and MariaDB return ErrUnsupported.
db.Row(v) turns a struct into the map[string]any form that Values, SetMap and UpdateOrInsert take, applying the tag rules above. (Rows and SetRow do this for you.)
row, err := db.Row(User{Name: "Ann"}) // map[string]any{"name": "Ann", "email": (*string)(nil)}
sql, args, err := db.Use(db.Postgres).Insert("users").Values(row).ToSQL()Row accepts a struct or non-nil pointer. Anything else (a map, nil, a scalar) is ErrInvalidArgs.
quill wiki · v0.2.0 · Repository · pkg.go.dev · Report an issue · MIT License
Basics
Building queries
Data access
- Writing Data
- Reading Data
- Pagination and Chunking
- Transactions
- Typed API
- Struct Mapping
- Hooks and Options
Reference
Guides