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
|