Notes/db.go

151 lines
2.9 KiB
Go
Raw Permalink Normal View History

2023-06-15 07:24:23 +02:00
package main
import (
// External
"github.com/jmoiron/sqlx"
_ "github.com/lib/pq"
// Standard
2023-06-18 22:05:10 +02:00
"errors"
2023-06-15 07:24:23 +02:00
"fmt"
2023-12-28 08:31:30 +01:00
"io/fs"
"regexp"
2023-06-18 22:05:10 +02:00
"strconv"
2023-06-15 07:24:23 +02:00
)
var (
dbConn string
db *sqlx.DB
)
2023-12-28 08:31:30 +01:00
func dbInit() (err error) { // {{{
2023-06-15 07:24:23 +02:00
dbConn = fmt.Sprintf(
"host=%s port=%d user=%s password=%s dbname=%s sslmode=disable",
config.Database.Host,
config.Database.Port,
config.Database.Username,
config.Database.Password,
config.Database.Name,
)
2023-12-28 09:09:56 +01:00
logger.Info("db", "op", "connect", "host", config.Database.Host, "port", config.Database.Port)
2023-06-15 07:24:23 +02:00
2023-06-18 22:25:34 +02:00
if db, err = sqlx.Connect("postgres", dbConn); err != nil {
return
}
if err = dbVerifyInternals(); err != nil {
return
}
err = dbUpdate()
2023-06-18 22:05:10 +02:00
return
2023-12-28 08:31:30 +01:00
} // }}}
func dbVerifyInternals() (err error) { // {{{
2023-06-18 22:25:34 +02:00
var rows *sqlx.Rows
if rows, err = db.Queryx(
`SELECT EXISTS (
SELECT FROM pg_catalog.pg_class c
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE n.nspname = '_internal'
AND c.relname = 'db'
)`,
); err != nil {
return
}
defer rows.Close()
var exists bool
rows.Next()
if err = rows.Scan(&exists); err != nil {
return
}
if !exists {
2023-12-28 09:09:56 +01:00
logger.Info("db", "op", "create_db", "db", "_internal.db")
2023-06-18 22:25:34 +02:00
if _, err = db.Exec(`
CREATE SCHEMA "_internal";
CREATE TABLE "_internal".db (
"key" varchar NOT NULL,
value varchar NULL,
CONSTRAINT db_pk PRIMARY KEY (key)
);
2023-06-18 22:05:10 +02:00
2023-06-18 22:25:34 +02:00
INSERT INTO _internal.db("key", "value")
VALUES('schema', '0');
`,
); err != nil {
return
}
}
return
2023-12-28 08:31:30 +01:00
} // }}}
func dbUpdate() (err error) { // {{{
2023-06-18 22:05:10 +02:00
/* Current schema revision is read from database.
* Used to iterate through the embedded SQL updates
2023-12-28 08:31:30 +01:00
* up to the db schema version currently compiled
2023-06-18 22:05:10 +02:00
* program is made for. */
var rows *sqlx.Rows
var schemaStr string
var schema int
rows, err = db.Queryx(`SELECT value FROM _internal.db WHERE "key"='schema'`)
2023-12-28 08:31:30 +01:00
if err != nil {
return
}
2023-06-18 22:05:10 +02:00
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)
2023-06-15 07:24:23 +02:00
if err != nil {
2023-06-18 22:05:10 +02:00
return err
2023-06-15 07:24:23 +02:00
}
2023-12-28 08:31:30 +01:00
sqlSchemaVersion := sqlSchema()
for i := (schema + 1); i <= sqlSchemaVersion; i++ {
2023-12-28 09:09:56 +01:00
logger.Info("db", "op", "upgrade_schema", "schema", i)
2023-06-18 22:05:10 +02:00
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
}
2023-12-28 09:09:56 +01:00
logger.Info("db", "op", "upgrade_schema", "schema", i, "result", "ok")
2023-06-18 22:05:10 +02:00
}
return
2023-12-28 08:31:30 +01:00
} // }}}
func sqlSchema() (max int) { // {{{
var num int
files, _ := fs.ReadDir(embedded, "sql")
sqlFilename := regexp.MustCompile(`^([0-9]+)\.sql$`)
for _, file := range files {
fname := sqlFilename.FindStringSubmatch(file.Name())
if len(fname) != 2 {
continue
}
num, _ = strconv.Atoi(fname[1])
}
if num > max {
max = num
}
return
} // }}}
2023-06-15 07:24:23 +02:00
// vim: foldmethod=marker