package sql import ( "database/sql" "fmt" "os" "git.sr.ht/~cco/go-scopes/logging/log" ) type Cfg struct { Driver, Connstr, Schema string } type Storage struct { *sql.DB *Cfg Errors []error } 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("sql.Open") return nil } return &Storage{DB: db, Cfg: cfg} } 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 := "sql.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 := "sql.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 { _, 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, "sql.Storage.RunScript", path) } _, err = db.Exec(string(b)) return err } func (db *Storage) logErr(err error, info, q string) error { log.Error(err).Str("query", q).Msg(info) db.Errors = append(db.Errors, err) return err } const ( sql_drop = `drop table %s` )