vendor dependencies with dep
This commit is contained in:
parent
93d8310491
commit
1384296a47
2712 changed files with 965742 additions and 0 deletions
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
|
||||
}
|
||||
139
vendor/github.com/go-pg/pg/orm/tag.go
generated
vendored
Normal file
139
vendor/github.com/go-pg/pg/orm/tag.go
generated
vendored
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
package orm
|
||||
|
||||
import (
|
||||
"github.com/go-pg/pg/internal/parser"
|
||||
)
|
||||
|
||||
type tag struct {
|
||||
Name string
|
||||
Options map[string]string
|
||||
}
|
||||
|
||||
func parseTag(s string) *tag {
|
||||
p := &tagParser{
|
||||
Parser: parser.NewString(s),
|
||||
}
|
||||
p.parseKey()
|
||||
return &p.tag
|
||||
}
|
||||
|
||||
type tagParser struct {
|
||||
*parser.Parser
|
||||
|
||||
tag tag
|
||||
key string
|
||||
}
|
||||
|
||||
func (p *tagParser) setTagOption(key, value string) {
|
||||
if p.tag.Options == nil {
|
||||
p.tag.Options = make(map[string]string)
|
||||
if value == "" && p.tag.Name == "" {
|
||||
p.tag.Name = key
|
||||
return
|
||||
}
|
||||
}
|
||||
if key != "" {
|
||||
p.tag.Options[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
func (p *tagParser) parseKey() {
|
||||
var b []byte
|
||||
for p.Valid() {
|
||||
c := p.Read()
|
||||
switch c {
|
||||
case ',':
|
||||
p.Skip(' ')
|
||||
p.setTagOption(string(b), "")
|
||||
p.parseKey()
|
||||
return
|
||||
case ':':
|
||||
p.key = string(b)
|
||||
p.parseValue()
|
||||
return
|
||||
default:
|
||||
b = append(b, c)
|
||||
}
|
||||
}
|
||||
if len(b) > 0 {
|
||||
p.setTagOption(string(b), "")
|
||||
}
|
||||
}
|
||||
|
||||
func (p *tagParser) parseValue() {
|
||||
const quote = '\''
|
||||
|
||||
c := p.Peek()
|
||||
if c == quote {
|
||||
p.parseQuotedValue()
|
||||
return
|
||||
}
|
||||
|
||||
var b []byte
|
||||
for p.Valid() {
|
||||
c = p.Read()
|
||||
switch c {
|
||||
case '\\':
|
||||
c = p.Read()
|
||||
b = append(b, c)
|
||||
case ',':
|
||||
p.Skip(' ')
|
||||
p.setTagOption(p.key, string(b))
|
||||
p.parseKey()
|
||||
return
|
||||
default:
|
||||
b = append(b, c)
|
||||
}
|
||||
}
|
||||
if len(b) > 0 {
|
||||
p.setTagOption(p.key, string(b))
|
||||
}
|
||||
}
|
||||
|
||||
func (p *tagParser) parseQuotedValue() {
|
||||
const quote = '\''
|
||||
|
||||
if !p.Skip(quote) {
|
||||
panic("not reached")
|
||||
}
|
||||
|
||||
var b []byte
|
||||
b = append(b, quote)
|
||||
|
||||
for p.Valid() {
|
||||
bb, ok := p.ReadSep(quote)
|
||||
if !ok {
|
||||
b = append(b, bb...)
|
||||
break
|
||||
}
|
||||
|
||||
if len(bb) > 0 && bb[len(bb)-1] == '\\' {
|
||||
b = append(b, bb[:len(bb)-1]...)
|
||||
b = append(b, quote)
|
||||
continue
|
||||
}
|
||||
|
||||
b = append(b, bb...)
|
||||
b = append(b, quote)
|
||||
|
||||
p.setTagOption(p.key, string(b))
|
||||
p.parseKey()
|
||||
return
|
||||
}
|
||||
|
||||
if len(b) > 0 {
|
||||
p.setTagOption(p.key, string(b))
|
||||
}
|
||||
}
|
||||
|
||||
func unquote(s string) (string, bool) {
|
||||
const quote = '\''
|
||||
|
||||
if len(s) < 2 {
|
||||
return s, false
|
||||
}
|
||||
if s[0] == quote && s[len(s)-1] == quote {
|
||||
return s[1 : len(s)-1], true
|
||||
}
|
||||
return s, false
|
||||
}
|
||||
42
vendor/github.com/go-pg/pg/orm/tag_test.go
generated
vendored
Normal file
42
vendor/github.com/go-pg/pg/orm/tag_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
package orm
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var tagTests = []struct {
|
||||
tag string
|
||||
name string
|
||||
opts map[string]string
|
||||
}{
|
||||
{"", "", nil},
|
||||
|
||||
{"hello", "hello", nil},
|
||||
{",hello", "", map[string]string{"hello": ""}},
|
||||
{"hello:world", "", map[string]string{"hello": "world"}},
|
||||
{"hello:world,foo:bar", "", map[string]string{"hello": "world", "foo": "bar"}},
|
||||
{"hello:'world1,world2'", "", map[string]string{"hello": "'world1,world2'"}},
|
||||
{`hello:'D\'Angelo', foo:bar`, "", map[string]string{"hello": "'D'Angelo'", "foo": "bar"}},
|
||||
}
|
||||
|
||||
func TestTagParser(t *testing.T) {
|
||||
for _, test := range tagTests {
|
||||
tag := parseTag(test.tag)
|
||||
if tag.Name != test.name {
|
||||
t.Fatalf("got %q, wanted %q (tag=%q)", tag.Name, test.name, test.tag)
|
||||
}
|
||||
|
||||
if len(tag.Options) != len(test.opts) {
|
||||
t.Fatalf(
|
||||
"got %#v options, wanted %#v (tag=%q)",
|
||||
tag.Options, test.opts, test.tag,
|
||||
)
|
||||
}
|
||||
|
||||
for k, v := range test.opts {
|
||||
if tag.Options[k] != v {
|
||||
t.Fatalf("got %s=%q, wanted %q (tag=%q)", k, tag.Options[k], v, test.tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
206
vendor/github.com/go-pg/pg/orm/update.go
generated
vendored
Normal file
206
vendor/github.com/go-pg/pg/orm/update.go
generated
vendored
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
package orm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
|
||||
"github.com/go-pg/pg/internal"
|
||||
)
|
||||
|
||||
func Update(db DB, model ...interface{}) error {
|
||||
res, err := NewQuery(db, model...).Update()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return internal.AssertOneRow(res.RowsAffected())
|
||||
}
|
||||
|
||||
type updateQuery struct {
|
||||
q *Query
|
||||
}
|
||||
|
||||
var _ QueryAppender = (*updateQuery)(nil)
|
||||
|
||||
func (q updateQuery) Copy() QueryAppender {
|
||||
return updateQuery{
|
||||
q: q.q.Copy(),
|
||||
}
|
||||
}
|
||||
|
||||
func (q updateQuery) Query() *Query {
|
||||
return q.q
|
||||
}
|
||||
|
||||
func (q updateQuery) 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, "UPDATE "...)
|
||||
b = q.q.appendFirstTableWithAlias(b)
|
||||
|
||||
b, err = q.mustAppendSet(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if q.q.hasOtherTables() || q.q.modelHasData() {
|
||||
b = append(b, " FROM "...)
|
||||
b = q.q.appendOtherTables(b)
|
||||
b, err = q.appendModelData(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (q updateQuery) mustAppendSet(b []byte) ([]byte, error) {
|
||||
if len(q.q.set) > 0 {
|
||||
b = q.q.appendSet(b)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
if q.q.model == nil {
|
||||
return nil, errors.New("pg: Model is nil")
|
||||
}
|
||||
|
||||
b = append(b, " SET "...)
|
||||
|
||||
value := q.q.model.Value()
|
||||
var err error
|
||||
if value.Kind() == reflect.Struct {
|
||||
b, err = q.appendSetStruct(b, value)
|
||||
} else {
|
||||
b, err = q.appendSetSlice(b, value)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (q updateQuery) appendSetStruct(b []byte, strct reflect.Value) ([]byte, error) {
|
||||
fields, err := q.q.getFields()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(fields) == 0 {
|
||||
fields = q.q.model.Table().Columns
|
||||
}
|
||||
|
||||
for i, f := range fields {
|
||||
if i > 0 {
|
||||
b = append(b, ", "...)
|
||||
}
|
||||
|
||||
b = append(b, f.Column...)
|
||||
b = append(b, " = "...)
|
||||
b = f.AppendValue(b, strct, 1)
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (q updateQuery) appendSetSlice(b []byte, slice reflect.Value) ([]byte, error) {
|
||||
fields, err := q.q.getFields()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(fields) == 0 {
|
||||
fields = q.q.model.Table().Columns
|
||||
}
|
||||
|
||||
for i, f := range fields {
|
||||
if i > 0 {
|
||||
b = append(b, ", "...)
|
||||
}
|
||||
|
||||
b = append(b, f.Column...)
|
||||
b = append(b, " = "...)
|
||||
b = append(b, "_data."...)
|
||||
b = append(b, f.Column...)
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (q updateQuery) appendModelData(b []byte) ([]byte, error) {
|
||||
if !q.q.hasModel() {
|
||||
return b, nil
|
||||
}
|
||||
|
||||
v := q.q.model.Value()
|
||||
if v.Kind() != reflect.Slice || v.Len() == 0 {
|
||||
return b, nil
|
||||
}
|
||||
|
||||
columns, err := q.q.getDataFields()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(columns) > 0 {
|
||||
columns = append(columns, q.q.model.Table().PKs...)
|
||||
} else {
|
||||
columns = q.q.model.Table().Fields
|
||||
}
|
||||
|
||||
return appendSliceValues(b, columns, v), nil
|
||||
}
|
||||
|
||||
func appendSliceValues(b []byte, fields []*Field, slice reflect.Value) []byte {
|
||||
b = append(b, "(VALUES ("...)
|
||||
for i := 0; i < slice.Len(); i++ {
|
||||
el := slice.Index(i)
|
||||
if el.Kind() == reflect.Interface {
|
||||
el = el.Elem()
|
||||
}
|
||||
b = appendValues(b, fields, reflect.Indirect(el))
|
||||
if i != slice.Len()-1 {
|
||||
b = append(b, "), ("...)
|
||||
}
|
||||
}
|
||||
b = append(b, ")) AS _data("...)
|
||||
b = appendColumns(b, fields)
|
||||
b = append(b, ")"...)
|
||||
return b
|
||||
}
|
||||
|
||||
func 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, "NULL"...)
|
||||
} else {
|
||||
b = f.AppendValue(b, v, 1)
|
||||
}
|
||||
if f.HasFlag(customTypeFlag) {
|
||||
b = append(b, "::"...)
|
||||
b = append(b, f.SQLType...)
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
39
vendor/github.com/go-pg/pg/orm/update_test.go
generated
vendored
Normal file
39
vendor/github.com/go-pg/pg/orm/update_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package orm
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
type UpdateTest struct {
|
||||
Id int
|
||||
Value string `sql:"type:mytype"`
|
||||
}
|
||||
|
||||
var _ = Describe("Update", func() {
|
||||
It("bulk updates", func() {
|
||||
q := NewQuery(nil, &UpdateTest{}).
|
||||
Model(&UpdateTest{
|
||||
Id: 1,
|
||||
Value: "hello",
|
||||
}, &UpdateTest{
|
||||
Id: 2,
|
||||
})
|
||||
|
||||
b, err := updateQuery{q}.AppendQuery(nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(string(b)).To(Equal(`UPDATE "update_tests" AS "update_test" SET "value" = _data."value" FROM (VALUES (1, 'hello'::mytype), (2, NULL::mytype)) AS _data("id", "value") WHERE "update_test"."id" = _data."id"`))
|
||||
})
|
||||
|
||||
It("supports WITH", func() {
|
||||
q := NewQuery(nil, &UpdateTest{}).
|
||||
WrapWith("wrapper").
|
||||
Model(&UpdateTest{}).
|
||||
Table("wrapper").
|
||||
Where("update_test.id = wrapper.id")
|
||||
|
||||
b, err := updateQuery{q}.AppendQuery(nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(string(b)).To(Equal(`WITH "wrapper" AS (SELECT "update_test"."id", "update_test"."value" FROM "update_tests" AS "update_test") UPDATE "update_tests" AS "update_test" SET "value" = NULL FROM "wrapper" WHERE (update_test.id = wrapper.id)`))
|
||||
})
|
||||
})
|
||||
185
vendor/github.com/go-pg/pg/orm/url_values.go
generated
vendored
Normal file
185
vendor/github.com/go-pg/pg/orm/url_values.go
generated
vendored
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
package orm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/go-pg/pg/types"
|
||||
)
|
||||
|
||||
// URLFilters is used with Query.Apply to add WHERE clauses from the URL values:
|
||||
// - ?foo=bar - Where(`"foo" = 'bar'`)
|
||||
// - ?foo=hello&foo=world - Where(`"foo" IN ('hello','world')`)
|
||||
// - ?foo__exclude=bar - Where(`"foo" != 'bar'`)
|
||||
// - ?foo__ieq=bar - Where(`"foo" ILIKE 'bar'`)
|
||||
// - ?foo__match=bar - Where(`"foo" SIMILAR TO 'bar'`)
|
||||
// - ?foo__gt=42 - Where(`"foo" > 42`)
|
||||
// - ?foo__gte=42 - Where(`"foo" >= 42`)
|
||||
// - ?foo__lt=42 - Where(`"foo" < 42`)
|
||||
// - ?foo__lte=42 - Where(`"foo" <= 42`)
|
||||
func URLFilters(urlValues url.Values) func(*Query) (*Query, error) {
|
||||
return func(q *Query) (*Query, error) {
|
||||
for fieldName, values := range urlValues {
|
||||
var operation string
|
||||
if i := strings.Index(fieldName, "__"); i != -1 {
|
||||
fieldName, operation = fieldName[:i], fieldName[i+2:]
|
||||
}
|
||||
|
||||
if q.model.Table().HasField(fieldName) {
|
||||
q = addOperator(q, fieldName, operation, values)
|
||||
}
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
}
|
||||
|
||||
func addOperator(q *Query, fieldName, operator string, values []string) *Query {
|
||||
switch operator {
|
||||
case "gt":
|
||||
q = forEachValue(q, fieldName, values, "? > ?")
|
||||
case "gte":
|
||||
q = forEachValue(q, fieldName, values, "? >= ?")
|
||||
case "lt":
|
||||
q = forEachValue(q, fieldName, values, "? < ?")
|
||||
case "lte":
|
||||
q = forEachValue(q, fieldName, values, "? <= ?")
|
||||
case "ieq":
|
||||
q = forEachValue(q, fieldName, values, "? ILIKE ?")
|
||||
case "match":
|
||||
q = forEachValue(q, fieldName, values, "? SIMILAR TO ?")
|
||||
case "exclude":
|
||||
q = forAllValues(q, fieldName, values, "? != ?", "? NOT IN (?)")
|
||||
case "", "include":
|
||||
q = forAllValues(q, fieldName, values, "? = ?", "? IN (?)")
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
func forEachValue(q *Query, fieldName string, values []string, queryTemplate string) *Query {
|
||||
for _, value := range values {
|
||||
q = q.Where(queryTemplate, types.F(fieldName), value)
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
func forAllValues(q *Query, fieldName string, values []string, queryTemplate, queryArrayTemplate string) *Query {
|
||||
if len(values) > 1 {
|
||||
q = q.Where(queryArrayTemplate, types.F(fieldName), types.In(values))
|
||||
} else {
|
||||
q = q.Where(queryTemplate, types.F(fieldName), values[0])
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
type Pager struct {
|
||||
Limit int
|
||||
Offset int
|
||||
|
||||
// Default max limit is 1000.
|
||||
MaxLimit int
|
||||
// Default max offset is 1000000.
|
||||
MaxOffset int
|
||||
|
||||
stickyErr error
|
||||
}
|
||||
|
||||
func NewPager(values url.Values) *Pager {
|
||||
p := &Pager{}
|
||||
p.SetURLValues(values)
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *Pager) SetURLValues(values url.Values) {
|
||||
limit, err := intParam(values, "limit")
|
||||
if err != nil {
|
||||
p.stickyErr = err
|
||||
return
|
||||
}
|
||||
p.Limit = limit
|
||||
|
||||
page, err := intParam(values, "page")
|
||||
if err != nil {
|
||||
p.stickyErr = err
|
||||
return
|
||||
}
|
||||
if page > 0 {
|
||||
p.SetPage(page)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Pager) maxLimit() int {
|
||||
if p.MaxLimit > 0 {
|
||||
return p.MaxLimit
|
||||
}
|
||||
return 1000
|
||||
}
|
||||
|
||||
func (p *Pager) maxOffset() int {
|
||||
if p.MaxOffset > 0 {
|
||||
return p.MaxOffset
|
||||
}
|
||||
return 1000000
|
||||
}
|
||||
|
||||
func (p *Pager) GetLimit() int {
|
||||
const defaultLimit = 100
|
||||
|
||||
if p.Limit <= 0 {
|
||||
return defaultLimit
|
||||
}
|
||||
if p.Limit > p.maxLimit() {
|
||||
return p.maxLimit()
|
||||
}
|
||||
return p.Limit
|
||||
}
|
||||
|
||||
func (p *Pager) GetOffset() int {
|
||||
if p.Offset > p.maxOffset() {
|
||||
return p.maxOffset()
|
||||
}
|
||||
if p.Offset > 0 {
|
||||
return p.Offset
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (p *Pager) SetPage(page int) {
|
||||
p.Offset = (page - 1) * p.GetLimit()
|
||||
}
|
||||
|
||||
func (p *Pager) GetPage() int {
|
||||
return (p.GetOffset() / p.GetLimit()) + 1
|
||||
}
|
||||
|
||||
func (p *Pager) Paginate(q *Query) (*Query, error) {
|
||||
if p.stickyErr != nil {
|
||||
return nil, p.stickyErr
|
||||
}
|
||||
|
||||
q = q.Limit(p.GetLimit()).
|
||||
Offset(p.GetOffset())
|
||||
return q, nil
|
||||
}
|
||||
|
||||
// Pagination is used with Query.Apply to set LIMIT and OFFSET from the URL values:
|
||||
// - ?limit=10 - sets q.Limit(10), max limit is 1000.
|
||||
// - ?page=5 - sets q.Offset((page - 1) * limit), max offset is 1000000.
|
||||
func Pagination(values url.Values) func(*Query) (*Query, error) {
|
||||
return NewPager(values).Paginate
|
||||
}
|
||||
|
||||
func intParam(urlValues url.Values, paramName string) (int, error) {
|
||||
values, ok := urlValues[paramName]
|
||||
if !ok {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
value, err := strconv.Atoi(values[0])
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("param=%s value=%s is invalid: %s", paramName, values[0], err)
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
117
vendor/github.com/go-pg/pg/orm/url_values_test.go
generated
vendored
Normal file
117
vendor/github.com/go-pg/pg/orm/url_values_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
package orm
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
type URLValuesModel struct {
|
||||
Id int
|
||||
Name string
|
||||
}
|
||||
|
||||
type urlValuesTest struct {
|
||||
url string
|
||||
query string
|
||||
}
|
||||
|
||||
var _ = Describe("URLValues", func() {
|
||||
query := `SELECT "url_values_model"."id", "url_values_model"."name" FROM "url_values_models" AS "url_values_model"`
|
||||
urlValuesTests := []urlValuesTest{
|
||||
{
|
||||
url: "http://localhost:8000/test?id__gt=1",
|
||||
query: query + ` WHERE ("id" > '1')`,
|
||||
},
|
||||
{
|
||||
url: "http://localhost:8000/test?name__gte=Michael",
|
||||
query: query + ` WHERE ("name" >= 'Michael')`,
|
||||
},
|
||||
{
|
||||
url: "http://localhost:8000/test?id__lt=10",
|
||||
query: query + ` WHERE ("id" < '10')`,
|
||||
},
|
||||
{
|
||||
url: "http://localhost:8000/test?name__lte=Peter",
|
||||
query: query + ` WHERE ("name" <= 'Peter')`,
|
||||
},
|
||||
{
|
||||
url: "http://localhost:8000/test?name__exclude=Peter",
|
||||
query: query + ` WHERE ("name" != 'Peter')`,
|
||||
},
|
||||
{
|
||||
url: "http://localhost:8000/test?name__exclude=Mike&name__exclude=Peter",
|
||||
query: query + ` WHERE ("name" NOT IN ('Mike','Peter'))`,
|
||||
},
|
||||
{
|
||||
url: "http://localhost:8000/test?name=Mike",
|
||||
query: query + ` WHERE ("name" = 'Mike')`,
|
||||
},
|
||||
{
|
||||
url: "http://localhost:8000/test?name__ieq=mik_",
|
||||
query: query + ` WHERE ("name" ILIKE 'mik_')`,
|
||||
},
|
||||
{
|
||||
url: "http://localhost:8000/test?name__match=(m|p).*",
|
||||
query: query + ` WHERE ("name" SIMILAR TO '(m|p).*')`,
|
||||
},
|
||||
{
|
||||
url: "http://localhost:8000/test?name__include=Peter&name__include=Mike",
|
||||
query: query + ` WHERE ("name" IN ('Peter','Mike'))`,
|
||||
},
|
||||
{
|
||||
url: "http://localhost:8000/test?name=Mike&name=Peter",
|
||||
query: query + ` WHERE ("name" IN ('Mike','Peter'))`,
|
||||
},
|
||||
{
|
||||
url: "http://localhost:8000/test?invalid_field=1",
|
||||
query: query,
|
||||
},
|
||||
}
|
||||
|
||||
It("adds conditions to the query", func() {
|
||||
for _, urlValuesTest := range urlValuesTests {
|
||||
req, _ := http.NewRequest("GET", urlValuesTest.url, nil)
|
||||
|
||||
q := NewQuery(nil, &URLValuesModel{})
|
||||
q = q.Apply(URLFilters(req.URL.Query()))
|
||||
|
||||
b, err := selectQuery{q: q}.AppendQuery(nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(string(b)).To(Equal(urlValuesTest.query))
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
var _ = Describe("Pager", func() {
|
||||
query := `SELECT "url_values_model"."id", "url_values_model"."name" FROM "url_values_models" AS "url_values_model"`
|
||||
urlValuesTests := []urlValuesTest{
|
||||
{
|
||||
url: "http://localhost:8000/test?limit=10",
|
||||
query: query + " LIMIT 10",
|
||||
},
|
||||
{
|
||||
url: "http://localhost:8000/test?page=5",
|
||||
query: query + ` LIMIT 100 OFFSET 400`,
|
||||
},
|
||||
|
||||
{
|
||||
url: "http://localhost:8000/test?page=5&limit=20",
|
||||
query: query + ` LIMIT 20 OFFSET 80`,
|
||||
},
|
||||
}
|
||||
|
||||
It("adds limit and offset to the query", func() {
|
||||
for _, urlValuesTest := range urlValuesTests {
|
||||
req, _ := http.NewRequest("GET", urlValuesTest.url, nil)
|
||||
|
||||
q := NewQuery(nil, &URLValuesModel{})
|
||||
q = q.Apply(Pagination(req.URL.Query()))
|
||||
|
||||
b, err := selectQuery{q: q}.AppendQuery(nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(string(b)).To(Equal(urlValuesTest.query))
|
||||
}
|
||||
})
|
||||
})
|
||||
155
vendor/github.com/go-pg/pg/orm/util.go
generated
vendored
Normal file
155
vendor/github.com/go-pg/pg/orm/util.go
generated
vendored
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
package orm
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/go-pg/pg/types"
|
||||
)
|
||||
|
||||
func indirectType(t reflect.Type) reflect.Type {
|
||||
if t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func sliceElemType(v reflect.Value) reflect.Type {
|
||||
elemType := v.Type().Elem()
|
||||
if elemType.Kind() == reflect.Interface && v.Len() > 0 {
|
||||
return reflect.Indirect(v.Index(0).Elem()).Type()
|
||||
} else {
|
||||
return indirectType(elemType)
|
||||
}
|
||||
}
|
||||
|
||||
func typeByIndex(t reflect.Type, index []int) reflect.Type {
|
||||
for _, x := range index {
|
||||
switch t.Kind() {
|
||||
case reflect.Ptr:
|
||||
t = t.Elem()
|
||||
case reflect.Slice:
|
||||
t = indirectType(t.Elem())
|
||||
}
|
||||
t = t.Field(x).Type
|
||||
}
|
||||
return indirectType(t)
|
||||
}
|
||||
|
||||
func fieldByIndex(v reflect.Value, index []int) reflect.Value {
|
||||
for i, x := range index {
|
||||
if i > 0 {
|
||||
v = indirectNew(v)
|
||||
}
|
||||
v = v.Field(x)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func indirectNew(v reflect.Value) reflect.Value {
|
||||
if v.Kind() == reflect.Ptr {
|
||||
if v.IsNil() {
|
||||
v.Set(reflect.New(v.Type().Elem()))
|
||||
}
|
||||
v = v.Elem()
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func columns(b []byte, table types.Q, prefix string, fields []*Field) []byte {
|
||||
for i, f := range fields {
|
||||
if i > 0 {
|
||||
b = append(b, ", "...)
|
||||
}
|
||||
|
||||
if len(table) > 0 {
|
||||
b = append(b, table...)
|
||||
b = append(b, '.')
|
||||
}
|
||||
b = types.AppendField(b, prefix+f.SQLName, 1)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func walk(v reflect.Value, index []int, fn func(reflect.Value)) {
|
||||
v = reflect.Indirect(v)
|
||||
switch v.Kind() {
|
||||
case reflect.Slice:
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
visitField(v.Index(i), index, fn)
|
||||
}
|
||||
default:
|
||||
visitField(v, index, fn)
|
||||
}
|
||||
}
|
||||
|
||||
func visitField(v reflect.Value, index []int, fn func(reflect.Value)) {
|
||||
v = reflect.Indirect(v)
|
||||
if len(index) > 0 {
|
||||
v = v.Field(index[0])
|
||||
walk(v, index[1:], fn)
|
||||
} else {
|
||||
fn(v)
|
||||
}
|
||||
}
|
||||
|
||||
func appendChildValues(b []byte, v reflect.Value, index []int, fields []*Field) []byte {
|
||||
seen := make(map[string]struct{})
|
||||
walk(v, index, func(v reflect.Value) {
|
||||
start := len(b)
|
||||
|
||||
b = append(b, '(')
|
||||
for i, f := range fields {
|
||||
if i > 0 {
|
||||
b = append(b, ", "...)
|
||||
}
|
||||
b = f.AppendValue(b, v, 1)
|
||||
}
|
||||
b = append(b, "), "...)
|
||||
|
||||
if _, ok := seen[string(b[start:])]; ok {
|
||||
b = b[:start]
|
||||
} else {
|
||||
seen[string(b[start:])] = struct{}{}
|
||||
}
|
||||
})
|
||||
if len(seen) > 0 {
|
||||
b = b[:len(b)-2] // trim ", "
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func dstValues(model tableModel, fields []*Field) map[string][]reflect.Value {
|
||||
mp := make(map[string][]reflect.Value)
|
||||
var id []byte
|
||||
walk(model.Root(), model.ParentIndex(), func(v reflect.Value) {
|
||||
id = modelId(id[:0], v, fields)
|
||||
mp[string(id)] = append(mp[string(id)], v.FieldByIndex(model.Relation().Field.Index))
|
||||
})
|
||||
return mp
|
||||
}
|
||||
|
||||
func modelId(b []byte, v reflect.Value, fields []*Field) []byte {
|
||||
for _, f := range fields {
|
||||
b = f.AppendValue(b, v, 0)
|
||||
b = append(b, ',')
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func modelIdMap(b []byte, m map[string]string, prefix string, fields []*Field) []byte {
|
||||
for _, f := range fields {
|
||||
b = append(b, m[prefix+f.SQLName]...)
|
||||
b = append(b, ',')
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func appendColumns(b []byte, fields []*Field) []byte {
|
||||
for i, f := range fields {
|
||||
if i > 0 {
|
||||
b = append(b, ", "...)
|
||||
}
|
||||
b = append(b, f.Column...)
|
||||
}
|
||||
return b
|
||||
}
|
||||
122
vendor/github.com/go-pg/pg/orm/zero.go
generated
vendored
Normal file
122
vendor/github.com/go-pg/pg/orm/zero.go
generated
vendored
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
package orm
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"reflect"
|
||||
|
||||
"github.com/go-pg/pg/types"
|
||||
)
|
||||
|
||||
var driverValuerType = reflect.TypeOf((*driver.Valuer)(nil)).Elem()
|
||||
var appenderType = reflect.TypeOf((*types.ValueAppender)(nil)).Elem()
|
||||
var isZeroerType = reflect.TypeOf((*isZeroer)(nil)).Elem()
|
||||
|
||||
type isZeroer interface {
|
||||
IsZero() bool
|
||||
}
|
||||
|
||||
func isZeroFunc(typ reflect.Type) func(reflect.Value) bool {
|
||||
if typ.Implements(isZeroerType) {
|
||||
return isZero
|
||||
}
|
||||
|
||||
switch typ.Kind() {
|
||||
case reflect.Array:
|
||||
if typ.Elem().Kind() == reflect.Uint8 {
|
||||
return isZeroBytes
|
||||
}
|
||||
return isZeroLen
|
||||
case reflect.Map, reflect.Slice, reflect.String:
|
||||
return isZeroLen
|
||||
case reflect.Bool:
|
||||
return isZeroBool
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return isZeroInt
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return isZeroUint
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return isZeroFloat
|
||||
case reflect.Interface, reflect.Ptr:
|
||||
return isZeroNil
|
||||
}
|
||||
|
||||
if typ.Implements(appenderType) {
|
||||
return isZeroAppenderValue
|
||||
}
|
||||
if typ.Implements(driverValuerType) {
|
||||
return isZeroDriverValue
|
||||
}
|
||||
|
||||
return isZeroFalse
|
||||
}
|
||||
|
||||
func isZero(v reflect.Value) bool {
|
||||
if v.Kind() == reflect.Ptr {
|
||||
return v.IsNil()
|
||||
}
|
||||
return v.Interface().(isZeroer).IsZero()
|
||||
}
|
||||
|
||||
func isZeroAppenderValue(v reflect.Value) bool {
|
||||
if v.Kind() == reflect.Ptr {
|
||||
return v.IsNil()
|
||||
}
|
||||
|
||||
appender := v.Interface().(types.ValueAppender)
|
||||
value, err := appender.AppendValue(nil, 0)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return value == nil
|
||||
}
|
||||
|
||||
func isZeroDriverValue(v reflect.Value) bool {
|
||||
if v.Kind() == reflect.Ptr {
|
||||
return v.IsNil()
|
||||
}
|
||||
|
||||
valuer := v.Interface().(driver.Valuer)
|
||||
value, err := valuer.Value()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return value == nil
|
||||
}
|
||||
|
||||
func isZeroLen(v reflect.Value) bool {
|
||||
return v.Len() == 0
|
||||
}
|
||||
|
||||
func isZeroNil(v reflect.Value) bool {
|
||||
return v.IsNil()
|
||||
}
|
||||
|
||||
func isZeroBool(v reflect.Value) bool {
|
||||
return !v.Bool()
|
||||
}
|
||||
|
||||
func isZeroInt(v reflect.Value) bool {
|
||||
return v.Int() == 0
|
||||
}
|
||||
|
||||
func isZeroUint(v reflect.Value) bool {
|
||||
return v.Uint() == 0
|
||||
}
|
||||
|
||||
func isZeroFloat(v reflect.Value) bool {
|
||||
return v.Float() == 0
|
||||
}
|
||||
|
||||
func isZeroBytes(v reflect.Value) bool {
|
||||
b := v.Slice(0, v.Len()).Bytes()
|
||||
for _, c := range b {
|
||||
if c != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func isZeroFalse(v reflect.Value) bool {
|
||||
return false
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue