Something went wrong on our end
-
Ernesto Rico Schmidt authored
This reverts commit a46824a3.
Ernesto Rico Schmidt authoredThis reverts commit a46824a3.
migrations.go 14.91 KiB
//
// tank, Import and Playlist Daemon for Aura project
// Copyright (C) 2017-2020 Christian Pointner <equinox@helsinki.at>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
package store
import (
"errors"
"time"
"github.com/jinzhu/gorm"
"gopkg.in/gormigrate.v1"
)
var (
dbMigrations = []*gormigrate.Migration{
{
ID: "201903131716",
Migrate: func(tx *gorm.DB) error { return nil },
Rollback: func(tx *gorm.DB) error { return nil },
},
{
ID: "201905160033",
Migrate: func(tx *gorm.DB) error {
type PlaylistEntry struct {
ID uint64 `json:"-" gorm:"primary_key"`
PlaylistID uint64 `json:"-" gorm:"not null;index;unique_index:unique_playlist_line_numbers"`
LineNum uint `json:"-" gorm:"not null;unique_index:unique_playlist_line_numbers"`
URI string `json:"uri" gorm:"size:1024"`
File *File `json:"file,omitempty" gorm:"association_autoupdate:false;association_autocreate:false"`
FileID *uint64 `json:"-" gorm:"index"`
}
type Playlist struct {
ID uint64 `json:"id" gorm:"primary_key"`
CreatedAt time.Time `json:"created"`
UpdatedAt time.Time `json:"updated"`
Description string `json:"description"`
ShowName string `json:"show" gorm:"not null;index"`
Show Show `json:"-" gorm:"association_foreignkey:Name"`
Entries []PlaylistEntry `json:"entries,omitempty"`
}
return tx.AutoMigrate(&Playlist{}).Error
},
Rollback: func(tx *gorm.DB) error {
return tx.Table("playlists").DropColumn("description").Error
},
},
{
ID: "201905291602",
Migrate: func(tx *gorm.DB) error {
type Import struct {
State ImportState `json:"state"`
Error string `json:"error,omitempty"`
}
type FileSource struct {
URI string `json:"uri" gorm:"size:1024"`
Hash string `json:"hash"`
Import Import `json:"import" gorm:"embedded;embedded_prefix:import__"`
}
type FileMetadata struct {
Artist string `json:"artist,omitempty" gorm:"index"`
Title string `json:"title,omitempty" gorm:"index"`
Album string `json:"album,omitempty" gorm:"index"`
}
type File struct {
ID uint64 `json:"id" gorm:"primary_key"`
CreatedAt time.Time `json:"created"`
UpdatedAt time.Time `json:"updated"`
ShowName string `json:"show" gorm:"not null;index"`
Show Show `json:"-" gorm:"association_foreignkey:Name"`
Source FileSource `json:"source" gorm:"embedded;embedded_prefix:source__"`
Metadata FileMetadata `json:"metadata" gorm:"embedded;embedded_prefix:metadata__"`
Size uint64 `json:"size"`
Duration time.Duration `json:"duration"`
}
return tx.AutoMigrate(&File{}).Error
},
Rollback: func(tx *gorm.DB) error {
return tx.Table("files").DropColumn("source__import__error").Error
},
},
{
ID: "201906010144",
Migrate: func(tx *gorm.DB) error {
type ImportLog struct {
ID uint64 `gorm:"primary_key"`
File File `gorm:"association_autoupdate:false;association_autocreate:false"`
FileID uint64 `gorm:"not null;index;unique_index:unique_import_log_step"`
ImportStep string `gorm:"not null;index;unique_index:unique_import_log_step"`
Encoded []byte
}
if err := tx.AutoMigrate(&ImportLog{}).Error; err != nil {
return err
}
if err := tx.Model(ImportLog{}).AddForeignKey("file_id", "files (id)", "CASCADE", "CASCADE").Error; err != nil {
return err
}
return nil
},
Rollback: func(tx *gorm.DB) error {
if err := tx.Model(ImportLog{}).RemoveForeignKey("file_id", "files (id)").Error; err != nil {
return err
}
return tx.DropTable("import_logs").Error
},
},
{
ID: "201906051520",
Migrate: func(tx *gorm.DB) error {
if tx.Dialect().GetName() == "mysql" {
tx.Model(ImportLog{}).ModifyColumn("encoded", "longblob")
}
return nil
},
Rollback: func(tx *gorm.DB) error {
// to be exact we would need to chang `encoded` back to varbinary(255) for mysql
// however this will open a box of worms....
return nil
},
},
{
ID: "201908110945",
Migrate: func(tx *gorm.DB) error {
type PlaylistEntry struct {
ID uint64 `json:"-" gorm:"primary_key"`
PlaylistID uint64 `json:"-" gorm:"not null;index;unique_index:unique_playlist_line_numbers"`
LineNum uint `json:"-" gorm:"not null;unique_index:unique_playlist_line_numbers"`
URI string `json:"uri" gorm:"size:1024"`
File *File `json:"file,omitempty" gorm:"association_autoupdate:false;association_autocreate:false"`
FileID *uint64 `json:"-" gorm:"index"`
}
type Playlist struct {
ID uint64 `json:"id" gorm:"primary_key"`
CreatedAt time.Time `json:"created"`
UpdatedAt time.Time `json:"updated"`
Description string `json:"description"`
PlayoutMode string `json:"playoutMode"`
ShowName string `json:"show" gorm:"not null;index"`
Show Show `json:"-" gorm:"association_foreignkey:Name"`
Entries []PlaylistEntry `json:"entries,omitempty"`
}
return tx.AutoMigrate(&Playlist{}).Error
},
Rollback: func(tx *gorm.DB) error {
return tx.Table("playlists").DropColumn("playout_mode").Error
},
},
{
ID: "201908150104",
Migrate: func(tx *gorm.DB) error {
type Import struct {
State ImportState `json:"state"`
Error string `json:"error,omitempty"`
}
type FileSource struct {
URI string `json:"uri" gorm:"size:1024"`
Hash string `json:"hash"`
Import Import `json:"import" gorm:"embedded;embedded_prefix:import__"`
}
type FileMetadata struct {
Artist string `json:"artist,omitempty" gorm:"index"`
Title string `json:"title,omitempty" gorm:"index"`
Album string `json:"album,omitempty" gorm:"index"`
Organization string `json:"organization,omitempty" gorm:"index"`
ISRC string `json:"isrc,omitempty" gorm:"index"`
}
type File struct {
ID uint64 `json:"id" gorm:"primary_key"`
CreatedAt time.Time `json:"created"`
UpdatedAt time.Time `json:"updated"`
ShowName string `json:"show" gorm:"not null;index"`
Show Show `json:"-" gorm:"association_foreignkey:Name"`
Source FileSource `json:"source" gorm:"embedded;embedded_prefix:source__"`
Metadata FileMetadata `json:"metadata" gorm:"embedded;embedded_prefix:metadata__"`
Size uint64 `json:"size"`
Duration time.Duration `json:"duration"`
}
return tx.AutoMigrate(&File{}).Error
},
Rollback: func(tx *gorm.DB) error {
if err := tx.Table("files").DropColumn("metadata__organization").Error; err != nil {
return err
}
return tx.Table("files").DropColumn("metadata__isrc").Error
},
},
{
ID: "202006130203",
Migrate: func(tx *gorm.DB) error {
type PlaylistEntry struct {
ID uint64 `json:"-" gorm:"primary_key"`
PlaylistID uint64 `json:"-" gorm:"not null;index;unique_index:unique_playlist_line_numbers"`
LineNum uint `json:"-" gorm:"not null;unique_index:unique_playlist_line_numbers"`
URI string `json:"uri" gorm:"size:1024"`
Duration *time.Duration `json:"duration,omitempty"`
File *File `json:"file,omitempty" gorm:"association_autoupdate:false;association_autocreate:false"`
FileID *uint64 `json:"-" gorm:"index"`
}
// actually all playlists would need to be verfied if they still fit the new contstraint
// that only allows a sinle non-file entry with durtion == NULL per playlist.
// However we are still pre-first-release and all migrations will likely be squashed before that
// release anyway...
return tx.AutoMigrate(&PlaylistEntry{}).Error
},
Rollback: func(tx *gorm.DB) error {
return tx.Table("playlist_entries").DropColumn("duration").Error
},
},
{
ID: "202309141500",
Migrate: func(tx *gorm.DB) error {
type File struct {
ID uint64 `json:"id" gorm:"primary_key"`
CreatedAt time.Time `json:"created"`
UpdatedAt time.Time `json:"updated"`
ShowName string `json:"show" gorm:"not null;index"`
Show Show `json:"-" gorm:"association_foreignkey:Name"`
Source FileSource `json:"source" gorm:"embedded;embedded_prefix:source__"`
Metadata FileMetadata `json:"metadata" gorm:"embedded;embedded_prefix:metadata__"`
Size uint64 `json:"size"`
Duration float64 `json:"duration"`
}
if err := tx.Model(&File{}).ModifyColumn("duration", "float").Error; err != nil {
return err
}
type PlaylistEntry struct {
ID uint64 `json:"-" gorm:"primary_key"`
PlaylistID uint64 `json:"-" gorm:"not null;index;unique_index:unique_playlist_line_numbers"`
LineNum uint `json:"-" gorm:"not null;unique_index:unique_playlist_line_numbers"`
URI string `json:"uri" gorm:"size:1024"`
Duration *float64 `json:"duration,omitempty"`
File *File `json:"file,omitempty" gorm:"association_autoupdate:false;association_autocreate:false"`
FileID *uint64 `json:"-" gorm:"index"`
}
return tx.Model(&PlaylistEntry{}).ModifyColumn("duration", "float").Error
},
Rollback: func(tx *gorm.DB) error {
type File struct {
ID uint64 `json:"id" gorm:"primary_key"`
CreatedAt time.Time `json:"created"`
UpdatedAt time.Time `json:"updated"`
ShowName string `json:"show" gorm:"not null;index"`
Show Show `json:"-" gorm:"association_foreignkey:Name"`
Source FileSource `json:"source" gorm:"embedded;embedded_prefix:source__"`
Metadata FileMetadata `json:"metadata" gorm:"embedded;embedded_prefix:metadata__"`
Size uint64 `json:"size"`
Duration time.Duration `json:"duration"`
}
if err := tx.Model(&File{}).ModifyColumn("duration", "int").Error; err != nil {
return err
}
type PlaylistEntry struct {
ID uint64 `json:"-" gorm:"primary_key"`
PlaylistID uint64 `json:"-" gorm:"not null;index;unique_index:unique_playlist_line_numbers"`
LineNum uint `json:"-" gorm:"not null;unique_index:unique_playlist_line_numbers"`
URI string `json:"uri" gorm:"size:1024"`
Duration *time.Duration `json:"duration,omitempty"`
File *File `json:"file,omitempty" gorm:"association_autoupdate:false;association_autocreate:false"`
FileID *uint64 `json:"-" gorm:"index"`
}
return tx.Model(&Playlist{}).ModifyColumn("duration", "int").Error
},
},
{
ID: "202312011500",
Migrate: func(tx *gorm.DB) error {
type Show struct {
ID uint64 `json:"id" gorm:"primary_key"`
CreatedAt time.Time `json:"created"`
UpdatedAt time.Time `json:"updated"`
}
type File struct {
ID uint64 `json:"id" gorm:"primary_key"`
CreatedAt time.Time `json:"created"`
UpdatedAt time.Time `json:"updated"`
ShowID uint64 `json:"showID" gorm:"not null;index"`
Show Show `json:"-" gorm:"association_foreignkey:ID"`
Source FileSource `json:"source" gorm:"embedded;embedded_prefix:source__"`
Metadata FileMetadata `json:"metadata" gorm:"embedded;embedded_prefix:metadata__"`
Size uint64 `json:"size"`
Duration float64 `json:"duration"`
}
type Playlist struct {
ID uint64 `json:"id" gorm:"primary_key"`
CreatedAt time.Time `json:"created"`
UpdatedAt time.Time `json:"updated"`
Description string `json:"description"`
PlayoutMode string `json:"playoutMode" gorm:"not null;default:'linear'"`
ShowID uint64 `json:"showID" gorm:"not null;index"`
Show Show `json:"-" gorm:"association_foreignkey:ID"`
Entries []PlaylistEntry `json:"entries"`
}
return tx.AutoMigrate(&Show{}, &File{}, &Playlist{}).Error
},
Rollback: func(tx *gorm.DB) error {
if err := tx.Table("shows").DropColumn("id").Error; err != nil {
return err
}
if err := tx.Table("files").DropColumn("show_id").Error; err != nil {
return err
}
return tx.Table("playlists").DropColumn("show_id").Error
},
},
}
)
func initialMigration(tx *gorm.DB) (err error) {
err = tx.AutoMigrate(
&Show{},
&File{},
&ImportLog{},
&Playlist{},
&PlaylistEntry{},
).Error
if err != nil {
return
}
// TODO: sadly this does not work on sqlite because constraints can only be added with create table...
// unfortunately gorm does not create foreign key contstraints in AutoMigrate(), see: https://github.com/jinzhu/gorm/issues/450
if err := tx.Model(File{}).AddForeignKey("show_id", "shows (id)", "CASCADE", "CASCADE").Error; err != nil {
return err
}
if err := tx.Model(ImportLog{}).AddForeignKey("file_id", "files (id)", "CASCADE", "CASCADE").Error; err != nil {
return err
}
if err := tx.Model(Playlist{}).AddForeignKey("show_id", "shows (id)", "CASCADE", "CASCADE").Error; err != nil {
return err
}
if err := tx.Model(PlaylistEntry{}).AddForeignKey("playlist_id", "playlists (id)", "CASCADE", "CASCADE").Error; err != nil {
return err
}
if err := tx.Model(PlaylistEntry{}).AddForeignKey("file_id", "files (id)", "RESTRICT", "CASCADE").Error; err != nil {
return err
}
return nil
}
func (st *Store) initDBModel(cfg DBConfig) (err error) {
opts := gormigrate.DefaultOptions
opts.TableName = migrationsTn
opts.IDColumnSize = 64
opts.UseTransaction = true
if cfg.Type == "mysql" {
// mySQl does not support DDL commands inside transactions
opts.UseTransaction = false
}
m := gormigrate.New(st.db, opts, dbMigrations)
m.InitSchema(initialMigration)
if err = m.Migrate(); err != nil {
return errors.New("running database migrations failed: " + err.Error())
}
if err = st.db.Table(migrationsTn).Select("id").Not("id = ?", "SCHEMA_INIT").Order("id DESC").Limit(1).Row().Scan(&st.revision); err != nil {
return errors.New("fetching current database revision failed: " + err.Error())
}
return
}