2016-05-17 12:00:56 +02:00
|
|
|
package boil
|
|
|
|
|
|
|
|
import (
|
2016-08-13 20:42:28 +02:00
|
|
|
"database/sql"
|
2016-05-17 12:00:56 +02:00
|
|
|
"fmt"
|
|
|
|
"reflect"
|
2016-08-08 02:11:45 +02:00
|
|
|
"strings"
|
2016-05-17 12:00:56 +02:00
|
|
|
|
2016-08-08 02:11:45 +02:00
|
|
|
"github.com/pkg/errors"
|
2016-08-09 09:59:30 +02:00
|
|
|
"github.com/vattle/sqlboiler/strmangle"
|
2016-05-17 12:00:56 +02:00
|
|
|
)
|
|
|
|
|
2016-08-08 02:11:45 +02:00
|
|
|
var (
|
|
|
|
bindAccepts = []reflect.Kind{reflect.Ptr, reflect.Slice, reflect.Ptr, reflect.Struct}
|
|
|
|
)
|
|
|
|
|
|
|
|
// BindP executes the query and inserts the
|
|
|
|
// result into the passed in object pointer.
|
2016-08-12 07:57:07 +02:00
|
|
|
// It panics on error. See boil.Bind() documentation.
|
2016-08-08 02:11:45 +02:00
|
|
|
func (q *Query) BindP(obj interface{}) {
|
|
|
|
if err := q.Bind(obj); err != nil {
|
|
|
|
panic(WrapErr(err))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-17 12:00:56 +02:00
|
|
|
// Bind executes the query and inserts the
|
|
|
|
// result into the passed in object pointer
|
2016-08-08 02:11:45 +02:00
|
|
|
//
|
|
|
|
// Bind rules:
|
2016-08-18 03:52:42 +02:00
|
|
|
// - Struct tags control bind, in the form of: `boil:"name,bind"`
|
2016-08-23 05:14:46 +02:00
|
|
|
// - If "name" is omitted the sql column names that come back are TitleCased
|
|
|
|
// and matched against the field name.
|
|
|
|
// - If the "name" part of the struct tag is specified, the given name will
|
|
|
|
// be used instead of the struct field name for binding.
|
|
|
|
// - If the "name" of the struct tag is "-", this field will not be bound to.
|
|
|
|
// - If the ",bind" option is specified on a struct field and that field
|
|
|
|
// is a struct itself, it will be recursed into to look for fields for binding.
|
2016-08-08 02:11:45 +02:00
|
|
|
//
|
|
|
|
// Example Query:
|
|
|
|
//
|
|
|
|
// type JoinStruct struct {
|
2016-08-23 05:14:46 +02:00
|
|
|
// // User1 can have it's struct fields bound to since it specifies
|
|
|
|
// // ,bind in the struct tag, it will look specifically for
|
|
|
|
// // fields that are prefixed with "user." returning from the query.
|
|
|
|
// // For example "user.id" column name will bind to User1.ID
|
2016-08-08 02:11:45 +02:00
|
|
|
// User1 *models.User `boil:"user,bind"`
|
2016-08-23 05:14:46 +02:00
|
|
|
// // User2 will follow the same rules as noted above except it will use
|
|
|
|
// // "friend." as the prefix it's looking for.
|
2016-08-08 02:11:45 +02:00
|
|
|
// User2 *models.User `boil:"friend,bind"`
|
|
|
|
// // RandomData will not be recursed into to look for fields to
|
|
|
|
// // bind and will not be bound to because of the - for the name.
|
|
|
|
// RandomData myStruct `boil:"-"`
|
|
|
|
// // Date will not be recursed into to look for fields to bind because
|
|
|
|
// // it does not specify ,bind in the struct tag. But it can be bound to
|
|
|
|
// // as it does not specify a - for the name.
|
|
|
|
// Date time.Time
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// models.Users(qm.InnerJoin("users as friend on users.friend_id = friend.id")).Bind(&joinStruct)
|
2016-08-18 03:52:42 +02:00
|
|
|
//
|
|
|
|
// For custom objects that want to use eager loading, please see the
|
|
|
|
// loadRelationships function.
|
2016-08-12 07:57:07 +02:00
|
|
|
func Bind(rows *sql.Rows, obj interface{}) error {
|
|
|
|
structType, sliceType, singular, err := bindChecks(obj)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return bind(rows, obj, structType, sliceType, singular)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bind executes the query and inserts the
|
|
|
|
// result into the passed in object pointer
|
|
|
|
//
|
|
|
|
// See documentation for boil.Bind()
|
2016-05-17 12:00:56 +02:00
|
|
|
func (q *Query) Bind(obj interface{}) error {
|
2016-08-12 07:57:07 +02:00
|
|
|
structType, sliceType, singular, err := bindChecks(obj)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
rows, err := ExecQueryAll(q)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "bind failed to execute query")
|
|
|
|
}
|
|
|
|
defer rows.Close()
|
|
|
|
|
2016-08-17 19:56:00 +02:00
|
|
|
if res := bind(rows, obj, structType, sliceType, singular); res != nil {
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(q.load) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return q.loadRelationships(obj, singular)
|
|
|
|
}
|
|
|
|
|
2016-08-18 03:52:42 +02:00
|
|
|
// loadRelationships dynamically calls the template generated eager load
|
|
|
|
// functions of the form:
|
|
|
|
//
|
2016-08-23 06:53:37 +02:00
|
|
|
// func (t *TableLoaded) LoadRelationshipName(exec Executor, singular bool, obj interface{})
|
2016-08-18 03:52:42 +02:00
|
|
|
//
|
|
|
|
// The arguments to this function are:
|
2016-08-23 06:53:37 +02:00
|
|
|
// - t is not considered here, and is always passed nil. The function exists on a loaded
|
2016-08-18 03:52:42 +02:00
|
|
|
// struct to avoid a circular dependency with boil, and the receiver is ignored.
|
|
|
|
// - exec is used to perform additional queries that might be required for loading the relationships.
|
|
|
|
// - singular is passed in to identify whether or not this was a single object
|
|
|
|
// or a slice that must be loaded into.
|
|
|
|
// - obj is the object or slice of objects, always of the type *obj or *[]*obj as per bind.
|
2016-08-17 19:56:00 +02:00
|
|
|
func (q *Query) loadRelationships(obj interface{}, singular bool) error {
|
2016-08-18 03:52:42 +02:00
|
|
|
typ := reflect.TypeOf(obj).Elem()
|
2016-08-17 19:56:00 +02:00
|
|
|
if !singular {
|
2016-08-24 07:07:51 +02:00
|
|
|
typ = typ.Elem().Elem()
|
2016-08-17 19:56:00 +02:00
|
|
|
}
|
|
|
|
|
2016-08-23 06:53:37 +02:00
|
|
|
rel, found := typ.FieldByName("Loaded")
|
|
|
|
// If the users object has no loaded struct, it must be
|
2016-08-17 19:56:00 +02:00
|
|
|
// a custom object and we should not attempt to load any relationships.
|
|
|
|
if !found {
|
2016-08-23 06:53:37 +02:00
|
|
|
return errors.New("load query mod was used but bound struct contained no Loaded field")
|
2016-08-17 19:56:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, relationship := range q.load {
|
|
|
|
// Attempt to find the LoadRelationshipName function
|
|
|
|
loadMethod, found := rel.Type.MethodByName("Load" + relationship)
|
|
|
|
if !found {
|
|
|
|
return errors.Errorf("could not find Load%s method for eager loading", relationship)
|
|
|
|
}
|
2016-08-18 03:52:42 +02:00
|
|
|
|
|
|
|
execArg := reflect.ValueOf(q.executor)
|
|
|
|
if !execArg.IsValid() {
|
|
|
|
execArg = reflect.ValueOf((*sql.DB)(nil))
|
|
|
|
}
|
|
|
|
|
2016-08-17 19:56:00 +02:00
|
|
|
methodArgs := []reflect.Value{
|
2016-08-18 03:52:42 +02:00
|
|
|
reflect.Indirect(reflect.New(rel.Type)),
|
|
|
|
execArg,
|
2016-08-17 19:56:00 +02:00
|
|
|
reflect.ValueOf(singular),
|
|
|
|
reflect.ValueOf(obj),
|
|
|
|
}
|
|
|
|
|
|
|
|
resp := loadMethod.Func.Call(methodArgs)
|
|
|
|
if resp[0].Interface() != nil {
|
|
|
|
return resp[0].Interface().(error)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2016-08-12 07:57:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// bindChecks resolves information about the bind target, and errors if it's not an object
|
|
|
|
// we can bind to.
|
|
|
|
func bindChecks(obj interface{}) (structType reflect.Type, sliceType reflect.Type, singular bool, err error) {
|
2016-08-08 02:11:45 +02:00
|
|
|
typ := reflect.TypeOf(obj)
|
2016-06-08 07:45:34 +02:00
|
|
|
kind := typ.Kind()
|
|
|
|
|
2016-08-08 02:11:45 +02:00
|
|
|
for i := 0; i < len(bindAccepts); i++ {
|
|
|
|
exp := bindAccepts[i]
|
2016-06-08 07:45:34 +02:00
|
|
|
|
2016-08-08 02:11:45 +02:00
|
|
|
if i != 0 {
|
|
|
|
typ = typ.Elem()
|
|
|
|
kind = typ.Kind()
|
2016-06-08 07:45:34 +02:00
|
|
|
}
|
2016-08-08 02:11:45 +02:00
|
|
|
|
|
|
|
if kind != exp {
|
|
|
|
if exp == reflect.Slice || kind == reflect.Struct {
|
|
|
|
structType = typ
|
|
|
|
singular = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
2016-08-12 07:57:07 +02:00
|
|
|
return nil, nil, false, errors.Errorf("obj type should be *[]*Type or *Type but was %q", reflect.TypeOf(obj).String())
|
2016-06-08 07:45:34 +02:00
|
|
|
}
|
2016-08-08 02:11:45 +02:00
|
|
|
|
|
|
|
switch kind {
|
|
|
|
case reflect.Struct:
|
|
|
|
structType = typ
|
|
|
|
case reflect.Slice:
|
|
|
|
sliceType = typ
|
2016-06-08 07:45:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-12 07:57:07 +02:00
|
|
|
return structType, sliceType, singular, nil
|
2016-05-17 12:00:56 +02:00
|
|
|
}
|
|
|
|
|
2016-08-12 07:57:07 +02:00
|
|
|
func bind(rows *sql.Rows, obj interface{}, structType, sliceType reflect.Type, singular bool) error {
|
2016-08-08 02:11:45 +02:00
|
|
|
cols, err := rows.Columns()
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "bind failed to get column names")
|
2016-06-05 08:13:38 +02:00
|
|
|
}
|
|
|
|
|
2016-08-08 02:11:45 +02:00
|
|
|
var ptrSlice reflect.Value
|
|
|
|
if !singular {
|
2016-08-08 03:06:09 +02:00
|
|
|
ptrSlice = reflect.Indirect(reflect.ValueOf(obj))
|
2016-08-08 02:11:45 +02:00
|
|
|
}
|
|
|
|
|
2016-08-13 20:42:28 +02:00
|
|
|
foundOne := false
|
2016-08-08 02:11:45 +02:00
|
|
|
for rows.Next() {
|
2016-08-13 20:42:28 +02:00
|
|
|
foundOne = true
|
2016-08-08 02:11:45 +02:00
|
|
|
var newStruct reflect.Value
|
|
|
|
var pointers []interface{}
|
|
|
|
|
|
|
|
if singular {
|
|
|
|
pointers, err = bindPtrs(obj, cols...)
|
|
|
|
} else {
|
|
|
|
newStruct = reflect.New(structType)
|
|
|
|
pointers, err = bindPtrs(newStruct.Interface(), cols...)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := rows.Scan(pointers...); err != nil {
|
|
|
|
return errors.Wrap(err, "failed to bind pointers to obj")
|
|
|
|
}
|
|
|
|
|
|
|
|
if !singular {
|
|
|
|
ptrSlice.Set(reflect.Append(ptrSlice, newStruct))
|
|
|
|
}
|
2016-06-05 08:13:38 +02:00
|
|
|
}
|
|
|
|
|
2016-08-13 20:42:28 +02:00
|
|
|
if singular && !foundOne {
|
|
|
|
return sql.ErrNoRows
|
|
|
|
}
|
|
|
|
|
2016-05-17 12:00:56 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-08-08 02:11:45 +02:00
|
|
|
func bindPtrs(obj interface{}, cols ...string) ([]interface{}, error) {
|
|
|
|
v := reflect.ValueOf(obj)
|
|
|
|
ptrs := make([]interface{}, len(cols))
|
2016-06-05 08:13:38 +02:00
|
|
|
|
2016-08-08 02:11:45 +02:00
|
|
|
for i, c := range cols {
|
|
|
|
names := strings.Split(c, ".")
|
2016-06-07 06:38:17 +02:00
|
|
|
|
2016-08-08 02:11:45 +02:00
|
|
|
ptr, ok := findField(names, v)
|
|
|
|
if !ok {
|
|
|
|
return nil, errors.Errorf("bindPtrs failed to find field %s", c)
|
2016-06-07 06:38:17 +02:00
|
|
|
}
|
2016-06-05 08:13:38 +02:00
|
|
|
|
2016-08-08 02:11:45 +02:00
|
|
|
ptrs[i] = ptr
|
|
|
|
}
|
|
|
|
|
|
|
|
return ptrs, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func findField(names []string, v reflect.Value) (interface{}, bool) {
|
|
|
|
if !v.IsValid() || len(names) == 0 {
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
|
|
|
|
if v.Kind() == reflect.Ptr {
|
|
|
|
if v.IsNil() {
|
|
|
|
return nil, false
|
2016-06-07 06:38:17 +02:00
|
|
|
}
|
2016-08-08 02:11:45 +02:00
|
|
|
v = reflect.Indirect(v)
|
|
|
|
}
|
|
|
|
|
|
|
|
if v.Kind() != reflect.Struct {
|
|
|
|
return nil, false
|
|
|
|
}
|
2016-06-05 08:13:38 +02:00
|
|
|
|
2016-08-08 02:11:45 +02:00
|
|
|
name := strmangle.TitleCase(names[0])
|
|
|
|
typ := v.Type()
|
|
|
|
|
|
|
|
n := typ.NumField()
|
|
|
|
for i := 0; i < n; i++ {
|
|
|
|
f := typ.Field(i)
|
|
|
|
fieldName, recurse := getBoilTag(f)
|
|
|
|
|
|
|
|
if fieldName == "-" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if recurse {
|
2016-08-08 03:06:09 +02:00
|
|
|
if fieldName == name {
|
|
|
|
names = names[1:]
|
|
|
|
}
|
|
|
|
if ptr, ok := findField(names, v.Field(i)); ok {
|
|
|
|
return ptr, ok
|
|
|
|
}
|
2016-06-05 08:13:38 +02:00
|
|
|
}
|
2016-06-07 06:38:17 +02:00
|
|
|
|
2016-08-08 03:06:09 +02:00
|
|
|
if fieldName != name || len(names) > 1 {
|
|
|
|
continue
|
2016-08-08 02:11:45 +02:00
|
|
|
}
|
2016-08-08 03:06:09 +02:00
|
|
|
|
|
|
|
fieldVal := v.Field(i)
|
|
|
|
if fieldVal.Kind() != reflect.Ptr {
|
|
|
|
return fieldVal.Addr().Interface(), true
|
|
|
|
}
|
|
|
|
return fieldVal.Interface(), true
|
2016-06-05 08:13:38 +02:00
|
|
|
}
|
|
|
|
|
2016-08-08 02:11:45 +02:00
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
|
|
|
|
func getBoilTag(field reflect.StructField) (name string, recurse bool) {
|
|
|
|
tag := field.Tag.Get("boil")
|
|
|
|
|
|
|
|
if len(tag) != 0 {
|
|
|
|
tagTokens := strings.Split(tag, ",")
|
|
|
|
name = strmangle.TitleCase(tagTokens[0])
|
|
|
|
recurse = len(tagTokens) > 1 && tagTokens[1] == "bind"
|
2016-08-08 03:06:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(name) == 0 {
|
2016-08-08 02:11:45 +02:00
|
|
|
name = field.Name
|
|
|
|
}
|
|
|
|
|
|
|
|
return name, recurse
|
2016-05-17 12:00:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetStructValues returns the values (as interface) of the matching columns in obj
|
|
|
|
func GetStructValues(obj interface{}, columns ...string) []interface{} {
|
|
|
|
ret := make([]interface{}, len(columns))
|
|
|
|
val := reflect.Indirect(reflect.ValueOf(obj))
|
|
|
|
|
|
|
|
for i, c := range columns {
|
|
|
|
field := val.FieldByName(strmangle.TitleCase(c))
|
2016-08-15 14:43:10 +02:00
|
|
|
if !field.IsValid() {
|
2016-08-16 07:24:28 +02:00
|
|
|
panic(fmt.Sprintf("unable to find field with name: %s\n%#v", strmangle.TitleCase(c), obj))
|
2016-08-15 14:43:10 +02:00
|
|
|
}
|
2016-05-17 12:00:56 +02:00
|
|
|
ret[i] = field.Interface()
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
2016-08-16 07:24:28 +02:00
|
|
|
// GetSliceValues returns the values (as interface) of the matching columns in obj.
|
|
|
|
func GetSliceValues(slice []interface{}, columns ...string) []interface{} {
|
|
|
|
ret := make([]interface{}, len(slice)*len(columns))
|
|
|
|
|
|
|
|
for i, obj := range slice {
|
|
|
|
val := reflect.Indirect(reflect.ValueOf(obj))
|
|
|
|
for j, c := range columns {
|
|
|
|
field := val.FieldByName(strmangle.TitleCase(c))
|
|
|
|
if !field.IsValid() {
|
|
|
|
panic(fmt.Sprintf("unable to find field with name: %s\n%#v", strmangle.TitleCase(c), obj))
|
|
|
|
}
|
|
|
|
ret[i*len(columns)+j] = field.Interface()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
2016-05-17 12:00:56 +02:00
|
|
|
// GetStructPointers returns a slice of pointers to the matching columns in obj
|
|
|
|
func GetStructPointers(obj interface{}, columns ...string) []interface{} {
|
|
|
|
val := reflect.ValueOf(obj).Elem()
|
2016-08-06 23:37:55 +02:00
|
|
|
|
|
|
|
var ln int
|
|
|
|
var getField func(reflect.Value, int) reflect.Value
|
2016-06-07 06:38:17 +02:00
|
|
|
|
|
|
|
if len(columns) == 0 {
|
2016-08-06 23:37:55 +02:00
|
|
|
ln = val.NumField()
|
|
|
|
getField = func(v reflect.Value, i int) reflect.Value {
|
|
|
|
return v.Field(i)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ln = len(columns)
|
|
|
|
getField = func(v reflect.Value, i int) reflect.Value {
|
|
|
|
return v.FieldByName(strmangle.TitleCase(columns[i]))
|
2016-06-07 06:38:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-06 23:37:55 +02:00
|
|
|
ret := make([]interface{}, ln)
|
|
|
|
for i := 0; i < ln; i++ {
|
|
|
|
field := getField(val, i)
|
2016-05-17 12:00:56 +02:00
|
|
|
|
|
|
|
if !field.IsValid() {
|
2016-08-06 23:37:55 +02:00
|
|
|
// Although this breaks the abstraction of getField above - we know that v.Field(i) can't actually
|
|
|
|
// produce an Invalid value, so we make a hopefully safe assumption here.
|
|
|
|
panic(fmt.Sprintf("Could not find field on struct %T for field %s", obj, strmangle.TitleCase(columns[i])))
|
2016-05-17 12:00:56 +02:00
|
|
|
}
|
|
|
|
|
2016-08-06 23:37:55 +02:00
|
|
|
ret[i] = field.Addr().Interface()
|
2016-05-17 12:00:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret
|
|
|
|
}
|