package pg_test
import (
"database/sql"
"fmt"
"time"
"github.com/go-pg/pg"
"github.com/go-pg/pg/orm"
)
func modelDB() *pg.DB {
db := pg.Connect(&pg.Options{
User: "postgres",
})
err := createTestSchema(db)
if err != nil {
panic(err)
}
err = db.Insert(&Author{
Name: "author 1",
})
if err != nil {
panic(err)
}
books := []Book{{
Title: "book 1",
AuthorID: 1,
EditorID: 11,
}, {
Title: "book 2",
AuthorID: 1,
EditorID: 12,
}, {
Title: "book 3",
AuthorID: 11,
EditorID: 11,
CreatedAt: time.Now(),
}}
err = db.Insert(&books)
if err != nil {
panic(err)
}
for i := 0; i < 2; i++ {
genre := Genre{
Name: fmt.Sprintf("genre %d", i+1),
}
err = db.Insert(&genre)
if err != nil {
panic(err)
}
err = db.Insert(&BookGenre{
BookId: 1,
GenreId: genre.Id,
})
if err != nil {
panic(err)
}
}
// For CountEstimate.
_, err = db.Exec("VACUUM")
if err != nil {
panic(err)
}
return db
}
func ExampleDB_Insert() {
db := modelDB()
book := Book{
Title: "new book",
AuthorID: 1,
}
err := db.Insert(&book)
if err != nil {
panic(err)
}
fmt.Println(book)
// Output: Book<Id=4 Title="new book">
err = db.Delete(&book)
if err != nil {
panic(err)
}
}
func ExampleDB_Insert_bulkInsert() {
db := modelDB()
book1 := Book{
Title: "new book 1",
}
book2 := Book{
Title: "new book 2",
}
err := db.Insert(&book1, &book2)
if err != nil {
panic(err)
}
fmt.Println(book1, book2)
// Output: Book<Id=4 Title="new book 1"> Book<Id=5 Title="new book 2">
for _, book := range []*Book{&book1, &book2} {
err := db.Delete(book)
if err != nil {
panic(err)
}
}
}
func ExampleDB_Insert_bulkInsertSlice() {
db := modelDB()
books := []Book{{
Title: "new book 1",
}, {
Title: "new book 2",
}}
err := db.Insert(&books)
if err != nil {
panic(err)
}
fmt.Println(books)
// Output: [Book<Id=4 Title="new book 1"> Book<Id=5 Title="new book 2">]
for i := range books {
err := db.Delete(&books[i])
if err != nil {
panic(err)
}
}
}
func ExampleDB_Insert_onConflictDoNothing() {
db := modelDB()
book := Book{
Id: 100,
Title: "book 100",
}
for i := 0; i < 2; i++ {
res, err := db.Model(&book).OnConflict("DO NOTHING").Insert()
if err != nil {
panic(err)
}
if res.RowsAffected() > 0 {
fmt.Println("created")
} else {
fmt.Println("did nothing")
}
}
err := db.Delete(&book)
if err != nil {
panic(err)
}
// Output: created
// did nothing
}
func ExampleDB_Insert_onConflictDoUpdate() {
db := modelDB()
var book *Book
for i := 0; i < 2; i++ {
book = &Book{
Id: 100,
Title: fmt.Sprintf("title version #%d", i),
}
_, err := db.Model(book).
OnConflict("(id) DO UPDATE").
Set("title = EXCLUDED.title").
Insert()
if err != nil {
panic(err)
}
err = db.Select(book)
if err != nil {
panic(err)
}
fmt.Println(book)
}
err := db.Delete(book)
if err != nil {
panic(err)
}
// Output: Book<Id=100 Title="title version #0">
// Book<Id=100 Title="title version #1">
}
func ExampleDB_Insert_selectOrInsert() {
db := modelDB()
author := Author{
Name: "R. Scott Bakker",
}
created, err := db.Model(&author).
Column("id").
Where("name = ?name").
OnConflict("DO NOTHING"). // OnConflict is optional
Returning("id").
SelectOrInsert()
if err != nil {
panic(err)
}
fmt.Println(created, author)
// Output: true Author<ID=2 Name="R. Scott Bakker">
}
func ExampleDB_Insert_dynamicTableName() {
type NamelessModel struct {
tableName struct{} `sql:"_"` // "_" means no name
Id int
}
db := modelDB()
err := db.Model((*NamelessModel)(nil)).Table("dynamic_name").CreateTable(nil)
panicIf(err)
row123 := &NamelessModel{
Id: 123,
}
_, err = db.Model(row123).Table("dynamic_name").Insert()
panicIf(err)
row := new(NamelessModel)
err = db.Model(row).Table("dynamic_name").First()
panicIf(err)
fmt.Println("id is", row.Id)
err = db.Model((*NamelessModel)(nil)).Table("dynamic_name").DropTable(nil)
panicIf(err)
// Output: id is 123
}
func ExampleDB_Select() {
db := modelDB()
book := Book{
Id: 1,
}
err := db.Select(&book)
if err != nil {
panic(err)
}
fmt.Println(book)
// Output: Book<Id=1 Title="book 1">
}
func ExampleDB_Select_firstRow() {
db := modelDB()
var firstBook Book
err := db.Model(&firstBook).First()
if err != nil {
panic(err)
}
fmt.Println(firstBook)
// Output: Book<Id=1 Title="book 1">
}
func ExampleDB_Select_lastRow() {
db := modelDB()
var lastBook Book
err := db.Model(&lastBook).Last()
if err != nil {
panic(err)
}
fmt.Println(lastBook)
// Output: Book<Id=3 Title="book 3">
}
func ExampleDB_Select_allColumns() {
db := modelDB()
var book Book
err := db.Model(&book).Column("book.*").First()
if err != nil {
panic(err)
}
fmt.Println(book, book.AuthorID)
// Output: Book<Id=1 Title="book 1"> 1
}
func ExampleDB_Select_someColumns() {
db := modelDB()
var book Book
err := db.Model(&book).
Column("book.id", "book.title").
OrderExpr("book.id ASC").
Limit(1).
Select()
if err != nil {
panic(err)
}
fmt.Println(book)
// Output: Book<Id=1 Title="book 1">
}
func ExampleDB_Select_someColumnsIntoVars() {
db := modelDB()
var id int
var title string
err := db.Model(&Book{}).
Column("book.id", "book.title").
OrderExpr("book.id ASC").
Limit(1).
Select(&id, &title)
if err != nil {
panic(err)
}
fmt.Println(id, title)
// Output: 1 book 1
}
func ExampleDB_Select_whereIn() {
db := modelDB()
var books []Book
err := db.Model(&books).WhereIn("id IN (?)", []int{1, 2}).Select()
if err != nil {
panic(err)
}
fmt.Println(books)
// Output: [Book<Id=1 Title="book 1"> Book<Id=2 Title="book 2">]
}
func ExampleDB_Select_whereGroup() {
db := modelDB()
var books []Book
err := db.Model(&books).
WhereGroup(func(q *orm.Query) (*orm.Query, error) {
q = q.WhereOr("id = 1").
WhereOr("id = 2")
return q, nil
}).
Where("title IS NOT NULL").
Select()
if err != nil {
panic(err)
}
fmt.Println(books)
// Output: [Book<Id=1 Title="book 1"> Book<Id=2 Title="book 2">]
}
func ExampleDB_Select_sqlExpression() {
db := modelDB()
var ids []int
err := db.Model(&Book{}).
ColumnExpr("array_agg(book.id)").
Select(pg.Array(&ids))
if err != nil {
panic(err)
}
fmt.Println(ids)
// Output: [1 2 3]
}
func ExampleDB_Select_groupBy() {
db := modelDB()
var res []struct {
AuthorId int
BookCount int
}
err := db.Model(&Book{}).
Column("author_id").
ColumnExpr("count(*) AS book_count").
Group("author_id").
OrderExpr("book_count DESC").
Select(&res)
if err != nil {
panic(err)
}
fmt.Println("len", len(res))
fmt.Printf("author %d has %d books\n", res[0].AuthorId, res[0].BookCount)
fmt.Printf("author %d has %d books\n", res[1].AuthorId, res[1].BookCount)
// Output: len 2
// author 1 has 2 books
// author 11 has 1 books
}
func ExampleDB_Select_with() {
authorBooks := pgdb.Model(&Book{}).Where("author_id = ?", 1)
var books []Book
err := pgdb.Model().
With("author_books", authorBooks).
Table("author_books").
Select(&books)
if err != nil {
panic(err)
}
fmt.Println(books)
// Output: [Book<Id=1 Title="book 1"> Book<Id=2 Title="book 2">]
}
func ExampleDB_Select_wrapWith() {
// WITH author_books AS (
// SELECT * books WHERE author_id = 1
// )
// SELECT * FROM author_books
var books []Book
err := pgdb.Model(&books).
Where("author_id = ?", 1).
WrapWith("author_books").
Table("author_books").
Select(&books)
if err != nil {
panic(err)
}
fmt.Println(books)
// Output: [Book<Id=1 Title="book 1"> Book<Id=2 Title="book 2">]
}
func ExampleDB_Select_applyFunc() {
db := modelDB()
var authorId int
var editorId int
filter := func(q *orm.Query) (*orm.Query, error) {
if authorId != 0 {
q = q.Where("author_id = ?", authorId)
}
if editorId != 0 {
q = q.Where("editor_id = ?", editorId)
}
return q, nil
}
var books []Book
authorId = 1
err := db.Model(&books).
Apply(filter).
Select()
if err != nil {
panic(err)
}
fmt.Println(books)
// Output: [Book<Id=1 Title="book 1"> Book<Id=2 Title="book 2">]
}
func ExampleDB_Model_count() {
db := modelDB()
count, err := db.Model(&Book{}).Count()
if err != nil {
panic(err)
}
fmt.Println(count)
// Output: 3
}
func ExampleDB_Model_countEstimate() {
db := modelDB()
count, err := db.Model(&Book{}).CountEstimate(0)
if err != nil {
panic(err)
}
fmt.Println(count)
// Output: 3
}
func ExampleDB_Model_selectAndCount() {
db := modelDB()
var books []Book
count, err := db.Model(&books).OrderExpr("id ASC").Limit(2).SelectAndCount()
if err != nil {
panic(err)
}
fmt.Println(count)
fmt.Println(books)
// Output: 3
// [Book<Id=1 Title="book 1"> Book<Id=2 Title="book 2">]
}
func ExampleDB_Model_exists() {
db := modelDB()
var books []Book
exists, err := db.Model(&books).Where("author_id = ?", 1).Exists()
if err != nil {
panic(err)
}
fmt.Println(exists)
// Output: true
}
func ExampleDB_Model_nullEmptyValue() {
type Example struct {
Hello string
}
var str sql.NullString
_, err := pgdb.QueryOne(pg.Scan(&str), "SELECT ?hello", &Example{Hello: ""})
if err != nil {
panic(err)
}
fmt.Println(str.Valid)
// Output: false
}
func ExampleDB_Model_forEach() {
err := pgdb.Model((*Book)(nil)).
OrderExpr("id ASC").
ForEach(func(b *Book) error {
fmt.Println(b)
return nil
})
if err != nil {
panic(err)
}
// Output: Book<Id=1 Title="book 1">
// Book<Id=2 Title="book 2">
// Book<Id=3 Title="book 3">
}
func ExampleDB_Model_hasOne() {
type Profile struct {
Id int
Lang string
}
// User has one profile.
type User struct {
Id int
Name string
ProfileId int
Profile *Profile
}
db := connect()
defer db.Close()
qs := []string{
"CREATE TEMP TABLE users (id int, name text, profile_id int)",
"CREATE TEMP TABLE profiles (id int, lang text)",
"INSERT INTO users VALUES (1, 'user 1', 1), (2, 'user 2', 2)",
"INSERT INTO profiles VALUES (1, 'en'), (2, 'ru')",
}
for _, q := range qs {
_, err := db.Exec(q)
if err != nil {
panic(err)
}
}
// Select users joining their profiles with following query:
//
// SELECT
// "user".*,
// "profile"."id" AS "profile__id",
// "profile"."lang" AS "profile__lang",
// "profile"."user_id" AS "profile__user_id"
// FROM "users" AS "user"
// LEFT JOIN "profiles" AS "profile" ON "profile"."user_id" = "user"."id"
var users []User
err := db.Model(&users).
Column("user.*").
Relation("Profile").
Select()
if err != nil {
panic(err)
}
fmt.Println(len(users), "results")
fmt.Println(users[0].Id, users[0].Name, users[0].Profile)
fmt.Println(users[1].Id, users[1].Name, users[1].Profile)
// Output: 2 results
// 1 user 1 &{1 en}
// 2 user 2 &{2 ru}
}
func ExampleDB_Model_belongsTo() {
// Profile belongs to User.
type Profile struct {
Id int
Lang string
UserId int
}
type User struct {
Id int
Name string
Profile *Profile
}
db := connect()
defer db.Close()
qs := []string{
"CREATE TEMP TABLE users (id int, name text)",
"CREATE TEMP TABLE profiles (id int, lang text, user_id int)",
"INSERT INTO users VALUES (1, 'user 1'), (2, 'user 2')",
"INSERT INTO profiles VALUES (1, 'en', 1), (2, 'ru', 2)",
}
for _, q := range qs {
_, err := db.Exec(q)
if err != nil {
panic(err)
}
}
// Select users joining their profiles with following query:
//
// SELECT
// "user".*,
// "profile"."id" AS "profile__id",
// "profile"."lang" AS "profile__lang"
// FROM "users" AS "user"
// LEFT JOIN "profiles" AS "profile" ON "profile"."id" = "user"."profile_id"
var users []User
err := db.Model(&users).
Column("user.*").
Relation("Profile").
Select()
if err != nil {
panic(err)
}
fmt.Println(len(users), "results")
fmt.Println(users[0].Id, users[0].Name, users[0].Profile)
fmt.Println(users[1].Id, users[1].Name, users[1].Profile)
// Output: 2 results
// 1 user 1 &{1 en 1}
// 2 user 2 &{2 ru 2}
}
func ExampleDB_Model_hasMany() {
type Profile struct {
Id int
Lang string
Active bool
UserId int
}
// User has many profiles.
type User struct {
Id int
Name string
Profiles []*Profile
}
db := connect()
defer db.Close()
qs := []string{
"CREATE TEMP TABLE users (id int, name text)",
"CREATE TEMP TABLE profiles (id int, lang text, active bool, user_id int)",
"INSERT INTO users VALUES (1, 'user 1')",
"INSERT INTO profiles VALUES (1, 'en', TRUE, 1), (2, 'ru', TRUE, 1), (3, 'md', FALSE, 1)",
}
for _, q := range qs {
_, err := db.Exec(q)
if err != nil {
panic(err)
}
}
// Select user and all his active profiles with following queries:
//
// SELECT "user".* FROM "users" AS "user" ORDER BY "user"."id" LIMIT 1
//
// SELECT "profile".* FROM "profiles" AS "profile"
// WHERE (active IS TRUE) AND (("profile"."user_id") IN ((1)))
var user User
err := db.Model(&user).
Column("user.*").
Relation("Profiles", func(q *orm.Query) (*orm.Query, error) {
return q.Where("active IS TRUE"), nil
}).
First()
if err != nil {
panic(err)
}
fmt.Println(user.Id, user.Name, user.Profiles[0], user.Profiles[1])
// Output: 1 user 1 &{1 en true 1} &{2 ru true 1}
}
func ExampleDB_Model_hasManySelf() {
type Item struct {
Id int
Items []Item `pg:"fk:parent_id"`
ParentId int
}
db := connect()
defer db.Close()
qs := []string{
"CREATE TEMP TABLE items (id int, parent_id int)",
"INSERT INTO items VALUES (1, NULL), (2, 1), (3, 1)",
}
for _, q := range qs {
_, err := db.Exec(q)
if err != nil {
panic(err)
}
}
// Select item and all subitems with following queries:
//
// SELECT "item".* FROM "items" AS "item" ORDER BY "item"."id" LIMIT 1
//
// SELECT "item".* FROM "items" AS "item" WHERE (("item"."parent_id") IN ((1)))
var item Item
err := db.Model(&item).Column("item.*").Relation("Items").First()
if err != nil {
panic(err)
}
fmt.Println("Item", item.Id)
fmt.Println("Subitems", item.Items[0].Id, item.Items[1].Id)
// Output: Item 1
// Subitems 2 3
}
func ExampleDB_Update() {
db := modelDB()
book := &Book{Id: 1}
err := db.Select(book)
if err != nil {
panic(err)
}
book.Title = "updated book 1"
err = db.Update(book)
if err != nil {
panic(err)
}
err = db.Select(book)
if err != nil {
panic(err)
}
fmt.Println(book)
// Output: Book<Id=1 Title="updated book 1">
}
func ExampleDB_Update_notNull() {
db := modelDB()
book := &Book{
Id: 1,
Title: "updated book 1",
}
_, err := db.Model(book).WherePK().UpdateNotNull()
if err != nil {
panic(err)
}
book = new(Book)
err = db.Model(book).Where("id = ?", 1).Select()
if err != nil {
panic(err)
}
fmt.Println(book)
// Output: Book<Id=1 Title="updated book 1">
}
func ExampleDB_Update_someColumns() {
db := modelDB()
book := Book{
Id: 1,
Title: "updated book 1", // only this column is going to be updated
AuthorID: 2,
}
_, err := db.Model(&book).Column("title").WherePK().Returning("*").Update()
if err != nil {
panic(err)
}
fmt.Println(book, book.AuthorID)
// Output: Book<Id=1 Title="updated book 1"> 1
}
func ExampleDB_Update_someColumns2() {
db := modelDB()
book := Book{
Id: 1,
Title: "updated book 1",
AuthorID: 2, // this column will not be updated
}
_, err := db.Model(&book).Set("title = ?title").WherePK().Returning("*").Update()
if err != nil {
panic(err)
}
fmt.Println(book, book.AuthorID)
// Output: Book<Id=1 Title="updated book 1"> 1
}
func ExampleDB_Update_setValues() {
db := modelDB()
var book Book
_, err := db.Model(&book).
Set("title = concat(?, title, ?)", "prefix ", " suffix").
Where("id = ?", 1).
Returning("*").
Update()
if err != nil {
panic(err)
}
fmt.Println(book)
// Output: Book<Id=1 Title="prefix book 1 suffix">
}
func ExampleDB_Update_bulkUpdate() {
db := modelDB()
book1 := &Book{
Id: 1,
Title: "updated book 1",
UpdatedAt: time.Now(),
}
book2 := &Book{
Id: 2,
Title: "updated book 2",
UpdatedAt: time.Now(),
}
// UPDATE "books" AS "book"
// SET "title" = _data."title"
// FROM (VALUES ('updated book 1', 1), ('updated book 2', 2)) AS _data("title", "id")
// WHERE "book"."id" = _data."id"
_, err := db.Model(book1, book2).Column("title", "updated_at").Update()
if err != nil {
panic(err)
}
var books []Book
err = db.Model(&books).Order("id").Select()
if err != nil {
panic(err)
}
fmt.Println(books)
// Output: [Book<Id=1 Title="updated book 1"> Book<Id=2 Title="updated book 2"> Book<Id=3 Title="book 3">]
}
func ExampleDB_Update_bulkUpdateSlice() {
db := modelDB()
books := []Book{{
Id: 1,
Title: "updated book 1",
UpdatedAt: time.Now(),
}, {
Id: 2,
Title: "updated book 2",
UpdatedAt: time.Now(),
}}
// UPDATE "books" AS "book"
// SET "title" = _data."title"
// FROM (VALUES ('updated book 1', 1), ('updated book 2', 2)) AS _data("title", "id")
// WHERE "book"."id" = _data."id"
_, err := db.Model(&books).Column("title", "updated_at").Update()
if err != nil {
panic(err)
}
books = nil
err = db.Model(&books).Order("id").Select()
if err != nil {
panic(err)
}
fmt.Println(books)
// Output: [Book<Id=1 Title="updated book 1"> Book<Id=2 Title="updated book 2"> Book<Id=3 Title="book 3">]
}
func ExampleDB_Delete() {
db := modelDB()
book := Book{
Title: "title 1",
AuthorID: 1,
}
err := db.Insert(&book)
if err != nil {
panic(err)
}
err = db.Delete(&book)
if err != nil {
panic(err)
}
err = db.Select(&book)
fmt.Println(err)
// Output: pg: no rows in result set
}
func ExampleDB_Delete_multipleRows() {
db := modelDB()
ids := pg.In([]int{1, 2, 3})
res, err := db.Model((*Book)(nil)).Where("id IN (?)", ids).Delete()
if err != nil {
panic(err)
}
fmt.Println("deleted", res.RowsAffected())
count, err := db.Model((*Book)(nil)).Count()
if err != nil {
panic(err)
}
fmt.Println("left", count)
// Output: deleted 3
// left 0
}
func ExampleDB_Delete_bulkDelete() {
db := modelDB()
var books []Book
err := db.Model(&books).Select()
if err != nil {
panic(err)
}
res, err := db.Model(&books).Delete()
if err != nil {
panic(err)
}
fmt.Println("deleted", res.RowsAffected())
count, err := db.Model((*Book)(nil)).Count()
if err != nil {
panic(err)
}
fmt.Println("left", count)
// Output: deleted 3
// left 0
}
func ExampleQ() {
db := modelDB()
cond := fmt.Sprintf("id = %d", 1)
var book Book
err := db.Model(&book).Where("?", pg.Q(cond)).Select()
if err != nil {
panic(err)
}
fmt.Println(book)
// Output: Book<Id=1 Title="book 1">
}
func ExampleF() {
db := modelDB()
var book Book
err := db.Model(&book).Where("? = 1", pg.F("id")).Select()
if err != nil {
panic(err)
}
fmt.Println(book)
// Output: Book<Id=1 Title="book 1">
}
func ExampleDB_jsonUseNumber() {
type Event struct {
Id int
Data map[string]interface{} `pg:",json_use_number"`
}
db := pg.Connect(pgOptions())
defer db.Close()
err := db.CreateTable((*Event)(nil), &orm.CreateTableOptions{
Temp: true,
})
if err != nil {
panic(err)
}
event := &Event{
Data: map[string]interface{}{
"price": 1.23,
},
}
err = db.Insert(event)
if err != nil {
panic(err)
}
event2 := new(Event)
err = db.Model(event2).Where("id = ?", event.Id).Select()
if err != nil {
panic(err)
}
// Check that price is decoded as json.Number.
fmt.Printf("%T", event2.Data["price"])
// Output: json.Number
}
func ExampleDB_discardUnknownColumns() {
type Model1 struct {
}
var model1 Model1
_, err := pgdb.QueryOne(&model1, "SELECT 1 AS id")
fmt.Printf("Model1: %v\n", err)
type Model2 struct {
tableName struct{} `pg:",discard_unknown_columns"`
}
var model2 Model2
_, err = pgdb.QueryOne(&model2, "SELECT 1 AS id")
fmt.Printf("Model2: %v\n", err)
// Output: Model1: pg: can't find column=id in model=Model1 (try discard_unknown_columns)
// Model2: <nil>
}