add ks-iam and ks-apigateway

Signed-off-by: hongming <talonwan@yunify.com>
This commit is contained in:
hongming
2019-03-08 11:09:05 +08:00
parent f579e97f6b
commit b59c244ca2
715 changed files with 108638 additions and 23446 deletions

View File

@@ -267,15 +267,16 @@ func (association *Association) Count() int {
query = scope.DB()
)
if relationship.Kind == "many_to_many" {
switch relationship.Kind {
case "many_to_many":
query = relationship.JoinTableHandler.JoinWith(relationship.JoinTableHandler, query, scope.Value)
} else if relationship.Kind == "has_many" || relationship.Kind == "has_one" {
case "has_many", "has_one":
primaryKeys := scope.getColumnAsArray(relationship.AssociationForeignFieldNames, scope.Value)
query = query.Where(
fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relationship.ForeignDBNames), toQueryMarks(primaryKeys)),
toQueryValues(primaryKeys)...,
)
} else if relationship.Kind == "belongs_to" {
case "belongs_to":
primaryKeys := scope.getColumnAsArray(relationship.ForeignFieldNames, scope.Value)
query = query.Where(
fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relationship.AssociationForeignDBNames), toQueryMarks(primaryKeys)),
@@ -367,6 +368,7 @@ func (association *Association) saveAssociations(values ...interface{}) *Associa
return association
}
// setErr set error when the error is not nil. And return Association.
func (association *Association) setErr(err error) *Association {
if err != nil {
association.Error = err

View File

@@ -59,7 +59,7 @@ func createCallback(scope *Scope) {
for _, field := range scope.Fields() {
if scope.changeableField(field) {
if field.IsNormal {
if field.IsNormal && !field.IsIgnored {
if field.IsBlank && field.HasDefaultValue {
blankColumnsWithDefaultValue = append(blankColumnsWithDefaultValue, scope.Quote(field.DBName))
scope.InstanceSet("gorm:blank_columns_with_default_value", blankColumnsWithDefaultValue)

View File

@@ -18,6 +18,11 @@ func queryCallback(scope *Scope) {
if _, skip := scope.InstanceGet("gorm:skip_query_callback"); skip {
return
}
//we are only preloading relations, dont touch base model
if _, skip := scope.InstanceGet("gorm:only_preload"); skip {
return
}
defer scope.trace(NowFunc())

View File

@@ -14,8 +14,14 @@ func preloadCallback(scope *Scope) {
return
}
if _, ok := scope.Get("gorm:auto_preload"); ok {
autoPreload(scope)
if ap, ok := scope.Get("gorm:auto_preload"); ok {
// If gorm:auto_preload IS NOT a bool then auto preload.
// Else if it IS a bool, use the value
if apb, ok := ap.(bool); !ok {
autoPreload(scope)
} else if apb {
autoPreload(scope)
}
}
if scope.Search.preload == nil || scope.HasError() {
@@ -94,7 +100,7 @@ func autoPreload(scope *Scope) {
continue
}
if val, ok := field.TagSettings["PRELOAD"]; ok {
if val, ok := field.TagSettingsGet("PRELOAD"); ok {
if preload, err := strconv.ParseBool(val); err != nil {
scope.Err(errors.New("invalid preload option"))
return
@@ -155,14 +161,17 @@ func (scope *Scope) handleHasOnePreload(field *Field, conditions []interface{})
)
if indirectScopeValue.Kind() == reflect.Slice {
foreignValuesToResults := make(map[string]reflect.Value)
for i := 0; i < resultsValue.Len(); i++ {
result := resultsValue.Index(i)
foreignValues := toString(getValueFromFields(result, relation.ForeignFieldNames))
foreignValuesToResults[foreignValues] = result
}
for j := 0; j < indirectScopeValue.Len(); j++ {
for i := 0; i < resultsValue.Len(); i++ {
result := resultsValue.Index(i)
foreignValues := getValueFromFields(result, relation.ForeignFieldNames)
if indirectValue := indirect(indirectScopeValue.Index(j)); equalAsString(getValueFromFields(indirectValue, relation.AssociationForeignFieldNames), foreignValues) {
indirectValue.FieldByName(field.Name).Set(result)
break
}
indirectValue := indirect(indirectScopeValue.Index(j))
valueString := toString(getValueFromFields(indirectValue, relation.AssociationForeignFieldNames))
if result, found := foreignValuesToResults[valueString]; found {
indirectValue.FieldByName(field.Name).Set(result)
}
}
} else {
@@ -249,13 +258,21 @@ func (scope *Scope) handleBelongsToPreload(field *Field, conditions []interface{
indirectScopeValue = scope.IndirectValue()
)
foreignFieldToObjects := make(map[string][]*reflect.Value)
if indirectScopeValue.Kind() == reflect.Slice {
for j := 0; j < indirectScopeValue.Len(); j++ {
object := indirect(indirectScopeValue.Index(j))
valueString := toString(getValueFromFields(object, relation.ForeignFieldNames))
foreignFieldToObjects[valueString] = append(foreignFieldToObjects[valueString], &object)
}
}
for i := 0; i < resultsValue.Len(); i++ {
result := resultsValue.Index(i)
if indirectScopeValue.Kind() == reflect.Slice {
value := getValueFromFields(result, relation.AssociationForeignFieldNames)
for j := 0; j < indirectScopeValue.Len(); j++ {
object := indirect(indirectScopeValue.Index(j))
if equalAsString(getValueFromFields(object, relation.ForeignFieldNames), value) {
valueString := toString(getValueFromFields(result, relation.AssociationForeignFieldNames))
if objects, found := foreignFieldToObjects[valueString]; found {
for _, object := range objects {
object.FieldByName(field.Name).Set(result)
}
}

View File

@@ -21,9 +21,7 @@ func saveAssociationCheck(scope *Scope, field *Field) (autoUpdate bool, autoCrea
if v, ok := value.(string); ok {
v = strings.ToLower(v)
if v == "false" || v != "skip" {
return false
}
return v == "true"
}
return true
@@ -36,26 +34,28 @@ func saveAssociationCheck(scope *Scope, field *Field) (autoUpdate bool, autoCrea
if value, ok := scope.Get("gorm:save_associations"); ok {
autoUpdate = checkTruth(value)
autoCreate = autoUpdate
} else if value, ok := field.TagSettings["SAVE_ASSOCIATIONS"]; ok {
saveReference = autoUpdate
} else if value, ok := field.TagSettingsGet("SAVE_ASSOCIATIONS"); ok {
autoUpdate = checkTruth(value)
autoCreate = autoUpdate
saveReference = autoUpdate
}
if value, ok := scope.Get("gorm:association_autoupdate"); ok {
autoUpdate = checkTruth(value)
} else if value, ok := field.TagSettings["ASSOCIATION_AUTOUPDATE"]; ok {
} else if value, ok := field.TagSettingsGet("ASSOCIATION_AUTOUPDATE"); ok {
autoUpdate = checkTruth(value)
}
if value, ok := scope.Get("gorm:association_autocreate"); ok {
autoCreate = checkTruth(value)
} else if value, ok := field.TagSettings["ASSOCIATION_AUTOCREATE"]; ok {
} else if value, ok := field.TagSettingsGet("ASSOCIATION_AUTOCREATE"); ok {
autoCreate = checkTruth(value)
}
if value, ok := scope.Get("gorm:association_save_reference"); ok {
saveReference = checkTruth(value)
} else if value, ok := field.TagSettings["ASSOCIATION_SAVE_REFERENCE"]; ok {
} else if value, ok := field.TagSettingsGet("ASSOCIATION_SAVE_REFERENCE"); ok {
saveReference = checkTruth(value)
}
}

View File

@@ -76,7 +76,9 @@ func updateCallback(scope *Scope) {
for _, field := range scope.Fields() {
if scope.changeableField(field) {
if !field.IsPrimaryKey && field.IsNormal {
sqls = append(sqls, fmt.Sprintf("%v = %v", scope.Quote(field.DBName), scope.AddToVars(field.Field.Interface())))
if !field.IsForeignKey || !field.IsBlank || !field.HasDefaultValue {
sqls = append(sqls, fmt.Sprintf("%v = %v", scope.Quote(field.DBName), scope.AddToVars(field.Field.Interface())))
}
} else if relationship := field.Relationship; relationship != nil && relationship.Kind == "belongs_to" {
for _, foreignKey := range relationship.ForeignDBNames {
if foreignField, ok := scope.FieldByName(foreignKey); ok && !scope.changeableField(foreignField) {

View File

@@ -72,12 +72,18 @@ func RegisterDialect(name string, dialect Dialect) {
dialectsMap[name] = dialect
}
// GetDialect gets the dialect for the specified dialect name
func GetDialect(name string) (dialect Dialect, ok bool) {
dialect, ok = dialectsMap[name]
return
}
// ParseFieldStructForDialect get field's sql data type
var ParseFieldStructForDialect = func(field *StructField, dialect Dialect) (fieldValue reflect.Value, sqlType string, size int, additionalType string) {
// Get redirected field type
var (
reflectType = field.Struct.Type
dataType = field.TagSettings["TYPE"]
dataType, _ = field.TagSettingsGet("TYPE")
)
for reflectType.Kind() == reflect.Ptr {
@@ -106,15 +112,17 @@ var ParseFieldStructForDialect = func(field *StructField, dialect Dialect) (fiel
}
// Default Size
if num, ok := field.TagSettings["SIZE"]; ok {
if num, ok := field.TagSettingsGet("SIZE"); ok {
size, _ = strconv.Atoi(num)
} else {
size = 255
}
// Default type from tag setting
additionalType = field.TagSettings["NOT NULL"] + " " + field.TagSettings["UNIQUE"]
if value, ok := field.TagSettings["DEFAULT"]; ok {
notNull, _ := field.TagSettingsGet("NOT NULL")
unique, _ := field.TagSettingsGet("UNIQUE")
additionalType = notNull + " " + unique
if value, ok := field.TagSettingsGet("DEFAULT"); ok {
additionalType = additionalType + " DEFAULT " + value
}

View File

@@ -39,7 +39,7 @@ func (commonDialect) Quote(key string) string {
}
func (s *commonDialect) fieldCanAutoIncrement(field *StructField) bool {
if value, ok := field.TagSettings["AUTO_INCREMENT"]; ok {
if value, ok := field.TagSettingsGet("AUTO_INCREMENT"); ok {
return strings.ToLower(value) != "false"
}
return field.IsPrimaryKey

View File

@@ -33,9 +33,9 @@ func (s *mysql) DataTypeOf(field *StructField) string {
// MySQL allows only one auto increment column per table, and it must
// be a KEY column.
if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok {
if _, ok = field.TagSettings["INDEX"]; !ok && !field.IsPrimaryKey {
delete(field.TagSettings, "AUTO_INCREMENT")
if _, ok := field.TagSettingsGet("AUTO_INCREMENT"); ok {
if _, ok = field.TagSettingsGet("INDEX"); !ok && !field.IsPrimaryKey {
field.TagSettingsDelete("AUTO_INCREMENT")
}
}
@@ -45,42 +45,42 @@ func (s *mysql) DataTypeOf(field *StructField) string {
sqlType = "boolean"
case reflect.Int8:
if s.fieldCanAutoIncrement(field) {
field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
field.TagSettingsSet("AUTO_INCREMENT", "AUTO_INCREMENT")
sqlType = "tinyint AUTO_INCREMENT"
} else {
sqlType = "tinyint"
}
case reflect.Int, reflect.Int16, reflect.Int32:
if s.fieldCanAutoIncrement(field) {
field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
field.TagSettingsSet("AUTO_INCREMENT", "AUTO_INCREMENT")
sqlType = "int AUTO_INCREMENT"
} else {
sqlType = "int"
}
case reflect.Uint8:
if s.fieldCanAutoIncrement(field) {
field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
field.TagSettingsSet("AUTO_INCREMENT", "AUTO_INCREMENT")
sqlType = "tinyint unsigned AUTO_INCREMENT"
} else {
sqlType = "tinyint unsigned"
}
case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uintptr:
if s.fieldCanAutoIncrement(field) {
field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
field.TagSettingsSet("AUTO_INCREMENT", "AUTO_INCREMENT")
sqlType = "int unsigned AUTO_INCREMENT"
} else {
sqlType = "int unsigned"
}
case reflect.Int64:
if s.fieldCanAutoIncrement(field) {
field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
field.TagSettingsSet("AUTO_INCREMENT", "AUTO_INCREMENT")
sqlType = "bigint AUTO_INCREMENT"
} else {
sqlType = "bigint"
}
case reflect.Uint64:
if s.fieldCanAutoIncrement(field) {
field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
field.TagSettingsSet("AUTO_INCREMENT", "AUTO_INCREMENT")
sqlType = "bigint unsigned AUTO_INCREMENT"
} else {
sqlType = "bigint unsigned"
@@ -96,11 +96,11 @@ func (s *mysql) DataTypeOf(field *StructField) string {
case reflect.Struct:
if _, ok := dataValue.Interface().(time.Time); ok {
precision := ""
if p, ok := field.TagSettings["PRECISION"]; ok {
if p, ok := field.TagSettingsGet("PRECISION"); ok {
precision = fmt.Sprintf("(%s)", p)
}
if _, ok := field.TagSettings["NOT NULL"]; ok {
if _, ok := field.TagSettingsGet("NOT NULL"); ok {
sqlType = fmt.Sprintf("timestamp%v", precision)
} else {
sqlType = fmt.Sprintf("timestamp%v NULL", precision)

View File

@@ -34,14 +34,14 @@ func (s *postgres) DataTypeOf(field *StructField) string {
sqlType = "boolean"
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uintptr:
if s.fieldCanAutoIncrement(field) {
field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
field.TagSettingsSet("AUTO_INCREMENT", "AUTO_INCREMENT")
sqlType = "serial"
} else {
sqlType = "integer"
}
case reflect.Int64, reflect.Uint32, reflect.Uint64:
if s.fieldCanAutoIncrement(field) {
field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
field.TagSettingsSet("AUTO_INCREMENT", "AUTO_INCREMENT")
sqlType = "bigserial"
} else {
sqlType = "bigint"
@@ -49,7 +49,7 @@ func (s *postgres) DataTypeOf(field *StructField) string {
case reflect.Float32, reflect.Float64:
sqlType = "numeric"
case reflect.String:
if _, ok := field.TagSettings["SIZE"]; !ok {
if _, ok := field.TagSettingsGet("SIZE"); !ok {
size = 0 // if SIZE haven't been set, use `text` as the default type, as there are no performance different
}

View File

@@ -29,14 +29,14 @@ func (s *sqlite3) DataTypeOf(field *StructField) string {
sqlType = "bool"
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uintptr:
if s.fieldCanAutoIncrement(field) {
field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
field.TagSettingsSet("AUTO_INCREMENT", "AUTO_INCREMENT")
sqlType = "integer primary key autoincrement"
} else {
sqlType = "integer"
}
case reflect.Int64, reflect.Uint64:
if s.fieldCanAutoIncrement(field) {
field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
field.TagSettingsSet("AUTO_INCREMENT", "AUTO_INCREMENT")
sqlType = "integer primary key autoincrement"
} else {
sqlType = "bigint"

View File

@@ -6,7 +6,7 @@ import (
)
var (
// ErrRecordNotFound record not found error, happens when haven't find any matched data when looking up with a struct
// ErrRecordNotFound record not found error, happens when only haven't find any matched data when looking up with a struct, finding a slice won't return this error
ErrRecordNotFound = errors.New("record not found")
// ErrInvalidSQL invalid SQL error, happens when you passed invalid SQL
ErrInvalidSQL = errors.New("invalid SQL")

View File

@@ -2,6 +2,7 @@ package gorm
import (
"database/sql"
"database/sql/driver"
"errors"
"fmt"
"reflect"
@@ -44,7 +45,14 @@ func (field *Field) Set(value interface{}) (err error) {
if reflectValue.Type().ConvertibleTo(fieldValue.Type()) {
fieldValue.Set(reflectValue.Convert(fieldValue.Type()))
} else if scanner, ok := fieldValue.Addr().Interface().(sql.Scanner); ok {
err = scanner.Scan(reflectValue.Interface())
v := reflectValue.Interface()
if valuer, ok := v.(driver.Valuer); ok {
if v, err = valuer.Value(); err == nil {
err = scanner.Scan(v)
}
} else {
err = scanner.Scan(v)
}
} else {
err = fmt.Errorf("could not convert argument of field %s from %s to %s", field.Name, reflectValue.Type(), fieldValue.Type())
}

View File

@@ -6,6 +6,7 @@ import (
"fmt"
"reflect"
"strings"
"sync"
"time"
)
@@ -21,7 +22,7 @@ type DB struct {
logMode int
logger logger
search *search
values map[string]interface{}
values sync.Map
// global db
parent *DB
@@ -48,6 +49,7 @@ func Open(dialect string, args ...interface{}) (db *DB, err error) {
}
var source string
var dbSQL SQLCommon
var ownDbSQL bool
switch value := args[0].(type) {
case string:
@@ -59,14 +61,17 @@ func Open(dialect string, args ...interface{}) (db *DB, err error) {
source = args[1].(string)
}
dbSQL, err = sql.Open(driver, source)
ownDbSQL = true
case SQLCommon:
dbSQL = value
ownDbSQL = false
default:
return nil, fmt.Errorf("invalid database source: %v is not a valid type", value)
}
db = &DB{
db: dbSQL,
logger: defaultLogger,
values: map[string]interface{}{},
callbacks: DefaultCallback,
dialect: newDialect(dialect, dbSQL),
}
@@ -76,7 +81,7 @@ func Open(dialect string, args ...interface{}) (db *DB, err error) {
}
// Send a ping to make sure the database connection is alive.
if d, ok := dbSQL.(*sql.DB); ok {
if err = d.Ping(); err != nil {
if err = d.Ping(); err != nil && ownDbSQL {
d.Close()
}
}
@@ -117,7 +122,7 @@ func (s *DB) CommonDB() SQLCommon {
// Dialect get dialect
func (s *DB) Dialect() Dialect {
return s.parent.dialect
return s.dialect
}
// Callback return `Callbacks` container, you could add/change/delete callbacks with it
@@ -157,7 +162,7 @@ func (s *DB) HasBlockGlobalUpdate() bool {
// SingularTable use singular table by default
func (s *DB) SingularTable(enable bool) {
modelStructsMap = newModelStructsMap()
modelStructsMap = sync.Map{}
s.parent.singularTable = enable
}
@@ -309,6 +314,11 @@ func (s *DB) Find(out interface{}, where ...interface{}) *DB {
return s.NewScope(out).inlineCondition(where...).callCallbacks(s.parent.callbacks.queries).db
}
//Preloads preloads relations, don`t touch out
func (s *DB) Preloads(out interface{}) *DB {
return s.NewScope(out).InstanceSet("gorm:only_preload", 1).callCallbacks(s.parent.callbacks.queries).db
}
// Scan scan value to a struct
func (s *DB) Scan(dest interface{}) *DB {
return s.NewScope(s.Value).Set("gorm:query_destination", dest).callCallbacks(s.parent.callbacks.queries).db
@@ -482,6 +492,8 @@ func (s *DB) Begin() *DB {
if db, ok := c.db.(sqlDb); ok && db != nil {
tx, err := db.Begin()
c.db = interface{}(tx).(SQLCommon)
c.dialect.SetDB(c.db)
c.AddError(err)
} else {
c.AddError(ErrCantStartTransaction)
@@ -491,7 +503,8 @@ func (s *DB) Begin() *DB {
// Commit commit a transaction
func (s *DB) Commit() *DB {
if db, ok := s.db.(sqlTx); ok && db != nil {
var emptySQLTx *sql.Tx
if db, ok := s.db.(sqlTx); ok && db != nil && db != emptySQLTx {
s.AddError(db.Commit())
} else {
s.AddError(ErrInvalidTransaction)
@@ -501,7 +514,8 @@ func (s *DB) Commit() *DB {
// Rollback rollback a transaction
func (s *DB) Rollback() *DB {
if db, ok := s.db.(sqlTx); ok && db != nil {
var emptySQLTx *sql.Tx
if db, ok := s.db.(sqlTx); ok && db != nil && db != emptySQLTx {
s.AddError(db.Rollback())
} else {
s.AddError(ErrInvalidTransaction)
@@ -670,13 +684,13 @@ func (s *DB) Set(name string, value interface{}) *DB {
// InstantSet instant set setting, will affect current db
func (s *DB) InstantSet(name string, value interface{}) *DB {
s.values[name] = value
s.values.Store(name, value)
return s
}
// Get get setting by name
func (s *DB) Get(name string) (value interface{}, ok bool) {
value, ok = s.values[name]
value, ok = s.values.Load(name)
return
}
@@ -685,7 +699,7 @@ func (s *DB) SetJoinTableHandler(source interface{}, column string, handler Join
scope := s.NewScope(source)
for _, field := range scope.GetModelStruct().StructFields {
if field.Name == column || field.DBName == column {
if many2many := field.TagSettings["MANY2MANY"]; many2many != "" {
if many2many, _ := field.TagSettingsGet("MANY2MANY"); many2many != "" {
source := (&Scope{Value: source}).GetModelStruct().ModelType
destination := (&Scope{Value: reflect.New(field.Struct.Type).Interface()}).GetModelStruct().ModelType
handler.Setup(field.Relationship, many2many, source, destination)
@@ -740,15 +754,16 @@ func (s *DB) clone() *DB {
parent: s.parent,
logger: s.logger,
logMode: s.logMode,
values: map[string]interface{}{},
Value: s.Value,
Error: s.Error,
blockGlobalUpdate: s.blockGlobalUpdate,
dialect: newDialect(s.dialect.GetName(), s.db),
}
for key, value := range s.values {
db.values[key] = value
}
s.values.Range(func(k, v interface{}) bool {
db.values.Store(k, v)
return true
})
if s.search == nil {
db.search = &search{limit: -1, offset: -1}

View File

@@ -17,45 +17,29 @@ var DefaultTableNameHandler = func(db *DB, defaultTableName string) string {
return defaultTableName
}
type safeModelStructsMap struct {
m map[reflect.Type]*ModelStruct
l *sync.RWMutex
}
func (s *safeModelStructsMap) Set(key reflect.Type, value *ModelStruct) {
s.l.Lock()
defer s.l.Unlock()
s.m[key] = value
}
func (s *safeModelStructsMap) Get(key reflect.Type) *ModelStruct {
s.l.RLock()
defer s.l.RUnlock()
return s.m[key]
}
func newModelStructsMap() *safeModelStructsMap {
return &safeModelStructsMap{l: new(sync.RWMutex), m: make(map[reflect.Type]*ModelStruct)}
}
var modelStructsMap = newModelStructsMap()
var modelStructsMap sync.Map
// ModelStruct model definition
type ModelStruct struct {
PrimaryFields []*StructField
StructFields []*StructField
ModelType reflect.Type
defaultTableName string
l sync.Mutex
}
// TableName get model's table name
// TableName returns model's table name
func (s *ModelStruct) TableName(db *DB) string {
s.l.Lock()
defer s.l.Unlock()
if s.defaultTableName == "" && db != nil && s.ModelType != nil {
// Set default table name
if tabler, ok := reflect.New(s.ModelType).Interface().(tabler); ok {
s.defaultTableName = tabler.TableName()
} else {
tableName := ToDBName(s.ModelType.Name())
tableName := ToTableName(s.ModelType.Name())
if db == nil || !db.parent.singularTable {
tableName = inflection.Plural(tableName)
}
@@ -81,6 +65,30 @@ type StructField struct {
Struct reflect.StructField
IsForeignKey bool
Relationship *Relationship
tagSettingsLock sync.RWMutex
}
// TagSettingsSet Sets a tag in the tag settings map
func (s *StructField) TagSettingsSet(key, val string) {
s.tagSettingsLock.Lock()
defer s.tagSettingsLock.Unlock()
s.TagSettings[key] = val
}
// TagSettingsGet returns a tag from the tag settings
func (s *StructField) TagSettingsGet(key string) (string, bool) {
s.tagSettingsLock.RLock()
defer s.tagSettingsLock.RUnlock()
val, ok := s.TagSettings[key]
return val, ok
}
// TagSettingsDelete deletes a tag
func (s *StructField) TagSettingsDelete(key string) {
s.tagSettingsLock.Lock()
defer s.tagSettingsLock.Unlock()
delete(s.TagSettings, key)
}
func (structField *StructField) clone() *StructField {
@@ -104,6 +112,9 @@ func (structField *StructField) clone() *StructField {
clone.Relationship = &relationship
}
// copy the struct field tagSettings, they should be read-locked while they are copied
structField.tagSettingsLock.Lock()
defer structField.tagSettingsLock.Unlock()
for key, value := range structField.TagSettings {
clone.TagSettings[key] = value
}
@@ -126,7 +137,7 @@ type Relationship struct {
func getForeignField(column string, fields []*StructField) *StructField {
for _, field := range fields {
if field.Name == column || field.DBName == column || field.DBName == ToDBName(column) {
if field.Name == column || field.DBName == column || field.DBName == ToColumnName(column) {
return field
}
}
@@ -152,8 +163,8 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
}
// Get Cached model struct
if value := modelStructsMap.Get(reflectType); value != nil {
return value
if value, ok := modelStructsMap.Load(reflectType); ok && value != nil {
return value.(*ModelStruct)
}
modelStruct.ModelType = reflectType
@@ -170,19 +181,19 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
}
// is ignored field
if _, ok := field.TagSettings["-"]; ok {
if _, ok := field.TagSettingsGet("-"); ok {
field.IsIgnored = true
} else {
if _, ok := field.TagSettings["PRIMARY_KEY"]; ok {
if _, ok := field.TagSettingsGet("PRIMARY_KEY"); ok {
field.IsPrimaryKey = true
modelStruct.PrimaryFields = append(modelStruct.PrimaryFields, field)
}
if _, ok := field.TagSettings["DEFAULT"]; ok {
if _, ok := field.TagSettingsGet("DEFAULT"); ok {
field.HasDefaultValue = true
}
if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok && !field.IsPrimaryKey {
if _, ok := field.TagSettingsGet("AUTO_INCREMENT"); ok && !field.IsPrimaryKey {
field.HasDefaultValue = true
}
@@ -198,8 +209,8 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
if indirectType.Kind() == reflect.Struct {
for i := 0; i < indirectType.NumField(); i++ {
for key, value := range parseTagSetting(indirectType.Field(i).Tag) {
if _, ok := field.TagSettings[key]; !ok {
field.TagSettings[key] = value
if _, ok := field.TagSettingsGet(key); !ok {
field.TagSettingsSet(key, value)
}
}
}
@@ -207,17 +218,17 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
} else if _, isTime := fieldValue.(*time.Time); isTime {
// is time
field.IsNormal = true
} else if _, ok := field.TagSettings["EMBEDDED"]; ok || fieldStruct.Anonymous {
} else if _, ok := field.TagSettingsGet("EMBEDDED"); ok || fieldStruct.Anonymous {
// is embedded struct
for _, subField := range scope.New(fieldValue).GetModelStruct().StructFields {
subField = subField.clone()
subField.Names = append([]string{fieldStruct.Name}, subField.Names...)
if prefix, ok := field.TagSettings["EMBEDDED_PREFIX"]; ok {
if prefix, ok := field.TagSettingsGet("EMBEDDED_PREFIX"); ok {
subField.DBName = prefix + subField.DBName
}
if subField.IsPrimaryKey {
if _, ok := subField.TagSettings["PRIMARY_KEY"]; ok {
if _, ok := subField.TagSettingsGet("PRIMARY_KEY"); ok {
modelStruct.PrimaryFields = append(modelStruct.PrimaryFields, subField)
} else {
subField.IsPrimaryKey = false
@@ -248,13 +259,13 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
elemType = field.Struct.Type
)
if foreignKey := field.TagSettings["FOREIGNKEY"]; foreignKey != "" {
if foreignKey, _ := field.TagSettingsGet("FOREIGNKEY"); foreignKey != "" {
foreignKeys = strings.Split(foreignKey, ",")
}
if foreignKey := field.TagSettings["ASSOCIATION_FOREIGNKEY"]; foreignKey != "" {
if foreignKey, _ := field.TagSettingsGet("ASSOCIATION_FOREIGNKEY"); foreignKey != "" {
associationForeignKeys = strings.Split(foreignKey, ",")
} else if foreignKey := field.TagSettings["ASSOCIATIONFOREIGNKEY"]; foreignKey != "" {
} else if foreignKey, _ := field.TagSettingsGet("ASSOCIATIONFOREIGNKEY"); foreignKey != "" {
associationForeignKeys = strings.Split(foreignKey, ",")
}
@@ -263,13 +274,13 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
}
if elemType.Kind() == reflect.Struct {
if many2many := field.TagSettings["MANY2MANY"]; many2many != "" {
if many2many, _ := field.TagSettingsGet("MANY2MANY"); many2many != "" {
relationship.Kind = "many_to_many"
{ // Foreign Keys for Source
joinTableDBNames := []string{}
if foreignKey := field.TagSettings["JOINTABLE_FOREIGNKEY"]; foreignKey != "" {
if foreignKey, _ := field.TagSettingsGet("JOINTABLE_FOREIGNKEY"); foreignKey != "" {
joinTableDBNames = strings.Split(foreignKey, ",")
}
@@ -290,7 +301,7 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
// if defined join table's foreign key
relationship.ForeignDBNames = append(relationship.ForeignDBNames, joinTableDBNames[idx])
} else {
defaultJointableForeignKey := ToDBName(reflectType.Name()) + "_" + foreignField.DBName
defaultJointableForeignKey := ToColumnName(reflectType.Name()) + "_" + foreignField.DBName
relationship.ForeignDBNames = append(relationship.ForeignDBNames, defaultJointableForeignKey)
}
}
@@ -300,7 +311,7 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
{ // Foreign Keys for Association (Destination)
associationJoinTableDBNames := []string{}
if foreignKey := field.TagSettings["ASSOCIATION_JOINTABLE_FOREIGNKEY"]; foreignKey != "" {
if foreignKey, _ := field.TagSettingsGet("ASSOCIATION_JOINTABLE_FOREIGNKEY"); foreignKey != "" {
associationJoinTableDBNames = strings.Split(foreignKey, ",")
}
@@ -321,7 +332,7 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, associationJoinTableDBNames[idx])
} else {
// join table foreign keys for association
joinTableDBName := ToDBName(elemType.Name()) + "_" + field.DBName
joinTableDBName := ToColumnName(elemType.Name()) + "_" + field.DBName
relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, joinTableDBName)
}
}
@@ -329,7 +340,7 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
}
joinTableHandler := JoinTableHandler{}
joinTableHandler.Setup(relationship, many2many, reflectType, elemType)
joinTableHandler.Setup(relationship, ToTableName(many2many), reflectType, elemType)
relationship.JoinTableHandler = &joinTableHandler
field.Relationship = relationship
} else {
@@ -338,7 +349,7 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
var toFields = toScope.GetStructFields()
relationship.Kind = "has_many"
if polymorphic := field.TagSettings["POLYMORPHIC"]; polymorphic != "" {
if polymorphic, _ := field.TagSettingsGet("POLYMORPHIC"); polymorphic != "" {
// Dog has many toys, tag polymorphic is Owner, then associationType is Owner
// Toy use OwnerID, OwnerType ('dogs') as foreign key
if polymorphicType := getForeignField(polymorphic+"Type", toFields); polymorphicType != nil {
@@ -346,7 +357,7 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
relationship.PolymorphicType = polymorphicType.Name
relationship.PolymorphicDBName = polymorphicType.DBName
// if Dog has multiple set of toys set name of the set (instead of default 'dogs')
if value, ok := field.TagSettings["POLYMORPHIC_VALUE"]; ok {
if value, ok := field.TagSettingsGet("POLYMORPHIC_VALUE"); ok {
relationship.PolymorphicValue = value
} else {
relationship.PolymorphicValue = scope.TableName()
@@ -428,17 +439,17 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
tagAssociationForeignKeys []string
)
if foreignKey := field.TagSettings["FOREIGNKEY"]; foreignKey != "" {
if foreignKey, _ := field.TagSettingsGet("FOREIGNKEY"); foreignKey != "" {
tagForeignKeys = strings.Split(foreignKey, ",")
}
if foreignKey := field.TagSettings["ASSOCIATION_FOREIGNKEY"]; foreignKey != "" {
if foreignKey, _ := field.TagSettingsGet("ASSOCIATION_FOREIGNKEY"); foreignKey != "" {
tagAssociationForeignKeys = strings.Split(foreignKey, ",")
} else if foreignKey := field.TagSettings["ASSOCIATIONFOREIGNKEY"]; foreignKey != "" {
} else if foreignKey, _ := field.TagSettingsGet("ASSOCIATIONFOREIGNKEY"); foreignKey != "" {
tagAssociationForeignKeys = strings.Split(foreignKey, ",")
}
if polymorphic := field.TagSettings["POLYMORPHIC"]; polymorphic != "" {
if polymorphic, _ := field.TagSettingsGet("POLYMORPHIC"); polymorphic != "" {
// Cat has one toy, tag polymorphic is Owner, then associationType is Owner
// Toy use OwnerID, OwnerType ('cats') as foreign key
if polymorphicType := getForeignField(polymorphic+"Type", toFields); polymorphicType != nil {
@@ -446,7 +457,7 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
relationship.PolymorphicType = polymorphicType.Name
relationship.PolymorphicDBName = polymorphicType.DBName
// if Cat has several different types of toys set name for each (instead of default 'cats')
if value, ok := field.TagSettings["POLYMORPHIC_VALUE"]; ok {
if value, ok := field.TagSettingsGet("POLYMORPHIC_VALUE"); ok {
relationship.PolymorphicValue = value
} else {
relationship.PolymorphicValue = scope.TableName()
@@ -584,10 +595,10 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
}
// Even it is ignored, also possible to decode db value into the field
if value, ok := field.TagSettings["COLUMN"]; ok {
if value, ok := field.TagSettingsGet("COLUMN"); ok {
field.DBName = value
} else {
field.DBName = ToDBName(fieldStruct.Name)
field.DBName = ToColumnName(fieldStruct.Name)
}
modelStruct.StructFields = append(modelStruct.StructFields, field)
@@ -601,7 +612,7 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
}
}
modelStructsMap.Set(reflectType, &modelStruct)
modelStructsMap.Store(reflectType, &modelStruct)
return &modelStruct
}

124
vendor/github.com/jinzhu/gorm/naming.go generated vendored Normal file
View File

@@ -0,0 +1,124 @@
package gorm
import (
"bytes"
"strings"
)
// Namer is a function type which is given a string and return a string
type Namer func(string) string
// NamingStrategy represents naming strategies
type NamingStrategy struct {
DB Namer
Table Namer
Column Namer
}
// TheNamingStrategy is being initialized with defaultNamingStrategy
var TheNamingStrategy = &NamingStrategy{
DB: defaultNamer,
Table: defaultNamer,
Column: defaultNamer,
}
// AddNamingStrategy sets the naming strategy
func AddNamingStrategy(ns *NamingStrategy) {
if ns.DB == nil {
ns.DB = defaultNamer
}
if ns.Table == nil {
ns.Table = defaultNamer
}
if ns.Column == nil {
ns.Column = defaultNamer
}
TheNamingStrategy = ns
}
// DBName alters the given name by DB
func (ns *NamingStrategy) DBName(name string) string {
return ns.DB(name)
}
// TableName alters the given name by Table
func (ns *NamingStrategy) TableName(name string) string {
return ns.Table(name)
}
// ColumnName alters the given name by Column
func (ns *NamingStrategy) ColumnName(name string) string {
return ns.Column(name)
}
// ToDBName convert string to db name
func ToDBName(name string) string {
return TheNamingStrategy.DBName(name)
}
// ToTableName convert string to table name
func ToTableName(name string) string {
return TheNamingStrategy.TableName(name)
}
// ToColumnName convert string to db name
func ToColumnName(name string) string {
return TheNamingStrategy.ColumnName(name)
}
var smap = newSafeMap()
func defaultNamer(name string) string {
const (
lower = false
upper = true
)
if v := smap.Get(name); v != "" {
return v
}
if name == "" {
return ""
}
var (
value = commonInitialismsReplacer.Replace(name)
buf = bytes.NewBufferString("")
lastCase, currCase, nextCase, nextNumber bool
)
for i, v := range value[:len(value)-1] {
nextCase = bool(value[i+1] >= 'A' && value[i+1] <= 'Z')
nextNumber = bool(value[i+1] >= '0' && value[i+1] <= '9')
if i > 0 {
if currCase == upper {
if lastCase == upper && (nextCase == upper || nextNumber == upper) {
buf.WriteRune(v)
} else {
if value[i-1] != '_' && value[i+1] != '_' {
buf.WriteRune('_')
}
buf.WriteRune(v)
}
} else {
buf.WriteRune(v)
if i == len(value)-2 && (nextCase == upper && nextNumber == lower) {
buf.WriteRune('_')
}
}
} else {
currCase = upper
buf.WriteRune(v)
}
lastCase = currCase
currCase = nextCase
}
buf.WriteByte(value[len(value)-1])
s := strings.ToLower(buf.String())
smap.Set(name, s)
return s
}

View File

@@ -63,12 +63,12 @@ func (scope *Scope) SQLDB() SQLCommon {
// Dialect get dialect
func (scope *Scope) Dialect() Dialect {
return scope.db.parent.dialect
return scope.db.dialect
}
// Quote used to quote string to escape them for database
func (scope *Scope) Quote(str string) string {
if strings.Index(str, ".") != -1 {
if strings.Contains(str, ".") {
newStrs := []string{}
for _, str := range strings.Split(str, ".") {
newStrs = append(newStrs, scope.Dialect().Quote(str))
@@ -134,7 +134,7 @@ func (scope *Scope) Fields() []*Field {
// FieldByName find `gorm.Field` with field name or db name
func (scope *Scope) FieldByName(name string) (field *Field, ok bool) {
var (
dbName = ToDBName(name)
dbName = ToColumnName(name)
mostMatchedField *Field
)
@@ -330,7 +330,7 @@ func (scope *Scope) TableName() string {
// QuotedTableName return quoted table name
func (scope *Scope) QuotedTableName() (name string) {
if scope.Search != nil && len(scope.Search.tableName) > 0 {
if strings.Index(scope.Search.tableName, " ") != -1 {
if strings.Contains(scope.Search.tableName, " ") {
return scope.Search.tableName
}
return scope.Quote(scope.Search.tableName)
@@ -486,8 +486,10 @@ func (scope *Scope) scan(rows *sql.Rows, columns []string, fields []*Field) {
values[index] = &ignored
selectFields = fields
offset := 0
if idx, ok := selectedColumnsMap[column]; ok {
selectFields = selectFields[idx+1:]
offset = idx + 1
selectFields = selectFields[offset:]
}
for fieldIndex, field := range selectFields {
@@ -501,7 +503,7 @@ func (scope *Scope) scan(rows *sql.Rows, columns []string, fields []*Field) {
resetFields[index] = field
}
selectedColumnsMap[column] = fieldIndex
selectedColumnsMap[column] = offset + fieldIndex
if field.IsNormal {
break
@@ -586,10 +588,10 @@ func (scope *Scope) buildCondition(clause map[string]interface{}, include bool)
scope.Err(fmt.Errorf("invalid query condition: %v", value))
return
}
scopeQuotedTableName := newScope.QuotedTableName()
for _, field := range newScope.Fields() {
if !field.IsIgnored && !field.IsBlank {
sqls = append(sqls, fmt.Sprintf("(%v.%v %s %v)", quotedTableName, scope.Quote(field.DBName), equalSQL, scope.AddToVars(field.Field.Interface())))
sqls = append(sqls, fmt.Sprintf("(%v.%v %s %v)", scopeQuotedTableName, scope.Quote(field.DBName), equalSQL, scope.AddToVars(field.Field.Interface())))
}
}
return strings.Join(sqls, " AND ")
@@ -692,12 +694,12 @@ func (scope *Scope) buildSelectQuery(clause map[string]interface{}) (str string)
buff := bytes.NewBuffer([]byte{})
i := 0
for pos := range str {
for pos, char := range str {
if str[pos] == '?' {
buff.WriteString(replacements[i])
i++
} else {
buff.WriteByte(str[pos])
buff.WriteRune(char)
}
}
@@ -853,6 +855,14 @@ func (scope *Scope) inlineCondition(values ...interface{}) *Scope {
}
func (scope *Scope) callCallbacks(funcs []*func(s *Scope)) *Scope {
defer func() {
if err := recover(); err != nil {
if db, ok := scope.db.db.(sqlTx); ok {
db.Rollback()
}
panic(err)
}
}()
for _, f := range funcs {
(*f)(scope)
if scope.skipLeft {
@@ -880,7 +890,7 @@ func convertInterfaceToMap(values interface{}, withIgnoredField bool) map[string
switch reflectValue.Kind() {
case reflect.Map:
for _, key := range reflectValue.MapKeys() {
attrs[ToDBName(key.Interface().(string))] = reflectValue.MapIndex(key).Interface()
attrs[ToColumnName(key.Interface().(string))] = reflectValue.MapIndex(key).Interface()
}
default:
for _, field := range (&Scope{Value: values}).Fields() {
@@ -907,7 +917,7 @@ func (scope *Scope) updatedAttrsWithValues(value interface{}) (results map[strin
results[field.DBName] = value
} else {
err := field.Set(value)
if field.IsNormal {
if field.IsNormal && !field.IsIgnored {
hasUpdate = true
if err == ErrUnaddressable {
results[field.DBName] = value
@@ -1113,8 +1123,8 @@ func (scope *Scope) createJoinTable(field *StructField) {
if field, ok := scope.FieldByName(fieldName); ok {
foreignKeyStruct := field.clone()
foreignKeyStruct.IsPrimaryKey = false
foreignKeyStruct.TagSettings["IS_JOINTABLE_FOREIGNKEY"] = "true"
delete(foreignKeyStruct.TagSettings, "AUTO_INCREMENT")
foreignKeyStruct.TagSettingsSet("IS_JOINTABLE_FOREIGNKEY", "true")
foreignKeyStruct.TagSettingsDelete("AUTO_INCREMENT")
sqlTypes = append(sqlTypes, scope.Quote(relationship.ForeignDBNames[idx])+" "+scope.Dialect().DataTypeOf(foreignKeyStruct))
primaryKeys = append(primaryKeys, scope.Quote(relationship.ForeignDBNames[idx]))
}
@@ -1124,8 +1134,8 @@ func (scope *Scope) createJoinTable(field *StructField) {
if field, ok := toScope.FieldByName(fieldName); ok {
foreignKeyStruct := field.clone()
foreignKeyStruct.IsPrimaryKey = false
foreignKeyStruct.TagSettings["IS_JOINTABLE_FOREIGNKEY"] = "true"
delete(foreignKeyStruct.TagSettings, "AUTO_INCREMENT")
foreignKeyStruct.TagSettingsSet("IS_JOINTABLE_FOREIGNKEY", "true")
foreignKeyStruct.TagSettingsDelete("AUTO_INCREMENT")
sqlTypes = append(sqlTypes, scope.Quote(relationship.AssociationForeignDBNames[idx])+" "+scope.Dialect().DataTypeOf(foreignKeyStruct))
primaryKeys = append(primaryKeys, scope.Quote(relationship.AssociationForeignDBNames[idx]))
}
@@ -1215,12 +1225,18 @@ func (scope *Scope) addForeignKey(field string, dest string, onDelete string, on
}
func (scope *Scope) removeForeignKey(field string, dest string) {
keyName := scope.Dialect().BuildKeyName(scope.TableName(), field, dest)
keyName := scope.Dialect().BuildKeyName(scope.TableName(), field, dest, "foreign")
if !scope.Dialect().HasForeignKey(scope.TableName(), keyName) {
return
}
var query = `ALTER TABLE %s DROP CONSTRAINT %s;`
var mysql mysql
var query string
if scope.Dialect().GetName() == mysql.GetName() {
query = `ALTER TABLE %s DROP FOREIGN KEY %s;`
} else {
query = `ALTER TABLE %s DROP CONSTRAINT %s;`
}
scope.Raw(fmt.Sprintf(query, scope.QuotedTableName(), scope.quoteIfPossible(keyName))).Exec()
}
@@ -1254,7 +1270,7 @@ func (scope *Scope) autoIndex() *Scope {
var uniqueIndexes = map[string][]string{}
for _, field := range scope.GetStructFields() {
if name, ok := field.TagSettings["INDEX"]; ok {
if name, ok := field.TagSettingsGet("INDEX"); ok {
names := strings.Split(name, ",")
for _, name := range names {
@@ -1265,7 +1281,7 @@ func (scope *Scope) autoIndex() *Scope {
}
}
if name, ok := field.TagSettings["UNIQUE_INDEX"]; ok {
if name, ok := field.TagSettingsGet("UNIQUE_INDEX"); ok {
names := strings.Split(name, ",")
for _, name := range names {

View File

@@ -1,7 +1,6 @@
package gorm
import (
"bytes"
"database/sql/driver"
"fmt"
"reflect"
@@ -26,8 +25,8 @@ var NowFunc = func() time.Time {
var commonInitialisms = []string{"API", "ASCII", "CPU", "CSS", "DNS", "EOF", "GUID", "HTML", "HTTP", "HTTPS", "ID", "IP", "JSON", "LHS", "QPS", "RAM", "RHS", "RPC", "SLA", "SMTP", "SSH", "TLS", "TTL", "UID", "UI", "UUID", "URI", "URL", "UTF8", "VM", "XML", "XSRF", "XSS"}
var commonInitialismsReplacer *strings.Replacer
var goSrcRegexp = regexp.MustCompile(`jinzhu/gorm/.*.go`)
var goTestRegexp = regexp.MustCompile(`jinzhu/gorm/.*test.go`)
var goSrcRegexp = regexp.MustCompile(`jinzhu/gorm(@.*)?/.*.go`)
var goTestRegexp = regexp.MustCompile(`jinzhu/gorm(@.*)?/.*test.go`)
func init() {
var commonInitialismsForReplacer []string
@@ -58,64 +57,6 @@ func newSafeMap() *safeMap {
return &safeMap{l: new(sync.RWMutex), m: make(map[string]string)}
}
var smap = newSafeMap()
type strCase bool
const (
lower strCase = false
upper strCase = true
)
// ToDBName convert string to db name
func ToDBName(name string) string {
if v := smap.Get(name); v != "" {
return v
}
if name == "" {
return ""
}
var (
value = commonInitialismsReplacer.Replace(name)
buf = bytes.NewBufferString("")
lastCase, currCase, nextCase strCase
)
for i, v := range value[:len(value)-1] {
nextCase = strCase(value[i+1] >= 'A' && value[i+1] <= 'Z')
if i > 0 {
if currCase == upper {
if lastCase == upper && nextCase == upper {
buf.WriteRune(v)
} else {
if value[i-1] != '_' && value[i+1] != '_' {
buf.WriteRune('_')
}
buf.WriteRune(v)
}
} else {
buf.WriteRune(v)
if i == len(value)-2 && nextCase == upper {
buf.WriteRune('_')
}
}
} else {
currCase = upper
buf.WriteRune(v)
}
lastCase = currCase
currCase = nextCase
}
buf.WriteByte(value[len(value)-1])
s := strings.ToLower(buf.String())
smap.Set(name, s)
return s
}
// SQL expression
type expr struct {
expr string
@@ -265,7 +206,7 @@ func getValueFromFields(value reflect.Value, fieldNames []string) (results []int
// as FieldByName could panic
if indirectValue := reflect.Indirect(value); indirectValue.IsValid() {
for _, fieldName := range fieldNames {
if fieldValue := indirectValue.FieldByName(fieldName); fieldValue.IsValid() {
if fieldValue := reflect.Indirect(indirectValue.FieldByName(fieldName)); fieldValue.IsValid() {
result := fieldValue.Interface()
if r, ok := result.(driver.Valuer); ok {
result, _ = r.Value()