go-scopes/storage/storage.go

140 lines
2.8 KiB
Go

package storage
import (
"database/sql"
"fmt"
"os"
lib "git.sr.ht/~cco/go-scopes"
"git.sr.ht/~cco/go-scopes/logging/log"
)
type Cfg struct {
Driver, Connstr, Schema string
}
type Storage struct {
*sql.DB
*Cfg
Schema string
Params lib.StrMap
Errors []error
}
func Start(cfg *Cfg) lib.Proc {
return func(ctx lib.Context) {
ctx.WithState(Open(cfg))
}
}
type Rows = sql.Rows
type rowsProc = func(*sql.Rows) error
type scn[T any] interface{ Scan(*Rows) (T, error) }
type scp interface{ ScanP(*Rows) error }
type scpcon[T any] interface {
*T
scp
}
func Open(cfg *Cfg) *Storage {
db, err := sql.Open(cfg.Driver, cfg.Connstr)
if err != nil {
log.Error(err).Msg("storage.Open")
return nil
}
storage := Storage{
DB: db,
Cfg: cfg,
Schema: cfg.Schema,
Params: map[string]string{},
}
return &storage
}
func QueryData[T scn[T]](db *Storage, q string, args ...interface{}) []T {
var data []T
var rec T
proc := func(r *sql.Rows) error {
rec, err := rec.Scan(r)
data = append(data, rec)
return err
}
db.Query(proc, q, args...) // ?? check error
return data
}
func QueryDataP[P scpcon[T], T any](db *Storage, q string, args ...interface{}) []T {
var data []T
rp := P(new(T))
proc := func(r *sql.Rows) error {
err := rp.ScanP(r)
data = append(data, *rp)
return err
}
db.Query(proc, q, args...) // ?? check error
return data
}
func QueryCol[T any](db *Storage, q string, args ...interface{}) []T {
var data []T
var field T
proc := func(r *sql.Rows) error {
err := r.Scan(&field)
data = append(data, field)
return err
}
db.Query(proc, q, args...) // ?? check error
return data
}
func (db *Storage) Query(process rowsProc, q string, args ...interface{}) error {
info := "storage.Query"
//log.Debug().Str("query", q).Msg(info)
rows, err := db.DB.Query(q, args...)
if err != nil {
return db.LogErr(err, info, q)
}
for rows.Next() {
if err := process(rows); err != nil {
return db.LogErr(err, info, q)
}
}
if err := rows.Err(); err != nil {
return db.LogErr(err, info, q)
}
return nil
}
func (db *Storage) Exec(q string, args ...interface{}) (int64, error) {
info := "storage.Exec"
//log.Debug().Str("query", q).Msg(info)
res, err := db.DB.Exec(q, args...)
if err != nil {
db.LogErr(err, info, q)
return 0, err
}
nrows, _ := res.RowsAffected()
return nrows, nil
}
func (db *Storage) DropTable(tn string) error {
sql_drop := `drop table if exists %s`
_, err := db.Exec(fmt.Sprintf(sql_drop, tn))
return err
}
func (db *Storage) RunScript(path string) error {
b, err := os.ReadFile(path)
if err != nil {
return db.LogErr(err, "storage.RunScript", path)
}
_, err = db.Exec(string(b))
return err
}
func (db *Storage) LogErr(err error, info, inp string) error {
log.Error(err).Str("input", inp).Msg(info)
db.Errors = append(db.Errors, err)
return err
}