feat(orm): add ORM Table and Indexes (#10670)

## Description

Closes: #10729 

Includes:
* table, auto-increment table, and singleton `Table` implementations
* primary key, index and unique index `Index` implementations
* store wrappers based on tm-db but that could be retargeted to the new ADR 040 db which separate index and commitment stores, with a debug wrapper
* streaming JSON import and export
* full logical decoding (and encoding)



---

### Author Checklist

*All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.*

I have...

- [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] added `!` to the type prefix if API or client breaking change
- [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting))
- [x] provided a link to the relevant issue or specification
- [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules)
- [x] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing)
- [ ] added a changelog entry to `CHANGELOG.md`
- [x] included comments for [documenting Go code](https://blog.golang.org/godoc)
- [x] updated the relevant documentation or specification
- [x] reviewed "Files changed" and left comments if necessary
- [ ] confirmed all CI checks have passed

### Reviewers Checklist

*All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.*

I have...

- [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] confirmed `!` in the type prefix if API or client breaking change
- [ ] confirmed all author checklist items have been addressed 
- [ ] reviewed state machine logic
- [ ] reviewed API design and naming
- [ ] reviewed documentation is accurate
- [ ] reviewed tests and test coverage
- [ ] manually tested (if applicable)
This commit is contained in:
Aaron Craelius
2022-01-20 16:22:06 +00:00
committed by GitHub
parent 033e5f3076
commit 531bf50845
55 changed files with 6435 additions and 847 deletions
+42
View File
@@ -0,0 +1,42 @@
package encodeutil
import (
"bytes"
"encoding/binary"
"io"
"google.golang.org/protobuf/reflect/protoreflect"
)
// SkipPrefix skips the provided prefix in the reader or returns an error.
// This is used for efficient logical decoding of keys.
func SkipPrefix(r *bytes.Reader, prefix []byte) error {
n := len(prefix)
if n > 0 {
// we skip checking the prefix for performance reasons because we assume
// that it was checked by the caller
_, err := r.Seek(int64(n), io.SeekCurrent)
return err
}
return nil
}
// AppendVarUInt32 creates a new key prefix, by encoding and appending a
// var-uint32 to the provided prefix.
func AppendVarUInt32(prefix []byte, x uint32) []byte {
prefixLen := len(prefix)
res := make([]byte, prefixLen+binary.MaxVarintLen32)
copy(res, prefix)
n := binary.PutUvarint(res[prefixLen:], uint64(x))
return res[:prefixLen+n]
}
// ValuesOf takes the arguments and converts them to protoreflect.Value's.
func ValuesOf(values ...interface{}) []protoreflect.Value {
n := len(values)
res := make([]protoreflect.Value, n)
for i := 0; i < n; i++ {
res[i] = protoreflect.ValueOf(values[i])
}
return res
}
+20
View File
@@ -19,10 +19,30 @@ type EntryCodec interface {
type IndexCodec interface {
EntryCodec
// MessageType returns the message type this index codec applies to.
MessageType() protoreflect.MessageType
// GetFieldNames returns the field names in the key of this index.
GetFieldNames() []protoreflect.Name
// DecodeIndexKey decodes a kv-pair into index-fields and primary-key field
// values. These fields may or may not overlap depending on the index.
DecodeIndexKey(k, v []byte) (indexFields, primaryKey []protoreflect.Value, err error)
// EncodeKVFromMessage encodes a kv-pair for the index from a message.
EncodeKVFromMessage(message protoreflect.Message) (k, v []byte, err error)
// CompareKeys compares the provided values which must correspond to the
// fields in this key. Prefix keys of different lengths are supported but the
// function will panic if either array is too long. A negative value is returned
// if values1 is less than values2, 0 is returned if the two arrays are equal,
// and a positive value is returned if values2 is greater.
CompareKeys(key1, key2 []protoreflect.Value) int
// EncodeKeyFromMessage encodes the key part of this index and returns both
// index values and encoded key.
EncodeKeyFromMessage(message protoreflect.Message) (keyValues []protoreflect.Value, key []byte, err error)
// IsFullyOrdered returns true if all fields in the key are also ordered.
IsFullyOrdered() bool
}
+8 -14
View File
@@ -42,17 +42,11 @@ func (p *PrimaryKeyEntry) GetTableName() protoreflect.FullName {
}
func (p *PrimaryKeyEntry) String() string {
msg := p.Value
msgStr := "_"
if msg != nil {
msgBz, err := protojson.Marshal(msg)
if err == nil {
msgStr = string(msgBz)
} else {
msgStr = fmt.Sprintf("ERR:%v", err)
}
if p.Value == nil {
return fmt.Sprintf("PK %s %s -> _", p.TableName, fmtValues(p.Key))
} else {
return fmt.Sprintf("PK %s %s -> %s", p.TableName, fmtValues(p.Key), p.Value)
}
return fmt.Sprintf("PK:%s/%s:%s", p.TableName, fmtValues(p.Key), msgStr)
}
func fmtValues(values []protoreflect.Value) string {
@@ -109,7 +103,7 @@ func (i *IndexKeyEntry) GetTableName() protoreflect.FullName {
func (i *IndexKeyEntry) doNotImplement() {}
func (i *IndexKeyEntry) string() string {
return fmt.Sprintf("%s/%s:%s:%s", i.TableName, fmtFields(i.Fields), fmtValues(i.IndexValues), fmtValues(i.PrimaryKey))
return fmt.Sprintf("%s %s : %s -> %s", i.TableName, fmtFields(i.Fields), fmtValues(i.IndexValues), fmtValues(i.PrimaryKey))
}
func fmtFields(fields []protoreflect.Name) string {
@@ -122,10 +116,10 @@ func fmtFields(fields []protoreflect.Name) string {
func (i *IndexKeyEntry) String() string {
if i.IsUnique {
return fmt.Sprintf("UNIQ:%s", i.string())
return fmt.Sprintf("UNIQ %s", i.string())
} else {
return fmt.Sprintf("IDX:%s", i.string())
return fmt.Sprintf("IDX %s", i.string())
}
}
@@ -146,7 +140,7 @@ func (s *SeqEntry) GetTableName() protoreflect.FullName {
func (s *SeqEntry) doNotImplement() {}
func (s *SeqEntry) String() string {
return fmt.Sprintf("SEQ:%s:%d", s.TableName, s.Value)
return fmt.Sprintf("SEQ %s %d", s.TableName, s.Value)
}
var _, _, _ Entry = &PrimaryKeyEntry{}, &IndexKeyEntry{}, &SeqEntry{}
+18 -20
View File
@@ -4,33 +4,31 @@ import (
"testing"
"google.golang.org/protobuf/reflect/protoreflect"
"github.com/cosmos/cosmos-sdk/orm/encoding/ormkv"
"gotest.tools/v3/assert"
"github.com/cosmos/cosmos-sdk/orm/encoding/encodeutil"
"github.com/cosmos/cosmos-sdk/orm/encoding/ormkv"
"github.com/cosmos/cosmos-sdk/orm/internal/testpb"
"github.com/cosmos/cosmos-sdk/orm/internal/testutil"
)
var aFullName = (&testpb.A{}).ProtoReflect().Descriptor().FullName()
var aFullName = (&testpb.ExampleTable{}).ProtoReflect().Descriptor().FullName()
func TestPrimaryKeyEntry(t *testing.T) {
entry := &ormkv.PrimaryKeyEntry{
TableName: aFullName,
Key: testutil.ValuesOf(uint32(1), "abc"),
Value: &testpb.A{I32: -1},
Key: encodeutil.ValuesOf(uint32(1), "abc"),
Value: &testpb.ExampleTable{I32: -1},
}
assert.Equal(t, `PK:testpb.A/1/"abc":{"i32":-1}`, entry.String())
assert.Equal(t, `PK testpb.ExampleTable 1/"abc" -> i32:-1`, entry.String())
assert.Equal(t, aFullName, entry.GetTableName())
// prefix key
entry = &ormkv.PrimaryKeyEntry{
TableName: aFullName,
Key: testutil.ValuesOf(uint32(1), "abc"),
Key: encodeutil.ValuesOf(uint32(1), "abc"),
Value: nil,
}
assert.Equal(t, `PK:testpb.A/1/"abc":_`, entry.String())
assert.Equal(t, `PK testpb.ExampleTable 1/"abc" -> _`, entry.String())
assert.Equal(t, aFullName, entry.GetTableName())
}
@@ -39,20 +37,20 @@ func TestIndexKeyEntry(t *testing.T) {
TableName: aFullName,
Fields: []protoreflect.Name{"u32", "i32", "str"},
IsUnique: false,
IndexValues: testutil.ValuesOf(uint32(10), int32(-1), "abc"),
PrimaryKey: testutil.ValuesOf("abc", int32(-1)),
IndexValues: encodeutil.ValuesOf(uint32(10), int32(-1), "abc"),
PrimaryKey: encodeutil.ValuesOf("abc", int32(-1)),
}
assert.Equal(t, `IDX:testpb.A/u32/i32/str:10/-1/"abc":"abc"/-1`, entry.String())
assert.Equal(t, `IDX testpb.ExampleTable u32/i32/str : 10/-1/"abc" -> "abc"/-1`, entry.String())
assert.Equal(t, aFullName, entry.GetTableName())
entry = &ormkv.IndexKeyEntry{
TableName: aFullName,
Fields: []protoreflect.Name{"u32"},
IsUnique: true,
IndexValues: testutil.ValuesOf(uint32(10)),
PrimaryKey: testutil.ValuesOf("abc", int32(-1)),
IndexValues: encodeutil.ValuesOf(uint32(10)),
PrimaryKey: encodeutil.ValuesOf("abc", int32(-1)),
}
assert.Equal(t, `UNIQ:testpb.A/u32:10:"abc"/-1`, entry.String())
assert.Equal(t, `UNIQ testpb.ExampleTable u32 : 10 -> "abc"/-1`, entry.String())
assert.Equal(t, aFullName, entry.GetTableName())
// prefix key
@@ -60,9 +58,9 @@ func TestIndexKeyEntry(t *testing.T) {
TableName: aFullName,
Fields: []protoreflect.Name{"u32", "i32", "str"},
IsUnique: false,
IndexValues: testutil.ValuesOf(uint32(10), int32(-1)),
IndexValues: encodeutil.ValuesOf(uint32(10), int32(-1)),
}
assert.Equal(t, `IDX:testpb.A/u32/i32/str:10/-1:_`, entry.String())
assert.Equal(t, `IDX testpb.ExampleTable u32/i32/str : 10/-1 -> _`, entry.String())
assert.Equal(t, aFullName, entry.GetTableName())
// prefix key
@@ -70,8 +68,8 @@ func TestIndexKeyEntry(t *testing.T) {
TableName: aFullName,
Fields: []protoreflect.Name{"str", "i32"},
IsUnique: true,
IndexValues: testutil.ValuesOf("abc", int32(1)),
IndexValues: encodeutil.ValuesOf("abc", int32(1)),
}
assert.Equal(t, `UNIQ:testpb.A/str/i32:"abc"/1:_`, entry.String())
assert.Equal(t, `UNIQ testpb.ExampleTable str/i32 : "abc"/1 -> _`, entry.String())
assert.Equal(t, aFullName, entry.GetTableName())
}
+17 -13
View File
@@ -12,7 +12,6 @@ import (
// IndexKeyCodec is the codec for (non-unique) index keys.
type IndexKeyCodec struct {
*KeyCodec
tableName protoreflect.FullName
pkFieldOrder []int
}
@@ -20,7 +19,15 @@ var _ IndexCodec = &IndexKeyCodec{}
// NewIndexKeyCodec creates a new IndexKeyCodec with an optional prefix for the
// provided message descriptor, index and primary key fields.
func NewIndexKeyCodec(prefix []byte, messageDescriptor protoreflect.MessageDescriptor, indexFields, primaryKeyFields []protoreflect.Name) (*IndexKeyCodec, error) {
func NewIndexKeyCodec(prefix []byte, messageType protoreflect.MessageType, indexFields, primaryKeyFields []protoreflect.Name) (*IndexKeyCodec, error) {
if len(indexFields) == 0 {
return nil, ormerrors.InvalidTableDefinition.Wrapf("index fields are empty")
}
if len(primaryKeyFields) == 0 {
return nil, ormerrors.InvalidTableDefinition.Wrapf("primary key fields are empty")
}
indexFieldMap := map[protoreflect.Name]int{}
keyFields := make([]protoreflect.Name, 0, len(indexFields)+len(primaryKeyFields))
@@ -43,7 +50,7 @@ func NewIndexKeyCodec(prefix []byte, messageDescriptor protoreflect.MessageDescr
k++
}
cdc, err := NewKeyCodec(prefix, messageDescriptor, keyFields)
cdc, err := NewKeyCodec(prefix, messageType, keyFields)
if err != nil {
return nil, err
}
@@ -51,13 +58,12 @@ func NewIndexKeyCodec(prefix []byte, messageDescriptor protoreflect.MessageDescr
return &IndexKeyCodec{
KeyCodec: cdc,
pkFieldOrder: pkFieldOrder,
tableName: messageDescriptor.FullName(),
}, nil
}
func (cdc IndexKeyCodec) DecodeIndexKey(k, _ []byte) (indexFields, primaryKey []protoreflect.Value, err error) {
values, err := cdc.Decode(bytes.NewReader(k))
values, err := cdc.DecodeKey(bytes.NewReader(k))
// got prefix key
if err == io.EOF {
return values, nil, nil
@@ -87,7 +93,7 @@ func (cdc IndexKeyCodec) DecodeEntry(k, v []byte) (Entry, error) {
}
return &IndexKeyEntry{
TableName: cdc.tableName,
TableName: cdc.messageType.Descriptor().FullName(),
Fields: cdc.fieldNames,
IndexValues: idxValues,
PrimaryKey: pk,
@@ -100,21 +106,19 @@ func (cdc IndexKeyCodec) EncodeEntry(entry Entry) (k, v []byte, err error) {
return nil, nil, ormerrors.BadDecodeEntry
}
if indexEntry.TableName != cdc.tableName {
if indexEntry.TableName != cdc.messageType.Descriptor().FullName() {
return nil, nil, ormerrors.BadDecodeEntry
}
bz, err := cdc.KeyCodec.Encode(indexEntry.IndexValues)
bz, err := cdc.KeyCodec.EncodeKey(indexEntry.IndexValues)
if err != nil {
return nil, nil, err
}
return bz, sentinel, nil
return bz, []byte{}, nil
}
var sentinel = []byte{0}
func (cdc IndexKeyCodec) EncodeKVFromMessage(message protoreflect.Message) (k, v []byte, err error) {
_, k, err = cdc.EncodeFromMessage(message)
return k, sentinel, err
_, k, err = cdc.EncodeKeyFromMessage(message)
return k, []byte{}, err
}
+11 -11
View File
@@ -18,20 +18,20 @@ func TestIndexKeyCodec(t *testing.T) {
idxPartCdc := testutil.TestKeyCodecGen(1, 5).Draw(t, "idxPartCdc").(testutil.TestKeyCodec)
pkCodec := testutil.TestKeyCodecGen(1, 5).Draw(t, "pkCdc").(testutil.TestKeyCodec)
prefix := rapid.SliceOfN(rapid.Byte(), 0, 5).Draw(t, "prefix").([]byte)
desc := (&testpb.A{}).ProtoReflect().Descriptor()
messageType := (&testpb.ExampleTable{}).ProtoReflect().Type()
indexKeyCdc, err := ormkv.NewIndexKeyCodec(
prefix,
desc,
messageType,
idxPartCdc.Codec.GetFieldNames(),
pkCodec.Codec.GetFieldNames(),
)
assert.NilError(t, err)
for i := 0; i < 100; i++ {
a := testutil.GenA.Draw(t, fmt.Sprintf("a%d", i)).(*testpb.A)
key := indexKeyCdc.GetValues(a.ProtoReflect())
pk := pkCodec.Codec.GetValues(a.ProtoReflect())
a := testutil.GenA.Draw(t, fmt.Sprintf("a%d", i)).(*testpb.ExampleTable)
key := indexKeyCdc.GetKeyValues(a.ProtoReflect())
pk := pkCodec.Codec.GetKeyValues(a.ProtoReflect())
idx1 := &ormkv.IndexKeyEntry{
TableName: desc.FullName(),
TableName: messageType.Descriptor().FullName(),
Fields: indexKeyCdc.GetFieldNames(),
IsUnique: false,
IndexValues: key,
@@ -48,16 +48,16 @@ func TestIndexKeyCodec(t *testing.T) {
entry2, err := indexKeyCdc.DecodeEntry(k, v)
assert.NilError(t, err)
idx2 := entry2.(*ormkv.IndexKeyEntry)
assert.Equal(t, 0, indexKeyCdc.CompareValues(idx1.IndexValues, idx2.IndexValues))
assert.Equal(t, 0, pkCodec.Codec.CompareValues(idx1.PrimaryKey, idx2.PrimaryKey))
assert.Equal(t, 0, indexKeyCdc.CompareKeys(idx1.IndexValues, idx2.IndexValues))
assert.Equal(t, 0, pkCodec.Codec.CompareKeys(idx1.PrimaryKey, idx2.PrimaryKey))
assert.Equal(t, false, idx2.IsUnique)
assert.Equal(t, desc.FullName(), idx2.TableName)
assert.Equal(t, messageType.Descriptor().FullName(), idx2.TableName)
assert.DeepEqual(t, idx1.Fields, idx2.Fields)
idxFields, pk2, err := indexKeyCdc.DecodeIndexKey(k, v)
assert.NilError(t, err)
assert.Equal(t, 0, indexKeyCdc.CompareValues(key, idxFields))
assert.Equal(t, 0, pkCodec.Codec.CompareValues(pk, pk2))
assert.Equal(t, 0, indexKeyCdc.CompareKeys(key, idxFields))
assert.Equal(t, 0, pkCodec.Codec.CompareKeys(pk, pk2))
}
})
}
+30 -20
View File
@@ -8,6 +8,7 @@ import (
"google.golang.org/protobuf/reflect/protoreflect"
"github.com/cosmos/cosmos-sdk/orm/encoding/encodeutil"
"github.com/cosmos/cosmos-sdk/orm/encoding/ormfield"
)
@@ -22,11 +23,12 @@ type KeyCodec struct {
fieldDescriptors []protoreflect.FieldDescriptor
fieldNames []protoreflect.Name
fieldCodecs []ormfield.Codec
messageType protoreflect.MessageType
}
// NewKeyCodec returns a new KeyCodec with an optional prefix for the provided
// message descriptor and fields.
func NewKeyCodec(prefix []byte, messageDescriptor protoreflect.MessageDescriptor, fieldNames []protoreflect.Name) (*KeyCodec, error) {
func NewKeyCodec(prefix []byte, messageType protoreflect.MessageType, fieldNames []protoreflect.Name) (*KeyCodec, error) {
n := len(fieldNames)
fieldCodecs := make([]ormfield.Codec, n)
fieldDescriptors := make([]protoreflect.FieldDescriptor, n)
@@ -35,7 +37,7 @@ func NewKeyCodec(prefix []byte, messageDescriptor protoreflect.MessageDescriptor
i int
}
fixedSize := 0
messageFields := messageDescriptor.Fields()
messageFields := messageType.Descriptor().Fields()
for i := 0; i < n; i++ {
nonTerminal := i != n-1
@@ -63,15 +65,16 @@ func NewKeyCodec(prefix []byte, messageDescriptor protoreflect.MessageDescriptor
prefix: prefix,
fixedSize: fixedSize,
variableSizers: variableSizers,
messageType: messageType,
}, nil
}
// Encode encodes the values assuming that they correspond to the fields
// EncodeKey encodes the values assuming that they correspond to the fields
// specified for the key. If the array of values is shorter than the
// number of fields in the key, a partial "prefix" key will be encoded
// which can be used for constructing a prefix iterator.
func (cdc *KeyCodec) Encode(values []protoreflect.Value) ([]byte, error) {
sz, err := cdc.ComputeBufferSize(values)
func (cdc *KeyCodec) EncodeKey(values []protoreflect.Value) ([]byte, error) {
sz, err := cdc.ComputeKeyBufferSize(values)
if err != nil {
return nil, err
}
@@ -94,8 +97,8 @@ func (cdc *KeyCodec) Encode(values []protoreflect.Value) ([]byte, error) {
return w.Bytes(), nil
}
// GetValues extracts the values specified by the key fields from the message.
func (cdc *KeyCodec) GetValues(message protoreflect.Message) []protoreflect.Value {
// GetKeyValues extracts the values specified by the key fields from the message.
func (cdc *KeyCodec) GetKeyValues(message protoreflect.Message) []protoreflect.Value {
res := make([]protoreflect.Value, len(cdc.fieldDescriptors))
for i, f := range cdc.fieldDescriptors {
res[i] = message.Get(f)
@@ -103,11 +106,11 @@ func (cdc *KeyCodec) GetValues(message protoreflect.Message) []protoreflect.Valu
return res
}
// Decode decodes the values in the key specified by the reader. If the
// DecodeKey decodes the values in the key specified by the reader. If the
// provided key is a prefix key, the values that could be decoded will
// be returned with io.EOF as the error.
func (cdc *KeyCodec) Decode(r *bytes.Reader) ([]protoreflect.Value, error) {
if err := skipPrefix(r, cdc.prefix); err != nil {
func (cdc *KeyCodec) DecodeKey(r *bytes.Reader) ([]protoreflect.Value, error) {
if err := encodeutil.SkipPrefix(r, cdc.prefix); err != nil {
return nil, err
}
@@ -125,10 +128,10 @@ func (cdc *KeyCodec) Decode(r *bytes.Reader) ([]protoreflect.Value, error) {
return values, nil
}
// EncodeFromMessage combines GetValues and Encode.
func (cdc *KeyCodec) EncodeFromMessage(message protoreflect.Message) ([]protoreflect.Value, []byte, error) {
values := cdc.GetValues(message)
bz, err := cdc.Encode(values)
// EncodeKeyFromMessage combines GetKeyValues and EncodeKey.
func (cdc *KeyCodec) EncodeKeyFromMessage(message protoreflect.Message) ([]protoreflect.Value, []byte, error) {
values := cdc.GetKeyValues(message)
bz, err := cdc.EncodeKey(values)
return values, bz, err
}
@@ -142,12 +145,12 @@ func (cdc *KeyCodec) IsFullyOrdered() bool {
return true
}
// CompareValues compares the provided values which must correspond to the
// CompareKeys compares the provided values which must correspond to the
// fields in this key. Prefix keys of different lengths are supported but the
// function will panic if either array is too long. A negative value is returned
// if values1 is less than values2, 0 is returned if the two arrays are equal,
// and a positive value is returned if values2 is greater.
func (cdc *KeyCodec) CompareValues(values1, values2 []protoreflect.Value) int {
func (cdc *KeyCodec) CompareKeys(values1, values2 []protoreflect.Value) int {
j := len(values1)
k := len(values2)
n := j
@@ -178,9 +181,9 @@ func (cdc *KeyCodec) CompareValues(values1, values2 []protoreflect.Value) int {
}
}
// ComputeBufferSize computes the required buffer size for the provided values
// ComputeKeyBufferSize computes the required buffer size for the provided values
// which can represent a full or prefix key.
func (cdc KeyCodec) ComputeBufferSize(values []protoreflect.Value) (int, error) {
func (cdc KeyCodec) ComputeKeyBufferSize(values []protoreflect.Value) (int, error) {
size := cdc.fixedSize
n := len(values)
for _, sz := range cdc.variableSizers {
@@ -198,10 +201,10 @@ func (cdc KeyCodec) ComputeBufferSize(values []protoreflect.Value) (int, error)
return size, nil
}
// SetValues sets the provided values on the message which must correspond
// SetKeyValues sets the provided values on the message which must correspond
// exactly to the field descriptors for this key. Prefix keys aren't
// supported.
func (cdc *KeyCodec) SetValues(message protoreflect.Message, values []protoreflect.Value) {
func (cdc *KeyCodec) SetKeyValues(message protoreflect.Message, values []protoreflect.Value) {
for i, f := range cdc.fieldDescriptors {
message.Set(f, values[i])
}
@@ -284,6 +287,13 @@ func (cdc *KeyCodec) GetFieldNames() []protoreflect.Name {
return cdc.fieldNames
}
// Prefix returns the prefix applied to keys in this codec before any field
// values are encoded.
func (cdc *KeyCodec) Prefix() []byte {
return cdc.prefix
}
// MessageType returns the message type of fields in this key.
func (cdc *KeyCodec) MessageType() protoreflect.MessageType {
return cdc.messageType
}
+76 -77
View File
@@ -5,14 +5,13 @@ import (
"io"
"testing"
"github.com/cosmos/cosmos-sdk/orm/internal/testpb"
"github.com/cosmos/cosmos-sdk/orm/encoding/ormkv"
"google.golang.org/protobuf/reflect/protoreflect"
"gotest.tools/v3/assert"
"pgregory.net/rapid"
"github.com/cosmos/cosmos-sdk/orm/encoding/encodeutil"
"github.com/cosmos/cosmos-sdk/orm/encoding/ormkv"
"github.com/cosmos/cosmos-sdk/orm/internal/testpb"
"github.com/cosmos/cosmos-sdk/orm/internal/testutil"
)
@@ -29,24 +28,24 @@ func TestKeyCodec(t *testing.T) {
keyValues2 := key.Draw(t, "values2")
bz2 := assertEncDecKey(t, key, keyValues2)
// bytes comparison should equal comparison of values
assert.Equal(t, key.Codec.CompareValues(keyValues, keyValues2), bytes.Compare(bz1, bz2))
assert.Equal(t, key.Codec.CompareKeys(keyValues, keyValues2), bytes.Compare(bz1, bz2))
}
}
})
}
func assertEncDecKey(t *rapid.T, key testutil.TestKeyCodec, keyValues []protoreflect.Value) []byte {
bz, err := key.Codec.Encode(keyValues)
bz, err := key.Codec.EncodeKey(keyValues)
assert.NilError(t, err)
keyValues2, err := key.Codec.Decode(bytes.NewReader(bz))
keyValues2, err := key.Codec.DecodeKey(bytes.NewReader(bz))
assert.NilError(t, err)
assert.Equal(t, 0, key.Codec.CompareValues(keyValues, keyValues2))
assert.Equal(t, 0, key.Codec.CompareKeys(keyValues, keyValues2))
return bz
}
func TestCompareValues(t *testing.T) {
cdc, err := ormkv.NewKeyCodec(nil,
(&testpb.A{}).ProtoReflect().Descriptor(),
(&testpb.ExampleTable{}).ProtoReflect().Type(),
[]protoreflect.Name{"u32", "str", "i32"})
assert.NilError(t, err)
@@ -59,113 +58,113 @@ func TestCompareValues(t *testing.T) {
}{
{
"eq",
testutil.ValuesOf(uint32(0), "abc", int32(-3)),
testutil.ValuesOf(uint32(0), "abc", int32(-3)),
encodeutil.ValuesOf(uint32(0), "abc", int32(-3)),
encodeutil.ValuesOf(uint32(0), "abc", int32(-3)),
0,
false,
},
{
"eq prefix 0",
testutil.ValuesOf(),
testutil.ValuesOf(),
encodeutil.ValuesOf(),
encodeutil.ValuesOf(),
0,
false,
},
{
"eq prefix 1",
testutil.ValuesOf(uint32(0)),
testutil.ValuesOf(uint32(0)),
encodeutil.ValuesOf(uint32(0)),
encodeutil.ValuesOf(uint32(0)),
0,
false,
},
{
"eq prefix 2",
testutil.ValuesOf(uint32(0), "abc"),
testutil.ValuesOf(uint32(0), "abc"),
encodeutil.ValuesOf(uint32(0), "abc"),
encodeutil.ValuesOf(uint32(0), "abc"),
0,
false,
},
{
"lt1",
testutil.ValuesOf(uint32(0), "abc", int32(-3)),
testutil.ValuesOf(uint32(1), "abc", int32(-3)),
encodeutil.ValuesOf(uint32(0), "abc", int32(-3)),
encodeutil.ValuesOf(uint32(1), "abc", int32(-3)),
-1,
true,
},
{
"lt2",
testutil.ValuesOf(uint32(1), "abb", int32(-3)),
testutil.ValuesOf(uint32(1), "abc", int32(-3)),
encodeutil.ValuesOf(uint32(1), "abb", int32(-3)),
encodeutil.ValuesOf(uint32(1), "abc", int32(-3)),
-1,
true,
},
{
"lt3",
testutil.ValuesOf(uint32(1), "abb", int32(-4)),
testutil.ValuesOf(uint32(1), "abb", int32(-3)),
encodeutil.ValuesOf(uint32(1), "abb", int32(-4)),
encodeutil.ValuesOf(uint32(1), "abb", int32(-3)),
-1,
true,
},
{
"less prefix 0",
testutil.ValuesOf(),
testutil.ValuesOf(uint32(1), "abb", int32(-4)),
encodeutil.ValuesOf(),
encodeutil.ValuesOf(uint32(1), "abb", int32(-4)),
-1,
true,
},
{
"less prefix 1",
testutil.ValuesOf(uint32(1)),
testutil.ValuesOf(uint32(1), "abb", int32(-4)),
encodeutil.ValuesOf(uint32(1)),
encodeutil.ValuesOf(uint32(1), "abb", int32(-4)),
-1,
true,
},
{
"less prefix 2",
testutil.ValuesOf(uint32(1), "abb"),
testutil.ValuesOf(uint32(1), "abb", int32(-4)),
encodeutil.ValuesOf(uint32(1), "abb"),
encodeutil.ValuesOf(uint32(1), "abb", int32(-4)),
-1,
true,
},
{
"gt1",
testutil.ValuesOf(uint32(2), "abb", int32(-4)),
testutil.ValuesOf(uint32(1), "abb", int32(-4)),
encodeutil.ValuesOf(uint32(2), "abb", int32(-4)),
encodeutil.ValuesOf(uint32(1), "abb", int32(-4)),
1,
false,
},
{
"gt2",
testutil.ValuesOf(uint32(2), "abc", int32(-4)),
testutil.ValuesOf(uint32(2), "abb", int32(-4)),
encodeutil.ValuesOf(uint32(2), "abc", int32(-4)),
encodeutil.ValuesOf(uint32(2), "abb", int32(-4)),
1,
false,
},
{
"gt3",
testutil.ValuesOf(uint32(2), "abc", int32(1)),
testutil.ValuesOf(uint32(2), "abc", int32(-3)),
encodeutil.ValuesOf(uint32(2), "abc", int32(1)),
encodeutil.ValuesOf(uint32(2), "abc", int32(-3)),
1,
false,
},
{
"gt prefix 0",
testutil.ValuesOf(uint32(2), "abc", int32(-3)),
testutil.ValuesOf(),
encodeutil.ValuesOf(uint32(2), "abc", int32(-3)),
encodeutil.ValuesOf(),
1,
true,
},
{
"gt prefix 1",
testutil.ValuesOf(uint32(2), "abc", int32(-3)),
testutil.ValuesOf(uint32(2)),
encodeutil.ValuesOf(uint32(2), "abc", int32(-3)),
encodeutil.ValuesOf(uint32(2)),
1,
true,
},
{
"gt prefix 2",
testutil.ValuesOf(uint32(2), "abc", int32(-3)),
testutil.ValuesOf(uint32(2), "abc"),
encodeutil.ValuesOf(uint32(2), "abc", int32(-3)),
encodeutil.ValuesOf(uint32(2), "abc"),
1,
true,
},
@@ -174,7 +173,7 @@ func TestCompareValues(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
assert.Equal(
t, test.expect,
cdc.CompareValues(test.values1, test.values2),
cdc.CompareKeys(test.values1, test.values2),
)
// CheckValidRangeIterationKeys should give comparable results
err := cdc.CheckValidRangeIterationKeys(test.values1, test.values2)
@@ -189,7 +188,7 @@ func TestCompareValues(t *testing.T) {
func TestDecodePrefixKey(t *testing.T) {
cdc, err := ormkv.NewKeyCodec(nil,
(&testpb.A{}).ProtoReflect().Descriptor(),
(&testpb.ExampleTable{}).ProtoReflect().Type(),
[]protoreflect.Name{"u32", "str", "bz", "i32"})
assert.NilError(t, err)
@@ -199,23 +198,23 @@ func TestDecodePrefixKey(t *testing.T) {
}{
{
"1",
testutil.ValuesOf(uint32(5), "abc"),
encodeutil.ValuesOf(uint32(5), "abc"),
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
bz, err := cdc.Encode(test.values)
bz, err := cdc.EncodeKey(test.values)
assert.NilError(t, err)
values, err := cdc.Decode(bytes.NewReader(bz))
values, err := cdc.DecodeKey(bytes.NewReader(bz))
assert.ErrorType(t, err, io.EOF)
assert.Equal(t, 0, cdc.CompareValues(test.values, values))
assert.Equal(t, 0, cdc.CompareKeys(test.values, values))
})
}
}
func TestValidRangeIterationKeys(t *testing.T) {
cdc, err := ormkv.NewKeyCodec(nil,
(&testpb.A{}).ProtoReflect().Descriptor(),
(&testpb.ExampleTable{}).ProtoReflect().Type(),
[]protoreflect.Name{"u32", "str", "bz", "i32"})
assert.NilError(t, err)
@@ -227,62 +226,62 @@ func TestValidRangeIterationKeys(t *testing.T) {
}{
{
"1 eq",
testutil.ValuesOf(uint32(0)),
testutil.ValuesOf(uint32(0)),
encodeutil.ValuesOf(uint32(0)),
encodeutil.ValuesOf(uint32(0)),
true,
},
{
"1 lt",
testutil.ValuesOf(uint32(0)),
testutil.ValuesOf(uint32(1)),
encodeutil.ValuesOf(uint32(0)),
encodeutil.ValuesOf(uint32(1)),
false,
},
{
"1 gt",
testutil.ValuesOf(uint32(1)),
testutil.ValuesOf(uint32(0)),
encodeutil.ValuesOf(uint32(1)),
encodeutil.ValuesOf(uint32(0)),
true,
},
{
"1,2 lt",
testutil.ValuesOf(uint32(0)),
testutil.ValuesOf(uint32(0), "abc"),
encodeutil.ValuesOf(uint32(0)),
encodeutil.ValuesOf(uint32(0), "abc"),
false,
},
{
"1,2 gt",
testutil.ValuesOf(uint32(0), "abc"),
testutil.ValuesOf(uint32(0)),
encodeutil.ValuesOf(uint32(0), "abc"),
encodeutil.ValuesOf(uint32(0)),
false,
},
{
"1,2,3",
testutil.ValuesOf(uint32(0)),
testutil.ValuesOf(uint32(0), "abc", []byte{1, 2}),
encodeutil.ValuesOf(uint32(0)),
encodeutil.ValuesOf(uint32(0), "abc", []byte{1, 2}),
true,
},
{
"1,2,3,4 lt",
testutil.ValuesOf(uint32(0), "abc", []byte{1, 2}, int32(-1)),
testutil.ValuesOf(uint32(0), "abc", []byte{1, 2}, int32(1)),
encodeutil.ValuesOf(uint32(0), "abc", []byte{1, 2}, int32(-1)),
encodeutil.ValuesOf(uint32(0), "abc", []byte{1, 2}, int32(1)),
false,
},
{
"too long",
testutil.ValuesOf(uint32(0), "abc", []byte{1, 2}, int32(-1)),
testutil.ValuesOf(uint32(0), "abc", []byte{1, 2}, int32(1), int32(1)),
encodeutil.ValuesOf(uint32(0), "abc", []byte{1, 2}, int32(-1)),
encodeutil.ValuesOf(uint32(0), "abc", []byte{1, 2}, int32(1), int32(1)),
true,
},
{
"1,2,3,4 eq",
testutil.ValuesOf(uint32(0), "abc", []byte{1, 2}, int32(1)),
testutil.ValuesOf(uint32(0), "abc", []byte{1, 2}, int32(1)),
encodeutil.ValuesOf(uint32(0), "abc", []byte{1, 2}, int32(1)),
encodeutil.ValuesOf(uint32(0), "abc", []byte{1, 2}, int32(1)),
true,
},
{
"1,2,3,4 bz err",
testutil.ValuesOf(uint32(0), "abc", []byte{1, 2}, int32(-1)),
testutil.ValuesOf(uint32(0), "abc", []byte{1, 2, 3}, int32(1)),
encodeutil.ValuesOf(uint32(0), "abc", []byte{1, 2}, int32(-1)),
encodeutil.ValuesOf(uint32(0), "abc", []byte{1, 2, 3}, int32(1)),
true,
},
}
@@ -300,19 +299,19 @@ func TestValidRangeIterationKeys(t *testing.T) {
func TestGetSet(t *testing.T) {
cdc, err := ormkv.NewKeyCodec(nil,
(&testpb.A{}).ProtoReflect().Descriptor(),
(&testpb.ExampleTable{}).ProtoReflect().Type(),
[]protoreflect.Name{"u32", "str", "i32"})
assert.NilError(t, err)
var a testpb.A
values := testutil.ValuesOf(uint32(4), "abc", int32(1))
cdc.SetValues(a.ProtoReflect(), values)
values2 := cdc.GetValues(a.ProtoReflect())
assert.Equal(t, 0, cdc.CompareValues(values, values2))
bz, err := cdc.Encode(values)
var a testpb.ExampleTable
values := encodeutil.ValuesOf(uint32(4), "abc", int32(1))
cdc.SetKeyValues(a.ProtoReflect(), values)
values2 := cdc.GetKeyValues(a.ProtoReflect())
assert.Equal(t, 0, cdc.CompareKeys(values, values2))
bz, err := cdc.EncodeKey(values)
assert.NilError(t, err)
values3, bz2, err := cdc.EncodeFromMessage(a.ProtoReflect())
values3, bz2, err := cdc.EncodeKeyFromMessage(a.ProtoReflect())
assert.NilError(t, err)
assert.Equal(t, 0, cdc.CompareValues(values, values3))
assert.Equal(t, 0, cdc.CompareKeys(values, values3))
assert.Assert(t, bytes.Equal(bz, bz2))
}
+19 -14
View File
@@ -14,21 +14,21 @@ import (
// PrimaryKeyCodec is the codec for primary keys.
type PrimaryKeyCodec struct {
*KeyCodec
msgType protoreflect.MessageType
unmarshalOptions proto.UnmarshalOptions
}
var _ IndexCodec = &PrimaryKeyCodec{}
// NewPrimaryKeyCodec creates a new PrimaryKeyCodec for the provided msg and
// fields, with an optional prefix and unmarshal options.
func NewPrimaryKeyCodec(prefix []byte, msgType protoreflect.MessageType, fieldNames []protoreflect.Name, unmarshalOptions proto.UnmarshalOptions) (*PrimaryKeyCodec, error) {
keyCodec, err := NewKeyCodec(prefix, msgType.Descriptor(), fieldNames)
keyCodec, err := NewKeyCodec(prefix, msgType, fieldNames)
if err != nil {
return nil, err
}
return &PrimaryKeyCodec{
KeyCodec: keyCodec,
msgType: msgType,
unmarshalOptions: unmarshalOptions,
}, nil
}
@@ -36,7 +36,7 @@ func NewPrimaryKeyCodec(prefix []byte, msgType protoreflect.MessageType, fieldNa
var _ IndexCodec = PrimaryKeyCodec{}
func (p PrimaryKeyCodec) DecodeIndexKey(k, _ []byte) (indexFields, primaryKey []protoreflect.Value, err error) {
indexFields, err = p.Decode(bytes.NewReader(k))
indexFields, err = p.DecodeKey(bytes.NewReader(k))
// got prefix key
if err == io.EOF {
@@ -55,16 +55,21 @@ func (p PrimaryKeyCodec) DecodeIndexKey(k, _ []byte) (indexFields, primaryKey []
}
func (p PrimaryKeyCodec) DecodeEntry(k, v []byte) (Entry, error) {
values, err := p.Decode(bytes.NewReader(k))
if err != nil {
values, err := p.DecodeKey(bytes.NewReader(k))
if err == io.EOF {
return &PrimaryKeyEntry{
TableName: p.messageType.Descriptor().FullName(),
Key: values,
}, nil
} else if err != nil {
return nil, err
}
msg := p.msgType.New().Interface()
msg := p.messageType.New().Interface()
err = p.Unmarshal(values, v, msg)
return &PrimaryKeyEntry{
TableName: p.msgType.Descriptor().FullName(),
TableName: p.messageType.Descriptor().FullName(),
Key: values,
Value: msg,
}, err
@@ -76,15 +81,15 @@ func (p PrimaryKeyCodec) EncodeEntry(entry Entry) (k, v []byte, err error) {
return nil, nil, ormerrors.BadDecodeEntry.Wrapf("expected %T, got %T", &PrimaryKeyEntry{}, entry)
}
if pkEntry.TableName != p.msgType.Descriptor().FullName() {
if pkEntry.TableName != p.messageType.Descriptor().FullName() {
return nil, nil, ormerrors.BadDecodeEntry.Wrapf(
"wrong table name, got %s, expected %s",
pkEntry.TableName,
p.msgType.Descriptor().FullName(),
p.messageType.Descriptor().FullName(),
)
}
k, err = p.KeyCodec.Encode(pkEntry.Key)
k, err = p.KeyCodec.EncodeKey(pkEntry.Key)
if err != nil {
return nil, nil, err
}
@@ -104,7 +109,7 @@ func (p PrimaryKeyCodec) marshal(key []protoreflect.Value, message proto.Message
}
// set the primary key values again returning the message to its original state
p.SetValues(message.ProtoReflect(), key)
p.SetKeyValues(message.ProtoReflect(), key)
return v, nil
}
@@ -122,12 +127,12 @@ func (p *PrimaryKeyCodec) Unmarshal(key []protoreflect.Value, value []byte, mess
}
// rehydrate primary key
p.SetValues(message.ProtoReflect(), key)
p.SetKeyValues(message.ProtoReflect(), key)
return nil
}
func (p PrimaryKeyCodec) EncodeKVFromMessage(message protoreflect.Message) (k, v []byte, err error) {
ks, k, err := p.KeyCodec.EncodeFromMessage(message)
ks, k, err := p.KeyCodec.EncodeKeyFromMessage(message)
if err != nil {
return nil, nil, err
}
+7 -7
View File
@@ -20,14 +20,14 @@ func TestPrimaryKeyCodec(t *testing.T) {
keyCodec := testutil.TestKeyCodecGen(0, 5).Draw(t, "keyCodec").(testutil.TestKeyCodec)
pkCodec, err := ormkv.NewPrimaryKeyCodec(
keyCodec.Codec.Prefix(),
(&testpb.A{}).ProtoReflect().Type(),
(&testpb.ExampleTable{}).ProtoReflect().Type(),
keyCodec.Codec.GetFieldNames(),
proto.UnmarshalOptions{},
)
assert.NilError(t, err)
for i := 0; i < 100; i++ {
a := testutil.GenA.Draw(t, fmt.Sprintf("a%d", i)).(*testpb.A)
key := keyCodec.Codec.GetValues(a.ProtoReflect())
a := testutil.GenA.Draw(t, fmt.Sprintf("a%d", i)).(*testpb.ExampleTable)
key := keyCodec.Codec.GetKeyValues(a.ProtoReflect())
pk1 := &ormkv.PrimaryKeyEntry{
TableName: aFullName,
Key: key,
@@ -44,16 +44,16 @@ func TestPrimaryKeyCodec(t *testing.T) {
entry2, err := pkCodec.DecodeEntry(k, v)
assert.NilError(t, err)
pk2 := entry2.(*ormkv.PrimaryKeyEntry)
assert.Equal(t, 0, pkCodec.CompareValues(pk1.Key, pk2.Key))
assert.Equal(t, 0, pkCodec.CompareKeys(pk1.Key, pk2.Key))
assert.DeepEqual(t, pk1.Value, pk2.Value, protocmp.Transform())
idxFields, pk3, err := pkCodec.DecodeIndexKey(k, v)
assert.NilError(t, err)
assert.Equal(t, 0, pkCodec.CompareValues(pk1.Key, pk3))
assert.Equal(t, 0, pkCodec.CompareValues(pk1.Key, idxFields))
assert.Equal(t, 0, pkCodec.CompareKeys(pk1.Key, pk3))
assert.Equal(t, 0, pkCodec.CompareKeys(pk1.Key, idxFields))
pkCodec.ClearValues(a.ProtoReflect())
pkCodec.SetValues(a.ProtoReflect(), pk1.Key)
pkCodec.SetKeyValues(a.ProtoReflect(), pk1.Key)
assert.DeepEqual(t, a, pk2.Value, protocmp.Transform())
}
})
+6 -6
View File
@@ -11,13 +11,13 @@ import (
// SeqCodec is the codec for auto-incrementing uint64 primary key sequences.
type SeqCodec struct {
tableName protoreflect.FullName
prefix []byte
messageType protoreflect.FullName
prefix []byte
}
// NewSeqCodec creates a new SeqCodec.
func NewSeqCodec(tableName protoreflect.FullName, prefix []byte) *SeqCodec {
return &SeqCodec{tableName: tableName, prefix: prefix}
func NewSeqCodec(messageType protoreflect.MessageType, prefix []byte) *SeqCodec {
return &SeqCodec{messageType: messageType.Descriptor().FullName(), prefix: prefix}
}
var _ EntryCodec = &SeqCodec{}
@@ -33,7 +33,7 @@ func (s SeqCodec) DecodeEntry(k, v []byte) (Entry, error) {
}
return &SeqEntry{
TableName: s.tableName,
TableName: s.messageType,
Value: x,
}, nil
}
@@ -44,7 +44,7 @@ func (s SeqCodec) EncodeEntry(entry Entry) (k, v []byte, err error) {
return nil, nil, ormerrors.BadDecodeEntry
}
if seqEntry.TableName != s.tableName {
if seqEntry.TableName != s.messageType {
return nil, nil, ormerrors.BadDecodeEntry
}
+3 -2
View File
@@ -15,8 +15,9 @@ import (
func TestSeqCodec(t *testing.T) {
rapid.Check(t, func(t *rapid.T) {
prefix := rapid.SliceOfN(rapid.Byte(), 0, 5).Draw(t, "prefix").([]byte)
tableName := (&testpb.A{}).ProtoReflect().Descriptor().FullName()
cdc := ormkv.NewSeqCodec(tableName, prefix)
typ := (&testpb.ExampleTable{}).ProtoReflect().Type()
tableName := typ.Descriptor().FullName()
cdc := ormkv.NewSeqCodec(typ, prefix)
seq, err := cdc.DecodeValue(nil)
assert.NilError(t, err)
+48 -14
View File
@@ -11,7 +11,6 @@ import (
// UniqueKeyCodec is the codec for unique indexes.
type UniqueKeyCodec struct {
tableName protoreflect.FullName
pkFieldOrder []struct {
inKey bool
i int
@@ -20,10 +19,20 @@ type UniqueKeyCodec struct {
valueCodec *KeyCodec
}
var _ IndexCodec = &UniqueKeyCodec{}
// NewUniqueKeyCodec creates a new UniqueKeyCodec with an optional prefix for the
// provided message descriptor, index and primary key fields.
func NewUniqueKeyCodec(prefix []byte, messageDescriptor protoreflect.MessageDescriptor, indexFields, primaryKeyFields []protoreflect.Name) (*UniqueKeyCodec, error) {
keyCodec, err := NewKeyCodec(prefix, messageDescriptor, indexFields)
func NewUniqueKeyCodec(prefix []byte, messageType protoreflect.MessageType, indexFields, primaryKeyFields []protoreflect.Name) (*UniqueKeyCodec, error) {
if len(indexFields) == 0 {
return nil, ormerrors.InvalidTableDefinition.Wrapf("index fields are empty")
}
if len(primaryKeyFields) == 0 {
return nil, ormerrors.InvalidTableDefinition.Wrapf("primary key fields are empty")
}
keyCodec, err := NewKeyCodec(prefix, messageType, indexFields)
if err != nil {
return nil, err
}
@@ -55,23 +64,20 @@ func NewUniqueKeyCodec(prefix []byte, messageDescriptor protoreflect.MessageDesc
}
}
valueCodec, err := NewKeyCodec(nil, messageDescriptor, valueFields)
valueCodec, err := NewKeyCodec(nil, messageType, valueFields)
if err != nil {
return nil, err
}
return &UniqueKeyCodec{
tableName: messageDescriptor.FullName(),
pkFieldOrder: pkFieldOrder,
keyCodec: keyCodec,
valueCodec: valueCodec,
}, nil
}
var _ IndexCodec = &UniqueKeyCodec{}
func (u UniqueKeyCodec) DecodeIndexKey(k, v []byte) (indexFields, primaryKey []protoreflect.Value, err error) {
ks, err := u.keyCodec.Decode(bytes.NewReader(k))
ks, err := u.keyCodec.DecodeKey(bytes.NewReader(k))
// got prefix key
if err == io.EOF {
@@ -85,7 +91,7 @@ func (u UniqueKeyCodec) DecodeIndexKey(k, v []byte) (indexFields, primaryKey []p
return ks, nil, err
}
vs, err := u.valueCodec.Decode(bytes.NewReader(v))
vs, err := u.valueCodec.DecodeKey(bytes.NewReader(v))
if err != nil {
return nil, nil, err
}
@@ -117,7 +123,7 @@ func (u UniqueKeyCodec) DecodeEntry(k, v []byte) (Entry, error) {
}
return &IndexKeyEntry{
TableName: u.tableName,
TableName: u.MessageType().Descriptor().FullName(),
Fields: u.keyCodec.fieldNames,
IsUnique: true,
IndexValues: idxVals,
@@ -130,7 +136,7 @@ func (u UniqueKeyCodec) EncodeEntry(entry Entry) (k, v []byte, err error) {
if !ok {
return nil, nil, ormerrors.BadDecodeEntry
}
k, err = u.keyCodec.Encode(indexEntry.IndexValues)
k, err = u.keyCodec.EncodeKey(indexEntry.IndexValues)
if err != nil {
return nil, nil, err
}
@@ -155,16 +161,44 @@ func (u UniqueKeyCodec) EncodeEntry(entry Entry) (k, v []byte, err error) {
}
}
v, err = u.valueCodec.Encode(values)
v, err = u.valueCodec.EncodeKey(values)
return k, v, err
}
func (u UniqueKeyCodec) EncodeKVFromMessage(message protoreflect.Message) (k, v []byte, err error) {
_, k, err = u.keyCodec.EncodeFromMessage(message)
_, k, err = u.keyCodec.EncodeKeyFromMessage(message)
if err != nil {
return nil, nil, err
}
_, v, err = u.valueCodec.EncodeFromMessage(message)
_, v, err = u.valueCodec.EncodeKeyFromMessage(message)
return k, v, err
}
func (u UniqueKeyCodec) GetFieldNames() []protoreflect.Name {
return u.keyCodec.GetFieldNames()
}
func (u UniqueKeyCodec) GetKeyCodec() *KeyCodec {
return u.keyCodec
}
func (u UniqueKeyCodec) GetValueCodec() *KeyCodec {
return u.valueCodec
}
func (u UniqueKeyCodec) CompareKeys(key1, key2 []protoreflect.Value) int {
return u.keyCodec.CompareKeys(key1, key2)
}
func (u UniqueKeyCodec) EncodeKeyFromMessage(message protoreflect.Message) (keyValues []protoreflect.Value, key []byte, err error) {
return u.keyCodec.EncodeKeyFromMessage(message)
}
func (u UniqueKeyCodec) IsFullyOrdered() bool {
return u.keyCodec.IsFullyOrdered()
}
func (u UniqueKeyCodec) MessageType() protoreflect.MessageType {
return u.keyCodec.messageType
}
+11 -11
View File
@@ -17,20 +17,20 @@ func TestUniqueKeyCodec(t *testing.T) {
rapid.Check(t, func(t *rapid.T) {
keyCodec := testutil.TestKeyCodecGen(1, 5).Draw(t, "keyCodec").(testutil.TestKeyCodec)
pkCodec := testutil.TestKeyCodecGen(1, 5).Draw(t, "primaryKeyCodec").(testutil.TestKeyCodec)
desc := (&testpb.A{}).ProtoReflect().Descriptor()
messageType := (&testpb.ExampleTable{}).ProtoReflect().Type()
uniqueKeyCdc, err := ormkv.NewUniqueKeyCodec(
keyCodec.Codec.Prefix(),
desc,
messageType,
keyCodec.Codec.GetFieldNames(),
pkCodec.Codec.GetFieldNames(),
)
assert.NilError(t, err)
for i := 0; i < 100; i++ {
a := testutil.GenA.Draw(t, fmt.Sprintf("a%d", i)).(*testpb.A)
key := keyCodec.Codec.GetValues(a.ProtoReflect())
pk := pkCodec.Codec.GetValues(a.ProtoReflect())
a := testutil.GenA.Draw(t, fmt.Sprintf("a%d", i)).(*testpb.ExampleTable)
key := keyCodec.Codec.GetKeyValues(a.ProtoReflect())
pk := pkCodec.Codec.GetKeyValues(a.ProtoReflect())
uniq1 := &ormkv.IndexKeyEntry{
TableName: desc.FullName(),
TableName: messageType.Descriptor().FullName(),
Fields: keyCodec.Codec.GetFieldNames(),
IsUnique: true,
IndexValues: key,
@@ -47,16 +47,16 @@ func TestUniqueKeyCodec(t *testing.T) {
entry2, err := uniqueKeyCdc.DecodeEntry(k, v)
assert.NilError(t, err)
uniq2 := entry2.(*ormkv.IndexKeyEntry)
assert.Equal(t, 0, keyCodec.Codec.CompareValues(uniq1.IndexValues, uniq2.IndexValues))
assert.Equal(t, 0, pkCodec.Codec.CompareValues(uniq1.PrimaryKey, uniq2.PrimaryKey))
assert.Equal(t, 0, keyCodec.Codec.CompareKeys(uniq1.IndexValues, uniq2.IndexValues))
assert.Equal(t, 0, pkCodec.Codec.CompareKeys(uniq1.PrimaryKey, uniq2.PrimaryKey))
assert.Equal(t, true, uniq2.IsUnique)
assert.Equal(t, desc.FullName(), uniq2.TableName)
assert.Equal(t, messageType.Descriptor().FullName(), uniq2.TableName)
assert.DeepEqual(t, uniq1.Fields, uniq2.Fields)
idxFields, pk2, err := uniqueKeyCdc.DecodeIndexKey(k, v)
assert.NilError(t, err)
assert.Equal(t, 0, keyCodec.Codec.CompareValues(key, idxFields))
assert.Equal(t, 0, pkCodec.Codec.CompareValues(pk, pk2))
assert.Equal(t, 0, keyCodec.Codec.CompareKeys(key, idxFields))
assert.Equal(t, 0, pkCodec.Codec.CompareKeys(pk, pk2))
}
})
}
-17
View File
@@ -1,17 +0,0 @@
package ormkv
import (
"bytes"
"io"
)
func skipPrefix(r *bytes.Reader, prefix []byte) error {
n := len(prefix)
if n > 0 {
// we skip checking the prefix for performance reasons because we assume
// that it was checked by the caller
_, err := r.Seek(int64(n), io.SeekCurrent)
return err
}
return nil
}