go-vise

Constrained Size Output Virtual Machine
Info | Log | Files | Refs | README | LICENSE

gdbm.go (2997B)


      1 package gdbm
      2 
      3 import (
      4 	"context"
      5 	"errors"
      6 	"fmt"
      7 	"os"
      8 
      9 	gdbm "github.com/graygnuorg/go-gdbm"
     10 
     11 	"git.defalsify.org/vise.git/db"
     12 )
     13 
     14 // gdbmDb is a gdbm backend implementation of the Db interface.
     15 type gdbmDb struct {
     16 	*db.DbBase
     17 	conn *gdbm.Database
     18 	readOnly bool
     19 	prefix uint8
     20 }
     21 
     22 // Creates a new gdbm backed Db implementation.
     23 func NewGdbmDb() *gdbmDb {
     24 	db := &gdbmDb{
     25 		DbBase: db.NewDbBase(),
     26 	}
     27 	return db
     28 }
     29 
     30 // WithReadOnly sets database as read only.
     31 //
     32 // There may exist more than one instance of read-only
     33 // databases to the same file at the same time.
     34 // However, only one single write database.
     35 //
     36 // Readonly cannot be set when creating a new database.
     37 func(gdb *gdbmDb) WithReadOnly() *gdbmDb {
     38 	gdb.readOnly = true
     39 	return gdb
     40 }
     41 
     42 // String implements the string interface.
     43 func(gdb *gdbmDb) String() string {
     44 	fn, err := gdb.conn.FileName()
     45 	if err != nil {
     46 		fn = "??"
     47 	}
     48 	return "gdbmdb: " + fn
     49 }
     50 
     51 // Connect implements Db
     52 func(gdb *gdbmDb) Connect(ctx context.Context, connStr string) error {
     53 	if gdb.conn != nil {
     54 		logg.WarnCtxf(ctx, "already connected", "conn", gdb.conn)
     55 		return nil
     56 	}
     57 	var db *gdbm.Database
     58 	cfg := gdbm.DatabaseConfig{
     59 		FileName: connStr,
     60 		Flags: gdbm.OF_NOLOCK | gdbm.OF_PREREAD,
     61 		FileMode: 0600,
     62 	}
     63 
     64 	_, err := os.Stat(connStr)
     65 	if err != nil {
     66 		if !errors.Is(err.(*os.PathError).Unwrap(), os.ErrNotExist) {
     67 			return fmt.Errorf("db path lookup err: %v", err)
     68 		}
     69 		if gdb.readOnly {
     70 			return fmt.Errorf("cannot open new database readonly")
     71 		}
     72 		cfg.Mode = gdbm.ModeReader | gdbm.ModeWrcreat
     73 	} else {
     74 		cfg.Mode = gdbm.ModeReader
     75 		if !gdb.readOnly {
     76 			cfg.Mode |= gdbm.ModeWriter
     77 		}
     78 	}
     79 	db, err = gdbm.OpenConfig(cfg)
     80 	if err != nil {
     81 		return fmt.Errorf("db open err: %v", err)
     82 	}
     83 	logg.DebugCtxf(ctx, "gdbm connected", "connstr", connStr)
     84 	gdb.conn = db
     85 	return nil
     86 }
     87 
     88 // Put implements Db
     89 func(gdb *gdbmDb) Put(ctx context.Context, key []byte, val []byte) error {
     90 	if !gdb.CheckPut() {
     91 		return errors.New("unsafe put and safety set")
     92 	}
     93 	lk, err := gdb.ToKey(ctx, key)
     94 	if err != nil {
     95 		return err
     96 	}
     97 	logg.TraceCtxf(ctx, "gdbm put", "key", key, "lk", lk, "val", val)
     98 	if lk.Translation != nil {
     99 		return gdb.conn.Store(lk.Translation, val, true)
    100 	}
    101 	return gdb.conn.Store(lk.Default, val, true)
    102 }
    103 
    104 // Get implements Db
    105 func(gdb *gdbmDb) Get(ctx context.Context, key []byte) ([]byte, error) {
    106 	var v []byte
    107 	lk, err := gdb.ToKey(ctx, key)
    108 	if err != nil {
    109 		return nil, err
    110 	}
    111 	if lk.Translation != nil {
    112 		v, err = gdb.conn.Fetch(lk.Translation)
    113 		if err != nil {
    114 			if !errors.Is(gdbm.ErrItemNotFound, err) {
    115 				return nil, err
    116 			}
    117 		}
    118 		return v, nil
    119 	}
    120 	v, err = gdb.conn.Fetch(lk.Default)
    121 	if err != nil {
    122 		if errors.Is(gdbm.ErrItemNotFound, err) {
    123 			return nil, db.NewErrNotFound(key)
    124 		}
    125 		return nil, err
    126 	}
    127 	logg.TraceCtxf(ctx, "gdbm get", "key", key, "lk", lk, "val", v)
    128 	return v, nil
    129 }
    130 
    131 // Close implements Db
    132 func(gdb *gdbmDb) Close() error {
    133 	logg.Tracef("closing gdbm", "path", gdb.conn)
    134 	return gdb.conn.Close()
    135 }