More tests for alter columns migration
Also change the way we handle columns rename migration. SQLite restrict column addition not to have PRIMARY KEY and/or UNIQUE attribute, so we have to change from: add new column -> migrate data from old column to new column -> rename old table -> create new table with old columns removed -> migrate data from old table to the new table -> drop old table to directly: rename old table -> create new table with renamed columns -> migrate data from old table to the new table -> drop old table
This commit is contained in:
parent
4465b2654d
commit
8ce87f0d2c
3 changed files with 100 additions and 24 deletions
|
@ -146,7 +146,7 @@ func (m *Migration) up(target, current int64) error {
|
|||
|
||||
// loop through and execute revisions
|
||||
for _, rev := range m.revs {
|
||||
if rev.Revision() > current {
|
||||
if rev.Revision() > current && rev.Revision() <= target {
|
||||
current = rev.Revision()
|
||||
// execute the revision Upgrade.
|
||||
if err := rev.Up(op); err != nil {
|
||||
|
|
|
@ -85,7 +85,7 @@ func (s *SQLiteDriver) DropColumns(tableName string, columnsToDrop []string) (sq
|
|||
}
|
||||
|
||||
// Clean up proxy table
|
||||
return s.Tx.Exec(fmt.Sprintf("DROP TABLE %s;", proxyName))
|
||||
return s.DropTable(proxyName)
|
||||
}
|
||||
|
||||
func (s *SQLiteDriver) RenameColumns(tableName string, columnChanges map[string]string) (sql.Result, error) {
|
||||
|
@ -99,30 +99,46 @@ func (s *SQLiteDriver) RenameColumns(tableName string, columnChanges map[string]
|
|||
return nil, err
|
||||
}
|
||||
|
||||
var oldColumns []string
|
||||
// We need a list of columns name to migrate data to the new table
|
||||
var oldColumnsName = selectName(columns)
|
||||
|
||||
// newColumns will be used to create the new table
|
||||
var newColumns []string
|
||||
for k, column := range selectName(columns) {
|
||||
|
||||
for k, column := range oldColumnsName {
|
||||
added := false
|
||||
for Old, New := range columnChanges {
|
||||
if column == Old {
|
||||
columnToAdd := strings.Replace(columns[k], Old, New, 1)
|
||||
|
||||
if results, err := s.AddColumn(tableName, columnToAdd); err != nil {
|
||||
return results, err
|
||||
}
|
||||
|
||||
oldColumns = append(oldColumns, Old)
|
||||
newColumns = append(newColumns, New)
|
||||
newColumns = append(newColumns, columnToAdd)
|
||||
added = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !added {
|
||||
newColumns = append(newColumns, columns[k])
|
||||
}
|
||||
}
|
||||
|
||||
statement := fmt.Sprintf("UPDATE %s SET %s;", tableName, setForUpdate(oldColumns, newColumns))
|
||||
if results, err := s.Tx.Exec(statement); err != nil {
|
||||
return results, err
|
||||
// Rename current table
|
||||
proxyName := fmt.Sprintf("%s_%s", tableName, uniuri.NewLen(16))
|
||||
if result, err := s.RenameTable(tableName, proxyName); err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return s.DropColumns(tableName, oldColumns)
|
||||
// Create new table with the new columns
|
||||
if result, err := s.CreateTable(tableName, newColumns); err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
// Migrate data
|
||||
if result, err := s.Tx.Exec(fmt.Sprintf("INSERT INTO %s SELECT %s FROM %s", tableName,
|
||||
strings.Join(oldColumnsName, ", "), proxyName)); err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
// Clean up proxy table
|
||||
return s.DropTable(proxyName)
|
||||
}
|
||||
|
||||
func (s *SQLiteDriver) getDDLFromTable(tableName string) (string, error) {
|
||||
|
|
|
@ -24,14 +24,10 @@ type AddColumnSample struct {
|
|||
ID int64 `meddler:"id,pk"`
|
||||
Imel string `meddler:"imel"`
|
||||
Name string `meddler:"name"`
|
||||
Url string `meddler:"url"`
|
||||
Num int64 `meddler:"num"`
|
||||
}
|
||||
|
||||
type RemoveColumnSample struct {
|
||||
ID int64 `meddler:"id,pk"`
|
||||
Name string `meddler:"name"`
|
||||
}
|
||||
|
||||
// ---------- revision 1
|
||||
|
||||
type revision1 struct{}
|
||||
|
@ -84,12 +80,12 @@ func (r *revision3) Up(op Operation) error {
|
|||
if _, err := op.AddColumn("samples", "url VARCHAR(255)"); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := op.AddColumn("samples", "likes INTEGER")
|
||||
_, err := op.AddColumn("samples", "num INTEGER")
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *revision3) Down(op Operation) error {
|
||||
_, err := op.DropColumns("samples", []string{"likes", "url"})
|
||||
_, err := op.DropColumns("samples", []string{"num", "url"})
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -99,6 +95,30 @@ func (r *revision3) Revision() int64 {
|
|||
|
||||
// ---------- end of revision 3
|
||||
|
||||
// ---------- revision 4
|
||||
|
||||
type revision4 struct{}
|
||||
|
||||
func (r *revision4) Up(op Operation) error {
|
||||
_, err := op.RenameColumns("samples", map[string]string{
|
||||
"imel": "email",
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *revision4) Down(op Operation) error {
|
||||
_, err := op.RenameColumns("samples", map[string]string{
|
||||
"email": "imel",
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *revision4) Revision() int64 {
|
||||
return 4
|
||||
}
|
||||
|
||||
// ----------
|
||||
|
||||
var db *sql.DB
|
||||
|
||||
var testSchema = `
|
||||
|
@ -163,7 +183,7 @@ func TestMigrateRenameTable(t *testing.T) {
|
|||
}
|
||||
|
||||
if sample.Imel != "foo@bar.com" {
|
||||
t.Errorf("Column doesn't match\n\texpect:\t%s\n\tget:\t%s", "foo@bar.com", sample.Imel)
|
||||
t.Errorf("Column doesn't match. Expect: %s, got: %s", "foo@bar.com", sample.Imel)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -198,6 +218,17 @@ func TestMigrateAddRemoveColumns(t *testing.T) {
|
|||
t.Errorf("Expect length columns: %d\nGot: %d", 5, len(columns))
|
||||
}
|
||||
|
||||
var row = AddColumnSample{
|
||||
ID: 33,
|
||||
Name: "Foo",
|
||||
Imel: "foo@bar.com",
|
||||
Url: "http://example.com",
|
||||
Num: 42,
|
||||
}
|
||||
if err := meddler.Save(db, "samples", &row); err != nil {
|
||||
t.Errorf("Can not save into database: %q", err)
|
||||
}
|
||||
|
||||
if err := mgr.MigrateTo(1); err != nil {
|
||||
t.Errorf("Can not migrate: %q", err)
|
||||
}
|
||||
|
@ -208,7 +239,36 @@ func TestMigrateAddRemoveColumns(t *testing.T) {
|
|||
}
|
||||
|
||||
if len(another_columns) != 3 {
|
||||
t.Errorf("Expect length columns: %d\nGot: %d", 3, len(columns))
|
||||
t.Errorf("Expect length columns = %d, got: %d", 3, len(columns))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenameColumn(t *testing.T) {
|
||||
defer tearDown()
|
||||
if err := setUp(); err != nil {
|
||||
t.Fatalf("Error preparing database: %q", err)
|
||||
}
|
||||
|
||||
Driver = SQLite
|
||||
|
||||
mgr := New(db)
|
||||
if err := mgr.Add(&revision1{}).Add(&revision4{}).MigrateTo(1); err != nil {
|
||||
t.Errorf("Can not migrate: %q", err)
|
||||
}
|
||||
|
||||
loadFixture(t)
|
||||
|
||||
if err := mgr.MigrateTo(4); err != nil {
|
||||
t.Errorf("Can not migrate: %q", err)
|
||||
}
|
||||
|
||||
row := RenameSample{}
|
||||
if err := meddler.QueryRow(db, &row, `SELECT * FROM samples WHERE id = 3;`); err != nil {
|
||||
t.Errorf("Can not query database: %q", err)
|
||||
}
|
||||
|
||||
if row.Email != "crash@bandicoot.io" {
|
||||
t.Errorf("Expect %s, got %s", "crash@bandicoot.io", row.Email)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue