gorm/scope.go

344 lines
8.3 KiB
Go
Raw Normal View History

2014-01-26 08:41:37 +04:00
package gorm
2014-01-26 09:51:23 +04:00
import (
"errors"
"fmt"
"github.com/jinzhu/gorm/dialect"
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-01-27 18:36:08 +04:00
Value interface{}
Search *search
Sql string
SqlVars []interface{}
db *DB
_values map[string]interface{}
skipLeft bool
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-01-27 07:56:04 +04:00
return &Scope{db: db, Search: db.search, Value: value, _values: map[string]interface{}{}}
2014-01-26 09:51:23 +04:00
}
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 {
return &Scope{db: scope.db.parent, Search: &search{}, Value: value}
}
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 {
2014-01-27 06:47:37 +04: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-01-26 08:41:37 +04:00
func (scope *Scope) Dialect() dialect.Dialect {
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
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-01-29 15:14:37 +04:00
// PrimaryKey get the primary key's column name
2014-01-26 08:41:37 +04:00
func (scope *Scope) PrimaryKey() string {
2014-01-26 10:55:41 +04:00
return "id"
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{} {
data := reflect.Indirect(reflect.ValueOf(scope.Value))
if data.Kind() == reflect.Struct {
if field := data.FieldByName(snakeToUpperCamel(scope.PrimaryKey())); field.IsValid() {
return field.Interface()
}
}
return 0
}
2014-01-29 15:14:37 +04:00
// HasColumn to check if has column
2014-01-26 08:41:37 +04:00
func (scope *Scope) HasColumn(name string) bool {
2014-01-28 08:28:44 +04:00
_, result := scope.FieldByName(name)
return result
}
2014-01-29 15:14:37 +04:00
// FieldByName to get column's value and existence
2014-01-28 08:28:44 +04:00
func (scope *Scope) FieldByName(name string) (interface{}, bool) {
2014-01-26 09:51:23 +04:00
data := reflect.Indirect(reflect.ValueOf(scope.Value))
if data.Kind() == reflect.Struct {
2014-01-28 08:28:44 +04:00
if field := data.FieldByName(name); field.IsValid() {
return field.Interface(), true
}
2014-01-26 09:51:23 +04:00
} else if data.Kind() == reflect.Slice {
2014-01-28 08:28:44 +04:00
return nil, reflect.New(data.Type().Elem()).Elem().FieldByName(name).IsValid()
2014-01-26 09:51:23 +04:00
}
2014-01-28 08:28:44 +04:00
return nil, false
2014-01-26 08:41:37 +04:00
}
2014-01-29 15:14:37 +04:00
// SetColumn to set the column's value
2014-01-26 08:41:37 +04:00
func (scope *Scope) SetColumn(column string, value interface{}) {
2014-01-27 18:36:08 +04:00
if scope.Value == nil {
return
}
2014-01-26 09:51:23 +04:00
data := reflect.Indirect(reflect.ValueOf(scope.Value))
setFieldValue(data.FieldByName(snakeToUpperCamel(column)), value)
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() {
fi := fm.Interface()
if f, ok := fi.(func()); ok {
f()
} else if f, ok := fi.(func(s *Scope)); ok {
f(scope)
} else if f, ok := fi.(func(s *DB)); ok {
f(scope.db.new())
} else if f, ok := fi.(func() error); ok {
scope.Err(f())
} else if f, ok := fi.(func(s *Scope) error); ok {
scope.Err(f(scope))
} else if f, ok := fi.(func(s *DB) error); ok {
scope.Err(f(scope.db.new()))
} else {
scope.Err(errors.New(fmt.Sprintf("unsupported function %v", name)))
}
2014-01-26 09:51:23 +04:00
}
}
2014-01-28 05:25:30 +04:00
if values := reflect.Indirect(reflect.ValueOf(scope.Value)); values.Kind() == reflect.Slice {
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-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
2014-01-26 10:18:21 +04:00
} else {
2014-01-28 07:37:32 +04:00
if scope.Value == nil {
scope.Err(errors.New("can't get table name"))
return ""
}
2014-01-26 10:18:21 +04:00
data := reflect.Indirect(reflect.ValueOf(scope.Value))
if data.Kind() == reflect.Slice {
data = reflect.New(data.Type().Elem()).Elem()
}
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
}
}
}
str := toSnake(data.Type().Name())
if !scope.db.parent.singularTable {
pluralMap := map[string]string{"ch": "ches", "ss": "sses", "sh": "shes", "day": "days", "y": "ies", "x": "xes", "s?": "s"}
for key, value := range pluralMap {
reg := regexp.MustCompile(key + "$")
if reg.MatchString(str) {
return reg.ReplaceAllString(str, value)
}
}
}
return str
}
}
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-01-29 15:14:37 +04:00
// Fields get value's fields
2014-01-26 10:55:41 +04:00
func (scope *Scope) Fields() []*Field {
2014-01-27 18:36:08 +04:00
indirectValue := reflect.Indirect(reflect.ValueOf(scope.Value))
2014-01-26 15:34:06 +04:00
fields := []*Field{}
2014-01-27 18:36:08 +04:00
if !indirectValue.IsValid() {
2014-01-26 15:34:06 +04:00
return fields
}
2014-01-27 18:36:08 +04:00
scopeTyp := indirectValue.Type()
for i := 0; i < scopeTyp.NumField(); i++ {
fieldStruct := scopeTyp.Field(i)
if fieldStruct.Anonymous || !ast.IsExported(fieldStruct.Name) {
2014-01-26 15:34:06 +04:00
continue
}
var field Field
2014-01-27 18:36:08 +04:00
field.Name = fieldStruct.Name
field.DBName = toSnake(fieldStruct.Name)
2014-01-26 15:34:06 +04:00
2014-01-27 18:36:08 +04:00
value := indirectValue.FieldByName(fieldStruct.Name)
2014-01-26 15:34:06 +04:00
field.Value = value.Interface()
field.IsBlank = isBlank(value)
2014-01-29 06:35:28 +04:00
field.isPrimaryKey = scope.PrimaryKey() == field.DBName
2014-01-26 15:34:06 +04:00
2014-01-27 18:36:08 +04:00
if scope.db != nil {
2014-01-29 06:35:28 +04:00
field.Tag = fieldStruct.Tag
2014-01-29 08:00:57 +04:00
field.SqlTag = scope.sqlTagForField(&field)
2014-01-26 15:34:06 +04:00
2014-01-27 18:36:08 +04:00
// parse association
elem := reflect.Indirect(value)
typ := elem.Type()
2014-01-27 06:47:37 +04:00
2014-01-27 18:36:08 +04:00
switch elem.Kind() {
case reflect.Slice:
typ = typ.Elem()
2014-01-27 06:47:37 +04:00
2014-01-27 18:36:08 +04:00
if _, ok := field.Value.([]byte); !ok {
foreignKey := scopeTyp.Name() + "Id"
2014-01-27 06:47:37 +04:00
if reflect.New(typ).Elem().FieldByName(foreignKey).IsValid() {
field.ForeignKey = foreignKey
}
field.AfterAssociation = true
}
2014-01-27 18:36:08 +04:00
case reflect.Struct:
if !field.IsTime() && !field.IsScanner() {
if scope.HasColumn(field.Name + "Id") {
field.ForeignKey = field.Name + "Id"
field.BeforeAssociation = true
} else {
foreignKey := scopeTyp.Name() + "Id"
if reflect.New(typ).Elem().FieldByName(foreignKey).IsValid() {
field.ForeignKey = foreignKey
}
field.AfterAssociation = true
}
}
2014-01-27 06:47:37 +04:00
}
}
2014-01-26 15:34:06 +04:00
fields = append(fields, &field)
}
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-01-28 11:54:19 +04:00
defer scope.Trace(time.Now())
2014-01-26 10:18:21 +04:00
if !scope.HasError() {
_, err := scope.DB().Exec(scope.Sql, scope.SqlVars...)
scope.Err(err)
}
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-01-27 18:36:08 +04:00
func (scope *Scope) Set(name string, value interface{}) *Scope {
2014-01-27 07:56:04 +04:00
scope._values[name] = value
2014-01-27 18:36:08 +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
func (scope *Scope) Get(name string) (value interface{}, ok bool) {
value, ok = scope._values[name]
return
}
// 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-01-27 07:56:04 +04:00
scope.Set("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-01-27 07:56:04 +04:00
if _, ok := scope.Get("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
}