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 }