Refactored, removed old remnants from v0.1.0, documentation added

This commit is contained in:
Magnus Åhall 2023-08-29 17:30:00 +02:00
parent a38bd4304f
commit d290a67800
2 changed files with 66 additions and 31 deletions

View File

@ -1,3 +1,22 @@
/*
Package dbschema is used to keep the SQL schema up to date.
func sqlProvider(dbName string, version int) (sql []byte, found bool) {
// read an SQL file and return the contents
return
}
upgrader := dbschema.NewUpgrader()
upgrader.SetSqlCallback(sqlProvider)
if err = upgrader.AddDatabase("127.0.0.1", 5432, "foo", "postgres", "password"); err != nil {
panic(err)
}
if err = upgrader.Run(); err != nil {
panic(err)
}
*/
package dbschema package dbschema
import ( import (
@ -8,6 +27,7 @@ import (
"database/sql" "database/sql"
) )
// An upgrader verifies the schema for one or more databases and upgrades them if possible.
type Upgrader struct { type Upgrader struct {
databases map[string]Database databases map[string]Database
logCallback func(string, string) logCallback func(string, string)

View File

@ -1,6 +1,9 @@
package dbschema package dbschema
import ( import (
// External
"github.com/lib/pq"
// Standard // Standard
"database/sql" "database/sql"
"fmt" "fmt"
@ -9,19 +12,49 @@ import (
func defaultCallback(topic, msg string) {// {{{ func defaultCallback(topic, msg string) {// {{{
fmt.Printf("[%s] %s\n", topic, msg) fmt.Printf("[%s] %s\n", topic, msg)
}// }}} }// }}}
func NewUpgrader(host string, port int, dbName, user, pass string) (upgrader Upgrader, err error) {// {{{
// NewUpgrader creates an upgrader with an empty list of databases.
func NewUpgrader() (upgrader Upgrader) {// {{{
upgrader.logCallback = defaultCallback upgrader.logCallback = defaultCallback
upgrader.databases = map[string]Database{} upgrader.databases = map[string]Database{}
return return
}// }}} }// }}}
// SetLogCallback allows to set a callback for custom logging.
func (upgrader *Upgrader) SetLogCallback(callback func(string, string)) {// {{{ func (upgrader *Upgrader) SetLogCallback(callback func(string, string)) {// {{{
upgrader.logCallback = callback upgrader.logCallback = callback
}// }}} }// }}}
// SetSqlCallback is required for providing the SQL schema updates.
func (upgrader *Upgrader) SetSqlCallback(callback func(string, int) ([]byte, bool)) {// {{{ func (upgrader *Upgrader) SetSqlCallback(callback func(string, int) ([]byte, bool)) {// {{{
upgrader.sqlCallback = callback upgrader.sqlCallback = callback
}// }}} }// }}}
func (dbase Database) createSchemaTable() (err error) {// {{{
dbase.upgrader.logCallback("create", fmt.Sprintf("%s, _db.schema", dbase.DbName))
_, err = dbase.db.Exec(`CREATE SCHEMA "_db"`)
// Error code 42P06 "duplicate_schema" is an OK error,
// table can still be missing and created.
pqErr, _ := err.(*pq.Error)
if pqErr != nil && pqErr.Code != "42P06" {
return
}
_, err = dbase.db.Exec(`
CREATE TABLE "_db"."schema" (
version int4 NOT NULL,
updated timestamp NOT NULL DEFAULT NOW(),
CONSTRAINT schema_pk PRIMARY KEY (version)
)`,
)
return
}// }}}
func (dbase Database) appendSchemaVersion(version int) (err error) {// {{{
_, err = dbase.db.Exec(`INSERT INTO _db.schema(version) VALUES($1)`, version)
return
}// }}}
func (dbase Database) verifySchemaTable() (err error) {// {{{ func (dbase Database) verifySchemaTable() (err error) {// {{{
var rows *sql.Rows var rows *sql.Rows
if rows, err = dbase.db.Query( if rows, err = dbase.db.Query(
@ -41,37 +74,21 @@ func (dbase Database) verifySchemaTable() (err error) {// {{{
return return
} }
if !exists { if exists {
dbase.upgrader.logCallback("create", fmt.Sprintf("%s, _db.schema", dbase.DbName)) return
dbase.db.Exec(`CREATE SCHEMA "_db"`)
if _, err = dbase.db.Exec(`
CREATE TABLE "_db"."schema" (
version int4 NOT NULL,
updated timestamp NOT NULL DEFAULT NOW(),
CONSTRAINT schema_pk PRIMARY KEY (version)
)`,
); err != nil {
return
}
} }
err = dbase.createSchemaTable()
return return
}// }}} }// }}}
func (dbase Database) verifySchemaEntry() (err error) {// {{{ func (dbase Database) verifySchemaEntry() (err error) {// {{{
var rows *sql.Rows var version int
rows, err = dbase.db.Query(`SELECT version FROM _db.schema LIMIT 1`) var row *sql.Row
if err != nil { row = dbase.db.QueryRow(`SELECT version FROM _db.schema LIMIT 1`)
return
}
defer rows.Close()
if !rows.Next() { err = row.Scan(&version)
if err == sql.ErrNoRows {
dbase.upgrader.logCallback("initiate version", dbase.DbName) dbase.upgrader.logCallback("initiate version", dbase.DbName)
_, err = dbase.db.Exec(`INSERT INTO _db.schema(version) VALUES(0)`) err = dbase.appendSchemaVersion(0)
if err != nil {
return
}
} }
return return
@ -94,6 +111,7 @@ func (dbase Database) version() (version int, err error) {// {{{
return return
}// }}} }// }}}
// AddDatabase sets a database up for the Run() function with verifying/creating the _db.schema table.
func (upgrader Upgrader) AddDatabase(host string, port int, dbName, user, pass string) (err error) {// {{{ func (upgrader Upgrader) AddDatabase(host string, port int, dbName, user, pass string) (err error) {// {{{
var db Database var db Database
if db, err = newDatabase(host, port, dbName, user, pass); err != nil { if db, err = newDatabase(host, port, dbName, user, pass); err != nil {
@ -110,6 +128,7 @@ func (upgrader Upgrader) AddDatabase(host string, port int, dbName, user, pass s
err = db.verifySchemaEntry() err = db.verifySchemaEntry()
return return
}// }}} }// }}}
// Run executes the actual schema updates until there are no more available.
func (upgrader Upgrader) Run() (err error) {// {{{ func (upgrader Upgrader) Run() (err error) {// {{{
var version int var version int
@ -131,11 +150,7 @@ func (upgrader Upgrader) Run() (err error) {// {{{
if _, err = dbase.db.Exec(string(sql)); err != nil { if _, err = dbase.db.Exec(string(sql)); err != nil {
return return
} }
_, err = dbase.db.Exec(` if err = dbase.appendSchemaVersion(version); err != nil {
INSERT INTO _db.schema(version)
VALUES($1)
`, version)
if err != nil {
return return
} }
} }