2014-01-26 08:41:37 +04:00
|
|
|
package gorm
|
|
|
|
|
2014-01-26 09:51:23 +04:00
|
|
|
import (
|
2014-01-28 05:48:44 +04:00
|
|
|
"database/sql"
|
2014-01-26 09:51:23 +04:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"github.com/jinzhu/gorm/dialect"
|
2014-01-26 15:34:06 +04:00
|
|
|
"go/ast"
|
2014-01-27 18:36:08 +04:00
|
|
|
"strconv"
|
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-28 12:29:42 +04:00
|
|
|
func (scope *Scope) Quote(str string) string {
|
|
|
|
return scope.Dialect().Quote(str)
|
|
|
|
}
|
|
|
|
|
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-27 18:36:08 +04:00
|
|
|
func (scope *Scope) SkipLeft() {
|
|
|
|
scope.skipLeft = true
|
|
|
|
}
|
|
|
|
|
2014-01-26 09:51:23 +04:00
|
|
|
func (scope *Scope) callCallbacks(funcs []*func(s *Scope)) *Scope {
|
|
|
|
for _, f := range funcs {
|
|
|
|
(*f)(scope)
|
2014-01-27 18:36:08 +04:00
|
|
|
if scope.skipLeft {
|
|
|
|
break
|
|
|
|
}
|
2014-01-26 09:51:23 +04:00
|
|
|
}
|
|
|
|
return scope
|
|
|
|
}
|
|
|
|
|
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}
|
|
|
|
}
|
|
|
|
|
|
|
|
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-26 08:41:37 +04:00
|
|
|
func (scope *Scope) DB() sqlCommon {
|
|
|
|
return scope.db.db
|
|
|
|
}
|
|
|
|
|
|
|
|
func (scope *Scope) Dialect() dialect.Dialect {
|
|
|
|
return scope.db.parent.dialect
|
|
|
|
}
|
|
|
|
|
|
|
|
func (scope *Scope) Err(err error) error {
|
|
|
|
if err != nil {
|
|
|
|
scope.db.err(err)
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2014-01-28 04:27:12 +04:00
|
|
|
func (scope *Scope) Log(v ...interface{}) {
|
|
|
|
scope.db.log(v...)
|
|
|
|
}
|
|
|
|
|
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) PrimaryKey() string {
|
2014-01-26 10:55:41 +04:00
|
|
|
return "id"
|
2014-01-26 08:41:37 +04:00
|
|
|
}
|
|
|
|
|
2014-01-26 13:10:33 +04:00
|
|
|
func (scope *Scope) PrimaryKeyZero() bool {
|
|
|
|
return isBlank(reflect.ValueOf(scope.PrimaryKeyValue()))
|
|
|
|
}
|
|
|
|
|
|
|
|
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-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
|
|
|
|
}
|
|
|
|
|
|
|
|
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-27 18:36:08 +04:00
|
|
|
func (scope *Scope) updatedAttrsWithValues(values map[string]interface{}, ignoreProtectedAttrs bool) (results map[string]interface{}, hasUpdate bool) {
|
|
|
|
data := reflect.Indirect(reflect.ValueOf(scope.Value))
|
|
|
|
if !data.CanAddr() {
|
|
|
|
return values, true
|
|
|
|
}
|
|
|
|
|
|
|
|
for key, value := range values {
|
|
|
|
if field := data.FieldByName(snakeToUpperCamel(key)); field.IsValid() {
|
|
|
|
if field.Interface() != value {
|
|
|
|
|
|
|
|
switch field.Kind() {
|
|
|
|
case reflect.Int, reflect.Int32, reflect.Int64:
|
|
|
|
if s, ok := value.(string); ok {
|
|
|
|
i, err := strconv.Atoi(s)
|
|
|
|
if scope.Err(err) == nil {
|
|
|
|
value = i
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
scope.db.log(field.Int() != reflect.ValueOf(value).Int())
|
|
|
|
if field.Int() != reflect.ValueOf(value).Int() {
|
|
|
|
hasUpdate = true
|
|
|
|
setFieldValue(field, value)
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
hasUpdate = true
|
|
|
|
setFieldValue(field, value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
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-26 10:55:41 +04:00
|
|
|
func (s *Scope) CombinedConditionSql() string {
|
|
|
|
return s.joinsSql() + s.whereSql() + s.groupSql() + s.havingSql() + s.orderSql() + s.limitSql() + s.offsetSql()
|
|
|
|
}
|
|
|
|
|
2014-01-26 15:34:06 +04:00
|
|
|
func (scope *Scope) SqlTagForField(field *Field) (tag string) {
|
|
|
|
value := field.Value
|
2014-01-28 13:09:43 +04:00
|
|
|
reflectValue := reflect.ValueOf(value)
|
2014-01-26 15:34:06 +04:00
|
|
|
|
|
|
|
if field.IsScanner() {
|
2014-01-28 13:09:43 +04:00
|
|
|
value = reflectValue.Field(0).Interface()
|
2014-01-26 15:34:06 +04:00
|
|
|
}
|
|
|
|
|
2014-01-28 13:09:43 +04:00
|
|
|
switch reflectValue.Kind() {
|
2014-01-26 15:34:06 +04:00
|
|
|
case reflect.Slice:
|
|
|
|
if _, ok := value.([]byte); !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
case reflect.Struct:
|
|
|
|
if !field.IsTime() && !field.IsScanner() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-28 11:54:19 +04:00
|
|
|
tag = field.Tag
|
|
|
|
if len(tag) == 0 && tag != "-" {
|
2014-01-26 15:34:06 +04:00
|
|
|
if field.isPrimaryKey {
|
|
|
|
tag = scope.Dialect().PrimaryKeyTag(value, field.Size)
|
|
|
|
} else {
|
|
|
|
tag = scope.Dialect().SqlTag(value, field.Size)
|
|
|
|
}
|
2014-01-28 11:54:19 +04:00
|
|
|
}
|
2014-01-26 15:34:06 +04:00
|
|
|
|
2014-01-28 11:54:19 +04:00
|
|
|
if len(field.AddationalTag) > 0 {
|
|
|
|
tag = tag + " " + field.AddationalTag
|
2014-01-26 15:34:06 +04:00
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
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-27 18:36:08 +04:00
|
|
|
if scope.db != nil {
|
|
|
|
tag, addationalTag, size := parseSqlTag(fieldStruct.Tag.Get(scope.db.parent.tagIdentifier))
|
|
|
|
field.Tag = tag
|
|
|
|
field.AddationalTag = addationalTag
|
2014-01-28 11:54:19 +04:00
|
|
|
field.isPrimaryKey = scope.PrimaryKey() == field.DBName
|
2014-01-27 18:36:08 +04:00
|
|
|
field.Size = size
|
2014-01-28 11:54:19 +04:00
|
|
|
|
2014-01-27 18:36:08 +04:00
|
|
|
field.SqlTag = scope.SqlTagForField(&field)
|
2014-01-26 15:34:06 +04:00
|
|
|
|
2014-01-27 18:36:08 +04:00
|
|
|
if tag == "-" {
|
|
|
|
field.IsIgnored = true
|
|
|
|
}
|
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-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-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-27 07:56:04 +04:00
|
|
|
func (scope *Scope) Get(name string) (value interface{}, ok bool) {
|
|
|
|
value, ok = scope._values[name]
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
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-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...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
2014-01-28 05:25:30 +04:00
|
|
|
|
2014-01-28 05:48:44 +04:00
|
|
|
func (scope *Scope) row() *sql.Row {
|
|
|
|
defer scope.Trace(time.Now())
|
|
|
|
scope.prepareQuerySql()
|
|
|
|
return scope.DB().QueryRow(scope.Sql, scope.SqlVars...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (scope *Scope) rows() (*sql.Rows, error) {
|
|
|
|
defer scope.Trace(time.Now())
|
|
|
|
scope.prepareQuerySql()
|
|
|
|
return scope.DB().Query(scope.Sql, scope.SqlVars...)
|
2014-01-28 05:25:30 +04:00
|
|
|
}
|
2014-01-28 06:01:53 +04:00
|
|
|
|
|
|
|
func (scope *Scope) initialize() *Scope {
|
2014-01-28 12:56:51 +04:00
|
|
|
for _, clause := range scope.Search.WhereConditions {
|
2014-01-28 06:01:53 +04:00
|
|
|
scope.updatedAttrsWithValues(convertInterfaceToMap(clause["query"]), false)
|
|
|
|
}
|
2014-01-28 12:56:51 +04:00
|
|
|
scope.updatedAttrsWithValues(convertInterfaceToMap(scope.Search.InitAttrs), false)
|
|
|
|
scope.updatedAttrsWithValues(convertInterfaceToMap(scope.Search.AssignAttrs), false)
|
2014-01-28 06:01:53 +04:00
|
|
|
return scope
|
|
|
|
}
|
2014-01-28 07:37:32 +04:00
|
|
|
|
|
|
|
func (scope *Scope) pluck(column string, value interface{}) *Scope {
|
|
|
|
dest := reflect.Indirect(reflect.ValueOf(value))
|
|
|
|
scope.Search = scope.Search.clone().selects(column)
|
|
|
|
if dest.Kind() != reflect.Slice {
|
|
|
|
scope.Err(errors.New("Results should be a slice"))
|
|
|
|
return scope
|
|
|
|
}
|
|
|
|
|
|
|
|
rows, err := scope.rows()
|
|
|
|
if scope.Err(err) == nil {
|
|
|
|
defer rows.Close()
|
|
|
|
for rows.Next() {
|
|
|
|
elem := reflect.New(dest.Type().Elem()).Interface()
|
|
|
|
scope.Err(rows.Scan(elem))
|
|
|
|
dest.Set(reflect.Append(dest, reflect.ValueOf(elem).Elem()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return scope
|
|
|
|
}
|
2014-01-28 07:38:53 +04:00
|
|
|
|
|
|
|
func (scope *Scope) count(value interface{}) *Scope {
|
|
|
|
scope.Search = scope.Search.clone().selects("count(*)")
|
|
|
|
scope.Err(scope.row().Scan(value))
|
|
|
|
return scope
|
|
|
|
}
|
2014-01-28 08:28:44 +04:00
|
|
|
|
|
|
|
func (scope *Scope) typeName() string {
|
|
|
|
value := reflect.Indirect(reflect.ValueOf(scope.Value))
|
|
|
|
if value.Kind() == reflect.Slice {
|
|
|
|
return value.Type().Elem().Name()
|
|
|
|
} else {
|
|
|
|
return value.Type().Name()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (scope *Scope) related(value interface{}, foreignKeys ...string) *Scope {
|
|
|
|
toScope := scope.New(value)
|
|
|
|
|
|
|
|
for _, foreignKey := range append(foreignKeys, toScope.typeName()+"Id", scope.typeName()+"Id") {
|
|
|
|
if foreignValue, ok := scope.FieldByName(foreignKey); ok {
|
|
|
|
return toScope.inlineCondition(foreignValue).callCallbacks(scope.db.parent.callback.queries)
|
|
|
|
} else if toScope.HasColumn(foreignKey) {
|
2014-01-28 12:22:41 +04:00
|
|
|
sql := fmt.Sprintf("%v = ?", scope.Quote(toSnake(foreignKey)))
|
2014-01-28 08:28:44 +04:00
|
|
|
return toScope.inlineCondition(sql, scope.PrimaryKeyValue()).callCallbacks(scope.db.parent.callback.queries)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return scope
|
|
|
|
}
|
2014-01-28 13:09:43 +04:00
|
|
|
|
|
|
|
func (scope *Scope) createTable() *Scope {
|
|
|
|
var sqls []string
|
|
|
|
for _, field := range scope.Fields() {
|
|
|
|
if !field.IsIgnored && len(field.SqlTag) > 0 {
|
|
|
|
sqls = append(sqls, scope.Quote(field.DBName)+" "+field.SqlTag)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
scope.Raw(fmt.Sprintf("CREATE TABLE %v (%v)", scope.TableName(), strings.Join(sqls, ","))).Exec()
|
|
|
|
return scope
|
|
|
|
}
|
|
|
|
|
|
|
|
func (scope *Scope) dropTable() *Scope {
|
|
|
|
scope.Raw(fmt.Sprintf("DROP TABLE %v", scope.TableName())).Exec()
|
|
|
|
return scope
|
|
|
|
}
|
|
|
|
|
|
|
|
func (scope *Scope) modifyColumn(column string, typ string) {
|
|
|
|
scope.Raw(fmt.Sprintf("ALTER TABLE %v MODIFY %v %v", scope.TableName(), scope.Quote(column), typ)).Exec()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (scope *Scope) dropColumn(column string) {
|
|
|
|
scope.Raw(fmt.Sprintf("ALTER TABLE %v DROP COLUMN %v", scope.TableName(), scope.Quote(column))).Exec()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (scope *Scope) addIndex(column string, names ...string) {
|
|
|
|
var indexName string
|
|
|
|
if len(names) > 0 {
|
|
|
|
indexName = names[0]
|
|
|
|
} else {
|
|
|
|
indexName = fmt.Sprintf("index_%v_on_%v", scope.TableName(), column)
|
|
|
|
}
|
|
|
|
|
|
|
|
scope.Raw(fmt.Sprintf("CREATE INDEX %v ON %v(%v);", indexName, scope.TableName(), scope.Quote(column))).Exec()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (scope *Scope) removeIndex(indexName string) {
|
|
|
|
scope.Raw(fmt.Sprintf("DROP INDEX %v ON %v", indexName, scope.TableName())).Exec()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (scope *Scope) autoMigrate() *Scope {
|
|
|
|
var tableName string
|
|
|
|
scope.Raw(fmt.Sprintf("SELECT table_name FROM INFORMATION_SCHEMA.tables where table_name = %v", scope.AddToVars(scope.TableName())))
|
|
|
|
scope.DB().QueryRow(scope.Sql, scope.SqlVars...).Scan(&tableName)
|
|
|
|
scope.SqlVars = []interface{}{}
|
|
|
|
|
|
|
|
// If table doesn't exist
|
|
|
|
if len(tableName) == 0 {
|
|
|
|
scope.createTable()
|
|
|
|
} else {
|
|
|
|
for _, field := range scope.Fields() {
|
|
|
|
var column, data string
|
|
|
|
scope.Raw(fmt.Sprintf("SELECT column_name, data_type FROM information_schema.columns WHERE table_name = %v and column_name = %v",
|
|
|
|
scope.AddToVars(scope.TableName()),
|
|
|
|
scope.AddToVars(field.DBName),
|
|
|
|
))
|
|
|
|
scope.DB().QueryRow(scope.Sql, scope.SqlVars...).Scan(&column, &data)
|
|
|
|
scope.SqlVars = []interface{}{}
|
|
|
|
|
|
|
|
// If column doesn't exist
|
|
|
|
if len(column) == 0 && len(field.SqlTag) > 0 && !field.IsIgnored {
|
|
|
|
scope.Raw(fmt.Sprintf("ALTER TABLE %v ADD %v %v;", scope.TableName(), field.DBName, field.SqlTag)).Exec()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return scope
|
|
|
|
}
|