diff --git a/clause/set.go b/clause/set.go index de78b1be..590e27d5 100644 --- a/clause/set.go +++ b/clause/set.go @@ -30,8 +30,5 @@ func (set Set) Build(builder Builder) { // MergeClause merge assignments clauses func (set Set) MergeClause(clause *Clause) { - if v, ok := clause.Expression.(Set); ok { - set = append(v, set...) - } clause.Expression = set } diff --git a/clause/set_test.go b/clause/set_test.go index 85754737..48131218 100644 --- a/clause/set_test.go +++ b/clause/set_test.go @@ -26,7 +26,7 @@ func TestSet(t *testing.T) { clause.Set([]clause.Assignment{{clause.PrimaryColumn, 1}}), clause.Set([]clause.Assignment{{clause.Column{Name: "name"}, "jinzhu"}}), }, - "UPDATE `users` SET `users`.`id`=?,`name`=?", []interface{}{1, "jinzhu"}, + "UPDATE `users` SET `name`=?", []interface{}{"jinzhu"}, }, } diff --git a/dialects/mysql/mysql.go b/dialects/mysql/mysql.go index 6ca9f5f5..23525ed7 100644 --- a/dialects/mysql/mysql.go +++ b/dialects/mysql/mysql.go @@ -116,8 +116,10 @@ func (dialector Dialector) DataTypeOf(field *schema.Field) string { return "double" case schema.String: size := field.Size - if field.PrimaryKey && size == 0 { - size = 256 + if size == 0 { + if field.PrimaryKey || field.HasDefaultValue { + size = 256 + } } if size >= 65536 && size <= int(math.Pow(2, 24)) { diff --git a/dialects/postgres/migrator.go b/dialects/postgres/migrator.go index b144f573..d93f681c 100644 --- a/dialects/postgres/migrator.go +++ b/dialects/postgres/migrator.go @@ -80,6 +80,25 @@ func (m Migrator) CreateIndex(value interface{}, name string) error { }) } +func (m Migrator) RenameIndex(value interface{}, oldName, newName string) error { + return m.RunWithValue(value, func(stmt *gorm.Statement) error { + return m.DB.Exec( + "ALTER INDEX ? RENAME TO ?", + clause.Column{Name: oldName}, clause.Column{Name: newName}, + ).Error + }) +} + +func (m Migrator) DropIndex(value interface{}, name string) error { + return m.RunWithValue(value, func(stmt *gorm.Statement) error { + if idx := stmt.Schema.LookIndex(name); idx != nil { + name = idx.Name + } + + return m.DB.Exec("DROP INDEX ?", clause.Column{Name: name}).Error + }) +} + func (m Migrator) HasTable(value interface{}) bool { var count int64 m.RunWithValue(value, func(stmt *gorm.Statement) error { diff --git a/gorm.go b/gorm.go index 6b2a6d75..9adc0858 100644 --- a/gorm.go +++ b/gorm.go @@ -66,7 +66,7 @@ func Open(dialector Dialector, config *Config) (db *DB, err error) { } if config.NowFunc == nil { - config.NowFunc = func() time.Time { return time.Now().Local() } + config.NowFunc = func() time.Time { return time.Now().Local().Round(time.Second) } } if dialector != nil { diff --git a/logger/logger.go b/logger/logger.go index 7121b4fb..ae7c22c9 100644 --- a/logger/logger.go +++ b/logger/logger.go @@ -28,7 +28,8 @@ const ( type LogLevel int const ( - Error LogLevel = iota + 1 + Silent LogLevel = iota + 1 + Error Warn Info ) @@ -129,7 +130,7 @@ func (l logger) Trace(ctx context.Context, begin time.Time, fc func() (string, i if l.LogLevel > 0 { elapsed := time.Now().Sub(begin) switch { - case err != nil: + case err != nil && l.LogLevel >= Error: sql, rows := fc() l.Printf(l.traceErrStr, utils.FileWithLineNum(), err, float64(elapsed.Nanoseconds())/1e6, rows, sql) case elapsed > l.SlowThreshold && l.SlowThreshold != 0 && l.LogLevel >= Warn: diff --git a/migrator/migrator.go b/migrator/migrator.go index 1b0edf68..8f35cbea 100644 --- a/migrator/migrator.go +++ b/migrator/migrator.go @@ -47,25 +47,32 @@ func (m Migrator) DataTypeOf(field *schema.Field) string { return m.Dialector.DataTypeOf(field) } -func (m Migrator) FullDataTypeOf(field *schema.Field) string { - dataType := m.DataTypeOf(field) +func (m Migrator) FullDataTypeOf(field *schema.Field) (expr clause.Expr) { + expr.SQL = m.DataTypeOf(field) if field.AutoIncrement { - dataType += " AUTO_INCREMENT" + expr.SQL += " AUTO_INCREMENT" } if field.NotNull { - dataType += " NOT NULL" + expr.SQL += " NOT NULL" } if field.Unique { - dataType += " UNIQUE" + expr.SQL += " UNIQUE" } if field.HasDefaultValue { - dataType += " DEFAULT " + field.DefaultValue + if field.DataType == schema.String { + defaultStmt := &gorm.Statement{Vars: []interface{}{field.DefaultValue}} + m.Dialector.BindVarTo(defaultStmt, defaultStmt, field.DefaultValue) + expr.SQL += " DEFAULT " + m.Dialector.Explain(defaultStmt.SQL.String(), field.DefaultValue) + } else { + expr.SQL += " DEFAULT " + field.DefaultValue + } } - return dataType + + return } // AutoMigrate @@ -138,7 +145,7 @@ func (m Migrator) CreateTable(values ...interface{}) error { field := stmt.Schema.FieldsByDBName[dbName] createTableSQL += fmt.Sprintf("? ?") hasPrimaryKeyInDataType = hasPrimaryKeyInDataType || strings.Contains(strings.ToUpper(field.DBDataType), "PRIMARY KEY") - values = append(values, clause.Column{Name: dbName}, clause.Expr{SQL: m.FullDataTypeOf(field)}) + values = append(values, clause.Column{Name: dbName}, m.FullDataTypeOf(field)) createTableSQL += "," } @@ -229,7 +236,7 @@ func (m Migrator) AddColumn(value interface{}, field string) error { if field := stmt.Schema.LookUpField(field); field != nil { return m.DB.Exec( "ALTER TABLE ? ADD ? ?", - clause.Table{Name: stmt.Table}, clause.Column{Name: field.DBName}, clause.Expr{SQL: m.FullDataTypeOf(field)}, + clause.Table{Name: stmt.Table}, clause.Column{Name: field.DBName}, m.FullDataTypeOf(field), ).Error } return fmt.Errorf("failed to look up field with name: %s", field) diff --git a/tests/migrate_test.go b/tests/migrate_test.go index d944dfa2..00025c58 100644 --- a/tests/migrate_test.go +++ b/tests/migrate_test.go @@ -30,44 +30,86 @@ func TestMigrate(t *testing.T) { } func TestIndexes(t *testing.T) { - type User struct { + type IndexStruct struct { gorm.Model - Name string `gorm:"index"` + Name string `gorm:"size:255;index"` } - if err := DB.Migrator().CreateIndex(&User{}, "Name"); err != nil { + DB.Migrator().DropTable(&IndexStruct{}) + DB.AutoMigrate(&IndexStruct{}) + + if err := DB.Migrator().DropIndex(&IndexStruct{}, "Name"); err != nil { + t.Errorf("Failed to drop index for user's name, got err %v", err) + } + + if err := DB.Migrator().CreateIndex(&IndexStruct{}, "Name"); err != nil { t.Errorf("Got error when tried to create index: %+v", err) } - if !DB.Migrator().HasIndex(&User{}, "Name") { + if !DB.Migrator().HasIndex(&IndexStruct{}, "Name") { t.Errorf("Failed to find index for user's name") } - if err := DB.Migrator().DropIndex(&User{}, "Name"); err != nil { + if err := DB.Migrator().DropIndex(&IndexStruct{}, "Name"); err != nil { t.Errorf("Failed to drop index for user's name, got err %v", err) } - if DB.Migrator().HasIndex(&User{}, "Name") { + if DB.Migrator().HasIndex(&IndexStruct{}, "Name") { t.Errorf("Should not find index for user's name after delete") } - if err := DB.Migrator().CreateIndex(&User{}, "Name"); err != nil { + if err := DB.Migrator().CreateIndex(&IndexStruct{}, "Name"); err != nil { t.Errorf("Got error when tried to create index: %+v", err) } - if err := DB.Migrator().RenameIndex(&User{}, "idx_users_name", "idx_users_name_1"); err != nil { + if err := DB.Migrator().RenameIndex(&IndexStruct{}, "idx_index_structs_name", "idx_users_name_1"); err != nil { t.Errorf("no error should happen when rename index, but got %v", err) } - if !DB.Migrator().HasIndex(&User{}, "idx_users_name_1") { + if !DB.Migrator().HasIndex(&IndexStruct{}, "idx_users_name_1") { t.Errorf("Should find index for user's name after rename") } - if err := DB.Migrator().DropIndex(&User{}, "idx_users_name_1"); err != nil { + if err := DB.Migrator().DropIndex(&IndexStruct{}, "idx_users_name_1"); err != nil { t.Errorf("Failed to drop index for user's name, got err %v", err) } - if DB.Migrator().HasIndex(&User{}, "idx_users_name_1") { + if DB.Migrator().HasIndex(&IndexStruct{}, "idx_users_name_1") { t.Errorf("Should not find index for user's name after delete") } } + +func TestColumns(t *testing.T) { + type ColumnStruct struct { + gorm.Model + Name string + } + + DB.Migrator().DropTable(&ColumnStruct{}) + + if err := DB.AutoMigrate(&ColumnStruct{}); err != nil { + t.Errorf("Failed to migrate, got %v", err) + } + + type NewColumnStruct struct { + gorm.Model + Name string + NewName string + } + + if err := DB.Table("column_structs").Migrator().AddColumn(&NewColumnStruct{}, "NewName"); err != nil { + t.Errorf("Failed to add column, got %v", err) + } + + if !DB.Table("column_structs").Migrator().HasColumn(&NewColumnStruct{}, "NewName") { + t.Errorf("Failed to find added column") + } + + if err := DB.Table("column_structs").Migrator().DropColumn(&NewColumnStruct{}, "NewName"); err != nil { + t.Errorf("Failed to add column, got %v", err) + } + + if DB.Table("column_structs").Migrator().HasColumn(&NewColumnStruct{}, "NewName") { + t.Errorf("Found deleted column") + } +} diff --git a/tests/non_std_test.go b/tests/non_std_test.go index b8a278fe..e5e50141 100644 --- a/tests/non_std_test.go +++ b/tests/non_std_test.go @@ -8,21 +8,21 @@ import ( ) type Animal struct { - Counter uint64 `gorm:"primary_key:yes"` - Name string `gorm:"DEFAULT:'galeone'"` - From string //test reserved sql keyword as field name - Age time.Time `gorm:"DEFAULT:current_timestamp"` - unexported string // unexported value + Counter uint64 `gorm:"primary_key:yes"` + Name string `gorm:"DEFAULT:'galeone'"` + From string //test reserved sql keyword as field name + Age *time.Time + unexported string // unexported value CreatedAt time.Time UpdatedAt time.Time } -func init() { - DB.Migrator().DropTable(&Animal{}) - DB.AutoMigrate(&Animal{}) -} - func TestNonStdPrimaryKeyAndDefaultValues(t *testing.T) { + DB.Migrator().DropTable(&Animal{}) + if err := DB.AutoMigrate(&Animal{}); err != nil { + t.Fatalf("no error should happen when migrate but got %v", err) + } + animal := Animal{Name: "Ferdinand"} DB.Save(&animal) updatedAt1 := animal.UpdatedAt diff --git a/tests/tests.go b/tests/tests.go index 2b2bfc20..7e216776 100644 --- a/tests/tests.go +++ b/tests/tests.go @@ -61,7 +61,7 @@ func OpenTestConnection() (db *gorm.DB, err error) { if debug := os.Getenv("DEBUG"); debug == "true" { db.Logger.LogMode(logger.Info) } else if debug == "false" { - db.Logger.LogMode(logger.Error) + db.Logger.LogMode(logger.Silent) } return diff --git a/tests/utils.go b/tests/utils.go index dfddf848..0a33edee 100644 --- a/tests/utils.go +++ b/tests/utils.go @@ -26,7 +26,7 @@ type Config struct { func GetUser(name string, config Config) *User { var ( - birthday = time.Now() + birthday = time.Now().Round(time.Second) user = User{ Name: name, Age: 18,