From 94eea2f32feca88e72088763c97ac79ad53f518d Mon Sep 17 00:00:00 2001 From: konrad Date: Sun, 24 Mar 2019 21:45:18 +0100 Subject: [PATCH] Started adding migrations --- pkg/migration/20190324205606.go | 17 ++ pkg/migration/migration.go | 8 + .../go-xorm/xorm/migrate/migrate.go | 214 ++++++++++++++++++ vendor/modules.txt | 1 + 4 files changed, 240 insertions(+) create mode 100644 pkg/migration/20190324205606.go create mode 100644 vendor/github.com/go-xorm/xorm/migrate/migrate.go diff --git a/pkg/migration/20190324205606.go b/pkg/migration/20190324205606.go new file mode 100644 index 000000000..a138d474c --- /dev/null +++ b/pkg/migration/20190324205606.go @@ -0,0 +1,17 @@ +// Vikunja is a todo-list application to facilitate your life. +// Copyright 2019 Vikunja and contributors. All rights reserved. +// +// 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. +// +// 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. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package migration diff --git a/pkg/migration/migration.go b/pkg/migration/migration.go index 410a7872b..49a9610dc 100644 --- a/pkg/migration/migration.go +++ b/pkg/migration/migration.go @@ -16,6 +16,14 @@ package migration +import "src.techknowlogick.com/xormigrate" + +// 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 + func Migrate() { + // 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 } diff --git a/vendor/github.com/go-xorm/xorm/migrate/migrate.go b/vendor/github.com/go-xorm/xorm/migrate/migrate.go new file mode 100644 index 000000000..6c2a13a8a --- /dev/null +++ b/vendor/github.com/go-xorm/xorm/migrate/migrate.go @@ -0,0 +1,214 @@ +package migrate + +import ( + "errors" + "fmt" + + "github.com/go-xorm/xorm" +) + +// MigrateFunc is the func signature for migrating. +type MigrateFunc func(*xorm.Engine) error + +// RollbackFunc is the func signature for rollbacking. +type RollbackFunc func(*xorm.Engine) error + +// InitSchemaFunc is the func signature for initializing the schema. +type InitSchemaFunc func(*xorm.Engine) error + +// Options define options for all migrations. +type Options struct { + // TableName is the migration table. + TableName string + // IDColumnName is the name of column where the migration id will be stored. + IDColumnName string +} + +// Migration represents a database migration (a modification to be made on the database). +type Migration struct { + // ID is the migration identifier. Usually a timestamp like "201601021504". + ID string + // Migrate is a function that will br executed while running this migration. + Migrate MigrateFunc + // Rollback will be executed on rollback. Can be nil. + Rollback RollbackFunc +} + +// Migrate represents a collection of all migrations of a database schema. +type Migrate struct { + db *xorm.Engine + options *Options + migrations []*Migration + initSchema InitSchemaFunc +} + +var ( + // DefaultOptions can be used if you don't want to think about options. + DefaultOptions = &Options{ + TableName: "migrations", + IDColumnName: "id", + } + + // ErrRollbackImpossible is returned when trying to rollback a migration + // that has no rollback function. + ErrRollbackImpossible = errors.New("It's impossible to rollback this migration") + + // ErrNoMigrationDefined is returned when no migration is defined. + ErrNoMigrationDefined = errors.New("No migration defined") + + // ErrMissingID is returned when the ID od migration is equal to "" + ErrMissingID = errors.New("Missing ID in migration") + + // ErrNoRunnedMigration is returned when any runned migration was found while + // running RollbackLast + ErrNoRunnedMigration = errors.New("Could not find last runned migration") +) + +// New returns a new Gormigrate. +func New(db *xorm.Engine, options *Options, migrations []*Migration) *Migrate { + return &Migrate{ + db: db, + options: options, + migrations: migrations, + } +} + +// InitSchema sets a function that is run if no migration is found. +// The idea is preventing to run all migrations when a new clean database +// is being migrating. In this function you should create all tables and +// foreign key necessary to your application. +func (m *Migrate) InitSchema(initSchema InitSchemaFunc) { + m.initSchema = initSchema +} + +// Migrate executes all migrations that did not run yet. +func (m *Migrate) Migrate() error { + if err := m.createMigrationTableIfNotExists(); err != nil { + return err + } + + if m.initSchema != nil && m.isFirstRun() { + if err := m.runInitSchema(); err != nil { + return err + } + return nil + } + + for _, migration := range m.migrations { + if err := m.runMigration(migration); err != nil { + return err + } + } + return nil +} + +// RollbackLast undo the last migration +func (m *Migrate) RollbackLast() error { + if len(m.migrations) == 0 { + return ErrNoMigrationDefined + } + + lastRunnedMigration, err := m.getLastRunnedMigration() + if err != nil { + return err + } + + if err := m.RollbackMigration(lastRunnedMigration); err != nil { + return err + } + return nil +} + +func (m *Migrate) getLastRunnedMigration() (*Migration, error) { + for i := len(m.migrations) - 1; i >= 0; i-- { + migration := m.migrations[i] + if m.migrationDidRun(migration) { + return migration, nil + } + } + return nil, ErrNoRunnedMigration +} + +// RollbackMigration undo a migration. +func (m *Migrate) RollbackMigration(mig *Migration) error { + if mig.Rollback == nil { + return ErrRollbackImpossible + } + + if err := mig.Rollback(m.db); err != nil { + return err + } + + sql := fmt.Sprintf("DELETE FROM %s WHERE %s = ?", m.options.TableName, m.options.IDColumnName) + if _, err := m.db.Exec(sql, mig.ID); err != nil { + return err + } + return nil +} + +func (m *Migrate) runInitSchema() error { + if err := m.initSchema(m.db); err != nil { + return err + } + + for _, migration := range m.migrations { + if err := m.insertMigration(migration.ID); err != nil { + return err + } + } + + return nil +} + +func (m *Migrate) runMigration(migration *Migration) error { + if len(migration.ID) == 0 { + return ErrMissingID + } + + if !m.migrationDidRun(migration) { + if err := migration.Migrate(m.db); err != nil { + return err + } + + if err := m.insertMigration(migration.ID); err != nil { + return err + } + } + return nil +} + +func (m *Migrate) createMigrationTableIfNotExists() error { + exists, err := m.db.IsTableExist(m.options.TableName) + if err != nil { + return err + } + if exists { + return nil + } + + sql := fmt.Sprintf("CREATE TABLE %s (%s VARCHAR(255) PRIMARY KEY)", m.options.TableName, m.options.IDColumnName) + if _, err := m.db.Exec(sql); err != nil { + return err + } + return nil +} + +func (m *Migrate) migrationDidRun(mig *Migration) bool { + row := m.db.DB().QueryRow(fmt.Sprintf("SELECT COUNT(*) FROM %s WHERE %s = ?", m.options.TableName, m.options.IDColumnName), mig.ID) + var count int + row.Scan(&count) + return count > 0 +} + +func (m *Migrate) isFirstRun() bool { + row := m.db.DB().QueryRow(fmt.Sprintf("SELECT COUNT(*) FROM %s", m.options.TableName)) + var count int + row.Scan(&count) + return count == 0 +} + +func (m *Migrate) insertMigration(id string) error { + sql := fmt.Sprintf("INSERT INTO %s (%s) VALUES (?)", m.options.TableName, m.options.IDColumnName) + _, err := m.db.Exec(sql, id) + return err +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 68deefeb3..9f13cea99 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -54,6 +54,7 @@ github.com/go-xorm/builder # github.com/go-xorm/core v0.5.8 github.com/go-xorm/core # github.com/go-xorm/xorm v0.0.0-20170930012613-29d4a0330a00 +github.com/go-xorm/xorm/migrate github.com/go-xorm/xorm # github.com/go-xorm/xorm-redis-cache v0.0.0-20180727005610-859b313566b2 github.com/go-xorm/xorm-redis-cache