vendor dependencies with dep
This commit is contained in:
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
22
vendor/github.com/go-pg/migrations/.travis.yml
generated
vendored
Normal 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
3
vendor/github.com/go-pg/migrations/Makefile
generated
vendored
Normal 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
5
vendor/github.com/go-pg/migrations/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# SQL migrations for Golang and PostgreSQL
|
||||
|
||||
[](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
79
vendor/github.com/go-pg/migrations/db.go
generated
vendored
Normal 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
161
vendor/github.com/go-pg/migrations/db_test.go
generated
vendored
Normal 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")
|
||||
}
|
||||
19
vendor/github.com/go-pg/migrations/example/1_initial.go
generated
vendored
Normal file
19
vendor/github.com/go-pg/migrations/example/1_initial.go
generated
vendored
Normal 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
19
vendor/github.com/go-pg/migrations/example/2_add_id.go
generated
vendored
Normal 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
|
||||
})
|
||||
}
|
||||
19
vendor/github.com/go-pg/migrations/example/3_seed_data.go
generated
vendored
Normal file
19
vendor/github.com/go-pg/migrations/example/3_seed_data.go
generated
vendored
Normal 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
64
vendor/github.com/go-pg/migrations/example/README.md
generated
vendored
Normal 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
57
vendor/github.com/go-pg/migrations/example/main.go
generated
vendored
Normal 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
5
vendor/github.com/go-pg/migrations/export_test.go
generated
vendored
Normal 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
264
vendor/github.com/go-pg/migrations/migrations.go
generated
vendored
Normal 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
24
vendor/github.com/go-pg/pg/.travis.yml
generated
vendored
Normal 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
33
vendor/github.com/go-pg/pg/CHANGELOG.md
generated
vendored
Normal 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
24
vendor/github.com/go-pg/pg/LICENSE
generated
vendored
Normal 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
4
vendor/github.com/go-pg/pg/Makefile
generated
vendored
Normal 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
155
vendor/github.com/go-pg/pg/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
# PostgreSQL client and ORM for Golang
|
||||
|
||||
[](https://travis-ci.org/go-pg/pg)
|
||||
[](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
548
vendor/github.com/go-pg/pg/bench_test.go
generated
vendored
Normal 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
444
vendor/github.com/go-pg/pg/conv_test.go
generated
vendored
Normal 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
428
vendor/github.com/go-pg/pg/db.go
generated
vendored
Normal 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
39
vendor/github.com/go-pg/pg/db_context.go
generated
vendored
Normal 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
19
vendor/github.com/go-pg/pg/db_no_context.go
generated
vendored
Normal 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
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
4
vendor/github.com/go-pg/pg/doc.go
generated
vendored
Normal 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
44
vendor/github.com/go-pg/pg/error.go
generated
vendored
Normal 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
49
vendor/github.com/go-pg/pg/example_array_test.go
generated
vendored
Normal 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
47
vendor/github.com/go-pg/pg/example_hstore_test.go
generated
vendored
Normal 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
971
vendor/github.com/go-pg/pg/example_model_test.go
generated
vendored
Normal 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
|
||||
}
|
||||
80
vendor/github.com/go-pg/pg/example_placeholders_test.go
generated
vendored
Normal file
80
vendor/github.com/go-pg/pg/example_placeholders_test.go
generated
vendored
Normal 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
284
vendor/github.com/go-pg/pg/example_test.go
generated
vendored
Normal 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
109
vendor/github.com/go-pg/pg/exampledb_model_test.go
generated
vendored
Normal 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
109
vendor/github.com/go-pg/pg/exampledb_query_test.go
generated
vendored
Normal 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
11
vendor/github.com/go-pg/pg/export_test.go
generated
vendored
Normal 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
142
vendor/github.com/go-pg/pg/hook.go
generated
vendored
Normal 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
236
vendor/github.com/go-pg/pg/hook_test.go
generated
vendored
Normal 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
62
vendor/github.com/go-pg/pg/internal/error.go
generated
vendored
Normal 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
24
vendor/github.com/go-pg/pg/internal/internal.go
generated
vendored
Normal 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
15
vendor/github.com/go-pg/pg/internal/log.go
generated
vendored
Normal 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...))
|
||||
}
|
||||
80
vendor/github.com/go-pg/pg/internal/parser/array_parser.go
generated
vendored
Normal file
80
vendor/github.com/go-pg/pg/internal/parser/array_parser.go
generated
vendored
Normal 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
|
||||
}
|
||||
55
vendor/github.com/go-pg/pg/internal/parser/array_parser_test.go
generated
vendored
Normal file
55
vendor/github.com/go-pg/pg/internal/parser/array_parser_test.go
generated
vendored
Normal 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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
40
vendor/github.com/go-pg/pg/internal/parser/hstore_parser.go
generated
vendored
Normal file
40
vendor/github.com/go-pg/pg/internal/parser/hstore_parser.go
generated
vendored
Normal 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
|
||||
}
|
||||
57
vendor/github.com/go-pg/pg/internal/parser/hstore_parser_test.go
generated
vendored
Normal file
57
vendor/github.com/go-pg/pg/internal/parser/hstore_parser_test.go
generated
vendored
Normal 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
153
vendor/github.com/go-pg/pg/internal/parser/parser.go
generated
vendored
Normal 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
15
vendor/github.com/go-pg/pg/internal/parser/util.go
generated
vendored
Normal 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
80
vendor/github.com/go-pg/pg/internal/pool/bench_test.go
generated
vendored
Normal 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
106
vendor/github.com/go-pg/pg/internal/pool/conn.go
generated
vendored
Normal 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
35
vendor/github.com/go-pg/pg/internal/pool/main_test.go
generated
vendored
Normal 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
377
vendor/github.com/go-pg/pg/internal/pool/pool.go
generated
vendored
Normal 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))
|
||||
}
|
||||
}
|
||||
55
vendor/github.com/go-pg/pg/internal/pool/pool_single.go
generated
vendored
Normal file
55
vendor/github.com/go-pg/pg/internal/pool/pool_single.go
generated
vendored
Normal 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
255
vendor/github.com/go-pg/pg/internal/pool/pool_test.go
generated
vendored
Normal 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())
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
90
vendor/github.com/go-pg/pg/internal/pool/write_buffer.go
generated
vendored
Normal file
90
vendor/github.com/go-pg/pg/internal/pool/write_buffer.go
generated
vendored
Normal 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
11
vendor/github.com/go-pg/pg/internal/safe.go
generated
vendored
Normal 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
73
vendor/github.com/go-pg/pg/internal/underscore.go
generated
vendored
Normal 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
23
vendor/github.com/go-pg/pg/internal/underscore_test.go
generated
vendored
Normal 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
27
vendor/github.com/go-pg/pg/internal/unsafe.go
generated
vendored
Normal 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
21
vendor/github.com/go-pg/pg/internal/util.go
generated
vendored
Normal 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
221
vendor/github.com/go-pg/pg/listener.go
generated
vendored
Normal 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
189
vendor/github.com/go-pg/pg/listener_test.go
generated
vendored
Normal 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
180
vendor/github.com/go-pg/pg/loader_test.go
generated
vendored
Normal 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
230
vendor/github.com/go-pg/pg/main_test.go
generated
vendored
Normal 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
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
225
vendor/github.com/go-pg/pg/options.go
generated
vendored
Normal 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
177
vendor/github.com/go-pg/pg/options_test.go
generated
vendored
Normal 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
120
vendor/github.com/go-pg/pg/orm/count_estimate.go
generated
vendored
Normal 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
122
vendor/github.com/go-pg/pg/orm/create_table.go
generated
vendored
Normal 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
70
vendor/github.com/go-pg/pg/orm/create_table_test.go
generated
vendored
Normal 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
61
vendor/github.com/go-pg/pg/orm/delete.go
generated
vendored
Normal 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
22
vendor/github.com/go-pg/pg/orm/delete_test.go
generated
vendored
Normal 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
45
vendor/github.com/go-pg/pg/orm/drop_table.go
generated
vendored
Normal 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
29
vendor/github.com/go-pg/pg/orm/drop_table_test.go
generated
vendored
Normal 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
95
vendor/github.com/go-pg/pg/orm/field.go
generated
vendored
Normal 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 ©
|
||||
}
|
||||
|
||||
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
281
vendor/github.com/go-pg/pg/orm/format.go
generated
vendored
Normal 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
203
vendor/github.com/go-pg/pg/orm/format_test.go
generated
vendored
Normal 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", ¶m)
|
||||
}
|
||||
}
|
||||
180
vendor/github.com/go-pg/pg/orm/hook.go
generated
vendored
Normal file
180
vendor/github.com/go-pg/pg/orm/hook.go
generated
vendored
Normal 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
17
vendor/github.com/go-pg/pg/orm/inflection.go
generated
vendored
Normal 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
139
vendor/github.com/go-pg/pg/orm/insert.go
generated
vendored
Normal 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
128
vendor/github.com/go-pg/pg/orm/insert_test.go
generated
vendored
Normal 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
278
vendor/github.com/go-pg/pg/orm/join.go
generated
vendored
Normal 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
51
vendor/github.com/go-pg/pg/orm/join_test.go
generated
vendored
Normal 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
13
vendor/github.com/go-pg/pg/orm/main_test.go
generated
vendored
Normal 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
88
vendor/github.com/go-pg/pg/orm/model.go
generated
vendored
Normal 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
19
vendor/github.com/go-pg/pg/orm/model_discard.go
generated
vendored
Normal 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
42
vendor/github.com/go-pg/pg/orm/model_scan.go
generated
vendored
Normal 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
35
vendor/github.com/go-pg/pg/orm/model_slice.go
generated
vendored
Normal 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
107
vendor/github.com/go-pg/pg/orm/model_table.go
generated
vendored
Normal 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
120
vendor/github.com/go-pg/pg/orm/model_table_m2m.go
generated
vendored
Normal 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
105
vendor/github.com/go-pg/pg/orm/model_table_many.go
generated
vendored
Normal 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
128
vendor/github.com/go-pg/pg/orm/model_table_slice.go
generated
vendored
Normal 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
314
vendor/github.com/go-pg/pg/orm/model_table_struct.go
generated
vendored
Normal 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
41
vendor/github.com/go-pg/pg/orm/orm.go
generated
vendored
Normal 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
986
vendor/github.com/go-pg/pg/orm/query.go
generated
vendored
Normal 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
61
vendor/github.com/go-pg/pg/orm/query_test.go
generated
vendored
Normal 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
23
vendor/github.com/go-pg/pg/orm/relation.go
generated
vendored
Normal 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
14
vendor/github.com/go-pg/pg/orm/result.go
generated
vendored
Normal 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
188
vendor/github.com/go-pg/pg/orm/select.go
generated
vendored
Normal 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
242
vendor/github.com/go-pg/pg/orm/select_test.go
generated
vendored
Normal 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
568
vendor/github.com/go-pg/pg/orm/table.go
generated
vendored
Normal 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
29
vendor/github.com/go-pg/pg/orm/table_params.go
generated
vendored
Normal 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
168
vendor/github.com/go-pg/pg/orm/table_test.go
generated
vendored
Normal 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
39
vendor/github.com/go-pg/pg/orm/tables.go
generated
vendored
Normal 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
Loading…
Add table
Add a link
Reference in a new issue