// Package migrate allows you to update your database from your application package migrate import ( "database/sql" "errors" "fmt" "strconv" ) // Options contains all settings type Options struct { TableName string // Name used for version info table; defaults to DefaultTableName if not set Schema string // Schema used for version info table; For PostgreSQL, ignored if not set AssetPrefix string } // DefaultTableName is the name used when no TableName is specified in Options const DefaultTableName = `version` // ErrUpdatesMissing indicates an update is missing, making it impossible to execute the migration var ErrUpdatesMissing = errors.New(`Missing migration files`) // ErrDatabaseNewer indicates that the database version is newer than the requested version. We throw an error because downgrades might cause dataloss var ErrDatabaseNewer = errors.New(`Current version is newer than the requested version`) const fileFormat = `%04d.sql` // AssetFunc is a function that returns the data for the given name type AssetFunc func(string) ([]byte, error) // Migrate executes func Migrate(db *sql.DB, version int, o Options, asset AssetFunc) error { if o.TableName == `` { o.TableName = DefaultTableName } table := o.TableName if o.Schema != `` { table = o.Schema + `.` + table } _, err := db.Exec(`CREATE TABLE IF NOT EXISTS ` + table + ` (Version integer NOT NULL PRIMARY KEY)`) if err != nil { return err } row := db.QueryRow(`SELECT Version FROM ` + table + ` ORDER BY Version DESC`) if err != nil { return err } var v int err = row.Scan(&v) if err != sql.ErrNoRows && err != nil { return err } if v > version { return ErrDatabaseNewer } tx, err := db.Begin() if err != nil { return err } defer tx.Rollback() for i := v + 1; i <= version; i++ { asset, err := asset(fmt.Sprintf(o.AssetPrefix+fileFormat, version)) if err != nil { return ErrUpdatesMissing } _, err = tx.Exec(string(asset)) if err != nil { return err } _, err = tx.Exec(`INSERT INTO ` + table + ` VALUES (` + strconv.Itoa(i) + `)`) if err != nil { return err } } err = tx.Commit() if err != nil { return err } return nil }