github.com/voedger/voedger@v0.0.0-20240520144910-273e84102129/pkg/istorage/bbolt/impl.go (about) 1 /* 2 * Copyright (c) 2022-present Sigma-Soft, Ltd. 3 * @author: Dmitry Molchanovsky 4 * @author: Maxim Geraskin (refactoring) 5 */ 6 7 package bbolt 8 9 import ( 10 "bytes" 11 "context" 12 "os" 13 "path/filepath" 14 15 bolt "go.etcd.io/bbolt" 16 17 "github.com/voedger/voedger/pkg/istorage" 18 coreutils "github.com/voedger/voedger/pkg/utils" 19 ) 20 21 type appStorageFactory struct { 22 bboltParams ParamsType 23 } 24 25 func (p *appStorageFactory) AppStorage(appName istorage.SafeAppName) (s istorage.IAppStorage, err error) { 26 dbName := filepath.Join(p.bboltParams.DBDir, appName.String()+".db") 27 exists, err := coreutils.Exists(dbName) 28 if err != nil { 29 // notest 30 return nil, err 31 } 32 if !exists { 33 return nil, istorage.ErrStorageDoesNotExist 34 } 35 db, err := bolt.Open(dbName, coreutils.FileMode_rw_rw_rw_, bolt.DefaultOptions) 36 if err != nil { 37 // notest 38 return nil, err 39 } 40 return &appStorageType{db}, nil 41 } 42 43 func (p *appStorageFactory) Init(appName istorage.SafeAppName) error { 44 dbName := filepath.Join(p.bboltParams.DBDir, appName.String()+".db") 45 exists, err := coreutils.Exists(dbName) 46 if err != nil { 47 // notest 48 return err 49 } 50 if exists { 51 return istorage.ErrStorageAlreadyExists 52 } 53 if err = os.MkdirAll(p.bboltParams.DBDir, coreutils.FileMode_rwxrwxrwx); err != nil { 54 // notest 55 return err 56 } 57 db, err := bolt.Open(dbName, coreutils.FileMode_rw_rw_rw_, bolt.DefaultOptions) 58 if err != nil { 59 // notest 60 return err 61 } 62 return db.Close() 63 } 64 65 // bolt cannot use empty keys so we declare nullKey 66 var nullKey = []byte{0} 67 68 // if the key is empty or equal to nil, then convert it to nullKey 69 func safeKey(value []byte) []byte { 70 if len(value) == 0 { 71 return nullKey 72 } 73 return value 74 } 75 76 // if the key is nullKey, then convert it to nil 77 func unSafeKey(value []byte) []byte { 78 if len(value) == 0 || (len(value) == 1 && value[0] == 0) { 79 return nil 80 } 81 return value 82 } 83 84 // implemetation for istorage.IAppStorage. 85 type appStorageType struct { 86 db *bolt.DB 87 } 88 89 // istorage.IAppStorage.Put(pKey []byte, cCols []byte, value []byte) (err error) 90 func (s *appStorageType) Put(pKey []byte, cCols []byte, value []byte) (err error) { 91 err = s.db.Update(func(tx *bolt.Tx) error { 92 b, e := tx.CreateBucketIfNotExists(pKey) 93 if e != nil { 94 // notest 95 return e 96 } 97 return b.Put(safeKey(cCols), unSafeKey(value)) 98 }) 99 return err 100 } 101 102 // istorage.IAppStorage.PutBatch(items []BatchItem) (err error) 103 func (s *appStorageType) PutBatch(items []istorage.BatchItem) (err error) { 104 err = s.db.Update(func(tx *bolt.Tx) error { 105 106 for i := 0; i < len(items); i++ { 107 108 PKey := items[i].PKey 109 b, e := tx.CreateBucketIfNotExists(PKey) 110 if e != nil { 111 // notest 112 return e 113 } 114 115 e = b.Put(safeKey(items[i].CCols), items[i].Value) 116 if e != nil { 117 return e 118 } 119 } 120 121 return nil 122 }) 123 124 return err 125 } 126 127 // istorage.IAppStorage.Get(pKey []byte, cCols []byte, data *[]byte) (ok bool, err error) 128 func (s *appStorageType) Get(pKey []byte, cCols []byte, data *[]byte) (ok bool, err error) { 129 *data = (*data)[0:0] 130 131 err = s.db.View(func(tx *bolt.Tx) error { 132 ok = false 133 bucket := tx.Bucket(pKey) 134 if bucket == nil { 135 return nil 136 } 137 138 v := bucket.Get(safeKey(cCols)) 139 if v == nil { 140 return nil 141 } 142 *data = append(*data, v...) 143 ok = true 144 return nil 145 }) 146 147 return ok, err 148 } 149 150 // istorage.IAppStorage.Read(ctx context.Context, pKey []byte, startCCols []byte, finishCCols []byte, cb ReadCallback) (err error) 151 func (s *appStorageType) Read(ctx context.Context, pKey []byte, startCCols, finishCCols []byte, cb istorage.ReadCallback) (err error) { 152 153 if (len(startCCols) > 0) && (len(finishCCols) > 0) && (bytes.Compare(startCCols, finishCCols) >= 0) { 154 return nil // absurd range 155 } 156 157 err = s.db.View(func(tx *bolt.Tx) error { 158 159 startCCols = unSafeKey(startCCols) 160 finishCCols = unSafeKey(finishCCols) 161 162 bucket := tx.Bucket(pKey) 163 if bucket == nil { 164 return nil 165 } 166 167 var ( 168 k []byte 169 v []byte 170 ) 171 172 cr := bucket.Cursor() 173 if startCCols == nil { 174 k, v = cr.First() 175 } else { 176 k, v = cr.Seek(startCCols) 177 } 178 179 var e error 180 181 for (k != nil) && (finishCCols == nil || string(k) <= string(finishCCols)) { 182 183 if ctx.Err() != nil { 184 return nil 185 } 186 187 if cb != nil { 188 e = cb(unSafeKey(k), unSafeKey(v)) 189 if e != nil { 190 return e 191 } 192 } 193 k, v = cr.Next() 194 } 195 196 return nil 197 }) 198 199 return err 200 } 201 202 // istorage.IAppStorage.GetBatch(pKey []byte, items []GetBatchItem) (err error) 203 func (s *appStorageType) GetBatch(pKey []byte, items []istorage.GetBatchItem) (err error) { 204 err = s.db.View(func(tx *bolt.Tx) error { 205 206 bucket := tx.Bucket(pKey) 207 if bucket == nil { 208 for i := 0; i < len(items); i++ { 209 items[i].Ok = false 210 } 211 return nil 212 } 213 for i := 0; i < len(items); i++ { 214 v := bucket.Get(safeKey(items[i].CCols)) 215 items[i].Ok = v != nil 216 *items[i].Data = append((*items[i].Data)[0:0], v...) 217 } 218 return nil 219 }) 220 221 return err 222 }