get database migrations up and running

This commit is contained in:
2021-07-02 18:13:58 -04:00
parent da239cf9ad
commit 04506ed01f
6 changed files with 227 additions and 43 deletions

View File

@@ -25,6 +25,7 @@ type migration struct {
type MySQL struct {
connection *sql.DB
tableName string
versionTable string
migrationsDirectory string
}
@@ -37,8 +38,9 @@ func NewMySQLConnection(user, pass, host, port, db string) (*MySQL, error) {
}
return &MySQL{
connection: connection,
tableName: "books",
versionTable: "migrations",
migrationsDirectory: "/migrations/mysql",
migrationsDirectory: "migrations/mysql",
}, nil
}
@@ -72,26 +74,26 @@ func (m *MySQL) GetLatestMigration(ctx context.Context) (int, error) {
}
var latestMigration int
err := m.connection.QueryRowContext(ctx, "SELECT MAX(id) FROM "+m.versionTable).Scan(&latestMigration)
err := m.connection.QueryRowContext(ctx, "SELECT COALESCE(MAX(id), 0) FROM "+m.versionTable).Scan(&latestMigration)
return latestMigration, err
}
func (m *MySQL) RunMigrations(ctx context.Context) (int, error) {
func (m *MySQL) RunMigrations(ctx context.Context) (int, int, error) {
if m.connection == nil || m.migrationsDirectory == "" || m.versionTable == "" {
return 0, fmt.Errorf("uninitialized mysql client")
return 0, 0, fmt.Errorf("uninitialized mysql client")
}
var migrations map[int]migration
migrations := map[int]migration{}
dir, err := migrationsFS.ReadDir(m.migrationsDirectory)
if err != nil {
return 0, nil
return 0, 0, err
}
for f := range dir {
if dir[f].Type().IsRegular() {
mig := migration{}
id, name, err := parseMigrationFileName(dir[f].Name())
if err != nil {
return 0, err
return 0, 0, err
}
mig.id, mig.name = id, name
mig.content, err = fs.ReadFile(migrationsFS, m.migrationsDirectory+"/"+dir[f].Name())
@@ -101,41 +103,43 @@ func (m *MySQL) RunMigrations(ctx context.Context) (int, error) {
latestMigrationRan, err := m.GetLatestMigration(ctx)
if err != nil {
return 0, err
return 0, 0, err
}
// exit if nothing to do (that is, there's no greater migration ID)
if _, ok := migrations[latestMigrationRan+1]; !ok {
return latestMigrationRan, nil
return latestMigrationRan, 0, nil
}
// loop over and apply migrations if required
tx, err := m.connection.BeginTx(ctx, nil)
if err != nil {
return latestMigrationRan, err
return latestMigrationRan, 0, err
}
migrationsRun := 0
for migrationsToRun := true; migrationsToRun; _, migrationsToRun = migrations[latestMigrationRan+1] {
mig := migrations[latestMigrationRan+1]
_, err := tx.ExecContext(ctx, string(mig.content))
if err != nil {
nestederr := tx.Rollback()
if nestederr != nil {
return latestMigrationRan, nestederr
return latestMigrationRan, migrationsRun, nestederr
}
return latestMigrationRan, err
return latestMigrationRan, migrationsRun, err
}
_, err = tx.ExecContext(ctx, "INSERT INTO "+m.versionTable+" (id, name, datetime) VALUES (?, ?, ?)", mig.id, mig.name, time.Now())
if err != nil {
nestederr := tx.Rollback()
if nestederr != nil {
return latestMigrationRan, nestederr
return latestMigrationRan, migrationsRun, nestederr
}
return latestMigrationRan, err
return latestMigrationRan, migrationsRun, err
}
latestMigrationRan = latestMigrationRan + 1
migrationsRun = migrationsRun + 1
}
err = tx.Commit()
return latestMigrationRan, err
return latestMigrationRan, migrationsRun, err
}
func (m *MySQL) GetAllBooks(ctx context.Context) ([]book.Book, error) {
@@ -144,7 +148,25 @@ func (m *MySQL) GetAllBooks(ctx context.Context) ([]book.Book, error) {
}
books := []book.Book{}
rows, err := m.connection.QueryContext(ctx, "SELECT id, title FROM books")
rows, err := m.connection.QueryContext(ctx, `
SELECT id,
title,
authors,
sortauthor,
isbn10,
isbn13,
format,
genre,
publisher,
series,
volume,
year,
signed,
description,
notes,
onloan,
coverurl
FROM `+m.tableName)
if err != nil {
return nil, err
}
@@ -152,10 +174,18 @@ func (m *MySQL) GetAllBooks(ctx context.Context) ([]book.Book, error) {
for rows.Next() {
b := book.Book{}
err := rows.Scan(&b.ID, &b.Title)
var authors string
err := rows.Scan(
&b.ID, &b.Title, &authors,
&b.SortAuthor, &b.ISBN10, &b.ISBN13,
&b.Format, &b.Genre, &b.Publisher,
&b.Series, &b.Volume, &b.Year,
&b.Signed, &b.Description, &b.Notes,
&b.OnLoan, &b.CoverURL)
if err != nil {
return nil, err
}
b.Authors = strings.Split(authors, ";")
books = append(books, b)
}
@@ -167,7 +197,28 @@ func (m *MySQL) AddBook(ctx context.Context, b *book.Book) error {
return fmt.Errorf("uninitialized mysql client")
}
res, err := m.connection.ExecContext(ctx, "INSERT INTO books (title) VALUES (?)", b.Title)
res, err := m.connection.ExecContext(ctx, `
INSERT INTO `+m.tableName+`
(title, authors, sortauthor, isbn10, isbn13, format, genre, publisher, series, volume, year, signed, description, notes, onloan, coverurl)
VALUES
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, )`,
b.Title,
strings.Join(b.Authors, ";"),
b.SortAuthor,
b.ISBN10,
b.ISBN13,
b.Format,
b.Genre,
b.Publisher,
b.Series,
b.Volume,
b.Year,
b.Signed,
b.Description,
b.Notes,
b.OnLoan,
b.CoverURL,
)
if err != nil {
return err
}
@@ -185,8 +236,47 @@ func (m *MySQL) UpdateBook(ctx context.Context, old, new *book.Book) error {
if m.connection == nil {
return fmt.Errorf("uninitialized mysql client")
}
if old.ID != new.ID {
return fmt.Errorf("cannot change book ID")
}
res, err := m.connection.ExecContext(ctx, "UPDATE books SET title=? WHERE id=?", new.Title, old.ID)
res, err := m.connection.ExecContext(ctx, `
UPDATE `+m.tableName+`
SET id=?
title=?
authors=?
sortauthor=?
isbn10=?
isbn13=?
format=?
genre=?
publisher=?
series=?
volume=?
year=?
signed=?
description=?
notes=?
onloan=?
coverurl=?
WHERE id=?`,
new.Title,
strings.Join(new.Authors, ";"),
new.SortAuthor,
new.ISBN10,
new.ISBN13,
new.Format,
new.Genre,
new.Publisher,
new.Series,
new.Volume,
new.Year,
new.Signed,
new.Description,
new.Notes,
new.OnLoan,
new.CoverURL,
old.ID)
if err != nil {
return err
}