Codebase list golang-github-jinzhu-gorm / f78b174
Import upstream version 1.9.8, md5 f052277d9a9121d74defdb2c03035d22 Debian Janitor 4 years ago
40 changed file(s) with 1399 addition(s) and 375 deletion(s). Raw diff Collapse all Expand all
+0
-11
.codeclimate.yml less more
0 ---
1 engines:
2 gofmt:
3 enabled: true
4 govet:
5 enabled: true
6 golint:
7 enabled: true
8 ratings:
9 paths:
10 - "**.go"
00 documents
1 coverage.txt
12 _book
33
44 [![go report card](https://goreportcard.com/badge/github.com/jinzhu/gorm "go report card")](https://goreportcard.com/report/github.com/jinzhu/gorm)
55 [![wercker status](https://app.wercker.com/status/8596cace912c9947dd9c8542ecc8cb8b/s/master "wercker status")](https://app.wercker.com/project/byKey/8596cace912c9947dd9c8542ecc8cb8b)
6 [![codecov](https://codecov.io/gh/jinzhu/gorm/branch/master/graph/badge.svg)](https://codecov.io/gh/jinzhu/gorm)
67 [![Join the chat at https://gitter.im/jinzhu/gorm](https://img.shields.io/gitter/room/jinzhu/gorm.svg)](https://gitter.im/jinzhu/gorm?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
78 [![Open Collective Backer](https://opencollective.com/gorm/tiers/backer/badge.svg?label=backer&color=brightgreen "Open Collective Backer")](https://opencollective.com/gorm)
89 [![Open Collective Sponsor](https://opencollective.com/gorm/tiers/sponsor/badge.svg?label=sponsor&color=brightgreen "Open Collective Sponsor")](https://opencollective.com/gorm)
9 [![MIT license](http://img.shields.io/badge/license-MIT-brightgreen.svg)](http://opensource.org/licenses/MIT)
10 [![MIT license](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://opensource.org/licenses/MIT)
1011 [![GoDoc](https://godoc.org/github.com/jinzhu/gorm?status.svg)](https://godoc.org/github.com/jinzhu/gorm)
1112
1213 ## Overview
2627
2728 ## Getting Started
2829
29 * GORM Guides [http://gorm.io](http://gorm.io)
30 * GORM Guides [https://gorm.io](https://gorm.io)
3031
3132 ## Contributing
3233
33 [You can help to deliver a better GORM, check out things you can do](http://gorm.io/contribute.html)
34 [You can help to deliver a better GORM, check out things you can do](https://gorm.io/contribute.html)
3435
3536 ## License
3637
266266 query = scope.DB()
267267 )
268268
269 if relationship.Kind == "many_to_many" {
269 switch relationship.Kind {
270 case "many_to_many":
270271 query = relationship.JoinTableHandler.JoinWith(relationship.JoinTableHandler, query, scope.Value)
271 } else if relationship.Kind == "has_many" || relationship.Kind == "has_one" {
272 case "has_many", "has_one":
272273 primaryKeys := scope.getColumnAsArray(relationship.AssociationForeignFieldNames, scope.Value)
273274 query = query.Where(
274275 fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relationship.ForeignDBNames), toQueryMarks(primaryKeys)),
275276 toQueryValues(primaryKeys)...,
276277 )
277 } else if relationship.Kind == "belongs_to" {
278 case "belongs_to":
278279 primaryKeys := scope.getColumnAsArray(relationship.ForeignFieldNames, scope.Value)
279280 query = query.Where(
280281 fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relationship.AssociationForeignDBNames), toQueryMarks(primaryKeys)),
366367 return association
367368 }
368369
370 // setErr set error when the error is not nil. And return Association.
369371 func (association *Association) setErr(err error) *Association {
370372 if err != nil {
371373 association.Error = err
00 package gorm
11
2 import "log"
2 import "fmt"
33
44 // DefaultCallback default callbacks defined by gorm
55 var DefaultCallback = &Callback{}
1212 // Field `rowQueries` contains callbacks will be call when querying object with Row, Rows...
1313 // Field `processors` contains all callback processors, will be used to generate above callbacks in order
1414 type Callback struct {
15 logger logger
1516 creates []*func(scope *Scope)
1617 updates []*func(scope *Scope)
1718 deletes []*func(scope *Scope)
2223
2324 // CallbackProcessor contains callback informations
2425 type CallbackProcessor struct {
26 logger logger
2527 name string // current callback's name
2628 before string // register current callback before a callback
2729 after string // register current callback after a callback
3234 parent *Callback
3335 }
3436
35 func (c *Callback) clone() *Callback {
37 func (c *Callback) clone(logger logger) *Callback {
3638 return &Callback{
39 logger: logger,
3740 creates: c.creates,
3841 updates: c.updates,
3942 deletes: c.deletes,
5255 // scope.Err(errors.New("error"))
5356 // })
5457 func (c *Callback) Create() *CallbackProcessor {
55 return &CallbackProcessor{kind: "create", parent: c}
58 return &CallbackProcessor{logger: c.logger, kind: "create", parent: c}
5659 }
5760
5861 // Update could be used to register callbacks for updating object, refer `Create` for usage
5962 func (c *Callback) Update() *CallbackProcessor {
60 return &CallbackProcessor{kind: "update", parent: c}
63 return &CallbackProcessor{logger: c.logger, kind: "update", parent: c}
6164 }
6265
6366 // Delete could be used to register callbacks for deleting object, refer `Create` for usage
6467 func (c *Callback) Delete() *CallbackProcessor {
65 return &CallbackProcessor{kind: "delete", parent: c}
68 return &CallbackProcessor{logger: c.logger, kind: "delete", parent: c}
6669 }
6770
6871 // Query could be used to register callbacks for querying objects with query methods like `Find`, `First`, `Related`, `Association`...
6972 // Refer `Create` for usage
7073 func (c *Callback) Query() *CallbackProcessor {
71 return &CallbackProcessor{kind: "query", parent: c}
74 return &CallbackProcessor{logger: c.logger, kind: "query", parent: c}
7275 }
7376
7477 // RowQuery could be used to register callbacks for querying objects with `Row`, `Rows`, refer `Create` for usage
7578 func (c *Callback) RowQuery() *CallbackProcessor {
76 return &CallbackProcessor{kind: "row_query", parent: c}
79 return &CallbackProcessor{logger: c.logger, kind: "row_query", parent: c}
7780 }
7881
7982 // After insert a new callback after callback `callbackName`, refer `Callbacks.Create`
9295 func (cp *CallbackProcessor) Register(callbackName string, callback func(scope *Scope)) {
9396 if cp.kind == "row_query" {
9497 if cp.before == "" && cp.after == "" && callbackName != "gorm:row_query" {
95 log.Printf("Registing RowQuery callback %v without specify order with Before(), After(), applying Before('gorm:row_query') by default for compatibility...\n", callbackName)
98 cp.logger.Print(fmt.Sprintf("Registering RowQuery callback %v without specify order with Before(), After(), applying Before('gorm:row_query') by default for compatibility...\n", callbackName))
9699 cp.before = "gorm:row_query"
97100 }
98101 }
106109 // Remove a registered callback
107110 // db.Callback().Create().Remove("gorm:update_time_stamp_when_create")
108111 func (cp *CallbackProcessor) Remove(callbackName string) {
109 log.Printf("[info] removing callback `%v` from %v\n", callbackName, fileWithLineNum())
112 cp.logger.Print(fmt.Sprintf("[info] removing callback `%v` from %v\n", callbackName, fileWithLineNum()))
110113 cp.name = callbackName
111114 cp.remove = true
112115 cp.parent.processors = append(cp.parent.processors, cp)
119122 // scope.SetColumn("Updated", now)
120123 // })
121124 func (cp *CallbackProcessor) Replace(callbackName string, callback func(scope *Scope)) {
122 log.Printf("[info] replacing callback `%v` from %v\n", callbackName, fileWithLineNum())
125 cp.logger.Print(fmt.Sprintf("[info] replacing callback `%v` from %v\n", callbackName, fileWithLineNum()))
123126 cp.name = callbackName
124127 cp.processor = &callback
125128 cp.replace = true
158161 for _, cp := range cps {
159162 // show warning message the callback name already exists
160163 if index := getRIndex(allNames, cp.name); index > -1 && !cp.replace && !cp.remove {
161 log.Printf("[warning] duplicated callback `%v` from %v\n", cp.name, fileWithLineNum())
164 cp.logger.Print(fmt.Sprintf("[warning] duplicated callback `%v` from %v\n", cp.name, fileWithLineNum()))
162165 }
163166 allNames = append(allNames, cp.name)
164167 }
5858
5959 for _, field := range scope.Fields() {
6060 if scope.changeableField(field) {
61 if field.IsNormal {
61 if field.IsNormal && !field.IsIgnored {
6262 if field.IsBlank && field.HasDefaultValue {
6363 blankColumnsWithDefaultValue = append(blankColumnsWithDefaultValue, scope.Quote(field.DBName))
6464 scope.InstanceSet("gorm:blank_columns_with_default_value", blankColumnsWithDefaultValue)
8282 quotedTableName = scope.QuotedTableName()
8383 primaryField = scope.PrimaryField()
8484 extraOption string
85 insertModifier string
8586 )
8687
8788 if str, ok := scope.Get("gorm:insert_option"); ok {
8889 extraOption = fmt.Sprint(str)
90 }
91 if str, ok := scope.Get("gorm:insert_modifier"); ok {
92 insertModifier = strings.ToUpper(fmt.Sprint(str))
93 if insertModifier == "INTO" {
94 insertModifier = ""
95 }
8996 }
9097
9198 if primaryField != nil {
96103
97104 if len(columns) == 0 {
98105 scope.Raw(fmt.Sprintf(
99 "INSERT INTO %v %v%v%v",
106 "INSERT %v INTO %v %v%v%v",
107 addExtraSpaceIfExist(insertModifier),
100108 quotedTableName,
101109 scope.Dialect().DefaultValueStr(),
102110 addExtraSpaceIfExist(extraOption),
104112 ))
105113 } else {
106114 scope.Raw(fmt.Sprintf(
107 "INSERT INTO %v (%v) VALUES (%v)%v%v",
115 "INSERT %v INTO %v (%v) VALUES (%v)%v%v",
116 addExtraSpaceIfExist(insertModifier),
108117 scope.QuotedTableName(),
109118 strings.Join(columns, ","),
110119 strings.Join(placeholders, ","),
1515 // queryCallback used to query data from database
1616 func queryCallback(scope *Scope) {
1717 if _, skip := scope.InstanceGet("gorm:skip_query_callback"); skip {
18 return
19 }
20
21 //we are only preloading relations, dont touch base model
22 if _, skip := scope.InstanceGet("gorm:only_preload"); skip {
1823 return
1924 }
2025
1313 return
1414 }
1515
16 if _, ok := scope.Get("gorm:auto_preload"); ok {
17 autoPreload(scope)
16 if ap, ok := scope.Get("gorm:auto_preload"); ok {
17 // If gorm:auto_preload IS NOT a bool then auto preload.
18 // Else if it IS a bool, use the value
19 if apb, ok := ap.(bool); !ok {
20 autoPreload(scope)
21 } else if apb {
22 autoPreload(scope)
23 }
1824 }
1925
2026 if scope.Search.preload == nil || scope.HasError() {
9399 continue
94100 }
95101
96 if val, ok := field.TagSettings["PRELOAD"]; ok {
102 if val, ok := field.TagSettingsGet("PRELOAD"); ok {
97103 if preload, err := strconv.ParseBool(val); err != nil {
98104 scope.Err(errors.New("invalid preload option"))
99105 return
154160 )
155161
156162 if indirectScopeValue.Kind() == reflect.Slice {
163 foreignValuesToResults := make(map[string]reflect.Value)
164 for i := 0; i < resultsValue.Len(); i++ {
165 result := resultsValue.Index(i)
166 foreignValues := toString(getValueFromFields(result, relation.ForeignFieldNames))
167 foreignValuesToResults[foreignValues] = result
168 }
157169 for j := 0; j < indirectScopeValue.Len(); j++ {
158 for i := 0; i < resultsValue.Len(); i++ {
159 result := resultsValue.Index(i)
160 foreignValues := getValueFromFields(result, relation.ForeignFieldNames)
161 if indirectValue := indirect(indirectScopeValue.Index(j)); equalAsString(getValueFromFields(indirectValue, relation.AssociationForeignFieldNames), foreignValues) {
162 indirectValue.FieldByName(field.Name).Set(result)
163 break
164 }
170 indirectValue := indirect(indirectScopeValue.Index(j))
171 valueString := toString(getValueFromFields(indirectValue, relation.AssociationForeignFieldNames))
172 if result, found := foreignValuesToResults[valueString]; found {
173 indirectValue.FieldByName(field.Name).Set(result)
165174 }
166175 }
167176 } else {
248257 indirectScopeValue = scope.IndirectValue()
249258 )
250259
260 foreignFieldToObjects := make(map[string][]*reflect.Value)
261 if indirectScopeValue.Kind() == reflect.Slice {
262 for j := 0; j < indirectScopeValue.Len(); j++ {
263 object := indirect(indirectScopeValue.Index(j))
264 valueString := toString(getValueFromFields(object, relation.ForeignFieldNames))
265 foreignFieldToObjects[valueString] = append(foreignFieldToObjects[valueString], &object)
266 }
267 }
268
251269 for i := 0; i < resultsValue.Len(); i++ {
252270 result := resultsValue.Index(i)
253271 if indirectScopeValue.Kind() == reflect.Slice {
254 value := getValueFromFields(result, relation.AssociationForeignFieldNames)
255 for j := 0; j < indirectScopeValue.Len(); j++ {
256 object := indirect(indirectScopeValue.Index(j))
257 if equalAsString(getValueFromFields(object, relation.ForeignFieldNames), value) {
272 valueString := toString(getValueFromFields(result, relation.AssociationForeignFieldNames))
273 if objects, found := foreignFieldToObjects[valueString]; found {
274 for _, object := range objects {
258275 object.FieldByName(field.Name).Set(result)
259276 }
260277 }
373390 key := toString(getValueFromFields(indirectScopeValue, foreignFieldNames))
374391 fieldsSourceMap[key] = append(fieldsSourceMap[key], indirectScopeValue.FieldByName(field.Name))
375392 }
376 for source, link := range linkHash {
377 for i, field := range fieldsSourceMap[source] {
393
394 for source, fields := range fieldsSourceMap {
395 for _, f := range fields {
378396 //If not 0 this means Value is a pointer and we already added preloaded models to it
379 if fieldsSourceMap[source][i].Len() != 0 {
397 if f.Len() != 0 {
380398 continue
381399 }
382 field.Set(reflect.Append(fieldsSourceMap[source][i], link...))
383 }
384
385 }
386 }
400
401 v := reflect.MakeSlice(f.Type(), 0, 0)
402 if len(linkHash[source]) > 0 {
403 v = reflect.Append(f, linkHash[source]...)
404 }
405
406 f.Set(v)
407 }
408 }
409 }
00 package gorm
11
2 import "database/sql"
2 import (
3 "database/sql"
4 "fmt"
5 )
36
47 // Define callbacks for row query
58 func init() {
1922 func rowQueryCallback(scope *Scope) {
2023 if result, ok := scope.InstanceGet("row_query_result"); ok {
2124 scope.prepareQuerySQL()
25 if str, ok := scope.Get("gorm:query_option"); ok {
26 scope.SQL += addExtraSpaceIfExist(fmt.Sprint(str))
27 }
2228
2329 if rowResult, ok := result.(*RowQueryResult); ok {
2430 rowResult.Row = scope.SQLDB().QueryRow(scope.SQL, scope.SQLVars...)
2020
2121 if v, ok := value.(string); ok {
2222 v = strings.ToLower(v)
23 if v == "false" || v != "skip" {
24 return false
25 }
23 return v == "true"
2624 }
2725
2826 return true
3533 if value, ok := scope.Get("gorm:save_associations"); ok {
3634 autoUpdate = checkTruth(value)
3735 autoCreate = autoUpdate
38 } else if value, ok := field.TagSettings["SAVE_ASSOCIATIONS"]; ok {
36 saveReference = autoUpdate
37 } else if value, ok := field.TagSettingsGet("SAVE_ASSOCIATIONS"); ok {
3938 autoUpdate = checkTruth(value)
4039 autoCreate = autoUpdate
40 saveReference = autoUpdate
4141 }
4242
4343 if value, ok := scope.Get("gorm:association_autoupdate"); ok {
4444 autoUpdate = checkTruth(value)
45 } else if value, ok := field.TagSettings["ASSOCIATION_AUTOUPDATE"]; ok {
45 } else if value, ok := field.TagSettingsGet("ASSOCIATION_AUTOUPDATE"); ok {
4646 autoUpdate = checkTruth(value)
4747 }
4848
4949 if value, ok := scope.Get("gorm:association_autocreate"); ok {
5050 autoCreate = checkTruth(value)
51 } else if value, ok := field.TagSettings["ASSOCIATION_AUTOCREATE"]; ok {
51 } else if value, ok := field.TagSettingsGet("ASSOCIATION_AUTOCREATE"); ok {
5252 autoCreate = checkTruth(value)
5353 }
5454
5555 if value, ok := scope.Get("gorm:association_save_reference"); ok {
5656 saveReference = checkTruth(value)
57 } else if value, ok := field.TagSettings["ASSOCIATION_SAVE_REFERENCE"]; ok {
57 } else if value, ok := field.TagSettingsGet("ASSOCIATION_SAVE_REFERENCE"); ok {
5858 saveReference = checkTruth(value)
5959 }
6060 }
2222 func afterCreate2(s *Scope) {}
2323
2424 func TestRegisterCallback(t *testing.T) {
25 var callback = &Callback{}
25 var callback = &Callback{logger: defaultLogger}
2626
2727 callback.Create().Register("before_create1", beforeCreate1)
2828 callback.Create().Register("before_create2", beforeCreate2)
3636 }
3737
3838 func TestRegisterCallbackWithOrder(t *testing.T) {
39 var callback1 = &Callback{}
39 var callback1 = &Callback{logger: defaultLogger}
4040 callback1.Create().Register("before_create1", beforeCreate1)
4141 callback1.Create().Register("create", create)
4242 callback1.Create().Register("after_create1", afterCreate1)
4545 t.Errorf("register callback with order")
4646 }
4747
48 var callback2 = &Callback{}
48 var callback2 = &Callback{logger: defaultLogger}
4949
5050 callback2.Update().Register("create", create)
5151 callback2.Update().Before("create").Register("before_create1", beforeCreate1)
5959 }
6060
6161 func TestRegisterCallbackWithComplexOrder(t *testing.T) {
62 var callback1 = &Callback{}
62 var callback1 = &Callback{logger: defaultLogger}
6363
6464 callback1.Query().Before("after_create1").After("before_create1").Register("create", create)
6565 callback1.Query().Register("before_create1", beforeCreate1)
6969 t.Errorf("register callback with order")
7070 }
7171
72 var callback2 = &Callback{}
72 var callback2 = &Callback{logger: defaultLogger}
7373
7474 callback2.Delete().Before("after_create1").After("before_create1").Register("create", create)
7575 callback2.Delete().Before("create").Register("before_create1", beforeCreate1)
8585 func replaceCreate(s *Scope) {}
8686
8787 func TestReplaceCallback(t *testing.T) {
88 var callback = &Callback{}
88 var callback = &Callback{logger: defaultLogger}
8989
9090 callback.Create().Before("after_create1").After("before_create1").Register("create", create)
9191 callback.Create().Register("before_create1", beforeCreate1)
9898 }
9999
100100 func TestRemoveCallback(t *testing.T) {
101 var callback = &Callback{}
101 var callback = &Callback{logger: defaultLogger}
102102
103103 callback.Create().Before("after_create1").After("before_create1").Register("create", create)
104104 callback.Create().Register("before_create1", beforeCreate1)
7474 } else {
7575 for _, field := range scope.Fields() {
7676 if scope.changeableField(field) {
77 if !field.IsPrimaryKey && field.IsNormal {
78 sqls = append(sqls, fmt.Sprintf("%v = %v", scope.Quote(field.DBName), scope.AddToVars(field.Field.Interface())))
77 if !field.IsPrimaryKey && field.IsNormal && (field.Name != "CreatedAt" || !field.IsBlank) {
78 if !field.IsForeignKey || !field.IsBlank || !field.HasDefaultValue {
79 sqls = append(sqls, fmt.Sprintf("%v = %v", scope.Quote(field.DBName), scope.AddToVars(field.Field.Interface())))
80 }
7981 } else if relationship := field.Relationship; relationship != nil && relationship.Kind == "belongs_to" {
8082 for _, foreignKey := range relationship.ForeignDBNames {
8183 if foreignField, ok := scope.FieldByName(foreignKey); ok && !scope.changeableField(foreignField) {
228228 t.Errorf("Should not create omitted relationships")
229229 }
230230 }
231
232 func TestCreateIgnore(t *testing.T) {
233 float := 35.03554004971999
234 now := time.Now()
235 user := User{Name: "CreateUser", Age: 18, Birthday: &now, UserNum: Num(111), PasswordHash: []byte{'f', 'a', 'k', '4'}, Latitude: float}
236
237 if !DB.NewRecord(user) || !DB.NewRecord(&user) {
238 t.Error("User should be new record before create")
239 }
240
241 if count := DB.Create(&user).RowsAffected; count != 1 {
242 t.Error("There should be one record be affected when create record")
243 }
244 if DB.Dialect().GetName() == "mysql" && DB.Set("gorm:insert_modifier", "IGNORE").Create(&user).Error != nil {
245 t.Error("Should ignore duplicate user insert by insert modifier:IGNORE ")
246 }
247 }
288288 func TestSelfReferencingMany2ManyColumn(t *testing.T) {
289289 DB.DropTable(&SelfReferencingUser{}, "UserFriends")
290290 DB.AutoMigrate(&SelfReferencingUser{})
291 if !DB.HasTable("UserFriends") {
292 t.Errorf("auto migrate error, table UserFriends should be created")
293 }
291294
292295 friend1 := SelfReferencingUser{Name: "friend1_m2m"}
293296 if err := DB.Create(&friend1).Error; err != nil {
310313
311314 if DB.Model(&user).Association("Friends").Count() != 2 {
312315 t.Errorf("Should find created friends correctly")
316 }
317
318 var count int
319 if err := DB.Table("UserFriends").Count(&count).Error; err != nil {
320 t.Errorf("no error should happen, but got %v", err)
321 }
322 if count == 0 {
323 t.Errorf("table UserFriends should have records")
313324 }
314325
315326 var newUser = SelfReferencingUser{}
4747 // BuildKeyName returns a valid key name (foreign key, index key) for the given table, field and reference
4848 BuildKeyName(kind, tableName string, fields ...string) string
4949
50 // NormalizeIndexAndColumn returns valid index name and column name depending on each dialect
51 NormalizeIndexAndColumn(indexName, columnName string) (string, string)
52
5053 // CurrentDatabase return current database name
5154 CurrentDatabase() string
5255 }
7174 dialectsMap[name] = dialect
7275 }
7376
77 // GetDialect gets the dialect for the specified dialect name
78 func GetDialect(name string) (dialect Dialect, ok bool) {
79 dialect, ok = dialectsMap[name]
80 return
81 }
82
7483 // ParseFieldStructForDialect get field's sql data type
7584 var ParseFieldStructForDialect = func(field *StructField, dialect Dialect) (fieldValue reflect.Value, sqlType string, size int, additionalType string) {
7685 // Get redirected field type
7786 var (
7887 reflectType = field.Struct.Type
79 dataType = field.TagSettings["TYPE"]
88 dataType, _ = field.TagSettingsGet("TYPE")
8089 )
8190
8291 for reflectType.Kind() == reflect.Ptr {
105114 }
106115
107116 // Default Size
108 if num, ok := field.TagSettings["SIZE"]; ok {
117 if num, ok := field.TagSettingsGet("SIZE"); ok {
109118 size, _ = strconv.Atoi(num)
110119 } else {
111120 size = 255
112121 }
113122
114123 // Default type from tag setting
115 additionalType = field.TagSettings["NOT NULL"] + " " + field.TagSettings["UNIQUE"]
116 if value, ok := field.TagSettings["DEFAULT"]; ok {
124 notNull, _ := field.TagSettingsGet("NOT NULL")
125 unique, _ := field.TagSettingsGet("UNIQUE")
126 additionalType = notNull + " " + unique
127 if value, ok := field.TagSettingsGet("DEFAULT"); ok {
117128 additionalType = additionalType + " DEFAULT " + value
129 }
130
131 if value, ok := field.TagSettingsGet("COMMENT"); ok {
132 additionalType = additionalType + " COMMENT " + value
118133 }
119134
120135 return fieldValue, dataType, size, strings.TrimSpace(additionalType)
77 "strings"
88 "time"
99 )
10
11 var keyNameRegex = regexp.MustCompile("[^a-zA-Z0-9]+")
1012
1113 // DefaultForeignKeyNamer contains the default foreign key name generator method
1214 type DefaultForeignKeyNamer struct {
3840 }
3941
4042 func (s *commonDialect) fieldCanAutoIncrement(field *StructField) bool {
41 if value, ok := field.TagSettings["AUTO_INCREMENT"]; ok {
43 if value, ok := field.TagSettingsGet("AUTO_INCREMENT"); ok {
4244 return strings.ToLower(value) != "false"
4345 }
4446 return field.IsPrimaryKey
165167 // BuildKeyName returns a valid key name (foreign key, index key) for the given table, field and reference
166168 func (DefaultForeignKeyNamer) BuildKeyName(kind, tableName string, fields ...string) string {
167169 keyName := fmt.Sprintf("%s_%s_%s", kind, tableName, strings.Join(fields, "_"))
168 keyName = regexp.MustCompile("[^a-zA-Z0-9]+").ReplaceAllString(keyName, "_")
170 keyName = keyNameRegex.ReplaceAllString(keyName, "_")
169171 return keyName
172 }
173
174 // NormalizeIndexAndColumn returns argument's index name and column name without doing anything
175 func (commonDialect) NormalizeIndexAndColumn(indexName, columnName string) (string, string) {
176 return indexName, columnName
170177 }
171178
172179 // IsByteArrayOrSlice returns true of the reflected value is an array or slice
1010 "unicode/utf8"
1111 )
1212
13 var mysqlIndexRegex = regexp.MustCompile(`^(.+)\((\d+)\)$`)
14
1315 type mysql struct {
1416 commonDialect
1517 }
3234
3335 // MySQL allows only one auto increment column per table, and it must
3436 // be a KEY column.
35 if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok {
36 if _, ok = field.TagSettings["INDEX"]; !ok && !field.IsPrimaryKey {
37 delete(field.TagSettings, "AUTO_INCREMENT")
37 if _, ok := field.TagSettingsGet("AUTO_INCREMENT"); ok {
38 if _, ok = field.TagSettingsGet("INDEX"); !ok && !field.IsPrimaryKey {
39 field.TagSettingsDelete("AUTO_INCREMENT")
3840 }
3941 }
4042
4446 sqlType = "boolean"
4547 case reflect.Int8:
4648 if s.fieldCanAutoIncrement(field) {
47 field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
49 field.TagSettingsSet("AUTO_INCREMENT", "AUTO_INCREMENT")
4850 sqlType = "tinyint AUTO_INCREMENT"
4951 } else {
5052 sqlType = "tinyint"
5153 }
5254 case reflect.Int, reflect.Int16, reflect.Int32:
5355 if s.fieldCanAutoIncrement(field) {
54 field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
56 field.TagSettingsSet("AUTO_INCREMENT", "AUTO_INCREMENT")
5557 sqlType = "int AUTO_INCREMENT"
5658 } else {
5759 sqlType = "int"
5860 }
5961 case reflect.Uint8:
6062 if s.fieldCanAutoIncrement(field) {
61 field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
63 field.TagSettingsSet("AUTO_INCREMENT", "AUTO_INCREMENT")
6264 sqlType = "tinyint unsigned AUTO_INCREMENT"
6365 } else {
6466 sqlType = "tinyint unsigned"
6567 }
6668 case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uintptr:
6769 if s.fieldCanAutoIncrement(field) {
68 field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
70 field.TagSettingsSet("AUTO_INCREMENT", "AUTO_INCREMENT")
6971 sqlType = "int unsigned AUTO_INCREMENT"
7072 } else {
7173 sqlType = "int unsigned"
7274 }
7375 case reflect.Int64:
7476 if s.fieldCanAutoIncrement(field) {
75 field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
77 field.TagSettingsSet("AUTO_INCREMENT", "AUTO_INCREMENT")
7678 sqlType = "bigint AUTO_INCREMENT"
7779 } else {
7880 sqlType = "bigint"
7981 }
8082 case reflect.Uint64:
8183 if s.fieldCanAutoIncrement(field) {
82 field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
84 field.TagSettingsSet("AUTO_INCREMENT", "AUTO_INCREMENT")
8385 sqlType = "bigint unsigned AUTO_INCREMENT"
8486 } else {
8587 sqlType = "bigint unsigned"
9597 case reflect.Struct:
9698 if _, ok := dataValue.Interface().(time.Time); ok {
9799 precision := ""
98 if p, ok := field.TagSettings["PRECISION"]; ok {
100 if p, ok := field.TagSettingsGet("PRECISION"); ok {
99101 precision = fmt.Sprintf("(%s)", p)
100102 }
101103
102 if _, ok := field.TagSettings["NOT NULL"]; ok {
104 if _, ok := field.TagSettingsGet("NOT NULL"); ok || field.IsPrimaryKey {
103105 sqlType = fmt.Sprintf("timestamp%v", precision)
104106 } else {
105107 sqlType = fmt.Sprintf("timestamp%v NULL", precision)
177179 bs := h.Sum(nil)
178180
179181 // sha1 is 40 characters, keep first 24 characters of destination
180 destRunes := []rune(regexp.MustCompile("[^a-zA-Z0-9]+").ReplaceAllString(fields[0], "_"))
182 destRunes := []rune(keyNameRegex.ReplaceAllString(fields[0], "_"))
181183 if len(destRunes) > 24 {
182184 destRunes = destRunes[:24]
183185 }
185187 return fmt.Sprintf("%s%x", string(destRunes), bs)
186188 }
187189
190 // NormalizeIndexAndColumn returns index name and column name for specify an index prefix length if needed
191 func (mysql) NormalizeIndexAndColumn(indexName, columnName string) (string, string) {
192 submatch := mysqlIndexRegex.FindStringSubmatch(indexName)
193 if len(submatch) != 3 {
194 return indexName, columnName
195 }
196 indexName = submatch[1]
197 columnName = fmt.Sprintf("%s(%s)", columnName, submatch[2])
198 return indexName, columnName
199 }
200
188201 func (mysql) DefaultValueStr() string {
189202 return "VALUES()"
190203 }
3333 sqlType = "boolean"
3434 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uintptr:
3535 if s.fieldCanAutoIncrement(field) {
36 field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
36 field.TagSettingsSet("AUTO_INCREMENT", "AUTO_INCREMENT")
3737 sqlType = "serial"
3838 } else {
3939 sqlType = "integer"
4040 }
4141 case reflect.Int64, reflect.Uint32, reflect.Uint64:
4242 if s.fieldCanAutoIncrement(field) {
43 field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
43 field.TagSettingsSet("AUTO_INCREMENT", "AUTO_INCREMENT")
4444 sqlType = "bigserial"
4545 } else {
4646 sqlType = "bigint"
4848 case reflect.Float32, reflect.Float64:
4949 sqlType = "numeric"
5050 case reflect.String:
51 if _, ok := field.TagSettings["SIZE"]; !ok {
51 if _, ok := field.TagSettingsGet("SIZE"); !ok {
5252 size = 0 // if SIZE haven't been set, use `text` as the default type, as there are no performance different
5353 }
5454
2828 sqlType = "bool"
2929 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uintptr:
3030 if s.fieldCanAutoIncrement(field) {
31 field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
31 field.TagSettingsSet("AUTO_INCREMENT", "AUTO_INCREMENT")
3232 sqlType = "integer primary key autoincrement"
3333 } else {
3434 sqlType = "integer"
3535 }
3636 case reflect.Int64, reflect.Uint64:
3737 if s.fieldCanAutoIncrement(field) {
38 field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
38 field.TagSettingsSet("AUTO_INCREMENT", "AUTO_INCREMENT")
3939 sqlType = "integer primary key autoincrement"
4040 } else {
4141 sqlType = "bigint"
00 package mssql
11
22 import (
3 "database/sql/driver"
4 "encoding/json"
5 "errors"
36 "fmt"
47 "reflect"
58 "strconv"
69 "strings"
710 "time"
811
12 // Importing mssql driver package only in dialect file, otherwide not needed
913 _ "github.com/denisenkom/go-mssqldb"
1014 "github.com/jinzhu/gorm"
1115 )
1317 func setIdentityInsert(scope *gorm.Scope) {
1418 if scope.Dialect().GetName() == "mssql" {
1519 for _, field := range scope.PrimaryFields() {
16 if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok && !field.IsBlank {
20 if _, ok := field.TagSettingsGet("AUTO_INCREMENT"); ok && !field.IsBlank {
1721 scope.NewDB().Exec(fmt.Sprintf("SET IDENTITY_INSERT %v ON", scope.TableName()))
1822 scope.InstanceSet("mssql:identity_insert_on", true)
1923 }
6569 sqlType = "bit"
6670 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uintptr:
6771 if s.fieldCanAutoIncrement(field) {
68 field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
72 field.TagSettingsSet("AUTO_INCREMENT", "AUTO_INCREMENT")
6973 sqlType = "int IDENTITY(1,1)"
7074 } else {
7175 sqlType = "int"
7276 }
7377 case reflect.Int64, reflect.Uint64:
7478 if s.fieldCanAutoIncrement(field) {
75 field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
79 field.TagSettingsSet("AUTO_INCREMENT", "AUTO_INCREMENT")
7680 sqlType = "bigint IDENTITY(1,1)"
7781 } else {
7882 sqlType = "bigint"
111115 }
112116
113117 func (s mssql) fieldCanAutoIncrement(field *gorm.StructField) bool {
114 if value, ok := field.TagSettings["AUTO_INCREMENT"]; ok {
118 if value, ok := field.TagSettingsGet("AUTO_INCREMENT"); ok {
115119 return value != "FALSE"
116120 }
117121 return field.IsPrimaryKey
129133 }
130134
131135 func (s mssql) HasForeignKey(tableName string, foreignKeyName string) bool {
132 return false
136 var count int
137 currentDatabase, tableName := currentDatabaseAndTable(&s, tableName)
138 s.db.QueryRow(`SELECT count(*)
139 FROM sys.foreign_keys as F inner join sys.tables as T on F.parent_object_id=T.object_id
140 inner join information_schema.tables as I on I.TABLE_NAME = T.name
141 WHERE F.name = ?
142 AND T.Name = ? AND I.TABLE_CATALOG = ?;`, foreignKeyName, tableName, currentDatabase).Scan(&count)
143 return count > 0
133144 }
134145
135146 func (s mssql) HasTable(tableName string) bool {
186197 return "DEFAULT VALUES"
187198 }
188199
200 // NormalizeIndexAndColumn returns argument's index name and column name without doing anything
201 func (mssql) NormalizeIndexAndColumn(indexName, columnName string) (string, string) {
202 return indexName, columnName
203 }
204
189205 func currentDatabaseAndTable(dialect gorm.Dialect, tableName string) (string, string) {
190206 if strings.Contains(tableName, ".") {
191207 splitStrings := strings.SplitN(tableName, ".", 2)
193209 }
194210 return dialect.CurrentDatabase(), tableName
195211 }
212
213 // JSON type to support easy handling of JSON data in character table fields
214 // using golang json.RawMessage for deferred decoding/encoding
215 type JSON struct {
216 json.RawMessage
217 }
218
219 // Value get value of JSON
220 func (j JSON) Value() (driver.Value, error) {
221 if len(j.RawMessage) == 0 {
222 return nil, nil
223 }
224 return j.MarshalJSON()
225 }
226
227 // Scan scan value into JSON
228 func (j *JSON) Scan(value interface{}) error {
229 str, ok := value.(string)
230 if !ok {
231 return errors.New(fmt.Sprint("Failed to unmarshal JSONB value (strcast):", value))
232 }
233 bytes := []byte(str)
234 return json.Unmarshal(bytes, j)
235 }
33 "database/sql"
44 "database/sql/driver"
55
6 _ "github.com/lib/pq"
7 "github.com/lib/pq/hstore"
86 "encoding/json"
97 "errors"
108 "fmt"
9 _ "github.com/lib/pq"
10 "github.com/lib/pq/hstore"
1111 )
1212
1313 type Hstore map[string]*string
55 )
66
77 var (
8 // ErrRecordNotFound record not found error, happens when haven't find any matched data when looking up with a struct
8 // ErrRecordNotFound returns a "record not found error". Occurs only when attempting to query the database with a struct; querying with a slice won't return this error
99 ErrRecordNotFound = errors.New("record not found")
10 // ErrInvalidSQL invalid SQL error, happens when you passed invalid SQL
10 // ErrInvalidSQL occurs when you attempt a query with invalid SQL
1111 ErrInvalidSQL = errors.New("invalid SQL")
12 // ErrInvalidTransaction invalid transaction when you are trying to `Commit` or `Rollback`
12 // ErrInvalidTransaction occurs when you are trying to `Commit` or `Rollback`
1313 ErrInvalidTransaction = errors.New("no valid transaction")
1414 // ErrCantStartTransaction can't start transaction when you are trying to start one with `Begin`
1515 ErrCantStartTransaction = errors.New("can't start transaction")
2020 // Errors contains all happened errors
2121 type Errors []error
2222
23 // IsRecordNotFoundError returns current error has record not found error or not
23 // IsRecordNotFoundError returns true if error contains a RecordNotFound error
2424 func IsRecordNotFoundError(err error) bool {
2525 if errs, ok := err.(Errors); ok {
2626 for _, err := range errs {
3232 return err == ErrRecordNotFound
3333 }
3434
35 // GetErrors gets all happened errors
35 // GetErrors gets all errors that have occurred and returns a slice of errors (Error type)
3636 func (errs Errors) GetErrors() []error {
3737 return errs
3838 }
3939
40 // Add adds an error
40 // Add adds an error to a given slice of errors
4141 func (errs Errors) Add(newErrors ...error) Errors {
4242 for _, err := range newErrors {
4343 if err == nil {
6161 return errs
6262 }
6363
64 // Error format happened errors
64 // Error takes a slice of all errors that have occurred and returns it as a formatted string
6565 func (errs Errors) Error() string {
6666 var errors = []string{}
6767 for _, e := range errs {
11
22 import (
33 "database/sql"
4 "database/sql/driver"
45 "errors"
56 "fmt"
67 "reflect"
4344 if reflectValue.Type().ConvertibleTo(fieldValue.Type()) {
4445 fieldValue.Set(reflectValue.Convert(fieldValue.Type()))
4546 } else if scanner, ok := fieldValue.Addr().Interface().(sql.Scanner); ok {
46 err = scanner.Scan(reflectValue.Interface())
47 v := reflectValue.Interface()
48 if valuer, ok := v.(driver.Valuer); ok {
49 if v, err = valuer.Value(); err == nil {
50 err = scanner.Scan(v)
51 }
52 } else {
53 err = scanner.Scan(v)
54 }
4755 } else {
4856 err = fmt.Errorf("could not convert argument of field %s from %s to %s", field.Name, reflectValue.Type(), fieldValue.Type())
4957 }
00 package gorm_test
11
22 import (
3 "database/sql/driver"
4 "encoding/hex"
5 "fmt"
36 "testing"
47
58 "github.com/jinzhu/gorm"
4245
4346 if field, ok := scope.FieldByName("embedded_name"); !ok {
4447 t.Errorf("should find embedded field")
45 } else if _, ok := field.TagSettings["NOT NULL"]; !ok {
48 } else if _, ok := field.TagSettingsGet("NOT NULL"); !ok {
4649 t.Errorf("should find embedded field's tag settings")
4750 }
4851 }
52
53 type UUID [16]byte
54
55 type NullUUID struct {
56 UUID
57 Valid bool
58 }
59
60 func FromString(input string) (u UUID) {
61 src := []byte(input)
62 return FromBytes(src)
63 }
64
65 func FromBytes(src []byte) (u UUID) {
66 dst := u[:]
67 hex.Decode(dst[0:4], src[0:8])
68 hex.Decode(dst[4:6], src[9:13])
69 hex.Decode(dst[6:8], src[14:18])
70 hex.Decode(dst[8:10], src[19:23])
71 hex.Decode(dst[10:], src[24:])
72 return
73 }
74
75 func (u UUID) String() string {
76 buf := make([]byte, 36)
77 src := u[:]
78 hex.Encode(buf[0:8], src[0:4])
79 buf[8] = '-'
80 hex.Encode(buf[9:13], src[4:6])
81 buf[13] = '-'
82 hex.Encode(buf[14:18], src[6:8])
83 buf[18] = '-'
84 hex.Encode(buf[19:23], src[8:10])
85 buf[23] = '-'
86 hex.Encode(buf[24:], src[10:])
87 return string(buf)
88 }
89
90 func (u UUID) Value() (driver.Value, error) {
91 return u.String(), nil
92 }
93
94 func (u *UUID) Scan(src interface{}) error {
95 switch src := src.(type) {
96 case UUID: // support gorm convert from UUID to NullUUID
97 *u = src
98 return nil
99 case []byte:
100 *u = FromBytes(src)
101 return nil
102 case string:
103 *u = FromString(src)
104 return nil
105 }
106 return fmt.Errorf("uuid: cannot convert %T to UUID", src)
107 }
108
109 func (u *NullUUID) Scan(src interface{}) error {
110 u.Valid = true
111 return u.UUID.Scan(src)
112 }
113
114 func TestFieldSet(t *testing.T) {
115 type TestFieldSetNullUUID struct {
116 NullUUID NullUUID
117 }
118 scope := DB.NewScope(&TestFieldSetNullUUID{})
119 field := scope.Fields()[0]
120 err := field.Set(FromString("3034d44a-da03-11e8-b366-4a00070b9f00"))
121 if err != nil {
122 t.Fatal(err)
123 }
124 if id, ok := field.Field.Addr().Interface().(*NullUUID); !ok {
125 t.Fatal()
126 } else if !id.Valid || id.UUID.String() != "3034d44a-da03-11e8-b366-4a00070b9f00" {
127 t.Fatal(id)
128 }
129 }
0 module github.com/jinzhu/gorm
1
2 require (
3 github.com/denisenkom/go-mssqldb v0.0.0-20190423183735-731ef375ac02
4 github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5
5 github.com/go-sql-driver/mysql v1.4.1
6 github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a
7 github.com/jinzhu/now v1.0.0
8 github.com/lib/pq v1.1.0
9 github.com/mattn/go-sqlite3 v1.10.0
10 )
0 cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
1 cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
2 cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU=
3 cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
4 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
5 github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
6 github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
7 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
8 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
9 github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
10 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
11 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
12 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
13 github.com/denisenkom/go-mssqldb v0.0.0-20190423183735-731ef375ac02 h1:PS3xfVPa8N84AzoWZHFCbA0+ikz4f4skktfjQoNMsgk=
14 github.com/denisenkom/go-mssqldb v0.0.0-20190423183735-731ef375ac02/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
15 github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
16 github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
17 github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
18 github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
19 github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
20 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
21 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
22 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
23 github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
24 github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
25 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
26 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
27 github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
28 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
29 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
30 github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
31 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
32 github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
33 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
34 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
35 github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
36 github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
37 github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
38 github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
39 github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
40 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
41 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
42 github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a h1:eeaG9XMUvRBYXJi4pg1ZKM7nxc5AfXfojeLLW7O5J3k=
43 github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
44 github.com/jinzhu/now v1.0.0 h1:6WV8LvwPpDhKjo5U9O6b4+xdG/jTXNPwlDme/MTo8Ns=
45 github.com/jinzhu/now v1.0.0/go.mod h1:oHTiXerJ20+SfYcrdlBO7rzZRJWGwSTQ0iUY2jI6Gfc=
46 github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
47 github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
48 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
49 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
50 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
51 github.com/lib/pq v1.1.0 h1:/5u4a+KGJptBRqGzPvYQL9p0d/tPR4S31+Tnzj9lEO4=
52 github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
53 github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
54 github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
55 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
56 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
57 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
58 github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
59 github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
60 github.com/openzipkin/zipkin-go v0.1.6 h1:yXiysv1CSK7Q5yjGy1710zZGnsbMUIjluWBxtLXHPBo=
61 github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
62 github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
63 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
64 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
65 github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
66 github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
67 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
68 github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
69 github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
70 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
71 github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
72 github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
73 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
74 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
75 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
76 go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
77 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
78 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
79 golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI=
80 golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
81 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
82 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
83 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
84 golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
85 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
86 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
87 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
88 golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
89 golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
90 golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
91 golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
92 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
93 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
94 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
95 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
96 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
97 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
98 golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
99 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
100 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
101 golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
102 golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
103 golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
104 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
105 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
106 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
107 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
108 golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
109 golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
110 golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
111 golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
112 google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
113 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
114 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
115 google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c=
116 google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
117 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
118 google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
119 google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
120 google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
121 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
122 google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU=
123 google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
124 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
125 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
126 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
127 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
128 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
129 gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
130 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
131 honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
132 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
133 honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
6262 formattedValues = append(formattedValues, "NULL")
6363 }
6464 } else {
65 formattedValues = append(formattedValues, fmt.Sprintf("'%v'", value))
65 switch value.(type) {
66 case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64, bool:
67 formattedValues = append(formattedValues, fmt.Sprintf("%v", value))
68 default:
69 formattedValues = append(formattedValues, fmt.Sprintf("'%v'", value))
70 }
6671 }
6772 } else {
6873 formattedValues = append(formattedValues, "NULL")
55 "fmt"
66 "reflect"
77 "strings"
8 "sync"
89 "time"
910 )
1011
1112 // DB contains information for current db connection
1213 type DB struct {
14 sync.RWMutex
1315 Value interface{}
1416 Error error
1517 RowsAffected int64
1719 // single db
1820 db SQLCommon
1921 blockGlobalUpdate bool
20 logMode int
22 logMode logModeValue
2123 logger logger
2224 search *search
23 values map[string]interface{}
25 values sync.Map
2426
2527 // global db
2628 parent *DB
2830 dialect Dialect
2931 singularTable bool
3032 }
33
34 type logModeValue int
35
36 const (
37 defaultLogMode logModeValue = iota
38 noLogMode
39 detailedLogMode
40 )
3141
3242 // Open initialize a new db connection, need to import driver first, e.g:
3343 //
4757 }
4858 var source string
4959 var dbSQL SQLCommon
60 var ownDbSQL bool
5061
5162 switch value := args[0].(type) {
5263 case string:
5869 source = args[1].(string)
5970 }
6071 dbSQL, err = sql.Open(driver, source)
72 ownDbSQL = true
6173 case SQLCommon:
6274 dbSQL = value
75 ownDbSQL = false
76 default:
77 return nil, fmt.Errorf("invalid database source: %v is not a valid type", value)
6378 }
6479
6580 db = &DB{
6681 db: dbSQL,
6782 logger: defaultLogger,
68 values: map[string]interface{}{},
6983 callbacks: DefaultCallback,
7084 dialect: newDialect(dialect, dbSQL),
7185 }
7589 }
7690 // Send a ping to make sure the database connection is alive.
7791 if d, ok := dbSQL.(*sql.DB); ok {
78 if err = d.Ping(); err != nil {
92 if err = d.Ping(); err != nil && ownDbSQL {
7993 d.Close()
8094 }
8195 }
116130
117131 // Dialect get dialect
118132 func (s *DB) Dialect() Dialect {
119 return s.parent.dialect
133 return s.dialect
120134 }
121135
122136 // Callback return `Callbacks` container, you could add/change/delete callbacks with it
123137 // db.Callback().Create().Register("update_created_at", updateCreated)
124138 // Refer https://jinzhu.github.io/gorm/development.html#callbacks
125139 func (s *DB) Callback() *Callback {
126 s.parent.callbacks = s.parent.callbacks.clone()
140 s.parent.callbacks = s.parent.callbacks.clone(s.logger)
127141 return s.parent.callbacks
128142 }
129143
135149 // LogMode set log mode, `true` for detailed logs, `false` for no log, default, will only print error logs
136150 func (s *DB) LogMode(enable bool) *DB {
137151 if enable {
138 s.logMode = 2
152 s.logMode = detailedLogMode
139153 } else {
140 s.logMode = 1
154 s.logMode = noLogMode
141155 }
142156 return s
143157 }
156170
157171 // SingularTable use singular table by default
158172 func (s *DB) SingularTable(enable bool) {
159 modelStructsMap = newModelStructsMap()
173 s.parent.Lock()
174 defer s.parent.Unlock()
160175 s.parent.singularTable = enable
161176 }
162177
164179 func (s *DB) NewScope(value interface{}) *Scope {
165180 dbClone := s.clone()
166181 dbClone.Value = value
167 return &Scope{db: dbClone, Search: dbClone.search.clone(), Value: value}
182 scope := &Scope{db: dbClone, Value: value}
183 if s.search != nil {
184 scope.Search = s.search.clone()
185 } else {
186 scope.Search = &search{}
187 }
188 return scope
168189 }
169190
170191 // QueryExpr returns the query as expr object
284305 func (s *DB) First(out interface{}, where ...interface{}) *DB {
285306 newScope := s.NewScope(out)
286307 newScope.Search.Limit(1)
308
287309 return newScope.Set("gorm:order_by_primary_key", "ASC").
288310 inlineCondition(where...).callCallbacks(s.parent.callbacks.queries).db
289311 }
306328 // Find find records that match given conditions
307329 func (s *DB) Find(out interface{}, where ...interface{}) *DB {
308330 return s.NewScope(out).inlineCondition(where...).callCallbacks(s.parent.callbacks.queries).db
331 }
332
333 //Preloads preloads relations, don`t touch out
334 func (s *DB) Preloads(out interface{}) *DB {
335 return s.NewScope(out).InstanceSet("gorm:only_preload", 1).callCallbacks(s.parent.callbacks.queries).db
309336 }
310337
311338 // Scan scan value to a struct
481508 if db, ok := c.db.(sqlDb); ok && db != nil {
482509 tx, err := db.Begin()
483510 c.db = interface{}(tx).(SQLCommon)
511
512 c.dialect.SetDB(c.db)
484513 c.AddError(err)
485514 } else {
486515 c.AddError(ErrCantStartTransaction)
490519
491520 // Commit commit a transaction
492521 func (s *DB) Commit() *DB {
493 if db, ok := s.db.(sqlTx); ok && db != nil {
522 var emptySQLTx *sql.Tx
523 if db, ok := s.db.(sqlTx); ok && db != nil && db != emptySQLTx {
494524 s.AddError(db.Commit())
495525 } else {
496526 s.AddError(ErrInvalidTransaction)
500530
501531 // Rollback rollback a transaction
502532 func (s *DB) Rollback() *DB {
503 if db, ok := s.db.(sqlTx); ok && db != nil {
533 var emptySQLTx *sql.Tx
534 if db, ok := s.db.(sqlTx); ok && db != nil && db != emptySQLTx {
504535 s.AddError(db.Rollback())
505536 } else {
506537 s.AddError(ErrInvalidTransaction)
669700
670701 // InstantSet instant set setting, will affect current db
671702 func (s *DB) InstantSet(name string, value interface{}) *DB {
672 s.values[name] = value
703 s.values.Store(name, value)
673704 return s
674705 }
675706
676707 // Get get setting by name
677708 func (s *DB) Get(name string) (value interface{}, ok bool) {
678 value, ok = s.values[name]
709 value, ok = s.values.Load(name)
679710 return
680711 }
681712
684715 scope := s.NewScope(source)
685716 for _, field := range scope.GetModelStruct().StructFields {
686717 if field.Name == column || field.DBName == column {
687 if many2many := field.TagSettings["MANY2MANY"]; many2many != "" {
718 if many2many, _ := field.TagSettingsGet("MANY2MANY"); many2many != "" {
688719 source := (&Scope{Value: source}).GetModelStruct().ModelType
689720 destination := (&Scope{Value: reflect.New(field.Struct.Type).Interface()}).GetModelStruct().ModelType
690721 handler.Setup(field.Relationship, many2many, source, destination)
701732 func (s *DB) AddError(err error) error {
702733 if err != nil {
703734 if err != ErrRecordNotFound {
704 if s.logMode == 0 {
705 go s.print(fileWithLineNum(), err)
735 if s.logMode == defaultLogMode {
736 go s.print("error", fileWithLineNum(), err)
706737 } else {
707738 s.log(err)
708739 }
739770 parent: s.parent,
740771 logger: s.logger,
741772 logMode: s.logMode,
742 values: map[string]interface{}{},
743773 Value: s.Value,
744774 Error: s.Error,
745775 blockGlobalUpdate: s.blockGlobalUpdate,
746 }
747
748 for key, value := range s.values {
749 db.values[key] = value
750 }
776 dialect: newDialect(s.dialect.GetName(), s.db),
777 }
778
779 s.values.Range(func(k, v interface{}) bool {
780 db.values.Store(k, v)
781 return true
782 })
751783
752784 if s.search == nil {
753785 db.search = &search{limit: -1, offset: -1}
764796 }
765797
766798 func (s *DB) log(v ...interface{}) {
767 if s != nil && s.logMode == 2 {
799 if s != nil && s.logMode == detailedLogMode {
768800 s.print(append([]interface{}{"log", fileWithLineNum()}, v...)...)
769801 }
770802 }
771803
772804 func (s *DB) slog(sql string, t time.Time, vars ...interface{}) {
773 if s.logMode == 2 {
805 if s.logMode == detailedLogMode {
774806 s.print("sql", fileWithLineNum(), NowFunc().Sub(t), sql, vars, s.RowsAffected)
775807 }
776808 }
77 "path/filepath"
88 "reflect"
99 "strconv"
10 "strings"
11 "sync"
1012 "testing"
1113 "time"
1214
7880 return
7981 }
8082
83 func TestOpen_ReturnsError_WithBadArgs(t *testing.T) {
84 stringRef := "foo"
85 testCases := []interface{}{42, time.Now(), &stringRef}
86 for _, tc := range testCases {
87 t.Run(fmt.Sprintf("%v", tc), func(t *testing.T) {
88 _, err := gorm.Open("postgresql", tc)
89 if err == nil {
90 t.Error("Should got error with invalid database source")
91 }
92 if !strings.HasPrefix(err.Error(), "invalid database source:") {
93 t.Errorf("Should got error starting with \"invalid database source:\", but got %q", err.Error())
94 }
95 })
96 }
97 }
98
8199 func TestStringPrimaryKey(t *testing.T) {
82100 type UUIDStruct struct {
83101 ID string `gorm:"primary_key"`
256274 if DB.NewScope([]Cart{}).TableName() != "shopping_cart" {
257275 t.Errorf("[]Cart's singular table name should be shopping_cart")
258276 }
277 DB.SingularTable(false)
278 }
279
280 func TestTableNameConcurrently(t *testing.T) {
281 DB := DB.Model("")
282 if DB.NewScope(Order{}).TableName() != "orders" {
283 t.Errorf("Order's table name should be orders")
284 }
285
286 var wg sync.WaitGroup
287 wg.Add(10)
288
289 for i := 1; i <= 10; i++ {
290 go func(db *gorm.DB) {
291 DB.SingularTable(true)
292 wg.Done()
293 }(DB)
294 }
295 wg.Wait()
296
297 if DB.NewScope(Order{}).TableName() != "order" {
298 t.Errorf("Order's singular table name should be order")
299 }
300
259301 DB.SingularTable(false)
260302 }
261303
560602 db5 := DB.Joins("join emails on emails.user_id = users.id AND emails.email = ?", "join1@example.com").Joins("join credit_cards on credit_cards.user_id = users.id AND credit_cards.number = ?", "411111111111").Where(User{Id: 1}).Where(Email{Id: 1}).Not(Email{Id: 10}).First(&users5)
561603 if db5.Error != nil {
562604 t.Errorf("Should not raise error for join where identical fields in different tables. Error: %s", db5.Error.Error())
605 }
606 }
607
608 type JoinedIds struct {
609 UserID int64 `gorm:"column:id"`
610 BillingAddressID int64 `gorm:"column:id"`
611 EmailID int64 `gorm:"column:id"`
612 }
613
614 func TestScanIdenticalColumnNames(t *testing.T) {
615 var user = User{
616 Name: "joinsIds",
617 Email: "joinIds@example.com",
618 BillingAddress: Address{
619 Address1: "One Park Place",
620 },
621 Emails: []Email{{Email: "join1@example.com"}, {Email: "join2@example.com"}},
622 }
623 DB.Save(&user)
624
625 var users []JoinedIds
626 DB.Select("users.id, addresses.id, emails.id").Table("users").
627 Joins("left join addresses on users.billing_address_id = addresses.id").
628 Joins("left join emails on emails.user_id = users.id").
629 Where("name = ?", "joinsIds").Scan(&users)
630
631 if len(users) != 2 {
632 t.Fatal("should find two rows using left join")
633 }
634
635 if user.Id != users[0].UserID {
636 t.Errorf("Expected result row to contain UserID %d, but got %d", user.Id, users[0].UserID)
637 }
638 if user.Id != users[1].UserID {
639 t.Errorf("Expected result row to contain UserID %d, but got %d", user.Id, users[1].UserID)
640 }
641
642 if user.BillingAddressID.Int64 != users[0].BillingAddressID {
643 t.Errorf("Expected result row to contain BillingAddressID %d, but got %d", user.BillingAddressID.Int64, users[0].BillingAddressID)
644 }
645 if user.BillingAddressID.Int64 != users[1].BillingAddressID {
646 t.Errorf("Expected result row to contain BillingAddressID %d, but got %d", user.BillingAddressID.Int64, users[0].BillingAddressID)
647 }
648
649 if users[0].EmailID == users[1].EmailID {
650 t.Errorf("Email ids should be unique. Got %d and %d", users[0].EmailID, users[1].EmailID)
651 }
652
653 if int64(user.Emails[0].Id) != users[0].EmailID && int64(user.Emails[1].Id) != users[0].EmailID {
654 t.Errorf("Expected result row ID to be either %d or %d, but was %d", user.Emails[0].Id, user.Emails[1].Id, users[0].EmailID)
655 }
656
657 if int64(user.Emails[0].Id) != users[1].EmailID && int64(user.Emails[1].Id) != users[1].EmailID {
658 t.Errorf("Expected result row ID to be either %d or %d, but was %d", user.Emails[0].Id, user.Emails[1].Id, users[1].EmailID)
563659 }
564660 }
565661
861957 }
862958 }
863959
960 func TestSaveAssociations(t *testing.T) {
961 db := DB.New()
962 deltaAddressCount := 0
963 if err := db.Model(&Address{}).Count(&deltaAddressCount).Error; err != nil {
964 t.Errorf("failed to fetch address count")
965 t.FailNow()
966 }
967
968 placeAddress := &Address{
969 Address1: "somewhere on earth",
970 }
971 ownerAddress1 := &Address{
972 Address1: "near place address",
973 }
974 ownerAddress2 := &Address{
975 Address1: "address2",
976 }
977 db.Create(placeAddress)
978
979 addressCountShouldBe := func(t *testing.T, expectedCount int) {
980 countFromDB := 0
981 t.Helper()
982 err := db.Model(&Address{}).Count(&countFromDB).Error
983 if err != nil {
984 t.Error("failed to fetch address count")
985 }
986 if countFromDB != expectedCount {
987 t.Errorf("address count mismatch: %d", countFromDB)
988 }
989 }
990 addressCountShouldBe(t, deltaAddressCount+1)
991
992 // owner address should be created, place address should be reused
993 place1 := &Place{
994 PlaceAddressID: placeAddress.ID,
995 PlaceAddress: placeAddress,
996 OwnerAddress: ownerAddress1,
997 }
998 err := db.Create(place1).Error
999 if err != nil {
1000 t.Errorf("failed to store place: %s", err.Error())
1001 }
1002 addressCountShouldBe(t, deltaAddressCount+2)
1003
1004 // owner address should be created again, place address should be reused
1005 place2 := &Place{
1006 PlaceAddressID: placeAddress.ID,
1007 PlaceAddress: &Address{
1008 ID: 777,
1009 Address1: "address1",
1010 },
1011 OwnerAddress: ownerAddress2,
1012 OwnerAddressID: 778,
1013 }
1014 err = db.Create(place2).Error
1015 if err != nil {
1016 t.Errorf("failed to store place: %s", err.Error())
1017 }
1018 addressCountShouldBe(t, deltaAddressCount+3)
1019
1020 count := 0
1021 db.Model(&Place{}).Where(&Place{
1022 PlaceAddressID: placeAddress.ID,
1023 OwnerAddressID: ownerAddress1.ID,
1024 }).Count(&count)
1025 if count != 1 {
1026 t.Errorf("only one instance of (%d, %d) should be available, found: %d",
1027 placeAddress.ID, ownerAddress1.ID, count)
1028 }
1029
1030 db.Model(&Place{}).Where(&Place{
1031 PlaceAddressID: placeAddress.ID,
1032 OwnerAddressID: ownerAddress2.ID,
1033 }).Count(&count)
1034 if count != 1 {
1035 t.Errorf("only one instance of (%d, %d) should be available, found: %d",
1036 placeAddress.ID, ownerAddress2.ID, count)
1037 }
1038
1039 db.Model(&Place{}).Where(&Place{
1040 PlaceAddressID: placeAddress.ID,
1041 }).Count(&count)
1042 if count != 2 {
1043 t.Errorf("two instances of (%d) should be available, found: %d",
1044 placeAddress.ID, count)
1045 }
1046 }
1047
8641048 func TestBlockGlobalUpdate(t *testing.T) {
8651049 db := DB.New()
8661050 db.Create(&Toy{Name: "Stuffed Animal", OwnerType: "Nobody"})
8971081 if err != nil {
8981082 t.Error("Unexpected error on conditional delete")
8991083 }
1084 }
1085
1086 func TestCountWithHaving(t *testing.T) {
1087 db := DB.New()
1088 db.Delete(User{})
1089 defer db.Delete(User{})
1090
1091 DB.Create(getPreparedUser("user1", "pluck_user"))
1092 DB.Create(getPreparedUser("user2", "pluck_user"))
1093 user3 := getPreparedUser("user3", "pluck_user")
1094 user3.Languages = []Language{}
1095 DB.Create(user3)
1096
1097 var count int
1098 err := db.Model(User{}).Select("users.id").
1099 Joins("LEFT JOIN user_languages ON user_languages.user_id = users.id").
1100 Joins("LEFT JOIN languages ON user_languages.language_id = languages.id").
1101 Group("users.id").Having("COUNT(languages.id) > 1").Count(&count).Error
1102
1103 if err != nil {
1104 t.Error("Unexpected error on query count with having")
1105 }
1106
1107 if count != 2 {
1108 t.Error("Unexpected result on query count with having")
1109 }
1110 }
1111
1112 func TestPluck(t *testing.T) {
1113 db := DB.New()
1114 db.Delete(User{})
1115 defer db.Delete(User{})
1116
1117 DB.Create(&User{Id: 1, Name: "user1"})
1118 DB.Create(&User{Id: 2, Name: "user2"})
1119 DB.Create(&User{Id: 3, Name: "user3"})
1120
1121 var ids []int64
1122 err := db.Model(User{}).Order("id").Pluck("id", &ids).Error
1123
1124 if err != nil {
1125 t.Error("Unexpected error on pluck")
1126 }
1127
1128 if len(ids) != 3 || ids[0] != 1 || ids[1] != 2 || ids[2] != 3 {
1129 t.Error("Unexpected result on pluck")
1130 }
1131
1132 err = db.Model(User{}).Order("id").Pluck("id", &ids).Error
1133
1134 if err != nil {
1135 t.Error("Unexpected error on pluck again")
1136 }
1137
1138 if len(ids) != 3 || ids[0] != 1 || ids[1] != 2 || ids[2] != 3 {
1139 t.Error("Unexpected result on pluck again")
1140 }
1141 }
1142
1143 func TestCountWithQueryOption(t *testing.T) {
1144 db := DB.New()
1145 db.Delete(User{})
1146 defer db.Delete(User{})
1147
1148 DB.Create(&User{Name: "user1"})
1149 DB.Create(&User{Name: "user2"})
1150 DB.Create(&User{Name: "user3"})
1151
1152 var count int
1153 err := db.Model(User{}).Select("users.id").
1154 Set("gorm:query_option", "WHERE users.name='user2'").
1155 Count(&count).Error
1156
1157 if err != nil {
1158 t.Error("Unexpected error on query count with query_option")
1159 }
1160
1161 if count != 1 {
1162 t.Error("Unexpected result on query count with query_option")
1163 }
1164 }
1165
1166 func TestFloatColumnPrecision(t *testing.T) {
1167 if dialect := os.Getenv("GORM_DIALECT"); dialect != "mysql" && dialect != "sqlite" {
1168 t.Skip()
1169 }
1170
1171 type FloatTest struct {
1172 ID string `gorm:"primary_key"`
1173 FloatValue float64 `gorm:"column:float_value" sql:"type:float(255,5);"`
1174 }
1175 DB.DropTable(&FloatTest{})
1176 DB.AutoMigrate(&FloatTest{})
1177
1178 data := FloatTest{ID: "uuid", FloatValue: 112.57315}
1179 if err := DB.Save(&data).Error; err != nil || data.ID != "uuid" || data.FloatValue != 112.57315 {
1180 t.Errorf("Float value should not lose precision")
1181 }
1182 }
1183
1184 func TestWhereUpdates(t *testing.T) {
1185 type OwnerEntity struct {
1186 gorm.Model
1187 OwnerID uint
1188 OwnerType string
1189 }
1190
1191 type SomeEntity struct {
1192 gorm.Model
1193 Name string
1194 OwnerEntity OwnerEntity `gorm:"polymorphic:Owner"`
1195 }
1196
1197 db := DB.Debug()
1198 db.DropTable(&SomeEntity{})
1199 db.AutoMigrate(&SomeEntity{})
1200
1201 a := SomeEntity{Name: "test"}
1202 db.Model(&a).Where(a).Updates(SomeEntity{Name: "test2"})
9001203 }
9011204
9021205 func BenchmarkGorm(b *testing.B) {
117117 Owner *User `sql:"-"`
118118 }
119119
120 type Place struct {
121 Id int64
122 PlaceAddressID int
123 PlaceAddress *Address `gorm:"save_associations:false"`
124 OwnerAddressID int
125 OwnerAddress *Address `gorm:"save_associations:true"`
126 }
127
120128 type EncryptedData []byte
121129
122130 func (data *EncryptedData) Scan(value interface{}) error {
283291 DB.Exec(fmt.Sprintf("drop table %v;", table))
284292 }
285293
286 values := []interface{}{&Short{}, &ReallyLongThingThatReferencesShort{}, &ReallyLongTableNameToTestMySQLNameLengthLimit{}, &NotSoLongTableName{}, &Product{}, &Email{}, &Address{}, &CreditCard{}, &Company{}, &Role{}, &Language{}, &HNPost{}, &EngadgetPost{}, &Animal{}, &User{}, &JoinTable{}, &Post{}, &Category{}, &Comment{}, &Cat{}, &Dog{}, &Hamster{}, &Toy{}, &ElementWithIgnoredField{}}
294 values := []interface{}{&Short{}, &ReallyLongThingThatReferencesShort{}, &ReallyLongTableNameToTestMySQLNameLengthLimit{}, &NotSoLongTableName{}, &Product{}, &Email{}, &Address{}, &CreditCard{}, &Company{}, &Role{}, &Language{}, &HNPost{}, &EngadgetPost{}, &Animal{}, &User{}, &JoinTable{}, &Post{}, &Category{}, &Comment{}, &Cat{}, &Dog{}, &Hamster{}, &Toy{}, &ElementWithIgnoredField{}, &Place{}}
287295 for _, value := range values {
288296 DB.DropTable(value)
289297 }
397405 }
398406 }
399407
408 func TestCreateAndAutomigrateTransaction(t *testing.T) {
409 tx := DB.Begin()
410
411 func() {
412 type Bar struct {
413 ID uint
414 }
415 DB.DropTableIfExists(&Bar{})
416
417 if ok := DB.HasTable("bars"); ok {
418 t.Errorf("Table should not exist, but does")
419 }
420
421 if ok := tx.HasTable("bars"); ok {
422 t.Errorf("Table should not exist, but does")
423 }
424 }()
425
426 func() {
427 type Bar struct {
428 Name string
429 }
430 err := tx.CreateTable(&Bar{}).Error
431
432 if err != nil {
433 t.Errorf("Should have been able to create the table, but couldn't: %s", err)
434 }
435
436 if ok := tx.HasTable(&Bar{}); !ok {
437 t.Errorf("The transaction should be able to see the table")
438 }
439 }()
440
441 func() {
442 type Bar struct {
443 Stuff string
444 }
445
446 err := tx.AutoMigrate(&Bar{}).Error
447 if err != nil {
448 t.Errorf("Should have been able to alter the table, but couldn't")
449 }
450 }()
451
452 tx.Rollback()
453 }
454
400455 type MultipleIndexes struct {
401456 ID int64
402457 UserID int64 `sql:"unique_index:uix_multipleindexes_user_name,uix_multipleindexes_user_email;index:idx_multipleindexes_user_other"`
482537 t.Errorf("No error should happen when ModifyColumn, but got %v", err)
483538 }
484539 }
540
541 func TestIndexWithPrefixLength(t *testing.T) {
542 if dialect := os.Getenv("GORM_DIALECT"); dialect != "mysql" {
543 t.Skip("Skipping this because only mysql support setting an index prefix length")
544 }
545
546 type IndexWithPrefix struct {
547 gorm.Model
548 Name string
549 Description string `gorm:"type:text;index:idx_index_with_prefixes_length(100)"`
550 }
551 type IndexesWithPrefix struct {
552 gorm.Model
553 Name string
554 Description1 string `gorm:"type:text;index:idx_index_with_prefixes_length(100)"`
555 Description2 string `gorm:"type:text;index:idx_index_with_prefixes_length(100)"`
556 }
557 type IndexesWithPrefixAndWithoutPrefix struct {
558 gorm.Model
559 Name string `gorm:"index:idx_index_with_prefixes_length"`
560 Description string `gorm:"type:text;index:idx_index_with_prefixes_length(100)"`
561 }
562 tables := []interface{}{&IndexWithPrefix{}, &IndexesWithPrefix{}, &IndexesWithPrefixAndWithoutPrefix{}}
563 for _, table := range tables {
564 scope := DB.NewScope(table)
565 tableName := scope.TableName()
566 t.Run(fmt.Sprintf("Create index with prefix length: %s", tableName), func(t *testing.T) {
567 if err := DB.DropTableIfExists(table).Error; err != nil {
568 t.Errorf("Failed to drop %s table: %v", tableName, err)
569 }
570 if err := DB.CreateTable(table).Error; err != nil {
571 t.Errorf("Failed to create %s table: %v", tableName, err)
572 }
573 if !scope.Dialect().HasIndex(tableName, "idx_index_with_prefixes_length") {
574 t.Errorf("Failed to create %s table index:", tableName)
575 }
576 })
577 }
578 }
1616 return defaultTableName
1717 }
1818
19 type safeModelStructsMap struct {
20 m map[reflect.Type]*ModelStruct
21 l *sync.RWMutex
22 }
23
24 func (s *safeModelStructsMap) Set(key reflect.Type, value *ModelStruct) {
19 var modelStructsMap sync.Map
20
21 // ModelStruct model definition
22 type ModelStruct struct {
23 PrimaryFields []*StructField
24 StructFields []*StructField
25 ModelType reflect.Type
26
27 defaultTableName string
28 l sync.Mutex
29 }
30
31 // TableName returns model's table name
32 func (s *ModelStruct) TableName(db *DB) string {
2533 s.l.Lock()
2634 defer s.l.Unlock()
27 s.m[key] = value
28 }
29
30 func (s *safeModelStructsMap) Get(key reflect.Type) *ModelStruct {
31 s.l.RLock()
32 defer s.l.RUnlock()
33 return s.m[key]
34 }
35
36 func newModelStructsMap() *safeModelStructsMap {
37 return &safeModelStructsMap{l: new(sync.RWMutex), m: make(map[reflect.Type]*ModelStruct)}
38 }
39
40 var modelStructsMap = newModelStructsMap()
41
42 // ModelStruct model definition
43 type ModelStruct struct {
44 PrimaryFields []*StructField
45 StructFields []*StructField
46 ModelType reflect.Type
47 defaultTableName string
48 }
49
50 // TableName get model's table name
51 func (s *ModelStruct) TableName(db *DB) string {
35
5236 if s.defaultTableName == "" && db != nil && s.ModelType != nil {
5337 // Set default table name
5438 if tabler, ok := reflect.New(s.ModelType).Interface().(tabler); ok {
5539 s.defaultTableName = tabler.TableName()
5640 } else {
57 tableName := ToDBName(s.ModelType.Name())
58 if db == nil || !db.parent.singularTable {
41 tableName := ToTableName(s.ModelType.Name())
42 db.parent.RLock()
43 if db == nil || (db.parent != nil && !db.parent.singularTable) {
5944 tableName = inflection.Plural(tableName)
6045 }
46 db.parent.RUnlock()
6147 s.defaultTableName = tableName
6248 }
6349 }
8066 Struct reflect.StructField
8167 IsForeignKey bool
8268 Relationship *Relationship
83 }
84
85 func (structField *StructField) clone() *StructField {
69
70 tagSettingsLock sync.RWMutex
71 }
72
73 // TagSettingsSet Sets a tag in the tag settings map
74 func (sf *StructField) TagSettingsSet(key, val string) {
75 sf.tagSettingsLock.Lock()
76 defer sf.tagSettingsLock.Unlock()
77 sf.TagSettings[key] = val
78 }
79
80 // TagSettingsGet returns a tag from the tag settings
81 func (sf *StructField) TagSettingsGet(key string) (string, bool) {
82 sf.tagSettingsLock.RLock()
83 defer sf.tagSettingsLock.RUnlock()
84 val, ok := sf.TagSettings[key]
85 return val, ok
86 }
87
88 // TagSettingsDelete deletes a tag
89 func (sf *StructField) TagSettingsDelete(key string) {
90 sf.tagSettingsLock.Lock()
91 defer sf.tagSettingsLock.Unlock()
92 delete(sf.TagSettings, key)
93 }
94
95 func (sf *StructField) clone() *StructField {
8696 clone := &StructField{
87 DBName: structField.DBName,
88 Name: structField.Name,
89 Names: structField.Names,
90 IsPrimaryKey: structField.IsPrimaryKey,
91 IsNormal: structField.IsNormal,
92 IsIgnored: structField.IsIgnored,
93 IsScanner: structField.IsScanner,
94 HasDefaultValue: structField.HasDefaultValue,
95 Tag: structField.Tag,
97 DBName: sf.DBName,
98 Name: sf.Name,
99 Names: sf.Names,
100 IsPrimaryKey: sf.IsPrimaryKey,
101 IsNormal: sf.IsNormal,
102 IsIgnored: sf.IsIgnored,
103 IsScanner: sf.IsScanner,
104 HasDefaultValue: sf.HasDefaultValue,
105 Tag: sf.Tag,
96106 TagSettings: map[string]string{},
97 Struct: structField.Struct,
98 IsForeignKey: structField.IsForeignKey,
99 }
100
101 if structField.Relationship != nil {
102 relationship := *structField.Relationship
107 Struct: sf.Struct,
108 IsForeignKey: sf.IsForeignKey,
109 }
110
111 if sf.Relationship != nil {
112 relationship := *sf.Relationship
103113 clone.Relationship = &relationship
104114 }
105115
106 for key, value := range structField.TagSettings {
116 // copy the struct field tagSettings, they should be read-locked while they are copied
117 sf.tagSettingsLock.Lock()
118 defer sf.tagSettingsLock.Unlock()
119 for key, value := range sf.TagSettings {
107120 clone.TagSettings[key] = value
108121 }
109122
125138
126139 func getForeignField(column string, fields []*StructField) *StructField {
127140 for _, field := range fields {
128 if field.Name == column || field.DBName == column || field.DBName == ToDBName(column) {
141 if field.Name == column || field.DBName == column || field.DBName == ToColumnName(column) {
129142 return field
130143 }
131144 }
151164 }
152165
153166 // Get Cached model struct
154 if value := modelStructsMap.Get(reflectType); value != nil {
155 return value
167 isSingularTable := false
168 if scope.db != nil && scope.db.parent != nil {
169 scope.db.parent.RLock()
170 isSingularTable = scope.db.parent.singularTable
171 scope.db.parent.RUnlock()
172 }
173
174 hashKey := struct {
175 singularTable bool
176 reflectType reflect.Type
177 }{isSingularTable, reflectType}
178 if value, ok := modelStructsMap.Load(hashKey); ok && value != nil {
179 return value.(*ModelStruct)
156180 }
157181
158182 modelStruct.ModelType = reflectType
169193 }
170194
171195 // is ignored field
172 if _, ok := field.TagSettings["-"]; ok {
196 if _, ok := field.TagSettingsGet("-"); ok {
173197 field.IsIgnored = true
174198 } else {
175 if _, ok := field.TagSettings["PRIMARY_KEY"]; ok {
199 if _, ok := field.TagSettingsGet("PRIMARY_KEY"); ok {
176200 field.IsPrimaryKey = true
177201 modelStruct.PrimaryFields = append(modelStruct.PrimaryFields, field)
178202 }
179203
180 if _, ok := field.TagSettings["DEFAULT"]; ok {
204 if _, ok := field.TagSettingsGet("DEFAULT"); ok {
181205 field.HasDefaultValue = true
182206 }
183207
184 if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok && !field.IsPrimaryKey {
208 if _, ok := field.TagSettingsGet("AUTO_INCREMENT"); ok && !field.IsPrimaryKey {
185209 field.HasDefaultValue = true
186210 }
187211
197221 if indirectType.Kind() == reflect.Struct {
198222 for i := 0; i < indirectType.NumField(); i++ {
199223 for key, value := range parseTagSetting(indirectType.Field(i).Tag) {
200 if _, ok := field.TagSettings[key]; !ok {
201 field.TagSettings[key] = value
224 if _, ok := field.TagSettingsGet(key); !ok {
225 field.TagSettingsSet(key, value)
202226 }
203227 }
204228 }
206230 } else if _, isTime := fieldValue.(*time.Time); isTime {
207231 // is time
208232 field.IsNormal = true
209 } else if _, ok := field.TagSettings["EMBEDDED"]; ok || fieldStruct.Anonymous {
233 } else if _, ok := field.TagSettingsGet("EMBEDDED"); ok || fieldStruct.Anonymous {
210234 // is embedded struct
211235 for _, subField := range scope.New(fieldValue).GetModelStruct().StructFields {
212236 subField = subField.clone()
213237 subField.Names = append([]string{fieldStruct.Name}, subField.Names...)
214 if prefix, ok := field.TagSettings["EMBEDDED_PREFIX"]; ok {
238 if prefix, ok := field.TagSettingsGet("EMBEDDED_PREFIX"); ok {
215239 subField.DBName = prefix + subField.DBName
216240 }
217241
218242 if subField.IsPrimaryKey {
219 if _, ok := subField.TagSettings["PRIMARY_KEY"]; ok {
243 if _, ok := subField.TagSettingsGet("PRIMARY_KEY"); ok {
220244 modelStruct.PrimaryFields = append(modelStruct.PrimaryFields, subField)
221245 } else {
222246 subField.IsPrimaryKey = false
247271 elemType = field.Struct.Type
248272 )
249273
250 if foreignKey := field.TagSettings["FOREIGNKEY"]; foreignKey != "" {
274 if foreignKey, _ := field.TagSettingsGet("FOREIGNKEY"); foreignKey != "" {
251275 foreignKeys = strings.Split(foreignKey, ",")
252276 }
253277
254 if foreignKey := field.TagSettings["ASSOCIATION_FOREIGNKEY"]; foreignKey != "" {
278 if foreignKey, _ := field.TagSettingsGet("ASSOCIATION_FOREIGNKEY"); foreignKey != "" {
255279 associationForeignKeys = strings.Split(foreignKey, ",")
256 } else if foreignKey := field.TagSettings["ASSOCIATIONFOREIGNKEY"]; foreignKey != "" {
280 } else if foreignKey, _ := field.TagSettingsGet("ASSOCIATIONFOREIGNKEY"); foreignKey != "" {
257281 associationForeignKeys = strings.Split(foreignKey, ",")
258282 }
259283
262286 }
263287
264288 if elemType.Kind() == reflect.Struct {
265 if many2many := field.TagSettings["MANY2MANY"]; many2many != "" {
289 if many2many, _ := field.TagSettingsGet("MANY2MANY"); many2many != "" {
266290 relationship.Kind = "many_to_many"
267291
268292 { // Foreign Keys for Source
269293 joinTableDBNames := []string{}
270294
271 if foreignKey := field.TagSettings["JOINTABLE_FOREIGNKEY"]; foreignKey != "" {
295 if foreignKey, _ := field.TagSettingsGet("JOINTABLE_FOREIGNKEY"); foreignKey != "" {
272296 joinTableDBNames = strings.Split(foreignKey, ",")
273297 }
274298
289313 // if defined join table's foreign key
290314 relationship.ForeignDBNames = append(relationship.ForeignDBNames, joinTableDBNames[idx])
291315 } else {
292 defaultJointableForeignKey := ToDBName(reflectType.Name()) + "_" + foreignField.DBName
316 defaultJointableForeignKey := ToColumnName(reflectType.Name()) + "_" + foreignField.DBName
293317 relationship.ForeignDBNames = append(relationship.ForeignDBNames, defaultJointableForeignKey)
294318 }
295319 }
299323 { // Foreign Keys for Association (Destination)
300324 associationJoinTableDBNames := []string{}
301325
302 if foreignKey := field.TagSettings["ASSOCIATION_JOINTABLE_FOREIGNKEY"]; foreignKey != "" {
326 if foreignKey, _ := field.TagSettingsGet("ASSOCIATION_JOINTABLE_FOREIGNKEY"); foreignKey != "" {
303327 associationJoinTableDBNames = strings.Split(foreignKey, ",")
304328 }
305329
320344 relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, associationJoinTableDBNames[idx])
321345 } else {
322346 // join table foreign keys for association
323 joinTableDBName := ToDBName(elemType.Name()) + "_" + field.DBName
347 joinTableDBName := ToColumnName(elemType.Name()) + "_" + field.DBName
324348 relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, joinTableDBName)
325349 }
326350 }
337361 var toFields = toScope.GetStructFields()
338362 relationship.Kind = "has_many"
339363
340 if polymorphic := field.TagSettings["POLYMORPHIC"]; polymorphic != "" {
364 if polymorphic, _ := field.TagSettingsGet("POLYMORPHIC"); polymorphic != "" {
341365 // Dog has many toys, tag polymorphic is Owner, then associationType is Owner
342366 // Toy use OwnerID, OwnerType ('dogs') as foreign key
343367 if polymorphicType := getForeignField(polymorphic+"Type", toFields); polymorphicType != nil {
345369 relationship.PolymorphicType = polymorphicType.Name
346370 relationship.PolymorphicDBName = polymorphicType.DBName
347371 // if Dog has multiple set of toys set name of the set (instead of default 'dogs')
348 if value, ok := field.TagSettings["POLYMORPHIC_VALUE"]; ok {
372 if value, ok := field.TagSettingsGet("POLYMORPHIC_VALUE"); ok {
349373 relationship.PolymorphicValue = value
350374 } else {
351375 relationship.PolymorphicValue = scope.TableName()
427451 tagAssociationForeignKeys []string
428452 )
429453
430 if foreignKey := field.TagSettings["FOREIGNKEY"]; foreignKey != "" {
454 if foreignKey, _ := field.TagSettingsGet("FOREIGNKEY"); foreignKey != "" {
431455 tagForeignKeys = strings.Split(foreignKey, ",")
432456 }
433457
434 if foreignKey := field.TagSettings["ASSOCIATION_FOREIGNKEY"]; foreignKey != "" {
458 if foreignKey, _ := field.TagSettingsGet("ASSOCIATION_FOREIGNKEY"); foreignKey != "" {
435459 tagAssociationForeignKeys = strings.Split(foreignKey, ",")
436 } else if foreignKey := field.TagSettings["ASSOCIATIONFOREIGNKEY"]; foreignKey != "" {
460 } else if foreignKey, _ := field.TagSettingsGet("ASSOCIATIONFOREIGNKEY"); foreignKey != "" {
437461 tagAssociationForeignKeys = strings.Split(foreignKey, ",")
438462 }
439463
440 if polymorphic := field.TagSettings["POLYMORPHIC"]; polymorphic != "" {
464 if polymorphic, _ := field.TagSettingsGet("POLYMORPHIC"); polymorphic != "" {
441465 // Cat has one toy, tag polymorphic is Owner, then associationType is Owner
442466 // Toy use OwnerID, OwnerType ('cats') as foreign key
443467 if polymorphicType := getForeignField(polymorphic+"Type", toFields); polymorphicType != nil {
445469 relationship.PolymorphicType = polymorphicType.Name
446470 relationship.PolymorphicDBName = polymorphicType.DBName
447471 // if Cat has several different types of toys set name for each (instead of default 'cats')
448 if value, ok := field.TagSettings["POLYMORPHIC_VALUE"]; ok {
472 if value, ok := field.TagSettingsGet("POLYMORPHIC_VALUE"); ok {
449473 relationship.PolymorphicValue = value
450474 } else {
451475 relationship.PolymorphicValue = scope.TableName()
583607 }
584608
585609 // Even it is ignored, also possible to decode db value into the field
586 if value, ok := field.TagSettings["COLUMN"]; ok {
610 if value, ok := field.TagSettingsGet("COLUMN"); ok {
587611 field.DBName = value
588612 } else {
589 field.DBName = ToDBName(fieldStruct.Name)
613 field.DBName = ToColumnName(fieldStruct.Name)
590614 }
591615
592616 modelStruct.StructFields = append(modelStruct.StructFields, field)
600624 }
601625 }
602626
603 modelStructsMap.Set(reflectType, &modelStruct)
627 modelStructsMap.Store(hashKey, &modelStruct)
604628
605629 return &modelStruct
606630 }
613637 func parseTagSetting(tags reflect.StructTag) map[string]string {
614638 setting := map[string]string{}
615639 for _, str := range []string{tags.Get("sql"), tags.Get("gorm")} {
640 if str == "" {
641 continue
642 }
616643 tags := strings.Split(str, ";")
617644 for _, value := range tags {
618645 v := strings.Split(value, ":")
0 package gorm
1
2 import (
3 "bytes"
4 "strings"
5 )
6
7 // Namer is a function type which is given a string and return a string
8 type Namer func(string) string
9
10 // NamingStrategy represents naming strategies
11 type NamingStrategy struct {
12 DB Namer
13 Table Namer
14 Column Namer
15 }
16
17 // TheNamingStrategy is being initialized with defaultNamingStrategy
18 var TheNamingStrategy = &NamingStrategy{
19 DB: defaultNamer,
20 Table: defaultNamer,
21 Column: defaultNamer,
22 }
23
24 // AddNamingStrategy sets the naming strategy
25 func AddNamingStrategy(ns *NamingStrategy) {
26 if ns.DB == nil {
27 ns.DB = defaultNamer
28 }
29 if ns.Table == nil {
30 ns.Table = defaultNamer
31 }
32 if ns.Column == nil {
33 ns.Column = defaultNamer
34 }
35 TheNamingStrategy = ns
36 }
37
38 // DBName alters the given name by DB
39 func (ns *NamingStrategy) DBName(name string) string {
40 return ns.DB(name)
41 }
42
43 // TableName alters the given name by Table
44 func (ns *NamingStrategy) TableName(name string) string {
45 return ns.Table(name)
46 }
47
48 // ColumnName alters the given name by Column
49 func (ns *NamingStrategy) ColumnName(name string) string {
50 return ns.Column(name)
51 }
52
53 // ToDBName convert string to db name
54 func ToDBName(name string) string {
55 return TheNamingStrategy.DBName(name)
56 }
57
58 // ToTableName convert string to table name
59 func ToTableName(name string) string {
60 return TheNamingStrategy.TableName(name)
61 }
62
63 // ToColumnName convert string to db name
64 func ToColumnName(name string) string {
65 return TheNamingStrategy.ColumnName(name)
66 }
67
68 var smap = newSafeMap()
69
70 func defaultNamer(name string) string {
71 const (
72 lower = false
73 upper = true
74 )
75
76 if v := smap.Get(name); v != "" {
77 return v
78 }
79
80 if name == "" {
81 return ""
82 }
83
84 var (
85 value = commonInitialismsReplacer.Replace(name)
86 buf = bytes.NewBufferString("")
87 lastCase, currCase, nextCase, nextNumber bool
88 )
89
90 for i, v := range value[:len(value)-1] {
91 nextCase = bool(value[i+1] >= 'A' && value[i+1] <= 'Z')
92 nextNumber = bool(value[i+1] >= '0' && value[i+1] <= '9')
93
94 if i > 0 {
95 if currCase == upper {
96 if lastCase == upper && (nextCase == upper || nextNumber == upper) {
97 buf.WriteRune(v)
98 } else {
99 if value[i-1] != '_' && value[i+1] != '_' {
100 buf.WriteRune('_')
101 }
102 buf.WriteRune(v)
103 }
104 } else {
105 buf.WriteRune(v)
106 if i == len(value)-2 && (nextCase == upper && nextNumber == lower) {
107 buf.WriteRune('_')
108 }
109 }
110 } else {
111 currCase = upper
112 buf.WriteRune(v)
113 }
114 lastCase = currCase
115 currCase = nextCase
116 }
117
118 buf.WriteByte(value[len(value)-1])
119
120 s := strings.ToLower(buf.String())
121 smap.Set(name, s)
122 return s
123 }
0 package gorm_test
1
2 import (
3 "testing"
4
5 "github.com/jinzhu/gorm"
6 )
7
8 func TestTheNamingStrategy(t *testing.T) {
9
10 cases := []struct {
11 name string
12 namer gorm.Namer
13 expected string
14 }{
15 {name: "auth", expected: "auth", namer: gorm.TheNamingStrategy.DB},
16 {name: "userRestrictions", expected: "user_restrictions", namer: gorm.TheNamingStrategy.Table},
17 {name: "clientID", expected: "client_id", namer: gorm.TheNamingStrategy.Column},
18 }
19
20 for _, c := range cases {
21 t.Run(c.name, func(t *testing.T) {
22 result := c.namer(c.name)
23 if result != c.expected {
24 t.Errorf("error in naming strategy. expected: %v got :%v\n", c.expected, result)
25 }
26 })
27 }
28
29 }
30
31 func TestNamingStrategy(t *testing.T) {
32
33 dbNameNS := func(name string) string {
34 return "db_" + name
35 }
36 tableNameNS := func(name string) string {
37 return "tbl_" + name
38 }
39 columnNameNS := func(name string) string {
40 return "col_" + name
41 }
42
43 ns := &gorm.NamingStrategy{
44 DB: dbNameNS,
45 Table: tableNameNS,
46 Column: columnNameNS,
47 }
48
49 cases := []struct {
50 name string
51 namer gorm.Namer
52 expected string
53 }{
54 {name: "auth", expected: "db_auth", namer: ns.DB},
55 {name: "user", expected: "tbl_user", namer: ns.Table},
56 {name: "password", expected: "col_password", namer: ns.Column},
57 }
58
59 for _, c := range cases {
60 t.Run(c.name, func(t *testing.T) {
61 result := c.namer(c.name)
62 if result != c.expected {
63 t.Errorf("error in naming strategy. expected: %v got :%v\n", c.expected, result)
64 }
65 })
66 }
67
68 }
122122 }
123123 }
124124
125 func TestAutoPreloadFalseDoesntPreload(t *testing.T) {
126 user1 := getPreloadUser("auto_user1")
127 DB.Save(user1)
128
129 preloadDB := DB.Set("gorm:auto_preload", false).Where("role = ?", "Preload")
130 var user User
131 preloadDB.Find(&user)
132
133 if user.BillingAddress.Address1 != "" {
134 t.Error("AutoPreload was set to fasle, but still fetched data")
135 }
136
137 user2 := getPreloadUser("auto_user2")
138 DB.Save(user2)
139
140 var users []User
141 preloadDB.Find(&users)
142
143 for _, user := range users {
144 if user.BillingAddress.Address1 != "" {
145 t.Error("AutoPreload was set to fasle, but still fetched data")
146 }
147 }
148 }
149
125150 func TestNestedPreload1(t *testing.T) {
126151 type (
127152 Level1 struct {
745770 levelB3 := &LevelB3{
746771 Value: "bar",
747772 LevelB1ID: sql.NullInt64{Valid: true, Int64: int64(levelB1.ID)},
773 LevelB2s: []*LevelB2{},
748774 }
749775 if err := DB.Create(levelB3).Error; err != nil {
750776 t.Error(err)
16501676 lvl := Level1{
16511677 Name: "l1",
16521678 Level2s: []Level2{
1653 Level2{Name: "l2-1"}, Level2{Name: "l2-2"},
1679 {Name: "l2-1"}, {Name: "l2-2"},
16541680 },
16551681 }
16561682 DB.Save(&lvl)
180180
181181 scopedb.Where("birthday > ?", parseTime("2000-1-1")).Find(&users)
182182 if len(users) != 2 {
183 t.Errorf("Should found 2 users's birthday > 2000-1-1, but got %v", len(users))
183 t.Errorf("Should found 2 users' birthday > 2000-1-1, but got %v", len(users))
184184 }
185185
186186 scopedb.Where("birthday > ?", "2002-10-10").Find(&users)
187187 if len(users) != 2 {
188 t.Errorf("Should found 2 users's birthday >= 2002-10-10, but got %v", len(users))
188 t.Errorf("Should found 2 users' birthday >= 2002-10-10, but got %v", len(users))
189189 }
190190
191191 scopedb.Where("birthday >= ?", "2010-1-1").Where("birthday < ?", "2020-1-1").Find(&users)
192192 if len(users) != 1 {
193 t.Errorf("Should found 1 users's birthday < 2020-1-1 and >= 2010-1-1, but got %v", len(users))
193 t.Errorf("Should found 1 users' birthday < 2020-1-1 and >= 2010-1-1, but got %v", len(users))
194194 }
195195
196196 DB.Where("name in (?)", []string{user1.Name, user2.Name}).Find(&users)
531531 DB.Table("users").Where("name = ?", "user3").Count(&name3Count)
532532 DB.Not("name", "user3").Find(&users4)
533533 if len(users1)-len(users4) != int(name3Count) {
534 t.Errorf("Should find all users's name not equal 3")
534 t.Errorf("Should find all users' name not equal 3")
535535 }
536536
537537 DB.Not("name = ?", "user3").Find(&users4)
538538 if len(users1)-len(users4) != int(name3Count) {
539 t.Errorf("Should find all users's name not equal 3")
539 t.Errorf("Should find all users' name not equal 3")
540540 }
541541
542542 DB.Not("name <> ?", "user3").Find(&users4)
543543 if len(users4) != int(name3Count) {
544 t.Errorf("Should find all users's name not equal 3")
544 t.Errorf("Should find all users' name not equal 3")
545545 }
546546
547547 DB.Not(User{Name: "user3"}).Find(&users5)
548548
549549 if len(users1)-len(users5) != int(name3Count) {
550 t.Errorf("Should find all users's name not equal 3")
550 t.Errorf("Should find all users' name not equal 3")
551551 }
552552
553553 DB.Not(map[string]interface{}{"name": "user3"}).Find(&users6)
554554 if len(users1)-len(users6) != int(name3Count) {
555 t.Errorf("Should find all users's name not equal 3")
555 t.Errorf("Should find all users' name not equal 3")
556556 }
557557
558558 DB.Not(map[string]interface{}{"name": "user3", "company_id": nil}).Find(&users7)
562562
563563 DB.Not("name", []string{"user3"}).Find(&users8)
564564 if len(users1)-len(users8) != int(name3Count) {
565 t.Errorf("Should find all users's name not equal 3")
565 t.Errorf("Should find all users' name not equal 3")
566566 }
567567
568568 var name2Count int64
569569 DB.Table("users").Where("name = ?", "user2").Count(&name2Count)
570570 DB.Not("name", []string{"user3", "user2"}).Find(&users9)
571571 if len(users1)-len(users9) != (int(name3Count) + int(name2Count)) {
572 t.Errorf("Should find all users's name not equal 3")
572 t.Errorf("Should find all users' name not equal 3")
573573 }
574574 }
575575
6262
6363 // Dialect get dialect
6464 func (scope *Scope) Dialect() Dialect {
65 return scope.db.parent.dialect
65 return scope.db.dialect
6666 }
6767
6868 // Quote used to quote string to escape them for database
6969 func (scope *Scope) Quote(str string) string {
70 if strings.Index(str, ".") != -1 {
70 if strings.Contains(str, ".") {
7171 newStrs := []string{}
7272 for _, str := range strings.Split(str, ".") {
7373 newStrs = append(newStrs, scope.Dialect().Quote(str))
133133 // FieldByName find `gorm.Field` with field name or db name
134134 func (scope *Scope) FieldByName(name string) (field *Field, ok bool) {
135135 var (
136 dbName = ToDBName(name)
136 dbName = ToColumnName(name)
137137 mostMatchedField *Field
138138 )
139139
329329 // QuotedTableName return quoted table name
330330 func (scope *Scope) QuotedTableName() (name string) {
331331 if scope.Search != nil && len(scope.Search.tableName) > 0 {
332 if strings.Index(scope.Search.tableName, " ") != -1 {
332 if strings.Contains(scope.Search.tableName, " ") {
333333 return scope.Search.tableName
334334 }
335335 return scope.Quote(scope.Search.tableName)
485485 values[index] = &ignored
486486
487487 selectFields = fields
488 offset := 0
488489 if idx, ok := selectedColumnsMap[column]; ok {
489 selectFields = selectFields[idx+1:]
490 offset = idx + 1
491 selectFields = selectFields[offset:]
490492 }
491493
492494 for fieldIndex, field := range selectFields {
500502 resetFields[index] = field
501503 }
502504
503 selectedColumnsMap[column] = fieldIndex
505 selectedColumnsMap[column] = offset + fieldIndex
504506
505507 if field.IsNormal {
506508 break
585587 scope.Err(fmt.Errorf("invalid query condition: %v", value))
586588 return
587589 }
588
590 scopeQuotedTableName := newScope.QuotedTableName()
589591 for _, field := range newScope.Fields() {
590592 if !field.IsIgnored && !field.IsBlank {
591 sqls = append(sqls, fmt.Sprintf("(%v.%v %s %v)", quotedTableName, scope.Quote(field.DBName), equalSQL, scope.AddToVars(field.Field.Interface())))
593 sqls = append(sqls, fmt.Sprintf("(%v.%v %s %v)", scopeQuotedTableName, scope.Quote(field.DBName), equalSQL, scope.AddToVars(field.Field.Interface())))
592594 }
593595 }
594596 return strings.Join(sqls, " AND ")
691693
692694 buff := bytes.NewBuffer([]byte{})
693695 i := 0
694 for pos := range str {
696 for pos, char := range str {
695697 if str[pos] == '?' {
696698 buff.WriteString(replacements[i])
697699 i++
698700 } else {
699 buff.WriteByte(str[pos])
701 buff.WriteRune(char)
700702 }
701703 }
702704
852854 }
853855
854856 func (scope *Scope) callCallbacks(funcs []*func(s *Scope)) *Scope {
857 defer func() {
858 if err := recover(); err != nil {
859 if db, ok := scope.db.db.(sqlTx); ok {
860 db.Rollback()
861 }
862 panic(err)
863 }
864 }()
855865 for _, f := range funcs {
856866 (*f)(scope)
857867 if scope.skipLeft {
861871 return scope
862872 }
863873
864 func convertInterfaceToMap(values interface{}, withIgnoredField bool) map[string]interface{} {
874 func convertInterfaceToMap(values interface{}, withIgnoredField bool, db *DB) map[string]interface{} {
865875 var attrs = map[string]interface{}{}
866876
867877 switch value := values.(type) {
869879 return value
870880 case []interface{}:
871881 for _, v := range value {
872 for key, value := range convertInterfaceToMap(v, withIgnoredField) {
882 for key, value := range convertInterfaceToMap(v, withIgnoredField, db) {
873883 attrs[key] = value
874884 }
875885 }
879889 switch reflectValue.Kind() {
880890 case reflect.Map:
881891 for _, key := range reflectValue.MapKeys() {
882 attrs[ToDBName(key.Interface().(string))] = reflectValue.MapIndex(key).Interface()
892 attrs[ToColumnName(key.Interface().(string))] = reflectValue.MapIndex(key).Interface()
883893 }
884894 default:
885 for _, field := range (&Scope{Value: values}).Fields() {
895 for _, field := range (&Scope{Value: values, db: db}).Fields() {
886896 if !field.IsBlank && (withIgnoredField || !field.IsIgnored) {
887897 attrs[field.DBName] = field.Field.Interface()
888898 }
894904
895905 func (scope *Scope) updatedAttrsWithValues(value interface{}) (results map[string]interface{}, hasUpdate bool) {
896906 if scope.IndirectValue().Kind() != reflect.Struct {
897 return convertInterfaceToMap(value, false), true
907 return convertInterfaceToMap(value, false, scope.db), true
898908 }
899909
900910 results = map[string]interface{}{}
901911
902 for key, value := range convertInterfaceToMap(value, true) {
912 for key, value := range convertInterfaceToMap(value, true, scope.db) {
903913 if field, ok := scope.FieldByName(key); ok && scope.changeableField(field) {
904914 if _, ok := value.(*expr); ok {
905915 hasUpdate = true
906916 results[field.DBName] = value
907917 } else {
908918 err := field.Set(value)
909 if field.IsNormal {
919 if field.IsNormal && !field.IsIgnored {
910920 hasUpdate = true
911921 if err == ErrUnaddressable {
912922 results[field.DBName] = value
971981 if dest.Kind() != reflect.Slice {
972982 scope.Err(fmt.Errorf("results should be a slice, not %s", dest.Kind()))
973983 return scope
984 }
985
986 if dest.Len() > 0 {
987 dest.Set(reflect.Zero(dest.Type()))
974988 }
975989
976990 if query, ok := scope.Search.selects["query"]; !ok || !scope.isQueryForColumn(query, column) {
9961010 func (scope *Scope) count(value interface{}) *Scope {
9971011 if query, ok := scope.Search.selects["query"]; !ok || !countingQueryRegexp.MatchString(fmt.Sprint(query)) {
9981012 if len(scope.Search.group) != 0 {
999 scope.Search.Select("count(*) FROM ( SELECT count(*) as name ")
1000 scope.Search.group += " ) AS count_table"
1013 if len(scope.Search.havingConditions) != 0 {
1014 scope.prepareQuerySQL()
1015 scope.Search = &search{}
1016 scope.Search.Select("count(*)")
1017 scope.Search.Table(fmt.Sprintf("( %s ) AS count_table", scope.SQL))
1018 } else {
1019 scope.Search.Select("count(*) FROM ( SELECT count(*) as name ")
1020 scope.Search.group += " ) AS count_table"
1021 }
10011022 } else {
10021023 scope.Search.Select("count(*)")
10031024 }
11121133 if field, ok := scope.FieldByName(fieldName); ok {
11131134 foreignKeyStruct := field.clone()
11141135 foreignKeyStruct.IsPrimaryKey = false
1115 foreignKeyStruct.TagSettings["IS_JOINTABLE_FOREIGNKEY"] = "true"
1116 delete(foreignKeyStruct.TagSettings, "AUTO_INCREMENT")
1136 foreignKeyStruct.TagSettingsSet("IS_JOINTABLE_FOREIGNKEY", "true")
1137 foreignKeyStruct.TagSettingsDelete("AUTO_INCREMENT")
11171138 sqlTypes = append(sqlTypes, scope.Quote(relationship.ForeignDBNames[idx])+" "+scope.Dialect().DataTypeOf(foreignKeyStruct))
11181139 primaryKeys = append(primaryKeys, scope.Quote(relationship.ForeignDBNames[idx]))
11191140 }
11231144 if field, ok := toScope.FieldByName(fieldName); ok {
11241145 foreignKeyStruct := field.clone()
11251146 foreignKeyStruct.IsPrimaryKey = false
1126 foreignKeyStruct.TagSettings["IS_JOINTABLE_FOREIGNKEY"] = "true"
1127 delete(foreignKeyStruct.TagSettings, "AUTO_INCREMENT")
1147 foreignKeyStruct.TagSettingsSet("IS_JOINTABLE_FOREIGNKEY", "true")
1148 foreignKeyStruct.TagSettingsDelete("AUTO_INCREMENT")
11281149 sqlTypes = append(sqlTypes, scope.Quote(relationship.AssociationForeignDBNames[idx])+" "+scope.Dialect().DataTypeOf(foreignKeyStruct))
11291150 primaryKeys = append(primaryKeys, scope.Quote(relationship.AssociationForeignDBNames[idx]))
11301151 }
11721193 }
11731194
11741195 func (scope *Scope) dropTable() *Scope {
1175 scope.Raw(fmt.Sprintf("DROP TABLE %v%s", scope.QuotedTableName(), scope.getTableOptions())).Exec()
1196 scope.Raw(fmt.Sprintf("DROP TABLE %v", scope.QuotedTableName())).Exec()
11761197 return scope
11771198 }
11781199
12141235 }
12151236
12161237 func (scope *Scope) removeForeignKey(field string, dest string) {
1217 keyName := scope.Dialect().BuildKeyName(scope.TableName(), field, dest)
1218
1238 keyName := scope.Dialect().BuildKeyName(scope.TableName(), field, dest, "foreign")
12191239 if !scope.Dialect().HasForeignKey(scope.TableName(), keyName) {
12201240 return
12211241 }
1222 var query = `ALTER TABLE %s DROP CONSTRAINT %s;`
1242 var mysql mysql
1243 var query string
1244 if scope.Dialect().GetName() == mysql.GetName() {
1245 query = `ALTER TABLE %s DROP FOREIGN KEY %s;`
1246 } else {
1247 query = `ALTER TABLE %s DROP CONSTRAINT %s;`
1248 }
1249
12231250 scope.Raw(fmt.Sprintf(query, scope.QuotedTableName(), scope.quoteIfPossible(keyName))).Exec()
12241251 }
12251252
12531280 var uniqueIndexes = map[string][]string{}
12541281
12551282 for _, field := range scope.GetStructFields() {
1256 if name, ok := field.TagSettings["INDEX"]; ok {
1283 if name, ok := field.TagSettingsGet("INDEX"); ok {
12571284 names := strings.Split(name, ",")
12581285
12591286 for _, name := range names {
12601287 if name == "INDEX" || name == "" {
12611288 name = scope.Dialect().BuildKeyName("idx", scope.TableName(), field.DBName)
12621289 }
1263 indexes[name] = append(indexes[name], field.DBName)
1264 }
1265 }
1266
1267 if name, ok := field.TagSettings["UNIQUE_INDEX"]; ok {
1290 name, column := scope.Dialect().NormalizeIndexAndColumn(name, field.DBName)
1291 indexes[name] = append(indexes[name], column)
1292 }
1293 }
1294
1295 if name, ok := field.TagSettingsGet("UNIQUE_INDEX"); ok {
12681296 names := strings.Split(name, ",")
12691297
12701298 for _, name := range names {
12711299 if name == "UNIQUE_INDEX" || name == "" {
12721300 name = scope.Dialect().BuildKeyName("uix", scope.TableName(), field.DBName)
12731301 }
1274 uniqueIndexes[name] = append(uniqueIndexes[name], field.DBName)
1302 name, column := scope.Dialect().NormalizeIndexAndColumn(name, field.DBName)
1303 uniqueIndexes[name] = append(uniqueIndexes[name], column)
12751304 }
12761305 }
12771306 }
12921321 }
12931322
12941323 func (scope *Scope) getColumnAsArray(columns []string, values ...interface{}) (results [][]interface{}) {
1324 resultMap := make(map[string][]interface{})
12951325 for _, value := range values {
12961326 indirectValue := indirect(reflect.ValueOf(value))
12971327
13101340 }
13111341
13121342 if hasValue {
1313 results = append(results, result)
1343 h := fmt.Sprint(result...)
1344 if _, exist := resultMap[h]; !exist {
1345 resultMap[h] = result
1346 }
13141347 }
13151348 }
13161349 case reflect.Struct:
13251358 }
13261359
13271360 if hasValue {
1328 results = append(results, result)
1329 }
1330 }
1331 }
1332
1361 h := fmt.Sprint(result...)
1362 if _, exist := resultMap[h]; !exist {
1363 resultMap[h] = result
1364 }
1365 }
1366 }
1367 }
1368 for _, v := range resultMap {
1369 results = append(results, v)
1370 }
13331371 return
13341372 }
13351373
7777 t.Errorf("The error should be returned from Valuer, but get %v", err)
7878 }
7979 }
80
81 func TestDropTableWithTableOptions(t *testing.T) {
82 type UserWithOptions struct {
83 gorm.Model
84 }
85 DB.AutoMigrate(&UserWithOptions{})
86
87 DB = DB.Set("gorm:table_options", "CHARSET=utf8")
88 err := DB.DropTable(&UserWithOptions{}).Error
89 if err != nil {
90 t.Errorf("Table must be dropped, got error %s", err)
91 }
92 }
00 package gorm
11
22 import (
3 "bytes"
43 "database/sql/driver"
54 "fmt"
65 "reflect"
2524 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"}
2625 var commonInitialismsReplacer *strings.Replacer
2726
28 var goSrcRegexp = regexp.MustCompile(`jinzhu/gorm/.*.go`)
29 var goTestRegexp = regexp.MustCompile(`jinzhu/gorm/.*test.go`)
27 var goSrcRegexp = regexp.MustCompile(`jinzhu/gorm(@.*)?/.*.go`)
28 var goTestRegexp = regexp.MustCompile(`jinzhu/gorm(@.*)?/.*test.go`)
3029
3130 func init() {
3231 var commonInitialismsForReplacer []string
5554
5655 func newSafeMap() *safeMap {
5756 return &safeMap{l: new(sync.RWMutex), m: make(map[string]string)}
58 }
59
60 var smap = newSafeMap()
61
62 type strCase bool
63
64 const (
65 lower strCase = false
66 upper strCase = true
67 )
68
69 // ToDBName convert string to db name
70 func ToDBName(name string) string {
71 if v := smap.Get(name); v != "" {
72 return v
73 }
74
75 if name == "" {
76 return ""
77 }
78
79 var (
80 value = commonInitialismsReplacer.Replace(name)
81 buf = bytes.NewBufferString("")
82 lastCase, currCase, nextCase strCase
83 )
84
85 for i, v := range value[:len(value)-1] {
86 nextCase = strCase(value[i+1] >= 'A' && value[i+1] <= 'Z')
87 if i > 0 {
88 if currCase == upper {
89 if lastCase == upper && nextCase == upper {
90 buf.WriteRune(v)
91 } else {
92 if value[i-1] != '_' && value[i+1] != '_' {
93 buf.WriteRune('_')
94 }
95 buf.WriteRune(v)
96 }
97 } else {
98 buf.WriteRune(v)
99 if i == len(value)-2 && nextCase == upper {
100 buf.WriteRune('_')
101 }
102 }
103 } else {
104 currCase = upper
105 buf.WriteRune(v)
106 }
107 lastCase = currCase
108 currCase = nextCase
109 }
110
111 buf.WriteByte(value[len(value)-1])
112
113 s := strings.ToLower(buf.String())
114 smap.Set(name, s)
115 return s
11657 }
11758
11859 // SQL expression
264205 // as FieldByName could panic
265206 if indirectValue := reflect.Indirect(value); indirectValue.IsValid() {
266207 for _, fieldName := range fieldNames {
267 if fieldValue := indirectValue.FieldByName(fieldName); fieldValue.IsValid() {
208 if fieldValue := reflect.Indirect(indirectValue.FieldByName(fieldName)); fieldValue.IsValid() {
268209 result := fieldValue.Interface()
269210 if r, ok := result.(driver.Valuer); ok {
270211 result, _ = r.Value()
+0
-32
utils_test.go less more
0 package gorm_test
1
2 import (
3 "testing"
4
5 "github.com/jinzhu/gorm"
6 )
7
8 func TestToDBNameGenerateFriendlyName(t *testing.T) {
9 var maps = map[string]string{
10 "": "",
11 "X": "x",
12 "ThisIsATest": "this_is_a_test",
13 "PFAndESI": "pf_and_esi",
14 "AbcAndJkl": "abc_and_jkl",
15 "EmployeeID": "employee_id",
16 "SKU_ID": "sku_id",
17 "FieldX": "field_x",
18 "HTTPAndSMTP": "http_and_smtp",
19 "HTTPServerHandlerForURLID": "http_server_handler_for_url_id",
20 "UUID": "uuid",
21 "HTTPURL": "http_url",
22 "HTTP_URL": "http_url",
23 "ThisIsActuallyATestSoWeMayBeAbleToUseThisCodeInGormPackageAlsoIdCanBeUsedAtTheEndAsID": "this_is_actually_a_test_so_we_may_be_able_to_use_this_code_in_gorm_package_also_id_can_be_used_at_the_end_as_id",
24 }
25
26 for key, value := range maps {
27 if gorm.ToDBName(key) != value {
28 t.Errorf("%v ToDBName should equal %v, but got %v", key, value, gorm.ToDBName(key))
29 }
30 }
31 }
8282 code: |
8383 cd $WERCKER_SOURCE_DIR
8484 go version
85 go get -t ./...
85 go get -t -v ./...
8686
8787 # Build the project
8888 - script:
9494 - script:
9595 name: test sqlite
9696 code: |
97 go test ./...
97 go test -race -v ./...
9898
9999 - script:
100100 name: test mariadb
101101 code: |
102 GORM_DIALECT=mysql GORM_DSN="gorm:gorm@tcp(mariadb:3306)/gorm?charset=utf8&parseTime=True" go test ./...
102 GORM_DIALECT=mysql GORM_DSN="gorm:gorm@tcp(mariadb:3306)/gorm?charset=utf8&parseTime=True" go test -race ./...
103103
104104 - script:
105105 name: test mysql5.7
106106 code: |
107 GORM_DIALECT=mysql GORM_DSN="gorm:gorm@tcp(mysql57:3306)/gorm?charset=utf8&parseTime=True" go test ./...
107 GORM_DIALECT=mysql GORM_DSN="gorm:gorm@tcp(mysql57:3306)/gorm?charset=utf8&parseTime=True" go test -race ./...
108108
109109 - script:
110110 name: test mysql5.6
111111 code: |
112 GORM_DIALECT=mysql GORM_DSN="gorm:gorm@tcp(mysql56:3306)/gorm?charset=utf8&parseTime=True" go test ./...
112 GORM_DIALECT=mysql GORM_DSN="gorm:gorm@tcp(mysql56:3306)/gorm?charset=utf8&parseTime=True" go test -race ./...
113113
114114 - script:
115115 name: test mysql5.5
116116 code: |
117 GORM_DIALECT=mysql GORM_DSN="gorm:gorm@tcp(mysql55:3306)/gorm?charset=utf8&parseTime=True" go test ./...
117 GORM_DIALECT=mysql GORM_DSN="gorm:gorm@tcp(mysql55:3306)/gorm?charset=utf8&parseTime=True" go test -race ./...
118118
119119 - script:
120120 name: test postgres
121121 code: |
122 GORM_DIALECT=postgres GORM_DSN="host=postgres user=gorm password=gorm DB.name=gorm port=5432 sslmode=disable" go test ./...
122 GORM_DIALECT=postgres GORM_DSN="host=postgres user=gorm password=gorm DB.name=gorm port=5432 sslmode=disable" go test -race ./...
123123
124124 - script:
125125 name: test postgres96
126126 code: |
127 GORM_DIALECT=postgres GORM_DSN="host=postgres96 user=gorm password=gorm DB.name=gorm port=5432 sslmode=disable" go test ./...
127 GORM_DIALECT=postgres GORM_DSN="host=postgres96 user=gorm password=gorm DB.name=gorm port=5432 sslmode=disable" go test -race ./...
128128
129129 - script:
130130 name: test postgres95
131131 code: |
132 GORM_DIALECT=postgres GORM_DSN="host=postgres95 user=gorm password=gorm DB.name=gorm port=5432 sslmode=disable" go test ./...
132 GORM_DIALECT=postgres GORM_DSN="host=postgres95 user=gorm password=gorm DB.name=gorm port=5432 sslmode=disable" go test -race ./...
133133
134134 - script:
135135 name: test postgres94
136136 code: |
137 GORM_DIALECT=postgres GORM_DSN="host=postgres94 user=gorm password=gorm DB.name=gorm port=5432 sslmode=disable" go test ./...
137 GORM_DIALECT=postgres GORM_DSN="host=postgres94 user=gorm password=gorm DB.name=gorm port=5432 sslmode=disable" go test -race ./...
138138
139139 - script:
140140 name: test postgres93
141141 code: |
142 GORM_DIALECT=postgres GORM_DSN="host=postgres93 user=gorm password=gorm DB.name=gorm port=5432 sslmode=disable" go test ./...
142 GORM_DIALECT=postgres GORM_DSN="host=postgres93 user=gorm password=gorm DB.name=gorm port=5432 sslmode=disable" go test -race ./...
143143
144144 - script:
145145 name: test mssql
146146 code: |
147 GORM_DIALECT=mssql GORM_DSN="sqlserver://gorm:LoremIpsum86@mssql:1433?database=gorm" go test ./...
147 GORM_DIALECT=mssql GORM_DSN="sqlserver://gorm:LoremIpsum86@mssql:1433?database=gorm" go test -race ./...
148
149 - script:
150 name: codecov
151 code: |
152 go test -race -coverprofile=coverage.txt -covermode=atomic ./...
153 bash <(curl -s https://codecov.io/bash)