gorm/scope.go

392 lines
9.4 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"
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 {
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
}
func (scope *Scope) NeedPtr() *Scope {
reflectKind := reflect.ValueOf(scope.Value).Kind()
if !((reflectKind == reflect.Invalid) || (reflectKind == reflect.Ptr)) {
err := errors.New(fmt.Sprintf("%v %v\n", fileWithLineNum(), "using unaddressable value"))
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 {
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
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
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
}
func (scope *Scope) PrimaryKeyField() *Field {
if scope.primaryKeyField == nil {
var indirectValue = scope.IndirectValue()
2014-08-30 18:39:28 +04:00
clone := scope
2015-02-11 12:58:19 +03:00
// FIXME
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()
}
clone = scope.New(reflect.New(typ).Elem().Addr().Interface())
}
2014-08-30 18:39:28 +04:00
for _, field := range clone.Fields() {
if field.IsPrimaryKey {
scope.primaryKeyField = field
break
}
2014-08-30 18:39:28 +04: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
} else {
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{} {
if field := scope.PrimaryKeyField(); field != nil {
return field.Field.Interface()
} else {
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 {
clone := scope
if scope.IndirectValue().Kind() == reflect.Slice {
value := reflect.New(scope.IndirectValue().Type().Elem()).Interface()
clone = scope.New(value)
}
dbName := ToSnake(column)
2015-02-15 18:01:09 +03:00
field, hasColumn := clone.Fields()[dbName]
2015-01-19 11:23:33 +03:00
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
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
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 {
return errors.New("scope value must not be nil for string columns")
2014-09-02 15:03:01 +04:00
}
2015-01-16 05:02:04 +03:00
if field, ok := scope.Fields()[dbName]; ok {
return field.Set(value)
}
dbName = ToSnake(dbName)
if field, ok := scope.Fields()[dbName]; ok {
return field.Set(value)
2014-08-30 18:39:28 +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() {
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:
scope.Err(errors.New(fmt.Sprintf("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
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
2014-07-30 10:58:00 +04:00
data := scope.IndirectValue()
2014-01-26 10:18:21 +04:00
if data.Kind() == reflect.Slice {
2014-07-08 06:45:31 +04:00
elem := data.Type().Elem()
if elem.Kind() == reflect.Ptr {
elem = elem.Elem()
}
data = reflect.New(elem).Elem()
2014-01-26 10:18:21 +04: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
}
}
}
str := ToSnake(data.Type().Name())
2014-01-26 10:18:21 +04:00
2014-08-20 09:46:10 +04:00
if scope.db == nil || !scope.db.parent.singularTable {
2014-07-02 07:56:37 +04:00
for index, reg := range pluralMapKeys {
2014-01-26 10:18:21 +04:00
if reg.MatchString(str) {
2014-07-02 07:56:37 +04:00
return reg.ReplaceAllString(str, pluralMapValues[index])
2014-01-26 10:18:21 +04:00
}
}
}
return str
}
}
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
} else {
2014-06-10 09:08:19 +04: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-07-30 10:58:00 +04:00
// Fields get value's fields
2015-02-15 18:01:09 +03:00
func (scope *Scope) Fields() map[string]*Field {
fields := map[string]*Field{}
structFields := scope.GetStructFields()
for _, structField := range structFields {
field := Field{StructField: structField}
fields[field.DBName] = &field
2014-09-02 15:03:01 +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 {
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
func (scope *Scope) Set(name string, value interface{}) *Scope {
2014-08-25 13:10:46 +04:00
scope.db.InstantSet(name, value)
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
}
// 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)
}
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)
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 {
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
}