2020-02-07 17:27:45 +01:00
|
|
|
// Vikunja is a to-do list application to facilitate your life.
|
2020-01-09 18:33:22 +01:00
|
|
|
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
|
2019-03-29 18:54:35 +01:00
|
|
|
//
|
2019-12-04 20:39:56 +01:00
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
2019-03-29 18:54:35 +01:00
|
|
|
//
|
2019-12-04 20:39:56 +01:00
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License for more details.
|
2019-03-29 18:54:35 +01:00
|
|
|
//
|
2019-12-04 20:39:56 +01:00
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
2019-03-29 18:54:35 +01:00
|
|
|
|
|
|
|
package migration
|
|
|
|
|
|
|
|
import (
|
2019-07-06 22:12:26 +02:00
|
|
|
"code.vikunja.io/api/pkg/config"
|
2019-03-29 18:54:35 +01:00
|
|
|
"code.vikunja.io/api/pkg/db"
|
2019-10-16 22:52:29 +02:00
|
|
|
"code.vikunja.io/api/pkg/files"
|
2019-03-29 18:54:35 +01:00
|
|
|
"code.vikunja.io/api/pkg/log"
|
|
|
|
"code.vikunja.io/api/pkg/models"
|
2020-01-26 18:08:06 +01:00
|
|
|
"code.vikunja.io/api/pkg/modules/migration"
|
|
|
|
"code.vikunja.io/api/pkg/user"
|
2019-03-29 18:54:35 +01:00
|
|
|
"github.com/olekukonko/tablewriter"
|
|
|
|
"os"
|
|
|
|
"sort"
|
|
|
|
"src.techknowlogick.com/xormigrate"
|
2020-02-14 17:34:25 +01:00
|
|
|
"xorm.io/xorm"
|
2019-03-29 18:54:35 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
// You can get the id string for new migrations by running `date +%Y%m%d%H%M%S` on a unix system.
|
|
|
|
|
|
|
|
var migrations []*xormigrate.Migration
|
|
|
|
|
|
|
|
// A helper function because we need a migration in various places which we can't really solve with an init() function.
|
|
|
|
func initMigration(x *xorm.Engine) *xormigrate.Xormigrate {
|
|
|
|
// Get our own xorm engine if we don't have one
|
|
|
|
if x == nil {
|
|
|
|
var err error
|
|
|
|
x, err = db.CreateDBEngine()
|
|
|
|
if err != nil {
|
2019-07-20 20:12:10 +02:00
|
|
|
log.Criticalf("Could not connect to db: %v", err.Error())
|
2019-03-29 18:54:35 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Because init() does not guarantee the order in which these are added to the slice,
|
|
|
|
// we need to sort them to ensure that they are in order
|
|
|
|
sort.Slice(migrations, func(i, j int) bool {
|
|
|
|
return migrations[i].ID < migrations[j].ID
|
|
|
|
})
|
|
|
|
|
|
|
|
m := xormigrate.New(x, migrations)
|
|
|
|
m.NewLogger(log.GetLogWriter("database"))
|
|
|
|
m.InitSchema(initSchema)
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
|
|
|
// Migrate runs all migrations
|
|
|
|
func Migrate(x *xorm.Engine) {
|
|
|
|
m := initMigration(x)
|
|
|
|
err := m.Migrate()
|
|
|
|
if err != nil {
|
2019-07-20 20:12:10 +02:00
|
|
|
log.Fatalf("Migration failed: %v", err)
|
2019-03-29 18:54:35 +01:00
|
|
|
}
|
2019-07-20 20:12:10 +02:00
|
|
|
log.Info("Ran all migrations successfully.")
|
2019-03-29 18:54:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// ListMigrations pretty-prints a list with all migrations.
|
|
|
|
func ListMigrations() {
|
|
|
|
x, err := db.CreateDBEngine()
|
|
|
|
if err != nil {
|
2019-07-20 20:12:10 +02:00
|
|
|
log.Fatalf("Could not connect to db: %v", err.Error())
|
2019-03-29 18:54:35 +01:00
|
|
|
}
|
|
|
|
ms := []*xormigrate.Migration{}
|
|
|
|
err = x.Find(&ms)
|
|
|
|
if err != nil {
|
2019-07-20 20:12:10 +02:00
|
|
|
log.Fatalf("Error getting migration table: %v", err.Error())
|
2019-03-29 18:54:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
table := tablewriter.NewWriter(os.Stdout)
|
|
|
|
table.SetHeader([]string{"ID", "Description"})
|
|
|
|
table.SetAlignment(tablewriter.ALIGN_LEFT)
|
|
|
|
table.SetHeaderColor(tablewriter.Colors{tablewriter.Bold, tablewriter.BgGreenColor},
|
|
|
|
tablewriter.Colors{tablewriter.Bold, tablewriter.BgGreenColor})
|
|
|
|
|
|
|
|
for _, m := range ms {
|
|
|
|
table.Append([]string{m.ID, m.Description})
|
|
|
|
}
|
|
|
|
table.Render()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Rollback rolls back all migrations until a certain point.
|
|
|
|
func Rollback(migrationID string) {
|
|
|
|
m := initMigration(nil)
|
|
|
|
err := m.RollbackTo(migrationID)
|
|
|
|
if err != nil {
|
2019-07-20 20:12:10 +02:00
|
|
|
log.Fatalf("Could not rollback: %v", err)
|
2019-03-29 18:54:35 +01:00
|
|
|
}
|
2019-07-20 20:12:10 +02:00
|
|
|
log.Info("Rolled back successfully.")
|
2019-03-29 18:54:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Deletes a column from a table. All arguments are strings, to let them be standalone and not depending on any struct.
|
|
|
|
func dropTableColum(x *xorm.Engine, tableName, col string) error {
|
|
|
|
|
2019-07-06 22:12:26 +02:00
|
|
|
switch config.DatabaseType.GetString() {
|
2019-03-29 18:54:35 +01:00
|
|
|
case "sqlite":
|
2019-07-20 20:12:10 +02:00
|
|
|
log.Warning("Unable to drop columns in SQLite")
|
2019-03-29 18:54:35 +01:00
|
|
|
case "mysql":
|
|
|
|
_, err := x.Exec("ALTER TABLE " + tableName + " DROP COLUMN " + col)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-02-16 22:42:04 +01:00
|
|
|
case "postgres":
|
|
|
|
_, err := x.Exec("ALTER TABLE " + tableName + " DROP COLUMN " + col)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-03-29 18:54:35 +01:00
|
|
|
default:
|
2019-07-20 20:12:10 +02:00
|
|
|
log.Fatal("Unknown db.")
|
2019-03-29 18:54:35 +01:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-07-18 21:56:34 +02:00
|
|
|
// Modifies a column definition
|
|
|
|
func modifyColumn(x *xorm.Engine, tableName, col, newDefinition string) error {
|
|
|
|
switch config.DatabaseType.GetString() {
|
|
|
|
case "sqlite":
|
2019-07-20 20:12:10 +02:00
|
|
|
log.Warning("Unable to modify columns in SQLite")
|
2019-07-18 21:56:34 +02:00
|
|
|
case "mysql":
|
|
|
|
_, err := x.Exec("ALTER TABLE " + tableName + " MODIFY COLUMN " + col + " " + newDefinition)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-02-16 22:42:04 +01:00
|
|
|
case "postgres":
|
|
|
|
_, err := x.Exec("ALTER TABLE " + tableName + " ALTER COLUMN " + col + " " + newDefinition)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-07-18 21:56:34 +02:00
|
|
|
default:
|
2019-07-20 20:12:10 +02:00
|
|
|
log.Fatal("Unknown db.")
|
2019-07-18 21:56:34 +02:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-03-29 18:54:35 +01:00
|
|
|
func initSchema(tx *xorm.Engine) error {
|
2019-10-16 22:52:29 +02:00
|
|
|
schemeBeans := []interface{}{}
|
|
|
|
schemeBeans = append(schemeBeans, models.GetTables()...)
|
|
|
|
schemeBeans = append(schemeBeans, files.GetTables()...)
|
2020-01-26 18:08:06 +01:00
|
|
|
schemeBeans = append(schemeBeans, migration.GetTables()...)
|
|
|
|
schemeBeans = append(schemeBeans, user.GetTables()...)
|
2019-10-16 22:52:29 +02:00
|
|
|
return tx.Sync2(schemeBeans...)
|
2019-03-29 18:54:35 +01:00
|
|
|
}
|