2021-10-21 03:19:52 -07:00
|
|
|
package orm
|
|
|
|
|
|
|
|
import (
|
2021-11-09 10:01:27 -08:00
|
|
|
"bytes"
|
2021-10-21 03:19:52 -07:00
|
|
|
"reflect"
|
|
|
|
|
|
|
|
"github.com/cosmos/cosmos-sdk/codec"
|
|
|
|
"github.com/cosmos/cosmos-sdk/store/prefix"
|
2021-11-09 10:01:27 -08:00
|
|
|
"github.com/cosmos/cosmos-sdk/store/types"
|
2021-10-21 03:19:52 -07:00
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
2021-11-09 10:01:27 -08:00
|
|
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
|
|
|
"github.com/cosmos/cosmos-sdk/x/group/errors"
|
2021-10-21 03:19:52 -07:00
|
|
|
)
|
|
|
|
|
2021-11-16 08:08:21 -08:00
|
|
|
var (
|
|
|
|
_ Indexable = &table{}
|
|
|
|
_ TableExportable = &table{}
|
|
|
|
)
|
2021-10-21 03:19:52 -07:00
|
|
|
|
|
|
|
// table is the high level object to storage mapper functionality. Persistent
|
|
|
|
// entities are stored by an unique identifier called `RowID`. The table struct
|
|
|
|
// does not:
|
|
|
|
// - enforce uniqueness of the `RowID`
|
|
|
|
// - enforce prefix uniqueness of keys, i.e. not allowing one key to be a prefix
|
|
|
|
// of another
|
|
|
|
// - optimize Gas usage conditions
|
|
|
|
// The caller must ensure that these things are handled. The table struct is
|
|
|
|
// private, so that we only have custom tables built on top of table, that do satisfy
|
|
|
|
// these requirements.
|
|
|
|
type table struct {
|
|
|
|
model reflect.Type
|
|
|
|
prefix [2]byte
|
|
|
|
afterSet []AfterSetInterceptor
|
|
|
|
afterDelete []AfterDeleteInterceptor
|
|
|
|
cdc codec.Codec
|
|
|
|
}
|
|
|
|
|
|
|
|
// newTable creates a new table
|
|
|
|
func newTable(prefix [2]byte, model codec.ProtoMarshaler, cdc codec.Codec) (*table, error) {
|
|
|
|
if model == nil {
|
2021-11-09 10:01:27 -08:00
|
|
|
return nil, errors.ErrORMInvalidArgument.Wrap("Model must not be nil")
|
2021-10-21 03:19:52 -07:00
|
|
|
}
|
|
|
|
tp := reflect.TypeOf(model)
|
|
|
|
if tp.Kind() == reflect.Ptr {
|
|
|
|
tp = tp.Elem()
|
|
|
|
}
|
|
|
|
return &table{
|
|
|
|
prefix: prefix,
|
|
|
|
model: tp,
|
|
|
|
cdc: cdc,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// RowGetter returns a type safe RowGetter.
|
|
|
|
func (a table) RowGetter() RowGetter {
|
|
|
|
return NewTypeSafeRowGetter(a.prefix, a.model, a.cdc)
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddAfterSetInterceptor can be used to register a callback function that is executed after an object is created and/or updated.
|
|
|
|
func (a *table) AddAfterSetInterceptor(interceptor AfterSetInterceptor) {
|
|
|
|
a.afterSet = append(a.afterSet, interceptor)
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddAfterDeleteInterceptor can be used to register a callback function that is executed after an object is deleted.
|
|
|
|
func (a *table) AddAfterDeleteInterceptor(interceptor AfterDeleteInterceptor) {
|
|
|
|
a.afterDelete = append(a.afterDelete, interceptor)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create persists the given object under the rowID key, returning an
|
|
|
|
// errors.ErrORMUniqueConstraint if a value already exists at that key.
|
|
|
|
//
|
|
|
|
// Create iterates through the registered callbacks that may add secondary index
|
|
|
|
// keys.
|
|
|
|
func (a table) Create(store sdk.KVStore, rowID RowID, obj codec.ProtoMarshaler) error {
|
|
|
|
if a.Has(store, rowID) {
|
|
|
|
return errors.ErrORMUniqueConstraint
|
|
|
|
}
|
|
|
|
|
|
|
|
return a.Set(store, rowID, obj)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update updates the given object under the rowID key. It expects the key to
|
2021-11-09 10:01:27 -08:00
|
|
|
// exists already and fails with an `sdkerrors.ErrNotFound` otherwise. Any caller must
|
2021-10-21 03:19:52 -07:00
|
|
|
// therefore make sure that this contract is fulfilled. Parameters must not be
|
|
|
|
// nil.
|
|
|
|
//
|
|
|
|
// Update triggers all "after set" hooks that may add or remove secondary index keys.
|
|
|
|
func (a table) Update(store sdk.KVStore, rowID RowID, newValue codec.ProtoMarshaler) error {
|
|
|
|
if !a.Has(store, rowID) {
|
2021-11-09 10:01:27 -08:00
|
|
|
return sdkerrors.ErrNotFound
|
2021-10-21 03:19:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return a.Set(store, rowID, newValue)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set persists the given object under the rowID key. It does not check if the
|
|
|
|
// key already exists and overwrites the value if it does.
|
|
|
|
//
|
|
|
|
// Set iterates through the registered callbacks that may add secondary index
|
|
|
|
// keys.
|
|
|
|
func (a table) Set(store sdk.KVStore, rowID RowID, newValue codec.ProtoMarshaler) error {
|
|
|
|
if len(rowID) == 0 {
|
|
|
|
return errors.ErrORMEmptyKey
|
|
|
|
}
|
|
|
|
if err := assertCorrectType(a.model, newValue); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := assertValid(newValue); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
pStore := prefix.NewStore(store, a.prefix[:])
|
|
|
|
|
|
|
|
var oldValue codec.ProtoMarshaler
|
|
|
|
if a.Has(store, rowID) {
|
|
|
|
oldValue = reflect.New(a.model).Interface().(codec.ProtoMarshaler)
|
|
|
|
a.GetOne(store, rowID, oldValue)
|
|
|
|
}
|
|
|
|
|
|
|
|
newValueEncoded, err := a.cdc.Marshal(newValue)
|
|
|
|
if err != nil {
|
2021-11-09 10:01:27 -08:00
|
|
|
return sdkerrors.Wrapf(err, "failed to serialize %T", newValue)
|
2021-10-21 03:19:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pStore.Set(rowID, newValueEncoded)
|
|
|
|
for i, itc := range a.afterSet {
|
|
|
|
if err := itc(store, rowID, newValue, oldValue); err != nil {
|
2021-11-09 10:01:27 -08:00
|
|
|
return sdkerrors.Wrapf(err, "interceptor %d failed", i)
|
2021-10-21 03:19:52 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func assertValid(obj codec.ProtoMarshaler) error {
|
|
|
|
if v, ok := obj.(Validateable); ok {
|
|
|
|
if err := v.ValidateBasic(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete removes the object under the rowID key. It expects the key to exists
|
2021-11-09 10:01:27 -08:00
|
|
|
// already and fails with a `sdkerrors.ErrNotFound` otherwise. Any caller must therefore
|
2021-10-21 03:19:52 -07:00
|
|
|
// make sure that this contract is fulfilled.
|
|
|
|
//
|
|
|
|
// Delete iterates through the registered callbacks that remove secondary index
|
|
|
|
// keys.
|
|
|
|
func (a table) Delete(store sdk.KVStore, rowID RowID) error {
|
|
|
|
pStore := prefix.NewStore(store, a.prefix[:])
|
|
|
|
|
|
|
|
var oldValue = reflect.New(a.model).Interface().(codec.ProtoMarshaler)
|
|
|
|
if err := a.GetOne(store, rowID, oldValue); err != nil {
|
2021-11-09 10:01:27 -08:00
|
|
|
return sdkerrors.Wrap(err, "load old value")
|
2021-10-21 03:19:52 -07:00
|
|
|
}
|
|
|
|
pStore.Delete(rowID)
|
|
|
|
|
|
|
|
for i, itc := range a.afterDelete {
|
|
|
|
if err := itc(store, rowID, oldValue); err != nil {
|
2021-11-09 10:01:27 -08:00
|
|
|
return sdkerrors.Wrapf(err, "delete interceptor %d failed", i)
|
2021-10-21 03:19:52 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Has checks if a key exists. Returns false when the key is empty or nil
|
|
|
|
// because we don't allow creation of values without a key.
|
|
|
|
func (a table) Has(store sdk.KVStore, key RowID) bool {
|
|
|
|
if len(key) == 0 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
pStore := prefix.NewStore(store, a.prefix[:])
|
|
|
|
it := pStore.Iterator(PrefixRange(key))
|
|
|
|
defer it.Close()
|
|
|
|
return it.Valid()
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetOne load the object persisted for the given RowID into the dest parameter.
|
2021-11-09 10:01:27 -08:00
|
|
|
// If none exists or `rowID==nil` then `sdkerrors.ErrNotFound` is returned instead.
|
2021-10-21 03:19:52 -07:00
|
|
|
// Parameters must not be nil - we don't allow creation of values with empty keys.
|
|
|
|
func (a table) GetOne(store sdk.KVStore, rowID RowID, dest codec.ProtoMarshaler) error {
|
|
|
|
if len(rowID) == 0 {
|
2021-11-09 10:01:27 -08:00
|
|
|
return sdkerrors.ErrNotFound
|
2021-10-21 03:19:52 -07:00
|
|
|
}
|
|
|
|
x := NewTypeSafeRowGetter(a.prefix, a.model, a.cdc)
|
|
|
|
return x(store, rowID, dest)
|
|
|
|
}
|
2021-11-09 10:01:27 -08:00
|
|
|
|
|
|
|
// PrefixScan returns an Iterator over a domain of keys in ascending order. End is exclusive.
|
|
|
|
// Start is an MultiKeyIndex key or prefix. It must be less than end, or the Iterator is invalid.
|
|
|
|
// Iterator must be closed by caller.
|
|
|
|
// To iterate over entire domain, use PrefixScan(nil, nil)
|
|
|
|
//
|
|
|
|
// WARNING: The use of a PrefixScan can be very expensive in terms of Gas. Please make sure you do not expose
|
|
|
|
// this as an endpoint to the public without further limits.
|
|
|
|
// Example:
|
|
|
|
// it, err := idx.PrefixScan(ctx, start, end)
|
|
|
|
// if err !=nil {
|
|
|
|
// return err
|
|
|
|
// }
|
|
|
|
// const defaultLimit = 20
|
|
|
|
// it = LimitIterator(it, defaultLimit)
|
|
|
|
//
|
|
|
|
// CONTRACT: No writes may happen within a domain while an iterator exists over it.
|
|
|
|
func (a table) PrefixScan(store sdk.KVStore, start, end RowID) (Iterator, error) {
|
|
|
|
if start != nil && end != nil && bytes.Compare(start, end) >= 0 {
|
|
|
|
return NewInvalidIterator(), sdkerrors.Wrap(errors.ErrORMInvalidArgument, "start must be before end")
|
|
|
|
}
|
|
|
|
pStore := prefix.NewStore(store, a.prefix[:])
|
|
|
|
return &typeSafeIterator{
|
|
|
|
store: store,
|
|
|
|
rowGetter: NewTypeSafeRowGetter(a.prefix, a.model, a.cdc),
|
|
|
|
it: pStore.Iterator(start, end),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ReversePrefixScan returns an Iterator over a domain of keys in descending order. End is exclusive.
|
|
|
|
// Start is an MultiKeyIndex key or prefix. It must be less than end, or the Iterator is invalid and error is returned.
|
|
|
|
// Iterator must be closed by caller.
|
|
|
|
// To iterate over entire domain, use PrefixScan(nil, nil)
|
|
|
|
//
|
|
|
|
// WARNING: The use of a ReversePrefixScan can be very expensive in terms of Gas. Please make sure you do not expose
|
|
|
|
// this as an endpoint to the public without further limits. See `LimitIterator`
|
|
|
|
//
|
|
|
|
// CONTRACT: No writes may happen within a domain while an iterator exists over it.
|
|
|
|
func (a table) ReversePrefixScan(store sdk.KVStore, start, end RowID) (Iterator, error) {
|
|
|
|
if start != nil && end != nil && bytes.Compare(start, end) >= 0 {
|
|
|
|
return NewInvalidIterator(), sdkerrors.Wrap(errors.ErrORMInvalidArgument, "start must be before end")
|
|
|
|
}
|
|
|
|
pStore := prefix.NewStore(store, a.prefix[:])
|
|
|
|
return &typeSafeIterator{
|
|
|
|
store: store,
|
|
|
|
rowGetter: NewTypeSafeRowGetter(a.prefix, a.model, a.cdc),
|
|
|
|
it: pStore.ReverseIterator(start, end),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2021-11-16 08:08:21 -08:00
|
|
|
// Export stores all the values in the table in the passed ModelSlicePtr.
|
|
|
|
func (a table) Export(store sdk.KVStore, dest ModelSlicePtr) (uint64, error) {
|
|
|
|
it, err := a.PrefixScan(store, nil, nil)
|
|
|
|
if err != nil {
|
|
|
|
return 0, sdkerrors.Wrap(err, "table Export failure when exporting table data")
|
|
|
|
}
|
|
|
|
_, err = ReadAll(it, dest)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Import clears the table and initializes it from the given data interface{}.
|
|
|
|
// data should be a slice of structs that implement PrimaryKeyed.
|
|
|
|
func (a table) Import(store sdk.KVStore, data interface{}, _ uint64) error {
|
|
|
|
// Clear all data
|
|
|
|
pStore := prefix.NewStore(store, a.prefix[:])
|
|
|
|
it := pStore.Iterator(nil, nil)
|
|
|
|
defer it.Close()
|
|
|
|
for ; it.Valid(); it.Next() {
|
|
|
|
if err := a.Delete(store, it.Key()); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Provided data must be a slice
|
|
|
|
modelSlice := reflect.ValueOf(data)
|
|
|
|
if modelSlice.Kind() != reflect.Slice {
|
|
|
|
return sdkerrors.Wrap(errors.ErrORMInvalidArgument, "data must be a slice")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Import values from slice
|
|
|
|
for i := 0; i < modelSlice.Len(); i++ {
|
|
|
|
obj, ok := modelSlice.Index(i).Interface().(PrimaryKeyed)
|
|
|
|
if !ok {
|
|
|
|
return sdkerrors.Wrapf(errors.ErrORMInvalidArgument, "unsupported type :%s", reflect.TypeOf(data).Elem().Elem())
|
|
|
|
}
|
|
|
|
err := a.Create(store, PrimaryKey(obj), obj)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-11-09 10:01:27 -08:00
|
|
|
// typeSafeIterator is initialized with a type safe RowGetter only.
|
|
|
|
type typeSafeIterator struct {
|
|
|
|
store sdk.KVStore
|
|
|
|
rowGetter RowGetter
|
|
|
|
it types.Iterator
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i typeSafeIterator) LoadNext(dest codec.ProtoMarshaler) (RowID, error) {
|
|
|
|
if !i.it.Valid() {
|
|
|
|
return nil, errors.ErrORMIteratorDone
|
|
|
|
}
|
|
|
|
rowID := i.it.Key()
|
|
|
|
i.it.Next()
|
|
|
|
return rowID, i.rowGetter(i.store, rowID, dest)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i typeSafeIterator) Close() error {
|
|
|
|
i.it.Close()
|
|
|
|
return nil
|
|
|
|
}
|