Files
gas-stack/pkg/db/connect.go

125 lines
3.4 KiB
Go

package db
import (
_ "embed"
"errors"
"fmt"
"os"
"github.com/jmoiron/sqlx"
_ "github.com/mattn/go-sqlite3"
"git.offline-twitter.com/offline-labs/gas-stack/pkg/textutils"
)
type DBConfig struct {
// // Tracks whether the DB connector has been initialized
// is_initialized bool
// The SQL schema of the database under management
sql_schema *string
// Database starts at version 0. First migration brings us to version 1
migrations *[]Migration
version_number uint
}
func Init(schema *string, migrationsList *[]Migration) DBConfig {
return DBConfig{
sql_schema: schema,
migrations: migrationsList,
version_number: uint(len(*migrationsList)),
}
}
func (c DBConfig) Create(path string) (*sqlx.DB, error) {
// First check if the path already exists
_, err := os.Stat(path)
if err == nil {
return nil, ErrDatabaseAlreadyExists
} else if !errors.Is(err, os.ErrNotExist) {
return nil, fmt.Errorf("path error: %w", err)
}
// Create DB file
fmt.Printf("Creating............. %s\n", path)
db, err := sqlx.Open("sqlite3", path)
if err != nil {
return nil, fmt.Errorf("opening db: %w", err)
}
// Initialize schema
if _, err = db.Exec("pragma foreign_keys=on; pragma journal_mode=WAL;"); err != nil {
return nil, fmt.Errorf("running pragma statements: %w", err)
}
if _, err = db.Exec(*c.sql_schema); err != nil {
return nil, fmt.Errorf("creating schema: %w", err)
}
return db, nil
}
func (c DBConfig) Connect(path string) (*sqlx.DB, error) {
db, err := sqlx.Open("sqlite3", path)
if err != nil {
return nil, fmt.Errorf("opening db: %w", err)
}
if _, err = db.Exec("pragma foreign_keys=on; pragma journal_mode=WAL;"); err != nil {
return nil, fmt.Errorf("running pragma statements: %w", err)
}
err = c.CheckAndUpdateVersion(db)
return db, err
}
func (c DBConfig) CheckAndUpdateVersion(db *sqlx.DB) error {
var version uint
err := db.Get(&version, "select version from db_version")
if err != nil {
return fmt.Errorf("couldn't check database version: %w", err)
}
if version > c.version_number {
return VersionMismatchError{c.version_number, version}
}
if c.version_number > version {
fmt.Print(textutils.ColorYellow)
fmt.Printf("================================================\n")
fmt.Printf("Database version is out of date. Upgrading database from version %d to version %d!\n", version,
c.version_number)
fmt.Print(textutils.ColorReset)
for i := version; i < c.version_number; i++ {
m := (*c.migrations)[i]
if err := m.Apply(db); err != nil {
return fmt.Errorf("migration %d: %w", m.ID, err)
}
fmt.Print(textutils.ColorYellow)
fmt.Printf("Now at database schema version %d.\n", m.ID)
fmt.Print(textutils.ColorReset)
}
fmt.Print(textutils.ColorGreen)
fmt.Printf("================================================\n")
fmt.Printf("Database version has been upgraded to version %d.\n", c.version_number)
fmt.Print(textutils.ColorReset)
}
return nil
}
type VersionMismatchError struct {
EngineVersion uint
DatabaseVersion uint
}
func (e VersionMismatchError) Error() string {
return fmt.Sprintf(
`This profile was created with database schema version %d, which is newer than this application's database schema version, %d.
Please upgrade this application to a newer version to use this profile. Or downgrade the profile's schema version, somehow.`,
e.DatabaseVersion, e.EngineVersion,
)
}