diff --git a/migration_test.go b/migration_test.go index 6b4470a6..d58e1fb5 100644 --- a/migration_test.go +++ b/migration_test.go @@ -33,6 +33,7 @@ type User struct { CompanyID *int Company Company Role Role + Password EncryptedData PasswordHash []byte IgnoreMe int64 `sql:"-"` IgnoreStringSlice []string `sql:"-"` @@ -116,6 +117,31 @@ type Company struct { Owner *User `sql:"-"` } +type EncryptedData []byte + +func (data *EncryptedData) Scan(value interface{}) error { + if b, ok := value.([]byte); ok { + if len(b) < 3 || b[0] != '*' || b[1] != '*' || b[2] != '*' { + return errors.New("Too short") + } + + *data = b[3:] + return nil + } + + return errors.New("Bytes expected") +} + +func (data EncryptedData) Value() (driver.Value, error) { + if len(data) > 0 && data[0] == 'x' { + //needed to test failures + return nil, errors.New("Should not start with 'x'") + } + + //prepend asterisks + return append([]byte("***"), data...), nil +} + type Role struct { Name string `gorm:"size:256"` } diff --git a/scope.go b/scope.go index ae98d251..0dcea855 100644 --- a/scope.go +++ b/scope.go @@ -557,9 +557,13 @@ func (scope *Scope) buildWhereCondition(clause map[string]interface{}) (str stri args := clause["args"].([]interface{}) for _, arg := range args { + var err error switch reflect.ValueOf(arg).Kind() { case reflect.Slice: // For where("id in (?)", []int64{1,2}) - if bytes, ok := arg.([]byte); ok { + if scanner, ok := interface{}(arg).(driver.Valuer); ok { + arg, err = scanner.Value() + str = strings.Replace(str, "?", scope.AddToVars(arg), 1) + } else if bytes, ok := arg.([]byte); ok { str = strings.Replace(str, "?", scope.AddToVars(bytes), 1) } else if values := reflect.ValueOf(arg); values.Len() > 0 { var tempMarks []string @@ -572,11 +576,14 @@ func (scope *Scope) buildWhereCondition(clause map[string]interface{}) (str stri } default: if valuer, ok := interface{}(arg).(driver.Valuer); ok { - arg, _ = valuer.Value() + arg, err = valuer.Value() } str = strings.Replace(str, "?", scope.AddToVars(arg), 1) } + if err != nil { + scope.Err(err) + } } return } @@ -629,9 +636,13 @@ func (scope *Scope) buildNotCondition(clause map[string]interface{}) (str string args := clause["args"].([]interface{}) for _, arg := range args { + var err error switch reflect.ValueOf(arg).Kind() { case reflect.Slice: // For where("id in (?)", []int64{1,2}) - if bytes, ok := arg.([]byte); ok { + if scanner, ok := interface{}(arg).(driver.Valuer); ok { + arg, err = scanner.Value() + str = strings.Replace(str, "?", scope.AddToVars(arg), 1) + } else if bytes, ok := arg.([]byte); ok { str = strings.Replace(str, "?", scope.AddToVars(bytes), 1) } else if values := reflect.ValueOf(arg); values.Len() > 0 { var tempMarks []string @@ -644,10 +655,13 @@ func (scope *Scope) buildNotCondition(clause map[string]interface{}) (str string } default: if scanner, ok := interface{}(arg).(driver.Valuer); ok { - arg, _ = scanner.Value() + arg, err = scanner.Value() } str = strings.Replace(notEqualSQL, "?", scope.AddToVars(arg), 1) } + if err != nil { + scope.Err(err) + } } return } diff --git a/scope_test.go b/scope_test.go index 42458995..3018f350 100644 --- a/scope_test.go +++ b/scope_test.go @@ -1,8 +1,12 @@ package gorm_test import ( - "github.com/jinzhu/gorm" + "encoding/hex" + "math/rand" + "strings" "testing" + + "github.com/jinzhu/gorm" ) func NameIn1And2(d *gorm.DB) *gorm.DB { @@ -41,3 +45,36 @@ func TestScopes(t *testing.T) { t.Errorf("Should found two users's name in 1, 3") } } + +func randName() string { + data := make([]byte, 8) + rand.Read(data) + + return "n-" + hex.EncodeToString(data) +} + +func TestValuer(t *testing.T) { + name := randName() + + origUser := User{Name: name, Age: 1, Password: EncryptedData("pass1"), PasswordHash: []byte("abc")} + if err := DB.Save(&origUser).Error; err != nil { + t.Errorf("No error should happen when saving user, but got %v", err) + } + + var user2 User + if err := DB.Where("name = ? AND password = ? AND password_hash = ?", name, EncryptedData("pass1"), []byte("abc")).First(&user2).Error; err != nil { + t.Errorf("No error should happen when querying user with valuer, but got %v", err) + } +} + +func TestFailedValuer(t *testing.T) { + name := randName() + + err := DB.Exec("INSERT INTO users(name, password) VALUES(?, ?)", name, EncryptedData("xpass1")).Error + + if err == nil { + t.Errorf("There should be an error should happen when insert data") + } else if !strings.HasPrefix(err.Error(), "Should not start with") { + t.Errorf("The error should be returned from Valuer, but get %v", err) + } +}