-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.go
298 lines (266 loc) · 9.67 KB
/
index.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
package orm
import (
"bytes"
"github.com/blockgenx/blockgen-sdk/codec"
"github.com/blockgenx/blockgen-sdk/store/prefix"
"github.com/blockgenx/blockgen-sdk/store/types"
sdk "github.com/blockgenx/blockgen-sdk/types"
sdkerrors "github.com/blockgenx/blockgen-sdk/types/errors"
"github.com/blockgenx/blockgen-sdk/types/query"
"github.com/blockgenx/blockgen-sdk/x/group/errors"
)
// indexer creates and modifies the second MultiKeyIndex based on the operations and changes on the primary object.
type indexer interface {
OnCreate(store sdk.KVStore, rowID RowID, value interface{}) error
OnDelete(store sdk.KVStore, rowID RowID, value interface{}) error
OnUpdate(store sdk.KVStore, rowID RowID, newValue, oldValue interface{}) error
}
var _ Index = &MultiKeyIndex{}
// MultiKeyIndex is an index where multiple entries can point to the same underlying object as opposite to a unique index
// where only one entry is allowed.
type MultiKeyIndex struct {
prefix byte
rowGetter RowGetter
indexer indexer
indexerFunc IndexerFunc
indexKey interface{}
}
// NewIndex builds a MultiKeyIndex.
// Only single-field indexes are supported and `indexKey` represents such a field value,
// which can be []byte, string or uint64.
func NewIndex(tb Indexable, prefix byte, indexerF IndexerFunc, indexKey interface{}) (MultiKeyIndex, error) {
indexer, err := NewIndexer(indexerF)
if err != nil {
return MultiKeyIndex{}, err
}
return newIndex(tb, prefix, indexer, indexer.IndexerFunc(), indexKey)
}
func newIndex(tb Indexable, prefix byte, indexer *Indexer, indexerF IndexerFunc, indexKey interface{}) (MultiKeyIndex, error) {
rowGetter := tb.RowGetter()
if rowGetter == nil {
return MultiKeyIndex{}, errors.ErrORMInvalidArgument.Wrap("rowGetter must not be nil")
}
if indexKey == nil {
return MultiKeyIndex{}, errors.ErrORMInvalidArgument.Wrap("indexKey must not be nil")
}
// Verify indexKey type is bytes, string or uint64
switch indexKey.(type) {
case []byte, string, uint64:
default:
return MultiKeyIndex{}, errors.ErrORMInvalidArgument.Wrap("indexKey must be []byte, string or uint64")
}
idx := MultiKeyIndex{
prefix: prefix,
rowGetter: rowGetter,
indexer: indexer,
indexerFunc: indexerF,
indexKey: indexKey,
}
tb.AddAfterSetInterceptor(idx.onSet)
tb.AddAfterDeleteInterceptor(idx.onDelete)
return idx, nil
}
// Has checks if a key exists. Returns an error on nil key.
func (i MultiKeyIndex) Has(store sdk.KVStore, key interface{}) (bool, error) {
pStore := prefix.NewStore(store, []byte{i.prefix})
encodedKey, err := keyPartBytes(key, false)
if err != nil {
return false, err
}
it := pStore.Iterator(PrefixRange(encodedKey))
defer it.Close()
return it.Valid(), nil
}
// Get returns a result iterator for the searchKey. Parameters must not be nil.
func (i MultiKeyIndex) Get(store sdk.KVStore, searchKey interface{}) (Iterator, error) {
pStore := prefix.NewStore(store, []byte{i.prefix})
encodedKey, err := keyPartBytes(searchKey, false)
if err != nil {
return nil, err
}
it := pStore.Iterator(PrefixRange(encodedKey))
return indexIterator{store: store, it: it, rowGetter: i.rowGetter, indexKey: i.indexKey}, nil
}
// GetPaginated creates an iterator for the searchKey
// starting from pageRequest.Key if provided.
// The pageRequest.Key is the rowID while searchKey is a MultiKeyIndex key.
func (i MultiKeyIndex) GetPaginated(store sdk.KVStore, searchKey interface{}, pageRequest *query.PageRequest) (Iterator, error) {
pStore := prefix.NewStore(store, []byte{i.prefix})
encodedKey, err := keyPartBytes(searchKey, false)
if err != nil {
return nil, err
}
start, end := PrefixRange(encodedKey)
if pageRequest != nil && len(pageRequest.Key) != 0 {
var err error
start, err = buildKeyFromParts([]interface{}{searchKey, pageRequest.Key})
if err != nil {
return nil, err
}
}
it := pStore.Iterator(start, end)
return indexIterator{store: store, it: it, rowGetter: i.rowGetter, indexKey: i.indexKey}, nil
}
// 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 and error is returned.
// 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 (i MultiKeyIndex) PrefixScan(store sdk.KVStore, startI interface{}, endI interface{}) (Iterator, error) {
start, end, err := getStartEndBz(startI, endI)
if err != nil {
return nil, err
}
pStore := prefix.NewStore(store, []byte{i.prefix})
it := pStore.Iterator(start, end)
return indexIterator{store: store, it: it, rowGetter: i.rowGetter, indexKey: i.indexKey}, 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 (i MultiKeyIndex) ReversePrefixScan(store sdk.KVStore, startI interface{}, endI interface{}) (Iterator, error) {
start, end, err := getStartEndBz(startI, endI)
if err != nil {
return nil, err
}
pStore := prefix.NewStore(store, []byte{i.prefix})
it := pStore.ReverseIterator(start, end)
return indexIterator{store: store, it: it, rowGetter: i.rowGetter, indexKey: i.indexKey}, nil
}
// getStartEndBz gets the start and end bytes to be passed into the SDK store
// iterator.
func getStartEndBz(startI interface{}, endI interface{}) ([]byte, []byte, error) {
start, err := getPrefixScanKeyBytes(startI)
if err != nil {
return nil, nil, err
}
end, err := getPrefixScanKeyBytes(endI)
if err != nil {
return nil, nil, err
}
if start != nil && end != nil && bytes.Compare(start, end) >= 0 {
return nil, nil, sdkerrors.Wrap(errors.ErrORMInvalidArgument, "start must be less than end")
}
return start, end, nil
}
func getPrefixScanKeyBytes(keyI interface{}) ([]byte, error) {
var (
key []byte
err error
)
// nil value are accepted in the context of PrefixScans
if keyI == nil {
return nil, nil
}
key, err = keyPartBytes(keyI, false)
if err != nil {
return nil, err
}
return key, nil
}
func (i MultiKeyIndex) onSet(store sdk.KVStore, rowID RowID, newValue, oldValue codec.ProtoMarshaler) error {
pStore := prefix.NewStore(store, []byte{i.prefix})
if oldValue == nil {
return i.indexer.OnCreate(pStore, rowID, newValue)
}
return i.indexer.OnUpdate(pStore, rowID, newValue, oldValue)
}
func (i MultiKeyIndex) onDelete(store sdk.KVStore, rowID RowID, oldValue codec.ProtoMarshaler) error {
pStore := prefix.NewStore(store, []byte{i.prefix})
return i.indexer.OnDelete(pStore, rowID, oldValue)
}
type UniqueIndex struct {
MultiKeyIndex
}
// NewUniqueIndex create a new Index object where duplicate keys are prohibited.
func NewUniqueIndex(tb Indexable, prefix byte, uniqueIndexerFunc UniqueIndexerFunc, indexKey interface{}) (UniqueIndex, error) {
uniqueIndexer, err := NewUniqueIndexer(uniqueIndexerFunc)
if err != nil {
return UniqueIndex{}, err
}
multiKeyIndex, err := newIndex(tb, prefix, uniqueIndexer, uniqueIndexer.IndexerFunc(), indexKey)
if err != nil {
return UniqueIndex{}, err
}
return UniqueIndex{
MultiKeyIndex: multiKeyIndex,
}, nil
}
// indexIterator uses rowGetter to lazy load new model values on request.
type indexIterator struct {
store sdk.KVStore
rowGetter RowGetter
it types.Iterator
indexKey interface{}
}
// LoadNext loads the next value in the sequence into the pointer passed as dest and returns the key. If there
// are no more items the errors.ErrORMIteratorDone error is returned
// The key is the rowID and not any MultiKeyIndex key.
func (i indexIterator) LoadNext(dest codec.ProtoMarshaler) (RowID, error) {
if !i.it.Valid() {
return nil, errors.ErrORMIteratorDone
}
indexPrefixKey := i.it.Key()
rowID, err := stripRowID(indexPrefixKey, i.indexKey)
if err != nil {
return nil, err
}
i.it.Next()
return rowID, i.rowGetter(i.store, rowID, dest)
}
// Close releases the iterator and should be called at the end of iteration
func (i indexIterator) Close() error {
i.it.Close()
return nil
}
// PrefixRange turns a prefix into a (start, end) range. The start is the given prefix value and
// the end is calculated by adding 1 bit to the start value. Nil is not allowed as prefix.
//
// Example: []byte{1, 3, 4} becomes []byte{1, 3, 5}
// []byte{15, 42, 255, 255} becomes []byte{15, 43, 0, 0}
//
// In case of an overflow the end is set to nil.
//
// Example: []byte{255, 255, 255, 255} becomes nil
func PrefixRange(prefix []byte) ([]byte, []byte) {
if prefix == nil {
panic("nil key not allowed")
}
// special case: no prefix is whole range
if len(prefix) == 0 {
return nil, nil
}
// copy the prefix and update last byte
end := make([]byte, len(prefix))
copy(end, prefix)
l := len(end) - 1
end[l]++
// wait, what if that overflowed?....
for end[l] == 0 && l > 0 {
l--
end[l]++
}
// okay, funny guy, you gave us FFF, no end to this range...
if l == 0 && end[0] == 0 {
end = nil
}
return prefix, end
}