github.com/cosmos/cosmos-sdk@v0.50.10/x/group/internal/orm/table.go (about) 1 package orm 2 3 import ( 4 "bytes" 5 "reflect" 6 7 "github.com/cosmos/gogoproto/proto" 8 9 errorsmod "cosmossdk.io/errors" 10 "cosmossdk.io/store/prefix" 11 "cosmossdk.io/store/types" 12 13 "github.com/cosmos/cosmos-sdk/codec" 14 sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 15 "github.com/cosmos/cosmos-sdk/x/group/errors" 16 ) 17 18 var ( 19 _ Indexable = &table{} 20 _ TableExportable = &table{} 21 ) 22 23 // table is the high level object to storage mapper functionality. Persistent 24 // entities are stored by an unique identifier called `RowID`. The table struct 25 // does not: 26 // - enforce uniqueness of the `RowID` 27 // - enforce prefix uniqueness of keys, i.e. not allowing one key to be a prefix 28 // of another 29 // - optimize Gas usage conditions 30 // The caller must ensure that these things are handled. The table struct is 31 // private, so that we only have custom tables built on top of table, that do satisfy 32 // these requirements. 33 type table struct { 34 model reflect.Type 35 prefix [2]byte 36 afterSet []AfterSetInterceptor 37 afterDelete []AfterDeleteInterceptor 38 cdc codec.Codec 39 } 40 41 // newTable creates a new table 42 func newTable(prefix [2]byte, model proto.Message, cdc codec.Codec) (*table, error) { 43 if model == nil { 44 return nil, errors.ErrORMInvalidArgument.Wrap("Model must not be nil") 45 } 46 tp := reflect.TypeOf(model) 47 if tp.Kind() == reflect.Ptr { 48 tp = tp.Elem() 49 } 50 return &table{ 51 prefix: prefix, 52 model: tp, 53 cdc: cdc, 54 }, nil 55 } 56 57 // RowGetter returns a type safe RowGetter. 58 func (a table) RowGetter() RowGetter { 59 return NewTypeSafeRowGetter(a.prefix, a.model, a.cdc) 60 } 61 62 // AddAfterSetInterceptor can be used to register a callback function that is executed after an object is created and/or updated. 63 func (a *table) AddAfterSetInterceptor(interceptor AfterSetInterceptor) { 64 a.afterSet = append(a.afterSet, interceptor) 65 } 66 67 // AddAfterDeleteInterceptor can be used to register a callback function that is executed after an object is deleted. 68 func (a *table) AddAfterDeleteInterceptor(interceptor AfterDeleteInterceptor) { 69 a.afterDelete = append(a.afterDelete, interceptor) 70 } 71 72 // Create persists the given object under the rowID key, returning an 73 // errors.ErrORMUniqueConstraint if a value already exists at that key. 74 // 75 // Create iterates through the registered callbacks that may add secondary index 76 // keys. 77 func (a table) Create(store types.KVStore, rowID RowID, obj proto.Message) error { 78 if a.Has(store, rowID) { 79 return errors.ErrORMUniqueConstraint 80 } 81 82 return a.Set(store, rowID, obj) 83 } 84 85 // Update updates the given object under the rowID key. It expects the key to 86 // exists already and fails with an `sdkerrors.ErrNotFound` otherwise. Any caller must 87 // therefore make sure that this contract is fulfilled. Parameters must not be 88 // nil. 89 // 90 // Update triggers all "after set" hooks that may add or remove secondary index keys. 91 func (a table) Update(store types.KVStore, rowID RowID, newValue proto.Message) error { 92 if !a.Has(store, rowID) { 93 return sdkerrors.ErrNotFound 94 } 95 96 return a.Set(store, rowID, newValue) 97 } 98 99 // Set persists the given object under the rowID key. It does not check if the 100 // key already exists and overwrites the value if it does. 101 // 102 // Set iterates through the registered callbacks that may add secondary index 103 // keys. 104 func (a table) Set(store types.KVStore, rowID RowID, newValue proto.Message) error { 105 if len(rowID) == 0 { 106 return errors.ErrORMEmptyKey 107 } 108 if err := assertCorrectType(a.model, newValue); err != nil { 109 return err 110 } 111 if err := assertValid(newValue); err != nil { 112 return err 113 } 114 115 pStore := prefix.NewStore(store, a.prefix[:]) 116 117 var oldValue proto.Message 118 if a.Has(store, rowID) { 119 oldValue = reflect.New(a.model).Interface().(proto.Message) 120 a.GetOne(store, rowID, oldValue) 121 } 122 123 newValueEncoded, err := a.cdc.Marshal(newValue) 124 if err != nil { 125 return errorsmod.Wrapf(err, "failed to serialize %T", newValue) 126 } 127 128 pStore.Set(rowID, newValueEncoded) 129 for i, itc := range a.afterSet { 130 if err := itc(store, rowID, newValue, oldValue); err != nil { 131 return errorsmod.Wrapf(err, "interceptor %d failed", i) 132 } 133 } 134 return nil 135 } 136 137 func assertValid(obj proto.Message) error { 138 if v, ok := obj.(Validateable); ok { 139 if err := v.ValidateBasic(); err != nil { 140 return err 141 } 142 } 143 return nil 144 } 145 146 // Delete removes the object under the rowID key. It expects the key to exists 147 // already and fails with a `sdkerrors.ErrNotFound` otherwise. Any caller must therefore 148 // make sure that this contract is fulfilled. 149 // 150 // Delete iterates through the registered callbacks that remove secondary index 151 // keys. 152 func (a table) Delete(store types.KVStore, rowID RowID) error { 153 pStore := prefix.NewStore(store, a.prefix[:]) 154 155 oldValue := reflect.New(a.model).Interface().(proto.Message) 156 if err := a.GetOne(store, rowID, oldValue); err != nil { 157 return errorsmod.Wrap(err, "load old value") 158 } 159 pStore.Delete(rowID) 160 161 for i, itc := range a.afterDelete { 162 if err := itc(store, rowID, oldValue); err != nil { 163 return errorsmod.Wrapf(err, "delete interceptor %d failed", i) 164 } 165 } 166 return nil 167 } 168 169 // Has checks if a key exists. Returns false when the key is empty or nil 170 // because we don't allow creation of values without a key. 171 func (a table) Has(store types.KVStore, key RowID) bool { 172 if len(key) == 0 { 173 return false 174 } 175 pStore := prefix.NewStore(store, a.prefix[:]) 176 return pStore.Has(key) 177 } 178 179 // GetOne load the object persisted for the given RowID into the dest parameter. 180 // If none exists or `rowID==nil` then `sdkerrors.ErrNotFound` is returned instead. 181 // Parameters must not be nil - we don't allow creation of values with empty keys. 182 func (a table) GetOne(store types.KVStore, rowID RowID, dest proto.Message) error { 183 if len(rowID) == 0 { 184 return sdkerrors.ErrNotFound 185 } 186 x := NewTypeSafeRowGetter(a.prefix, a.model, a.cdc) 187 return x(store, rowID, dest) 188 } 189 190 // PrefixScan returns an Iterator over a domain of keys in ascending order. End is exclusive. 191 // Start is an MultiKeyIndex key or prefix. It must be less than end, or the Iterator is invalid. 192 // Iterator must be closed by caller. 193 // To iterate over entire domain, use PrefixScan(nil, nil) 194 // 195 // WARNING: The use of a PrefixScan can be very expensive in terms of Gas. Please make sure you do not expose 196 // this as an endpoint to the public without further limits. 197 // Example: 198 // 199 // it, err := idx.PrefixScan(ctx, start, end) 200 // if err !=nil { 201 // return err 202 // } 203 // const defaultLimit = 20 204 // it = LimitIterator(it, defaultLimit) 205 // 206 // CONTRACT: No writes may happen within a domain while an iterator exists over it. 207 func (a table) PrefixScan(store types.KVStore, start, end RowID) (Iterator, error) { 208 if start != nil && end != nil && bytes.Compare(start, end) >= 0 { 209 return NewInvalidIterator(), errorsmod.Wrap(errors.ErrORMInvalidArgument, "start must be before end") 210 } 211 pStore := prefix.NewStore(store, a.prefix[:]) 212 return &typeSafeIterator{ 213 store: store, 214 rowGetter: NewTypeSafeRowGetter(a.prefix, a.model, a.cdc), 215 it: pStore.Iterator(start, end), 216 }, nil 217 } 218 219 // ReversePrefixScan returns an Iterator over a domain of keys in descending order. End is exclusive. 220 // Start is an MultiKeyIndex key or prefix. It must be less than end, or the Iterator is invalid and error is returned. 221 // Iterator must be closed by caller. 222 // To iterate over entire domain, use PrefixScan(nil, nil) 223 // 224 // WARNING: The use of a ReversePrefixScan can be very expensive in terms of Gas. Please make sure you do not expose 225 // this as an endpoint to the public without further limits. See `LimitIterator` 226 // 227 // CONTRACT: No writes may happen within a domain while an iterator exists over it. 228 func (a table) ReversePrefixScan(store types.KVStore, start, end RowID) (Iterator, error) { 229 if start != nil && end != nil && bytes.Compare(start, end) >= 0 { 230 return NewInvalidIterator(), errorsmod.Wrap(errors.ErrORMInvalidArgument, "start must be before end") 231 } 232 pStore := prefix.NewStore(store, a.prefix[:]) 233 return &typeSafeIterator{ 234 store: store, 235 rowGetter: NewTypeSafeRowGetter(a.prefix, a.model, a.cdc), 236 it: pStore.ReverseIterator(start, end), 237 }, nil 238 } 239 240 // Export stores all the values in the table in the passed ModelSlicePtr. 241 func (a table) Export(store types.KVStore, dest ModelSlicePtr) (uint64, error) { 242 it, err := a.PrefixScan(store, nil, nil) 243 if err != nil { 244 return 0, errorsmod.Wrap(err, "table Export failure when exporting table data") 245 } 246 _, err = ReadAll(it, dest) 247 if err != nil { 248 return 0, err 249 } 250 return 0, nil 251 } 252 253 // Import clears the table and initializes it from the given data interface{}. 254 // data should be a slice of structs that implement PrimaryKeyed. 255 func (a table) Import(store types.KVStore, data interface{}, _ uint64) error { 256 // Clear all data 257 keys := a.keys(store) 258 for _, key := range keys { 259 if err := a.Delete(store, key); err != nil { 260 return err 261 } 262 } 263 264 // Provided data must be a slice 265 modelSlice := reflect.ValueOf(data) 266 if modelSlice.Kind() != reflect.Slice { 267 return errorsmod.Wrap(errors.ErrORMInvalidArgument, "data must be a slice") 268 } 269 270 // Import values from slice 271 for i := 0; i < modelSlice.Len(); i++ { 272 obj, ok := modelSlice.Index(i).Interface().(PrimaryKeyed) 273 if !ok { 274 return errorsmod.Wrapf(errors.ErrORMInvalidArgument, "unsupported type :%s", reflect.TypeOf(data).Elem().Elem()) 275 } 276 err := a.Create(store, PrimaryKey(obj), obj) 277 if err != nil { 278 return err 279 } 280 } 281 282 return nil 283 } 284 285 func (a table) keys(store types.KVStore) [][]byte { 286 pStore := prefix.NewStore(store, a.prefix[:]) 287 it := pStore.Iterator(nil, nil) 288 defer it.Close() 289 290 var keys [][]byte 291 for ; it.Valid(); it.Next() { 292 keys = append(keys, it.Key()) 293 } 294 return keys 295 } 296 297 // typeSafeIterator is initialized with a type safe RowGetter only. 298 type typeSafeIterator struct { 299 store types.KVStore 300 rowGetter RowGetter 301 it types.Iterator 302 } 303 304 func (i typeSafeIterator) LoadNext(dest proto.Message) (RowID, error) { 305 if !i.it.Valid() { 306 return nil, errors.ErrORMIteratorDone 307 } 308 rowID := i.it.Key() 309 i.it.Next() 310 return rowID, i.rowGetter(i.store, rowID, dest) 311 } 312 313 func (i typeSafeIterator) Close() error { 314 return i.it.Close() 315 }