Insert
Insert writes one entity or several. Every field is written as it is, so a zero is stored as zero, and generated keys come back on your struct.
One or several
alice := &User{
Name: "Alice",
Email: "alice@example.com",
Age: 30,
}
err := db.Users.Insert(ctx, alice)
if err != nil {
return err
}
fmt.Println(alice.ID) // set by the database
Several go in together, all or nothing: if one fails, every row is rolled back and the keys it set are cleared.
err := db.Users.Insert(ctx, alice, bob)
// A slice of pointers spreads.
err = db.Users.Insert(ctx, users...)
A whole graph
Insert also saves the entities a new struct points at, parents first, filling in foreign keys as it goes.
alice := &User{
Name: "Alice",
Email: "alice@example.com",
Posts: []Post{{Title: "Hello"}, {Title: "Second"}},
}
err := db.Users.Insert(ctx, alice)
fmt.Println(alice.Posts[1].UserID == alice.ID) // true
It works from the child too, and an existing parent is linked, not inserted again.
post := &Post{Title: "New", User: alice}
err := db.Posts.Insert(ctx, post)
INSERT INTO "users" (...) VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id"
INSERT INTO "posts" (...) VALUES ($1, $2, $3, $4, $5), ($6, $7, $8, $9, $10) RETURNING "id"
New rows that depend on each other in a loop are refused with ant.ErrCycle.
Ignoring relations
InsertOnly writes the row and leaves its relation fields alone.
err := db.Users.InsertOnly(ctx, alice)
Find or create
FirstOrCreate returns the first row that matches the conditions, or inserts yours. The bool tells you which happened. If another writer gets there first, a unique constraint stops this insert, and you get the winner's row back.
user, created, err := db.Users.FirstOrCreate(
ctx,
&User{Name: "Ada", Email: "ada@example.com"},
ant.Eq(&U.Email, "ada@example.com"),
)
FirstOrNew is the same without saving. Its bool reports whether a row was found.
user, found, err := db.Users.FirstOrNew(ctx, &User{Name: "Ada"}, ant.Eq(&U.Name, "Ada"))
Copying a row
ant.Clone copies an entity with its key and relations cleared, ready to insert as a new row.
twin := ant.Clone(alice)
twin.Email = "twin@example.com"
err := db.Users.Insert(ctx, twin)
When a constraint fails
A constraint failure is the database's own error, and the Is helpers read it on every database.
err := db.Users.Insert(ctx, &User{Email: alice.Email})
if ant.IsUniqueViolation(err) {
return errors.New("that email is taken")
}
See it tested: tests/features/writing/insert_test.go