diff --git a/README.md b/README.md index 083010e1..86320446 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ Yet Another ORM library for Go, aims for developer friendly * Automatically CreatedAt, UpdatedAt * Soft Delete * Create/Drop table from struct +* Automating Migrations * Dynamically set table name when search, create, update, delete... * Prevent SQL Injection * Goroutines friendly @@ -97,6 +98,18 @@ db.CreateTable(User{}) db.DropTable(User{}) ``` +### Automating Migrations + +Feel Free to update your struct, AutoMigrate will keep your database update to date. + +FYI, AutoMigrate will only add new columns, won't change column's type or delete unused columns, to make sure gorm won't harm your data. + +If table doesn't exist when AutoMigrate, it will run create table automatically. + +```go +db.AutoMigrate(User{}) +``` + ## Create ```go diff --git a/chain.go b/chain.go index 87c410bd..771a481e 100644 --- a/chain.go +++ b/chain.go @@ -236,17 +236,17 @@ func (s *Chain) Or(querystring interface{}, args ...interface{}) *Chain { } func (s *Chain) CreateTable(value interface{}) *Chain { - s.do(value).createTable().exec() + s.do(value).createTable() return s } func (s *Chain) DropTable(value interface{}) *Chain { - s.do(value).dropTable().exec() + s.do(value).dropTable() return s } func (s *Chain) AutoMigrate(value interface{}) *Chain { - s.do(value).autoMigrate().exec() + s.do(value).autoMigrate() return s } diff --git a/do.go b/do.go index b20c0841..02c57efc 100644 --- a/do.go +++ b/do.go @@ -715,6 +715,7 @@ func (s *Do) createTable() *Do { s.tableName(), strings.Join(sqls, ","), ) + s.exec() return s } @@ -723,13 +724,32 @@ func (s *Do) dropTable() *Do { "DROP TABLE %v", s.tableName(), ) + s.exec() return s } func (s *Do) autoMigrate() *Do { - sql := fmt.Sprintf("SELECT column_name, data_type FROM information_schema.columns WHERE table_name = %v", s.tableName()) - for _, field := range s.model.fields("other") { - s.sql = fmt.Sprintf(sql+"and column_name = %v", field.DbName) + var table_name string + sql := fmt.Sprintf("SELECT table_name FROM INFORMATION_SCHEMA.tables where table_name = %v", s.addToVars(s.tableName())) + s.db.QueryRow(sql, s.sqlVars...).Scan(&table_name) + s.sqlVars = []interface{}{} + + // If table doesn't exist + if len(table_name) == 0 { + s.createTable() + } else { + for _, field := range s.model.fields("other") { + var column_name, data_type string + sql := fmt.Sprintf("SELECT column_name, data_type FROM information_schema.columns WHERE table_name = %v", s.addToVars(s.tableName())) + s.db.QueryRow(fmt.Sprintf(sql+" and column_name = %v", s.addToVars(field.DbName)), s.sqlVars...).Scan(&column_name, &data_type) + s.sqlVars = []interface{}{} + + // If column doesn't exist + if len(column_name) == 0 { + s.sql = fmt.Sprintf("ALTER TABLE %v ADD %v %v", s.tableName(), field.DbName, field.SqlType) + s.exec() + } + } } return s } diff --git a/gorm_test.go b/gorm_test.go index a1287afe..8a60b597 100644 --- a/gorm_test.go +++ b/gorm_test.go @@ -38,13 +38,12 @@ type CreditCard struct { } type Email struct { - Id int64 - UserId int64 - Email string - Subscribed bool - CreatedAt time.Time - UpdatedAt time.Time - DeletedAt time.Time + Id int64 + UserId int64 + Email string + CreatedAt time.Time + UpdatedAt time.Time + DeletedAt time.Time } type Address struct { @@ -1236,6 +1235,32 @@ func TestTableName(t *testing.T) { } } +type BigEmail struct { + Id int64 + UserId int64 + Email string + UserAgent string + RegisteredAt time.Time + CreatedAt time.Time + UpdatedAt time.Time + DeletedAt time.Time +} + +func (b BigEmail) TableName() string { + return "emails" +} + func TestAutoMigration(t *testing.T) { db.AutoMigrate(Address{}) + if err := db.Table("emails").AutoMigrate(BigEmail{}).Error; err != nil { + t.Errorf("Auto Migrate should not raise any error", err) + } + + db.Save(&BigEmail{Email: "jinzhu@example.org", UserAgent: "pc", RegisteredAt: time.Now()}) + + var big_email BigEmail + db.First(&big_email, "user_agent = ?", "pc") + if big_email.Email != "jinzhu@example.org" || big_email.UserAgent != "pc" || big_email.RegisteredAt.IsZero() { + t.Error("Big Emails should be saved and fetched correctly") + } }