vendor dependencies with dep

This commit is contained in:
dhax 2017-09-25 20:20:52 +02:00
parent 93d8310491
commit 1384296a47
2712 changed files with 965742 additions and 0 deletions

22
vendor/github.com/go-pg/migrations/.travis.yml generated vendored Normal file
View file

@ -0,0 +1,22 @@
sudo: false
language: go
services:
- postgresql
addons:
postgresql: "9.4"
go:
- 1.7
- 1.8
- tip
matrix:
allow_failures:
- go: tip
install:
- go get github.com/go-pg/pg
- go get github.com/onsi/ginkgo
- go get github.com/onsi/gomega

3
vendor/github.com/go-pg/migrations/Makefile generated vendored Normal file
View file

@ -0,0 +1,3 @@
all:
go test ./...
go test ./... -short -race

5
vendor/github.com/go-pg/migrations/README.md generated vendored Normal file
View file

@ -0,0 +1,5 @@
# SQL migrations for Golang and PostgreSQL
[![Build Status](https://travis-ci.org/go-pg/migrations.svg)](https://travis-ci.org/go-pg/migrations)
This package allows you to run migrations on your PostgreSQL database using [Golang Postgres client](https://github.com/go-pg/pg). See [example](example) for details.

79
vendor/github.com/go-pg/migrations/db.go generated vendored Normal file
View file

@ -0,0 +1,79 @@
package migrations
import (
"io"
"strings"
"github.com/go-pg/pg"
"github.com/go-pg/pg/orm"
"github.com/go-pg/pg/types"
)
var tableName = "gopg_migrations"
func SetTableName(name string) {
tableName = name
}
type DB interface {
Exec(query interface{}, params ...interface{}) (orm.Result, error)
ExecOne(query interface{}, params ...interface{}) (orm.Result, error)
Query(model, query interface{}, params ...interface{}) (orm.Result, error)
QueryOne(model, query interface{}, params ...interface{}) (orm.Result, error)
Model(model ...interface{}) *orm.Query
Select(model interface{}) error
Insert(model ...interface{}) error
Update(model ...interface{}) error
Delete(model interface{}) error
CreateTable(model interface{}, opt *orm.CreateTableOptions) error
DropTable(model interface{}, opt *orm.DropTableOptions) error
CopyFrom(r io.Reader, query interface{}, params ...interface{}) (orm.Result, error)
CopyTo(w io.Writer, query interface{}, params ...interface{}) (orm.Result, error)
FormatQuery(dst []byte, query string, params ...interface{}) []byte
}
func getTableName() types.ValueAppender {
return pg.Q(tableName)
}
func Version(db DB) (int64, error) {
var version int64
_, err := db.QueryOne(pg.Scan(&version), `
SELECT version FROM ? ORDER BY id DESC LIMIT 1
`, getTableName())
if err != nil {
if err == pg.ErrNoRows {
return 0, nil
}
return 0, err
}
return version, nil
}
func SetVersion(db DB, version int64) error {
_, err := db.Exec(`
INSERT INTO ? (version, created_at) VALUES (?, now())
`, getTableName(), version)
return err
}
func createTables(db DB) error {
if ind := strings.IndexByte(tableName, '.'); ind >= 0 {
_, err := db.Exec(`CREATE SCHEMA IF NOT EXISTS ?`, pg.Q(tableName[:ind]))
if err != nil {
return err
}
}
_, err := db.Exec(`
CREATE TABLE IF NOT EXISTS ? (
id serial,
version bigint,
created_at timestamptz
)
`, getTableName())
return err
}

161
vendor/github.com/go-pg/migrations/db_test.go generated vendored Normal file
View file

@ -0,0 +1,161 @@
package migrations_test
import (
"fmt"
"testing"
"github.com/go-pg/migrations"
"github.com/go-pg/pg"
)
func connectDB() *pg.DB {
db := pg.Connect(&pg.Options{
User: "postgres",
})
_, err := db.Exec("DROP TABLE IF EXISTS gopg_migrations")
if err != nil {
panic(err)
}
return db
}
func TestVersion(t *testing.T) {
db := connectDB()
_, _, err := migrations.Run(db, "init")
if err != nil {
t.Fatalf("init failed: %s", err)
}
version, err := migrations.Version(db)
if err != nil {
t.Fatalf("Version failed: %s", err)
}
if version != 0 {
t.Fatalf("got version %d, wanted 0", version)
}
if err := migrations.SetVersion(db, 999); err != nil {
t.Fatalf("SetVersion failed: %s", err)
}
version, err = migrations.Version(db)
if err != nil {
t.Fatalf("Version failed: %s", err)
}
if version != 999 {
t.Fatalf("got version %d, wanted %d", version)
}
}
func TestUpDown(t *testing.T) {
db := connectDB()
_, _, err := migrations.Run(db, "init")
if err != nil {
t.Fatalf("init failed: %s", err)
}
migrations.Set([]migrations.Migration{
{Version: 2, Up: doNothing, Down: doNothing},
{Version: 1, Up: doNothing, Down: doNothing},
{Version: 3, Up: doNothing, Down: doNothing},
})
oldVersion, newVersion, err := migrations.Run(db, "up")
if err != nil {
t.Fatal(err)
}
if oldVersion != 0 {
t.Fatalf("got %d, wanted 0", oldVersion)
}
if newVersion != 3 {
t.Fatalf("got %d, wanted 3", newVersion)
}
version, err := migrations.Version(db)
if err != nil {
t.Fatal(err)
}
if version != 3 {
t.Fatalf("got version %d, wanted 3", version)
}
for i := 2; i >= -5; i-- {
wantOldVersion := int64(i + 1)
wantNewVersion := int64(i)
if wantNewVersion < 0 {
wantOldVersion = 0
wantNewVersion = 0
}
oldVersion, newVersion, err = migrations.Run(db, "down")
if err != nil {
t.Fatal(err)
}
if oldVersion != wantOldVersion {
t.Fatalf("got %d, wanted %d", oldVersion, wantOldVersion)
}
if newVersion != wantNewVersion {
t.Fatalf("got %d, wanted %d", newVersion, wantNewVersion)
}
version, err = migrations.Version(db)
if err != nil {
t.Fatal(err)
}
if version != wantNewVersion {
t.Fatalf("got version %d, wanted %d", version, wantNewVersion)
}
}
}
func TestSetVersion(t *testing.T) {
db := connectDB()
_, _, err := migrations.Run(db, "init")
if err != nil {
t.Fatalf("init failed: %s", err)
}
migrations.Set([]migrations.Migration{
{Version: 1, Up: doPanic, Down: doPanic},
{Version: 2, Up: doPanic, Down: doPanic},
{Version: 3, Up: doPanic, Down: doPanic},
})
for i := 0; i < 5; i++ {
wantOldVersion := int64(i)
wantNewVersion := int64(i + 1)
oldVersion, newVersion, err := migrations.Run(
db, "set_version", fmt.Sprint(wantNewVersion))
if err != nil {
t.Fatal(err)
}
if oldVersion != wantOldVersion {
t.Fatalf("got %d, wanted %d", oldVersion, wantOldVersion)
}
if newVersion != wantNewVersion {
t.Fatalf("got %d, wanted %d", newVersion, wantNewVersion)
}
version, err := migrations.Version(db)
if err != nil {
t.Fatal(err)
}
if version != wantNewVersion {
t.Fatalf("got version %d, wanted %d", version, wantNewVersion)
}
}
}
func doNothing(db migrations.DB) error {
return nil
}
func doPanic(db migrations.DB) error {
panic("this migration should not be run")
}

View file

@ -0,0 +1,19 @@
package main
import (
"fmt"
"github.com/go-pg/migrations"
)
func init() {
migrations.Register(func(db migrations.DB) error {
fmt.Println("creating table my_table...")
_, err := db.Exec(`CREATE TABLE my_table()`)
return err
}, func(db migrations.DB) error {
fmt.Println("dropping table my_table...")
_, err := db.Exec(`DROP TABLE my_table`)
return err
})
}

19
vendor/github.com/go-pg/migrations/example/2_add_id.go generated vendored Normal file
View file

@ -0,0 +1,19 @@
package main
import (
"fmt"
"github.com/go-pg/migrations"
)
func init() {
migrations.Register(func(db migrations.DB) error {
fmt.Println("adding id column...")
_, err := db.Exec(`ALTER TABLE my_table ADD id serial`)
return err
}, func(db migrations.DB) error {
fmt.Println("dropping id column...")
_, err := db.Exec(`ALTER TABLE my_table DROP id`)
return err
})
}

View file

@ -0,0 +1,19 @@
package main
import (
"fmt"
"github.com/go-pg/migrations"
)
func init() {
migrations.Register(func(db migrations.DB) error {
fmt.Println("seeding my_table...")
_, err := db.Exec(`INSERT INTO my_table VALUES (1)`)
return err
}, func(db migrations.DB) error {
fmt.Println("truncating my_table...")
_, err := db.Exec(`TRUNCATE my_table`)
return err
})
}

64
vendor/github.com/go-pg/migrations/example/README.md generated vendored Normal file
View file

@ -0,0 +1,64 @@
# Example
You need to create database `pg_migrations_example` before running this example.
```bash
> psql -c "CREATE DATABASE pg_migrations_example"
CREATE DATABASE
> go run *.go init
version is 0
> go run *.go version
version is 0
> go run *.go
creating table my_table...
adding id column...
seeding my_table...
migrated from version 0 to 3
> go run *.go version
version is 3
> go run *.go reset
truncating my_table...
dropping id column...
dropping table my_table...
migrated from version 3 to 0
> go run *.go
creating table my_table...
adding id column...
seeding my_table...
migrated from version 0 to 3
> go run *.go down
truncating my_table...
migrated from version 3 to 2
> go run *.go version
version is 2
> go run *.go set_version 1
migrated from version 2 to 1
> go run *.go create add email to users
created migration 4_add_email_to_users.go
```
## Transactions
If you'd want to wrap the whole run in a big transaction, which may be the case if you have multi-statement migrations, the code in `main.go` should be slightly modified:
```go
var oldVersion, newVersion int64
err := db.RunInTransaction(func(tx *pg.Tx) (err error) {
oldVersion, newVersion, err = migrations.Run(tx, flag.Args()...)
return
})
if err != nil {
exitf(err.Error())
}
```

57
vendor/github.com/go-pg/migrations/example/main.go generated vendored Normal file
View file

@ -0,0 +1,57 @@
package main
import (
"flag"
"fmt"
"os"
"github.com/go-pg/migrations"
"github.com/go-pg/pg"
)
const usageText = `This program runs command on the db. Supported commands are:
- init - creates gopg_migrations table.
- up - runs all available migrations.
- down - reverts last migration.
- reset - reverts all migrations.
- version - prints current db version.
- set_version [version] - sets db version without running migrations.
Usage:
go run *.go <command> [args]
`
func main() {
flag.Usage = usage
flag.Parse()
db := pg.Connect(&pg.Options{
User: "postgres",
Database: "pg_migrations_example",
})
oldVersion, newVersion, err := migrations.Run(db, flag.Args()...)
if err != nil {
exitf(err.Error())
}
if newVersion != oldVersion {
fmt.Printf("migrated from version %d to %d\n", oldVersion, newVersion)
} else {
fmt.Printf("version is %d\n", oldVersion)
}
}
func usage() {
fmt.Printf(usageText)
flag.PrintDefaults()
os.Exit(2)
}
func errorf(s string, args ...interface{}) {
fmt.Fprintf(os.Stderr, s+"\n", args...)
}
func exitf(s string, args ...interface{}) {
errorf(s, args...)
os.Exit(1)
}

5
vendor/github.com/go-pg/migrations/export_test.go generated vendored Normal file
View file

@ -0,0 +1,5 @@
package migrations
func Set(ms []Migration) {
allMigrations = ms
}

264
vendor/github.com/go-pg/migrations/migrations.go generated vendored Normal file
View file

@ -0,0 +1,264 @@
package migrations
import (
"errors"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"regexp"
"runtime"
"sort"
"strconv"
"strings"
)
var allMigrations []Migration
type Migration struct {
Version int64
Up func(DB) error
Down func(DB) error
}
func (m *Migration) String() string {
return strconv.FormatInt(m.Version, 10)
}
// Register registers new database migration. Must be called
// from file with name like "1_initialize_db.go", where:
// - 1 - migration version;
// - initialize_db - comment.
func Register(up, down func(DB) error) error {
_, file, _, _ := runtime.Caller(1)
version, err := extractVersion(file)
if err != nil {
return err
}
allMigrations = append(allMigrations, Migration{
Version: version,
Up: up,
Down: down,
})
return nil
}
// Run runs command on the db. Supported commands are:
// - init - creates gopg_migrations table.
// - up - runs all available migrations.
// - down - reverts last migration.
// - reset - reverts all migrations.
// - version - prints current db version.
// - set_version - sets db version without running migrations.
func Run(db DB, a ...string) (oldVersion, newVersion int64, err error) {
// Make a copy so there are no side effects of sorting.
migrations := make([]Migration, len(allMigrations))
copy(migrations, allMigrations)
return RunMigrations(db, migrations, a...)
}
// RunMigrations is like Run, but accepts list of migrations.
func RunMigrations(db DB, migrations []Migration, a ...string) (oldVersion, newVersion int64, err error) {
sortMigrations(migrations)
var cmd string
if len(a) > 0 {
cmd = a[0]
}
if cmd == "init" {
err = createTables(db)
if err != nil {
return
}
cmd = "version"
}
oldVersion, err = Version(db)
if err != nil {
return
}
switch cmd {
case "create":
if len(a) < 2 {
fmt.Println("Please enter migration description")
return
}
var version int64
if len(migrations) > 0 {
version = migrations[len(migrations)-1].Version
}
filename := fmtMigrationFilename(version+1, strings.Join(a[1:], "_"))
err = createMigrationFile(filename)
if err != nil {
return
}
fmt.Println("created migration", filename)
return
case "version":
return
case "up", "":
for i := range migrations {
m := &migrations[i]
if m.Version <= oldVersion {
continue
}
err = m.Up(db)
if err != nil {
return
}
newVersion = m.Version
err = SetVersion(db, newVersion)
if err != nil {
return
}
}
return
case "down":
newVersion, err = down(db, migrations, oldVersion)
return
case "reset":
version := oldVersion
for {
newVersion, err = down(db, migrations, version)
if err != nil || newVersion == version {
return
}
version = newVersion
}
case "set_version":
if len(a) < 2 {
err = fmt.Errorf("set_version requires version as 2nd arg, e.g. set_version 42")
return
}
newVersion, err = strconv.ParseInt(a[1], 10, 64)
if err != nil {
return
}
err = SetVersion(db, newVersion)
return
default:
err = fmt.Errorf("unsupported command: %q", cmd)
return
}
}
func down(db DB, migrations []Migration, oldVersion int64) (newVersion int64, err error) {
if oldVersion == 0 {
return
}
var m *Migration
for i := len(migrations) - 1; i >= 0; i-- {
mm := &migrations[i]
if mm.Version <= oldVersion {
m = mm
break
}
}
if m == nil {
err = fmt.Errorf("migration %d not found\n", oldVersion)
return
}
if m.Down != nil {
err = m.Down(db)
if err != nil {
return
}
}
newVersion = m.Version - 1
err = SetVersion(db, newVersion)
return
}
func extractVersion(name string) (int64, error) {
base := filepath.Base(name)
if ext := filepath.Ext(base); ext != ".go" {
return 0, fmt.Errorf("can not extract version from %q", base)
}
idx := strings.IndexByte(base, '_')
if idx == -1 {
return 0, fmt.Errorf("can not extract version from %q", base)
}
n, err := strconv.ParseInt(base[:idx], 10, 64)
if err != nil {
return 0, err
}
if n <= 0 {
return 0, errors.New("version must be greater than zero")
}
return n, nil
}
type migrationSorter []Migration
func (ms migrationSorter) Len() int {
return len(ms)
}
func (ms migrationSorter) Swap(i, j int) {
ms[i], ms[j] = ms[j], ms[i]
}
func (ms migrationSorter) Less(i, j int) bool {
return ms[i].Version < ms[j].Version
}
func sortMigrations(migrations []Migration) {
ms := migrationSorter(migrations)
sort.Sort(ms)
}
var migrationNameRE = regexp.MustCompile(`[^a-z0-9]+`)
func fmtMigrationFilename(version int64, descr string) string {
descr = strings.ToLower(descr)
descr = migrationNameRE.ReplaceAllString(descr, "_")
return fmt.Sprintf("%d_%s.go", version, descr)
}
func createMigrationFile(filename string) error {
basepath, err := os.Getwd()
if err != nil {
return err
}
filename = path.Join(basepath, filename)
_, err = os.Stat(filename)
if !os.IsNotExist(err) {
return fmt.Errorf("file=%q already exists (%s)", filename, err)
}
return ioutil.WriteFile(filename, migrationTemplate, 0644)
}
var migrationTemplate = []byte(`package main
import (
"github.com/go-pg/migrations"
)
func init() {
migrations.Register(func(db migrations.DB) error {
_, err := db.Exec("")
return err
}, func(db migrations.DB) error {
_, err := db.Exec("")
return err
})
}
`)

24
vendor/github.com/go-pg/pg/.travis.yml generated vendored Normal file
View file

@ -0,0 +1,24 @@
dist: trusty
language: go
addons:
postgresql: "9.6"
go:
- 1.7
- 1.8
- tip
matrix:
allow_failures:
- go: tip
before_install:
- psql -U postgres -c "CREATE EXTENSION hstore"
install:
- go get github.com/jinzhu/inflection
- go get gopkg.in/check.v1
- go get github.com/onsi/ginkgo
- go get github.com/onsi/gomega

33
vendor/github.com/go-pg/pg/CHANGELOG.md generated vendored Normal file
View file

@ -0,0 +1,33 @@
# Changelog
## v6
- `types.Result` is renamed to `orm.Result`.
- Added `OnQueryProcessed` event that can be used to log / report queries timing. Query logger is removed.
- `orm.URLValues` is renamed to `orm.URLFilters`. It no longer adds ORDER clause.
- `orm.Pager` is renamed to `orm.Pagination`.
- Support for net.IP and net.IPNet.
- Support for context.Context.
- Bulk/multi updates.
- Query.WhereGroup for enclosing conditions in paretheses.
## v5
- All fields are nullable by default. `,null` tag is replaced with `,notnull`.
- `Result.Affected` renamed to `Result.RowsAffected`.
- Added `Result.RowsReturned`.
- `Create` renamed to `Insert`, `BeforeCreate` to `BeforeInsert`, `AfterCreate` to `AfterInsert`.
- Indexed placeholders support, e.g. `db.Exec("SELECT ?0 + ?0", 1)`.
- Named placeholders are evaluated when query is executed.
- Added Update and Delete hooks.
- Order reworked to quote column names. OrderExpr added to bypass Order quoting restrictions.
- Group reworked to quote column names. GroupExpr added to bypass Group quoting restrictions.
## v4
- `Options.Host` and `Options.Port` merged into `Options.Addr`.
- Added `Options.MaxRetries`. Now queries are not retried by default.
- `LoadInto` renamed to `Scan`, `ColumnLoader` renamed to `ColumnScanner`, LoadColumn renamed to ScanColumn, `NewRecord() interface{}` changed to `NewModel() ColumnScanner`, `AppendQuery(dst []byte) []byte` changed to `AppendValue(dst []byte, quote bool) ([]byte, error)`.
- Structs, maps and slices are marshalled to JSON by default.
- Added support for scanning slices, .e.g. scanning `[]int`.
- Added object relational mapping.

24
vendor/github.com/go-pg/pg/LICENSE generated vendored Normal file
View file

@ -0,0 +1,24 @@
Copyright (c) 2013 github.com/go-pg/pg Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

4
vendor/github.com/go-pg/pg/Makefile generated vendored Normal file
View file

@ -0,0 +1,4 @@
all:
go test ./...
go test ./... -short -race
go vet

155
vendor/github.com/go-pg/pg/README.md generated vendored Normal file
View file

@ -0,0 +1,155 @@
# PostgreSQL client and ORM for Golang
[![Build Status](https://travis-ci.org/go-pg/pg.svg?branch=master)](https://travis-ci.org/go-pg/pg)
[![GoDoc](https://godoc.org/github.com/go-pg/pg?status.svg)](https://godoc.org/github.com/go-pg/pg)
## Features:
- Basic types: integers, floats, string, bool, time.Time, net.IP, net.IPNet.
- sql.NullBool, sql.NullString, sql.NullInt64, sql.NullFloat64 and [pg.NullTime](http://godoc.org/github.com/go-pg/pg#NullTime).
- [sql.Scanner](http://golang.org/pkg/database/sql/#Scanner) and [sql/driver.Valuer](http://golang.org/pkg/database/sql/driver/#Valuer) interfaces.
- Structs, maps and arrays are marshalled as JSON by default.
- PostgreSQL multidimensional Arrays using [array tag](https://godoc.org/github.com/go-pg/pg#example-DB-Model-PostgresArrayStructTag) and [Array wrapper](https://godoc.org/github.com/go-pg/pg#example-Array).
- Hstore using [hstore tag](https://godoc.org/github.com/go-pg/pg#example-DB-Model-HstoreStructTag) and [Hstore wrapper](https://godoc.org/github.com/go-pg/pg#example-Hstore).
- All struct fields are nullable by default and zero values (empty string, 0, zero time) are marshalled as SQL `NULL`. `sql:",notnull"` tag is used to reverse this behaviour.
- [Transactions](http://godoc.org/github.com/go-pg/pg#example-DB-Begin).
- [Prepared statements](http://godoc.org/github.com/go-pg/pg#example-DB-Prepare).
- [Notifications](http://godoc.org/github.com/go-pg/pg#example-Listener) using `LISTEN` and `NOTIFY`.
- [Copying data](http://godoc.org/github.com/go-pg/pg#example-DB-CopyFrom) using `COPY FROM` and `COPY TO`.
- [Timeouts](http://godoc.org/github.com/go-pg/pg#Options).
- Automatic connection pooling with [circuit breaker](https://en.wikipedia.org/wiki/Circuit_breaker_design_pattern) support.
- Queries retries on network errors.
- Working with models using [ORM](https://godoc.org/github.com/go-pg/pg#example-DB-Model) and [SQL](https://godoc.org/github.com/go-pg/pg#example-DB-Query).
- Scanning variables using [ORM](https://godoc.org/github.com/go-pg/pg#example-DB-Select-SomeColumnsIntoVars) and [SQL](https://godoc.org/github.com/go-pg/pg#example-Scan).
- [SelectOrInsert](https://godoc.org/github.com/go-pg/pg#example-DB-Insert-SelectOrInsert) using on-conflict.
- [INSERT ... ON CONFLICT DO UPDATE](https://godoc.org/github.com/go-pg/pg#example-DB-Insert-OnConflictDoUpdate) using ORM.
- Bulk/batch [inserts](https://godoc.org/github.com/go-pg/pg#example-DB-Insert-BulkInsert) and [updates](https://godoc.org/github.com/go-pg/pg#example-DB-Update-BulkUpdate).
- Common table expressions using [WITH](https://godoc.org/github.com/go-pg/pg#example-DB-Select-With) and [WrapWith](https://godoc.org/github.com/go-pg/pg#example-DB-Select-WrapWith).
- [CountEstimate](https://godoc.org/github.com/go-pg/pg#example-DB-Model-CountEstimate) using `EXPLAIN` to get [estimated number of matching rows](https://wiki.postgresql.org/wiki/Count_estimate).
- ORM supports [has one](https://godoc.org/github.com/go-pg/pg#example-DB-Model-HasOne), [belongs to](https://godoc.org/github.com/go-pg/pg#example-DB-Model-BelongsTo), [has many](https://godoc.org/github.com/go-pg/pg#example-DB-Model-HasMany), and [many to many](https://godoc.org/github.com/go-pg/pg#example-DB-Model-ManyToMany) with composite/multi-column primary keys.
- [Creating tables from structs](https://godoc.org/github.com/go-pg/pg#example-DB-CreateTable).
- [Pagination](https://godoc.org/github.com/go-pg/pg/orm#Pagination) and [URL filters](https://godoc.org/github.com/go-pg/pg/orm#URLFilters) helpers.
- [Migrations](https://github.com/go-pg/migrations).
- [Sharding](https://github.com/go-pg/sharding).
## Get Started
- [Wiki](https://github.com/go-pg/pg/wiki)
- [API docs](http://godoc.org/github.com/go-pg/pg)
- [Examples](http://godoc.org/github.com/go-pg/pg#pkg-examples)
## Look & Feel
```go
package pg_test
import (
"fmt"
"github.com/go-pg/pg"
)
type User struct {
Id int64
Name string
Emails []string
}
func (u User) String() string {
return fmt.Sprintf("User<%d %s %v>", u.Id, u.Name, u.Emails)
}
type Story struct {
Id int64
Title string
AuthorId int64
Author *User
}
func (s Story) String() string {
return fmt.Sprintf("Story<%d %s %s>", s.Id, s.Title, s.Author)
}
func ExampleDB_Model() {
db := pg.Connect(&pg.Options{
User: "postgres",
})
err := createSchema(db)
if err != nil {
panic(err)
}
user1 := &User{
Name: "admin",
Emails: []string{"admin1@admin", "admin2@admin"},
}
err = db.Insert(user1)
if err != nil {
panic(err)
}
err = db.Insert(&User{
Name: "root",
Emails: []string{"root1@root", "root2@root"},
})
if err != nil {
panic(err)
}
story1 := &Story{
Title: "Cool story",
AuthorId: user1.Id,
}
err = db.Insert(story1)
if err != nil {
panic(err)
}
// Select user by primary key.
user := User{Id: user1.Id}
err = db.Select(&user)
if err != nil {
panic(err)
}
// Select all users.
var users []User
err = db.Model(&users).Select()
if err != nil {
panic(err)
}
// Select story and associated author in one query.
var story Story
err = db.Model(&story).
Column("story.*", "Author").
Where("story.id = ?", story1.Id).
Select()
if err != nil {
panic(err)
}
fmt.Println(user)
fmt.Println(users)
fmt.Println(story)
// Output: User<1 admin [admin1@admin admin2@admin]>
// [User<1 admin [admin1@admin admin2@admin]> User<2 root [root1@root root2@root]>]
// Story<1 Cool story User<1 admin [admin1@admin admin2@admin]>>
}
func createSchema(db *pg.DB) error {
for _, model := range []interface{}{&User{}, &Story{}} {
err := db.CreateTable(model, nil)
if err != nil {
return err
}
}
return nil
}
```
## See also
- [Golang msgpack](https://github.com/vmihailenco/msgpack)
- [Golang message task queue](https://github.com/go-msgqueue/msgqueue)

548
vendor/github.com/go-pg/pg/bench_test.go generated vendored Normal file
View file

@ -0,0 +1,548 @@
package pg_test
import (
"fmt"
"math/rand"
"strconv"
"sync"
"testing"
"time"
"github.com/go-pg/pg"
"github.com/go-pg/pg/orm"
)
func benchmarkDB() *pg.DB {
return pg.Connect(&pg.Options{
User: "postgres",
Database: "postgres",
DialTimeout: 30 * time.Second,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
PoolSize: 10,
PoolTimeout: 30 * time.Second,
})
}
func BenchmarkQueryRowsGopgDiscard(b *testing.B) {
seedDB()
db := benchmarkDB()
defer db.Close()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_, err := db.Query(pg.Discard, `SELECT * FROM records LIMIT 100`)
if err != nil {
b.Fatal(err)
}
}
})
}
func BenchmarkQueryRowsGopgOptimized(b *testing.B) {
seedDB()
db := benchmarkDB()
defer db.Close()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
var rs OptRecords
_, err := db.Query(&rs, `SELECT * FROM records LIMIT 100`)
if err != nil {
b.Fatal(err)
}
if len(rs.C) != 100 {
b.Fatalf("got %d, wanted 100", len(rs.C))
}
}
})
}
func BenchmarkQueryRowsGopgReflect(b *testing.B) {
seedDB()
db := benchmarkDB()
defer db.Close()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
var rs []Record
_, err := db.Query(&rs, `SELECT * FROM records LIMIT 100`)
if err != nil {
b.Fatal(err)
}
if len(rs) != 100 {
b.Fatalf("got %d, wanted 100", len(rs))
}
}
})
}
func BenchmarkQueryRowsGopgORM(b *testing.B) {
seedDB()
db := benchmarkDB()
defer db.Close()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
var rs []Record
err := db.Model(&rs).Limit(100).Select()
if err != nil {
b.Fatal(err)
}
if len(rs) != 100 {
b.Fatalf("got %d, wanted 100", len(rs))
}
}
})
}
func BenchmarkModelHasOneGopg(b *testing.B) {
seedDB()
db := benchmarkDB()
defer db.Close()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
var books []Book
err := db.Model(&books).Column("book.*", "Author").Limit(100).Select()
if err != nil {
b.Fatal(err)
}
if len(books) != 100 {
b.Fatalf("got %d, wanted 100", len(books))
}
}
})
}
func BenchmarkModelHasManyGopg(b *testing.B) {
seedDB()
db := benchmarkDB()
defer db.Close()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
var books []Book
err := db.Model(&books).Column("book.*", "Translations").Limit(100).Select()
if err != nil {
b.Fatal(err)
}
if len(books) != 100 {
b.Fatalf("got %d, wanted 100", len(books))
}
for _, book := range books {
if len(book.Translations) != 10 {
b.Fatalf("got %d, wanted 10", len(book.Translations))
}
}
}
})
}
func BenchmarkModelHasMany2ManyGopg(b *testing.B) {
seedDB()
db := benchmarkDB()
defer db.Close()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
var books []Book
err := db.Model(&books).
Column("book.*", "Genres").
Limit(100).
Select()
if err != nil {
b.Fatal(err)
}
if len(books) != 100 {
b.Fatalf("got %d, wanted 100", len(books))
}
for _, book := range books {
if len(book.Genres) != 10 {
b.Fatalf("got %d, wanted 10", len(book.Genres))
}
}
}
})
}
func BenchmarkQueryRow(b *testing.B) {
db := benchmarkDB()
defer db.Close()
b.ResetTimer()
for i := 0; i < b.N; i++ {
var dst numLoader
_, err := db.QueryOne(&dst, `SELECT ?::bigint AS num`, 1)
if err != nil {
b.Fatal(err)
}
if dst.Num != 1 {
b.Fatalf("got %d, wanted 1", dst.Num)
}
}
}
func BenchmarkQueryRowStmt(b *testing.B) {
db := benchmarkDB()
defer db.Close()
stmt, err := db.Prepare(`SELECT $1::bigint AS num`)
if err != nil {
b.Fatal(err)
}
defer stmt.Close()
b.ResetTimer()
for i := 0; i < b.N; i++ {
var dst numLoader
_, err := stmt.QueryOne(&dst, 1)
if err != nil {
b.Fatal(err)
}
if dst.Num != 1 {
b.Fatalf("got %d, wanted 1", dst.Num)
}
}
}
func BenchmarkQueryRowScan(b *testing.B) {
db := benchmarkDB()
defer db.Close()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
var n int64
_, err := db.QueryOne(pg.Scan(&n), `SELECT ? AS num`, 1)
if err != nil {
b.Fatal(err)
}
if n != 1 {
b.Fatalf("got %d, wanted 1", n)
}
}
})
}
func BenchmarkQueryRowStmtScan(b *testing.B) {
db := benchmarkDB()
defer db.Close()
stmt, err := db.Prepare(`SELECT $1::bigint AS num`)
if err != nil {
b.Fatal(err)
}
defer stmt.Close()
b.ResetTimer()
for i := 0; i < b.N; i++ {
var n int64
_, err := stmt.QueryOne(pg.Scan(&n), 1)
if err != nil {
b.Fatal(err)
}
if n != 1 {
b.Fatalf("got %d, wanted 1", n)
}
}
}
func BenchmarkExec(b *testing.B) {
db := benchmarkDB()
defer db.Close()
qs := []string{
`DROP TABLE IF EXISTS exec_test`,
`CREATE TABLE exec_test(id bigint, name varchar(500))`,
}
for _, q := range qs {
_, err := db.Exec(q)
if err != nil {
b.Fatal(err)
}
}
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_, err := db.Exec(`INSERT INTO exec_test (id, name) VALUES (?, ?)`, 1, "hello world")
if err != nil {
b.Fatal(err)
}
}
})
}
func BenchmarkExecWithError(b *testing.B) {
db := benchmarkDB()
defer db.Close()
qs := []string{
`DROP TABLE IF EXISTS exec_with_error_test`,
`CREATE TABLE exec_with_error_test(id bigint PRIMARY KEY, name varchar(500))`,
}
for _, q := range qs {
_, err := db.Exec(q)
if err != nil {
b.Fatal(err)
}
}
_, err := db.Exec(`
INSERT INTO exec_with_error_test(id, name) VALUES(?, ?)
`, 1, "hello world")
if err != nil {
b.Fatal(err)
}
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_, err := db.Exec(`INSERT INTO exec_with_error_test(id) VALUES(?)`, 1)
if err == nil {
b.Fatalf("got nil error, expected integrity violation")
} else if pgErr, ok := err.(pg.Error); !ok || !pgErr.IntegrityViolation() {
b.Fatalf("got %s, expected integrity violation", err)
}
}
})
}
func BenchmarkExecStmt(b *testing.B) {
db := benchmarkDB()
defer db.Close()
_, err := db.Exec(`CREATE TEMP TABLE statement_exec(id bigint, name varchar(500))`)
if err != nil {
b.Fatal(err)
}
stmt, err := db.Prepare(`INSERT INTO statement_exec (id, name) VALUES ($1, $2)`)
if err != nil {
b.Fatal(err)
}
defer stmt.Close()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := stmt.Exec(1, "hello world")
if err != nil {
b.Fatal(err)
}
}
}
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
func randSeq(n int) string {
b := make([]rune, n)
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
return string(b)
}
type Record struct {
Num1, Num2, Num3 int64
Str1, Str2, Str3 string
}
func (r *Record) GetNum1() int64 {
return r.Num1
}
func (r *Record) GetNum2() int64 {
return r.Num2
}
func (r *Record) GetNum3() int64 {
return r.Num3
}
func (r *Record) GetStr1() string {
return r.Str1
}
func (r *Record) GetStr2() string {
return r.Str2
}
func (r *Record) GetStr3() string {
return r.Str3
}
type OptRecord struct {
Num1, Num2, Num3 int64
Str1, Str2, Str3 string
}
var _ orm.ColumnScanner = (*OptRecord)(nil)
func (r *OptRecord) ScanColumn(colIdx int, colName string, b []byte) error {
var err error
switch colName {
case "num1":
r.Num1, err = strconv.ParseInt(string(b), 10, 64)
case "num2":
r.Num2, err = strconv.ParseInt(string(b), 10, 64)
case "num3":
r.Num3, err = strconv.ParseInt(string(b), 10, 64)
case "str1":
r.Str1 = string(b)
case "str2":
r.Str2 = string(b)
case "str3":
r.Str3 = string(b)
default:
return fmt.Errorf("unknown column: %q", colName)
}
return err
}
type OptRecords struct {
C []OptRecord
}
func (rs *OptRecords) NewModel() orm.ColumnScanner {
rs.C = append(rs.C, OptRecord{})
return &rs.C[len(rs.C)-1]
}
func (OptRecords) AddModel(_ orm.ColumnScanner) error {
return nil
}
func (OptRecords) AfterSelect(_ orm.DB) error {
return nil
}
var seedDBOnce sync.Once
func seedDB() {
seedDBOnce.Do(func() {
if err := _seedDB(); err != nil {
panic(err)
}
})
}
func _seedDB() error {
db := benchmarkDB()
defer db.Close()
_, err := db.Exec(`DROP TABLE IF EXISTS records`)
if err != nil {
return err
}
_, err = db.Exec(`
CREATE TABLE records(
num1 serial,
num2 serial,
num3 serial,
str1 text,
str2 text,
str3 text
)
`)
if err != nil {
return err
}
for i := 0; i < 1000; i++ {
_, err := db.Exec(`
INSERT INTO records (str1, str2, str3) VALUES (?, ?, ?)
`, randSeq(100), randSeq(200), randSeq(300))
if err != nil {
return err
}
}
err = createTestSchema(db)
if err != nil {
return err
}
for i := 1; i < 100; i++ {
genre := Genre{
Id: i,
Name: fmt.Sprintf("genre %d", i),
}
err = db.Insert(&genre)
if err != nil {
return err
}
author := Author{
ID: i,
Name: fmt.Sprintf("author %d", i),
}
err = db.Insert(&author)
if err != nil {
return err
}
}
for i := 1; i <= 1000; i++ {
err = db.Insert(&Book{
Id: i,
Title: fmt.Sprintf("book %d", i),
AuthorID: rand.Intn(99) + 1,
CreatedAt: time.Now(),
})
if err != nil {
return err
}
for j := 1; j <= 10; j++ {
err = db.Insert(&BookGenre{
BookId: i,
GenreId: rand.Intn(99) + 1,
})
if err != nil {
return err
}
err = db.Insert(&Translation{
BookId: i,
Lang: fmt.Sprintf("%d", j),
})
if err != nil {
return err
}
}
}
return nil
}

444
vendor/github.com/go-pg/pg/conv_test.go generated vendored Normal file
View file

@ -0,0 +1,444 @@
package pg_test
import (
"database/sql"
"database/sql/driver"
"encoding/json"
"fmt"
"math"
"net"
"reflect"
"testing"
"time"
"github.com/go-pg/pg"
"github.com/go-pg/pg/orm"
"github.com/go-pg/pg/types"
)
type JSONMap map[string]interface{}
func (m *JSONMap) Scan(b interface{}) error {
if b == nil {
*m = nil
return nil
}
return json.Unmarshal(b.([]byte), m)
}
func (m JSONMap) Value() (driver.Value, error) {
b, err := json.Marshal(m)
if err != nil {
return nil, err
}
return string(b), nil
}
type (
StringSlice []string
IntSlice []int
Int64Slice []int64
Float64Slice []float64
)
type Struct struct {
Foo string
}
type conversionTest struct {
i int
src, dst, wanted interface{}
pgtype string
wanterr string
wantnil bool
wantzero bool
}
func unwrap(v interface{}) interface{} {
if arr, ok := v.(*types.Array); ok {
return arr.Value()
}
if hstore, ok := v.(*types.Hstore); ok {
return hstore.Value()
}
return v
}
func deref(vi interface{}) interface{} {
v := reflect.ValueOf(vi)
for v.Kind() == reflect.Ptr {
v = v.Elem()
}
if v.IsValid() {
return v.Interface()
}
return nil
}
func zero(v interface{}) interface{} {
return reflect.Zero(reflect.ValueOf(v).Elem().Type()).Interface()
}
func (test *conversionTest) String() string {
return fmt.Sprintf("#%d src=%#v dst=%#v", test.i, test.src, test.dst)
}
func (test *conversionTest) Assert(t *testing.T, err error) {
if test.wanterr != "" {
if err == nil || err.Error() != test.wanterr {
t.Fatalf("got error %q, wanted %q (%s)", err, test.wanterr, test)
}
return
}
if err != nil {
t.Fatalf("got error %q, wanted nil (%s)", err, test)
}
dst := reflect.Indirect(reflect.ValueOf(unwrap(test.dst))).Interface()
if test.wantnil {
dstValue := reflect.ValueOf(dst)
if !dstValue.IsValid() {
return
}
if dstValue.IsNil() {
return
}
t.Fatalf("got %#v, wanted nil (%s)", dst, test)
return
}
// Remove any intermediate pointers to compare values.
dst = deref(unwrap(dst))
src := deref(unwrap(test.src))
if test.wantzero {
dstValue := reflect.ValueOf(dst)
switch dstValue.Kind() {
case reflect.Slice, reflect.Map:
if dstValue.IsNil() {
t.Fatalf("got nil, wanted zero value")
}
if dstValue.Len() != 0 {
t.Fatalf("got %d items, wanted 0", dstValue.Len())
}
default:
zero := zero(test.dst)
if dst != zero {
t.Fatalf("%#v != %#v (%s)", dst, zero, test)
}
}
return
}
if dstTime, ok := dst.(time.Time); ok {
srcTime := src.(time.Time)
if dstTime.Unix() != srcTime.Unix() {
t.Fatalf("%#v != %#v", dstTime, srcTime)
}
return
}
if dstTimes, ok := dst.([]time.Time); ok {
srcTimes := src.([]time.Time)
for i, dstTime := range dstTimes {
srcTime := srcTimes[i]
if dstTime.Unix() != srcTime.Unix() {
t.Fatalf("%#v != %#v", dstTime, srcTime)
}
}
return
}
wanted := test.wanted
if wanted == nil {
wanted = src
}
if !reflect.DeepEqual(dst, wanted) {
t.Fatalf("%#v != %#v (%s)", dst, wanted, test)
}
}
func conversionTests() []conversionTest {
return []conversionTest{
{src: nil, dst: nil, wanterr: "pg: Scan(nil)"},
{src: nil, dst: new(uintptr), wanterr: "pg: Scan(unsupported uintptr)"},
{src: nil, dst: true, pgtype: "bool", wanterr: "pg: Scan(non-pointer bool)"},
{src: nil, dst: new(*bool), pgtype: "bool", wantnil: true},
{src: nil, dst: new(bool), pgtype: "bool", wantzero: true},
{src: true, dst: new(bool), pgtype: "bool"},
{src: true, dst: new(*bool), pgtype: "bool"},
{src: 1, dst: new(bool), wanted: true},
{src: nil, dst: "", pgtype: "text", wanterr: "pg: Scan(non-pointer string)"},
{src: nil, dst: new(string), pgtype: "text", wantzero: true},
{src: nil, dst: new(*string), pgtype: "text", wantnil: true},
{src: "hello world", dst: new(string), pgtype: "text"},
{src: "hello world", dst: new(*string), pgtype: "text"},
{src: "'\"\000", dst: new(string), wanted: `'"`, pgtype: "text"},
{src: nil, dst: []byte(nil), pgtype: "bytea", wanterr: "pg: Scan(non-pointer []uint8)"},
{src: nil, dst: new([]byte), pgtype: "bytea", wantnil: true},
{src: []byte("hello world\000"), dst: new([]byte), pgtype: "bytea"},
{src: []byte{}, dst: new([]byte), pgtype: "bytea", wantzero: true},
{src: nil, dst: int8(0), pgtype: "smallint", wanterr: "pg: Scan(non-pointer int8)"},
{src: nil, dst: new(int8), pgtype: "smallint", wantzero: true},
{src: int8(math.MaxInt8), dst: new(int8), pgtype: "smallint"},
{src: int8(math.MaxInt8), dst: new(*int8), pgtype: "smallint"},
{src: int8(math.MinInt8), dst: new(int8), pgtype: "smallint"},
{src: nil, dst: int16(0), pgtype: "smallint", wanterr: "pg: Scan(non-pointer int16)"},
{src: nil, dst: new(int16), pgtype: "smallint", wantzero: true},
{src: int16(math.MaxInt16), dst: new(int16), pgtype: "smallint"},
{src: int16(math.MaxInt16), dst: new(*int16), pgtype: "smallint"},
{src: int16(math.MinInt16), dst: new(int16), pgtype: "smallint"},
{src: nil, dst: int32(0), pgtype: "int", wanterr: "pg: Scan(non-pointer int32)"},
{src: nil, dst: new(int32), pgtype: "int", wantzero: true},
{src: int32(math.MaxInt32), dst: new(int32), pgtype: "int"},
{src: int32(math.MaxInt32), dst: new(*int32), pgtype: "int"},
{src: int32(math.MinInt32), dst: new(int32), pgtype: "int"},
{src: nil, dst: int64(0), pgtype: "bigint", wanterr: "pg: Scan(non-pointer int64)"},
{src: nil, dst: new(int64), pgtype: "bigint", wantzero: true},
{src: int64(math.MaxInt64), dst: new(int64), pgtype: "bigint"},
{src: int64(math.MaxInt64), dst: new(*int64), pgtype: "bigint"},
{src: int64(math.MinInt64), dst: new(int64), pgtype: "bigint"},
{src: nil, dst: int(0), pgtype: "bigint", wanterr: "pg: Scan(non-pointer int)"},
{src: nil, dst: new(int), pgtype: "bigint", wantzero: true},
{src: int(math.MaxInt64), dst: new(int), pgtype: "bigint"},
{src: int(math.MaxInt64), dst: new(*int), pgtype: "bigint"},
{src: int(math.MinInt32), dst: new(int), pgtype: "bigint"},
{src: nil, dst: uint8(0), pgtype: "smallint", wanterr: "pg: Scan(non-pointer uint8)"},
{src: nil, dst: new(uint8), pgtype: "smallint", wantzero: true},
{src: uint8(math.MaxUint8), dst: new(uint8), pgtype: "smallint"},
{src: uint8(math.MaxUint8), dst: new(*uint8), pgtype: "smallint"},
{src: nil, dst: uint16(0), pgtype: "smallint", wanterr: "pg: Scan(non-pointer uint16)"},
{src: nil, dst: new(uint16), pgtype: "smallint", wantzero: true},
{src: uint16(math.MaxUint16), dst: new(uint16), pgtype: "int"},
{src: uint16(math.MaxUint16), dst: new(*uint16), pgtype: "int"},
{src: nil, dst: uint32(0), pgtype: "bigint", wanterr: "pg: Scan(non-pointer uint32)"},
{src: nil, dst: new(uint32), pgtype: "bigint", wantzero: true},
{src: uint32(math.MaxUint32), dst: new(uint32), pgtype: "bigint"},
{src: uint32(math.MaxUint32), dst: new(*uint32), pgtype: "bigint"},
{src: nil, dst: uint64(0), pgtype: "bigint", wanterr: "pg: Scan(non-pointer uint64)"},
{src: nil, dst: new(uint64), pgtype: "bigint", wantzero: true},
{src: uint64(math.MaxUint64), dst: new(uint64)},
{src: uint64(math.MaxUint64), dst: new(*uint64)},
{src: uint64(math.MaxUint32), dst: new(uint64), pgtype: "bigint"},
{src: nil, dst: uint(0), pgtype: "smallint", wanterr: "pg: Scan(non-pointer uint)"},
{src: nil, dst: new(uint), pgtype: "bigint", wantzero: true},
{src: uint(math.MaxUint64), dst: new(uint)},
{src: uint(math.MaxUint64), dst: new(*uint)},
{src: uint(math.MaxUint32), dst: new(uint), pgtype: "bigint"},
{src: nil, dst: float32(0), pgtype: "decimal", wanterr: "pg: Scan(non-pointer float32)"},
{src: nil, dst: new(float32), pgtype: "decimal", wantzero: true},
{src: float32(math.MaxFloat32), dst: new(float32), pgtype: "decimal"},
{src: float32(math.MaxFloat32), dst: new(*float32), pgtype: "decimal"},
{src: float32(math.SmallestNonzeroFloat32), dst: new(float32), pgtype: "decimal"},
{src: nil, dst: float64(0), pgtype: "decimal", wanterr: "pg: Scan(non-pointer float64)"},
{src: nil, dst: new(float64), pgtype: "decimal", wantzero: true},
{src: float64(math.MaxFloat64), dst: new(float64), pgtype: "decimal"},
{src: float64(math.MaxFloat64), dst: new(*float64), pgtype: "decimal"},
{src: float64(math.SmallestNonzeroFloat64), dst: new(float64), pgtype: "decimal"},
{src: nil, dst: []int(nil), pgtype: "jsonb", wanterr: "pg: Scan(non-pointer []int)"},
{src: nil, dst: new([]int), pgtype: "jsonb", wantnil: true},
{src: []int(nil), dst: new([]int), pgtype: "jsonb", wantnil: true},
{src: []int{}, dst: new([]int), pgtype: "jsonb", wantzero: true},
{src: []int{1, 2, 3}, dst: new([]int), pgtype: "jsonb"},
{src: IntSlice{1, 2, 3}, dst: new(IntSlice), pgtype: "jsonb"},
{src: nil, dst: pg.Array([]int(nil)), pgtype: "int[]", wanterr: "pg: Scan(non-pointer []int)"},
{src: pg.Array([]int(nil)), dst: pg.Array(new([]int)), pgtype: "int[]", wantnil: true},
{src: pg.Array([]int{}), dst: pg.Array(new([]int)), pgtype: "int[]"},
{src: pg.Array([]int{1, 2, 3}), dst: pg.Array(new([]int)), pgtype: "int[]"},
{src: nil, dst: pg.Array([]int64(nil)), pgtype: "bigint[]", wanterr: "pg: Scan(non-pointer []int64)"},
{src: nil, dst: pg.Array(new([]int64)), pgtype: "bigint[]", wantnil: true},
{src: pg.Array([]int64(nil)), dst: pg.Array(new([]int64)), pgtype: "bigint[]", wantnil: true},
{src: pg.Array([]int64{}), dst: pg.Array(new([]int64)), pgtype: "bigint[]"},
{src: pg.Array([]int64{1, 2, 3}), dst: pg.Array(new([]int64)), pgtype: "bigint[]"},
{src: nil, dst: pg.Array([]float64(nil)), pgtype: "decimal[]", wanterr: "pg: Scan(non-pointer []float64)"},
{src: nil, dst: pg.Array(new([]float64)), pgtype: "decimal[]", wantnil: true},
{src: pg.Array([]float64(nil)), dst: pg.Array(new([]float64)), pgtype: "decimal[]", wantnil: true},
{src: pg.Array([]float64{}), dst: pg.Array(new([]float64)), pgtype: "decimal[]"},
{src: pg.Array([]float64{1.1, 2.22, 3.333}), dst: pg.Array(new([]float64)), pgtype: "decimal[]"},
{src: nil, dst: pg.Array([]string(nil)), pgtype: "text[]", wanterr: "pg: Scan(non-pointer []string)"},
{src: nil, dst: pg.Array(new([]string)), pgtype: "text[]", wantnil: true},
{src: pg.Array([]string(nil)), dst: pg.Array(new([]string)), pgtype: "text[]", wantnil: true},
{src: pg.Array([]string{}), dst: pg.Array(new([]string)), pgtype: "text[]"},
{src: pg.Array([]string{"one", "two", "three"}), dst: pg.Array(new([]string)), pgtype: "text[]"},
{src: pg.Array([]string{`'"{}`}), dst: pg.Array(new([]string)), pgtype: "text[]"},
{src: nil, dst: pg.Array([][]string(nil)), pgtype: "text[][]", wanterr: "pg: Scan(non-pointer [][]string)"},
{src: nil, dst: pg.Array(new([][]string)), pgtype: "text[][]", wantnil: true},
{src: pg.Array([][]string(nil)), dst: pg.Array(new([]string)), pgtype: "text[][]", wantnil: true},
{src: pg.Array([][]string{}), dst: pg.Array(new([][]string)), pgtype: "text[][]"},
{src: pg.Array([][]string{{"one", "two"}, {"three", "four"}}), dst: pg.Array(new([][]string)), pgtype: "text[][]"},
{src: pg.Array([][]string{{`'"\{}`}}), dst: pg.Array(new([][]string)), pgtype: "text[][]"},
{src: nil, dst: pg.Hstore(map[string]string(nil)), pgtype: "hstore", wanterr: "pg: Scan(non-pointer map[string]string)"},
{src: nil, dst: pg.Hstore(new(map[string]string)), pgtype: "hstore", wantnil: true},
{src: pg.Hstore(map[string]string(nil)), dst: pg.Hstore(new(map[string]string)), pgtype: "hstore", wantnil: true},
{src: pg.Hstore(map[string]string{}), dst: pg.Hstore(new(map[string]string)), pgtype: "hstore"},
{src: pg.Hstore(map[string]string{"foo": "bar"}), dst: pg.Hstore(new(map[string]string)), pgtype: "hstore"},
{src: pg.Hstore(map[string]string{`'"\{}=>`: `'"\{}=>`}), dst: pg.Hstore(new(map[string]string)), pgtype: "hstore"},
{src: nil, dst: sql.NullBool{}, pgtype: "bool", wanterr: "pg: Scan(non-pointer sql.NullBool)"},
{src: nil, dst: new(*sql.NullBool), pgtype: "bool", wantnil: true},
{src: nil, dst: new(sql.NullBool), pgtype: "bool", wanted: sql.NullBool{}},
{src: &sql.NullBool{}, dst: new(sql.NullBool), pgtype: "bool"},
{src: &sql.NullBool{Valid: true}, dst: new(sql.NullBool), pgtype: "bool"},
{src: &sql.NullBool{Valid: true, Bool: true}, dst: new(sql.NullBool), pgtype: "bool"},
{src: &sql.NullString{}, dst: new(sql.NullString), pgtype: "text"},
{src: &sql.NullString{Valid: true}, dst: new(sql.NullString), pgtype: "text"},
{src: &sql.NullString{Valid: true, String: "foo"}, dst: new(sql.NullString), pgtype: "text"},
{src: &sql.NullInt64{}, dst: new(sql.NullInt64), pgtype: "bigint"},
{src: &sql.NullInt64{Valid: true}, dst: new(sql.NullInt64), pgtype: "bigint"},
{src: &sql.NullInt64{Valid: true, Int64: math.MaxInt64}, dst: new(sql.NullInt64), pgtype: "bigint"},
{src: &sql.NullFloat64{}, dst: new(sql.NullFloat64), pgtype: "decimal"},
{src: &sql.NullFloat64{Valid: true}, dst: new(sql.NullFloat64), pgtype: "decimal"},
{src: &sql.NullFloat64{Valid: true, Float64: math.MaxFloat64}, dst: new(sql.NullFloat64), pgtype: "decimal"},
{src: nil, dst: customStrSlice{}, wanterr: "pg: Scan(non-pointer pg_test.customStrSlice)"},
{src: nil, dst: new(customStrSlice), wantnil: true},
{src: nil, dst: new(*customStrSlice), wantnil: true},
{src: customStrSlice{}, dst: new(customStrSlice), wantzero: true},
{src: customStrSlice{"one", "two"}, dst: new(customStrSlice)},
{src: nil, dst: time.Time{}, wanterr: "pg: Scan(non-pointer time.Time)"},
{src: nil, dst: new(time.Time), pgtype: "timestamptz", wantzero: true},
{src: nil, dst: new(*time.Time), pgtype: "timestamptz", wantnil: true},
{src: time.Now(), dst: new(time.Time), pgtype: "timestamptz"},
{src: time.Now(), dst: new(*time.Time), pgtype: "timestamptz"},
{src: time.Now().UTC(), dst: new(time.Time), pgtype: "timestamptz"},
{src: time.Time{}, dst: new(time.Time), pgtype: "timestamptz"},
{src: nil, dst: pg.Array([]time.Time(nil)), pgtype: "timestamptz[]", wanterr: "pg: Scan(non-pointer []time.Time)"},
{src: nil, dst: pg.Array(new([]time.Time)), pgtype: "timestamptz[]", wantnil: true},
{src: pg.Array([]time.Time(nil)), dst: pg.Array(new([]time.Time)), pgtype: "timestamptz[]", wantnil: true},
{src: pg.Array([]time.Time{}), dst: pg.Array(new([]time.Time)), pgtype: "timestamptz[]"},
{src: pg.Array([]time.Time{time.Now(), time.Now(), time.Now()}), dst: pg.Array(new([]time.Time)), pgtype: "timestamptz[]"},
{src: nil, dst: pg.Ints{}, wanterr: "pg: Scan(non-pointer pg.Ints)"},
{src: 1, dst: new(pg.Ints), wanted: pg.Ints{1}},
{src: nil, dst: pg.Strings{}, wanterr: "pg: Scan(non-pointer pg.Strings)"},
{src: "hello", dst: new(pg.Strings), wanted: pg.Strings{"hello"}},
{src: nil, dst: pg.IntSet{}, wanterr: "pg: Scan(non-pointer pg.IntSet)"},
{src: 1, dst: new(pg.IntSet), wanted: pg.IntSet{1: struct{}{}}},
{src: nil, dst: JSONMap{}, pgtype: "json", wanterr: "pg: Scan(non-pointer pg_test.JSONMap)"},
{src: nil, dst: new(JSONMap), pgtype: "json", wantnil: true},
{src: nil, dst: new(*JSONMap), pgtype: "json", wantnil: true},
{src: JSONMap{}, dst: new(JSONMap), pgtype: "json"},
{src: JSONMap{}, dst: new(*JSONMap), pgtype: "json"},
{src: JSONMap{"foo": "bar"}, dst: new(JSONMap), pgtype: "json"},
{src: `{"foo": "bar"}`, dst: new(JSONMap), pgtype: "json", wanted: JSONMap{"foo": "bar"}},
{src: nil, dst: Struct{}, pgtype: "json", wanterr: "pg: Scan(non-pointer pg_test.Struct)"},
{src: nil, dst: new(*Struct), pgtype: "json", wantnil: true},
{src: nil, dst: new(Struct), pgtype: "json", wantzero: true},
{src: Struct{}, dst: new(Struct), pgtype: "json"},
{src: Struct{Foo: "bar"}, dst: new(Struct), pgtype: "json"},
{src: `{"foo": "bar"}`, dst: new(Struct), wanted: Struct{Foo: "bar"}},
{src: nil, dst: new(net.IP), wanted: net.IP(nil), pgtype: "inet"},
{src: net.ParseIP("127.0.0.1"), dst: new(net.IP), pgtype: "inet"},
{src: net.ParseIP("::10.2.3.4"), dst: new(net.IP), pgtype: "inet"},
{src: net.ParseIP("::ffff:10.4.3.2"), dst: new(net.IP), pgtype: "inet"},
{src: nil, dst: (*net.IPNet)(nil), pgtype: "cidr", wanterr: "pg: Scan(non-pointer *net.IPNet)"},
{src: nil, dst: new(net.IPNet), wanted: net.IPNet{}, pgtype: "cidr"},
{src: nil, dst: mustParseCIDR("192.168.100.128/25"), wanted: net.IPNet{}, pgtype: "cidr"},
{src: mustParseCIDR("192.168.100.128/25"), dst: new(net.IPNet), pgtype: "cidr"},
{src: mustParseCIDR("2001:4f8:3:ba::/64"), dst: new(net.IPNet), pgtype: "cidr"},
{src: mustParseCIDR("2001:4f8:3:ba:2e0:81ff:fe22:d1f1/128"), dst: new(net.IPNet), pgtype: "cidr"},
}
}
func TestConversion(t *testing.T) {
db := pg.Connect(pgOptions())
for i, test := range conversionTests() {
test.i = i
var scanner orm.ColumnScanner
if v, ok := test.dst.(orm.ColumnScanner); ok {
scanner = v
} else {
scanner = pg.Scan(test.dst)
}
_, err := db.QueryOne(scanner, "SELECT (?) AS dst", test.src)
test.Assert(t, err)
}
for i, test := range conversionTests() {
test.i = i
var scanner orm.ColumnScanner
if v, ok := test.dst.(orm.ColumnScanner); ok {
scanner = v
} else {
scanner = pg.Scan(test.dst)
}
err := db.Model().ColumnExpr("(?) AS dst", test.src).Select(scanner)
test.Assert(t, err)
}
for i, test := range conversionTests() {
test.i = i
if test.pgtype == "" {
continue
}
stmt, err := db.Prepare(fmt.Sprintf("SELECT ($1::%s) AS dst", test.pgtype))
if err != nil {
t.Fatal(err)
}
var scanner orm.ColumnScanner
if v, ok := test.dst.(orm.ColumnScanner); ok {
scanner = v
} else {
scanner = pg.Scan(test.dst)
}
_, err = stmt.QueryOne(scanner, test.src)
test.Assert(t, err)
if err := stmt.Close(); err != nil {
t.Fatal(err)
}
}
}
func mustParseCIDR(s string) *net.IPNet {
_, ipnet, err := net.ParseCIDR(s)
if err != nil {
panic(err)
}
return ipnet
}

428
vendor/github.com/go-pg/pg/db.go generated vendored Normal file
View file

@ -0,0 +1,428 @@
package pg
import (
"fmt"
"io"
"time"
"github.com/go-pg/pg/internal"
"github.com/go-pg/pg/internal/pool"
"github.com/go-pg/pg/orm"
)
// Connect connects to a database using provided options.
//
// The returned DB is safe for concurrent use by multiple goroutines
// and maintains its own connection pool.
func Connect(opt *Options) *DB {
opt.init()
return &DB{
opt: opt,
pool: newConnPool(opt),
}
}
var _ orm.DB = (*DB)(nil)
func (db *DB) String() string {
return fmt.Sprintf("DB<Addr=%q%s>", db.opt.Addr, db.fmter)
}
// Options returns read-only Options that were used to connect to the DB.
func (db *DB) Options() *Options {
return db.opt
}
type PoolStats pool.Stats
// PoolStats returns connection pool stats.
func (db *DB) PoolStats() *PoolStats {
stats := db.pool.Stats()
return (*PoolStats)(stats)
}
// WithTimeout returns a DB that uses d as the read/write timeout.
func (db *DB) WithTimeout(d time.Duration) *DB {
newopt := *db.opt
newopt.ReadTimeout = d
newopt.WriteTimeout = d
return &DB{
opt: &newopt,
pool: db.pool,
fmter: db.fmter,
queryProcessedHooks: copyQueryProcessedHooks(db.queryProcessedHooks),
}
}
// WithParam returns a DB that replaces the param with the value in queries.
func (db *DB) WithParam(param string, value interface{}) *DB {
return &DB{
opt: db.opt,
pool: db.pool,
fmter: db.fmter.WithParam(param, value),
queryProcessedHooks: copyQueryProcessedHooks(db.queryProcessedHooks),
}
}
func (db *DB) retryBackoff(retry int) time.Duration {
return internal.RetryBackoff(retry, db.opt.MinRetryBackoff, db.opt.MaxRetryBackoff)
}
func (db *DB) conn() (*pool.Conn, error) {
cn, _, err := db.pool.Get()
if err != nil {
return nil, err
}
cn.SetTimeout(db.opt.ReadTimeout, db.opt.WriteTimeout)
if cn.InitedAt.IsZero() {
cn.InitedAt = time.Now()
if err := db.initConn(cn); err != nil {
_ = db.pool.Remove(cn)
return nil, err
}
}
return cn, nil
}
func (db *DB) initConn(cn *pool.Conn) error {
if db.opt.TLSConfig != nil {
if err := enableSSL(cn, db.opt.TLSConfig); err != nil {
return err
}
}
err := startup(cn, db.opt.User, db.opt.Password, db.opt.Database)
if err != nil {
return err
}
if db.opt.OnConnect != nil {
dbConn := &DB{
opt: db.opt,
pool: pool.NewSingleConnPool(cn),
fmter: db.fmter,
}
return db.opt.OnConnect(dbConn)
}
return nil
}
func (db *DB) freeConn(cn *pool.Conn, err error) error {
if !isBadConn(err, false) {
return db.pool.Put(cn)
}
return db.pool.Remove(cn)
}
func (db *DB) shouldRetry(err error) bool {
if err == nil {
return false
}
if pgerr, ok := err.(Error); ok {
switch pgerr.Field('C') {
case "40001": // serialization_failure
return true
case "55000": // attempted to delete invisible tuple
return true
case "57014": // statement_timeout
return db.opt.RetryStatementTimeout
default:
return false
}
}
return isNetworkError(err)
}
// Close closes the database client, releasing any open resources.
//
// It is rare to Close a DB, as the DB handle is meant to be
// long-lived and shared between many goroutines.
func (db *DB) Close() error {
return db.pool.Close()
}
// Exec executes a query ignoring returned rows. The params are for any
// placeholders in the query.
func (db *DB) Exec(query interface{}, params ...interface{}) (res orm.Result, err error) {
for i := 0; i <= db.opt.MaxRetries; i++ {
var cn *pool.Conn
if i >= 1 {
time.Sleep(db.retryBackoff(i - 1))
}
cn, err = db.conn()
if err != nil {
continue
}
start := time.Now()
res, err = db.simpleQuery(cn, query, params...)
db.freeConn(cn, err)
db.queryProcessed(db, start, query, params, res, err)
if !db.shouldRetry(err) {
break
}
}
return res, err
}
// ExecOne acts like Exec, but query must affect only one row. It
// returns ErrNoRows error when query returns zero rows or
// ErrMultiRows when query returns multiple rows.
func (db *DB) ExecOne(query interface{}, params ...interface{}) (orm.Result, error) {
res, err := db.Exec(query, params...)
if err != nil {
return nil, err
}
if err := internal.AssertOneRow(res.RowsAffected()); err != nil {
return nil, err
}
return res, nil
}
// Query executes a query that returns rows, typically a SELECT.
// The params are for any placeholders in the query.
func (db *DB) Query(model, query interface{}, params ...interface{}) (res orm.Result, err error) {
for i := 0; i <= db.opt.MaxRetries; i++ {
var cn *pool.Conn
if i >= 1 {
time.Sleep(db.retryBackoff(i - 1))
}
cn, err = db.conn()
if err != nil {
continue
}
start := time.Now()
res, err = db.simpleQueryData(cn, model, query, params...)
db.freeConn(cn, err)
db.queryProcessed(db, start, query, params, res, err)
if !db.shouldRetry(err) {
break
}
}
if err != nil {
return nil, err
}
if mod := res.Model(); mod != nil && res.RowsReturned() > 0 {
if err = mod.AfterQuery(db); err != nil {
return res, err
}
}
return res, nil
}
// QueryOne acts like Query, but query must return only one row. It
// returns ErrNoRows error when query returns zero rows or
// ErrMultiRows when query returns multiple rows.
func (db *DB) QueryOne(model, query interface{}, params ...interface{}) (orm.Result, error) {
res, err := db.Query(model, query, params...)
if err != nil {
return nil, err
}
if err := internal.AssertOneRow(res.RowsAffected()); err != nil {
return nil, err
}
return res, nil
}
// Listen listens for notifications sent with NOTIFY command.
func (db *DB) Listen(channels ...string) *Listener {
ln := &Listener{
db: db,
}
_ = ln.Listen(channels...)
return ln
}
// CopyFrom copies data from reader to a table.
func (db *DB) CopyFrom(r io.Reader, query interface{}, params ...interface{}) (orm.Result, error) {
cn, err := db.conn()
if err != nil {
return nil, err
}
res, err := db.copyFrom(cn, r, query, params...)
db.freeConn(cn, err)
return res, err
}
func (db *DB) copyFrom(cn *pool.Conn, r io.Reader, query interface{}, params ...interface{}) (orm.Result, error) {
if err := writeQueryMsg(cn.Writer, db, query, params...); err != nil {
return nil, err
}
if err := cn.FlushWriter(); err != nil {
return nil, err
}
if err := readCopyInResponse(cn); err != nil {
return nil, err
}
for {
if err := writeCopyData(cn.Writer, r); err != nil {
if err == io.EOF {
break
}
return nil, err
}
if err := cn.FlushWriter(); err != nil {
return nil, err
}
}
writeCopyDone(cn.Writer)
if err := cn.FlushWriter(); err != nil {
return nil, err
}
return readReadyForQuery(cn)
}
// CopyTo copies data from a table to writer.
func (db *DB) CopyTo(w io.Writer, query interface{}, params ...interface{}) (orm.Result, error) {
cn, err := db.conn()
if err != nil {
return nil, err
}
res, err := db.copyTo(cn, w, query, params...)
if err != nil {
db.freeConn(cn, err)
return nil, err
}
db.pool.Put(cn)
return res, nil
}
func (db *DB) copyTo(cn *pool.Conn, w io.Writer, query interface{}, params ...interface{}) (orm.Result, error) {
if err := writeQueryMsg(cn.Writer, db, query, params...); err != nil {
return nil, err
}
if err := cn.FlushWriter(); err != nil {
return nil, err
}
if err := readCopyOutResponse(cn); err != nil {
return nil, err
}
return readCopyData(cn, w)
}
// Model returns new query for the model.
func (db *DB) Model(model ...interface{}) *orm.Query {
return orm.NewQuery(db, model...)
}
// Select selects the model by primary key.
func (db *DB) Select(model interface{}) error {
return orm.Select(db, model)
}
// Insert inserts the model updating primary keys if they are empty.
func (db *DB) Insert(model ...interface{}) error {
return orm.Insert(db, model...)
}
// Update updates the model by primary key.
func (db *DB) Update(model ...interface{}) error {
return orm.Update(db, model...)
}
// Delete deletes the model by primary key.
func (db *DB) Delete(model interface{}) error {
return orm.Delete(db, model)
}
// CreateTable creates table for the model. It recognizes following field tags:
// - notnull - sets NOT NULL constraint.
// - unique - sets UNIQUE constraint.
// - default:value - sets default value.
func (db *DB) CreateTable(model interface{}, opt *orm.CreateTableOptions) error {
_, err := orm.CreateTable(db, model, opt)
return err
}
// DropTable drops table for the model.
func (db *DB) DropTable(model interface{}, opt *orm.DropTableOptions) error {
_, err := orm.DropTable(db, model, opt)
return err
}
func (db *DB) FormatQuery(dst []byte, query string, params ...interface{}) []byte {
return db.fmter.Append(dst, query, params...)
}
func (db *DB) cancelRequest(processId, secretKey int32) error {
cn, err := db.pool.NewConn()
if err != nil {
return err
}
writeCancelRequestMsg(cn.Writer, processId, secretKey)
if err = cn.FlushWriter(); err != nil {
return err
}
cn.Close()
return nil
}
func (db *DB) simpleQuery(
cn *pool.Conn, query interface{}, params ...interface{},
) (orm.Result, error) {
if err := writeQueryMsg(cn.Writer, db, query, params...); err != nil {
return nil, err
}
if err := cn.FlushWriter(); err != nil {
return nil, err
}
res, err := readSimpleQuery(cn)
if err != nil {
return nil, err
}
return res, nil
}
func (db *DB) simpleQueryData(
cn *pool.Conn, model, query interface{}, params ...interface{},
) (orm.Result, error) {
if err := writeQueryMsg(cn.Writer, db, query, params...); err != nil {
return nil, err
}
if err := cn.FlushWriter(); err != nil {
return nil, err
}
res, err := readSimpleQueryData(cn, model)
if err != nil {
return nil, err
}
return res, nil
}

39
vendor/github.com/go-pg/pg/db_context.go generated vendored Normal file
View file

@ -0,0 +1,39 @@
// +build go1.7
package pg
import (
"context"
"github.com/go-pg/pg/internal/pool"
"github.com/go-pg/pg/orm"
)
// DB is a database handle representing a pool of zero or more
// underlying connections. It's safe for concurrent use by multiple
// goroutines.
type DB struct {
opt *Options
pool pool.Pooler
fmter orm.Formatter
queryProcessedHooks []queryProcessedHook
ctx context.Context
}
func (db *DB) Context() context.Context {
if db.ctx != nil {
return db.ctx
}
return context.Background()
}
func (db *DB) WithContext(ctx context.Context) *DB {
if ctx == nil {
panic("nil context")
}
cp := *db
cp.ctx = ctx
return &cp
}

19
vendor/github.com/go-pg/pg/db_no_context.go generated vendored Normal file
View file

@ -0,0 +1,19 @@
// +build !go1.7
package pg
import (
"github.com/go-pg/pg/internal/pool"
"github.com/go-pg/pg/orm"
)
// DB is a database handle representing a pool of zero or more
// underlying connections. It's safe for concurrent use by multiple
// goroutines.
type DB struct {
opt *Options
pool pool.Pooler
fmter orm.Formatter
queryProcessedHooks []queryProcessedHook
}

1529
vendor/github.com/go-pg/pg/db_test.go generated vendored Normal file

File diff suppressed because it is too large Load diff

4
vendor/github.com/go-pg/pg/doc.go generated vendored Normal file
View file

@ -0,0 +1,4 @@
/*
Package github.com/go-pg/pg implements a PostgreSQL client.
*/
package pg

44
vendor/github.com/go-pg/pg/error.go generated vendored Normal file
View file

@ -0,0 +1,44 @@
package pg
import (
"io"
"net"
"github.com/go-pg/pg/internal"
)
var ErrNoRows = internal.ErrNoRows
var ErrMultiRows = internal.ErrMultiRows
type Error interface {
Field(byte) string
IntegrityViolation() bool
}
var _ Error = (*internal.PGError)(nil)
func isBadConn(err error, allowTimeout bool) bool {
if err == nil {
return false
}
if _, ok := err.(internal.Error); ok {
return false
}
if pgErr, ok := err.(Error); ok && pgErr.Field('S') != "FATAL" {
return false
}
if allowTimeout {
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
return false
}
}
return true
}
func isNetworkError(err error) bool {
if err == io.EOF {
return true
}
_, ok := err.(net.Error)
return ok
}

49
vendor/github.com/go-pg/pg/example_array_test.go generated vendored Normal file
View file

@ -0,0 +1,49 @@
package pg_test
import (
"fmt"
"github.com/go-pg/pg"
)
func ExampleDB_Model_postgresArrayStructTag() {
type Item struct {
Id int64
Emails []string `pg:",array"` // marshalled as PostgreSQL array
Numbers [][]int `pg:",array"` // marshalled as PostgreSQL array
}
_, err := db.Exec(`CREATE TEMP TABLE items (id serial, emails text[], numbers int[][])`)
if err != nil {
panic(err)
}
defer db.Exec("DROP TABLE items")
item1 := Item{
Id: 1,
Emails: []string{"one@example.com", "two@example.com"},
Numbers: [][]int{{1, 2}, {3, 4}},
}
if err := db.Insert(&item1); err != nil {
panic(err)
}
var item Item
err = db.Model(&item).Where("id = ?", 1).Select()
if err != nil {
panic(err)
}
fmt.Println(item)
// Output: {1 [one@example.com two@example.com] [[1 2] [3 4]]}
}
func ExampleArray() {
src := []string{"one@example.com", "two@example.com"}
var dst []string
_, err := db.QueryOne(pg.Scan(pg.Array(&dst)), `SELECT ?`, pg.Array(src))
if err != nil {
panic(err)
}
fmt.Println(dst)
// Output: [one@example.com two@example.com]
}

47
vendor/github.com/go-pg/pg/example_hstore_test.go generated vendored Normal file
View file

@ -0,0 +1,47 @@
package pg_test
import (
"fmt"
"github.com/go-pg/pg"
)
func ExampleDB_Model_hstoreStructTag() {
type Item struct {
Id int64
Attrs map[string]string `pg:",hstore"` // marshalled as PostgreSQL hstore
}
_, err := db.Exec(`CREATE TEMP TABLE items (id serial, attrs hstore)`)
if err != nil {
panic(err)
}
defer db.Exec("DROP TABLE items")
item1 := Item{
Id: 1,
Attrs: map[string]string{"hello": "world"},
}
if err := db.Insert(&item1); err != nil {
panic(err)
}
var item Item
err = db.Model(&item).Where("id = ?", 1).Select()
if err != nil {
panic(err)
}
fmt.Println(item)
// Output: {1 map[hello:world]}
}
func ExampleHstore() {
src := map[string]string{"hello": "world"}
var dst map[string]string
_, err := db.QueryOne(pg.Scan(pg.Hstore(&dst)), `SELECT ?`, pg.Hstore(src))
if err != nil {
panic(err)
}
fmt.Println(dst)
// Output: map[hello:world]
}

971
vendor/github.com/go-pg/pg/example_model_test.go generated vendored Normal file
View file

@ -0,0 +1,971 @@
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_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 (?)", 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 := db.Model(&Book{}).Where("author_id = ?", 1)
var books []Book
err := db.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 := db.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_nullEmptyValue() {
type Example struct {
Hello string
}
var str sql.NullString
_, err := db.QueryOne(pg.Scan(&str), "SELECT ?hello", &Example{Hello: ""})
if err != nil {
panic(err)
}
fmt.Println(str.Valid)
// Output: false
}
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.*", "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.*", "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.*", "Profiles").
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"`
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.*", "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_Model_manyToMany() {
type Item struct {
Id int
Items []Item `pg:",many2many:item_to_items,joinFK:Sub"`
}
db := connect()
defer db.Close()
qs := []string{
"CREATE TEMP TABLE items (id int)",
"CREATE TEMP TABLE item_to_items (item_id int, sub_id int)",
"INSERT INTO items VALUES (1), (2), (3)",
"INSERT INTO item_to_items VALUES (1, 2), (1, 3)",
}
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 * FROM "items" AS "item"
// JOIN "item_to_items" ON ("item_to_items"."item_id") IN ((1))
// WHERE ("item"."id" = "item_to_items"."sub_id")
var item Item
err := db.Model(&item).Column("item.*", "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()
err := db.Update(&Book{
Id: 1,
Title: "updated book 1",
})
if err != nil {
panic(err)
}
var book 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").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").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",
}
book2 := &Book{
Id: 2,
Title: "updated book 2",
}
// 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").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",
}, {
Id: 2,
Title: "updated book 2",
}}
// 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").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{}).Where("id IN (?)", ids).Delete()
if err != nil {
panic(err)
}
fmt.Println("deleted", res.RowsAffected())
count, err := db.Model(&Book{}).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
}

View file

@ -0,0 +1,80 @@
package pg_test
import (
"fmt"
"github.com/go-pg/pg"
)
type Params struct {
X int
Y int
}
func (p *Params) Sum() int {
return p.X + p.Y
}
// go-pg recognizes `?` in queries as placeholders and replaces them
// with parameters when queries are executed. `?` can be escaped with backslash.
// Parameters are escaped before replacing according to PostgreSQL rules.
// Specifically:
// - all parameters are properly quoted against SQL injections;
// - null byte is removed;
// - JSON/JSONB gets `\u0000` escaped as `\\u0000`.
func Example_placeholders() {
var num int
// Simple params.
_, err := db.Query(pg.Scan(&num), "SELECT ?", 42)
if err != nil {
panic(err)
}
fmt.Println("simple:", num)
// Indexed params.
_, err = db.Query(pg.Scan(&num), "SELECT ?0 + ?0", 1)
if err != nil {
panic(err)
}
fmt.Println("indexed:", num)
// Named params.
params := &Params{
X: 1,
Y: 1,
}
_, err = db.Query(pg.Scan(&num), "SELECT ?x + ?y + ?Sum", params)
if err != nil {
panic(err)
}
fmt.Println("named:", num)
// Global params.
_, err = db.WithParam("z", 1).Query(pg.Scan(&num), "SELECT ?x + ?y + ?z", params)
if err != nil {
panic(err)
}
fmt.Println("global:", num)
// Model params.
var tableName, tableAlias, columns string
_, err = db.Model(&Params{}).Query(
pg.Scan(&tableName, &tableAlias, &columns),
"SELECT '?TableName', '?TableAlias', '?Columns'",
)
if err != nil {
panic(err)
}
fmt.Println("table name:", tableName)
fmt.Println("table alias:", tableAlias)
fmt.Println("columns:", columns)
// Output: simple: 42
// indexed: 2
// named: 4
// global: 3
// table name: "params"
// table alias: "params"
// columns: "x", "y"
}

284
vendor/github.com/go-pg/pg/example_test.go generated vendored Normal file
View file

@ -0,0 +1,284 @@
package pg_test
import (
"bytes"
"fmt"
"strings"
"time"
"github.com/go-pg/pg"
"github.com/go-pg/pg/orm"
)
var db *pg.DB
func init() {
db = connect()
}
func connect() *pg.DB {
return pg.Connect(pgOptions())
}
func ExampleConnect() {
db := pg.Connect(&pg.Options{
User: "postgres",
Password: "",
Database: "postgres",
})
var n int
_, err := db.QueryOne(pg.Scan(&n), "SELECT 1")
if err != nil {
panic(err)
}
fmt.Println(n)
err = db.Close()
if err != nil {
panic(err)
}
// Output: 1
}
func ExampleDB_QueryOne() {
var user struct {
Name string
}
res, err := db.QueryOne(&user, `
WITH users (name) AS (VALUES (?))
SELECT * FROM users
`, "admin")
if err != nil {
panic(err)
}
fmt.Println(res.RowsAffected())
fmt.Println(user)
// Output: 1
// {admin}
}
func ExampleDB_QueryOne_returning_id() {
_, err := db.Exec(`CREATE TEMP TABLE users(id serial, name varchar(500))`)
if err != nil {
panic(err)
}
var user struct {
Id int32
Name string
}
user.Name = "admin"
_, err = db.QueryOne(&user, `
INSERT INTO users (name) VALUES (?name) RETURNING id
`, &user)
if err != nil {
panic(err)
}
fmt.Println(user)
// Output: {1 admin}
}
func ExampleDB_Exec() {
res, err := db.Exec(`CREATE TEMP TABLE test()`)
fmt.Println(res.RowsAffected(), err)
// Output: -1 <nil>
}
func ExampleListener() {
ln := db.Listen("mychan")
defer ln.Close()
ch := ln.Channel()
go func() {
time.Sleep(time.Millisecond)
_, err := db.Exec("NOTIFY mychan, ?", "hello world")
if err != nil {
panic(err)
}
}()
notif := <-ch
fmt.Println(notif)
// Output: &{mychan hello world}
}
func txExample() *pg.DB {
db := pg.Connect(&pg.Options{
User: "postgres",
})
queries := []string{
`DROP TABLE IF EXISTS tx_test`,
`CREATE TABLE tx_test(counter int)`,
`INSERT INTO tx_test (counter) VALUES (0)`,
}
for _, q := range queries {
_, err := db.Exec(q)
if err != nil {
panic(err)
}
}
return db
}
func ExampleDB_Begin() {
db := txExample()
tx, err := db.Begin()
if err != nil {
panic(err)
}
var counter int
_, err = tx.QueryOne(pg.Scan(&counter), `SELECT counter FROM tx_test`)
if err != nil {
tx.Rollback()
panic(err)
}
counter++
_, err = tx.Exec(`UPDATE tx_test SET counter = ?`, counter)
if err != nil {
tx.Rollback()
panic(err)
}
err = tx.Commit()
if err != nil {
panic(err)
}
fmt.Println(counter)
// Output: 1
}
func ExampleDB_RunInTransaction() {
db := txExample()
var counter int
// Transaction is automatically rollbacked on error.
err := db.RunInTransaction(func(tx *pg.Tx) error {
_, err := tx.QueryOne(pg.Scan(&counter), `SELECT counter FROM tx_test`)
if err != nil {
return err
}
counter++
_, err = tx.Exec(`UPDATE tx_test SET counter = ?`, counter)
return err
})
if err != nil {
panic(err)
}
fmt.Println(counter)
// Output: 1
}
func ExampleDB_Prepare() {
stmt, err := db.Prepare(`SELECT $1::text, $2::text`)
if err != nil {
panic(err)
}
var s1, s2 string
_, err = stmt.QueryOne(pg.Scan(&s1, &s2), "foo", "bar")
if err != nil {
panic(err)
}
fmt.Println(s1, s2)
// Output: foo bar
}
func ExampleDB_CreateTable() {
type Model struct {
Id int
Name string
}
err := db.CreateTable(&Model{}, &orm.CreateTableOptions{
Temp: true, // create temp table
})
if err != nil {
panic(err)
}
var info []struct {
ColumnName string
DataType string
}
_, err = db.Query(&info, `
SELECT column_name, data_type
FROM information_schema.columns
WHERE table_name = 'models'
`)
if err != nil {
panic(err)
}
fmt.Println(info)
// Output: [{id bigint} {name text}]
}
func ExampleInts() {
var nums pg.Ints
_, err := db.Query(&nums, `SELECT generate_series(0, 10)`)
fmt.Println(nums, err)
// Output: [0 1 2 3 4 5 6 7 8 9 10] <nil>
}
func ExampleStrings() {
var strs pg.Strings
_, err := db.Query(&strs, `
WITH users AS (VALUES ('foo'), ('bar')) SELECT * FROM users
`)
fmt.Println(strs, err)
// Output: [foo bar] <nil>
}
func ExampleDB_CopyFrom() {
_, err := db.Exec(`CREATE TEMP TABLE words(word text, len int)`)
if err != nil {
panic(err)
}
r := strings.NewReader("hello,5\nfoo,3\n")
_, err = db.CopyFrom(r, `COPY words FROM STDIN WITH CSV`)
if err != nil {
panic(err)
}
var buf bytes.Buffer
_, err = db.CopyTo(&buf, `COPY words TO STDOUT WITH CSV`)
if err != nil {
panic(err)
}
fmt.Println(buf.String())
// Output: hello,5
// foo,3
}
func ExampleDB_WithTimeout() {
var count int
// Use bigger timeout since this query is known to be slow.
_, err := db.WithTimeout(time.Minute).QueryOne(pg.Scan(&count), `
SELECT count(*) FROM big_table
`)
if err != nil {
panic(err)
}
}
func ExampleScan() {
var s1, s2 string
_, err := db.QueryOne(pg.Scan(&s1, &s2), `SELECT ?, ?`, "foo", "bar")
fmt.Println(s1, s2, err)
// Output: foo bar <nil>
}

109
vendor/github.com/go-pg/pg/exampledb_model_test.go generated vendored Normal file
View file

@ -0,0 +1,109 @@
package pg_test
import (
"fmt"
"github.com/go-pg/pg"
"github.com/go-pg/pg/orm"
)
type User struct {
Id int64
Name string
Emails []string
}
func (u User) String() string {
return fmt.Sprintf("User<%d %s %v>", u.Id, u.Name, u.Emails)
}
type Story struct {
Id int64
Title string
AuthorId int64
Author *User
}
func (s Story) String() string {
return fmt.Sprintf("Story<%d %s %s>", s.Id, s.Title, s.Author)
}
func ExampleDB_Model() {
db := pg.Connect(&pg.Options{
User: "postgres",
})
err := createSchema(db)
if err != nil {
panic(err)
}
user1 := &User{
Name: "admin",
Emails: []string{"admin1@admin", "admin2@admin"},
}
err = db.Insert(user1)
if err != nil {
panic(err)
}
err = db.Insert(&User{
Name: "root",
Emails: []string{"root1@root", "root2@root"},
})
if err != nil {
panic(err)
}
story1 := &Story{
Title: "Cool story",
AuthorId: user1.Id,
}
err = db.Insert(story1)
if err != nil {
panic(err)
}
// Select user by primary key.
user := User{Id: user1.Id}
err = db.Select(&user)
if err != nil {
panic(err)
}
// Select all users.
var users []User
err = db.Model(&users).Select()
if err != nil {
panic(err)
}
// Select story and associated author in one query.
var story Story
err = db.Model(&story).
Column("story.*", "Author").
Where("story.id = ?", story1.Id).
Select()
if err != nil {
panic(err)
}
fmt.Println(user)
fmt.Println(users)
fmt.Println(story)
// Output: User<1 admin [admin1@admin admin2@admin]>
// [User<1 admin [admin1@admin admin2@admin]> User<2 root [root1@root root2@root]>]
// Story<1 Cool story User<1 admin [admin1@admin admin2@admin]>>
}
func createSchema(db *pg.DB) error {
for _, model := range []interface{}{&User{}, &Story{}} {
err := db.CreateTable(model, &orm.CreateTableOptions{
Temp: true,
})
if err != nil {
return err
}
}
return nil
}

109
vendor/github.com/go-pg/pg/exampledb_query_test.go generated vendored Normal file
View file

@ -0,0 +1,109 @@
package pg_test
import (
"fmt"
"github.com/go-pg/pg"
)
func CreateUser(db *pg.DB, user *User) error {
_, err := db.QueryOne(user, `
INSERT INTO users (name, emails) VALUES (?name, ?emails)
RETURNING id
`, user)
return err
}
func GetUser(db *pg.DB, id int64) (*User, error) {
var user User
_, err := db.QueryOne(&user, `SELECT * FROM users WHERE id = ?`, id)
return &user, err
}
func GetUsers(db *pg.DB) ([]User, error) {
var users []User
_, err := db.Query(&users, `SELECT * FROM users`)
return users, err
}
func GetUsersByIds(db *pg.DB, ids []int64) ([]User, error) {
var users []User
_, err := db.Query(&users, `SELECT * FROM users WHERE id IN (?)`, pg.In(ids))
return users, err
}
func CreateStory(db *pg.DB, story *Story) error {
_, err := db.QueryOne(story, `
INSERT INTO stories (title, author_id) VALUES (?title, ?author_id)
RETURNING id
`, story)
return err
}
// GetStory returns story with associated author.
func GetStory(db *pg.DB, id int64) (*Story, error) {
var story Story
_, err := db.QueryOne(&story, `
SELECT s.*,
u.id AS author__id, u.name AS author__name, u.emails AS author__emails
FROM stories AS s, users AS u
WHERE s.id = ? AND u.id = s.author_id
`, id)
return &story, err
}
func ExampleDB_Query() {
db := pg.Connect(&pg.Options{
User: "postgres",
})
err := createSchema(db)
if err != nil {
panic(err)
}
user1 := &User{
Name: "admin",
Emails: []string{"admin1@admin", "admin2@admin"},
}
err = CreateUser(db, user1)
if err != nil {
panic(err)
}
err = CreateUser(db, &User{
Name: "root",
Emails: []string{"root1@root", "root2@root"},
})
if err != nil {
panic(err)
}
story1 := &Story{
Title: "Cool story",
AuthorId: user1.Id,
}
err = CreateStory(db, story1)
user, err := GetUser(db, user1.Id)
if err != nil {
panic(err)
}
users, err := GetUsers(db)
if err != nil {
panic(err)
}
story, err := GetStory(db, story1.Id)
if err != nil {
panic(err)
}
fmt.Println(user)
fmt.Println(users)
fmt.Println(story)
// Output: User<1 admin [admin1@admin admin2@admin]>
// [User<1 admin [admin1@admin admin2@admin]> User<2 root [root1@root root2@root]>]
// Story<1 Cool story User<1 admin [admin1@admin admin2@admin]>>
}

11
vendor/github.com/go-pg/pg/export_test.go generated vendored Normal file
View file

@ -0,0 +1,11 @@
package pg
import "github.com/go-pg/pg/internal/pool"
func (db *DB) Pool() pool.Pooler {
return db.pool
}
func (ln *Listener) CurrentConn() *pool.Conn {
return ln.cn
}

142
vendor/github.com/go-pg/pg/hook.go generated vendored Normal file
View file

@ -0,0 +1,142 @@
package pg
import (
"fmt"
"runtime"
"strings"
"time"
"github.com/go-pg/pg/orm"
)
type dummyDB struct {
orm.DB
}
var _ orm.DB = dummyDB{}
func (dummyDB) FormatQuery(dst []byte, query string, params ...interface{}) []byte {
return append(dst, query...)
}
type QueryProcessedEvent struct {
StartTime time.Time
Func string
File string
Line int
DB orm.DB
Query interface{}
Params []interface{}
Result orm.Result
Error error
}
func (ev *QueryProcessedEvent) UnformattedQuery() (string, error) {
b, err := queryString(ev.Query)
if err != nil {
return "", err
}
return string(b), nil
}
func (ev *QueryProcessedEvent) FormattedQuery() (string, error) {
b, err := appendQuery(nil, ev.DB, ev.Query, ev.Params...)
if err != nil {
return "", err
}
return string(b), nil
}
func queryString(query interface{}) ([]byte, error) {
switch query := query.(type) {
case orm.QueryAppender:
query = query.Copy()
query.Query().DB(dummyDB{})
return query.AppendQuery(nil)
case string:
return dummyDB{}.FormatQuery(nil, query), nil
default:
return nil, fmt.Errorf("pg: can't append %T", query)
}
}
type queryProcessedHook func(*QueryProcessedEvent)
// OnQueryProcessed calls the fn with QueryProcessedEvent
// when query is processed.
func (db *DB) OnQueryProcessed(fn func(*QueryProcessedEvent)) {
db.queryProcessedHooks = append(db.queryProcessedHooks, fn)
}
func (db *DB) queryProcessed(
ormDB orm.DB,
start time.Time,
query interface{},
params []interface{},
res orm.Result,
err error,
) {
if len(db.queryProcessedHooks) == 0 {
return
}
funcName, file, line := fileLine(2)
event := &QueryProcessedEvent{
StartTime: start,
Func: funcName,
File: file,
Line: line,
DB: ormDB,
Query: query,
Params: params,
Result: res,
Error: err,
}
for _, hook := range db.queryProcessedHooks {
hook(event)
}
}
const packageName = "github.com/go-pg/pg"
func fileLine(depth int) (string, string, int) {
for i := depth; ; i++ {
pc, file, line, ok := runtime.Caller(i)
if !ok {
break
}
if strings.Contains(file, packageName) {
continue
}
_, funcName := packageFuncName(pc)
return funcName, file, line
}
return "", "", 0
}
func packageFuncName(pc uintptr) (string, string) {
f := runtime.FuncForPC(pc)
if f == nil {
return "", ""
}
packageName := ""
funcName := f.Name()
if ind := strings.LastIndex(funcName, "/"); ind > 0 {
packageName += funcName[:ind+1]
funcName = funcName[ind+1:]
}
if ind := strings.Index(funcName, "."); ind > 0 {
packageName += funcName[:ind]
funcName = funcName[ind+1:]
}
return packageName, funcName
}
func copyQueryProcessedHooks(s []queryProcessedHook) []queryProcessedHook {
return s[:len(s):len(s)]
}

236
vendor/github.com/go-pg/pg/hook_test.go generated vendored Normal file
View file

@ -0,0 +1,236 @@
package pg_test
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/go-pg/pg"
"github.com/go-pg/pg/orm"
)
type HookTest struct {
Id int
Value string
afterQuery int
afterSelect int
beforeInsert int
afterInsert int
beforeUpdate int
afterUpdate int
beforeDelete int
afterDelete int
}
func (t *HookTest) AfterQuery(db orm.DB) error {
t.afterQuery++
return nil
}
func (t *HookTest) AfterSelect(db orm.DB) error {
t.afterSelect++
return nil
}
func (t *HookTest) BeforeInsert(db orm.DB) error {
t.beforeInsert++
return nil
}
func (t *HookTest) AfterInsert(db orm.DB) error {
t.afterInsert++
return nil
}
func (t *HookTest) BeforeUpdate(db orm.DB) error {
t.beforeUpdate++
return nil
}
func (t *HookTest) AfterUpdate(db orm.DB) error {
t.afterUpdate++
return nil
}
func (t *HookTest) BeforeDelete(db orm.DB) error {
t.beforeDelete++
return nil
}
func (t *HookTest) AfterDelete(db orm.DB) error {
t.afterDelete++
return nil
}
var _ = Describe("HookTest", func() {
var db *pg.DB
BeforeEach(func() {
db = pg.Connect(pgOptions())
qs := []string{
"CREATE TEMP TABLE hook_tests (id int, value text)",
"INSERT INTO hook_tests VALUES (1, '')",
}
for _, q := range qs {
_, err := db.Exec(q)
Expect(err).NotTo(HaveOccurred())
}
})
AfterEach(func() {
Expect(db.Close()).NotTo(HaveOccurred())
})
It("calls AfterQuery for struct", func() {
var hook HookTest
_, err := db.QueryOne(&hook, "SELECT 1 AS id")
Expect(err).NotTo(HaveOccurred())
Expect(hook.afterQuery).To(Equal(1))
Expect(hook.afterSelect).To(Equal(0))
})
It("calls AfterQuery and AfterSelect for struct model", func() {
var hook HookTest
err := db.Model(&hook).Select()
Expect(err).NotTo(HaveOccurred())
Expect(hook.afterQuery).To(Equal(1))
Expect(hook.afterSelect).To(Equal(1))
})
It("calls AfterQuery for slice", func() {
var hooks []HookTest
_, err := db.Query(&hooks, "SELECT 1 AS id")
Expect(err).NotTo(HaveOccurred())
Expect(hooks).To(HaveLen(1))
Expect(hooks[0].afterQuery).To(Equal(1))
Expect(hooks[0].afterSelect).To(Equal(0))
})
It("calls AfterQuery and AfterSelect for slice model", func() {
var hooks []HookTest
err := db.Model(&hooks).Select()
Expect(err).NotTo(HaveOccurred())
Expect(hooks).To(HaveLen(1))
Expect(hooks[0].afterQuery).To(Equal(1))
Expect(hooks[0].afterSelect).To(Equal(1))
})
It("calls BeforeInsert and AfterInsert", func() {
hook := HookTest{
Id: 1,
Value: "value",
}
err := db.Insert(&hook)
Expect(err).NotTo(HaveOccurred())
Expect(hook.afterQuery).To(Equal(0))
Expect(hook.beforeInsert).To(Equal(1))
Expect(hook.afterInsert).To(Equal(1))
})
It("calls BeforeUpdate and AfterUpdate", func() {
hook := HookTest{
Id: 1,
}
err := db.Update(&hook)
Expect(err).NotTo(HaveOccurred())
Expect(hook.afterQuery).To(Equal(0))
Expect(hook.beforeUpdate).To(Equal(1))
Expect(hook.afterUpdate).To(Equal(1))
})
It("calls BeforeDelete and AfterDelete", func() {
hook := HookTest{
Id: 1,
}
err := db.Delete(&hook)
Expect(err).NotTo(HaveOccurred())
Expect(hook.afterQuery).To(Equal(0))
Expect(hook.beforeDelete).To(Equal(1))
Expect(hook.afterDelete).To(Equal(1))
})
})
var _ = Describe("OnQueryProcessed", func() {
var db *pg.DB
var count int
BeforeEach(func() {
db = pg.Connect(pgOptions())
count = 0
})
Describe("Query/Exec", func() {
BeforeEach(func() {
db.OnQueryProcessed(func(event *pg.QueryProcessedEvent) {
Expect(event.StartTime).NotTo(BeZero())
Expect(event.Func).NotTo(BeZero())
Expect(event.File).NotTo(BeZero())
Expect(event.Line).NotTo(BeZero())
Expect(event.DB).To(Equal(db))
Expect(event.Query).To(Equal("SELECT ?"))
Expect(event.Params).To(Equal([]interface{}{1}))
Expect(event.Result).NotTo(BeNil())
Expect(event.Error).NotTo(HaveOccurred())
q, err := event.UnformattedQuery()
Expect(err).NotTo(HaveOccurred())
Expect(q).To(Equal("SELECT ?"))
q, err = event.FormattedQuery()
Expect(err).NotTo(HaveOccurred())
Expect(q).To(Equal("SELECT 1"))
count++
})
})
It("is called for Query", func() {
_, err := db.Query(pg.Discard, "SELECT ?", 1)
Expect(err).NotTo(HaveOccurred())
Expect(count).To(Equal(1))
})
It("is called for Exec", func() {
_, err := db.Exec("SELECT ?", 1)
Expect(err).NotTo(HaveOccurred())
Expect(count).To(Equal(1))
})
})
Describe("Model", func() {
BeforeEach(func() {
db.OnQueryProcessed(func(event *pg.QueryProcessedEvent) {
Expect(event.StartTime).NotTo(BeZero())
Expect(event.Func).NotTo(BeZero())
Expect(event.File).NotTo(BeZero())
Expect(event.Line).NotTo(BeZero())
Expect(event.DB).To(Equal(db))
Expect(event.Query).NotTo(BeNil())
Expect(event.Params).To(HaveLen(1))
Expect(event.Error).NotTo(HaveOccurred())
Expect(event.Result).NotTo(BeNil())
q, err := event.UnformattedQuery()
Expect(err).NotTo(HaveOccurred())
Expect(q).To(Equal("SELECT ?"))
q, err = event.FormattedQuery()
Expect(err).NotTo(HaveOccurred())
Expect(q).To(Equal("SELECT 1"))
count++
})
})
It("is called for Model", func() {
var n int
err := db.Model().ColumnExpr("?", 1).Select(&n)
Expect(err).NotTo(HaveOccurred())
Expect(count).To(Equal(1))
})
})
})

62
vendor/github.com/go-pg/pg/internal/error.go generated vendored Normal file
View file

@ -0,0 +1,62 @@
package internal
import (
"errors"
"fmt"
)
var ErrNoRows = errors.New("pg: no rows in result set")
var ErrMultiRows = errors.New("pg: multiple rows in result set")
type Error struct {
s string
}
func Errorf(s string, args ...interface{}) Error {
return Error{s: fmt.Sprintf(s, args...)}
}
func (err Error) Error() string {
return err.s
}
type PGError struct {
m map[byte]string
}
func NewPGError(m map[byte]string) PGError {
return PGError{
m: m,
}
}
func (err PGError) Field(k byte) string {
return err.m[k]
}
func (err PGError) IntegrityViolation() bool {
switch err.Field('C') {
case "23000", "23001", "23502", "23503", "23505", "23514", "23P01":
return true
default:
return false
}
}
func (err PGError) Error() string {
return fmt.Sprintf(
"%s #%s %s (addr=%q)",
err.Field('S'), err.Field('C'), err.Field('M'), err.Field('a'),
)
}
func AssertOneRow(l int) error {
switch {
case l == 0:
return ErrNoRows
case l > 1:
return ErrMultiRows
default:
return nil
}
}

24
vendor/github.com/go-pg/pg/internal/internal.go generated vendored Normal file
View file

@ -0,0 +1,24 @@
package internal
import (
"math/rand"
"time"
)
// Retry backoff with jitter sleep to prevent overloaded conditions during intervals
// https://www.awsarchitectureblog.com/2015/03/backoff.html
func RetryBackoff(retry int, minBackoff, maxBackoff time.Duration) time.Duration {
if retry < 0 {
retry = 0
}
backoff := minBackoff << uint(retry)
if backoff > maxBackoff || backoff < minBackoff {
backoff = maxBackoff
}
if backoff == 0 {
return 0
}
return time.Duration(rand.Int63n(int64(backoff)))
}

15
vendor/github.com/go-pg/pg/internal/log.go generated vendored Normal file
View file

@ -0,0 +1,15 @@
package internal
import (
"fmt"
"log"
)
var Logger *log.Logger
func Logf(s string, args ...interface{}) {
if Logger == nil {
return
}
Logger.Output(2, fmt.Sprintf(s, args...))
}

View file

@ -0,0 +1,80 @@
package parser
import (
"bytes"
"fmt"
)
type ArrayParser struct {
*Parser
stickyErr error
}
func NewArrayParser(b []byte) *ArrayParser {
var err error
if len(b) < 2 || b[0] != '{' || b[len(b)-1] != '}' {
err = fmt.Errorf("pg: can't parse array: %s", string(b))
} else {
b = b[1 : len(b)-1]
}
return &ArrayParser{
Parser: New(b),
stickyErr: err,
}
}
func (p *ArrayParser) NextElem() ([]byte, error) {
if p.stickyErr != nil {
return nil, p.stickyErr
}
switch c := p.Peek(); c {
case '"':
p.Advance()
b := p.readSubstring()
p.Skip(',')
return b, nil
case '{':
b := p.readElem()
if b != nil {
b = append(b, '}')
}
p.Skip(',')
return b, nil
default:
b, _ := p.ReadSep(',')
if bytes.Equal(b, pgNull) {
b = nil
}
return b, nil
}
}
func (p *ArrayParser) readElem() []byte {
var b []byte
for p.Valid() {
c := p.Read()
switch c {
case '"':
b = append(b, '"')
for {
bb, ok := p.ReadSep('"')
b = append(b, bb...)
stop := len(b) > 0 && b[len(b)-1] != '\\'
if ok {
b = append(b, '"')
}
if stop {
break
}
}
case '}':
return b
default:
b = append(b, c)
}
}
return b
}

View file

@ -0,0 +1,55 @@
package parser_test
import (
"testing"
"github.com/go-pg/pg/internal/parser"
)
var arrayTests = []struct {
s string
els []string
}{
{`{"\\"}`, []string{`\`}},
{`{"''"}`, []string{`'`}},
{`{{"''\"{}"}}`, []string{`{"''\"{}"}`}},
{`{"''\"{}"}`, []string{`'"{}`}},
{"{1,2}", []string{"1", "2"}},
{"{1,NULL}", []string{"1", ""}},
{`{"1","2"}`, []string{"1", "2"}},
{`{"{1}","{2}"}`, []string{"{1}", "{2}"}},
{"{{1,2},{3}}", []string{"{1,2}", "{3}"}},
}
func TestArrayParser(t *testing.T) {
for testi, test := range arrayTests {
p := parser.NewArrayParser([]byte(test.s))
var got []string
for p.Valid() {
b, err := p.NextElem()
if err != nil {
t.Fatal(err)
}
got = append(got, string(b))
}
if len(got) != len(test.els) {
t.Fatalf(
"#%d got %d elements, wanted %d (got=%#v wanted=%#v)",
testi, len(got), len(test.els), got, test.els,
)
}
for i, el := range got {
if el != test.els[i] {
t.Fatalf(
"#%d el #%d does not match: %q != %q (got=%#v wanted=%#v)",
testi, i, el, test.els[i], got, test.els,
)
}
}
}
}

View file

@ -0,0 +1,40 @@
package parser
import "fmt"
type HstoreParser struct {
*Parser
}
func NewHstoreParser(b []byte) *HstoreParser {
return &HstoreParser{
Parser: New(b),
}
}
func (p *HstoreParser) NextKey() ([]byte, error) {
if p.Skip(',') {
p.Skip(' ')
}
if !p.Skip('"') {
return nil, fmt.Errorf("pg: can't parse hstore key: %q", p.Bytes())
}
key := p.readSubstring()
if !(p.Skip('=') && p.Skip('>')) {
return nil, fmt.Errorf("pg: can't parse hstore key: %q", p.Bytes())
}
return key, nil
}
func (p *HstoreParser) NextValue() ([]byte, error) {
if !p.Skip('"') {
return nil, fmt.Errorf("pg: can't parse hstore value: %q", p.Bytes())
}
value := p.readSubstring()
p.SkipBytes([]byte(", "))
return value, nil
}

View file

@ -0,0 +1,57 @@
package parser_test
import (
"testing"
"github.com/go-pg/pg/internal/parser"
)
var hstoreTests = []struct {
s string
m map[string]string
}{
{`""=>""`, map[string]string{"": ""}},
{`"k''k"=>"k''k"`, map[string]string{"k'k": "k'k"}},
{`"k\"k"=>"k\"k"`, map[string]string{`k"k`: `k"k`}},
{`"k\k"=>"k\k"`, map[string]string{`k\k`: `k\k`}},
{`"foo"=>"bar"`, map[string]string{"foo": "bar"}},
{`"foo"=>"bar","k"=>"v"`, map[string]string{"foo": "bar", "k": "v"}},
}
func TestHstoreParser(t *testing.T) {
for testi, test := range hstoreTests {
p := parser.NewHstoreParser([]byte(test.s))
got := make(map[string]string)
for p.Valid() {
key, err := p.NextKey()
if err != nil {
t.Fatal(err)
}
value, err := p.NextValue()
if err != nil {
t.Fatal(err)
}
got[string(key)] = string(value)
}
if len(got) != len(test.m) {
t.Fatalf(
"#%d got %d elements, wanted %d (got=%#v wanted=%#v)",
testi, len(got), len(test.m), got, test.m,
)
}
for k, v := range got {
if v != test.m[k] {
t.Fatalf(
"#%d el %q does not match: %q != %q (got=%#v wanted=%#v)",
testi, k, v, test.m[k], got, test.m,
)
}
}
}
}

153
vendor/github.com/go-pg/pg/internal/parser/parser.go generated vendored Normal file
View file

@ -0,0 +1,153 @@
package parser
import (
"bytes"
"strconv"
"github.com/go-pg/pg/internal"
)
type Parser struct {
b []byte
}
func New(b []byte) *Parser {
return &Parser{
b: b,
}
}
func NewString(s string) *Parser {
return New(internal.StringToBytes(s))
}
func (p *Parser) Bytes() []byte {
return p.b
}
func (p *Parser) Valid() bool {
return len(p.b) > 0
}
func (p *Parser) Read() byte {
if p.Valid() {
c := p.b[0]
p.Skip(c)
return c
}
return 0
}
func (p *Parser) Peek() byte {
if p.Valid() {
return p.b[0]
}
return 0
}
func (p *Parser) Advance() {
p.b = p.b[1:]
}
func (p *Parser) Skip(c byte) bool {
if p.Peek() == c {
p.Advance()
return true
}
return false
}
func (p *Parser) SkipBytes(b []byte) bool {
if len(b) > len(p.b) {
return false
}
if !bytes.Equal(p.b[:len(b)], b) {
return false
}
p.b = p.b[len(b):]
return true
}
func (p *Parser) ReadSep(c byte) ([]byte, bool) {
ind := bytes.IndexByte(p.b, c)
if ind == -1 {
b := p.b
p.b = p.b[len(p.b):]
return b, false
}
b := p.b[:ind]
p.b = p.b[ind+1:]
return b, true
}
func (p *Parser) ReadIdentifier() (s string, numeric bool) {
end := len(p.b)
numeric = true
for i, ch := range p.b {
if isNum(ch) {
continue
}
if isAlpha(ch) || ch == '_' {
numeric = false
continue
}
end = i
break
}
if end <= 0 {
return "", false
}
b := p.b[:end]
p.b = p.b[end:]
return internal.BytesToString(b), numeric
}
func (p *Parser) ReadNumber() int {
end := len(p.b)
for i, ch := range p.b {
if !isNum(ch) {
end = i
break
}
}
if end <= 0 {
return 0
}
n, _ := strconv.Atoi(string(p.b[:end]))
p.b = p.b[end:]
return n
}
func (p *Parser) readSubstring() []byte {
var b []byte
for p.Valid() {
c := p.Read()
switch c {
case '\\':
switch p.Peek() {
case '\\':
b = append(b, '\\')
p.Advance()
case '"':
b = append(b, '"')
p.Advance()
default:
b = append(b, c)
}
case '\'':
switch p.Peek() {
case '\'':
b = append(b, '\'')
p.Skip(c)
default:
b = append(b, c)
}
case '"':
return b
default:
b = append(b, c)
}
}
return b
}

15
vendor/github.com/go-pg/pg/internal/parser/util.go generated vendored Normal file
View file

@ -0,0 +1,15 @@
package parser
var pgNull = []byte("NULL")
func isNum(c byte) bool {
return c >= '0' && c <= '9'
}
func isAlpha(c byte) bool {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
}
func isAlnum(c byte) bool {
return isAlpha(c) || isNum(c)
}

80
vendor/github.com/go-pg/pg/internal/pool/bench_test.go generated vendored Normal file
View file

@ -0,0 +1,80 @@
package pool_test
import (
"testing"
"time"
"github.com/go-pg/pg/internal/pool"
)
func benchmarkPoolGetPut(b *testing.B, poolSize int) {
connPool := pool.NewConnPool(&pool.Options{
Dialer: dummyDialer,
PoolSize: poolSize,
PoolTimeout: time.Second,
IdleTimeout: time.Hour,
IdleCheckFrequency: time.Hour,
})
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
cn, _, err := connPool.Get()
if err != nil {
b.Fatal(err)
}
if err = connPool.Put(cn); err != nil {
b.Fatal(err)
}
}
})
}
func BenchmarkPoolGetPut10Conns(b *testing.B) {
benchmarkPoolGetPut(b, 10)
}
func BenchmarkPoolGetPut100Conns(b *testing.B) {
benchmarkPoolGetPut(b, 100)
}
func BenchmarkPoolGetPut1000Conns(b *testing.B) {
benchmarkPoolGetPut(b, 1000)
}
func benchmarkPoolGetRemove(b *testing.B, poolSize int) {
connPool := pool.NewConnPool(&pool.Options{
Dialer: dummyDialer,
PoolSize: poolSize,
PoolTimeout: time.Second,
IdleTimeout: time.Hour,
IdleCheckFrequency: time.Hour,
})
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
cn, _, err := connPool.Get()
if err != nil {
b.Fatal(err)
}
if err := connPool.Remove(cn); err != nil {
b.Fatal(err)
}
}
})
}
func BenchmarkPoolGetRemove10Conns(b *testing.B) {
benchmarkPoolGetRemove(b, 10)
}
func BenchmarkPoolGetRemove100Conns(b *testing.B) {
benchmarkPoolGetRemove(b, 100)
}
func BenchmarkPoolGetRemove1000Conns(b *testing.B) {
benchmarkPoolGetRemove(b, 1000)
}

106
vendor/github.com/go-pg/pg/internal/pool/conn.go generated vendored Normal file
View file

@ -0,0 +1,106 @@
package pool
import (
"bufio"
"encoding/hex"
"fmt"
"io"
"net"
"strconv"
"time"
)
var noDeadline = time.Time{}
type Conn struct {
netConn net.Conn
Reader *bufio.Reader
readBuf []byte
Columns [][]byte
Writer *WriteBuffer
InitedAt time.Time
UsedAt time.Time
ProcessId int32
SecretKey int32
_lastId int64
}
func NewConn(netConn net.Conn) *Conn {
cn := &Conn{
Reader: bufio.NewReader(netConn),
readBuf: make([]byte, 0, 512),
Writer: NewWriteBuffer(),
UsedAt: time.Now(),
}
cn.SetNetConn(netConn)
return cn
}
func (cn *Conn) RemoteAddr() net.Addr {
return cn.netConn.RemoteAddr()
}
func (cn *Conn) SetNetConn(netConn net.Conn) {
cn.netConn = netConn
cn.Reader.Reset(netConn)
}
func (cn *Conn) NetConn() net.Conn {
return cn.netConn
}
func (cn *Conn) NextId() string {
cn._lastId++
return strconv.FormatInt(cn._lastId, 10)
}
func (cn *Conn) SetTimeout(rt, wt time.Duration) {
cn.UsedAt = time.Now()
if rt > 0 {
cn.netConn.SetReadDeadline(cn.UsedAt.Add(rt))
} else {
cn.netConn.SetReadDeadline(noDeadline)
}
if wt > 0 {
cn.netConn.SetWriteDeadline(cn.UsedAt.Add(wt))
} else {
cn.netConn.SetWriteDeadline(noDeadline)
}
}
func (cn *Conn) ReadN(n int) ([]byte, error) {
if d := n - cap(cn.readBuf); d > 0 {
cn.readBuf = cn.readBuf[:cap(cn.readBuf)]
cn.readBuf = append(cn.readBuf, make([]byte, d)...)
} else {
cn.readBuf = cn.readBuf[:n]
}
_, err := io.ReadFull(cn.Reader, cn.readBuf)
return cn.readBuf, err
}
func (cn *Conn) FlushWriter() error {
_, err := cn.netConn.Write(cn.Writer.Bytes)
cn.Writer.Reset()
return err
}
func (cn *Conn) Close() error {
return cn.netConn.Close()
}
func (cn *Conn) CheckHealth() error {
if cn.Reader.Buffered() != 0 {
b, _ := cn.Reader.Peek(cn.Reader.Buffered())
err := fmt.Errorf("connection has unread data:\n%s", hex.Dump(b))
return err
}
return nil
}

35
vendor/github.com/go-pg/pg/internal/pool/main_test.go generated vendored Normal file
View file

@ -0,0 +1,35 @@
package pool_test
import (
"net"
"sync"
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
func TestGinkgoSuite(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "pool")
}
func perform(n int, cbs ...func(int)) {
var wg sync.WaitGroup
for _, cb := range cbs {
for i := 0; i < n; i++ {
wg.Add(1)
go func(cb func(int), i int) {
defer GinkgoRecover()
defer wg.Done()
cb(i)
}(cb, i)
}
}
wg.Wait()
}
func dummyDialer() (net.Conn, error) {
return &net.TCPConn{}, nil
}

377
vendor/github.com/go-pg/pg/internal/pool/pool.go generated vendored Normal file
View file

@ -0,0 +1,377 @@
package pool
import (
"errors"
"net"
"sync"
"sync/atomic"
"time"
"github.com/go-pg/pg/internal"
)
var ErrClosed = errors.New("pg: database is closed")
var ErrPoolTimeout = errors.New("pg: connection pool timeout")
var timers = sync.Pool{
New: func() interface{} {
t := time.NewTimer(time.Hour)
t.Stop()
return t
},
}
// Stats contains pool state information and accumulated stats.
type Stats struct {
Hits uint32 // number of times free connection was found in the pool
Misses uint32 // number of times free connection was NOT found in the pool
Timeouts uint32 // number of times a wait timeout occurred
TotalConns uint32 // number of total connections in the pool
FreeConns uint32 // number of free connections in the pool
StaleConns uint32 // number of stale connections removed from the pool
}
type Pooler interface {
NewConn() (*Conn, error)
CloseConn(*Conn) error
Get() (*Conn, bool, error)
Put(*Conn) error
Remove(*Conn) error
Len() int
FreeLen() int
Stats() *Stats
Close() error
}
type Options struct {
Dialer func() (net.Conn, error)
OnClose func(*Conn) error
PoolSize int
PoolTimeout time.Duration
IdleTimeout time.Duration
IdleCheckFrequency time.Duration
MaxAge time.Duration
}
type ConnPool struct {
opt *Options
dialErrorsNum uint32 // atomic
_lastDialError atomic.Value
queue chan struct{}
connsMu sync.Mutex
conns []*Conn
freeConnsMu sync.Mutex
freeConns []*Conn
stats Stats
_closed uint32 // atomic
}
var _ Pooler = (*ConnPool)(nil)
func NewConnPool(opt *Options) *ConnPool {
p := &ConnPool{
opt: opt,
queue: make(chan struct{}, opt.PoolSize),
conns: make([]*Conn, 0, opt.PoolSize),
freeConns: make([]*Conn, 0, opt.PoolSize),
}
if opt.IdleTimeout > 0 && opt.IdleCheckFrequency > 0 {
go p.reaper(opt.IdleCheckFrequency)
}
return p
}
func (p *ConnPool) NewConn() (*Conn, error) {
if p.closed() {
return nil, ErrClosed
}
if atomic.LoadUint32(&p.dialErrorsNum) >= uint32(p.opt.PoolSize) {
return nil, p.lastDialError()
}
netConn, err := p.opt.Dialer()
if err != nil {
p.setLastDialError(err)
if atomic.AddUint32(&p.dialErrorsNum, 1) == uint32(p.opt.PoolSize) {
go p.tryDial()
}
return nil, err
}
atomic.StoreUint32(&p.dialErrorsNum, 0)
cn := NewConn(netConn)
p.connsMu.Lock()
p.conns = append(p.conns, cn)
p.connsMu.Unlock()
return cn, nil
}
func (p *ConnPool) tryDial() {
for {
if p.closed() {
return
}
conn, err := p.opt.Dialer()
if err != nil {
p.setLastDialError(err)
time.Sleep(time.Second)
continue
}
atomic.StoreUint32(&p.dialErrorsNum, 0)
_ = conn.Close()
return
}
}
func (p *ConnPool) setLastDialError(err error) {
p._lastDialError.Store(err)
}
func (p *ConnPool) lastDialError() error {
return p._lastDialError.Load().(error)
}
func (p *ConnPool) isStaleConn(cn *Conn) bool {
if p.opt.IdleTimeout == 0 && p.opt.MaxAge == 0 {
return false
}
now := time.Now()
if p.opt.IdleTimeout > 0 && now.Sub(cn.UsedAt) >= p.opt.IdleTimeout {
return true
}
if p.opt.MaxAge > 0 && now.Sub(cn.InitedAt) >= p.opt.MaxAge {
return true
}
return false
}
func (p *ConnPool) popFree() *Conn {
if len(p.freeConns) == 0 {
return nil
}
idx := len(p.freeConns) - 1
cn := p.freeConns[idx]
p.freeConns = p.freeConns[:idx]
return cn
}
// Get returns existed connection from the pool or creates a new one.
func (p *ConnPool) Get() (*Conn, bool, error) {
if p.closed() {
return nil, false, ErrClosed
}
select {
case p.queue <- struct{}{}:
default:
timer := timers.Get().(*time.Timer)
timer.Reset(p.opt.PoolTimeout)
select {
case p.queue <- struct{}{}:
if !timer.Stop() {
<-timer.C
}
timers.Put(timer)
case <-timer.C:
timers.Put(timer)
atomic.AddUint32(&p.stats.Timeouts, 1)
return nil, false, ErrPoolTimeout
}
}
for {
p.freeConnsMu.Lock()
cn := p.popFree()
p.freeConnsMu.Unlock()
if cn == nil {
break
}
if p.isStaleConn(cn) {
p.CloseConn(cn)
continue
}
atomic.AddUint32(&p.stats.Hits, 1)
return cn, false, nil
}
atomic.AddUint32(&p.stats.Misses, 1)
newcn, err := p.NewConn()
if err != nil {
<-p.queue
return nil, false, err
}
return newcn, true, nil
}
func (p *ConnPool) Put(cn *Conn) error {
if e := cn.CheckHealth(); e != nil {
internal.Logf(e.Error())
return p.Remove(cn)
}
p.freeConnsMu.Lock()
p.freeConns = append(p.freeConns, cn)
p.freeConnsMu.Unlock()
<-p.queue
return nil
}
func (p *ConnPool) Remove(cn *Conn) error {
_ = p.CloseConn(cn)
<-p.queue
return nil
}
func (p *ConnPool) CloseConn(cn *Conn) error {
p.connsMu.Lock()
for i, c := range p.conns {
if c == cn {
p.conns = append(p.conns[:i], p.conns[i+1:]...)
break
}
}
p.connsMu.Unlock()
return p.closeConn(cn)
}
func (p *ConnPool) closeConn(cn *Conn) error {
if p.opt.OnClose != nil {
_ = p.opt.OnClose(cn)
}
return cn.Close()
}
// Len returns total number of connections.
func (p *ConnPool) Len() int {
p.connsMu.Lock()
l := len(p.conns)
p.connsMu.Unlock()
return l
}
// FreeLen returns number of free connections.
func (p *ConnPool) FreeLen() int {
p.freeConnsMu.Lock()
l := len(p.freeConns)
p.freeConnsMu.Unlock()
return l
}
func (p *ConnPool) Stats() *Stats {
return &Stats{
Hits: atomic.LoadUint32(&p.stats.Hits),
Misses: atomic.LoadUint32(&p.stats.Misses),
Timeouts: atomic.LoadUint32(&p.stats.Timeouts),
TotalConns: uint32(p.Len()),
FreeConns: uint32(p.FreeLen()),
}
}
func (p *ConnPool) closed() bool {
return atomic.LoadUint32(&p._closed) == 1
}
func (p *ConnPool) Close() error {
if !atomic.CompareAndSwapUint32(&p._closed, 0, 1) {
return ErrClosed
}
p.connsMu.Lock()
var firstErr error
for _, cn := range p.conns {
if cn == nil {
continue
}
if err := p.closeConn(cn); err != nil && firstErr == nil {
firstErr = err
}
}
p.conns = nil
p.connsMu.Unlock()
p.freeConnsMu.Lock()
p.freeConns = nil
p.freeConnsMu.Unlock()
return firstErr
}
func (p *ConnPool) reapStaleConn() bool {
if len(p.freeConns) == 0 {
return false
}
cn := p.freeConns[0]
if !p.isStaleConn(cn) {
return false
}
_ = p.CloseConn(cn)
p.freeConns = append(p.freeConns[:0], p.freeConns[1:]...)
return true
}
func (p *ConnPool) ReapStaleConns() (int, error) {
var n int
for {
p.queue <- struct{}{}
p.freeConnsMu.Lock()
reaped := p.reapStaleConn()
p.freeConnsMu.Unlock()
<-p.queue
if reaped {
n++
} else {
break
}
}
return n, nil
}
func (p *ConnPool) reaper(frequency time.Duration) {
ticker := time.NewTicker(frequency)
defer ticker.Stop()
for _ = range ticker.C {
if p.closed() {
break
}
n, err := p.ReapStaleConns()
if err != nil {
internal.Logf("ReapStaleConns failed: %s", err)
continue
}
atomic.AddUint32(&p.stats.StaleConns, uint32(n))
}
}

View file

@ -0,0 +1,55 @@
package pool
type SingleConnPool struct {
cn *Conn
}
var _ Pooler = (*SingleConnPool)(nil)
func NewSingleConnPool(cn *Conn) *SingleConnPool {
return &SingleConnPool{
cn: cn,
}
}
func (p *SingleConnPool) NewConn() (*Conn, error) {
panic("not implemented")
}
func (p *SingleConnPool) CloseConn(*Conn) error {
panic("not implemented")
}
func (p *SingleConnPool) Get() (*Conn, bool, error) {
return p.cn, false, nil
}
func (p *SingleConnPool) Put(cn *Conn) error {
if p.cn != cn {
panic("p.cn != cn")
}
return nil
}
func (p *SingleConnPool) Remove(cn *Conn) error {
if p.cn != cn {
panic("p.cn != cn")
}
return nil
}
func (p *SingleConnPool) Len() int {
return 1
}
func (p *SingleConnPool) FreeLen() int {
return 0
}
func (p *SingleConnPool) Stats() *Stats {
return nil
}
func (p *SingleConnPool) Close() error {
return nil
}

255
vendor/github.com/go-pg/pg/internal/pool/pool_test.go generated vendored Normal file
View file

@ -0,0 +1,255 @@
package pool_test
import (
"testing"
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/go-pg/pg/internal/pool"
)
var _ = Describe("ConnPool", func() {
var connPool *pool.ConnPool
BeforeEach(func() {
connPool = pool.NewConnPool(&pool.Options{
Dialer: dummyDialer,
PoolSize: 10,
PoolTimeout: time.Hour,
IdleTimeout: time.Millisecond,
IdleCheckFrequency: time.Millisecond,
})
})
AfterEach(func() {
connPool.Close()
})
It("should unblock client when conn is removed", func() {
// Reserve one connection.
cn, _, err := connPool.Get()
Expect(err).NotTo(HaveOccurred())
// Reserve all other connections.
var cns []*pool.Conn
for i := 0; i < 9; i++ {
cn, _, err := connPool.Get()
Expect(err).NotTo(HaveOccurred())
cns = append(cns, cn)
}
started := make(chan bool, 1)
done := make(chan bool, 1)
go func() {
defer GinkgoRecover()
started <- true
_, _, err := connPool.Get()
Expect(err).NotTo(HaveOccurred())
done <- true
err = connPool.Put(cn)
Expect(err).NotTo(HaveOccurred())
}()
<-started
// Check that Get is blocked.
select {
case <-done:
Fail("Get is not blocked")
default:
// ok
}
err = connPool.Remove(cn)
Expect(err).NotTo(HaveOccurred())
// Check that Ping is unblocked.
select {
case <-done:
// ok
case <-time.After(time.Second):
Fail("Get is not unblocked")
}
for _, cn := range cns {
err = connPool.Put(cn)
Expect(err).NotTo(HaveOccurred())
}
})
})
var _ = Describe("conns reaper", func() {
const idleTimeout = time.Minute
const maxAge = time.Hour
var connPool *pool.ConnPool
var conns, staleConns, closedConns []*pool.Conn
assert := func(typ string) {
BeforeEach(func() {
closedConns = nil
connPool = pool.NewConnPool(&pool.Options{
Dialer: dummyDialer,
PoolSize: 10,
PoolTimeout: time.Second,
IdleTimeout: idleTimeout,
MaxAge: maxAge,
IdleCheckFrequency: time.Hour,
OnClose: func(cn *pool.Conn) error {
closedConns = append(closedConns, cn)
return nil
},
})
conns = nil
// add stale connections
staleConns = nil
for i := 0; i < 3; i++ {
cn, _, err := connPool.Get()
Expect(err).NotTo(HaveOccurred())
switch typ {
case "idle":
cn.UsedAt = time.Now().Add(-2 * idleTimeout)
case "aged":
cn.InitedAt = time.Now().Add(-2 * maxAge)
}
conns = append(conns, cn)
staleConns = append(staleConns, cn)
}
// add fresh connections
for i := 0; i < 3; i++ {
cn, _, err := connPool.Get()
Expect(err).NotTo(HaveOccurred())
conns = append(conns, cn)
}
for _, cn := range conns {
if cn.InitedAt.IsZero() {
cn.InitedAt = time.Now()
}
Expect(connPool.Put(cn)).NotTo(HaveOccurred())
}
Expect(connPool.Len()).To(Equal(6))
Expect(connPool.FreeLen()).To(Equal(6))
n, err := connPool.ReapStaleConns()
Expect(err).NotTo(HaveOccurred())
Expect(n).To(Equal(3))
})
AfterEach(func() {
_ = connPool.Close()
Expect(connPool.Len()).To(Equal(0))
Expect(connPool.FreeLen()).To(Equal(0))
Expect(len(closedConns)).To(Equal(len(conns)))
Expect(closedConns).To(ConsistOf(conns))
})
It("reaps stale connections", func() {
Expect(connPool.Len()).To(Equal(3))
Expect(connPool.FreeLen()).To(Equal(3))
})
It("does not reap fresh connections", func() {
n, err := connPool.ReapStaleConns()
Expect(err).NotTo(HaveOccurred())
Expect(n).To(Equal(0))
})
It("stale connections are closed", func() {
Expect(len(closedConns)).To(Equal(len(staleConns)))
Expect(closedConns).To(ConsistOf(staleConns))
})
It("pool is functional", func() {
for j := 0; j < 3; j++ {
var freeCns []*pool.Conn
for i := 0; i < 3; i++ {
cn, _, err := connPool.Get()
Expect(err).NotTo(HaveOccurred())
Expect(cn).NotTo(BeNil())
freeCns = append(freeCns, cn)
}
Expect(connPool.Len()).To(Equal(3))
Expect(connPool.FreeLen()).To(Equal(0))
cn, _, err := connPool.Get()
Expect(err).NotTo(HaveOccurred())
Expect(cn).NotTo(BeNil())
conns = append(conns, cn)
Expect(connPool.Len()).To(Equal(4))
Expect(connPool.FreeLen()).To(Equal(0))
err = connPool.Remove(cn)
Expect(err).NotTo(HaveOccurred())
Expect(connPool.Len()).To(Equal(3))
Expect(connPool.FreeLen()).To(Equal(0))
for _, cn := range freeCns {
err := connPool.Put(cn)
Expect(err).NotTo(HaveOccurred())
}
Expect(connPool.Len()).To(Equal(3))
Expect(connPool.FreeLen()).To(Equal(3))
}
})
}
assert("idle")
assert("aged")
})
var _ = Describe("race", func() {
var connPool *pool.ConnPool
var C, N int
BeforeEach(func() {
C, N = 10, 1000
if testing.Short() {
C = 4
N = 100
}
})
AfterEach(func() {
connPool.Close()
})
It("does not happen on Get, Put, and Remove", func() {
connPool = pool.NewConnPool(&pool.Options{
Dialer: dummyDialer,
PoolSize: 10,
PoolTimeout: time.Minute,
IdleTimeout: time.Millisecond,
IdleCheckFrequency: time.Millisecond,
})
perform(C, func(id int) {
for i := 0; i < N; i++ {
cn, _, err := connPool.Get()
Expect(err).NotTo(HaveOccurred())
if err == nil {
Expect(connPool.Put(cn)).NotTo(HaveOccurred())
}
}
}, func(id int) {
for i := 0; i < N; i++ {
cn, _, err := connPool.Get()
Expect(err).NotTo(HaveOccurred())
if err == nil {
Expect(connPool.Remove(cn)).NotTo(HaveOccurred())
}
}
})
})
})

View file

@ -0,0 +1,90 @@
package pool
import (
"encoding/binary"
"io"
)
type WriteBuffer struct {
Bytes []byte
msgStart, paramStart int
}
func NewWriteBuffer() *WriteBuffer {
return &WriteBuffer{
Bytes: make([]byte, 0, 4096),
}
}
func (buf *WriteBuffer) StartMessage(c byte) {
if c == 0 {
buf.msgStart = len(buf.Bytes)
buf.Bytes = append(buf.Bytes, 0, 0, 0, 0)
} else {
buf.msgStart = len(buf.Bytes) + 1
buf.Bytes = append(buf.Bytes, c, 0, 0, 0, 0)
}
}
func (buf *WriteBuffer) FinishMessage() {
binary.BigEndian.PutUint32(
buf.Bytes[buf.msgStart:], uint32(len(buf.Bytes)-buf.msgStart))
}
func (buf *WriteBuffer) StartParam() {
buf.paramStart = len(buf.Bytes)
buf.Bytes = append(buf.Bytes, 0, 0, 0, 0)
}
func (buf *WriteBuffer) FinishParam() {
binary.BigEndian.PutUint32(
buf.Bytes[buf.paramStart:], uint32(len(buf.Bytes)-buf.paramStart-4))
}
var nullParamLength = int32(-1)
func (buf *WriteBuffer) FinishNullParam() {
binary.BigEndian.PutUint32(
buf.Bytes[buf.paramStart:], uint32(nullParamLength))
}
func (buf *WriteBuffer) Write(b []byte) (int, error) {
buf.Bytes = append(buf.Bytes, b...)
return len(b), nil
}
func (buf *WriteBuffer) WriteInt16(num int16) {
buf.Bytes = append(buf.Bytes, 0, 0)
binary.BigEndian.PutUint16(buf.Bytes[len(buf.Bytes)-2:], uint16(num))
}
func (buf *WriteBuffer) WriteInt32(num int32) {
buf.Bytes = append(buf.Bytes, 0, 0, 0, 0)
binary.BigEndian.PutUint32(buf.Bytes[len(buf.Bytes)-4:], uint32(num))
}
func (buf *WriteBuffer) WriteString(s string) {
buf.Bytes = append(buf.Bytes, s...)
buf.Bytes = append(buf.Bytes, 0)
}
func (buf *WriteBuffer) WriteBytes(b []byte) {
buf.Bytes = append(buf.Bytes, b...)
buf.Bytes = append(buf.Bytes, 0)
}
func (buf *WriteBuffer) WriteByte(c byte) error {
buf.Bytes = append(buf.Bytes, c)
return nil
}
func (buf *WriteBuffer) Reset() {
buf.Bytes = buf.Bytes[:0]
}
func (buf *WriteBuffer) ReadFrom(r io.Reader) (int64, error) {
n, err := r.Read(buf.Bytes[len(buf.Bytes):cap(buf.Bytes)])
buf.Bytes = buf.Bytes[:len(buf.Bytes)+int(n)]
return int64(n), err
}

11
vendor/github.com/go-pg/pg/internal/safe.go generated vendored Normal file
View file

@ -0,0 +1,11 @@
// +build appengine
package internal
func BytesToString(b []byte) string {
return string(b)
}
func StringToBytes(s string) []byte {
return []byte(s)
}

73
vendor/github.com/go-pg/pg/internal/underscore.go generated vendored Normal file
View file

@ -0,0 +1,73 @@
package internal
func isUpper(c byte) bool {
return c >= 'A' && c <= 'Z'
}
func isLower(c byte) bool {
return !isUpper(c)
}
func toUpper(c byte) byte {
return c - 32
}
func toLower(c byte) byte {
return c + 32
}
// Underscore converts "CamelCasedString" to "camel_cased_string".
func Underscore(s string) string {
r := make([]byte, 0, len(s))
for i := 0; i < len(s); i++ {
c := s[i]
if isUpper(c) {
if i > 0 && i+1 < len(s) && (isLower(s[i-1]) || isLower(s[i+1])) {
r = append(r, '_', toLower(c))
} else {
r = append(r, toLower(c))
}
} else {
r = append(r, c)
}
}
return string(r)
}
func ToUpper(s string) string {
if isUpperString(s) {
return s
}
b := make([]byte, len(s))
for i := range b {
c := s[i]
if c >= 'a' && c <= 'z' {
c -= 'a' - 'A'
}
b[i] = c
}
return string(b)
}
func isUpperString(s string) bool {
for i := 0; i < len(s); i++ {
c := s[i]
if c >= 'a' && c <= 'z' {
return false
}
}
return true
}
func ToExported(s string) string {
if len(s) == 0 {
return s
}
if c := s[0]; isLower(c) {
b := []byte(s)
b[0] = toUpper(c)
return string(b)
}
return s
}

23
vendor/github.com/go-pg/pg/internal/underscore_test.go generated vendored Normal file
View file

@ -0,0 +1,23 @@
package internal_test
import (
"testing"
"github.com/go-pg/pg/internal"
)
func TestUnderscore(t *testing.T) {
tests := []struct {
s, wanted string
}{
{"Megacolumn", "megacolumn"},
{"MegaColumn", "mega_column"},
{"MegaColumn_Id", "mega_column__id"},
{"MegaColumn_id", "mega_column_id"},
}
for _, v := range tests {
if got := internal.Underscore(v.s); got != v.wanted {
t.Errorf("got %q, wanted %q", got, v.wanted)
}
}
}

27
vendor/github.com/go-pg/pg/internal/unsafe.go generated vendored Normal file
View file

@ -0,0 +1,27 @@
// +build !appengine
package internal
import (
"reflect"
"unsafe"
)
func BytesToString(b []byte) string {
bytesHeader := (*reflect.SliceHeader)(unsafe.Pointer(&b))
strHeader := reflect.StringHeader{
Data: bytesHeader.Data,
Len: bytesHeader.Len,
}
return *(*string)(unsafe.Pointer(&strHeader))
}
func StringToBytes(s string) []byte {
sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
bh := reflect.SliceHeader{
Data: sh.Data,
Len: sh.Len,
Cap: sh.Len,
}
return *(*[]byte)(unsafe.Pointer(&bh))
}

21
vendor/github.com/go-pg/pg/internal/util.go generated vendored Normal file
View file

@ -0,0 +1,21 @@
package internal
import "reflect"
func SliceNextElem(v reflect.Value) reflect.Value {
if v.Len() < v.Cap() {
v.Set(v.Slice(0, v.Len()+1))
return v.Index(v.Len() - 1)
}
elemType := v.Type().Elem()
if elemType.Kind() == reflect.Ptr {
elem := reflect.New(elemType.Elem())
v.Set(reflect.Append(v, elem))
return elem.Elem()
}
v.Set(reflect.Append(v, reflect.Zero(elemType)))
return v.Index(v.Len() - 1)
}

221
vendor/github.com/go-pg/pg/listener.go generated vendored Normal file
View file

@ -0,0 +1,221 @@
package pg
import (
"errors"
"sync"
"time"
"github.com/go-pg/pg/internal"
"github.com/go-pg/pg/internal/pool"
"github.com/go-pg/pg/types"
)
var errListenerClosed = errors.New("pg: listener is closed")
// A notification received with LISTEN command.
type Notification struct {
Channel string
Payload string
}
// Listener listens for notifications sent with NOTIFY command.
// It's NOT safe for concurrent use by multiple goroutines
// except the Channel API.
type Listener struct {
db *DB
channels []string
mu sync.Mutex
cn *pool.Conn
closed bool
}
func (ln *Listener) conn(readTimeout time.Duration) (*pool.Conn, error) {
ln.mu.Lock()
cn, err := ln._conn(readTimeout)
ln.mu.Unlock()
if err != nil {
if err == pool.ErrClosed {
_ = ln.Close()
return nil, errListenerClosed
}
return nil, err
}
cn.SetTimeout(readTimeout, ln.db.opt.WriteTimeout)
return cn, nil
}
func (ln *Listener) _conn(readTimeout time.Duration) (*pool.Conn, error) {
if ln.closed {
return nil, errListenerClosed
}
if ln.cn != nil {
return ln.cn, nil
}
cn, err := ln.db.pool.NewConn()
if err != nil {
return nil, err
}
if cn.InitedAt.IsZero() {
if err := ln.db.initConn(cn); err != nil {
_ = ln.db.pool.CloseConn(cn)
return nil, err
}
cn.InitedAt = time.Now()
}
if len(ln.channels) > 0 {
if err := ln.listen(cn, ln.channels...); err != nil {
_ = ln.db.pool.CloseConn(cn)
return nil, err
}
}
ln.cn = cn
return cn, nil
}
func (ln *Listener) freeConn(cn *pool.Conn, err error) {
if !isBadConn(err, true) {
return
}
ln.mu.Lock()
if cn == ln.cn {
_ = ln.closeConn(err)
}
ln.mu.Unlock()
}
func (ln *Listener) closeConn(reason error) error {
if !ln.closed {
internal.Logf("pg: discarding bad listener connection: %s", reason)
}
err := ln.db.pool.CloseConn(ln.cn)
ln.cn = nil
return err
}
// Close closes the listener, releasing any open resources.
func (ln *Listener) Close() error {
ln.mu.Lock()
defer ln.mu.Unlock()
if ln.closed {
return errListenerClosed
}
ln.closed = true
if ln.cn != nil {
return ln.closeConn(errListenerClosed)
}
return nil
}
// Channel returns a channel for concurrently receiving notifications.
// The channel is closed with Listener.
func (ln *Listener) Channel() <-chan *Notification {
ch := make(chan *Notification, 100)
go func() {
for {
channel, payload, err := ln.ReceiveTimeout(5 * time.Second)
if err != nil {
if err == errListenerClosed {
break
}
continue
}
ch <- &Notification{channel, payload}
}
close(ch)
}()
return ch
}
// Listen starts listening for notifications on channels.
func (ln *Listener) Listen(channels ...string) error {
cn, err := ln.conn(ln.db.opt.ReadTimeout)
if err != nil {
return err
}
if err := ln.listen(cn, channels...); err != nil {
if err != nil {
ln.freeConn(cn, err)
}
return err
}
ln.channels = appendIfNotExists(ln.channels, channels...)
return nil
}
func (ln *Listener) listen(cn *pool.Conn, channels ...string) error {
for _, channel := range channels {
if err := writeQueryMsg(cn.Writer, ln.db, "LISTEN ?", pgChan(channel)); err != nil {
return err
}
}
return cn.FlushWriter()
}
// Receive indefinitely waits for a notification.
func (ln *Listener) Receive() (channel string, payload string, err error) {
return ln.ReceiveTimeout(0)
}
// ReceiveTimeout waits for a notification until timeout is reached.
func (ln *Listener) ReceiveTimeout(timeout time.Duration) (channel, payload string, err error) {
cn, err := ln.conn(timeout)
if err != nil {
return "", "", err
}
channel, payload, err = readNotification(cn)
if err != nil {
ln.freeConn(cn, err)
return "", "", err
}
return channel, payload, nil
}
func appendIfNotExists(ss []string, es ...string) []string {
loop:
for _, e := range es {
for _, s := range ss {
if s == e {
continue loop
}
}
ss = append(ss, e)
}
return ss
}
type pgChan string
var _ types.ValueAppender = pgChan("")
func (ch pgChan) AppendValue(b []byte, quote int) ([]byte, error) {
if quote == 0 {
return append(b, ch...), nil
}
b = append(b, '"')
for _, c := range []byte(ch) {
if c == '"' {
b = append(b, '"', '"')
} else {
b = append(b, c)
}
}
b = append(b, '"')
return b, nil
}

189
vendor/github.com/go-pg/pg/listener_test.go generated vendored Normal file
View file

@ -0,0 +1,189 @@
package pg_test
import (
"net"
"time"
"github.com/go-pg/pg"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Context("Listener", func() {
var db *pg.DB
var ln *pg.Listener
BeforeEach(func() {
opt := pgOptions()
opt.PoolSize = 2
opt.PoolTimeout = time.Second
db = pg.Connect(opt)
ln = db.Listen("test.channel")
})
var _ = AfterEach(func() {
_ = ln.Close()
_ = db.Close()
})
It("reuses connection", func() {
for i := 0; i < 100; i++ {
_, _, err := ln.ReceiveTimeout(time.Nanosecond)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(MatchRegexp(".+ i/o timeout"))
}
st := db.PoolStats()
Expect(st.Hits).To(Equal(uint32(0)))
Expect(st.Misses).To(Equal(uint32(0)))
Expect(st.Timeouts).To(Equal(uint32(0)))
Expect(st.TotalConns).To(Equal(uint32(1)))
Expect(st.FreeConns).To(Equal(uint32(0)))
})
It("listens for notifications", func() {
wait := make(chan struct{}, 2)
go func() {
defer GinkgoRecover()
wait <- struct{}{}
channel, payload, err := ln.Receive()
Expect(err).NotTo(HaveOccurred())
Expect(channel).To(Equal("test.channel"))
Expect(payload).To(Equal(""))
wait <- struct{}{}
}()
select {
case <-wait:
// ok
case <-time.After(3 * time.Second):
Fail("timeout")
}
_, err := db.Exec(`NOTIFY "test.channel"`)
Expect(err).NotTo(HaveOccurred())
select {
case <-wait:
// ok
case <-time.After(3 * time.Second):
Fail("timeout")
}
})
It("is closed when DB is closed", func() {
wait := make(chan struct{}, 2)
go func() {
defer GinkgoRecover()
wait <- struct{}{}
_, _, err := ln.Receive()
Expect(err.Error()).To(SatisfyAny(
Equal("EOF"),
MatchRegexp(`use of closed (file or )?network connection$`),
))
wait <- struct{}{}
}()
select {
case <-wait:
// ok
case <-time.After(time.Second):
Fail("timeout")
}
select {
case <-wait:
Fail("Receive is not blocked")
case <-time.After(time.Second):
// ok
}
Expect(db.Close()).To(BeNil())
select {
case <-wait:
// ok
case <-time.After(3 * time.Second):
Fail("Listener is not closed")
}
_, _, err := ln.Receive()
Expect(err).To(MatchError("pg: listener is closed"))
err = ln.Close()
Expect(err).To(MatchError("pg: listener is closed"))
})
It("returns an error on timeout", func() {
channel, payload, err := ln.ReceiveTimeout(time.Second)
Expect(err.(net.Error).Timeout()).To(BeTrue())
Expect(channel).To(Equal(""))
Expect(payload).To(Equal(""))
})
It("reconnects on listen error", func() {
cn := ln.CurrentConn()
Expect(cn).NotTo(BeNil())
cn.SetNetConn(&badConn{})
err := ln.Listen("test_channel2")
Expect(err).Should(MatchError("bad connection"))
err = ln.Listen("test_channel2")
Expect(err).NotTo(HaveOccurred())
})
It("reconnects on receive error", func() {
cn := ln.CurrentConn()
Expect(cn).NotTo(BeNil())
cn.SetNetConn(&badConn{})
_, _, err := ln.ReceiveTimeout(time.Second)
Expect(err).Should(MatchError("bad connection"))
_, _, err = ln.ReceiveTimeout(time.Second)
Expect(err.(net.Error).Timeout()).To(BeTrue())
wait := make(chan struct{}, 2)
go func() {
defer GinkgoRecover()
wait <- struct{}{}
_, _, err := ln.Receive()
Expect(err).NotTo(HaveOccurred())
wait <- struct{}{}
}()
select {
case <-wait:
// ok
case <-time.After(3 * time.Second):
Fail("timeout")
}
select {
case <-wait:
Fail("Receive is not blocked")
case <-time.After(time.Second):
// ok
}
_, err = db.Exec(`NOTIFY "test.channel"`)
Expect(err).NotTo(HaveOccurred())
select {
case <-wait:
// ok
case <-time.After(3 * time.Second):
Fail("timeout")
}
})
})

180
vendor/github.com/go-pg/pg/loader_test.go generated vendored Normal file
View file

@ -0,0 +1,180 @@
package pg_test
import (
"errors"
"github.com/go-pg/pg"
"github.com/go-pg/pg/orm"
. "gopkg.in/check.v1"
)
type LoaderTest struct {
db *pg.DB
}
var _ = Suite(&LoaderTest{})
func (t *LoaderTest) SetUpTest(c *C) {
t.db = pg.Connect(pgOptions())
}
func (t *LoaderTest) TearDownTest(c *C) {
c.Assert(t.db.Close(), IsNil)
}
type numLoader struct {
Num int
}
type embeddedLoader struct {
*numLoader
Num2 int
}
type multipleLoader struct {
One struct {
Num int
}
Num int
}
func (t *LoaderTest) TestQuery(c *C) {
var dst numLoader
_, err := t.db.Query(&dst, "SELECT 1 AS num")
c.Assert(err, IsNil)
c.Assert(dst.Num, Equals, 1)
}
func (t *LoaderTest) TestQueryNull(c *C) {
var dst numLoader
_, err := t.db.Query(&dst, "SELECT NULL AS num")
c.Assert(err, IsNil)
c.Assert(dst.Num, Equals, 0)
}
func (t *LoaderTest) TestQueryEmbeddedStruct(c *C) {
src := &embeddedLoader{
numLoader: &numLoader{
Num: 1,
},
Num2: 2,
}
dst := &embeddedLoader{
numLoader: &numLoader{},
}
_, err := t.db.QueryOne(dst, "SELECT ?num AS num, ?num2 as num2", src)
c.Assert(err, IsNil)
c.Assert(dst, DeepEquals, src)
}
func (t *LoaderTest) TestQueryNestedStructs(c *C) {
src := &multipleLoader{}
src.One.Num = 1
src.Num = 2
dst := &multipleLoader{}
_, err := t.db.QueryOne(dst, `SELECT ?one__num AS one__num, ?num as num`, src)
c.Assert(err, IsNil)
c.Assert(dst, DeepEquals, src)
}
func (t *LoaderTest) TestQueryStmt(c *C) {
stmt, err := t.db.Prepare("SELECT 1 AS num")
c.Assert(err, IsNil)
defer stmt.Close()
dst := &numLoader{}
_, err = stmt.Query(dst)
c.Assert(err, IsNil)
c.Assert(dst.Num, Equals, 1)
}
func (t *LoaderTest) TestQueryInts(c *C) {
var ids pg.Ints
_, err := t.db.Query(&ids, "SELECT s.num AS num FROM generate_series(0, 10) AS s(num)")
c.Assert(err, IsNil)
c.Assert(ids, DeepEquals, pg.Ints{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10})
}
func (t *LoaderTest) TestQueryInts2(c *C) {
var ints pg.Ints
_, err := t.db.Query(&ints, "SELECT * FROM generate_series(1, 1000000)")
c.Assert(err, IsNil)
c.Assert(ints, HasLen, 1000000)
}
func (t *LoaderTest) TestQueryStrings(c *C) {
var strings pg.Strings
_, err := t.db.Query(&strings, "SELECT 'hello'")
c.Assert(err, IsNil)
c.Assert(strings, DeepEquals, pg.Strings{"hello"})
}
type errLoader string
var _ orm.Model = errLoader("")
func (errLoader) Init() error {
return nil
}
func (m errLoader) NewModel() orm.ColumnScanner {
return m
}
func (errLoader) AddModel(_ orm.ColumnScanner) error {
return nil
}
func (errLoader) AfterQuery(_ orm.DB) error {
return nil
}
func (errLoader) AfterSelect(_ orm.DB) error {
return nil
}
func (errLoader) BeforeInsert(_ orm.DB) error {
return nil
}
func (errLoader) AfterInsert(_ orm.DB) error {
return nil
}
func (errLoader) BeforeUpdate(_ orm.DB) error {
return nil
}
func (errLoader) AfterUpdate(_ orm.DB) error {
return nil
}
func (errLoader) BeforeDelete(_ orm.DB) error {
return nil
}
func (errLoader) AfterDelete(_ orm.DB) error {
return nil
}
func (m errLoader) ScanColumn(int, string, []byte) error {
return errors.New(string(m))
}
func (t *LoaderTest) TestLoaderError(c *C) {
tx, err := t.db.Begin()
c.Assert(err, IsNil)
defer tx.Rollback()
loader := errLoader("my error")
_, err = tx.QueryOne(loader, "SELECT 1, 2")
c.Assert(err.Error(), Equals, "my error")
// Verify that client is still functional.
var n1, n2 int
_, err = tx.QueryOne(pg.Scan(&n1, &n2), "SELECT 1, 2")
c.Assert(err, IsNil)
c.Assert(n1, Equals, 1)
c.Assert(n2, Equals, 2)
}

230
vendor/github.com/go-pg/pg/main_test.go generated vendored Normal file
View file

@ -0,0 +1,230 @@
package pg_test
import (
"bytes"
"database/sql/driver"
"net"
"strings"
"sync"
"sync/atomic"
"testing"
"time"
"github.com/go-pg/pg"
. "github.com/onsi/ginkgo"
. "gopkg.in/check.v1"
)
func TestUnixSocket(t *testing.T) {
opt := pgOptions()
opt.Network = "unix"
opt.Addr = "/var/run/postgresql/.s.PGSQL.5432"
opt.TLSConfig = nil
db := pg.Connect(opt)
defer db.Close()
_, err := db.Exec("SELECT 'test_unix_socket'")
if err != nil {
t.Fatal(err)
}
}
func TestGocheck(t *testing.T) { TestingT(t) }
var _ = Suite(&DBTest{})
type DBTest struct {
db *pg.DB
}
func (t *DBTest) SetUpTest(c *C) {
t.db = pg.Connect(pgOptions())
}
func (t *DBTest) TearDownTest(c *C) {
c.Assert(t.db.Close(), IsNil)
}
func (t *DBTest) TestQueryZeroRows(c *C) {
res, err := t.db.Query(pg.Discard, "SELECT 1 WHERE 1 != 1")
c.Assert(err, IsNil)
c.Assert(res.RowsAffected(), Equals, 0)
}
func (t *DBTest) TestQueryOneErrNoRows(c *C) {
_, err := t.db.QueryOne(pg.Discard, "SELECT 1 WHERE 1 != 1")
c.Assert(err, Equals, pg.ErrNoRows)
}
func (t *DBTest) TestQueryOneErrMultiRows(c *C) {
_, err := t.db.QueryOne(pg.Discard, "SELECT generate_series(0, 1)")
c.Assert(err, Equals, pg.ErrMultiRows)
}
func (t *DBTest) TestExecOne(c *C) {
res, err := t.db.ExecOne("SELECT 'test_exec_one'")
c.Assert(err, IsNil)
c.Assert(res.RowsAffected(), Equals, 1)
}
func (t *DBTest) TestExecOneErrNoRows(c *C) {
_, err := t.db.ExecOne("SELECT 1 WHERE 1 != 1")
c.Assert(err, Equals, pg.ErrNoRows)
}
func (t *DBTest) TestExecOneErrMultiRows(c *C) {
_, err := t.db.ExecOne("SELECT generate_series(0, 1)")
c.Assert(err, Equals, pg.ErrMultiRows)
}
func (t *DBTest) TestScan(c *C) {
var dst int
_, err := t.db.QueryOne(pg.Scan(&dst), "SELECT 1")
c.Assert(err, IsNil)
c.Assert(dst, Equals, 1)
}
func (t *DBTest) TestExec(c *C) {
res, err := t.db.Exec("CREATE TEMP TABLE test(id serial PRIMARY KEY)")
c.Assert(err, IsNil)
c.Assert(res.RowsAffected(), Equals, -1)
res, err = t.db.Exec("INSERT INTO test VALUES (1)")
c.Assert(err, IsNil)
c.Assert(res.RowsAffected(), Equals, 1)
}
func (t *DBTest) TestStatementExec(c *C) {
res, err := t.db.Exec("CREATE TEMP TABLE test(id serial PRIMARY KEY)")
c.Assert(err, IsNil)
c.Assert(res.RowsAffected(), Equals, -1)
stmt, err := t.db.Prepare("INSERT INTO test VALUES($1)")
c.Assert(err, IsNil)
defer stmt.Close()
res, err = stmt.Exec(1)
c.Assert(err, IsNil)
c.Assert(res.RowsAffected(), Equals, 1)
}
func (t *DBTest) TestLargeWriteRead(c *C) {
src := bytes.Repeat([]byte{0x1}, 1e6)
var dst []byte
_, err := t.db.QueryOne(pg.Scan(&dst), "SELECT ?", src)
c.Assert(err, IsNil)
c.Assert(dst, DeepEquals, src)
}
func (t *DBTest) TestIntegrityError(c *C) {
_, err := t.db.Exec("DO $$BEGIN RAISE unique_violation USING MESSAGE='foo'; END$$;")
c.Assert(err.(pg.Error).IntegrityViolation(), Equals, true)
}
type customStrSlice []string
func (s customStrSlice) Value() (driver.Value, error) {
return strings.Join(s, "\n"), nil
}
func (s *customStrSlice) Scan(v interface{}) error {
if v == nil {
*s = nil
return nil
}
b := v.([]byte)
if len(b) == 0 {
*s = []string{}
return nil
}
*s = strings.Split(string(b), "\n")
return nil
}
func (t *DBTest) TestScannerValueOnStruct(c *C) {
src := customStrSlice{"foo", "bar"}
dst := struct{ Dst customStrSlice }{}
_, err := t.db.QueryOne(&dst, "SELECT ? AS dst", src)
c.Assert(err, IsNil)
c.Assert(dst.Dst, DeepEquals, src)
}
//------------------------------------------------------------------------------
type badConnError string
func (e badConnError) Error() string { return string(e) }
func (e badConnError) Timeout() bool { return false }
func (e badConnError) Temporary() bool { return false }
type badConn struct {
net.TCPConn
readDelay, writeDelay time.Duration
readErr, writeErr error
}
var _ net.Conn = &badConn{}
func (cn *badConn) Read([]byte) (int, error) {
if cn.readDelay != 0 {
time.Sleep(cn.readDelay)
}
if cn.readErr != nil {
return 0, cn.readErr
}
return 0, badConnError("bad connection")
}
func (cn *badConn) Write([]byte) (int, error) {
if cn.writeDelay != 0 {
time.Sleep(cn.writeDelay)
}
if cn.writeErr != nil {
return 0, cn.writeErr
}
return 0, badConnError("bad connection")
}
func perform(n int, cbs ...func(int)) {
var wg sync.WaitGroup
for _, cb := range cbs {
for i := 0; i < n; i++ {
wg.Add(1)
go func(cb func(int), i int) {
defer GinkgoRecover()
defer wg.Done()
cb(i)
}(cb, i)
}
}
wg.Wait()
}
func eventually(fn func() error, timeout time.Duration) (err error) {
done := make(chan struct{})
var exit int32
go func() {
for atomic.LoadInt32(&exit) == 0 {
err = fn()
if err == nil {
close(done)
return
}
time.Sleep(timeout / 100)
}
}()
select {
case <-done:
return nil
case <-time.After(timeout):
atomic.StoreInt32(&exit, 1)
return err
}
}

1112
vendor/github.com/go-pg/pg/messages.go generated vendored Normal file

File diff suppressed because it is too large Load diff

225
vendor/github.com/go-pg/pg/options.go generated vendored Normal file
View file

@ -0,0 +1,225 @@
package pg
import (
"crypto/tls"
"errors"
"fmt"
"net"
"net/url"
"runtime"
"strings"
"time"
"github.com/go-pg/pg/internal/pool"
)
// Database connection options.
type Options struct {
// Network type, either tcp or unix.
// Default is tcp.
Network string
// TCP host:port or Unix socket depending on Network.
Addr string
// Dialer creates new network connection and has priority over
// Network and Addr options.
Dialer func(network, addr string) (net.Conn, error)
// Hook that is called when new connection is established.
OnConnect func(*DB) error
User string
Password string
Database string
// TLS config for secure connections.
TLSConfig *tls.Config
// Maximum number of retries before giving up.
// Default is to not retry failed queries.
MaxRetries int
// Whether to retry queries cancelled because of statement_timeout.
RetryStatementTimeout bool
// Minimum backoff between each retry.
// Default is 250 milliseconds; -1 disables backoff.
MinRetryBackoff time.Duration
// Maximum backoff between each retry.
// Default is 4 seconds; -1 disables backoff.
MaxRetryBackoff time.Duration
// Dial timeout for establishing new connections.
// Default is 5 seconds.
DialTimeout time.Duration
// Timeout for socket reads. If reached, commands will fail
// with a timeout instead of blocking.
ReadTimeout time.Duration
// Timeout for socket writes. If reached, commands will fail
// with a timeout instead of blocking.
WriteTimeout time.Duration
// Maximum number of socket connections.
// Default is 10 connections per every CPU as reported by runtime.NumCPU.
PoolSize int
// Time for which client waits for free connection if all
// connections are busy before returning an error.
// Default is 5 seconds.
PoolTimeout time.Duration
// Time after which client closes idle connections.
// Default is to not close idle connections.
IdleTimeout time.Duration
// Connection age at which client retires (closes) the connection.
// Primarily useful with proxies like HAProxy.
// Default is to not close aged connections.
MaxAge time.Duration
// Frequency of idle checks.
// Default is 1 minute.
IdleCheckFrequency time.Duration
// When true Tx does not issue BEGIN, COMMIT, or ROLLBACK.
// Also underlying database connection is immediately returned to the pool.
// This is primarily useful for running your database tests in one big
// transaction, because PostgreSQL does not support nested transactions.
DisableTransaction bool
}
func (opt *Options) init() {
if opt.Network == "" {
opt.Network = "tcp"
}
if opt.Addr == "" {
switch opt.Network {
case "tcp":
opt.Addr = "localhost:5432"
case "unix":
opt.Addr = "/var/run/postgresql/.s.PGSQL.5432"
}
}
if opt.PoolSize == 0 {
opt.PoolSize = 10 * runtime.NumCPU()
}
if opt.PoolTimeout == 0 {
if opt.ReadTimeout != 0 {
opt.PoolTimeout = opt.ReadTimeout + time.Second
} else {
opt.PoolTimeout = 30 * time.Second
}
}
if opt.DialTimeout == 0 {
opt.DialTimeout = 5 * time.Second
}
if opt.IdleCheckFrequency == 0 {
opt.IdleCheckFrequency = time.Minute
}
switch opt.MinRetryBackoff {
case -1:
opt.MinRetryBackoff = 0
case 0:
opt.MinRetryBackoff = 250 * time.Millisecond
}
switch opt.MaxRetryBackoff {
case -1:
opt.MaxRetryBackoff = 0
case 0:
opt.MaxRetryBackoff = 4 * time.Second
}
}
// ParseURL parses an URL into options that can be used to connect to PostgreSQL.
func ParseURL(sURL string) (*Options, error) {
parsedUrl, err := url.Parse(sURL)
if err != nil {
return nil, err
}
// scheme
if parsedUrl.Scheme != "postgres" {
return nil, errors.New("pg: invalid scheme: " + parsedUrl.Scheme)
}
// host and port
options := &Options{
Addr: parsedUrl.Host,
}
if !strings.Contains(options.Addr, ":") {
options.Addr = options.Addr + ":5432"
}
// username and password
if parsedUrl.User != nil {
options.User = parsedUrl.User.Username()
if password, ok := parsedUrl.User.Password(); ok {
options.Password = password
}
}
if options.User == "" {
options.User = "postgres"
}
// database
if len(strings.Trim(parsedUrl.Path, "/")) > 0 {
options.Database = parsedUrl.Path[1:]
} else {
return nil, errors.New("pg: database name not provided")
}
// ssl mode
query, err := url.ParseQuery(parsedUrl.RawQuery)
if err != nil {
return nil, err
}
if sslMode, ok := query["sslmode"]; ok && len(sslMode) > 0 {
switch sslMode[0] {
case "allow":
fallthrough
case "prefer":
options.TLSConfig = &tls.Config{InsecureSkipVerify: true}
case "disable":
options.TLSConfig = nil
default:
return nil, errors.New(fmt.Sprintf("pg: sslmode '%v' is not supported", sslMode[0]))
}
} else {
options.TLSConfig = &tls.Config{InsecureSkipVerify: true}
}
delete(query, "sslmode")
if len(query) > 0 {
return nil, errors.New("pg: options other than 'sslmode' are not supported")
}
return options, nil
}
func (opt *Options) getDialer() func() (net.Conn, error) {
if opt.Dialer != nil {
return func() (net.Conn, error) {
return opt.Dialer(opt.Network, opt.Addr)
}
}
return func() (net.Conn, error) {
return net.DialTimeout(opt.Network, opt.Addr, opt.DialTimeout)
}
}
func newConnPool(opt *Options) *pool.ConnPool {
return pool.NewConnPool(&pool.Options{
Dialer: opt.getDialer(),
PoolSize: opt.PoolSize,
PoolTimeout: opt.PoolTimeout,
IdleTimeout: opt.IdleTimeout,
IdleCheckFrequency: opt.IdleCheckFrequency,
OnClose: func(cn *pool.Conn) error {
return terminateConn(cn)
},
})
}

177
vendor/github.com/go-pg/pg/options_test.go generated vendored Normal file
View file

@ -0,0 +1,177 @@
// +build go1.7
package pg
import (
"errors"
"testing"
)
func TestParseURL(t *testing.T) {
cases := []struct {
url string
addr string
user string
password string
database string
tls bool
err error
}{
{
"postgres://vasya:pupkin@somewhere.at.amazonaws.com:5432/postgres",
"somewhere.at.amazonaws.com:5432",
"vasya",
"pupkin",
"postgres",
true,
nil,
},
{
"postgres://vasya:pupkin@somewhere.at.amazonaws.com:5432/postgres?sslmode=allow",
"somewhere.at.amazonaws.com:5432",
"vasya",
"pupkin",
"postgres",
true,
nil,
},
{
"postgres://vasya:pupkin@somewhere.at.amazonaws.com:5432/postgres?sslmode=prefer",
"somewhere.at.amazonaws.com:5432",
"vasya",
"pupkin",
"postgres",
true,
nil,
},
{
"postgres://vasya:pupkin@somewhere.at.amazonaws.com:5432/postgres?sslmode=require",
"somewhere.at.amazonaws.com:5432",
"vasya",
"pupkin",
"postgres",
true,
errors.New("pg: sslmode 'require' is not supported"),
},
{
"postgres://vasya:pupkin@somewhere.at.amazonaws.com:5432/postgres?sslmode=verify-ca",
"somewhere.at.amazonaws.com:5432",
"vasya",
"pupkin",
"postgres",
true,
errors.New("pg: sslmode 'verify-ca' is not supported"),
},
{
"postgres://vasya:pupkin@somewhere.at.amazonaws.com:5432/postgres?sslmode=verify-full",
"somewhere.at.amazonaws.com:5432",
"vasya",
"pupkin",
"postgres",
true,
errors.New("pg: sslmode 'verify-full' is not supported"),
},
{
"postgres://vasya:pupkin@somewhere.at.amazonaws.com:5432/postgres?sslmode=disable",
"somewhere.at.amazonaws.com:5432",
"vasya",
"pupkin",
"postgres",
false,
nil,
},
{
"postgres://vasya:pupkin@somewhere.at.amazonaws.com:5432/",
"somewhere.at.amazonaws.com:5432",
"vasya",
"pupkin",
"",
true,
errors.New("pg: database name not provided"),
},
{
"postgres://vasya:pupkin@somewhere.at.amazonaws.com/postgres",
"somewhere.at.amazonaws.com:5432",
"vasya",
"pupkin",
"postgres",
true,
nil,
},
{
"postgres://vasya:pupkin@somewhere.at.amazonaws.com:5432/postgres?abc=123",
"somewhere.at.amazonaws.com:5432",
"vasya",
"pupkin",
"postgres",
true,
errors.New("pg: options other than 'sslmode' are not supported"),
},
{
"postgres://vasya@somewhere.at.amazonaws.com:5432/postgres",
"somewhere.at.amazonaws.com:5432",
"vasya",
"",
"postgres",
true,
nil,
},
{
"postgres://somewhere.at.amazonaws.com:5432/postgres",
"somewhere.at.amazonaws.com:5432",
"postgres",
"",
"postgres",
true,
nil,
},
{
"http://google.com/test",
"google.com:5432",
"postgres",
"",
"test",
true,
errors.New("pg: invalid scheme: http"),
},
}
for _, c := range cases {
t.Run(c.url, func(t *testing.T) {
o, err := ParseURL(c.url)
if c.err == nil && err != nil {
t.Fatalf("unexpected error: '%q'", err)
return
}
if c.err != nil && err != nil {
if c.err.Error() != err.Error() {
t.Fatalf("expected error %q, want %q", err, c.err)
}
return
}
if c.err != nil && err == nil {
t.Errorf("expected error %q, got nothing", c.err)
}
if o.Addr != c.addr {
t.Errorf("addr: got %q, want %q", o.Addr, c.addr)
}
if o.User != c.user {
t.Errorf("user: got %q, want %q", o.User, c.user)
}
if o.Password != c.password {
t.Errorf("password: got %q, want %q", o.Password, c.password)
}
if o.Database != c.database {
t.Errorf("database: got %q, want %q", o.Database, c.database)
}
if c.tls {
if o.TLSConfig == nil {
t.Error("got nil TLSConfig, expected a TLSConfig")
} else if !o.TLSConfig.InsecureSkipVerify {
t.Error("must set InsecureSkipVerify to true in TLSConfig, got false")
}
}
})
}
}

120
vendor/github.com/go-pg/pg/orm/count_estimate.go generated vendored Normal file
View file

@ -0,0 +1,120 @@
package orm
import (
"fmt"
"sync"
"github.com/go-pg/pg/internal"
)
// Placeholder that is replaced with count(*).
const placeholder = `'_go_pg_placeholder'`
// https://wiki.postgresql.org/wiki/Count_estimate
var pgCountEstimateFunc = fmt.Sprintf(`
CREATE OR REPLACE FUNCTION _go_pg_count_estimate_v2(query text, threshold int)
RETURNS int AS $$
DECLARE
rec record;
nrows int;
BEGIN
FOR rec IN EXECUTE 'EXPLAIN ' || query LOOP
nrows := substring(rec."QUERY PLAN" FROM ' rows=(\d+)');
EXIT WHEN nrows IS NOT NULL;
END LOOP;
-- Return the estimation if there are too many rows.
IF nrows > threshold THEN
RETURN nrows;
END IF;
-- Otherwise execute real count query.
query := replace(query, 'SELECT '%s'', 'SELECT count(*)');
EXECUTE query INTO nrows;
IF nrows IS NULL THEN
nrows := 0;
END IF;
RETURN nrows;
END;
$$ LANGUAGE plpgsql;
`, placeholder)
// CountEstimate uses EXPLAIN to get estimated number of rows matching the query.
// If that number is bigger than the threshold it returns the estimation.
// Otherwise it executes another query using count aggregate function and
// returns the result.
//
// Based on https://wiki.postgresql.org/wiki/Count_estimate
func (q *Query) CountEstimate(threshold int) (int, error) {
if q.stickyErr != nil {
return 0, q.stickyErr
}
query, err := q.countSelectQuery(placeholder).AppendQuery(nil)
if err != nil {
return 0, err
}
for i := 0; i < 3; i++ {
var count int
_, err = q.db.QueryOne(
Scan(&count),
"SELECT _go_pg_count_estimate_v2(?, ?)",
string(query), threshold,
)
if err != nil {
if pgerr, ok := err.(internal.PGError); ok && pgerr.Field('C') == "42883" {
// undefined_function
if err := q.createCountEstimateFunc(); err != nil {
return 0, err
}
continue
}
}
return count, err
}
return 0, err
}
func (q *Query) createCountEstimateFunc() error {
_, err := q.db.Exec(pgCountEstimateFunc)
return err
}
// SelectAndCountEstimate runs Select and CountEstimate in two goroutines,
// waits for them to finish and returns the result.
func (q *Query) SelectAndCountEstimate(threshold int, values ...interface{}) (count int, err error) {
if q.stickyErr != nil {
return 0, q.stickyErr
}
var wg sync.WaitGroup
wg.Add(2)
var mu sync.Mutex
go func() {
defer wg.Done()
if e := q.Select(values...); e != nil {
mu.Lock()
err = e
mu.Unlock()
}
}()
go func() {
defer wg.Done()
var e error
count, e = q.CountEstimate(threshold)
if e != nil {
mu.Lock()
err = e
mu.Unlock()
}
}()
wg.Wait()
return count, err
}

122
vendor/github.com/go-pg/pg/orm/create_table.go generated vendored Normal file
View file

@ -0,0 +1,122 @@
package orm
import (
"errors"
"strconv"
)
type CreateTableOptions struct {
Temp bool
IfNotExists bool
Varchar int // replaces PostgreSQL data type `text` with `varchar(n)`
FKConstraints bool // whether to create foreign key constraints
}
func CreateTable(db DB, model interface{}, opt *CreateTableOptions) (Result, error) {
return NewQuery(db, model).CreateTable(opt)
}
type createTableQuery struct {
q *Query
opt *CreateTableOptions
}
func (q createTableQuery) Copy() QueryAppender {
return q
}
func (q createTableQuery) Query() *Query {
return q.q
}
func (q createTableQuery) AppendQuery(b []byte) ([]byte, error) {
if q.q.stickyErr != nil {
return nil, q.q.stickyErr
}
if q.q.model == nil {
return nil, errors.New("pg: Model is nil")
}
table := q.q.model.Table()
b = append(b, "CREATE "...)
if q.opt != nil && q.opt.Temp {
b = append(b, "TEMP "...)
}
b = append(b, "TABLE "...)
if q.opt != nil && q.opt.IfNotExists {
b = append(b, "IF NOT EXISTS "...)
}
b = q.q.appendTableName(b)
b = append(b, " ("...)
for i, field := range table.Fields {
b = append(b, field.Column...)
b = append(b, " "...)
if q.opt != nil && q.opt.Varchar > 0 &&
field.SQLType == "text" && !field.HasFlag(customTypeFlag) {
b = append(b, "varchar("...)
b = strconv.AppendInt(b, int64(q.opt.Varchar), 10)
b = append(b, ")"...)
} else {
b = append(b, field.SQLType...)
}
if field.HasFlag(NotNullFlag) {
b = append(b, " NOT NULL"...)
}
if field.HasFlag(UniqueFlag) {
b = append(b, " UNIQUE"...)
}
if field.Default != "" {
b = append(b, " DEFAULT "...)
b = append(b, field.Default...)
}
if i != len(table.Fields)-1 {
b = append(b, ", "...)
}
}
b = appendPKConstraint(b, table.PKs)
if q.opt != nil && q.opt.FKConstraints {
for _, rel := range table.Relations {
b = q.appendFKConstraint(b, table, rel)
}
}
b = append(b, ")"...)
return b, nil
}
func appendPKConstraint(b []byte, pks []*Field) []byte {
if len(pks) == 0 {
return b
}
b = append(b, ", PRIMARY KEY ("...)
b = appendColumns(b, pks)
b = append(b, ")"...)
return b
}
func (q createTableQuery) appendFKConstraint(b []byte, table *Table, rel *Relation) []byte {
if rel.Type != HasOneRelation {
return b
}
b = append(b, ", FOREIGN KEY ("...)
b = appendColumns(b, rel.FKs)
b = append(b, ")"...)
b = append(b, " REFERENCES "...)
b = q.q.FormatQuery(b, string(rel.JoinTable.Name))
b = append(b, " ("...)
b = appendColumns(b, rel.JoinTable.PKs)
b = append(b, ")"...)
b = append(b, " ON DELETE CASCADE"...)
return b
}

70
vendor/github.com/go-pg/pg/orm/create_table_test.go generated vendored Normal file
View file

@ -0,0 +1,70 @@
package orm
import (
"database/sql"
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
type CreateTableModel struct {
Id int
Int8 int8
Uint8 uint8
Int16 int16
Uint16 uint16
Int32 int32
Uint32 uint32
Int64 int64
Uint64 uint64
Float32 float32
Float64 float64
Decimal float64 `sql:"type:'decimal(10,10)'"`
ByteSlice []byte
ByteArray [32]byte
String string `sql:"default:'D\\'Angelo'"`
Varchar string `sql:",type:varchar(500)"`
Time time.Time `sql:"default:now()"`
NotNull int `sql:",notnull"`
Unique int `sql:",unique"`
NullBool sql.NullBool
NullFloat64 sql.NullFloat64
NullInt64 sql.NullInt64
NullString sql.NullString
Slice []int
SliceArray []int `pg:",array"`
Map map[int]int
Struct struct{}
}
type CreateTableWithoutPKModel struct {
String string
}
var _ = Describe("CreateTable", func() {
It("creates new table", func() {
q := NewQuery(nil, &CreateTableModel{})
b, err := createTableQuery{q: q}.AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`CREATE TABLE "create_table_models" ("id" bigserial, "int8" smallint, "uint8" smallint, "int16" smallint, "uint16" integer, "int32" integer, "uint32" bigint, "int64" bigint, "uint64" decimal, "float32" real, "float64" double precision, "decimal" decimal(10,10), "byte_slice" bytea, "byte_array" bytea, "string" text DEFAULT 'D''Angelo', "varchar" varchar(500), "time" timestamptz DEFAULT now(), "not_null" bigint NOT NULL, "unique" bigint UNIQUE, "null_bool" boolean, "null_float64" double precision, "null_int64" bigint, "null_string" text, "slice" jsonb, "slice_array" bigint[], "map" jsonb, "struct" jsonb, PRIMARY KEY ("id"))`))
})
It("creates new table without primary key", func() {
q := NewQuery(nil, &CreateTableWithoutPKModel{})
b, err := createTableQuery{q: q}.AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`CREATE TABLE "create_table_without_pk_models" ("string" text)`))
})
It("creates new table with Varchar=255", func() {
q := NewQuery(nil, &CreateTableWithoutPKModel{})
opt := &CreateTableOptions{Varchar: 255}
b, err := createTableQuery{q: q, opt: opt}.AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`CREATE TABLE "create_table_without_pk_models" ("string" varchar(255))`))
})
})

61
vendor/github.com/go-pg/pg/orm/delete.go generated vendored Normal file
View file

@ -0,0 +1,61 @@
package orm
import "github.com/go-pg/pg/internal"
func Delete(db DB, model interface{}) error {
res, err := NewQuery(db, model).Delete()
if err != nil {
return err
}
return internal.AssertOneRow(res.RowsAffected())
}
type deleteQuery struct {
q *Query
}
var _ QueryAppender = (*deleteQuery)(nil)
func (q deleteQuery) Copy() QueryAppender {
return deleteQuery{
q: q.q.Copy(),
}
}
func (q deleteQuery) Query() *Query {
return q.q
}
func (q deleteQuery) AppendQuery(b []byte) ([]byte, error) {
if q.q.stickyErr != nil {
return nil, q.q.stickyErr
}
var err error
if len(q.q.with) > 0 {
b, err = q.q.appendWith(b)
if err != nil {
return nil, err
}
}
b = append(b, "DELETE FROM "...)
b = q.q.appendFirstTableWithAlias(b)
if q.q.hasOtherTables() {
b = append(b, " USING "...)
b = q.q.appendOtherTables(b)
}
b, err = q.q.mustAppendWhere(b)
if err != nil {
return nil, err
}
if len(q.q.returning) > 0 {
b = q.q.appendReturning(b)
}
return b, nil
}

22
vendor/github.com/go-pg/pg/orm/delete_test.go generated vendored Normal file
View file

@ -0,0 +1,22 @@
package orm
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
type DeleteTest struct{}
var _ = Describe("Delete", func() {
It("supports WITH", func() {
q := NewQuery(nil, &DeleteTest{}).
WrapWith("wrapper").
Model(&DeleteTest{}).
Table("wrapper").
Where("delete_test.id = wrapper.id")
b, err := deleteQuery{q}.AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`WITH "wrapper" AS (SELECT FROM "delete_tests" AS "delete_test") DELETE FROM "delete_tests" AS "delete_test" USING "wrapper" WHERE (delete_test.id = wrapper.id)`))
})
})

45
vendor/github.com/go-pg/pg/orm/drop_table.go generated vendored Normal file
View file

@ -0,0 +1,45 @@
package orm
import "errors"
type DropTableOptions struct {
IfExists bool
Cascade bool
}
func DropTable(db DB, model interface{}, opt *DropTableOptions) (Result, error) {
return NewQuery(db, model).DropTable(opt)
}
type dropTableQuery struct {
q *Query
opt *DropTableOptions
}
func (q dropTableQuery) Copy() QueryAppender {
return q
}
func (q dropTableQuery) Query() *Query {
return q.q
}
func (q dropTableQuery) AppendQuery(b []byte) ([]byte, error) {
if q.q.stickyErr != nil {
return nil, q.q.stickyErr
}
if q.q.model == nil {
return nil, errors.New("pg: Model is nil")
}
b = append(b, "DROP TABLE "...)
if q.opt != nil && q.opt.IfExists {
b = append(b, "IF EXISTS "...)
}
b = q.q.appendTableName(b)
if q.opt != nil && q.opt.Cascade {
b = append(b, " CASCADE"...)
}
return b, nil
}

29
vendor/github.com/go-pg/pg/orm/drop_table_test.go generated vendored Normal file
View file

@ -0,0 +1,29 @@
package orm
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
type DropTableModel struct{}
var _ = Describe("CreateTable", func() {
It("drops table", func() {
q := NewQuery(nil, &DropTableModel{})
b, err := dropTableQuery{q: q}.AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`DROP TABLE "drop_table_models"`))
})
It("drops table if exists", func() {
q := NewQuery(nil, &DropTableModel{})
b, err := dropTableQuery{
q: q,
opt: &DropTableOptions{IfExists: true},
}.AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`DROP TABLE IF EXISTS "drop_table_models"`))
})
})

95
vendor/github.com/go-pg/pg/orm/field.go generated vendored Normal file
View file

@ -0,0 +1,95 @@
package orm
import (
"reflect"
"github.com/go-pg/pg/types"
)
const (
PrimaryKeyFlag = uint8(1) << iota
ForeignKeyFlag
NotNullFlag
UniqueFlag
ArrayFlag
customTypeFlag
)
type Field struct {
Type reflect.Type
GoName string // struct field name, e.g. Id
SQLName string // SQL name, .e.g. id
Column types.Q // escaped SQL name
SQLType string
Index []int
Default types.Q
flags uint8
append types.AppenderFunc
scan types.ScannerFunc
isZero func(reflect.Value) bool
}
func (f *Field) Copy() *Field {
copy := *f
copy.Index = copy.Index[:len(f.Index):len(f.Index)]
return &copy
}
func (f *Field) SetFlag(flag uint8) {
f.flags |= flag
}
func (f *Field) HasFlag(flag uint8) bool {
return f.flags&flag != 0
}
func (f *Field) Value(strct reflect.Value) reflect.Value {
return strct.FieldByIndex(f.Index)
}
func (f *Field) IsZero(strct reflect.Value) bool {
fv := f.Value(strct)
return f.isZero(fv)
}
func (f *Field) OmitZero(strct reflect.Value) bool {
return !f.HasFlag(NotNullFlag) && f.isZero(f.Value(strct))
}
func (f *Field) AppendValue(b []byte, strct reflect.Value, quote int) []byte {
fv := f.Value(strct)
if !f.HasFlag(NotNullFlag) && f.isZero(fv) {
return types.AppendNull(b, quote)
}
return f.append(b, fv, quote)
}
func (f *Field) ScanValue(strct reflect.Value, b []byte) error {
fv := fieldByIndex(strct, f.Index)
return f.scan(fv, b)
}
type Method struct {
Index int
flags int8
appender func([]byte, reflect.Value, int) []byte
}
func (m *Method) Has(flag int8) bool {
return m.flags&flag != 0
}
func (m *Method) Value(strct reflect.Value) reflect.Value {
return strct.Method(m.Index).Call(nil)[0]
}
func (m *Method) AppendValue(dst []byte, strct reflect.Value, quote int) []byte {
mv := m.Value(strct)
return m.appender(dst, mv, quote)
}

281
vendor/github.com/go-pg/pg/orm/format.go generated vendored Normal file
View file

@ -0,0 +1,281 @@
package orm
import (
"bytes"
"fmt"
"sort"
"strconv"
"strings"
"github.com/go-pg/pg/internal/parser"
"github.com/go-pg/pg/types"
)
var formatter Formatter
type FormatAppender interface {
AppendFormat([]byte, QueryFormatter) []byte
}
type sepFormatAppender interface {
FormatAppender
AppendSep([]byte) []byte
}
//------------------------------------------------------------------------------
type queryParamsAppender struct {
query string
params []interface{}
}
var _ FormatAppender = (*queryParamsAppender)(nil)
func Q(query string, params ...interface{}) queryParamsAppender {
return queryParamsAppender{query, params}
}
func (q queryParamsAppender) AppendFormat(b []byte, f QueryFormatter) []byte {
return f.FormatQuery(b, q.query, q.params...)
}
func (q queryParamsAppender) AppendValue(b []byte, quote int) ([]byte, error) {
return q.AppendFormat(b, formatter), nil
}
//------------------------------------------------------------------------------
type whereGroupAppender struct {
sep string
where []sepFormatAppender
}
var _ FormatAppender = (*whereAppender)(nil)
var _ sepFormatAppender = (*whereAppender)(nil)
func (q whereGroupAppender) AppendSep(b []byte) []byte {
return append(b, q.sep...)
}
func (q whereGroupAppender) AppendFormat(b []byte, f QueryFormatter) []byte {
b = append(b, '(')
for i, app := range q.where {
if i > 0 {
b = append(b, ' ')
b = app.AppendSep(b)
b = append(b, ' ')
}
b = app.AppendFormat(b, f)
}
b = append(b, ')')
return b
}
//------------------------------------------------------------------------------
type whereAppender struct {
sep string
where string
params []interface{}
}
var _ FormatAppender = (*whereAppender)(nil)
var _ sepFormatAppender = (*whereAppender)(nil)
func (q whereAppender) AppendSep(b []byte) []byte {
return append(b, q.sep...)
}
func (q whereAppender) AppendFormat(b []byte, f QueryFormatter) []byte {
b = append(b, '(')
b = f.FormatQuery(b, q.where, q.params...)
b = append(b, ')')
return b
}
//------------------------------------------------------------------------------
type fieldAppender struct {
field string
}
var _ FormatAppender = (*fieldAppender)(nil)
func (a fieldAppender) AppendFormat(b []byte, f QueryFormatter) []byte {
return types.AppendField(b, a.field, 1)
}
//------------------------------------------------------------------------------
type Formatter struct {
namedParams map[string]interface{}
}
func (f Formatter) String() string {
if len(f.namedParams) == 0 {
return ""
}
var keys []string
for k, _ := range f.namedParams {
keys = append(keys, k)
}
sort.Strings(keys)
var ss []string
for _, k := range keys {
ss = append(ss, fmt.Sprintf("%s=%v", k, f.namedParams[k]))
}
return " " + strings.Join(ss, " ")
}
func (f Formatter) copy() Formatter {
var cp Formatter
for param, value := range f.namedParams {
cp.SetParam(param, value)
}
return cp
}
func (f *Formatter) SetParam(param string, value interface{}) {
if f.namedParams == nil {
f.namedParams = make(map[string]interface{})
}
f.namedParams[param] = value
}
func (f *Formatter) WithParam(param string, value interface{}) Formatter {
cp := f.copy()
cp.SetParam(param, value)
return cp
}
func (f Formatter) Append(dst []byte, src string, params ...interface{}) []byte {
if (params == nil && f.namedParams == nil) || strings.IndexByte(src, '?') == -1 {
return append(dst, src...)
}
return f.append(dst, parser.NewString(src), params)
}
func (f Formatter) AppendBytes(dst, src []byte, params ...interface{}) []byte {
if (params == nil && f.namedParams == nil) || bytes.IndexByte(src, '?') == -1 {
return append(dst, src...)
}
return f.append(dst, parser.New(src), params)
}
func (f Formatter) FormatQuery(dst []byte, query string, params ...interface{}) []byte {
return f.Append(dst, query, params...)
}
func (f Formatter) append(dst []byte, p *parser.Parser, params []interface{}) []byte {
var paramsIndex int
var namedParamsOnce bool
var tableParams *tableParams
var model tableModel
if len(params) > 0 {
var ok bool
model, ok = params[len(params)-1].(tableModel)
if ok {
params = params[:len(params)-1]
}
}
for p.Valid() {
b, ok := p.ReadSep('?')
if !ok {
dst = append(dst, b...)
continue
}
if len(b) > 0 && b[len(b)-1] == '\\' {
dst = append(dst, b[:len(b)-1]...)
dst = append(dst, '?')
continue
}
dst = append(dst, b...)
if id, numeric := p.ReadIdentifier(); id != "" {
if numeric {
idx, err := strconv.Atoi(id)
if err != nil {
goto restore_param
}
if idx >= len(params) {
goto restore_param
}
dst = f.appendParam(dst, params[idx])
continue
}
if f.namedParams != nil {
if param, ok := f.namedParams[id]; ok {
dst = f.appendParam(dst, param)
continue
}
}
if !namedParamsOnce && len(params) > 0 {
namedParamsOnce = true
if len(params) > 0 {
tableParams, ok = newTableParams(params[len(params)-1])
if ok {
params = params[:len(params)-1]
}
}
}
if tableParams != nil {
dst, ok = tableParams.AppendParam(dst, f, id)
if ok {
continue
}
}
if model != nil {
dst, ok = model.AppendParam(dst, f, id)
if ok {
continue
}
}
restore_param:
dst = append(dst, '?')
dst = append(dst, id...)
continue
}
if paramsIndex >= len(params) {
dst = append(dst, '?')
continue
}
param := params[paramsIndex]
paramsIndex++
dst = f.appendParam(dst, param)
}
return dst
}
type queryAppender interface {
AppendQuery(dst []byte) ([]byte, error)
}
func (f Formatter) appendParam(b []byte, param interface{}) []byte {
switch param := param.(type) {
case queryAppender:
bb, err := param.AppendQuery(b)
if err != nil {
return types.AppendError(b, err)
}
return bb
case FormatAppender:
return param.AppendFormat(b, f)
default:
return types.Append(b, param, 1)
}
}

203
vendor/github.com/go-pg/pg/orm/format_test.go generated vendored Normal file
View file

@ -0,0 +1,203 @@
package orm_test
import (
"database/sql/driver"
"errors"
"fmt"
"math"
"testing"
"github.com/go-pg/pg/orm"
"github.com/go-pg/pg/types"
)
type ValuerError string
func (e ValuerError) Value() (driver.Value, error) {
return nil, errors.New(string(e))
}
type StructFormatter struct {
tableName struct{} `sql:"my_name,alias:my_alias"`
String string
NotNull string `sql:",notnull"`
Iface interface{}
}
func (StructFormatter) Method() string {
return "method_value"
}
func (StructFormatter) MethodParam() types.Q {
return types.Q("?string")
}
func (StructFormatter) MethodWithArgs(string) string {
return "method_value"
}
func (StructFormatter) MethodWithCompositeReturn() (string, string) {
return "method_value1", "method_value2"
}
type EmbeddedStructFormatter struct {
*StructFormatter
}
func (EmbeddedStructFormatter) Method2() string {
return "method_value2"
}
type params []interface{}
type paramsMap map[string]interface{}
type formatTest struct {
q string
params params
paramsMap paramsMap
wanted string
}
var (
structv = &StructFormatter{
String: "string_value",
Iface: "iface_value",
}
embeddedStructv = &EmbeddedStructFormatter{structv}
)
var formatTests = []formatTest{
{q: "?", params: params{ValuerError("error")}, wanted: "?!(error)"},
{q: "?", wanted: "?"},
{q: "? ? ?", params: params{"foo", "bar"}, wanted: "'foo' 'bar' ?"},
{q: "?0 ?1", params: params{"foo", "bar"}, wanted: "'foo' 'bar'"},
{q: "?0 ?1 ?2", params: params{"foo", "bar"}, wanted: "'foo' 'bar' ?2"},
{q: "?0 ?1 ?0", params: params{"foo", "bar"}, wanted: "'foo' 'bar' 'foo'"},
{q: "one ?foo two", wanted: "one ?foo two"},
{q: "one ?foo two", params: params{structv}, wanted: "one ?foo two"},
{q: "one ?MethodWithArgs two", params: params{structv}, wanted: "one ?MethodWithArgs two"},
{q: "one ?MethodWithCompositeReturn two", params: params{structv}, wanted: "one ?MethodWithCompositeReturn two"},
{q: "?", params: params{uint64(math.MaxUint64)}, wanted: "18446744073709551615"},
{q: "?", params: params{orm.Q("query")}, wanted: "query"},
{q: "?", params: params{types.F("field")}, wanted: `"field"`},
{q: "?", params: params{structv}, wanted: `'{"String":"string_value","NotNull":"","Iface":"iface_value"}'`},
{q: `\? ?`, params: params{1}, wanted: "? 1"},
{q: `?`, params: params{types.Q(`\?`)}, wanted: `\?`},
{q: `?`, params: params{types.Q(`\\?`)}, wanted: `\\?`},
{q: `?`, params: params{types.Q(`\?param`)}, wanted: `\?param`},
{q: "?string", params: params{structv}, wanted: `'string_value'`},
{q: "?iface", params: params{structv}, wanted: `'iface_value'`},
{q: "?string", params: params{&StructFormatter{}}, wanted: `NULL`},
{
q: "? ?string ?",
params: params{"one", "two", structv},
wanted: "'one' 'string_value' 'two'",
},
{
q: "?string ?Method",
params: params{structv},
wanted: "'string_value' 'method_value'",
},
{
q: "?string ?Method ?Method2",
params: params{embeddedStructv},
wanted: "'string_value' 'method_value' 'method_value2'",
},
{
q: "?string",
params: params{structv},
paramsMap: paramsMap{"string": "my_value"},
wanted: "'my_value'",
},
{
q: "?",
params: params{types.Q("?string")},
paramsMap: paramsMap{"string": "my_value"},
wanted: "?string",
},
{
q: "?",
params: params{types.F("?string")},
paramsMap: paramsMap{"string": types.Q("my_value")},
wanted: `"?string"`,
},
{
q: "?",
params: params{orm.Q("?string")},
paramsMap: paramsMap{"string": "my_value"},
wanted: "'my_value'",
},
{
q: "?MethodParam",
params: params{structv},
paramsMap: paramsMap{"string": "my_value"},
wanted: "?string",
},
}
func TestFormatQuery(t *testing.T) {
for _, test := range formatTests {
var f orm.Formatter
for k, v := range test.paramsMap {
f.SetParam(k, v)
}
got := f.Append(nil, test.q, test.params...)
if string(got) != test.wanted {
t.Fatalf(
"got %q, wanted %q (q=%q params=%v paramsMap=%v)",
got, test.wanted, test.q, test.params, test.paramsMap,
)
}
}
}
func BenchmarkFormatQueryWithoutParams(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = orm.Q("SELECT * FROM my_table WHERE id = 1")
}
}
func BenchmarkFormatQuery1Param(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = orm.Q("SELECT * FROM my_table WHERE id = ?", 1)
}
}
func BenchmarkFormatQuery10Params(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = orm.Q(
"SELECT * FROM my_table WHERE id IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
)
}
}
func BenchmarkFormatQuerySprintf(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = fmt.Sprintf("SELECT * FROM my_table WHERE id = %d", 1)
}
}
func BenchmarkFormatQueryStructParam(b *testing.B) {
param := StructFormatter{
String: "1",
}
for i := 0; i < b.N; i++ {
_ = orm.Q("SELECT * FROM my_table WHERE id = ?string", param)
}
}
func BenchmarkFormatQueryStructMethod(b *testing.B) {
param := StructFormatter{}
for i := 0; i < b.N; i++ {
_ = orm.Q("SELECT * FROM my_table WHERE id = ?Method", &param)
}
}

180
vendor/github.com/go-pg/pg/orm/hook.go generated vendored Normal file
View file

@ -0,0 +1,180 @@
package orm
import "reflect"
const (
AfterQueryHookFlag = uint8(1) << iota
AfterSelectHookFlag
BeforeInsertHookFlag
AfterInsertHookFlag
BeforeUpdateHookFlag
AfterUpdateHookFlag
BeforeDeleteHookFlag
AfterDeleteHookFlag
)
type hookStubs struct{}
func (hookStubs) Init() error {
return nil
}
func (hookStubs) AfterQuery(_ DB) error {
return nil
}
func (hookStubs) AfterSelect(_ DB) error {
return nil
}
func (hookStubs) BeforeInsert(_ DB) error {
return nil
}
func (hookStubs) AfterInsert(_ DB) error {
return nil
}
func (hookStubs) BeforeUpdate(_ DB) error {
return nil
}
func (hookStubs) AfterUpdate(_ DB) error {
return nil
}
func (hookStubs) BeforeDelete(_ DB) error {
return nil
}
func (hookStubs) AfterDelete(_ DB) error {
return nil
}
func callHookSlice(slice reflect.Value, ptr bool, db DB, hook func(reflect.Value, DB) error) error {
var firstErr error
for i := 0; i < slice.Len(); i++ {
var err error
if ptr {
err = hook(slice.Index(i), db)
} else {
err = hook(slice.Index(i).Addr(), db)
}
if err != nil && firstErr == nil {
firstErr = err
}
}
return firstErr
}
type afterQueryHook interface {
AfterQuery(db DB) error
}
var afterQueryHookType = reflect.TypeOf((*afterQueryHook)(nil)).Elem()
func callAfterQueryHook(v reflect.Value, db DB) error {
return v.Interface().(afterQueryHook).AfterQuery(db)
}
func callAfterQueryHookSlice(slice reflect.Value, ptr bool, db DB) error {
return callHookSlice(slice, ptr, db, callAfterQueryHook)
}
type afterSelectHook interface {
AfterSelect(db DB) error
}
var afterSelectHookType = reflect.TypeOf((*afterSelectHook)(nil)).Elem()
func callAfterSelectHook(v reflect.Value, db DB) error {
return v.Interface().(afterSelectHook).AfterSelect(db)
}
func callAfterSelectHookSlice(slice reflect.Value, ptr bool, db DB) error {
return callHookSlice(slice, ptr, db, callAfterSelectHook)
}
type beforeInsertHook interface {
BeforeInsert(db DB) error
}
var beforeInsertHookType = reflect.TypeOf((*beforeInsertHook)(nil)).Elem()
func callBeforeInsertHook(v reflect.Value, db DB) error {
return v.Interface().(beforeInsertHook).BeforeInsert(db)
}
func callBeforeInsertHookSlice(slice reflect.Value, ptr bool, db DB) error {
return callHookSlice(slice, ptr, db, callBeforeInsertHook)
}
type afterInsertHook interface {
AfterInsert(db DB) error
}
var afterInsertHookType = reflect.TypeOf((*afterInsertHook)(nil)).Elem()
func callAfterInsertHook(v reflect.Value, db DB) error {
return v.Interface().(afterInsertHook).AfterInsert(db)
}
func callAfterInsertHookSlice(slice reflect.Value, ptr bool, db DB) error {
return callHookSlice(slice, ptr, db, callAfterInsertHook)
}
type beforeUpdateHook interface {
BeforeUpdate(db DB) error
}
var beforeUpdateHookType = reflect.TypeOf((*beforeUpdateHook)(nil)).Elem()
func callBeforeUpdateHook(v reflect.Value, db DB) error {
return v.Interface().(beforeUpdateHook).BeforeUpdate(db)
}
func callBeforeUpdateHookSlice(slice reflect.Value, ptr bool, db DB) error {
return callHookSlice(slice, ptr, db, callBeforeUpdateHook)
}
type afterUpdateHook interface {
AfterUpdate(db DB) error
}
var afterUpdateHookType = reflect.TypeOf((*afterUpdateHook)(nil)).Elem()
func callAfterUpdateHook(v reflect.Value, db DB) error {
return v.Interface().(afterUpdateHook).AfterUpdate(db)
}
func callAfterUpdateHookSlice(slice reflect.Value, ptr bool, db DB) error {
return callHookSlice(slice, ptr, db, callAfterUpdateHook)
}
type beforeDeleteHook interface {
BeforeDelete(db DB) error
}
var beforeDeleteHookType = reflect.TypeOf((*beforeDeleteHook)(nil)).Elem()
func callBeforeDeleteHook(v reflect.Value, db DB) error {
return v.Interface().(beforeDeleteHook).BeforeDelete(db)
}
func callBeforeDeleteHookSlice(slice reflect.Value, ptr bool, db DB) error {
return callHookSlice(slice, ptr, db, callBeforeDeleteHook)
}
type afterDeleteHook interface {
AfterDelete(db DB) error
}
var afterDeleteHookType = reflect.TypeOf((*afterDeleteHook)(nil)).Elem()
func callAfterDeleteHook(v reflect.Value, db DB) error {
return v.Interface().(afterDeleteHook).AfterDelete(db)
}
func callAfterDeleteHookSlice(slice reflect.Value, ptr bool, db DB) error {
return callHookSlice(slice, ptr, db, callAfterDeleteHook)
}

17
vendor/github.com/go-pg/pg/orm/inflection.go generated vendored Normal file
View file

@ -0,0 +1,17 @@
package orm
import (
"github.com/jinzhu/inflection"
)
var tableNameInflector func(string) string
func init() {
SetTableNameInflector(inflection.Plural)
}
// SetTableNameInflector overrides the default func that pluralizes
// model name to get table name, e.g. my_article becomes my_articles.
func SetTableNameInflector(fn func(string) string) {
tableNameInflector = fn
}

139
vendor/github.com/go-pg/pg/orm/insert.go generated vendored Normal file
View file

@ -0,0 +1,139 @@
package orm
import (
"errors"
"reflect"
)
func Insert(db DB, v ...interface{}) error {
_, err := NewQuery(db, v...).Insert()
return err
}
type insertQuery struct {
q *Query
returningFields []*Field
}
var _ QueryAppender = (*insertQuery)(nil)
func (q insertQuery) Copy() QueryAppender {
return insertQuery{
q: q.q.Copy(),
}
}
func (q insertQuery) Query() *Query {
return q.q
}
func (q insertQuery) AppendQuery(b []byte) ([]byte, error) {
if q.q.stickyErr != nil {
return nil, q.q.stickyErr
}
if q.q.model == nil {
return nil, errors.New("pg: Model is nil")
}
table := q.q.model.Table()
value := q.q.model.Value()
var err error
if len(q.q.with) > 0 {
b, err = q.q.appendWith(b)
if err != nil {
return nil, err
}
}
b = append(b, "INSERT INTO "...)
if q.q.onConflict != nil {
b = q.q.appendFirstTableWithAlias(b)
} else {
b = q.q.appendFirstTable(b)
}
b = append(b, " ("...)
if q.q.hasModel() {
b = appendColumns(b, table.Fields)
} else if q.q.columns != nil {
b = q.q.appendColumns(b)
}
b = append(b, ')')
if q.q.hasModel() {
b = append(b, " VALUES ("...)
if value.Kind() == reflect.Struct {
b = q.appendValues(b, table.Fields, value)
} else {
for i := 0; i < value.Len(); i++ {
el := value.Index(i)
if el.Kind() == reflect.Interface {
el = el.Elem()
}
b = q.appendValues(b, table.Fields, reflect.Indirect(el))
if i != value.Len()-1 {
b = append(b, "), ("...)
}
}
}
b = append(b, ')')
}
if q.q.hasOtherTables() {
b = append(b, " SELECT * FROM "...)
b = q.q.appendOtherTables(b)
}
if q.q.onConflict != nil {
b = append(b, " ON CONFLICT "...)
b = q.q.onConflict.AppendFormat(b, q.q)
if q.q.onConflictDoUpdate() {
if len(q.q.set) > 0 {
b = q.q.appendSet(b)
}
if len(q.q.updWhere) > 0 {
b = q.q.appendUpdWhere(b)
}
}
}
if len(q.q.returning) > 0 {
b = q.q.appendReturning(b)
} else if len(q.returningFields) > 0 {
b = q.appendReturningFields(b, q.returningFields)
}
return b, nil
}
func (q *insertQuery) appendValues(b []byte, fields []*Field, v reflect.Value) []byte {
for i, f := range fields {
if i > 0 {
b = append(b, ", "...)
}
if f.OmitZero(v) {
b = append(b, "DEFAULT"...)
q.addReturningField(f)
} else {
b = f.AppendValue(b, v, 1)
}
}
return b
}
func (ins *insertQuery) addReturningField(field *Field) {
for _, f := range ins.returningFields {
if f == field {
return
}
}
ins.returningFields = append(ins.returningFields, field)
}
func (insertQuery) appendReturningFields(b []byte, fields []*Field) []byte {
b = append(b, " RETURNING "...)
b = appendColumns(b, fields)
return b
}

128
vendor/github.com/go-pg/pg/orm/insert_test.go generated vendored Normal file
View file

@ -0,0 +1,128 @@
package orm
import (
"github.com/go-pg/pg/types"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
type InsertTest struct {
Id int
Value string
}
type EmbeddingTest struct {
tableName struct{} `sql:"name"`
Id int
Field int
}
type EmbeddedInsertTest struct {
tableName struct{} `sql:"my_name"`
EmbeddingTest
Field2 int
}
type OverrideInsertTest struct {
EmbeddingTest `pg:",override"`
Field2 int
}
type InsertNullTest struct {
F1 int
F2 int `sql:",notnull"`
F3 int `sql:",pk"`
F4 int `sql:",pk,notnull"`
}
type InsertQTest struct {
Geo types.Q
Func types.ValueAppender
}
var _ = Describe("Insert", func() {
It("multi inserts", func() {
q := NewQuery(nil, &InsertTest{
Id: 1,
Value: "hello",
}, &InsertTest{
Id: 2,
})
b, err := insertQuery{q: q}.AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`INSERT INTO "insert_tests" ("id", "value") VALUES (1, 'hello'), (2, DEFAULT) RETURNING "value"`))
})
It("supports ON CONFLICT DO UPDATE", func() {
q := NewQuery(nil, &InsertTest{}).
Where("1 = 1").
OnConflict("(unq1) DO UPDATE").
Set("count1 = count1 + 1").
Where("2 = 2")
b, err := insertQuery{q: q}.AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`INSERT INTO "insert_tests" AS "insert_test" ("id", "value") VALUES (DEFAULT, DEFAULT) ON CONFLICT (unq1) DO UPDATE SET count1 = count1 + 1 WHERE (2 = 2) RETURNING "id", "value"`))
})
It("supports ON CONFLICT DO NOTHING", func() {
q := NewQuery(nil, &InsertTest{}).
OnConflict("(unq1) DO NOTHING").
Set("count1 = count1 + 1").
Where("cond1 IS TRUE")
b, err := insertQuery{q: q}.AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`INSERT INTO "insert_tests" AS "insert_test" ("id", "value") VALUES (DEFAULT, DEFAULT) ON CONFLICT (unq1) DO NOTHING RETURNING "id", "value"`))
})
It("supports custom table name on embedded struct", func() {
q := NewQuery(nil, &EmbeddedInsertTest{})
b, err := insertQuery{q: q}.AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`INSERT INTO my_name ("id", "field", "field2") VALUES (DEFAULT, DEFAULT, DEFAULT) RETURNING "id", "field", "field2"`))
})
It("overrides table name with embedded struct", func() {
q := NewQuery(nil, &OverrideInsertTest{})
b, err := insertQuery{q: q}.AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`INSERT INTO name ("id", "field", "field2") VALUES (DEFAULT, DEFAULT, DEFAULT) RETURNING "id", "field", "field2"`))
})
It("supports notnull", func() {
q := NewQuery(nil, &InsertNullTest{})
b, err := insertQuery{q: q}.AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`INSERT INTO "insert_null_tests" ("f1", "f2", "f3", "f4") VALUES (DEFAULT, 0, DEFAULT, 0) RETURNING "f1", "f3"`))
})
It("inserts types.Q", func() {
q := NewQuery(nil, &InsertQTest{
Geo: types.Q("ST_GeomFromText('POLYGON((75.150000 29.530000, 77.000000 29.000000, 77.600000 29.500000, 75.150000 29.530000))')"),
Func: Q("my_func(?)", "param"),
})
b, err := insertQuery{q: q}.AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`INSERT INTO "insert_q_tests" ("geo", "func") VALUES (ST_GeomFromText('POLYGON((75.150000 29.530000, 77.000000 29.000000, 77.600000 29.500000, 75.150000 29.530000))'), my_func('param'))`))
})
It("supports FROM", func() {
q := NewQuery(nil, &InsertTest{})
q = q.WrapWith("data").
TableExpr("dst").
ColumnExpr("dst_col1, dst_col2").
TableExpr("data")
b, err := insertQuery{q: q}.AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`WITH "data" AS (SELECT "insert_test"."id", "insert_test"."value" FROM "insert_tests" AS "insert_test") INSERT INTO dst (dst_col1, dst_col2) SELECT * FROM data`))
})
})

278
vendor/github.com/go-pg/pg/orm/join.go generated vendored Normal file
View file

@ -0,0 +1,278 @@
package orm
import (
"github.com/go-pg/pg/internal"
"github.com/go-pg/pg/types"
)
type join struct {
Parent *join
BaseModel tableModel
JoinModel tableModel
Rel *Relation
ApplyQuery func(*Query) (*Query, error)
Columns []string
}
func (j *join) Select(db DB) error {
switch j.Rel.Type {
case HasManyRelation:
return j.selectMany(db)
case Many2ManyRelation:
return j.selectM2M(db)
}
panic("not reached")
}
func (j *join) selectMany(db DB) error {
q, err := j.manyQuery(db)
if err != nil {
return err
}
return q.Select()
}
func (j *join) manyQuery(db DB) (*Query, error) {
root := j.JoinModel.Root()
index := j.JoinModel.ParentIndex()
manyModel := newManyModel(j)
q := NewQuery(db, manyModel)
if j.ApplyQuery != nil {
var err error
q, err = j.ApplyQuery(q)
if err != nil {
return nil, err
}
}
q.columns = append(q.columns, hasManyColumnsAppender{j})
baseTable := j.BaseModel.Table()
var where []byte
where = append(where, "("...)
where = columns(where, j.JoinModel.Table().Alias, "", j.Rel.FKs)
where = append(where, ") IN ("...)
where = appendChildValues(where, root, index, baseTable.PKs)
where = append(where, ")"...)
q = q.Where(internal.BytesToString(where))
if j.Rel.Polymorphic {
q = q.Where(
`? IN (?, ?)`,
types.F(j.Rel.BasePrefix+"type"),
baseTable.ModelName, baseTable.TypeName,
)
}
return q, nil
}
func (j *join) selectM2M(db DB) error {
q, err := j.m2mQuery(db)
if err != nil {
return err
}
return q.Select()
}
func (j *join) m2mQuery(db DB) (*Query, error) {
m2mModel := newM2MModel(j)
q := NewQuery(db, m2mModel)
if j.ApplyQuery != nil {
var err error
q, err = j.ApplyQuery(q)
if err != nil {
return nil, err
}
}
q.columns = append(q.columns, hasManyColumnsAppender{j})
index := j.JoinModel.ParentIndex()
baseTable := j.BaseModel.Table()
var join []byte
join = append(join, "JOIN "...)
if db != nil {
join = db.FormatQuery(join, string(j.Rel.M2MTableName))
} else {
join = append(join, j.Rel.M2MTableName...)
}
join = append(join, " AS "...)
join = append(join, j.Rel.M2MTableAlias...)
join = append(join, " ON ("...)
join = columns(join, j.Rel.M2MTableAlias, j.Rel.BasePrefix, baseTable.PKs)
join = append(join, ") IN ("...)
join = appendChildValues(join, j.BaseModel.Root(), index, baseTable.PKs)
join = append(join, ")"...)
q = q.Join(internal.BytesToString(join))
joinAlias := j.JoinModel.Table().Alias
for _, pk := range j.JoinModel.Table().PKs {
q = q.Where(
"?.? = ?.?",
joinAlias, pk.Column,
j.Rel.M2MTableAlias, types.F(j.Rel.JoinPrefix+pk.SQLName),
)
}
return q, nil
}
func (j *join) hasParent() bool {
if j.Parent != nil {
switch j.Parent.Rel.Type {
case HasOneRelation, BelongsToRelation:
return true
}
}
return false
}
func (j *join) appendAlias(b []byte) []byte {
b = append(b, '"')
b = appendAlias(b, j, true)
b = append(b, '"')
return b
}
func (j *join) appendAliasColumn(b []byte, column string) []byte {
b = append(b, '"')
b = appendAlias(b, j, true)
b = append(b, "__"...)
b = types.AppendField(b, column, 2)
b = append(b, '"')
return b
}
func (j *join) appendBaseAlias(b []byte) []byte {
if j.hasParent() {
b = append(b, '"')
b = appendAlias(b, j.Parent, true)
b = append(b, '"')
return b
}
return append(b, j.BaseModel.Table().Alias...)
}
func appendAlias(b []byte, j *join, topLevel bool) []byte {
if j.hasParent() {
b = appendAlias(b, j.Parent, topLevel)
topLevel = false
}
if !topLevel {
b = append(b, "__"...)
}
b = append(b, j.Rel.Field.SQLName...)
return b
}
func (j *join) appendHasOneColumns(b []byte) []byte {
if j.Columns == nil {
for i, f := range j.JoinModel.Table().Fields {
if i > 0 {
b = append(b, ", "...)
}
b = j.appendAlias(b)
b = append(b, '.')
b = append(b, f.Column...)
b = append(b, " AS "...)
b = j.appendAliasColumn(b, f.SQLName)
}
return b
}
for i, column := range j.Columns {
if i > 0 {
b = append(b, ", "...)
}
b = j.appendAlias(b)
b = append(b, '.')
b = types.AppendField(b, column, 1)
b = append(b, " AS "...)
b = j.appendAliasColumn(b, column)
}
return b
}
func (j *join) appendHasOneJoin(db DB, b []byte) []byte {
b = append(b, "LEFT JOIN "...)
if db != nil {
b = db.FormatQuery(b, string(j.JoinModel.Table().Name))
} else {
b = append(b, j.JoinModel.Table().Name...)
}
b = append(b, " AS "...)
b = j.appendAlias(b)
b = append(b, " ON "...)
if j.Rel.Type == HasOneRelation {
joinTable := j.Rel.JoinTable
for i, fk := range j.Rel.FKs {
if i > 0 {
b = append(b, " AND "...)
}
b = j.appendAlias(b)
b = append(b, '.')
b = append(b, joinTable.PKs[i].Column...)
b = append(b, " = "...)
b = j.appendBaseAlias(b)
b = append(b, '.')
b = append(b, fk.Column...)
}
} else {
baseTable := j.BaseModel.Table()
for i, fk := range j.Rel.FKs {
if i > 0 {
b = append(b, " AND "...)
}
b = j.appendAlias(b)
b = append(b, '.')
b = append(b, fk.Column...)
b = append(b, " = "...)
b = j.appendBaseAlias(b)
b = append(b, '.')
b = append(b, baseTable.PKs[i].Column...)
}
}
return b
}
type hasManyColumnsAppender struct {
*join
}
func (q hasManyColumnsAppender) AppendFormat(b []byte, f QueryFormatter) []byte {
if q.Rel.M2MTableAlias != "" {
b = append(b, q.Rel.M2MTableAlias...)
b = append(b, ".*, "...)
}
joinTable := q.JoinModel.Table()
if q.Columns == nil {
for i, f := range joinTable.Fields {
if i > 0 {
b = append(b, ", "...)
}
b = append(b, joinTable.Alias...)
b = append(b, '.')
b = append(b, f.Column...)
}
return b
}
for i, column := range q.Columns {
if i > 0 {
b = append(b, ", "...)
}
b = append(b, joinTable.Alias...)
b = append(b, '.')
b = types.AppendField(b, column, 1)
}
return b
}

51
vendor/github.com/go-pg/pg/orm/join_test.go generated vendored Normal file
View file

@ -0,0 +1,51 @@
package orm
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
type JoinTest struct {
tableName struct{} `sql:"JoinTest,alias:JoinTest"`
Id int
HasOne *HasOne
HasOneId int
BelongsTo *BelongsTo
}
type HasOne struct {
tableName struct{} `sql:"HasOne,alias:HasOne"`
Id int
HasOne *HasOne
HasOneId int
}
type BelongsTo struct {
tableName struct{} `sql:"BelongsTo,alias:BelongsTo"`
Id int
JoinTestId int
}
var _ = Describe("Select", func() {
It("supports has one", func() {
q := NewQuery(nil, &JoinTest{}).Relation("HasOne.HasOne", nil)
b, err := selectQuery{q: q}.AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`SELECT JoinTest."id", JoinTest."has_one_id", "has_one"."id" AS "has_one__id", "has_one"."has_one_id" AS "has_one__has_one_id", "has_one__has_one"."id" AS "has_one__has_one__id", "has_one__has_one"."has_one_id" AS "has_one__has_one__has_one_id" FROM JoinTest AS JoinTest LEFT JOIN HasOne AS "has_one" ON "has_one"."id" = JoinTest."has_one_id" LEFT JOIN HasOne AS "has_one__has_one" ON "has_one__has_one"."id" = "has_one"."has_one_id"`))
})
It("supports belongs to", func() {
q := NewQuery(nil, &JoinTest{}).Relation("BelongsTo", nil)
b, err := selectQuery{q: q}.AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`SELECT JoinTest."id", JoinTest."has_one_id", "belongs_to"."id" AS "belongs_to__id", "belongs_to"."join_test_id" AS "belongs_to__join_test_id" FROM JoinTest AS JoinTest LEFT JOIN BelongsTo AS "belongs_to" ON "belongs_to"."join_test_id" = JoinTest."id"`))
})
})

13
vendor/github.com/go-pg/pg/orm/main_test.go generated vendored Normal file
View file

@ -0,0 +1,13 @@
package orm_test
import (
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
func TestGinkgo(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "ORM")
}

88
vendor/github.com/go-pg/pg/orm/model.go generated vendored Normal file
View file

@ -0,0 +1,88 @@
package orm
import (
"database/sql"
"errors"
"fmt"
"reflect"
"github.com/go-pg/pg/types"
)
type useQueryOne interface {
useQueryOne() bool
}
type Model interface {
ColumnScanner
Init() error
// NewModel returns ColumnScanner that is used to scan columns
// from the current row.
NewModel() ColumnScanner
// AddModel adds ColumnScanner to the Collection.
AddModel(ColumnScanner) error
AfterQuery(DB) error
AfterSelect(DB) error
BeforeInsert(DB) error
AfterInsert(DB) error
BeforeUpdate(DB) error
AfterUpdate(DB) error
BeforeDelete(DB) error
AfterDelete(DB) error
}
func NewModel(values ...interface{}) (Model, error) {
if len(values) > 1 {
return Scan(values...), nil
}
v0 := values[0]
switch v0 := v0.(type) {
case Model:
return v0, nil
case sql.Scanner:
return Scan(v0), nil
}
v := reflect.ValueOf(v0)
if !v.IsValid() {
return nil, errors.New("pg: Model(nil)")
}
if v.Kind() != reflect.Ptr {
return nil, fmt.Errorf("pg: Model(non-pointer %T)", v0)
}
v = v.Elem()
switch v.Kind() {
case reflect.Struct:
return newStructTableModelValue(v), nil
case reflect.Slice:
typ := v.Type()
structType := indirectType(typ.Elem())
if structType.Kind() == reflect.Struct && structType != timeType {
m := sliceTableModel{
structTableModel: structTableModel{
table: Tables.Get(structType),
root: v,
},
slice: v,
}
m.init(typ)
return &m, nil
} else {
return &sliceModel{
slice: v,
scan: types.Scanner(structType),
}, nil
}
}
return Scan(v0), nil
}

19
vendor/github.com/go-pg/pg/orm/model_discard.go generated vendored Normal file
View file

@ -0,0 +1,19 @@
package orm
type Discard struct {
hookStubs
}
var _ Model = (*Discard)(nil)
func (d Discard) NewModel() ColumnScanner {
return d
}
func (Discard) AddModel(_ ColumnScanner) error {
return nil
}
func (Discard) ScanColumn(colIdx int, colName string, b []byte) error {
return nil
}

42
vendor/github.com/go-pg/pg/orm/model_scan.go generated vendored Normal file
View file

@ -0,0 +1,42 @@
package orm
import (
"fmt"
"github.com/go-pg/pg/types"
)
type valuesModel struct {
hookStubs
values []interface{}
}
var _ Model = valuesModel{}
func Scan(values ...interface{}) valuesModel {
return valuesModel{
values: values,
}
}
func (valuesModel) useQueryOne() bool {
return true
}
func (m valuesModel) NewModel() ColumnScanner {
return m
}
func (valuesModel) AddModel(_ ColumnScanner) error {
return nil
}
func (m valuesModel) ScanColumn(colIdx int, colName string, b []byte) error {
if colIdx >= len(m.values) {
return fmt.Errorf(
"pg: no Scan var for column index=%d name=%q",
colIdx, colName,
)
}
return types.Scan(m.values[colIdx], b)
}

35
vendor/github.com/go-pg/pg/orm/model_slice.go generated vendored Normal file
View file

@ -0,0 +1,35 @@
package orm
import (
"reflect"
"github.com/go-pg/pg/internal"
)
type sliceModel struct {
hookStubs
slice reflect.Value
scan func(reflect.Value, []byte) error
}
var _ Model = (*sliceModel)(nil)
func (m *sliceModel) Init() error {
if m.slice.IsValid() && m.slice.Len() > 0 {
m.slice.Set(m.slice.Slice(0, 0))
}
return nil
}
func (m *sliceModel) NewModel() ColumnScanner {
return m
}
func (sliceModel) AddModel(_ ColumnScanner) error {
return nil
}
func (m *sliceModel) ScanColumn(colIdx int, _ string, b []byte) error {
v := internal.SliceNextElem(m.slice)
return m.scan(v, b)
}

107
vendor/github.com/go-pg/pg/orm/model_table.go generated vendored Normal file
View file

@ -0,0 +1,107 @@
package orm
import (
"errors"
"fmt"
"reflect"
)
type tableModel interface {
Model
Table() *Table
Relation() *Relation
AppendParam([]byte, QueryFormatter, string) ([]byte, bool)
Join(string, func(*Query) (*Query, error)) (bool, *join)
GetJoin(string) *join
GetJoins() []join
AddJoin(join) *join
Root() reflect.Value
Index() []int
ParentIndex() []int
Mount(reflect.Value)
Value() reflect.Value
scanColumn(int, string, []byte) (bool, error)
}
func newTableModel(value interface{}) (tableModel, error) {
if value, ok := value.(tableModel); ok {
return value, nil
}
v := reflect.ValueOf(value)
if !v.IsValid() {
return nil, errors.New("pg: Model(nil)")
}
if v.Kind() != reflect.Ptr {
return nil, fmt.Errorf("pg: Model(non-pointer %T)", value)
}
if v.IsNil() {
typ := v.Type().Elem()
if typ.Kind() == reflect.Struct {
return newStructTableModelType(typ), nil
}
return nil, errors.New("pg: Model(nil)")
}
return newTableModelValue(v.Elem())
}
func newTableModelValue(v reflect.Value) (tableModel, error) {
switch v.Kind() {
case reflect.Struct:
return newStructTableModelValue(v), nil
case reflect.Slice:
structType := sliceElemType(v)
if structType.Kind() == reflect.Struct {
m := sliceTableModel{
structTableModel: structTableModel{
table: Tables.Get(structType),
root: v,
},
slice: v,
}
m.init(v.Type())
return &m, nil
}
}
return nil, fmt.Errorf("pg: Model(unsupported %s)", v.Type())
}
func newTableModelIndex(root reflect.Value, index []int, rel *Relation) (tableModel, error) {
typ := typeByIndex(root.Type(), index)
if typ.Kind() == reflect.Struct {
return &structTableModel{
table: Tables.Get(typ),
rel: rel,
root: root,
index: index,
}, nil
}
if typ.Kind() == reflect.Slice {
structType := indirectType(typ.Elem())
if structType.Kind() == reflect.Struct {
m := sliceTableModel{
structTableModel: structTableModel{
table: Tables.Get(structType),
rel: rel,
root: root,
index: index,
},
}
m.init(typ)
return &m, nil
}
}
return nil, fmt.Errorf("pg: NewModel(%s)", typ)
}

120
vendor/github.com/go-pg/pg/orm/model_table_m2m.go generated vendored Normal file
View file

@ -0,0 +1,120 @@
package orm
import (
"fmt"
"reflect"
)
type m2mModel struct {
*sliceTableModel
baseTable *Table
rel *Relation
buf []byte
dstValues map[string][]reflect.Value
columns map[string]string
}
var _ tableModel = (*m2mModel)(nil)
func newM2MModel(join *join) *m2mModel {
baseTable := join.BaseModel.Table()
joinModel := join.JoinModel.(*sliceTableModel)
dstValues := dstValues(joinModel, baseTable.PKs)
m := &m2mModel{
sliceTableModel: joinModel,
baseTable: baseTable,
rel: join.Rel,
dstValues: dstValues,
columns: make(map[string]string),
}
if !m.sliceOfPtr {
m.strct = reflect.New(m.table.Type).Elem()
}
return m
}
func (m *m2mModel) NewModel() ColumnScanner {
if m.sliceOfPtr {
m.strct = reflect.New(m.table.Type).Elem()
} else {
m.strct.Set(m.table.zeroStruct)
}
m.structInited = false
m.structTableModel.NewModel()
return m
}
func (m *m2mModel) AddModel(model ColumnScanner) error {
m.buf = modelIdMap(m.buf[:0], m.columns, m.rel.BasePrefix, m.baseTable.PKs)
dstValues, ok := m.dstValues[string(m.buf)]
if !ok {
return fmt.Errorf("pg: can't find dst value for model id=%q", m.buf)
}
for _, v := range dstValues {
if m.sliceOfPtr {
v.Set(reflect.Append(v, m.strct.Addr()))
} else {
v.Set(reflect.Append(v, m.strct))
}
}
return nil
}
func (m *m2mModel) AfterQuery(db DB) error {
if !m.rel.JoinTable.HasFlag(AfterQueryHookFlag) {
return nil
}
var retErr error
for _, slices := range m.dstValues {
for _, slice := range slices {
err := callAfterQueryHookSlice(slice, m.sliceOfPtr, db)
if err != nil && retErr == nil {
retErr = err
}
}
}
return retErr
}
func (m *m2mModel) AfterSelect(db DB) error {
return nil
}
func (m *m2mModel) BeforeInsert(db DB) error {
return nil
}
func (m *m2mModel) AfterInsert(db DB) error {
return nil
}
func (m *m2mModel) BeforeUpdate(db DB) error {
return nil
}
func (m *m2mModel) AfterUpdate(db DB) error {
return nil
}
func (m *m2mModel) BeforeDelete(db DB) error {
return nil
}
func (m *m2mModel) AfterDelete(db DB) error {
return nil
}
func (m *m2mModel) ScanColumn(colIdx int, colName string, b []byte) error {
ok, err := m.sliceTableModel.scanColumn(colIdx, colName, b)
if ok {
return err
}
m.columns[colName] = string(b)
return nil
}

105
vendor/github.com/go-pg/pg/orm/model_table_many.go generated vendored Normal file
View file

@ -0,0 +1,105 @@
package orm
import (
"fmt"
"reflect"
)
type manyModel struct {
*sliceTableModel
rel *Relation
buf []byte
dstValues map[string][]reflect.Value
}
var _ tableModel = (*manyModel)(nil)
func newManyModel(j *join) *manyModel {
joinModel := j.JoinModel.(*sliceTableModel)
dstValues := dstValues(joinModel, j.BaseModel.Table().PKs)
m := manyModel{
sliceTableModel: joinModel,
rel: j.Rel,
dstValues: dstValues,
}
if !m.sliceOfPtr {
m.strct = reflect.New(m.table.Type).Elem()
}
return &m
}
func (m *manyModel) NewModel() ColumnScanner {
if m.sliceOfPtr {
m.strct = reflect.New(m.table.Type).Elem()
} else {
m.strct.Set(m.table.zeroStruct)
}
m.structInited = false
m.structTableModel.NewModel()
return m
}
func (m *manyModel) AddModel(model ColumnScanner) error {
m.buf = modelId(m.buf[:0], m.strct, m.rel.FKs)
dstValues, ok := m.dstValues[string(m.buf)]
if !ok {
return fmt.Errorf("pg: can't find dst value for model id=%q", m.buf)
}
for _, v := range dstValues {
if m.sliceOfPtr {
v.Set(reflect.Append(v, m.strct.Addr()))
} else {
v.Set(reflect.Append(v, m.strct))
}
}
return nil
}
func (m *manyModel) AfterQuery(db DB) error {
if !m.rel.JoinTable.HasFlag(AfterQueryHookFlag) {
return nil
}
var retErr error
for _, slices := range m.dstValues {
for _, slice := range slices {
err := callAfterQueryHookSlice(slice, m.sliceOfPtr, db)
if err != nil && retErr == nil {
retErr = err
}
}
}
return retErr
}
func (m *manyModel) AfterSelect(db DB) error {
return nil
}
func (m *manyModel) BeforeInsert(db DB) error {
return nil
}
func (m *manyModel) AfterInsert(db DB) error {
return nil
}
func (m *manyModel) BeforeUpdate(db DB) error {
return nil
}
func (m *manyModel) AfterUpdate(db DB) error {
return nil
}
func (m *manyModel) BeforeDelete(db DB) error {
return nil
}
func (m *manyModel) AfterDelete(db DB) error {
return nil
}

128
vendor/github.com/go-pg/pg/orm/model_table_slice.go generated vendored Normal file
View file

@ -0,0 +1,128 @@
package orm
import "reflect"
type sliceTableModel struct {
structTableModel
slice reflect.Value
sliceOfPtr bool
}
var _ tableModel = (*sliceTableModel)(nil)
func (m *sliceTableModel) init(sliceType reflect.Type) {
switch sliceType.Elem().Kind() {
case reflect.Ptr, reflect.Interface:
m.sliceOfPtr = true
}
}
func (sliceTableModel) useQueryOne() {}
func (m *sliceTableModel) Join(name string, apply func(*Query) (*Query, error)) (bool, *join) {
return m.join(m.Value(), name, apply)
}
func (m *sliceTableModel) Bind(bind reflect.Value) {
m.slice = bind.Field(m.index[len(m.index)-1])
}
func (m *sliceTableModel) Value() reflect.Value {
return m.slice
}
func (m *sliceTableModel) Init() error {
if m.slice.IsValid() && m.slice.Len() > 0 {
m.slice.Set(m.slice.Slice(0, 0))
}
return nil
}
func (m *sliceTableModel) NewModel() ColumnScanner {
m.strct = m.nextElem()
m.structInited = false
m.structTableModel.NewModel()
return m
}
func (m *sliceTableModel) AfterQuery(db DB) error {
if !m.table.HasFlag(AfterQueryHookFlag) {
return nil
}
return callAfterQueryHookSlice(m.slice, m.sliceOfPtr, db)
}
func (m *sliceTableModel) AfterSelect(db DB) error {
if !m.table.HasFlag(AfterSelectHookFlag) {
return nil
}
return callAfterSelectHookSlice(m.slice, m.sliceOfPtr, db)
}
func (m *sliceTableModel) BeforeInsert(db DB) error {
if !m.table.HasFlag(BeforeInsertHookFlag) {
return nil
}
return callBeforeInsertHookSlice(m.slice, m.sliceOfPtr, db)
}
func (m *sliceTableModel) AfterInsert(db DB) error {
if !m.table.HasFlag(AfterInsertHookFlag) {
return nil
}
return callAfterInsertHookSlice(m.slice, m.sliceOfPtr, db)
}
func (m *sliceTableModel) BeforeUpdate(db DB) error {
if !m.table.HasFlag(BeforeUpdateHookFlag) {
return nil
}
return callBeforeUpdateHookSlice(m.slice, m.sliceOfPtr, db)
}
func (m *sliceTableModel) AfterUpdate(db DB) error {
if !m.table.HasFlag(AfterUpdateHookFlag) {
return nil
}
return callAfterUpdateHookSlice(m.slice, m.sliceOfPtr, db)
}
func (m *sliceTableModel) BeforeDelete(db DB) error {
if !m.table.HasFlag(BeforeDeleteHookFlag) {
return nil
}
return callBeforeDeleteHookSlice(m.slice, m.sliceOfPtr, db)
}
func (m *sliceTableModel) AfterDelete(db DB) error {
if !m.table.HasFlag(AfterDeleteHookFlag) {
return nil
}
return callAfterDeleteHookSlice(m.slice, m.sliceOfPtr, db)
}
func (m *sliceTableModel) nextElem() reflect.Value {
if m.slice.Len() < m.slice.Cap() {
m.slice.Set(m.slice.Slice(0, m.slice.Len()+1))
elem := m.slice.Index(m.slice.Len() - 1)
if m.sliceOfPtr {
if elem.IsNil() {
elem.Set(reflect.New(elem.Type().Elem()))
}
return elem.Elem()
} else {
return elem
}
}
if m.sliceOfPtr {
elem := reflect.New(m.table.Type)
m.slice.Set(reflect.Append(m.slice, elem))
return elem.Elem()
} else {
m.slice.Set(reflect.Append(m.slice, m.table.zeroStruct))
return m.slice.Index(m.slice.Len() - 1)
}
}

314
vendor/github.com/go-pg/pg/orm/model_table_struct.go generated vendored Normal file
View file

@ -0,0 +1,314 @@
package orm
import (
"fmt"
"reflect"
"strings"
)
type structTableModel struct {
table *Table
rel *Relation
joins []join
root reflect.Value
index []int
strct reflect.Value
structInited bool
}
var _ tableModel = (*structTableModel)(nil)
func newStructTableModelValue(v reflect.Value) *structTableModel {
return &structTableModel{
table: Tables.Get(v.Type()),
root: v,
strct: v,
}
}
func newStructTableModelType(typ reflect.Type) *structTableModel {
return &structTableModel{
table: Tables.Get(typ),
}
}
func (structTableModel) useQueryOne() bool {
return true
}
func (m *structTableModel) Table() *Table {
return m.table
}
func (m *structTableModel) Relation() *Relation {
return m.rel
}
func (m *structTableModel) AppendParam(b []byte, f QueryFormatter, name string) ([]byte, bool) {
b, ok := m.table.AppendParam(b, m.strct, name)
if ok {
return b, true
}
switch name {
case "TableName":
b = f.FormatQuery(b, string(m.table.Name))
return b, true
case "TableAlias":
b = append(b, m.table.Alias...)
return b, true
case "Columns":
b = appendColumns(b, m.table.Fields)
return b, true
}
return b, false
}
func (m *structTableModel) Root() reflect.Value {
return m.root
}
func (m *structTableModel) Index() []int {
return m.index
}
func (m *structTableModel) ParentIndex() []int {
return m.index[:len(m.index)-len(m.rel.Field.Index)]
}
func (m *structTableModel) Value() reflect.Value {
return m.strct
}
func (m *structTableModel) Mount(host reflect.Value) {
m.strct = host.FieldByIndex(m.rel.Field.Index)
m.structInited = false
}
func (m *structTableModel) initStruct() {
if m.structInited {
return
}
m.structInited = true
if m.strct.Kind() == reflect.Interface {
m.strct = m.strct.Elem()
}
if m.strct.Kind() == reflect.Ptr {
if m.strct.IsNil() {
m.strct.Set(reflect.New(m.strct.Type().Elem()))
m.strct = m.strct.Elem()
} else {
m.strct = m.strct.Elem()
}
}
m.mountJoins()
}
func (m *structTableModel) mountJoins() {
for i := range m.joins {
j := &m.joins[i]
switch j.Rel.Type {
case HasOneRelation, BelongsToRelation:
j.JoinModel.Mount(m.strct)
}
}
}
func (structTableModel) Init() error {
return nil
}
func (m *structTableModel) NewModel() ColumnScanner {
m.initStruct()
return m
}
func (m *structTableModel) AddModel(_ ColumnScanner) error {
return nil
}
func (m *structTableModel) AfterQuery(db DB) error {
if !m.table.HasFlag(AfterQueryHookFlag) {
return nil
}
return callAfterQueryHook(m.strct.Addr(), db)
}
func (m *structTableModel) AfterSelect(db DB) error {
if !m.table.HasFlag(AfterSelectHookFlag) {
return nil
}
return callAfterSelectHook(m.strct.Addr(), db)
}
func (m *structTableModel) BeforeInsert(db DB) error {
if !m.table.HasFlag(BeforeInsertHookFlag) {
return nil
}
return callBeforeInsertHook(m.strct.Addr(), db)
}
func (m *structTableModel) AfterInsert(db DB) error {
if !m.table.HasFlag(AfterInsertHookFlag) {
return nil
}
return callAfterInsertHook(m.strct.Addr(), db)
}
func (m *structTableModel) BeforeUpdate(db DB) error {
if !m.table.HasFlag(BeforeUpdateHookFlag) {
return nil
}
return callBeforeUpdateHook(m.strct.Addr(), db)
}
func (m *structTableModel) AfterUpdate(db DB) error {
if !m.table.HasFlag(AfterUpdateHookFlag) {
return nil
}
return callAfterUpdateHook(m.strct.Addr(), db)
}
func (m *structTableModel) BeforeDelete(db DB) error {
if !m.table.HasFlag(BeforeDeleteHookFlag) {
return nil
}
return callBeforeDeleteHook(m.strct.Addr(), db)
}
func (m *structTableModel) AfterDelete(db DB) error {
if !m.table.HasFlag(AfterDeleteHookFlag) {
return nil
}
return callAfterDeleteHook(m.strct.Addr(), db)
}
func (m *structTableModel) ScanColumn(colIdx int, colName string, b []byte) error {
ok, err := m.scanColumn(colIdx, colName, b)
if ok {
return err
}
return fmt.Errorf("pg: can't find column=%s in model=%s", colName, m.table.Type.Name())
}
func (m *structTableModel) scanColumn(colIdx int, colName string, b []byte) (bool, error) {
m.initStruct()
joinName, fieldName := splitColumn(colName)
if joinName != "" {
if join := m.GetJoin(joinName); join != nil {
return join.JoinModel.scanColumn(colIdx, fieldName, b)
}
if m.table.ModelName == joinName {
return m.scanColumn(colIdx, fieldName, b)
}
}
field, ok := m.table.FieldsMap[colName]
if !ok {
return false, nil
}
return true, field.ScanValue(m.strct, b)
}
func (m *structTableModel) GetJoin(name string) *join {
for i := range m.joins {
j := &m.joins[i]
if j.Rel.Field.GoName == name || j.Rel.Field.SQLName == name {
return j
}
}
return nil
}
func (m *structTableModel) GetJoins() []join {
return m.joins
}
func (m *structTableModel) AddJoin(j join) *join {
m.joins = append(m.joins, j)
return &m.joins[len(m.joins)-1]
}
func (m *structTableModel) Join(name string, apply func(*Query) (*Query, error)) (bool, *join) {
return m.join(m.Value(), name, apply)
}
func (m *structTableModel) join(
bind reflect.Value, name string, apply func(*Query) (*Query, error),
) (bool, *join) {
path := strings.Split(name, ".")
index := make([]int, 0, len(path))
currJoin := join{
BaseModel: m,
JoinModel: m,
}
var created bool
var lastJoin *join
var hasColumnName bool
for _, name := range path {
rel, ok := currJoin.JoinModel.Table().Relations[name]
if !ok {
hasColumnName = true
break
}
currJoin.Rel = rel
index = append(index, rel.Field.Index...)
if j := currJoin.JoinModel.GetJoin(name); j != nil {
currJoin.BaseModel = j.BaseModel
currJoin.JoinModel = j.JoinModel
created = false
lastJoin = j
} else {
model, err := newTableModelIndex(bind, index, rel)
if err != nil {
return false, nil
}
currJoin.Parent = lastJoin
currJoin.BaseModel = currJoin.JoinModel
currJoin.JoinModel = model
created = true
lastJoin = currJoin.BaseModel.AddJoin(currJoin)
}
}
// No joins with such name.
if lastJoin == nil {
return false, nil
}
if apply != nil {
lastJoin.ApplyQuery = apply
}
if hasColumnName {
column := path[len(path)-1]
if column == "_" {
if lastJoin.Columns == nil {
lastJoin.Columns = make([]string, 0)
}
} else {
lastJoin.Columns = append(lastJoin.Columns, column)
}
}
return created, lastJoin
}
func splitColumn(s string) (string, string) {
ind := strings.Index(s, "__")
if ind == -1 {
return "", s
}
return s[:ind], s[ind+2:]
}

41
vendor/github.com/go-pg/pg/orm/orm.go generated vendored Normal file
View file

@ -0,0 +1,41 @@
package orm
import "io"
// ColumnScanner is used to scan column values.
type ColumnScanner interface {
// Scan assigns a column value from a row.
//
// An error should be returned if the value can not be stored
// without loss of information.
ScanColumn(colIdx int, colName string, b []byte) error
}
type QueryAppender interface {
Copy() QueryAppender
Query() *Query
AppendQuery(dst []byte) ([]byte, error)
}
type QueryFormatter interface {
FormatQuery(b []byte, query string, params ...interface{}) []byte
}
// DB is a common interface for pg.DB and pg.Tx types.
type DB interface {
Model(model ...interface{}) *Query
Select(model interface{}) error
Insert(model ...interface{}) error
Update(model ...interface{}) error
Delete(model interface{}) error
Exec(query interface{}, params ...interface{}) (Result, error)
ExecOne(query interface{}, params ...interface{}) (Result, error)
Query(coll, query interface{}, params ...interface{}) (Result, error)
QueryOne(model, query interface{}, params ...interface{}) (Result, error)
CopyFrom(r io.Reader, query interface{}, params ...interface{}) (Result, error)
CopyTo(w io.Writer, query interface{}, params ...interface{}) (Result, error)
QueryFormatter
}

986
vendor/github.com/go-pg/pg/orm/query.go generated vendored Normal file
View file

@ -0,0 +1,986 @@
package orm
import (
"errors"
"fmt"
"io"
"reflect"
"strings"
"sync"
"time"
"github.com/go-pg/pg/internal"
"github.com/go-pg/pg/types"
)
type withQuery struct {
name string
query *Query
}
type Query struct {
db DB
stickyErr error
model tableModel
ignoreModel bool
with []withQuery
tables []FormatAppender
columns []FormatAppender
set []FormatAppender
where []sepFormatAppender
updWhere []sepFormatAppender
joins []FormatAppender
group []FormatAppender
having []queryParamsAppender
order []FormatAppender
onConflict *queryParamsAppender
returning []queryParamsAppender
limit int
offset int
selFor FormatAppender
}
var _ queryAppender = (*Query)(nil)
func NewQuery(db DB, model ...interface{}) *Query {
return (&Query{}).DB(db).Model(model...)
}
// New returns new zero Query binded to the current db and model.
func (q *Query) New() *Query {
return &Query{
db: q.db,
model: q.model,
ignoreModel: true,
}
}
func (q *Query) AppendQuery(b []byte) ([]byte, error) {
return selectQuery{q: q}.AppendQuery(b)
}
// Copy returns copy of the Query.
func (q *Query) Copy() *Query {
copy := &Query{
db: q.db,
stickyErr: q.stickyErr,
model: q.model,
ignoreModel: q.ignoreModel,
tables: q.tables[:len(q.tables):len(q.tables)],
columns: q.columns[:len(q.columns):len(q.columns)],
set: q.set[:len(q.set):len(q.set)],
where: q.where[:len(q.where):len(q.where)],
updWhere: q.updWhere[:len(q.updWhere):len(q.updWhere)],
joins: q.joins[:len(q.joins):len(q.joins)],
group: q.group[:len(q.group):len(q.group)],
having: q.having[:len(q.having):len(q.having)],
order: q.order[:len(q.order):len(q.order)],
onConflict: q.onConflict,
returning: q.returning[:len(q.returning):len(q.returning)],
limit: q.limit,
offset: q.offset,
}
for _, with := range q.with {
copy = copy.With(with.name, with.query.Copy())
}
return copy
}
func (q *Query) err(err error) *Query {
if q.stickyErr == nil {
q.stickyErr = err
}
return q
}
func (q *Query) DB(db DB) *Query {
q.db = db
for _, with := range q.with {
with.query.db = db
}
return q
}
func (q *Query) Model(model ...interface{}) *Query {
var err error
switch l := len(model); {
case l == 0:
q.model = nil
case l == 1:
q.model, err = newTableModel(model[0])
case l > 1:
q.model, err = newTableModel(&model)
}
if err != nil {
q = q.err(err)
}
if q.ignoreModel {
q.ignoreModel = false
}
return q
}
// With adds subq as common table expression with the given name.
func (q *Query) With(name string, subq *Query) *Query {
q.with = append(q.with, withQuery{name, subq})
return q
}
// WrapWith creates new Query and adds to it current query as
// common table expression with the given name.
func (q *Query) WrapWith(name string) *Query {
wrapper := q.New()
wrapper.with = q.with
q.with = nil
wrapper = wrapper.With(name, q)
return wrapper
}
func (q *Query) Table(tables ...string) *Query {
for _, table := range tables {
q.tables = append(q.tables, fieldAppender{table})
}
return q
}
func (q *Query) TableExpr(expr string, params ...interface{}) *Query {
q.tables = append(q.tables, queryParamsAppender{expr, params})
return q
}
// Column adds column to the Query quoting it according to PostgreSQL rules.
// ColumnExpr can be used to bypass quoting restriction.
func (q *Query) Column(columns ...string) *Query {
for _, column := range columns {
if column == "_" {
if q.columns == nil {
q.columns = make([]FormatAppender, 0)
}
continue
}
if q.model != nil {
if _, j := q.model.Join(column, nil); j != nil {
continue
}
}
q.columns = append(q.columns, fieldAppender{column})
}
return q
}
// ColumnExpr adds column expression to the Query.
func (q *Query) ColumnExpr(expr string, params ...interface{}) *Query {
q.columns = append(q.columns, queryParamsAppender{expr, params})
return q
}
func (q *Query) getFields() ([]*Field, error) {
return q._getFields(false)
}
func (q *Query) getDataFields() ([]*Field, error) {
return q._getFields(true)
}
func (q *Query) _getFields(filterPKs bool) ([]*Field, error) {
table := q.model.Table()
var columns []*Field
for _, col := range q.columns {
f, ok := col.(fieldAppender)
if !ok {
continue
}
field, err := table.GetField(f.field)
if err != nil {
return nil, err
}
if filterPKs && field.HasFlag(PrimaryKeyFlag) {
continue
}
columns = append(columns, field)
}
return columns, nil
}
func (q *Query) Relation(name string, apply func(*Query) (*Query, error)) *Query {
if _, j := q.model.Join(name, apply); j == nil {
return q.err(fmt.Errorf(
"model=%s does not have relation=%s",
q.model.Table().Type.Name(), name,
))
}
return q
}
func (q *Query) Set(set string, params ...interface{}) *Query {
q.set = append(q.set, queryParamsAppender{set, params})
return q
}
func (q *Query) Where(where string, params ...interface{}) *Query {
q.addWhere(&whereAppender{
sep: "AND",
where: where,
params: params,
})
return q
}
func (q *Query) WhereOr(where string, params ...interface{}) *Query {
q.addWhere(&whereAppender{
sep: "OR",
where: where,
params: params,
})
return q
}
// WhereGroup encloses conditions added in the function in parentheses.
//
// q.Where("TRUE").
// WhereGroup(func(q *orm.Query) (*orm.Query, error)) {
// q = q.WhereOr("FALSE").WhereOr("TRUE").
// return q, nil
// })
//
// generates
//
// WHERE TRUE AND (FALSE OR TRUE)
func (q *Query) WhereGroup(fn func(*Query) (*Query, error)) *Query {
return q.whereGroup("AND", fn)
}
// WhereOrGroup encloses conditions added in the function in parentheses.
//
// q.Where("TRUE").
// WhereOrGroup(func(q *orm.Query) (*orm.Query, error)) {
// q = q.Where("FALSE").Where("TRUE").
// return q, nil
// })
//
// generates
//
// WHERE TRUE OR (FALSE AND TRUE)
func (q *Query) WhereOrGroup(fn func(*Query) (*Query, error)) *Query {
return q.whereGroup("OR", fn)
}
func (q *Query) whereGroup(conj string, fn func(*Query) (*Query, error)) *Query {
saved := q.where
q.where = nil
newq, err := fn(q)
if err != nil {
q.err(err)
return q
}
f := whereGroupAppender{
sep: conj,
where: newq.where,
}
newq.where = saved
newq.addWhere(f)
return newq
}
// WhereIn is a shortcut for Where and pg.In to work with IN operator:
//
// WhereIn("id IN (?)", 1, 2, 3)
func (q *Query) WhereIn(where string, params ...interface{}) *Query {
return q.Where(where, types.In(params))
}
func (q *Query) addWhere(f sepFormatAppender) {
if q.onConflictDoUpdate() {
q.updWhere = append(q.updWhere, f)
} else {
q.where = append(q.where, f)
}
}
func (q *Query) Join(join string, params ...interface{}) *Query {
q.joins = append(q.joins, queryParamsAppender{join, params})
return q
}
func (q *Query) Group(columns ...string) *Query {
for _, column := range columns {
q.group = append(q.group, fieldAppender{column})
}
return q
}
func (q *Query) GroupExpr(group string, params ...interface{}) *Query {
q.group = append(q.group, queryParamsAppender{group, params})
return q
}
func (q *Query) Having(having string, params ...interface{}) *Query {
q.having = append(q.having, queryParamsAppender{having, params})
return q
}
// Order adds sort order to the Query quoting column name.
// OrderExpr can be used to bypass quoting restriction.
func (q *Query) Order(orders ...string) *Query {
loop:
for _, order := range orders {
ind := strings.Index(order, " ")
if ind != -1 {
field := order[:ind]
sort := order[ind+1:]
switch internal.ToUpper(sort) {
case "ASC", "DESC", "ASC NULLS FIRST", "DESC NULLS FIRST",
"ASC NULLS LAST", "DESC NULLS LAST":
q = q.OrderExpr("? ?", types.F(field), types.Q(sort))
continue loop
}
}
q.order = append(q.order, fieldAppender{order})
}
return q
}
// Order adds sort order to the Query.
func (q *Query) OrderExpr(order string, params ...interface{}) *Query {
q.order = append(q.order, queryParamsAppender{order, params})
return q
}
func (q *Query) Limit(n int) *Query {
q.limit = n
return q
}
func (q *Query) Offset(n int) *Query {
q.offset = n
return q
}
func (q *Query) OnConflict(s string, params ...interface{}) *Query {
q.onConflict = &queryParamsAppender{s, params}
return q
}
func (q *Query) onConflictDoUpdate() bool {
return q.onConflict != nil &&
strings.HasSuffix(q.onConflict.query, "DO UPDATE")
}
func (q *Query) Returning(s string, params ...interface{}) *Query {
q.returning = append(q.returning, queryParamsAppender{s, params})
return q
}
func (q *Query) For(s string, params ...interface{}) *Query {
q.selFor = queryParamsAppender{s, params}
return q
}
// Apply calls the fn passing the Query as an argument.
func (q *Query) Apply(fn func(*Query) (*Query, error)) *Query {
qq, err := fn(q)
if err != nil {
q.err(err)
return q
}
return qq
}
// Count returns number of rows matching the query using count aggregate function.
func (q *Query) Count() (int, error) {
if q.stickyErr != nil {
return 0, q.stickyErr
}
var count int
_, err := q.db.QueryOne(
Scan(&count),
q.countSelectQuery("count(*)"),
q.model,
)
return count, err
}
func (q *Query) countSelectQuery(column string) selectQuery {
return selectQuery{
q: q,
count: column,
}
}
// First selects the first row.
func (q *Query) First() error {
err := q.model.Table().checkPKs()
if err != nil {
return err
}
b := columns(nil, q.model.Table().Alias, "", q.model.Table().PKs)
return q.OrderExpr(internal.BytesToString(b)).Limit(1).Select()
}
// Last selects the last row.
func (q *Query) Last() error {
err := q.model.Table().checkPKs()
if err != nil {
return err
}
b := columns(nil, q.model.Table().Alias, "", q.model.Table().PKs)
b = append(b, " DESC"...)
return q.OrderExpr(internal.BytesToString(b)).Limit(1).Select()
}
// Select selects the model.
func (q *Query) Select(values ...interface{}) error {
if q.stickyErr != nil {
return q.stickyErr
}
model, err := q.newModel(values...)
if err != nil {
return err
}
res, err := q.query(model, selectQuery{q: q})
if err != nil {
return err
}
if res.RowsReturned() > 0 {
if q.model != nil {
if err := q.selectJoins(q.model.GetJoins()); err != nil {
return err
}
}
if err := model.AfterSelect(q.db); err != nil {
return err
}
}
return nil
}
func (q *Query) newModel(values ...interface{}) (Model, error) {
if len(values) > 0 {
return NewModel(values...)
}
return q.model, nil
}
func (q *Query) query(model Model, query interface{}) (Result, error) {
if _, ok := model.(useQueryOne); ok {
return q.db.QueryOne(model, query, q.model)
}
return q.db.Query(model, query, q.model)
}
// SelectAndCount runs Select and Count in two goroutines,
// waits for them to finish and returns the result.
func (q *Query) SelectAndCount(values ...interface{}) (count int, err error) {
if q.stickyErr != nil {
return 0, q.stickyErr
}
var wg sync.WaitGroup
wg.Add(2)
var mu sync.Mutex
go func() {
defer wg.Done()
if e := q.Select(values...); e != nil {
mu.Lock()
err = e
mu.Unlock()
}
}()
go func() {
defer wg.Done()
var e error
count, e = q.Count()
if e != nil {
mu.Lock()
err = e
mu.Unlock()
}
}()
wg.Wait()
return count, err
}
func (q *Query) forEachHasOneJoin(fn func(*join)) {
if q.model == nil {
return
}
q._forEachHasOneJoin(fn, q.model.GetJoins())
}
func (q *Query) _forEachHasOneJoin(fn func(*join), joins []join) {
for i := range joins {
j := &joins[i]
switch j.Rel.Type {
case HasOneRelation, BelongsToRelation:
fn(j)
q._forEachHasOneJoin(fn, j.JoinModel.GetJoins())
}
}
}
func (q *Query) selectJoins(joins []join) error {
var err error
for i := range joins {
j := &joins[i]
if j.Rel.Type == HasOneRelation || j.Rel.Type == BelongsToRelation {
err = q.selectJoins(j.JoinModel.GetJoins())
} else {
err = j.Select(q.db)
}
if err != nil {
return err
}
}
return nil
}
// Insert inserts the model.
func (q *Query) Insert(values ...interface{}) (Result, error) {
if q.stickyErr != nil {
return nil, q.stickyErr
}
model, err := q.newModel(values...)
if err != nil {
return nil, err
}
if q.model != nil {
if err := q.model.BeforeInsert(q.db); err != nil {
return nil, err
}
}
res, err := q.db.Query(model, insertQuery{q: q}, q.model)
if err != nil {
return nil, err
}
if q.model != nil {
if err := q.model.AfterInsert(q.db); err != nil {
return nil, err
}
}
return res, nil
}
// SelectOrInsert selects the model inserting one if it does not exist.
func (q *Query) SelectOrInsert(values ...interface{}) (inserted bool, err error) {
if q.stickyErr != nil {
return false, q.stickyErr
}
var insertErr error
for i := 0; i < 5; i++ {
if i >= 2 {
time.Sleep(internal.RetryBackoff(i-2, 250*time.Millisecond, 4*time.Second))
}
err := q.Select(values...)
if err == nil {
return false, nil
}
if err != internal.ErrNoRows {
return false, err
}
res, err := q.Insert(values...)
if err != nil {
insertErr = err
if pgErr, ok := err.(internal.PGError); ok {
if pgErr.IntegrityViolation() {
continue
}
if pgErr.Field('C') == "55000" {
// Retry on "#55000 attempted to delete invisible tuple".
continue
}
}
return false, err
}
if res.RowsAffected() == 1 {
return true, nil
}
}
err = fmt.Errorf(
"pg: SelectOrInsert: select returns no rows (insert fails with err=%q)",
insertErr,
)
return false, err
}
// Update updates the model.
func (q *Query) Update(values ...interface{}) (Result, error) {
if q.stickyErr != nil {
return nil, q.stickyErr
}
model, err := q.newModel(values...)
if err != nil {
return nil, err
}
if q.model != nil {
if err := q.model.BeforeUpdate(q.db); err != nil {
return nil, err
}
}
res, err := q.db.Query(model, updateQuery{q}, q.model)
if err != nil {
return nil, err
}
if q.model != nil {
if err := q.model.AfterUpdate(q.db); err != nil {
return nil, err
}
}
return res, nil
}
// Delete deletes the model.
func (q *Query) Delete(values ...interface{}) (Result, error) {
if q.stickyErr != nil {
return nil, q.stickyErr
}
model, err := q.newModel(values...)
if err != nil {
return nil, err
}
if q.model != nil {
if err := q.model.BeforeDelete(q.db); err != nil {
return nil, err
}
}
res, err := q.db.Query(model, deleteQuery{q}, q.model)
if err != nil {
return nil, err
}
if q.model != nil {
if err := q.model.AfterDelete(q.db); err != nil {
return nil, err
}
}
return res, nil
}
func (q *Query) CreateTable(opt *CreateTableOptions) (Result, error) {
if q.stickyErr != nil {
return nil, q.stickyErr
}
return q.db.Exec(createTableQuery{
q: q,
opt: opt,
})
}
func (q *Query) DropTable(opt *DropTableOptions) (Result, error) {
if q.stickyErr != nil {
return nil, q.stickyErr
}
return q.db.Exec(dropTableQuery{
q: q,
opt: opt,
})
}
// Exec is an alias for DB.Exec.
func (q *Query) Exec(query interface{}, params ...interface{}) (Result, error) {
params = append(params, q.model)
return q.db.Exec(query, params...)
}
// ExecOne is an alias for DB.ExecOne.
func (q *Query) ExecOne(query interface{}, params ...interface{}) (Result, error) {
params = append(params, q.model)
return q.db.ExecOne(query, params...)
}
// Query is an alias for DB.Query.
func (q *Query) Query(model, query interface{}, params ...interface{}) (Result, error) {
params = append(params, q.model)
return q.db.Query(model, query, params...)
}
// QueryOne is an alias for DB.QueryOne.
func (q *Query) QueryOne(model, query interface{}, params ...interface{}) (Result, error) {
params = append(params, q.model)
return q.db.QueryOne(model, query, params...)
}
// CopyFrom is an alias from DB.CopyFrom.
func (q *Query) CopyFrom(r io.Reader, query interface{}, params ...interface{}) (Result, error) {
params = append(params, q.model)
return q.db.CopyFrom(r, query, params...)
}
// CopyTo is an alias from DB.CopyTo.
func (q *Query) CopyTo(w io.Writer, query interface{}, params ...interface{}) (Result, error) {
params = append(params, q.model)
return q.db.CopyTo(w, query, params...)
}
func (q *Query) FormatQuery(b []byte, query string, params ...interface{}) []byte {
params = append(params, q.model)
if q.db != nil {
return q.db.FormatQuery(b, query, params...)
}
return formatter.Append(b, query, params...)
}
func (q *Query) hasModel() bool {
return !q.ignoreModel && q.model != nil
}
func (q *Query) hasTables() bool {
return q.hasModel() || len(q.tables) > 0
}
func (q *Query) appendTableName(b []byte) []byte {
return q.FormatQuery(b, string(q.model.Table().Name))
}
func (q *Query) appendTableNameWithAlias(b []byte) []byte {
b = q.appendTableName(b)
b = append(b, " AS "...)
b = append(b, q.model.Table().Alias...)
return b
}
func (q *Query) appendFirstTable(b []byte) []byte {
if q.hasModel() {
return q.appendTableName(b)
}
if len(q.tables) > 0 {
b = q.tables[0].AppendFormat(b, q)
}
return b
}
func (q *Query) appendFirstTableWithAlias(b []byte) []byte {
if q.hasModel() {
return q.appendTableNameWithAlias(b)
}
if len(q.tables) > 0 {
b = q.tables[0].AppendFormat(b, q)
}
return b
}
func (q *Query) appendTables(b []byte) []byte {
if q.hasModel() {
b = q.appendTableNameWithAlias(b)
if len(q.tables) > 0 {
b = append(b, ", "...)
}
}
for i, f := range q.tables {
if i > 0 {
b = append(b, ", "...)
}
b = f.AppendFormat(b, q)
}
return b
}
func (q *Query) hasOtherTables() bool {
if q.hasModel() {
return len(q.tables) > 0
}
return len(q.tables) > 1
}
func (q *Query) modelHasData() bool {
if !q.hasModel() {
return false
}
v := q.model.Value()
return v.Kind() == reflect.Slice && v.Len() > 0
}
func (q *Query) appendOtherTables(b []byte) []byte {
tables := q.tables
if !q.hasModel() {
tables = tables[1:]
}
for i, f := range tables {
if i > 0 {
b = append(b, ", "...)
}
b = f.AppendFormat(b, q)
}
return b
}
func (q *Query) appendColumns(b []byte) []byte {
for i, f := range q.columns {
if i > 0 {
b = append(b, ", "...)
}
b = f.AppendFormat(b, q)
}
return b
}
func (q *Query) mustAppendWhere(b []byte) ([]byte, error) {
if len(q.where) > 0 {
b = q.appendWhere(b)
return b, nil
}
if q.model == nil {
return nil, errors.New("pg: Model is nil")
}
if err := q.model.Table().checkPKs(); err != nil {
return nil, err
}
b = append(b, " WHERE "...)
return wherePKQuery{q}.AppendFormat(b, nil), nil
}
func (q *Query) appendWhere(b []byte) []byte {
return q._appendWhere(b, q.where)
}
func (q *Query) appendUpdWhere(b []byte) []byte {
return q._appendWhere(b, q.updWhere)
}
func (q *Query) _appendWhere(b []byte, where []sepFormatAppender) []byte {
b = append(b, " WHERE "...)
for i, f := range where {
if i > 0 {
b = append(b, ' ')
b = f.AppendSep(b)
b = append(b, ' ')
}
b = f.AppendFormat(b, q)
}
return b
}
func (q *Query) appendSet(b []byte) []byte {
b = append(b, " SET "...)
for i, f := range q.set {
if i > 0 {
b = append(b, ", "...)
}
b = f.AppendFormat(b, q)
}
return b
}
func (q *Query) appendReturning(b []byte) []byte {
b = append(b, " RETURNING "...)
for i, f := range q.returning {
if i > 0 {
b = append(b, ", "...)
}
b = f.AppendFormat(b, q)
}
return b
}
func (q *Query) appendWith(b []byte) ([]byte, error) {
var err error
b = append(b, "WITH "...)
for i, with := range q.with {
if i > 0 {
b = append(b, ", "...)
}
b = types.AppendField(b, with.name, 1)
b = append(b, " AS ("...)
b, err = selectQuery{q: with.query}.AppendQuery(b)
if err != nil {
return nil, err
}
b = append(b, ')')
}
b = append(b, ' ')
return b, nil
}
//------------------------------------------------------------------------------
type wherePKQuery struct {
*Query
}
func (wherePKQuery) AppendSep(b []byte) []byte {
return append(b, "AND"...)
}
func (q wherePKQuery) AppendFormat(b []byte, f QueryFormatter) []byte {
table := q.model.Table()
value := q.model.Value()
if value.Kind() == reflect.Struct {
return appendColumnAndValue(b, value, table.Alias, table.PKs)
} else {
return appendColumnAndColumn(b, value, table.Alias, table.PKs)
}
}
func appendColumnAndValue(b []byte, v reflect.Value, alias types.Q, fields []*Field) []byte {
for i, f := range fields {
if i > 0 {
b = append(b, " AND "...)
}
b = append(b, alias...)
b = append(b, '.')
b = append(b, f.Column...)
b = append(b, " = "...)
b = f.AppendValue(b, v, 1)
}
return b
}
func appendColumnAndColumn(b []byte, v reflect.Value, alias types.Q, fields []*Field) []byte {
for i, f := range fields {
if i > 0 {
b = append(b, " AND "...)
}
b = append(b, alias...)
b = append(b, '.')
b = append(b, f.Column...)
b = append(b, " = _data."...)
b = append(b, f.Column...)
}
return b
}

61
vendor/github.com/go-pg/pg/orm/query_test.go generated vendored Normal file
View file

@ -0,0 +1,61 @@
package orm_test
import (
"testing"
"unsafe"
"github.com/go-pg/pg/orm"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
func TestQuerySize(t *testing.T) {
size := int(unsafe.Sizeof(orm.Query{}))
wanted := 360
if size != wanted {
t.Fatalf("got %d, wanted %d", size, wanted)
}
}
func TestQueryFormatQuery(t *testing.T) {
type FormatModel struct {
Foo string
Bar string
}
q := orm.NewQuery(nil, &FormatModel{"foo", "bar"})
params := &struct {
Foo string
}{
"not_foo",
}
b := q.FormatQuery(nil, "?foo ?TableName ?TableAlias ?Columns", params)
wanted := `'not_foo' "format_models" "format_model" "foo", "bar"`
if string(b) != wanted {
t.Fatalf("got `%s`, wanted `%s`", string(b), wanted)
}
}
var _ = Describe("NewQuery", func() {
It("works with nil db", func() {
q := orm.NewQuery(nil)
b, err := q.AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal("SELECT *"))
})
It("works with nil model", func() {
type Model struct {
Id int
}
q := orm.NewQuery(nil, (*Model)(nil))
b, err := q.AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`SELECT "model"."id" FROM "models" AS "model"`))
})
})

23
vendor/github.com/go-pg/pg/orm/relation.go generated vendored Normal file
View file

@ -0,0 +1,23 @@
package orm
import "github.com/go-pg/pg/types"
const (
HasOneRelation = 1 << iota
BelongsToRelation
HasManyRelation
Many2ManyRelation
)
type Relation struct {
Type int
Polymorphic bool
Field *Field
JoinTable *Table
FKs []*Field
M2MTableName types.Q
M2MTableAlias types.Q
BasePrefix string
JoinPrefix string
}

14
vendor/github.com/go-pg/pg/orm/result.go generated vendored Normal file
View file

@ -0,0 +1,14 @@
package orm
// A Result summarizes an executed SQL command.
type Result interface {
Model() Model
// RowsAffected returns the number of rows affected by SELECT, INSERT, UPDATE,
// or DELETE queries. It returns -1 if query can't possibly affect any rows,
// e.g. in case of CREATE or SHOW queries.
RowsAffected() int
// RowsReturned returns the number of rows returned by the query.
RowsReturned() int
}

188
vendor/github.com/go-pg/pg/orm/select.go generated vendored Normal file
View file

@ -0,0 +1,188 @@
package orm
import (
"strconv"
"strings"
)
func Select(db DB, model interface{}) error {
q := NewQuery(db, model)
if err := q.model.Table().checkPKs(); err != nil {
return err
}
q.where = append(q.where, wherePKQuery{q})
return q.Select()
}
type selectQuery struct {
q *Query
count string
}
var _ QueryAppender = (*selectQuery)(nil)
func (q selectQuery) Copy() QueryAppender {
return selectQuery{
q: q.q.Copy(),
count: q.count,
}
}
func (q selectQuery) Query() *Query {
return q.q
}
func (q selectQuery) AppendQuery(b []byte) ([]byte, error) {
if q.q.stickyErr != nil {
return nil, q.q.stickyErr
}
var err error
cteCount := q.count != "" && (len(q.q.group) > 0 || q.isDistinct())
if cteCount {
b = append(b, `WITH "_count_wrapper" AS (`...)
}
if len(q.q.with) > 0 {
b, err = q.q.appendWith(b)
if err != nil {
return nil, err
}
}
b = append(b, "SELECT "...)
if q.count != "" && !cteCount {
b = append(b, q.count...)
} else {
b = q.appendColumns(b)
}
if q.q.hasTables() {
b = append(b, " FROM "...)
b = q.q.appendTables(b)
}
q.q.forEachHasOneJoin(func(j *join) {
b = append(b, ' ')
b = j.appendHasOneJoin(q.q.db, b)
})
if len(q.q.joins) > 0 {
for _, f := range q.q.joins {
b = append(b, ' ')
b = f.AppendFormat(b, q.q)
}
}
if len(q.q.where) > 0 {
b = q.q.appendWhere(b)
}
if len(q.q.group) > 0 {
b = append(b, " GROUP BY "...)
for i, f := range q.q.group {
if i > 0 {
b = append(b, ", "...)
}
b = f.AppendFormat(b, q.q)
}
}
if len(q.q.having) > 0 {
b = append(b, " HAVING "...)
for i, f := range q.q.having {
if i > 0 {
b = append(b, " AND "...)
}
b = append(b, '(')
b = f.AppendFormat(b, q.q)
b = append(b, ')')
}
}
if q.count == "" {
if len(q.q.order) > 0 {
b = append(b, " ORDER BY "...)
for i, f := range q.q.order {
if i > 0 {
b = append(b, ", "...)
}
b = f.AppendFormat(b, q.q)
}
}
if q.q.limit != 0 {
b = append(b, " LIMIT "...)
b = strconv.AppendInt(b, int64(q.q.limit), 10)
}
if q.q.offset != 0 {
b = append(b, " OFFSET "...)
b = strconv.AppendInt(b, int64(q.q.offset), 10)
}
if q.q.selFor != nil {
b = append(b, " FOR "...)
b = q.q.selFor.AppendFormat(b, q.q)
}
} else if cteCount {
b = append(b, `) SELECT `...)
b = append(b, q.count...)
b = append(b, ` FROM "_count_wrapper"`...)
}
return b, nil
}
func (q selectQuery) appendColumns(b []byte) []byte {
start := len(b)
if q.q.columns != nil {
b = q.q.appendColumns(b)
} else if q.q.hasModel() {
b = q.appendTableColumns(b, q.q.model.Table())
} else {
b = append(b, '*')
}
q.q.forEachHasOneJoin(func(j *join) {
if len(b) != start {
b = append(b, ", "...)
start = len(b)
}
b = j.appendHasOneColumns(b)
if len(b) == start {
b = b[:len(b)-2]
}
})
return b
}
func (q selectQuery) appendTableColumns(b []byte, table *Table) []byte {
for i, f := range table.Fields {
if i > 0 {
b = append(b, ", "...)
}
b = append(b, table.Alias...)
b = append(b, '.')
b = append(b, f.Column...)
}
return b
}
func (q selectQuery) isDistinct() bool {
for _, column := range q.q.columns {
column, ok := column.(queryParamsAppender)
if ok {
if strings.Contains(column.query, "DISTINCT") ||
strings.Contains(column.query, "distinct") {
return true
}
}
}
return false
}

242
vendor/github.com/go-pg/pg/orm/select_test.go generated vendored Normal file
View file

@ -0,0 +1,242 @@
package orm
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
type User struct {
tableName struct{} `sql:"user"`
}
type SelectModel struct {
Id int
Name string
HasOne *HasOneModel
HasOneId int
HasMany []HasManyModel
}
type HasOneModel struct {
Id int
}
type HasManyModel struct {
Id int
SelectModelId int
}
var _ = Describe("Select", func() {
It("works with User model", func() {
q := NewQuery(nil, &User{})
b, err := selectQuery{q: q}.AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`SELECT FROM "user" AS "user"`))
})
It("copies query", func() {
q1 := NewQuery(nil).Where("1 = 1").Where("2 = 2").Where("3 = 3")
q2 := q1.Copy().Where("q2 = ?", "v2")
_ = q1.Where("q1 = ?", "v1")
b, err := selectQuery{q: q2}.AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal("SELECT * WHERE (1 = 1) AND (2 = 2) AND (3 = 3) AND (q2 = 'v2')"))
})
It("specifies all columns", func() {
q := NewQuery(nil, &SelectModel{})
b, err := selectQuery{q: q}.AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`SELECT "select_model"."id", "select_model"."name", "select_model"."has_one_id" FROM "select_models" AS "select_model"`))
})
It("omits columns in main query", func() {
q := NewQuery(nil, &SelectModel{}).Column("_", "HasOne")
b, err := selectQuery{q: q}.AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`SELECT "has_one"."id" AS "has_one__id" FROM "select_models" AS "select_model" LEFT JOIN "has_one_models" AS "has_one" ON "has_one"."id" = "select_model"."has_one_id"`))
})
It("omits columns in join query", func() {
q := NewQuery(nil, &SelectModel{}).Column("HasOne._")
b, err := selectQuery{q: q}.AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`SELECT "select_model"."id", "select_model"."name", "select_model"."has_one_id" FROM "select_models" AS "select_model" LEFT JOIN "has_one_models" AS "has_one" ON "has_one"."id" = "select_model"."has_one_id"`))
})
It("specifies all columns for has one", func() {
q := NewQuery(nil, &SelectModel{Id: 1}).Column("HasOne")
b, err := selectQuery{q: q}.AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`SELECT "select_model"."id", "select_model"."name", "select_model"."has_one_id", "has_one"."id" AS "has_one__id" FROM "select_models" AS "select_model" LEFT JOIN "has_one_models" AS "has_one" ON "has_one"."id" = "select_model"."has_one_id"`))
})
It("specifies all columns for has many", func() {
q := NewQuery(nil, &SelectModel{Id: 1}).Column("HasMany")
q, err := q.model.GetJoin("HasMany").manyQuery(nil)
Expect(err).NotTo(HaveOccurred())
b, err := selectQuery{q: q}.AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`SELECT "has_many_model"."id", "has_many_model"."select_model_id" FROM "has_many_models" AS "has_many_model" WHERE (("has_many_model"."select_model_id") IN ((1)))`))
})
It("supports multiple groups", func() {
q := NewQuery(nil).Group("one").Group("two")
b, err := selectQuery{q: q}.AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`SELECT * GROUP BY "one", "two"`))
})
It("WhereOr", func() {
q := NewQuery(nil).Where("1 = 1").WhereOr("1 = 2")
b, err := selectQuery{q: q}.AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`SELECT * WHERE (1 = 1) OR (1 = 2)`))
})
It("supports subqueries", func() {
subq := NewQuery(nil, &SelectModel{}).Column("id").Where("name IS NOT NULL")
q := NewQuery(nil).Where("id IN (?)", subq)
b, err := selectQuery{q: q}.AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`SELECT * WHERE (id IN (SELECT "id" FROM "select_models" AS "select_model" WHERE (name IS NOT NULL)))`))
})
It("supports locking", func() {
q := NewQuery(nil).For("UPDATE SKIP LOCKED")
b, err := selectQuery{q: q}.AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`SELECT * FOR UPDATE SKIP LOCKED`))
})
It("supports WhereGroup", func() {
q := NewQuery(nil).Where("TRUE").WhereGroup(func(q *Query) (*Query, error) {
q = q.Where("FALSE").WhereOr("TRUE")
return q, nil
})
b, err := selectQuery{q: q}.AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`SELECT * WHERE (TRUE) AND ((FALSE) OR (TRUE))`))
})
It("supports WhereOrGroup", func() {
q := NewQuery(nil).Where("TRUE").WhereOrGroup(func(q *Query) (*Query, error) {
q = q.Where("FALSE").Where("TRUE")
return q, nil
})
b, err := selectQuery{q: q}.AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`SELECT * WHERE (TRUE) OR ((FALSE) AND (TRUE))`))
})
})
var _ = Describe("Count", func() {
It("removes LIMIT, OFFSET, and ORDER", func() {
q := NewQuery(nil).Order("order").Limit(1).Offset(2)
b, err := q.countSelectQuery("count(*)").AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`SELECT count(*)`))
})
It("does not remove LIMIT, OFFSET, and ORDER from CTE", func() {
q := NewQuery(nil).
Column("col1", "col2").
Order("order").
Limit(1).
Offset(2).
WrapWith("wrapper").
Table("wrapper").
Order("order").
Limit(1).
Offset(2)
b, err := q.countSelectQuery("count(*)").AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`WITH "wrapper" AS (SELECT "col1", "col2" ORDER BY "order" LIMIT 1 OFFSET 2) SELECT count(*) FROM "wrapper"`))
})
It("includes has one joins", func() {
q := NewQuery(nil, &SelectModel{Id: 1}).Column("HasOne")
b, err := q.countSelectQuery("count(*)").AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`SELECT count(*) FROM "select_models" AS "select_model" LEFT JOIN "has_one_models" AS "has_one" ON "has_one"."id" = "select_model"."has_one_id"`))
})
It("uses CTE when query contains GROUP BY", func() {
q := NewQuery(nil).Group("one")
b, err := q.countSelectQuery("count(*)").AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`WITH "_count_wrapper" AS (SELECT * GROUP BY "one") SELECT count(*) FROM "_count_wrapper"`))
})
It("uses CTE when column contains DISTINCT", func() {
q := NewQuery(nil).ColumnExpr("DISTINCT group_id")
b, err := q.countSelectQuery("count(*)").AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`WITH "_count_wrapper" AS (SELECT DISTINCT group_id) SELECT count(*) FROM "_count_wrapper"`))
})
})
var _ = Describe("With", func() {
It("WrapWith wraps query in CTE", func() {
q := NewQuery(nil, &SelectModel{}).
Where("cond1").
WrapWith("wrapper").
Table("wrapper").
Where("cond2")
b, err := selectQuery{q: q}.AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`WITH "wrapper" AS (SELECT "select_model"."id", "select_model"."name", "select_model"."has_one_id" FROM "select_models" AS "select_model" WHERE (cond1)) SELECT * FROM "wrapper" WHERE (cond2)`))
})
It("generates nested CTE", func() {
q1 := NewQuery(nil).Table("q1")
q2 := NewQuery(nil).With("q1", q1).Table("q2", "q1")
q3 := NewQuery(nil).With("q2", q2).Table("q3", "q2")
b, err := selectQuery{q: q3}.AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`WITH "q2" AS (WITH "q1" AS (SELECT * FROM "q1") SELECT * FROM "q2", "q1") SELECT * FROM "q3", "q2"`))
})
})
type orderTest struct {
order string
query string
}
var _ = Describe("Select Order", func() {
orderTests := []orderTest{
{"id", `"id"`},
{"id asc", `"id" asc`},
{"id desc", `"id" desc`},
{"id ASC", `"id" ASC`},
{"id DESC", `"id" DESC`},
{"id ASC NULLS FIRST", `"id" ASC NULLS FIRST`},
}
It("sets order", func() {
for _, test := range orderTests {
q := NewQuery(nil).Order(test.order)
b, err := selectQuery{q: q}.AppendQuery(nil)
Expect(err).NotTo(HaveOccurred())
Expect(string(b)).To(Equal(`SELECT * ORDER BY ` + test.query))
}
})
})

568
vendor/github.com/go-pg/pg/orm/table.go generated vendored Normal file
View file

@ -0,0 +1,568 @@
package orm
import (
"bytes"
"database/sql"
"encoding/json"
"fmt"
"net"
"reflect"
"strings"
"time"
"github.com/go-pg/pg/internal"
"github.com/go-pg/pg/types"
)
var timeType = reflect.TypeOf((*time.Time)(nil)).Elem()
var ipType = reflect.TypeOf((*net.IP)(nil)).Elem()
var ipNetType = reflect.TypeOf((*net.IPNet)(nil)).Elem()
var scannerType = reflect.TypeOf((*sql.Scanner)(nil)).Elem()
var nullBoolType = reflect.TypeOf((*sql.NullBool)(nil)).Elem()
var nullFloatType = reflect.TypeOf((*sql.NullFloat64)(nil)).Elem()
var nullIntType = reflect.TypeOf((*sql.NullInt64)(nil)).Elem()
var nullStringType = reflect.TypeOf((*sql.NullString)(nil)).Elem()
type Table struct {
Type reflect.Type
zeroStruct reflect.Value
TypeName string
Name types.Q
Alias types.Q
ModelName string
Fields []*Field
PKs []*Field
Columns []*Field
FieldsMap map[string]*Field
Methods map[string]*Method
Relations map[string]*Relation
flags uint8
}
func (t *Table) SetFlag(flag uint8) {
t.flags |= flag
}
func (t *Table) HasFlag(flag uint8) bool {
if t == nil {
return false
}
return t.flags&flag != 0
}
func (t *Table) HasField(field string) bool {
_, err := t.GetField(field)
return err == nil
}
func (t *Table) checkPKs() error {
if len(t.PKs) == 0 {
return fmt.Errorf("model=%s does not have primary keys", t.Type.Name())
}
return nil
}
func (t *Table) AddField(field *Field) {
t.Fields = append(t.Fields, field)
if field.HasFlag(PrimaryKeyFlag) {
t.PKs = append(t.PKs, field)
} else {
t.Columns = append(t.Columns, field)
}
t.FieldsMap[field.SQLName] = field
}
func (t *Table) GetField(fieldName string) (*Field, error) {
field, ok := t.FieldsMap[fieldName]
if !ok {
return nil, fmt.Errorf("can't find column=%s in table=%s", fieldName, t.Name)
}
return field, nil
}
func (t *Table) AppendParam(b []byte, strct reflect.Value, name string) ([]byte, bool) {
if field, ok := t.FieldsMap[name]; ok {
b = field.AppendValue(b, strct, 1)
return b, true
}
if method, ok := t.Methods[name]; ok {
b = method.AppendValue(b, strct.Addr(), 1)
return b, true
}
return b, false
}
func (t *Table) addRelation(rel *Relation) {
if t.Relations == nil {
t.Relations = make(map[string]*Relation)
}
t.Relations[rel.Field.GoName] = rel
}
func newTable(typ reflect.Type) *Table {
table, ok := Tables.tables[typ]
if ok {
return table
}
modelName := internal.Underscore(typ.Name())
table = &Table{
Type: typ,
zeroStruct: reflect.Zero(typ),
TypeName: internal.ToExported(typ.Name()),
Name: types.Q(types.AppendField(nil, tableNameInflector(modelName), 1)),
Alias: types.Q(types.AppendField(nil, modelName, 1)),
ModelName: modelName,
Fields: make([]*Field, 0, typ.NumField()),
FieldsMap: make(map[string]*Field, typ.NumField()),
}
Tables.tables[typ] = table
table.addFields(typ, nil)
typ = reflect.PtrTo(typ)
if typ.Implements(afterQueryHookType) {
table.SetFlag(AfterQueryHookFlag)
}
if typ.Implements(afterSelectHookType) {
table.SetFlag(AfterSelectHookFlag)
}
if typ.Implements(beforeInsertHookType) {
table.SetFlag(BeforeInsertHookFlag)
}
if typ.Implements(afterInsertHookType) {
table.SetFlag(AfterInsertHookFlag)
}
if typ.Implements(beforeUpdateHookType) {
table.SetFlag(BeforeUpdateHookFlag)
}
if typ.Implements(afterUpdateHookType) {
table.SetFlag(AfterUpdateHookFlag)
}
if typ.Implements(beforeDeleteHookType) {
table.SetFlag(BeforeDeleteHookFlag)
}
if typ.Implements(afterDeleteHookType) {
table.SetFlag(AfterDeleteHookFlag)
}
if table.Methods == nil {
table.Methods = make(map[string]*Method)
}
for i := 0; i < typ.NumMethod(); i++ {
m := typ.Method(i)
if m.PkgPath != "" {
continue
}
if m.Type.NumIn() > 1 {
continue
}
if m.Type.NumOut() != 1 {
continue
}
retType := m.Type.Out(0)
method := Method{
Index: m.Index,
appender: types.Appender(retType),
}
table.Methods[m.Name] = &method
}
return table
}
func (t *Table) addFields(typ reflect.Type, baseIndex []int) {
for i := 0; i < typ.NumField(); i++ {
f := typ.Field(i)
// Make a copy so slice is not shared between fields.
var index []int
index = append(index, baseIndex...)
if f.Anonymous {
embeddedTable := newTable(indirectType(f.Type))
pgTag := parseTag(f.Tag.Get("pg"))
if _, ok := pgTag.Options["override"]; ok {
t.TypeName = embeddedTable.TypeName
t.Name = embeddedTable.Name
t.Alias = embeddedTable.Alias
t.ModelName = embeddedTable.ModelName
}
t.addFields(embeddedTable.Type, append(index, f.Index...))
continue
}
field := t.newField(f, index)
if field != nil {
t.AddField(field)
}
}
}
func (t *Table) getField(name string) *Field {
for _, f := range t.Fields {
if f.GoName == name {
return f
}
}
f, ok := t.Type.FieldByName(name)
if !ok {
return nil
}
return t.newField(f, nil)
}
func (t *Table) newField(f reflect.StructField, index []int) *Field {
sqlTag := parseTag(f.Tag.Get("sql"))
switch f.Name {
case "tableName", "TableName":
if index != nil {
return nil
}
if sqlTag.Name != "" {
if isPostgresKeyword(sqlTag.Name) {
sqlTag.Name = `"` + sqlTag.Name + `"`
}
t.Name = types.Q(sqlTag.Name)
}
if alias, ok := sqlTag.Options["alias"]; ok {
t.Alias = types.Q(alias)
}
return nil
}
if f.PkgPath != "" {
return nil
}
skip := sqlTag.Name == "-"
if skip || sqlTag.Name == "" {
sqlTag.Name = internal.Underscore(f.Name)
}
if field, ok := t.FieldsMap[sqlTag.Name]; ok {
if field.GoName == f.Name {
return field
}
}
field := Field{
Type: indirectType(f.Type),
GoName: f.Name,
SQLName: sqlTag.Name,
Column: types.Q(types.AppendField(nil, sqlTag.Name, 1)),
Index: append(index, f.Index...),
}
if _, ok := sqlTag.Options["notnull"]; ok {
field.SetFlag(NotNullFlag)
}
if _, ok := sqlTag.Options["unique"]; ok {
field.SetFlag(UniqueFlag)
}
if v, ok := sqlTag.Options["default"]; ok {
v, ok = unquote(v)
if ok {
field.Default = types.Q(types.AppendString(nil, v, 1))
} else {
field.Default = types.Q(v)
}
}
if len(t.PKs) == 0 && (field.SQLName == "id" || field.SQLName == "uuid") {
field.SetFlag(PrimaryKeyFlag)
} else if _, ok := sqlTag.Options["pk"]; ok {
field.SetFlag(PrimaryKeyFlag)
} else if strings.HasSuffix(field.SQLName, "_id") ||
strings.HasSuffix(field.SQLName, "_uuid") {
field.SetFlag(ForeignKeyFlag)
}
pgTag := parseTag(f.Tag.Get("pg"))
if _, ok := pgTag.Options["array"]; ok {
field.SetFlag(ArrayFlag)
}
field.SQLType = fieldSQLType(&field, sqlTag)
if strings.HasSuffix(field.SQLType, "[]") {
field.SetFlag(ArrayFlag)
}
if _, ok := pgTag.Options["json_use_number"]; ok {
field.append = types.Appender(f.Type)
field.scan = scanJSONValue
} else if field.HasFlag(ArrayFlag) {
field.append = types.ArrayAppender(f.Type)
field.scan = types.ArrayScanner(f.Type)
} else if _, ok := pgTag.Options["hstore"]; ok {
field.append = types.HstoreAppender(f.Type)
field.scan = types.HstoreScanner(f.Type)
} else {
field.append = types.Appender(f.Type)
field.scan = types.Scanner(f.Type)
}
field.isZero = isZeroFunc(f.Type)
if !skip && isColumn(f.Type) {
return &field
}
switch field.Type.Kind() {
case reflect.Slice:
elemType := indirectType(field.Type.Elem())
if elemType.Kind() != reflect.Struct {
break
}
joinTable := newTable(elemType)
fk, ok := pgTag.Options["fk"]
if !ok {
fk = t.TypeName
}
if m2mTable, _ := pgTag.Options["many2many"]; m2mTable != "" {
m2mTableAlias := m2mTable
if ind := strings.IndexByte(m2mTable, '.'); ind >= 0 {
m2mTableAlias = m2mTable[ind+1:]
}
joinFK, ok := pgTag.Options["joinFK"]
if !ok {
joinFK = joinTable.TypeName
}
t.addRelation(&Relation{
Type: Many2ManyRelation,
Field: &field,
JoinTable: joinTable,
M2MTableName: types.Q(m2mTable),
M2MTableAlias: types.Q(m2mTableAlias),
BasePrefix: internal.Underscore(fk + "_"),
JoinPrefix: internal.Underscore(joinFK + "_"),
})
return nil
}
s, polymorphic := pgTag.Options["polymorphic"]
if polymorphic {
fk = s
}
fks := foreignKeys(t, joinTable, fk, t.TypeName)
if len(fks) > 0 {
t.addRelation(&Relation{
Type: HasManyRelation,
Polymorphic: polymorphic,
Field: &field,
FKs: fks,
JoinTable: joinTable,
BasePrefix: internal.Underscore(fk + "_"),
})
return nil
}
case reflect.Struct:
joinTable := newTable(field.Type)
if len(joinTable.Fields) == 0 {
break
}
for _, ff := range joinTable.FieldsMap {
ff = ff.Copy()
ff.SQLName = field.SQLName + "__" + ff.SQLName
ff.Column = types.Q(types.AppendField(nil, ff.SQLName, 1))
ff.Index = append(field.Index, ff.Index...)
if _, ok := t.FieldsMap[ff.SQLName]; !ok {
t.FieldsMap[ff.SQLName] = ff
}
}
if t.tryHasOne(joinTable, &field, pgTag) ||
t.tryBelongsToOne(joinTable, &field, pgTag) {
t.FieldsMap[field.SQLName] = &field
return nil
}
}
if skip {
t.FieldsMap[field.SQLName] = &field
return nil
}
return &field
}
func isPostgresKeyword(s string) bool {
switch s {
case "user":
return true
}
return false
}
func isColumn(typ reflect.Type) bool {
return typ.Implements(scannerType) || reflect.PtrTo(typ).Implements(scannerType)
}
func fieldSQLType(field *Field, sqlTag *tag) string {
if v, ok := sqlTag.Options["type"]; ok {
field.SetFlag(customTypeFlag)
v, _ := unquote(v)
return v
}
if field.HasFlag(ArrayFlag) {
sqlType := sqlType(field.Type.Elem())
return sqlType + "[]"
}
sqlType := sqlType(field.Type)
if field.HasFlag(PrimaryKeyFlag) {
switch sqlType {
case "smallint":
return "smallserial"
case "integer":
return "serial"
case "bigint":
return "bigserial"
}
}
return sqlType
}
func sqlType(typ reflect.Type) string {
switch typ {
case timeType:
return "timestamptz"
case ipType:
return "inet"
case ipNetType:
return "cidr"
case nullBoolType:
return "boolean"
case nullFloatType:
return "double precision"
case nullIntType:
return "bigint"
case nullStringType:
return "text"
}
switch typ.Kind() {
case reflect.Int8, reflect.Uint8, reflect.Int16:
return "smallint"
case reflect.Uint16, reflect.Int32:
return "integer"
case reflect.Uint32, reflect.Int64, reflect.Int:
return "bigint"
case reflect.Uint, reflect.Uint64:
return "decimal"
case reflect.Float32:
return "real"
case reflect.Float64:
return "double precision"
case reflect.Bool:
return "boolean"
case reflect.String:
return "text"
case reflect.Map, reflect.Struct:
return "jsonb"
case reflect.Array, reflect.Slice:
if typ.Elem().Kind() == reflect.Uint8 {
return "bytea"
}
return "jsonb"
default:
return typ.Kind().String()
}
}
func foreignKeys(base, join *Table, fk, fieldName string) []*Field {
var fks []*Field
for _, pk := range base.PKs {
fkName := fk + pk.GoName
if f := join.getField(fkName); f != nil {
fks = append(fks, f)
}
}
if len(fks) > 0 {
return fks
}
if fk != "" && fk != fieldName {
if f := join.getField(fk); f != nil {
fks = append(fks, f)
}
}
return fks
}
func (t *Table) tryHasOne(joinTable *Table, field *Field, tag *tag) bool {
fk, ok := tag.Options["fk"]
if !ok {
fk = field.GoName
}
fks := foreignKeys(joinTable, t, fk, field.GoName)
if len(fks) > 0 {
t.addRelation(&Relation{
Type: HasOneRelation,
Field: field,
FKs: fks,
JoinTable: joinTable,
})
return true
}
return false
}
func (t *Table) tryBelongsToOne(joinTable *Table, field *Field, tag *tag) bool {
fk, ok := tag.Options["fk"]
if !ok {
fk = t.TypeName
}
fks := foreignKeys(t, joinTable, fk, t.TypeName)
if len(fks) > 0 {
t.addRelation(&Relation{
Type: BelongsToRelation,
Field: field,
FKs: fks,
JoinTable: joinTable,
})
return true
}
return false
}
func scanJSONValue(v reflect.Value, b []byte) error {
if !v.CanSet() {
return fmt.Errorf("pg: Scan(non-pointer %s)", v.Type())
}
if b == nil {
v.Set(reflect.New(v.Type()).Elem())
return nil
}
dec := json.NewDecoder(bytes.NewReader(b))
dec.UseNumber()
return dec.Decode(v.Addr().Interface())
}

29
vendor/github.com/go-pg/pg/orm/table_params.go generated vendored Normal file
View file

@ -0,0 +1,29 @@
package orm
import "reflect"
type tableParams struct {
table *Table
strct reflect.Value
}
func newTableParams(strct interface{}) (*tableParams, bool) {
v := reflect.ValueOf(strct)
if !v.IsValid() {
return nil, false
}
v = reflect.Indirect(v)
if v.Kind() != reflect.Struct {
return nil, false
}
return &tableParams{
table: Tables.Get(v.Type()),
strct: v,
}, true
}
func (m tableParams) AppendParam(b []byte, f QueryFormatter, name string) ([]byte, bool) {
return m.table.AppendParam(b, m.strct, name)
}

168
vendor/github.com/go-pg/pg/orm/table_test.go generated vendored Normal file
View file

@ -0,0 +1,168 @@
package orm_test
import (
"reflect"
"github.com/go-pg/pg/orm"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
type A struct {
Id int
}
func (A) Method() int {
return 10
}
type B struct {
A
}
var _ = Describe("embedded Model", func() {
var strct reflect.Value
var table *orm.Table
BeforeEach(func() {
strct = reflect.ValueOf(B{A: A{Id: 1}})
table = orm.Tables.Get(strct.Type())
})
It("has fields", func() {
Expect(table.Fields).To(HaveLen(1))
Expect(table.FieldsMap).To(HaveLen(1))
id, ok := table.FieldsMap["id"]
Expect(ok).To(BeTrue())
Expect(id.GoName).To(Equal("Id"))
Expect(id.SQLName).To(Equal("id"))
Expect(string(id.Column)).To(Equal(`"id"`))
Expect(id.HasFlag(orm.PrimaryKeyFlag)).To(BeTrue())
Expect(string(id.AppendValue(nil, strct, 1))).To(Equal("1"))
Expect(table.PKs).To(HaveLen(1))
Expect(table.PKs[0]).To(Equal(id))
})
It("has methods", func() {
Expect(table.Methods).To(HaveLen(1))
m, ok := table.Methods["Method"]
Expect(ok).To(BeTrue())
Expect(m.Index).To(Equal(0))
Expect(string(m.AppendValue(nil, strct, 1))).To(Equal("10"))
})
})
type C struct {
Name int `sql:",pk"`
Id int
UUID int
}
var _ = Describe("primary key annotation", func() {
var table *orm.Table
BeforeEach(func() {
strct := reflect.ValueOf(C{})
table = orm.Tables.Get(strct.Type())
})
It("has precedence over auto-detection", func() {
Expect(table.PKs).To(HaveLen(1))
Expect(table.PKs[0].GoName).To(Equal("Name"))
})
})
type D struct {
UUID int
}
var _ = Describe("uuid field", func() {
var table *orm.Table
BeforeEach(func() {
strct := reflect.ValueOf(D{})
table = orm.Tables.Get(strct.Type())
})
It("is detected as primary key", func() {
Expect(table.PKs).To(HaveLen(1))
Expect(table.PKs[0].GoName).To(Equal("UUID"))
})
})
type E struct {
Id int
StructField struct {
Foo string
Bar string
}
}
var _ = Describe("struct field", func() {
var table *orm.Table
BeforeEach(func() {
strct := reflect.ValueOf(E{})
table = orm.Tables.Get(strct.Type())
})
It("is present in the list", func() {
Expect(table.Fields).To(HaveLen(2))
_, ok := table.FieldsMap["struct_field"]
Expect(ok).To(BeTrue())
})
})
type f struct {
Id int
G *g
}
type g struct {
Id int
FId int
F *f
}
var _ = Describe("unexported types", func() {
It("work with belongs to relation", func() {
strct := reflect.ValueOf(f{})
table := orm.Tables.Get(strct.Type())
rel, ok := table.Relations["G"]
Expect(ok).To(BeTrue())
Expect(rel.Type).To(Equal(orm.BelongsToRelation))
})
It("work with has one relation", func() {
strct := reflect.ValueOf(g{})
table := orm.Tables.Get(strct.Type())
rel, ok := table.Relations["F"]
Expect(ok).To(BeTrue())
Expect(rel.Type).To(Equal(orm.HasOneRelation))
})
})
type H struct {
I *I
}
type I struct {
H *H
}
var _ = Describe("model with circular reference", func() {
It("works", func() {
table := orm.Tables.Get(reflect.TypeOf(H{}))
Expect(table).NotTo(BeNil())
table = orm.Tables.Get(reflect.TypeOf(I{}))
Expect(table).NotTo(BeNil())
})
})

39
vendor/github.com/go-pg/pg/orm/tables.go generated vendored Normal file
View file

@ -0,0 +1,39 @@
package orm
import (
"fmt"
"reflect"
"sync"
)
var Tables = newTables()
type tables struct {
mu sync.RWMutex
tables map[reflect.Type]*Table
}
func newTables() *tables {
return &tables{
tables: make(map[reflect.Type]*Table),
}
}
func (t *tables) Get(typ reflect.Type) *Table {
if typ.Kind() != reflect.Struct {
panic(fmt.Errorf("got %s, wanted %s", typ.Kind(), reflect.Struct))
}
t.mu.RLock()
table, ok := t.tables[typ]
t.mu.RUnlock()
if ok {
return table
}
t.mu.Lock()
table = newTable(typ)
t.mu.Unlock()
return table
}

Some files were not shown because too many files have changed in this diff Show more