commit 326bdb5018ae81ee493371b56bd4564a8b35da59
parent 654cd518130db0a8ff3dd8f18504257bb4a37dd6
Author: lash <dev@holbrook.no>
Date: Fri, 30 Aug 2024 17:58:10 +0100
Add documentation for db
Diffstat:
9 files changed, 87 insertions(+), 57 deletions(-)
diff --git a/db/db.go b/db/db.go
@@ -15,13 +15,23 @@ const (
DATATYPE_USERSTART
)
+// Db abstracts all data storage and retrieval as a key-value store
type Db interface {
+ // Connect prepares the storage backend for use
Connect(ctx context.Context, connStr string) error
+ // Close implements io.Closer
Close() error
+ // Get retrieves the value belonging to a key. Errors if the key does not exist, or if the retrieval otherwise fails.
Get(ctx context.Context, key []byte) ([]byte, error)
+ // Put stores a value under a key. Any existing value will be replaced. Errors if the value could not be stored.
Put(ctx context.Context, key []byte, val []byte) error
}
+// ToDbKey generates a key to use Db to store a value for a particular context.
+//
+// If language is nil, then default language storage context will be used.
+//
+// If language is not nil, and the context does not support language, the language value will silently will be ignored.
func ToDbKey(typ uint8, b []byte, l *lang.Language) []byte {
k := []byte{typ}
if l != nil && l.Code != "" {
@@ -31,19 +41,23 @@ func ToDbKey(typ uint8, b []byte, l *lang.Language) []byte {
return append(k, b...)
}
+// BaseDb is a base class for all Db implementations.
type BaseDb struct {
pfx uint8
sid []byte
}
+// SetPrefix sets the storage context prefix to use for consecutive Get and Put operations.
func(db *BaseDb) SetPrefix(pfx uint8) {
db.pfx = pfx
}
+// SetSession sets the session context to use for consecutive Get and Put operations.
func(db *BaseDb) SetSession(sessionId string) {
db.sid = append([]byte(sessionId), 0x2E)
}
+// ToKey creates a DbKey within the current session context.
func(db *BaseDb) ToKey(key []byte) ([]byte, error) {
if db.pfx == DATATYPE_UNKNOWN {
return nil, errors.New("datatype prefix cannot be UNKNOWN")
diff --git a/db/fs.go b/db/fs.go
@@ -8,11 +8,13 @@ import (
"path"
)
+// FsDb is a pure filesystem backend implementation if the Db interface.
type FsDb struct {
BaseDb
dir string
}
+// Connect implements Db
func(fdb *FsDb) Connect(ctx context.Context, connStr string) error {
fi, err := os.Stat(connStr)
if err != nil {
@@ -25,6 +27,7 @@ func(fdb *FsDb) Connect(ctx context.Context, connStr string) error {
return nil
}
+// Get implements Db
func(fdb *FsDb) Get(ctx context.Context, key []byte) ([]byte, error) {
fp, err := fdb.pathFor(key)
if err != nil {
@@ -42,6 +45,7 @@ func(fdb *FsDb) Get(ctx context.Context, key []byte) ([]byte, error) {
return b, nil
}
+// Put implements Db
func(fdb *FsDb) Put(ctx context.Context, key []byte, val []byte) error {
fp, err := fdb.pathFor(key)
if err != nil {
@@ -50,10 +54,12 @@ func(fdb *FsDb) Put(ctx context.Context, key []byte, val []byte) error {
return ioutil.WriteFile(fp, val, 0600)
}
+// Close implements Db
func(fdb *FsDb) Close() error {
return nil
}
-
+
+// create a key safe for the filesystem
func(fdb *FsDb) pathFor(key []byte) (string, error) {
kb, err := fdb.ToKey(key)
if err != nil {
diff --git a/db/fs_test.go b/db/fs_test.go
@@ -8,6 +8,7 @@ import (
)
func TestPutGetFs(t *testing.T) {
+ var dbi Db
ctx := context.Background()
sid := "ses"
d, err := ioutil.TempDir("", "vise-db-*")
@@ -17,6 +18,10 @@ func TestPutGetFs(t *testing.T) {
db := &FsDb{}
db.SetPrefix(DATATYPE_USERSTART)
db.SetSession(sid)
+
+ dbi = db
+ _ = dbi
+
err = db.Connect(ctx, d)
if err != nil {
t.Fatal(err)
diff --git a/db/gdbm.go b/db/gdbm.go
@@ -7,12 +7,14 @@ import (
gdbm "github.com/graygnuorg/go-gdbm"
)
+// GdbmDb is a gdbm backend implementation of the Db interface.
type GdbmDb struct {
BaseDb
conn *gdbm.Database
prefix uint8
}
+// Connect implements Db
func(gdb *GdbmDb) Connect(ctx context.Context, connStr string) error {
db, err := gdbm.Open(connStr, gdbm.ModeWrcreat)
if err != nil {
@@ -22,6 +24,7 @@ func(gdb *GdbmDb) Connect(ctx context.Context, connStr string) error {
return nil
}
+// Put implements Db
func(gdb *GdbmDb) Put(ctx context.Context, key []byte, val []byte) error {
k, err := gdb.ToKey(key)
if err != nil {
@@ -30,6 +33,7 @@ func(gdb *GdbmDb) Put(ctx context.Context, key []byte, val []byte) error {
return gdb.conn.Store(k, val, true)
}
+// Get implements Db
func(gdb *GdbmDb) Get(ctx context.Context, key []byte) ([]byte, error) {
k, err := gdb.ToKey(key)
if err != nil {
@@ -45,6 +49,7 @@ func(gdb *GdbmDb) Get(ctx context.Context, key []byte) ([]byte, error) {
return v, nil
}
+// Close implements Db
func(gdb *GdbmDb) Close() error {
return gdb.Close()
}
diff --git a/db/gdbm_test.go b/db/gdbm_test.go
@@ -8,6 +8,7 @@ import (
)
func TestPutGetGdbm(t *testing.T) {
+ var dbi Db
ctx := context.Background()
sid := "ses"
f, err := ioutil.TempFile("", "vise-db-*")
@@ -17,6 +18,10 @@ func TestPutGetGdbm(t *testing.T) {
db := &GdbmDb{}
db.SetPrefix(DATATYPE_USERSTART)
db.SetSession(sid)
+
+ dbi = db
+ _ = dbi
+
err = db.Connect(ctx, f.Name())
if err != nil {
t.Fatal(err)
diff --git a/db/mem.go b/db/mem.go
@@ -5,21 +5,25 @@ import (
"fmt"
)
+// MemDb is a memory backend implementation of the Db interface.
type MemDb struct {
BaseDb
store map[string][]byte
}
+// Connect implements Db
func(mdb *MemDb) Connect(ctx context.Context, connStr string) error {
mdb.store = make(map[string][]byte)
return nil
}
+// convert to a supported map key type
func(mdb *MemDb) toHexKey(key []byte) (string, error) {
k, err := mdb.ToKey(key)
return fmt.Sprintf("%x", k), err
}
+// Get implements Db
func(mdb *MemDb) Get(ctx context.Context, key []byte) ([]byte, error) {
k, err := mdb.toHexKey(key)
if err != nil {
@@ -32,6 +36,7 @@ func(mdb *MemDb) Get(ctx context.Context, key []byte) ([]byte, error) {
return v, nil
}
+// Put implements Db
func(mdb *MemDb) Put(ctx context.Context, key []byte, val []byte) error {
k, err := mdb.toHexKey(key)
if err != nil {
@@ -41,6 +46,7 @@ func(mdb *MemDb) Put(ctx context.Context, key []byte, val []byte) error {
return nil
}
+// Close implements Db
func(mdb *MemDb) Close() error {
return nil
}
diff --git a/db/mem_test.go b/db/mem_test.go
@@ -7,11 +7,16 @@ import (
)
func TestPutGetMem(t *testing.T) {
+ var dbi Db
ctx := context.Background()
sid := "ses"
db := &MemDb{}
db.SetPrefix(DATATYPE_USERSTART)
db.SetSession(sid)
+
+ dbi = db
+ _ = dbi
+
err := db.Connect(ctx, "")
if err != nil {
t.Fatal(err)
diff --git a/db/pg.go b/db/pg.go
@@ -7,6 +7,7 @@ import (
"github.com/jackc/pgx/v5/pgxpool"
)
+// PgDb is a Postgresql backend implementation of the Db interface.
type PgDb struct {
BaseDb
conn *pgxpool.Pool
@@ -14,17 +15,20 @@ type PgDb struct {
prefix uint8
}
+// NewPgDb creates a new PgDb reference.
func NewPgDb() *PgDb {
return &PgDb{
schema: "public",
}
}
+// WithSchema sets the Postgres schema to use for the storage table.
func(pdb *PgDb) WithSchema(schema string) *PgDb {
pdb.schema = schema
return pdb
}
+// Connect implements Db.
func(pdb *PgDb) Connect(ctx context.Context, connStr string) error {
var err error
conn, err := pgxpool.New(ctx, connStr)
@@ -35,61 +39,7 @@ func(pdb *PgDb) Connect(ctx context.Context, connStr string) error {
return pdb.prepare(ctx)
}
-func(pdb *PgDb) prepare(ctx context.Context) error {
- tx, err := pdb.conn.Begin(ctx)
- if err != nil {
- tx.Rollback(ctx)
- return err
- }
-// query := fmt.Sprintf(`CREATE TABLE IF NOT EXISTS %s.kv_vise_domain (
-// id SERIAL PRIMARY KEY,
-// name VARCHAR(256) NOT NULL
-// );
-//`, pdb.schema)
-// _, err = tx.Exec(ctx, query)
-// if err != nil {
-// tx.Rollback(ctx)
-// return err
-// }
-//
-// query = fmt.Sprintf(`CREATE TABLE IF NOT EXISTS %s.kv_vise (
-// id SERIAL NOT NULL,
-// domain_id INT NOT NULL,
-// key VARCHAR(256) NOT NULL,
-// value BYTEA NOT NULL,
-// constraint fk_domain
-// FOREIGN KEY (domain_id)
-// REFERENCES %s.kv_vise_domain(id)
-// );
-//`, pdb.schema, pdb.schema)
-// _, err = tx.Exec(ctx, query)
-// if err != nil {
-// tx.Rollback(ctx)
-// return err
-// }
-
- query := fmt.Sprintf(`CREATE TABLE IF NOT EXISTS %s.kv_vise (
- id SERIAL NOT NULL,
- key BYTEA NOT NULL UNIQUE,
- value BYTEA NOT NULL
- );
-`, pdb.schema)
- _, err = tx.Exec(ctx, query)
- if err != nil {
- tx.Rollback(ctx)
- return err
- }
-
- err = tx.Commit(ctx)
- if err != nil {
- //if !errors.Is(pgx.ErrTxCommitRollback) {
- tx.Rollback(ctx)
- return err
- //}
- }
- return nil
-}
-
+// Put implements Db.
func(pdb *PgDb) Put(ctx context.Context, key []byte, val []byte) error {
k, err := pdb.ToKey(key)
if err != nil {
@@ -109,6 +59,7 @@ func(pdb *PgDb) Put(ctx context.Context, key []byte, val []byte) error {
return nil
}
+// Get implements Db.
func(pdb *PgDb) Get(ctx context.Context, key []byte) ([]byte, error) {
k, err := pdb.ToKey(key)
if err != nil {
@@ -133,7 +84,35 @@ func(pdb *PgDb) Get(ctx context.Context, key []byte) ([]byte, error) {
return b, nil
}
+// Close implements Db.
func(pdb *PgDb) Close() error {
pdb.Close()
return nil
}
+
+// set up table
+func(pdb *PgDb) prepare(ctx context.Context) error {
+ tx, err := pdb.conn.Begin(ctx)
+ if err != nil {
+ tx.Rollback(ctx)
+ return err
+ }
+ query := fmt.Sprintf(`CREATE TABLE IF NOT EXISTS %s.kv_vise (
+ id SERIAL NOT NULL,
+ key BYTEA NOT NULL UNIQUE,
+ value BYTEA NOT NULL
+ );
+`, pdb.schema)
+ _, err = tx.Exec(ctx, query)
+ if err != nil {
+ tx.Rollback(ctx)
+ return err
+ }
+
+ err = tx.Commit(ctx)
+ if err != nil {
+ tx.Rollback(ctx)
+ return err
+ }
+ return nil
+}
diff --git a/db/pg_test.go b/db/pg_test.go
@@ -7,12 +7,17 @@ import (
)
func TestPutGetPg(t *testing.T) {
- t.Skip("need postgresql mock")
+ var dbi Db
ses := "xyzzy"
db := NewPgDb().WithSchema("vvise")
db.SetPrefix(DATATYPE_USERSTART)
db.SetSession(ses)
ctx := context.Background()
+
+ dbi = db
+ _ = dbi
+
+ t.Skip("need postgresql mock")
err := db.Connect(ctx, "postgres://vise:esiv@localhost:5432/visedb")
if err != nil {
t.Fatal(err)