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:
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user