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 }