diff --git a/callback_update.go b/callback_update.go index f6ba0ffd..c52162c8 100644 --- a/callback_update.go +++ b/callback_update.go @@ -75,7 +75,7 @@ func updateCallback(scope *Scope) { } else { for _, field := range scope.Fields() { if scope.changeableField(field) { - if !field.IsPrimaryKey && field.IsNormal { + if !field.IsPrimaryKey && field.IsNormal && (field.Name != "CreatedAt" || !field.IsBlank) { if !field.IsForeignKey || !field.IsBlank || !field.HasDefaultValue { sqls = append(sqls, fmt.Sprintf("%v = %v", scope.Quote(field.DBName), scope.AddToVars(field.Field.Interface()))) } diff --git a/errors.go b/errors.go index 27c9a92d..d5ef8d57 100644 --- a/errors.go +++ b/errors.go @@ -6,11 +6,11 @@ import ( ) var ( - // ErrRecordNotFound record not found error, happens when only haven't find any matched data when looking up with a struct, finding a slice won't return this error + // ErrRecordNotFound 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 ErrRecordNotFound = errors.New("record not found") - // ErrInvalidSQL invalid SQL error, happens when you passed invalid SQL + // ErrInvalidSQL occurs when you attempt a query with invalid SQL ErrInvalidSQL = errors.New("invalid SQL") - // ErrInvalidTransaction invalid transaction when you are trying to `Commit` or `Rollback` + // ErrInvalidTransaction occurs when you are trying to `Commit` or `Rollback` ErrInvalidTransaction = errors.New("no valid transaction") // ErrCantStartTransaction can't start transaction when you are trying to start one with `Begin` ErrCantStartTransaction = errors.New("can't start transaction") @@ -21,7 +21,7 @@ var ( // Errors contains all happened errors type Errors []error -// IsRecordNotFoundError returns current error has record not found error or not +// IsRecordNotFoundError returns true if error contains a RecordNotFound error func IsRecordNotFoundError(err error) bool { if errs, ok := err.(Errors); ok { for _, err := range errs { @@ -33,12 +33,12 @@ func IsRecordNotFoundError(err error) bool { return err == ErrRecordNotFound } -// GetErrors gets all happened errors +// GetErrors gets all errors that have occurred and returns a slice of errors (Error type) func (errs Errors) GetErrors() []error { return errs } -// Add adds an error +// Add adds an error to a given slice of errors func (errs Errors) Add(newErrors ...error) Errors { for _, err := range newErrors { if err == nil { @@ -62,7 +62,7 @@ func (errs Errors) Add(newErrors ...error) Errors { return errs } -// Error format happened errors +// Error takes a slice of all errors that have occurred and returns it as a formatted string func (errs Errors) Error() string { var errors = []string{} for _, e := range errs { diff --git a/main.go b/main.go index c1197bc9..34a6ddc8 100644 --- a/main.go +++ b/main.go @@ -178,7 +178,7 @@ func (s *DB) SingularTable(enable bool) { func (s *DB) NewScope(value interface{}) *Scope { dbClone := s.clone() dbClone.Value = value - return &Scope{db: dbClone, Search: dbClone.search.clone(), Value: value} + return &Scope{db: dbClone, Search: dbClone.search, Value: value} } // QueryExpr returns the query as expr object diff --git a/model_struct.go b/model_struct.go index 8c27e209..9e93db63 100644 --- a/model_struct.go +++ b/model_struct.go @@ -21,12 +21,12 @@ var modelStructsMap sync.Map // ModelStruct model definition type ModelStruct struct { - PrimaryFields []*StructField - StructFields []*StructField - ModelType reflect.Type + PrimaryFields []*StructField + StructFields []*StructField + ModelType reflect.Type defaultTableName string - l sync.Mutex + l sync.Mutex } // TableName returns model's table name @@ -40,7 +40,7 @@ func (s *ModelStruct) TableName(db *DB) string { s.defaultTableName = tabler.TableName() } else { tableName := ToTableName(s.ModelType.Name()) - if db == nil || !db.parent.singularTable { + if db == nil || (db.parent != nil && !db.parent.singularTable) { tableName = inflection.Plural(tableName) } s.defaultTableName = tableName @@ -70,52 +70,52 @@ type StructField struct { } // TagSettingsSet Sets a tag in the tag settings map -func (s *StructField) TagSettingsSet(key, val string) { - s.tagSettingsLock.Lock() - defer s.tagSettingsLock.Unlock() - s.TagSettings[key] = val +func (sf *StructField) TagSettingsSet(key, val string) { + sf.tagSettingsLock.Lock() + defer sf.tagSettingsLock.Unlock() + sf.TagSettings[key] = val } // TagSettingsGet returns a tag from the tag settings -func (s *StructField) TagSettingsGet(key string) (string, bool) { - s.tagSettingsLock.RLock() - defer s.tagSettingsLock.RUnlock() - val, ok := s.TagSettings[key] +func (sf *StructField) TagSettingsGet(key string) (string, bool) { + sf.tagSettingsLock.RLock() + defer sf.tagSettingsLock.RUnlock() + val, ok := sf.TagSettings[key] return val, ok } // TagSettingsDelete deletes a tag -func (s *StructField) TagSettingsDelete(key string) { - s.tagSettingsLock.Lock() - defer s.tagSettingsLock.Unlock() - delete(s.TagSettings, key) +func (sf *StructField) TagSettingsDelete(key string) { + sf.tagSettingsLock.Lock() + defer sf.tagSettingsLock.Unlock() + delete(sf.TagSettings, key) } -func (structField *StructField) clone() *StructField { +func (sf *StructField) clone() *StructField { clone := &StructField{ - DBName: structField.DBName, - Name: structField.Name, - Names: structField.Names, - IsPrimaryKey: structField.IsPrimaryKey, - IsNormal: structField.IsNormal, - IsIgnored: structField.IsIgnored, - IsScanner: structField.IsScanner, - HasDefaultValue: structField.HasDefaultValue, - Tag: structField.Tag, + DBName: sf.DBName, + Name: sf.Name, + Names: sf.Names, + IsPrimaryKey: sf.IsPrimaryKey, + IsNormal: sf.IsNormal, + IsIgnored: sf.IsIgnored, + IsScanner: sf.IsScanner, + HasDefaultValue: sf.HasDefaultValue, + Tag: sf.Tag, TagSettings: map[string]string{}, - Struct: structField.Struct, - IsForeignKey: structField.IsForeignKey, + Struct: sf.Struct, + IsForeignKey: sf.IsForeignKey, } - if structField.Relationship != nil { - relationship := *structField.Relationship + if sf.Relationship != nil { + relationship := *sf.Relationship clone.Relationship = &relationship } // copy the struct field tagSettings, they should be read-locked while they are copied - structField.tagSettingsLock.Lock() - defer structField.tagSettingsLock.Unlock() - for key, value := range structField.TagSettings { + sf.tagSettingsLock.Lock() + defer sf.tagSettingsLock.Unlock() + for key, value := range sf.TagSettings { clone.TagSettings[key] = value } diff --git a/scope.go b/scope.go index 806ccb7d..90e16782 100644 --- a/scope.go +++ b/scope.go @@ -1309,6 +1309,7 @@ func (scope *Scope) autoIndex() *Scope { } func (scope *Scope) getColumnAsArray(columns []string, values ...interface{}) (results [][]interface{}) { + resultMap := make(map[string][]interface{}) for _, value := range values { indirectValue := indirect(reflect.ValueOf(value)) @@ -1327,7 +1328,10 @@ func (scope *Scope) getColumnAsArray(columns []string, values ...interface{}) (r } if hasValue { - results = append(results, result) + h := fmt.Sprint(result...) + if _, exist := resultMap[h]; !exist { + resultMap[h] = result + } } } case reflect.Struct: @@ -1342,11 +1346,16 @@ func (scope *Scope) getColumnAsArray(columns []string, values ...interface{}) (r } if hasValue { - results = append(results, result) + h := fmt.Sprint(result...) + if _, exist := resultMap[h]; !exist { + resultMap[h] = result + } } } } - + for _, v := range resultMap { + results = append(results, v) + } return }