go-vise

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

gdbm.go (3185B)


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