go-vise

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

gdbm.go (3268B)


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