Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add Table-Store (aka ORM) package - Index and Iterator #10451

Merged
merged 75 commits into from
Nov 9, 2021
Merged
Show file tree
Hide file tree
Changes from 66 commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
5539498
WIP on adding table/indexable
blushi Jul 8, 2021
10a3bec
Add some tests
blushi Jul 20, 2021
86a5ac6
Add more tests
blushi Jul 22, 2021
580c41b
Add sequence
blushi Jul 22, 2021
d0a77b6
Update testdata
blushi Jul 22, 2021
fc72796
Lint
blushi Jul 22, 2021
71cac77
Merge branch 'master' into marie/9237-table-store-1
blushi Jul 22, 2021
a4a4e09
Add docs
blushi Jul 22, 2021
b6f2d08
Update docs
blushi Jul 22, 2021
ed2a1d2
Use Update instead of Save
blushi Jul 28, 2021
b0a8586
Merge branch 'master' into marie/9237-table-store-1
blushi Jul 28, 2021
f4df020
Add AutoUInt64Table
blushi Jul 30, 2021
f1dd883
WIP on adding tests
blushi Jul 30, 2021
0234149
Fix table tests
blushi Aug 5, 2021
30c765b
Move orm to x/group
blushi Aug 11, 2021
5e71573
Add orm
blushi Aug 11, 2021
63168ed
Merge branch 'master' into marie/9237-table-store-1
blushi Aug 11, 2021
482617d
Merge branch 'master' into marie/9237-table-store-1
blushi Sep 2, 2021
09d832a
Merge branch 'master' into marie/9237-table-store-1
blushi Oct 7, 2021
0151b74
Update orm with latest changes
blushi Oct 8, 2021
4d2486a
Mv orm to x/group/internal
blushi Oct 8, 2021
a368554
Update go.mod and fix tests
blushi Oct 8, 2021
ce21252
Update README
blushi Oct 8, 2021
8956be8
Merge branch 'master' into marie/9237-table-store-1
blushi Oct 8, 2021
560662e
Fix tests
blushi Oct 8, 2021
bb495f7
Use [2]byte for table prefix key
blushi Oct 8, 2021
915d734
Update docs
blushi Oct 8, 2021
0ef7f82
Merge branch 'master' into marie/9237-table-store-1
blushi Oct 8, 2021
e3aa3e8
Rm file
blushi Oct 8, 2021
df0c304
Rm file
blushi Oct 8, 2021
661d449
Revert store/README
blushi Oct 8, 2021
b47264d
Register errors in types/errors
blushi Oct 8, 2021
00b97d8
Fix group err
blushi Oct 8, 2021
247fcbe
Merge branch 'marie/9237-table-store-1' into marie/9237-table-store-2
blushi Oct 8, 2021
61cbb3c
Merge branch 'master' into marie/9237-table-store-1
blushi Oct 14, 2021
5079788
WIP adding key codec
blushi Oct 14, 2021
5b38f67
Add property tests
blushi Oct 15, 2021
55e5ef2
WIP adding orm scenario test
blushi Oct 15, 2021
8311958
Add orm scenario test
blushi Oct 20, 2021
fc4f5d7
Merge branch 'master' into marie/9237-table-store-2
blushi Oct 20, 2021
f1712c8
go mod tidy
blushi Oct 21, 2021
f16d662
WIP on docs
blushi Oct 21, 2021
a8b57d2
Simplify table creation
blushi Oct 21, 2021
7c63cbd
Merge branch 'master' into marie/9237-table-store-1
blushi Oct 21, 2021
9f78f16
Merge branch 'marie/9237-table-store-1' into marie/9237-table-store-2
blushi Oct 21, 2021
8504cd4
Replace code snippets with github links
blushi Oct 21, 2021
851e6e7
Merge branch 'master' into marie/9237-table-store-2
blushi Oct 21, 2021
87fc7b0
Update x/group/internal/orm/table.go
blushi Oct 21, 2021
d2f9a52
Address review comments
blushi Oct 21, 2021
21a5d8b
Merge branch 'marie/9237-table-store-1' into marie/9237-table-store-2
blushi Oct 21, 2021
28f601e
Fix tests
blushi Oct 21, 2021
c1a4e74
Merge branch 'marie/9237-table-store-2' of github.com:cosmos/cosmos-s…
blushi Oct 21, 2021
5aa297e
Merge branch 'master' into marie/9237-table-store-2
blushi Oct 21, 2021
77fd4d1
Small nits
blushi Oct 21, 2021
6594197
wip adding indexer
blushi Oct 27, 2021
a073a39
Merge branch 'master' into marie/9237-table-store-3
blushi Oct 27, 2021
09e7830
Add indexer and iterator tests
blushi Oct 28, 2021
899cd49
Add iterator property tests
blushi Oct 28, 2021
538c19f
Update orm scenario tests
blushi Oct 28, 2021
a49e591
Add prefix scan methods to tables types
blushi Oct 28, 2021
430163e
Merge branch 'master' into marie/9237-table-store-3
blushi Oct 28, 2021
badd821
Add docs
blushi Oct 29, 2021
837bf90
Merge branch 'master' into marie/9237-table-store-3
blushi Oct 29, 2021
44d7e26
Update var name
blushi Oct 29, 2021
21cd674
Update docs
blushi Oct 29, 2021
0f329d4
Fix naming
blushi Oct 29, 2021
8d0c2b0
Update x/group/internal/orm/auto_uint64_test.go
blushi Nov 2, 2021
6f45be8
Update x/group/internal/orm/auto_uint64_test.go
blushi Nov 2, 2021
0dbc7d4
Update prefix scan docs
blushi Nov 2, 2021
3b37ef2
Use const
blushi Nov 2, 2021
6f86782
Merge branch 'master' into marie/9237-table-store-3
blushi Nov 2, 2021
ef9a3a4
Move registered errors to x/group/errors
blushi Nov 2, 2021
fb1e63c
Merge branch 'master' into marie/9237-table-store-3
blushi Nov 9, 2021
9fb1c59
Fix dec errors
blushi Nov 9, 2021
68effe9
Merge branch 'master' into marie/9237-table-store-3
blushi Nov 9, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions types/errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,8 @@ var (
// ErrORMUniqueConstraint defines an error when a value already exists at a given key
ErrORMUniqueConstraint = Register(ormCodespace, 44, "unique constraint violation")

// ErrORMEmptyModel defines an error when an empty model is provided for building a table
ErrORMEmptyModel = Register(ormCodespace, 45, "invalid argument")
// ErrORMInvalidArgument defines an error when an invalid argument is provided as part of ORM functions
ErrORMInvalidArgument = Register(ormCodespace, 45, "invalid argument")
blushi marked this conversation as resolved.
Show resolved Hide resolved

// ErrORMKeyMaxLength defines an error when a key exceeds max length
ErrORMKeyMaxLength = Register(ormCodespace, 46, "key exceeds max length")
Expand Down
33 changes: 33 additions & 0 deletions x/group/internal/orm/auto_uint64.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,36 @@ func (a AutoUInt64Table) GetOne(store sdk.KVStore, rowID uint64, dest codec.Prot
}
return rawRowID, 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)
blushi marked this conversation as resolved.
Show resolved Hide resolved
//
// 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 AutoUInt64Table) PrefixScan(store sdk.KVStore, start, end uint64) (Iterator, error) {
return a.table.PrefixScan(store, EncodeSequence(start), EncodeSequence(end))
}

// 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 AutoUInt64Table) ReversePrefixScan(store sdk.KVStore, start uint64, end uint64) (Iterator, error) {
return a.table.ReversePrefixScan(store, EncodeSequence(start), EncodeSequence(end))
}
163 changes: 163 additions & 0 deletions x/group/internal/orm/auto_uint64_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package orm

import (
"math"
"testing"

"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestAutoUInt64PrefixScan(t *testing.T) {
interfaceRegistry := types.NewInterfaceRegistry()
cdc := codec.NewProtoCodec(interfaceRegistry)

tb, err := NewAutoUInt64Table(AutoUInt64TablePrefix, AutoUInt64TableSeqPrefix, &testdata.TableModel{}, cdc)
require.NoError(t, err)

ctx := NewMockContext()
store := ctx.KVStore(sdk.NewKVStoreKey("test"))

metadata := []byte("metadata")
t1 := testdata.TableModel{
Id: 1,
Name: "my test 1",
Metadata: metadata,
}
t2 := testdata.TableModel{
Id: 2,
Name: "my test 2",
Metadata: metadata,
}
t3 := testdata.TableModel{
Id: 3,
Name: "my test 3",
Metadata: metadata,
}
for _, g := range []testdata.TableModel{t1, t2, t3} {
_, err := tb.Create(store, &g)
require.NoError(t, err)
}

specs := map[string]struct {
start, end uint64
expResult []testdata.TableModel
expRowIDs []RowID
expError *errors.Error
method func(store sdk.KVStore, start uint64, end uint64) (Iterator, error)
}{
"first element": {
start: 1,
end: 2,
method: tb.PrefixScan,
expResult: []testdata.TableModel{t1},
expRowIDs: []RowID{EncodeSequence(1)},
},
"first 2 elements": {
start: 1,
end: 3,
method: tb.PrefixScan,
expResult: []testdata.TableModel{t1, t2},
expRowIDs: []RowID{EncodeSequence(1), EncodeSequence(2)},
},
"first 3 elements": {
start: 1,
end: 4,
method: tb.PrefixScan,
expResult: []testdata.TableModel{t1, t2, t3},
expRowIDs: []RowID{EncodeSequence(1), EncodeSequence(2), EncodeSequence(3)},
},
"search with max end": {
start: 1,
end: math.MaxUint64,
method: tb.PrefixScan,
expResult: []testdata.TableModel{t1, t2, t3},
expRowIDs: []RowID{EncodeSequence(1), EncodeSequence(2), EncodeSequence(3)},
},
"2 to end": {
start: 2,
end: 5,
method: tb.PrefixScan,
expResult: []testdata.TableModel{t2, t3},
expRowIDs: []RowID{EncodeSequence(2), EncodeSequence(3)},
},
"start before end should fail": {
blushi marked this conversation as resolved.
Show resolved Hide resolved
start: 2,
end: 1,
method: tb.PrefixScan,
expError: errors.ErrORMInvalidArgument,
},
"start equals end should fail": {
start: 1,
end: 1,
method: tb.PrefixScan,
expError: errors.ErrORMInvalidArgument,
},
"reverse first element": {
start: 1,
end: 2,
method: tb.ReversePrefixScan,
expResult: []testdata.TableModel{t1},
expRowIDs: []RowID{EncodeSequence(1)},
},
"reverse first 2 elements": {
start: 1,
end: 3,
method: tb.ReversePrefixScan,
expResult: []testdata.TableModel{t2, t1},
expRowIDs: []RowID{EncodeSequence(2), EncodeSequence(1)},
},
"reverse first 3 elements": {
start: 1,
end: 4,
method: tb.ReversePrefixScan,
expResult: []testdata.TableModel{t3, t2, t1},
expRowIDs: []RowID{EncodeSequence(3), EncodeSequence(2), EncodeSequence(1)},
},
"reverse search with max end": {
start: 1,
end: math.MaxUint64,
method: tb.ReversePrefixScan,
expResult: []testdata.TableModel{t3, t2, t1},
expRowIDs: []RowID{EncodeSequence(3), EncodeSequence(2), EncodeSequence(1)},
},
"reverse 2 to end": {
start: 2,
end: 5,
method: tb.ReversePrefixScan,
expResult: []testdata.TableModel{t3, t2},
expRowIDs: []RowID{EncodeSequence(3), EncodeSequence(2)},
},
"reverse start before end should fail": {
blushi marked this conversation as resolved.
Show resolved Hide resolved
start: 2,
end: 1,
method: tb.ReversePrefixScan,
expError: errors.ErrORMInvalidArgument,
},
"reverse start equals end should fail": {
start: 1,
end: 1,
method: tb.ReversePrefixScan,
expError: errors.ErrORMInvalidArgument,
},
}
for msg, spec := range specs {
t.Run(msg, func(t *testing.T) {
it, err := spec.method(store, spec.start, spec.end)
require.True(t, spec.expError.Is(err), "expected #+v but got #+v", spec.expError, err)
if spec.expError != nil {
return
}
var loaded []testdata.TableModel
rowIDs, err := ReadAll(it, &loaded)
require.NoError(t, err)
assert.Equal(t, spec.expResult, loaded)
assert.Equal(t, spec.expRowIDs, rowIDs)
})
}
}
49 changes: 40 additions & 9 deletions x/group/internal/orm/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,61 @@ import (
)

type TestKeeper struct {
autoUInt64Table *AutoUInt64Table
primaryKeyTable *PrimaryKeyTable
autoUInt64Table *AutoUInt64Table
primaryKeyTable *PrimaryKeyTable
autoUInt64TableModelByMetadataIndex Index
primaryKeyTableModelByNameIndex Index
primaryKeyTableModelByNumberIndex Index
primaryKeyTableModelByMetadataIndex Index
}

var (
AutoUInt64TableTablePrefix [2]byte = [2]byte{0x0}
PrimaryKeyTablePrefix [2]byte = [2]byte{0x1}
AutoUInt64TableSeqPrefix byte = 0x2
AutoUInt64TablePrefix [2]byte = [2]byte{0x0}
PrimaryKeyTablePrefix [2]byte = [2]byte{0x1}
AutoUInt64TableSeqPrefix byte = 0x2
AutoUInt64TableModelByMetadataPrefix byte = 0x4
PrimaryKeyTableModelByNamePrefix byte = 0x5
PrimaryKeyTableModelByNumberPrefix byte = 0x6
PrimaryKeyTableModelByMetadataPrefix byte = 0x7
)

func NewTestKeeper(cdc codec.Codec) TestKeeper {
k := TestKeeper{}
var err error

autoUInt64Table, err := NewAutoUInt64Table(AutoUInt64TableTablePrefix, AutoUInt64TableSeqPrefix, &testdata.TableModel{}, cdc)
k.autoUInt64Table, err = NewAutoUInt64Table(AutoUInt64TablePrefix, AutoUInt64TableSeqPrefix, &testdata.TableModel{}, cdc)
if err != nil {
panic(err.Error())
}
k.autoUInt64TableModelByMetadataIndex, err = NewIndex(k.autoUInt64Table, AutoUInt64TableModelByMetadataPrefix, func(val interface{}) ([]interface{}, error) {
return []interface{}{val.(*testdata.TableModel).Metadata}, nil
}, testdata.TableModel{}.Metadata)
if err != nil {
panic(err.Error())
}
k.autoUInt64Table = autoUInt64Table

primaryKeyTable, err := NewPrimaryKeyTable(PrimaryKeyTablePrefix, &testdata.TableModel{}, cdc)
k.primaryKeyTable, err = NewPrimaryKeyTable(PrimaryKeyTablePrefix, &testdata.TableModel{}, cdc)
if err != nil {
panic(err.Error())
}
k.primaryKeyTableModelByNameIndex, err = NewIndex(k.primaryKeyTable, PrimaryKeyTableModelByNamePrefix, func(val interface{}) ([]interface{}, error) {
return []interface{}{val.(*testdata.TableModel).Name}, nil
}, testdata.TableModel{}.Name)
if err != nil {
panic(err.Error())
}
k.primaryKeyTableModelByNumberIndex, err = NewIndex(k.primaryKeyTable, PrimaryKeyTableModelByNumberPrefix, func(val interface{}) ([]interface{}, error) {
return []interface{}{val.(*testdata.TableModel).Number}, nil
}, testdata.TableModel{}.Number)
if err != nil {
panic(err.Error())
}
k.primaryKeyTableModelByMetadataIndex, err = NewIndex(k.primaryKeyTable, PrimaryKeyTableModelByMetadataPrefix, func(val interface{}) ([]interface{}, error) {
return []interface{}{val.(*testdata.TableModel).Metadata}, nil
}, testdata.TableModel{}.Metadata)
if err != nil {
panic(err.Error())
}
k.primaryKeyTable = primaryKeyTable

return k
}
Loading