/*
* Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
*
* This file is licensed under AGPL-3.0-or-later, see NOTICE and LICENSE for
* more information.
*/
package db
import (
"context"
"fmt"
"log"
"github.com/jackc/pgx/v5/pgtype"
)
var migrationsTable string = "schema_migrations"
func RunMigrations() {
log.Print("[tixe/db] Running migrations")
var ranTimestamp pgtype.Timestamptz
var schemaVersion int = 0
schemaVersionQuery := fmt.Sprintf(
`SELECT ran_timestamp, schema_version
FROM %s
ORDER BY schema_version
DESC LIMIT 1;`,
migrationsTable)
err := PgPool.QueryRow(context.Background(), schemaVersionQuery).Scan(&ranTimestamp, &schemaVersion)
if err != nil {
log.Print("[tixe/db] No schema version found, running all migrations")
} else {
log.Printf("[tixe/db] Last migration ran on %s, with schema version %d", ranTimestamp.Time, schemaVersion)
}
migrations := migrations()
if schemaVersion != len(migrations) {
for i := 0; i < len(migrations); i++ {
if i >= schemaVersion {
log.Printf("[tixe/db] Running migration %d", i + 1) // + 1 is just a visual thing
_, err := PgPool.Exec(context.Background(), migrations[i])
if err != nil {
log.Fatalf("[tixe/db] Migration %d failed to run!", i)
}
}
}
// We are now up to date
schemaVersion = len(migrations)
// Create a new entry in the migrations table
schemaMigrationInsertQuery := fmt.Sprintf(
`INSERT INTO %s(ran_timestamp, schema_version) VALUES(CURRENT_TIMESTAMP, %d);`,
migrationsTable, schemaVersion)
_, err = PgPool.Exec(context.Background(), schemaMigrationInsertQuery)
if err != nil {
log.Fatal("[tixe/db] Migrations ran, but was not able to create migration entry")
} else {
log.Print("[tixe/db] Migrations ran successfully")
}
} else {
log.Printf("[tixe/db] Already on schema version %d, no migrations to run", schemaVersion)
}
}
func migrations() []string {
return []string{
fmt.Sprintf(`CREATE TABLE %s (
ran_timestamp timestamp,
schema_version integer
)`, migrationsTable),
fmt.Sprintf(`CREATE TABLE users (
id CHAR(26) NOT NULL PRIMARY KEY,
display_name TEXT NOT NULL,
oidc_subject TEXT
)`),
fmt.Sprintf(`CREATE TABLE tags (
id CHAR(26) NOT NULL PRIMARY KEY,
user_id CHAR(26) NOT NULL REFERENCES users(id),
tag TEXT NOT NULL,
UNIQUE (user_id, tag)
)`),
fmt.Sprintf(`CREATE TABLE links (
id CHAR(26) NOT NULL PRIMARY KEY,
user_id CHAR(26) NOT NULL REFERENCES users(id),
visual TEXT NOT NULL,
link TEXT NOT NULL
)`),
fmt.Sprintf(`CREATE TABLE linktags (
link_id CHAR(26) NOT NULL REFERENCES links(id),
tag_id CHAR(26) NOT NULL REFERENCES tags(id)
)`),
}
}