Initial commit
This commit is contained in:
commit
f80de360ba
31
database.go
Normal file
31
database.go
Normal file
@ -0,0 +1,31 @@
|
||||
package dbschema
|
||||
|
||||
import (
|
||||
// Standard
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func newDatabase(host string, port int, dbName, user, pass string) (dbase Database, err error) {// {{{
|
||||
dbase.Host = host
|
||||
dbase.Port = port
|
||||
dbase.DbName = dbName
|
||||
dbase.Username = user
|
||||
dbase.Password = pass
|
||||
|
||||
dbase.db, err = sql.Open("postgres", dbase.sqlConnString())
|
||||
return
|
||||
}// }}}
|
||||
|
||||
func (dbase Database) sqlConnString() string {// {{{
|
||||
return fmt.Sprintf(
|
||||
"postgresql://%s:%s@%s:%d/%s?sslmode=disable",
|
||||
dbase.Username,
|
||||
dbase.Password,
|
||||
dbase.Host,
|
||||
dbase.Port,
|
||||
dbase.DbName,
|
||||
)
|
||||
}// }}}
|
||||
|
||||
// vim: foldmethod=marker
|
5
go.mod
Normal file
5
go.mod
Normal file
@ -0,0 +1,5 @@
|
||||
module git.gibonuddevalla.se/go/dbschema
|
||||
|
||||
go 1.20
|
||||
|
||||
require github.com/lib/pq v1.10.9
|
2
go.sum
Normal file
2
go.sum
Normal file
@ -0,0 +1,2 @@
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
71
schema.go
Normal file
71
schema.go
Normal file
@ -0,0 +1,71 @@
|
||||
package dbschema
|
||||
|
||||
import (
|
||||
// External
|
||||
_ "github.com/lib/pq"
|
||||
|
||||
// Standard
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
type Upgrader struct {
|
||||
schemaDb Database
|
||||
databases map[string]Database
|
||||
logCallback func(string, string)
|
||||
sqlCallback func(string, int) ([]byte, bool)
|
||||
}
|
||||
|
||||
type Database struct {
|
||||
Host string
|
||||
Port int
|
||||
DbName string
|
||||
Username string
|
||||
Password string
|
||||
|
||||
db *sql.DB
|
||||
schemaDb *Database
|
||||
}
|
||||
|
||||
/*
|
||||
func dbUpdate() (err error) {// {{{
|
||||
var rows *sqlx.Rows
|
||||
var schemaStr string
|
||||
var schema int
|
||||
rows, err = db.Queryx(`SELECT value FROM _internal.db WHERE "key"='schema'`)
|
||||
if err != nil { return }
|
||||
defer rows.Close()
|
||||
|
||||
if !rows.Next() {
|
||||
return errors.New("Table _interval.db missing schema row")
|
||||
}
|
||||
|
||||
if err = rows.Scan(&schemaStr); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Run updates
|
||||
schema, err = strconv.Atoi(schemaStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := (schema+1); i <= DB_SCHEMA; i++ {
|
||||
log.Printf("\x1b[32mNotes\x1b[0m Upgrading SQL schema to revision %d...", i)
|
||||
sql, _ := embedded.ReadFile(
|
||||
fmt.Sprintf("sql/%04d.sql", i),
|
||||
)
|
||||
_, err = db.Exec(string(sql))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = db.Exec(`UPDATE _internal.db SET "value"=$1 WHERE "key"='schema'`, i)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
log.Printf("\x1b[32mNotes\x1b[0m OK: %d", i)
|
||||
}
|
||||
|
||||
return
|
||||
}// }}}
|
||||
*/
|
||||
|
||||
// vim: foldmethod=marker
|
154
upgrader.go
Normal file
154
upgrader.go
Normal file
@ -0,0 +1,154 @@
|
||||
package dbschema
|
||||
|
||||
import (
|
||||
// Standard
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func defaultCallback(topic, msg string) {// {{{
|
||||
fmt.Printf("[%s] %s\n", topic, msg)
|
||||
}// }}}
|
||||
func NewUpgrader(host string, port int, dbName, user, pass string) (upgrader Upgrader, err error) {// {{{
|
||||
upgrader.logCallback = defaultCallback
|
||||
upgrader.databases = map[string]Database{}
|
||||
upgrader.schemaDb, err = newDatabase(
|
||||
host,
|
||||
port,
|
||||
dbName,
|
||||
user,
|
||||
pass,
|
||||
)
|
||||
err = upgrader.verifySchemaTable()
|
||||
return
|
||||
}// }}}
|
||||
|
||||
func (upgrader *Upgrader) SetLogCallback(callback func(string, string)) {// {{{
|
||||
upgrader.logCallback = callback
|
||||
}// }}}
|
||||
func (upgrader *Upgrader) SetSqlCallback(callback func(string, int) ([]byte, bool)) {// {{{
|
||||
upgrader.sqlCallback = callback
|
||||
}// }}}
|
||||
func (upgrader Upgrader) verifySchemaTable() (err error) {// {{{
|
||||
var rows *sql.Rows
|
||||
if rows, err = upgrader.schemaDb.db.Query(
|
||||
`SELECT EXISTS (
|
||||
SELECT FROM pg_catalog.pg_class c
|
||||
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
||||
WHERE n.nspname = '_db'
|
||||
AND c.relname = 'schema'
|
||||
)`,
|
||||
); err != nil {
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
var exists bool
|
||||
rows.Next()
|
||||
if err = rows.Scan(&exists); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !exists {
|
||||
upgrader.logCallback("create", "_db.schema")
|
||||
upgrader.schemaDb.db.Exec(`CREATE SCHEMA "_db"`)
|
||||
|
||||
if _, err = upgrader.schemaDb.db.Exec(`
|
||||
CREATE TABLE "_db"."schema" (
|
||||
database varchar NOT NULL,
|
||||
version int4 NOT NULL,
|
||||
updated timestamp NOT NULL DEFAULT NOW(),
|
||||
|
||||
CONSTRAINT schema_pk PRIMARY KEY (database)
|
||||
);
|
||||
`,
|
||||
); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}// }}}
|
||||
func (upgrader Upgrader) verifySchemaEntry(dbase Database) (err error) {// {{{
|
||||
var rows *sql.Rows
|
||||
rows, err = upgrader.schemaDb.db.Query(`SELECT version FROM _db.schema WHERE database=$1`, dbase.DbName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
if !rows.Next() {
|
||||
upgrader.logCallback("initiate version", dbase.DbName)
|
||||
_, err = upgrader.schemaDb.db.Exec(`INSERT INTO _db.schema(database, version) VALUES($1, 0)`, dbase.DbName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}// }}}
|
||||
func (upgrader Upgrader) version(dbName string) (version int, err error) {// {{{
|
||||
var rows *sql.Rows
|
||||
rows, err = upgrader.schemaDb.db.Query(
|
||||
`SELECT version FROM _db.schema WHERE database=$1`,
|
||||
dbName,
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
if rows.Next() {
|
||||
err = rows.Scan(&version)
|
||||
} else {
|
||||
err = fmt.Errorf(`Database "%s" is missing an entry in _db.schema`, dbName)
|
||||
}
|
||||
return
|
||||
}// }}}
|
||||
|
||||
func (upgrader Upgrader) AddDatabase(host string, port int, dbName, user, pass string) (err error) {// {{{
|
||||
var db Database
|
||||
if db, err = newDatabase(host, port, dbName, user, pass); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
upgrader.databases[dbName] = db
|
||||
|
||||
err = upgrader.verifySchemaEntry(db)
|
||||
return
|
||||
}// }}}
|
||||
func (upgrader Upgrader) Run() (err error) {// {{{
|
||||
var version int
|
||||
|
||||
for dbName, db := range upgrader.databases {
|
||||
version, err = upgrader.version(dbName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
upgrader.logCallback("version", fmt.Sprintf("%s: %d", dbName, version))
|
||||
|
||||
for {
|
||||
version++
|
||||
sql, found := upgrader.sqlCallback(dbName, version)
|
||||
if !found {
|
||||
break
|
||||
}
|
||||
|
||||
upgrader.logCallback("exec", fmt.Sprintf("%s: %d", dbName, version))
|
||||
if _, err = db.db.Exec(string(sql)); err != nil {
|
||||
return
|
||||
}
|
||||
_, err = upgrader.schemaDb.db.Exec(`
|
||||
UPDATE _db.schema
|
||||
SET
|
||||
version=$1,
|
||||
updated=NOW()
|
||||
WHERE database=$2
|
||||
`, version, dbName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}// }}}
|
||||
|
||||
// vim: foldmethod=marker
|
Loading…
Reference in New Issue
Block a user