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
|
// loop through and execute revisions
|
||||||
for _, rev := range m.revs {
|
for _, rev := range m.revs {
|
||||||
if rev.Revision() > current {
|
if rev.Revision() > current && rev.Revision() <= target {
|
||||||
current = rev.Revision()
|
current = rev.Revision()
|
||||||
// execute the revision Upgrade.
|
// execute the revision Upgrade.
|
||||||
if err := rev.Up(op); err != nil {
|
if err := rev.Up(op); err != nil {
|
||||||
|
|
|
@ -85,7 +85,7 @@ func (s *SQLiteDriver) DropColumns(tableName string, columnsToDrop []string) (sq
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up proxy table
|
// 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) {
|
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
|
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
|
var newColumns []string
|
||||||
for k, column := range selectName(columns) {
|
|
||||||
|
for k, column := range oldColumnsName {
|
||||||
|
added := false
|
||||||
for Old, New := range columnChanges {
|
for Old, New := range columnChanges {
|
||||||
if column == Old {
|
if column == Old {
|
||||||
columnToAdd := strings.Replace(columns[k], Old, New, 1)
|
columnToAdd := strings.Replace(columns[k], Old, New, 1)
|
||||||
|
newColumns = append(newColumns, columnToAdd)
|
||||||
if results, err := s.AddColumn(tableName, columnToAdd); err != nil {
|
added = true
|
||||||
return results, err
|
|
||||||
}
|
|
||||||
|
|
||||||
oldColumns = append(oldColumns, Old)
|
|
||||||
newColumns = append(newColumns, New)
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !added {
|
||||||
|
newColumns = append(newColumns, columns[k])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
statement := fmt.Sprintf("UPDATE %s SET %s;", tableName, setForUpdate(oldColumns, newColumns))
|
// Rename current table
|
||||||
if results, err := s.Tx.Exec(statement); err != nil {
|
proxyName := fmt.Sprintf("%s_%s", tableName, uniuri.NewLen(16))
|
||||||
return results, err
|
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) {
|
func (s *SQLiteDriver) getDDLFromTable(tableName string) (string, error) {
|
||||||
|
|
|
@ -24,14 +24,10 @@ type AddColumnSample struct {
|
||||||
ID int64 `meddler:"id,pk"`
|
ID int64 `meddler:"id,pk"`
|
||||||
Imel string `meddler:"imel"`
|
Imel string `meddler:"imel"`
|
||||||
Name string `meddler:"name"`
|
Name string `meddler:"name"`
|
||||||
|
Url string `meddler:"url"`
|
||||||
Num int64 `meddler:"num"`
|
Num int64 `meddler:"num"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type RemoveColumnSample struct {
|
|
||||||
ID int64 `meddler:"id,pk"`
|
|
||||||
Name string `meddler:"name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------- revision 1
|
// ---------- revision 1
|
||||||
|
|
||||||
type revision1 struct{}
|
type revision1 struct{}
|
||||||
|
@ -84,12 +80,12 @@ func (r *revision3) Up(op Operation) error {
|
||||||
if _, err := op.AddColumn("samples", "url VARCHAR(255)"); err != nil {
|
if _, err := op.AddColumn("samples", "url VARCHAR(255)"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err := op.AddColumn("samples", "likes INTEGER")
|
_, err := op.AddColumn("samples", "num INTEGER")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *revision3) Down(op Operation) error {
|
func (r *revision3) Down(op Operation) error {
|
||||||
_, err := op.DropColumns("samples", []string{"likes", "url"})
|
_, err := op.DropColumns("samples", []string{"num", "url"})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,6 +95,30 @@ func (r *revision3) Revision() int64 {
|
||||||
|
|
||||||
// ---------- end of revision 3
|
// ---------- 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 db *sql.DB
|
||||||
|
|
||||||
var testSchema = `
|
var testSchema = `
|
||||||
|
@ -163,7 +183,7 @@ func TestMigrateRenameTable(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if sample.Imel != "foo@bar.com" {
|
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))
|
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 {
|
if err := mgr.MigrateTo(1); err != nil {
|
||||||
t.Errorf("Can not migrate: %q", err)
|
t.Errorf("Can not migrate: %q", err)
|
||||||
}
|
}
|
||||||
|
@ -208,7 +239,36 @@ func TestMigrateAddRemoveColumns(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(another_columns) != 3 {
|
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