2014-01-26 08:41:37 +04:00
|
|
|
package gorm
|
|
|
|
|
2014-01-26 09:51:23 +04:00
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
2014-01-26 15:34:06 +04:00
|
|
|
"go/ast"
|
2014-01-26 10:18:21 +04:00
|
|
|
"strings"
|
2014-01-26 13:10:33 +04:00
|
|
|
"time"
|
2014-01-26 09:51:23 +04:00
|
|
|
|
|
|
|
"reflect"
|
2014-01-26 10:18:21 +04:00
|
|
|
"regexp"
|
2014-01-26 09:51:23 +04:00
|
|
|
)
|
2014-01-26 08:41:37 +04:00
|
|
|
|
|
|
|
type Scope struct {
|
2014-11-11 06:46:21 +03:00
|
|
|
Value interface{}
|
|
|
|
indirectValue *reflect.Value
|
|
|
|
Search *search
|
|
|
|
Sql string
|
|
|
|
SqlVars []interface{}
|
|
|
|
db *DB
|
|
|
|
skipLeft bool
|
|
|
|
primaryKeyField *Field
|
|
|
|
instanceId string
|
|
|
|
fields map[string]*Field
|
2014-07-30 10:58:00 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (scope *Scope) IndirectValue() reflect.Value {
|
|
|
|
if scope.indirectValue == nil {
|
|
|
|
value := reflect.Indirect(reflect.ValueOf(scope.Value))
|
|
|
|
scope.indirectValue = &value
|
|
|
|
}
|
|
|
|
return *scope.indirectValue
|
2014-01-26 08:41:37 +04:00
|
|
|
}
|
|
|
|
|
2014-01-29 15:14:37 +04:00
|
|
|
// NewScope create scope for callbacks, including DB's search information
|
2014-01-27 04:26:59 +04:00
|
|
|
func (db *DB) NewScope(value interface{}) *Scope {
|
2014-01-27 07:06:13 +04:00
|
|
|
db.Value = value
|
2014-08-20 12:25:01 +04:00
|
|
|
return &Scope{db: db, Search: db.search, Value: value}
|
2014-01-26 09:51:23 +04:00
|
|
|
}
|
|
|
|
|
2014-11-25 08:44:14 +03:00
|
|
|
func (scope *Scope) NeedPtr() *Scope {
|
|
|
|
reflectKind := reflect.ValueOf(scope.Value).Kind()
|
|
|
|
if !((reflectKind == reflect.Invalid) || (reflectKind == reflect.Ptr)) {
|
2015-02-17 02:15:34 +03:00
|
|
|
err := fmt.Errorf("%v %v\n", fileWithLineNum(), "using unaddressable value")
|
2014-11-25 08:44:14 +03:00
|
|
|
scope.Err(err)
|
|
|
|
fmt.Printf(err.Error())
|
|
|
|
}
|
|
|
|
return scope
|
|
|
|
}
|
|
|
|
|
2014-01-29 15:14:37 +04:00
|
|
|
// New create a new Scope without search information
|
2014-01-27 04:26:59 +04:00
|
|
|
func (scope *Scope) New(value interface{}) *Scope {
|
2014-08-27 12:40:32 +04:00
|
|
|
return &Scope{db: scope.db, Search: &search{}, Value: value}
|
2014-01-27 04:26:59 +04:00
|
|
|
}
|
|
|
|
|
2014-01-29 15:14:37 +04:00
|
|
|
// NewDB create a new DB without search information
|
2014-01-27 04:26:59 +04:00
|
|
|
func (scope *Scope) NewDB() *DB {
|
2015-01-06 10:11:41 +03:00
|
|
|
return scope.db.New()
|
2014-01-27 04:26:59 +04:00
|
|
|
}
|
|
|
|
|
2014-01-29 15:14:37 +04:00
|
|
|
// DB get *sql.DB
|
2014-01-26 08:41:37 +04:00
|
|
|
func (scope *Scope) DB() sqlCommon {
|
|
|
|
return scope.db.db
|
|
|
|
}
|
|
|
|
|
2014-01-29 15:14:37 +04:00
|
|
|
// SkipLeft skip remaining callbacks
|
2014-01-29 08:00:57 +04:00
|
|
|
func (scope *Scope) SkipLeft() {
|
|
|
|
scope.skipLeft = true
|
|
|
|
}
|
|
|
|
|
2014-01-29 15:14:37 +04:00
|
|
|
// Quote used to quote database column name according to database dialect
|
2014-01-29 08:00:57 +04:00
|
|
|
func (scope *Scope) Quote(str string) string {
|
|
|
|
return scope.Dialect().Quote(str)
|
|
|
|
}
|
|
|
|
|
2014-01-29 15:14:37 +04:00
|
|
|
// Dialect get dialect
|
2014-04-25 03:20:23 +04:00
|
|
|
func (scope *Scope) Dialect() Dialect {
|
2014-01-26 08:41:37 +04:00
|
|
|
return scope.db.parent.dialect
|
|
|
|
}
|
|
|
|
|
2014-01-29 15:14:37 +04:00
|
|
|
// Err write error
|
2014-01-26 08:41:37 +04:00
|
|
|
func (scope *Scope) Err(err error) error {
|
|
|
|
if err != nil {
|
|
|
|
scope.db.err(err)
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2014-01-29 15:14:37 +04:00
|
|
|
// Log print log message
|
2014-01-28 04:27:12 +04:00
|
|
|
func (scope *Scope) Log(v ...interface{}) {
|
|
|
|
scope.db.log(v...)
|
|
|
|
}
|
|
|
|
|
2014-01-29 15:14:37 +04:00
|
|
|
// HasError check if there are any error
|
2014-01-26 08:41:37 +04:00
|
|
|
func (scope *Scope) HasError() bool {
|
2014-01-28 12:29:42 +04:00
|
|
|
return scope.db.Error != nil
|
2014-01-26 08:41:37 +04:00
|
|
|
}
|
|
|
|
|
2014-11-11 06:46:21 +03:00
|
|
|
func (scope *Scope) PrimaryKeyField() *Field {
|
|
|
|
if scope.primaryKeyField == nil {
|
|
|
|
var indirectValue = scope.IndirectValue()
|
2014-08-30 18:39:28 +04:00
|
|
|
|
2014-11-11 06:46:21 +03:00
|
|
|
clone := scope
|
2015-02-11 12:58:19 +03:00
|
|
|
// FIXME
|
2014-11-11 06:46:21 +03:00
|
|
|
if indirectValue.Kind() == reflect.Slice {
|
2015-02-11 12:58:19 +03:00
|
|
|
typ := indirectValue.Type().Elem()
|
|
|
|
if typ.Kind() == reflect.Ptr {
|
|
|
|
typ = typ.Elem()
|
|
|
|
}
|
2015-02-11 13:10:59 +03:00
|
|
|
clone = scope.New(reflect.New(typ).Elem().Addr().Interface())
|
2014-11-11 06:46:21 +03:00
|
|
|
}
|
2014-08-30 18:39:28 +04:00
|
|
|
|
2014-11-11 06:46:21 +03:00
|
|
|
for _, field := range clone.Fields() {
|
|
|
|
if field.IsPrimaryKey {
|
|
|
|
scope.primaryKeyField = field
|
|
|
|
break
|
|
|
|
}
|
2014-08-30 18:39:28 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-11 06:46:21 +03:00
|
|
|
return scope.primaryKeyField
|
|
|
|
}
|
|
|
|
|
|
|
|
// PrimaryKey get the primary key's column name
|
|
|
|
func (scope *Scope) PrimaryKey() string {
|
|
|
|
if field := scope.PrimaryKeyField(); field != nil {
|
|
|
|
return field.DBName
|
|
|
|
}
|
2015-02-17 02:15:34 +03:00
|
|
|
return ""
|
2014-01-26 08:41:37 +04:00
|
|
|
}
|
|
|
|
|
2014-01-29 15:14:37 +04:00
|
|
|
// PrimaryKeyZero check the primary key is blank or not
|
2014-01-26 13:10:33 +04:00
|
|
|
func (scope *Scope) PrimaryKeyZero() bool {
|
|
|
|
return isBlank(reflect.ValueOf(scope.PrimaryKeyValue()))
|
|
|
|
}
|
|
|
|
|
2014-01-29 15:14:37 +04:00
|
|
|
// PrimaryKeyValue get the primary key's value
|
2014-01-26 13:10:33 +04:00
|
|
|
func (scope *Scope) PrimaryKeyValue() interface{} {
|
2014-11-11 06:46:21 +03:00
|
|
|
if field := scope.PrimaryKeyField(); field != nil {
|
|
|
|
return field.Field.Interface()
|
2014-01-26 13:10:33 +04:00
|
|
|
}
|
2015-02-17 02:15:34 +03:00
|
|
|
return 0
|
2014-01-26 13:10:33 +04:00
|
|
|
}
|
|
|
|
|
2014-01-29 15:14:37 +04:00
|
|
|
// HasColumn to check if has column
|
2015-01-19 11:23:33 +03:00
|
|
|
func (scope *Scope) HasColumn(column string) bool {
|
2014-09-01 13:03:58 +04:00
|
|
|
clone := scope
|
|
|
|
if scope.IndirectValue().Kind() == reflect.Slice {
|
|
|
|
value := reflect.New(scope.IndirectValue().Type().Elem()).Interface()
|
|
|
|
clone = scope.New(value)
|
|
|
|
}
|
|
|
|
|
2014-09-30 16:02:51 +04:00
|
|
|
dbName := ToSnake(column)
|
|
|
|
|
2015-01-19 11:23:33 +03:00
|
|
|
field, hasColumn := clone.Fields(false)[dbName]
|
|
|
|
return hasColumn && !field.IsIgnored
|
2014-01-28 08:28:44 +04:00
|
|
|
}
|
|
|
|
|
2014-07-31 07:08:26 +04:00
|
|
|
// FieldValueByName to get column's value and existence
|
2014-10-01 17:58:13 +04:00
|
|
|
func (scope *Scope) FieldValueByName(name string) (interface{}, error) {
|
2014-07-31 07:08:26 +04:00
|
|
|
return FieldValueByName(name, scope.Value)
|
2014-01-26 08:41:37 +04:00
|
|
|
}
|
|
|
|
|
2014-01-29 15:14:37 +04:00
|
|
|
// SetColumn to set the column's value
|
2014-09-30 16:02:51 +04:00
|
|
|
func (scope *Scope) SetColumn(column interface{}, value interface{}) error {
|
2014-09-02 15:03:01 +04:00
|
|
|
if field, ok := column.(*Field); ok {
|
|
|
|
return field.Set(value)
|
2015-01-16 05:02:04 +03:00
|
|
|
} else if dbName, ok := column.(string); ok {
|
2014-09-02 15:03:01 +04:00
|
|
|
if scope.Value == nil {
|
2014-09-30 16:02:51 +04:00
|
|
|
return errors.New("scope value must not be nil for string columns")
|
2014-09-02 15:03:01 +04:00
|
|
|
}
|
2014-09-30 16:02:51 +04:00
|
|
|
|
2015-01-16 05:02:04 +03:00
|
|
|
if field, ok := scope.Fields()[dbName]; ok {
|
|
|
|
return field.Set(value)
|
|
|
|
}
|
|
|
|
|
|
|
|
dbName = ToSnake(dbName)
|
2014-09-30 16:02:51 +04:00
|
|
|
|
|
|
|
if field, ok := scope.Fields()[dbName]; ok {
|
|
|
|
return field.Set(value)
|
2014-08-30 18:39:28 +04:00
|
|
|
}
|
|
|
|
}
|
2014-09-30 16:02:51 +04:00
|
|
|
return errors.New("could not convert column to field")
|
2014-01-26 08:41:37 +04:00
|
|
|
}
|
|
|
|
|
2014-01-29 15:14:37 +04:00
|
|
|
// CallMethod invoke method with necessary argument
|
2014-01-26 08:41:37 +04:00
|
|
|
func (scope *Scope) CallMethod(name string) {
|
2014-01-27 18:36:08 +04:00
|
|
|
if scope.Value == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-01-28 05:25:30 +04:00
|
|
|
call := func(value interface{}) {
|
|
|
|
if fm := reflect.ValueOf(value).MethodByName(name); fm.IsValid() {
|
2014-10-06 13:11:07 +04:00
|
|
|
switch f := fm.Interface().(type) {
|
2014-10-07 18:33:57 +04:00
|
|
|
case func():
|
|
|
|
f()
|
|
|
|
case func(s *Scope):
|
|
|
|
f(scope)
|
|
|
|
case func(s *DB):
|
2015-01-06 10:11:41 +03:00
|
|
|
f(scope.db.New())
|
2014-10-07 18:33:57 +04:00
|
|
|
case func() error:
|
|
|
|
scope.Err(f())
|
|
|
|
case func(s *Scope) error:
|
|
|
|
scope.Err(f(scope))
|
|
|
|
case func(s *DB) error:
|
2015-01-06 10:11:41 +03:00
|
|
|
scope.Err(f(scope.db.New()))
|
2014-10-07 18:33:57 +04:00
|
|
|
default:
|
2015-02-17 02:15:34 +03:00
|
|
|
scope.Err(fmt.Errorf("unsupported function %v", name))
|
2014-01-28 05:25:30 +04:00
|
|
|
}
|
2014-01-26 09:51:23 +04:00
|
|
|
}
|
|
|
|
}
|
2014-01-28 05:25:30 +04:00
|
|
|
|
2014-07-30 10:58:00 +04:00
|
|
|
if values := scope.IndirectValue(); values.Kind() == reflect.Slice {
|
2014-01-28 05:25:30 +04:00
|
|
|
for i := 0; i < values.Len(); i++ {
|
|
|
|
call(values.Index(i).Addr().Interface())
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
call(scope.Value)
|
|
|
|
}
|
2014-01-26 08:41:37 +04:00
|
|
|
}
|
|
|
|
|
2014-01-29 15:14:37 +04:00
|
|
|
// AddToVars add value as sql's vars, gorm will escape them
|
2014-01-26 08:41:37 +04:00
|
|
|
func (scope *Scope) AddToVars(value interface{}) string {
|
2014-01-26 10:18:21 +04:00
|
|
|
scope.SqlVars = append(scope.SqlVars, value)
|
|
|
|
return scope.Dialect().BinVar(len(scope.SqlVars))
|
2014-01-26 08:41:37 +04:00
|
|
|
}
|
|
|
|
|
2014-01-29 15:14:37 +04:00
|
|
|
// TableName get table name
|
2014-07-02 07:56:37 +04:00
|
|
|
var pluralMapKeys = []*regexp.Regexp{regexp.MustCompile("ch$"), regexp.MustCompile("ss$"), regexp.MustCompile("sh$"), regexp.MustCompile("day$"), regexp.MustCompile("y$"), regexp.MustCompile("x$"), regexp.MustCompile("([^s])s?$")}
|
|
|
|
var pluralMapValues = []string{"ches", "sses", "shes", "days", "ies", "xes", "${1}s"}
|
|
|
|
|
2014-01-26 08:41:37 +04:00
|
|
|
func (scope *Scope) TableName() string {
|
2014-01-28 12:56:51 +04:00
|
|
|
if scope.Search != nil && len(scope.Search.TableName) > 0 {
|
|
|
|
return scope.Search.TableName
|
2015-02-17 03:34:01 +03:00
|
|
|
}
|
2014-01-26 10:18:21 +04:00
|
|
|
|
2015-02-17 03:34:01 +03:00
|
|
|
if scope.Value == nil {
|
|
|
|
scope.Err(errors.New("can't get table name"))
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
data := scope.IndirectValue()
|
|
|
|
if data.Kind() == reflect.Slice {
|
|
|
|
elem := data.Type().Elem()
|
|
|
|
if elem.Kind() == reflect.Ptr {
|
|
|
|
elem = elem.Elem()
|
2014-01-26 10:18:21 +04:00
|
|
|
}
|
2015-02-17 03:34:01 +03:00
|
|
|
data = reflect.New(elem).Elem()
|
|
|
|
}
|
2014-01-26 10:18:21 +04:00
|
|
|
|
2015-02-17 03:34:01 +03:00
|
|
|
if fm := data.MethodByName("TableName"); fm.IsValid() {
|
|
|
|
if v := fm.Call([]reflect.Value{}); len(v) > 0 {
|
|
|
|
if result, ok := v[0].Interface().(string); ok {
|
|
|
|
return result
|
2014-01-26 10:18:21 +04:00
|
|
|
}
|
|
|
|
}
|
2015-02-17 03:34:01 +03:00
|
|
|
}
|
2014-01-26 10:18:21 +04:00
|
|
|
|
2015-02-17 03:34:01 +03:00
|
|
|
str := ToSnake(data.Type().Name())
|
2014-01-26 10:18:21 +04:00
|
|
|
|
2015-02-17 03:34:01 +03:00
|
|
|
if scope.db == nil || !scope.db.parent.singularTable {
|
|
|
|
for index, reg := range pluralMapKeys {
|
|
|
|
if reg.MatchString(str) {
|
|
|
|
return reg.ReplaceAllString(str, pluralMapValues[index])
|
2014-01-26 10:18:21 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-02-17 03:34:01 +03:00
|
|
|
|
|
|
|
return str
|
2014-01-26 10:18:21 +04:00
|
|
|
}
|
|
|
|
|
2014-06-03 13:15:05 +04:00
|
|
|
func (scope *Scope) QuotedTableName() string {
|
|
|
|
if scope.Search != nil && len(scope.Search.TableName) > 0 {
|
|
|
|
return scope.Search.TableName
|
|
|
|
}
|
2015-02-17 02:15:34 +03:00
|
|
|
|
|
|
|
keys := strings.Split(scope.TableName(), ".")
|
|
|
|
for i, v := range keys {
|
|
|
|
keys[i] = scope.Quote(v)
|
|
|
|
}
|
|
|
|
return strings.Join(keys, ".")
|
|
|
|
|
2014-06-03 13:15:05 +04:00
|
|
|
}
|
|
|
|
|
2014-01-29 15:14:37 +04:00
|
|
|
// CombinedConditionSql get combined condition sql
|
2014-01-29 04:55:45 +04:00
|
|
|
func (scope *Scope) CombinedConditionSql() string {
|
|
|
|
return scope.joinsSql() + scope.whereSql() + scope.groupSql() +
|
|
|
|
scope.havingSql() + scope.orderSql() + scope.limitSql() + scope.offsetSql()
|
2014-01-26 10:55:41 +04:00
|
|
|
}
|
|
|
|
|
2014-07-31 07:08:26 +04:00
|
|
|
func (scope *Scope) FieldByName(name string) (field *Field, ok bool) {
|
2014-09-02 15:03:01 +04:00
|
|
|
for _, field := range scope.Fields() {
|
|
|
|
if field.Name == name {
|
|
|
|
return field, true
|
2014-07-31 07:08:26 +04:00
|
|
|
}
|
|
|
|
}
|
2014-08-28 13:21:43 +04:00
|
|
|
return nil, false
|
2014-07-31 07:08:26 +04:00
|
|
|
}
|
|
|
|
|
2014-09-01 13:03:58 +04:00
|
|
|
func (scope *Scope) fieldFromStruct(fieldStruct reflect.StructField, withRelation bool) []*Field {
|
2014-07-30 10:58:00 +04:00
|
|
|
var field Field
|
|
|
|
field.Name = fieldStruct.Name
|
2014-01-26 15:34:06 +04:00
|
|
|
|
2014-07-30 10:58:00 +04:00
|
|
|
value := scope.IndirectValue().FieldByName(fieldStruct.Name)
|
|
|
|
indirectValue := reflect.Indirect(value)
|
2014-08-28 14:25:05 +04:00
|
|
|
field.Field = value
|
2014-07-30 10:58:00 +04:00
|
|
|
field.IsBlank = isBlank(value)
|
2014-01-26 15:34:06 +04:00
|
|
|
|
2014-07-30 10:58:00 +04:00
|
|
|
// Search for primary key tag identifier
|
|
|
|
settings := parseTagSetting(fieldStruct.Tag.Get("gorm"))
|
2014-08-30 18:39:28 +04:00
|
|
|
if _, ok := settings["PRIMARY_KEY"]; ok {
|
2014-07-31 07:08:26 +04:00
|
|
|
field.IsPrimaryKey = true
|
2014-07-30 10:58:00 +04:00
|
|
|
}
|
2014-01-26 15:34:06 +04:00
|
|
|
|
2014-11-15 20:32:35 +03:00
|
|
|
if def, ok := parseTagSetting(fieldStruct.Tag.Get("sql"))["DEFAULT"]; ok {
|
|
|
|
field.DefaultValue = def
|
|
|
|
}
|
|
|
|
|
2014-07-31 07:08:26 +04:00
|
|
|
field.Tag = fieldStruct.Tag
|
2014-08-30 18:39:28 +04:00
|
|
|
|
2014-10-07 18:33:57 +04:00
|
|
|
if value, ok := settings["COLUMN"]; ok {
|
|
|
|
field.DBName = value
|
|
|
|
} else {
|
|
|
|
field.DBName = ToSnake(fieldStruct.Name)
|
|
|
|
}
|
|
|
|
|
2014-08-30 18:39:28 +04:00
|
|
|
tagIdentifier := "sql"
|
|
|
|
if scope.db != nil {
|
|
|
|
tagIdentifier = scope.db.parent.tagIdentifier
|
|
|
|
}
|
|
|
|
if fieldStruct.Tag.Get(tagIdentifier) == "-" {
|
|
|
|
field.IsIgnored = true
|
|
|
|
}
|
2014-04-02 12:59:07 +04:00
|
|
|
|
2014-08-14 17:45:42 +04:00
|
|
|
if !field.IsIgnored {
|
|
|
|
// parse association
|
2014-08-15 07:14:33 +04:00
|
|
|
if !indirectValue.IsValid() {
|
|
|
|
indirectValue = reflect.New(value.Type())
|
|
|
|
}
|
2014-08-14 17:45:42 +04:00
|
|
|
typ := indirectValue.Type()
|
2014-08-28 11:33:43 +04:00
|
|
|
scopeTyp := scope.IndirectValue().Type()
|
|
|
|
|
2014-08-14 17:45:42 +04:00
|
|
|
foreignKey := SnakeToUpperCamel(settings["FOREIGNKEY"])
|
2014-11-26 08:23:43 +03:00
|
|
|
foreignType := SnakeToUpperCamel(settings["FOREIGNTYPE"])
|
2014-08-14 17:45:42 +04:00
|
|
|
associationForeignKey := SnakeToUpperCamel(settings["ASSOCIATIONFOREIGNKEY"])
|
|
|
|
many2many := settings["MANY2MANY"]
|
2014-11-26 08:23:43 +03:00
|
|
|
polymorphic := SnakeToUpperCamel(settings["POLYMORPHIC"])
|
|
|
|
|
|
|
|
if polymorphic != "" {
|
|
|
|
foreignKey = polymorphic + "Id"
|
|
|
|
foreignType = polymorphic + "Type"
|
|
|
|
}
|
2014-01-27 06:47:37 +04:00
|
|
|
|
2014-08-14 17:45:42 +04:00
|
|
|
switch indirectValue.Kind() {
|
|
|
|
case reflect.Slice:
|
|
|
|
typ = typ.Elem()
|
2014-07-30 17:14:10 +04:00
|
|
|
|
2015-01-30 13:24:56 +03:00
|
|
|
if field.IsScanner() {
|
|
|
|
field.IsNormal = true
|
|
|
|
} else if (typ.Kind() == reflect.Struct) && withRelation {
|
2014-07-31 07:08:26 +04:00
|
|
|
if foreignKey == "" {
|
|
|
|
foreignKey = scopeTyp.Name() + "Id"
|
|
|
|
}
|
2014-08-14 17:45:42 +04:00
|
|
|
if associationForeignKey == "" {
|
|
|
|
associationForeignKey = typ.Name() + "Id"
|
|
|
|
}
|
|
|
|
|
|
|
|
// if not many to many, foreign key could be null
|
|
|
|
if many2many == "" {
|
|
|
|
if !reflect.New(typ).Elem().FieldByName(foreignKey).IsValid() {
|
|
|
|
foreignKey = ""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
field.Relationship = &relationship{
|
|
|
|
JoinTable: many2many,
|
|
|
|
ForeignKey: foreignKey,
|
2014-11-26 08:23:43 +03:00
|
|
|
ForeignType: foreignType,
|
2014-08-14 17:45:42 +04:00
|
|
|
AssociationForeignKey: associationForeignKey,
|
|
|
|
Kind: "has_many",
|
|
|
|
}
|
|
|
|
|
|
|
|
if many2many != "" {
|
|
|
|
field.Relationship.Kind = "many_to_many"
|
|
|
|
}
|
2014-08-30 18:39:28 +04:00
|
|
|
} else {
|
|
|
|
field.IsNormal = true
|
2014-08-14 17:45:42 +04:00
|
|
|
}
|
|
|
|
case reflect.Struct:
|
2014-08-30 19:08:58 +04:00
|
|
|
if field.IsTime() || field.IsScanner() {
|
|
|
|
field.IsNormal = true
|
2014-09-26 17:35:04 +04:00
|
|
|
} else if _, ok := settings["EMBEDDED"]; ok || fieldStruct.Anonymous {
|
2014-08-28 14:25:05 +04:00
|
|
|
var fields []*Field
|
2014-09-01 10:15:29 +04:00
|
|
|
if field.Field.CanAddr() {
|
|
|
|
for _, field := range scope.New(field.Field.Addr().Interface()).Fields() {
|
|
|
|
field.DBName = field.DBName
|
|
|
|
fields = append(fields, field)
|
|
|
|
}
|
2014-08-28 14:25:05 +04:00
|
|
|
}
|
|
|
|
return fields
|
2014-09-01 13:03:58 +04:00
|
|
|
} else if withRelation {
|
2014-09-26 17:35:04 +04:00
|
|
|
var belongsToForeignKey, hasOneForeignKey, kind string
|
|
|
|
|
|
|
|
if foreignKey == "" {
|
|
|
|
belongsToForeignKey = field.Name + "Id"
|
|
|
|
hasOneForeignKey = scopeTyp.Name() + "Id"
|
2014-08-14 17:45:42 +04:00
|
|
|
} else {
|
2014-09-26 17:35:04 +04:00
|
|
|
belongsToForeignKey = foreignKey
|
|
|
|
hasOneForeignKey = foreignKey
|
2014-01-27 18:36:08 +04:00
|
|
|
}
|
2014-09-26 17:35:04 +04:00
|
|
|
|
|
|
|
if scope.HasColumn(belongsToForeignKey) {
|
|
|
|
foreignKey = belongsToForeignKey
|
|
|
|
kind = "belongs_to"
|
|
|
|
} else {
|
|
|
|
foreignKey = hasOneForeignKey
|
|
|
|
kind = "has_one"
|
|
|
|
}
|
|
|
|
|
2014-11-26 08:23:43 +03:00
|
|
|
field.Relationship = &relationship{ForeignKey: foreignKey, ForeignType: foreignType, Kind: kind}
|
2014-01-27 06:47:37 +04:00
|
|
|
}
|
2014-08-30 18:39:28 +04:00
|
|
|
default:
|
|
|
|
field.IsNormal = true
|
2014-01-27 06:47:37 +04:00
|
|
|
}
|
2014-07-30 10:58:00 +04:00
|
|
|
}
|
2014-08-28 13:21:43 +04:00
|
|
|
return []*Field{&field}
|
2014-07-30 10:58:00 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Fields get value's fields
|
2014-09-01 13:03:58 +04:00
|
|
|
func (scope *Scope) Fields(noRelations ...bool) map[string]*Field {
|
2014-11-28 00:59:49 +03:00
|
|
|
var withRelation = len(noRelations) == 0
|
|
|
|
|
|
|
|
if withRelation && scope.fields != nil {
|
2014-09-01 13:03:58 +04:00
|
|
|
return scope.fields
|
|
|
|
}
|
|
|
|
|
2014-08-28 14:25:05 +04:00
|
|
|
var fields = map[string]*Field{}
|
2014-09-01 13:03:58 +04:00
|
|
|
if scope.IndirectValue().IsValid() && scope.IndirectValue().Kind() == reflect.Struct {
|
2014-07-30 11:15:23 +04:00
|
|
|
scopeTyp := scope.IndirectValue().Type()
|
2014-08-30 18:39:28 +04:00
|
|
|
var hasPrimaryKey = false
|
2014-07-30 11:15:23 +04:00
|
|
|
for i := 0; i < scopeTyp.NumField(); i++ {
|
|
|
|
fieldStruct := scopeTyp.Field(i)
|
|
|
|
if !ast.IsExported(fieldStruct.Name) {
|
|
|
|
continue
|
|
|
|
}
|
2014-09-01 13:03:58 +04:00
|
|
|
for _, field := range scope.fieldFromStruct(fieldStruct, withRelation) {
|
2014-08-30 18:39:28 +04:00
|
|
|
if field.IsPrimaryKey {
|
|
|
|
hasPrimaryKey = true
|
|
|
|
}
|
2014-10-13 20:06:41 +04:00
|
|
|
if value, ok := fields[field.DBName]; ok {
|
|
|
|
if value.IsIgnored {
|
|
|
|
fields[field.DBName] = field
|
2015-01-23 03:59:05 +03:00
|
|
|
} else {
|
2014-10-13 20:06:41 +04:00
|
|
|
panic(fmt.Sprintf("Duplicated column name for %v (%v)\n", scope.typeName(), fileWithLineNum()))
|
|
|
|
}
|
2014-08-28 15:14:58 +04:00
|
|
|
} else {
|
|
|
|
fields[field.DBName] = field
|
|
|
|
}
|
2014-08-28 14:25:05 +04:00
|
|
|
}
|
2014-07-30 10:58:00 +04:00
|
|
|
}
|
2014-08-30 18:39:28 +04:00
|
|
|
|
|
|
|
if !hasPrimaryKey {
|
|
|
|
if field, ok := fields["id"]; ok {
|
|
|
|
field.IsPrimaryKey = true
|
|
|
|
}
|
|
|
|
}
|
2014-01-26 15:34:06 +04:00
|
|
|
}
|
2014-09-01 13:03:58 +04:00
|
|
|
|
2014-09-02 15:03:01 +04:00
|
|
|
if withRelation {
|
|
|
|
scope.fields = fields
|
|
|
|
}
|
2014-09-01 13:03:58 +04:00
|
|
|
|
2014-08-28 14:25:05 +04:00
|
|
|
return fields
|
2014-01-26 08:41:37 +04:00
|
|
|
}
|
|
|
|
|
2014-01-29 15:14:37 +04:00
|
|
|
// Raw set sql
|
2014-01-28 11:54:19 +04:00
|
|
|
func (scope *Scope) Raw(sql string) *Scope {
|
2014-01-26 10:18:21 +04:00
|
|
|
scope.Sql = strings.Replace(sql, "$$", "?", -1)
|
2014-01-28 11:54:19 +04:00
|
|
|
return scope
|
2014-01-26 08:41:37 +04:00
|
|
|
}
|
|
|
|
|
2014-01-29 15:14:37 +04:00
|
|
|
// Exec invoke sql
|
2014-01-28 06:23:31 +04:00
|
|
|
func (scope *Scope) Exec() *Scope {
|
2014-08-23 12:00:04 +04:00
|
|
|
defer scope.Trace(NowFunc())
|
2014-01-28 11:54:19 +04:00
|
|
|
|
2014-01-26 10:18:21 +04:00
|
|
|
if !scope.HasError() {
|
2014-06-05 13:58:14 +04:00
|
|
|
result, err := scope.DB().Exec(scope.Sql, scope.SqlVars...)
|
|
|
|
if scope.Err(err) == nil {
|
|
|
|
if count, err := result.RowsAffected(); err == nil {
|
|
|
|
scope.db.RowsAffected = count
|
|
|
|
}
|
|
|
|
}
|
2014-01-26 10:18:21 +04:00
|
|
|
}
|
2014-01-28 06:23:31 +04:00
|
|
|
return scope
|
2014-01-26 08:41:37 +04:00
|
|
|
}
|
2014-01-26 13:10:33 +04:00
|
|
|
|
2014-01-29 15:14:37 +04:00
|
|
|
// Set set value by name
|
2014-08-20 13:05:02 +04:00
|
|
|
func (scope *Scope) Set(name string, value interface{}) *Scope {
|
2014-08-25 13:10:46 +04:00
|
|
|
scope.db.InstantSet(name, value)
|
2014-08-20 13:05:02 +04:00
|
|
|
return scope
|
2014-01-27 07:56:04 +04:00
|
|
|
}
|
|
|
|
|
2014-01-29 15:14:37 +04:00
|
|
|
// Get get value by name
|
2014-08-20 12:25:01 +04:00
|
|
|
func (scope *Scope) Get(name string) (interface{}, bool) {
|
|
|
|
return scope.db.Get(name)
|
2014-01-29 15:14:37 +04:00
|
|
|
}
|
|
|
|
|
2014-08-20 13:05:02 +04:00
|
|
|
// InstanceId get InstanceId for scope
|
|
|
|
func (scope *Scope) InstanceId() string {
|
|
|
|
if scope.instanceId == "" {
|
2015-01-26 12:59:31 +03:00
|
|
|
scope.instanceId = fmt.Sprintf("%v%v", &scope, &scope.db)
|
2014-08-20 13:05:02 +04:00
|
|
|
}
|
|
|
|
return scope.instanceId
|
|
|
|
}
|
|
|
|
|
|
|
|
func (scope *Scope) InstanceSet(name string, value interface{}) *Scope {
|
|
|
|
return scope.Set(name+scope.InstanceId(), value)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (scope *Scope) InstanceGet(name string) (interface{}, bool) {
|
|
|
|
return scope.Get(name + scope.InstanceId())
|
|
|
|
}
|
|
|
|
|
2014-01-29 15:14:37 +04:00
|
|
|
// Trace print sql log
|
2014-01-26 13:10:33 +04:00
|
|
|
func (scope *Scope) Trace(t time.Time) {
|
|
|
|
if len(scope.Sql) > 0 {
|
|
|
|
scope.db.slog(scope.Sql, t, scope.SqlVars...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-29 15:14:37 +04:00
|
|
|
// Begin start a transaction
|
2014-01-26 13:10:33 +04:00
|
|
|
func (scope *Scope) Begin() *Scope {
|
2014-01-27 06:47:37 +04:00
|
|
|
if db, ok := scope.DB().(sqlDb); ok {
|
|
|
|
if tx, err := db.Begin(); err == nil {
|
|
|
|
scope.db.db = interface{}(tx).(sqlCommon)
|
2014-08-20 13:05:02 +04:00
|
|
|
scope.InstanceSet("gorm:started_transaction", true)
|
2014-01-27 06:47:37 +04:00
|
|
|
}
|
2014-01-26 13:10:33 +04:00
|
|
|
}
|
|
|
|
return scope
|
|
|
|
}
|
|
|
|
|
2014-01-29 15:14:37 +04:00
|
|
|
// CommitOrRollback commit current transaction if there is no error, otherwise rollback it
|
2014-01-26 13:10:33 +04:00
|
|
|
func (scope *Scope) CommitOrRollback() *Scope {
|
2014-08-20 13:05:02 +04:00
|
|
|
if _, ok := scope.InstanceGet("gorm:started_transaction"); ok {
|
2014-01-26 13:10:33 +04:00
|
|
|
if db, ok := scope.db.db.(sqlTx); ok {
|
|
|
|
if scope.HasError() {
|
|
|
|
db.Rollback()
|
|
|
|
} else {
|
|
|
|
db.Commit()
|
|
|
|
}
|
|
|
|
scope.db.db = scope.db.parent.db
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return scope
|
|
|
|
}
|