fix(orm)!: duration encoding doesn't handle nil values properly (#15138)

Co-authored-by: marbar3778 <marbar3778@yahoo.com>
Co-authored-by: Ryan Christoffersen <12519942+ryanchristo@users.noreply.github.com>
This commit is contained in:
Aaron Craelius 2023-04-13 13:37:46 -04:00 committed by GitHub
parent 9e4fbb6db1
commit 13493d3645
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 2336 additions and 734 deletions

View File

@ -47,3 +47,4 @@ Ref: https://keepachangelog.com/en/1.0.0/
### State-machine Breaking Changes
* [#12273](https://github.com/cosmos/cosmos-sdk/pull/12273) The timestamp key encoding was reworked to properly handle nil values. Existing users will need to manually migrate their data to the new encoding before upgrading.
* [#15138](https://github.com/cosmos/cosmos-sdk/pull/15138) The duration key encoding was reworked to properly handle nil values. Existing users will need to manually migrate their data to the new encoding before upgrading.

View File

@ -4,9 +4,6 @@ import (
"bytes"
"fmt"
"testing"
"time"
"google.golang.org/protobuf/types/known/timestamppb"
"github.com/cosmos/cosmos-sdk/orm/encoding/ormfield"
@ -174,35 +171,3 @@ func TestCompactUInt64(t *testing.T) {
assert.Equal(t, y, y2)
})
}
func TestTimestamp(t *testing.T) {
cdc := ormfield.TimestampCodec{}
// nil value
buf := &bytes.Buffer{}
assert.NilError(t, cdc.Encode(protoreflect.Value{}, buf))
assert.Equal(t, 1, len(buf.Bytes()))
val, err := cdc.Decode(buf)
assert.NilError(t, err)
assert.Assert(t, !val.IsValid())
// no nanos
ts := timestamppb.New(time.Date(2022, 1, 1, 12, 30, 15, 0, time.UTC))
val = protoreflect.ValueOfMessage(ts.ProtoReflect())
buf = &bytes.Buffer{}
assert.NilError(t, cdc.Encode(val, buf))
assert.Equal(t, 6, len(buf.Bytes()))
val2, err := cdc.Decode(buf)
assert.NilError(t, err)
assert.Equal(t, 0, cdc.Compare(val, val2))
// nanos
ts = timestamppb.New(time.Date(2022, 1, 1, 12, 30, 15, 235809753, time.UTC))
val = protoreflect.ValueOfMessage(ts.ProtoReflect())
buf = &bytes.Buffer{}
assert.NilError(t, cdc.Encode(val, buf))
assert.Equal(t, 9, len(buf.Bytes()))
val2, err = cdc.Decode(buf)
assert.NilError(t, err)
assert.Equal(t, 0, cdc.Compare(val, val2))
}

View File

@ -1,51 +1,102 @@
package ormfield
import (
"fmt"
io "io"
"google.golang.org/protobuf/reflect/protoreflect"
)
var (
durationSecondsField = durationMsgType.Descriptor().Fields().ByName("seconds")
durationNanosField = durationMsgType.Descriptor().Fields().ByName("nanos")
const (
DurationSecondsMin = -315576000000
DurationSecondsMax = 315576000000
DurationNanosMin = -999999999
DurationNanosMax = 999999999
)
func getDurationSecondsAndNanos(value protoreflect.Value) (protoreflect.Value, protoreflect.Value) {
msg := value.Message()
return msg.Get(durationSecondsField), msg.Get(durationNanosField)
}
// DurationCodec encodes a google.protobuf.Duration value as 12 bytes using
// Int64Codec for seconds followed by Int32Codec for nanos. This allows for
// sorted iteration.
type DurationCodec struct{}
func (d DurationCodec) Decode(r Reader) (protoreflect.Value, error) {
seconds, err := int64Codec.Decode(r)
if err != nil {
return protoreflect.Value{}, err
}
nanos, err := int32Codec.Decode(r)
if err != nil {
return protoreflect.Value{}, err
}
msg := durationMsgType.New()
msg.Set(durationSecondsField, seconds)
msg.Set(durationNanosField, nanos)
return protoreflect.ValueOfMessage(msg), nil
}
func (d DurationCodec) Encode(value protoreflect.Value, w io.Writer) error {
// nil case
if !value.IsValid() {
_, err := w.Write(timestampDurationNilBz)
return err
}
seconds, nanos := getDurationSecondsAndNanos(value)
err := int64Codec.Encode(seconds, w)
secondsInt := seconds.Int()
if secondsInt < DurationSecondsMin || secondsInt > DurationSecondsMax {
return fmt.Errorf("duration seconds is out of range %d, must be between %d and %d", secondsInt, DurationSecondsMin, DurationSecondsMax)
}
negative := secondsInt < 0
// we subtract the min duration value to make sure secondsInt is always non-negative and starts at 0
secondsInt -= DurationSecondsMin
err := encodeSeconds(secondsInt, w)
if err != nil {
return err
}
return int32Codec.Encode(nanos, w)
nanosInt := nanos.Int()
if nanosInt == 0 {
_, err = w.Write(timestampZeroNanosBz)
return err
}
if negative {
if nanosInt < DurationNanosMin || nanosInt > 0 {
return fmt.Errorf("negative duration nanos is out of range %d, must be between %d and %d", nanosInt, DurationNanosMin, 0)
}
nanosInt = -nanosInt
} else if nanosInt < 0 || nanosInt > DurationNanosMax {
return fmt.Errorf("duration nanos is out of range %d, must be between %d and %d", nanosInt, 0, DurationNanosMax)
}
return encodeNanos(nanosInt, w)
}
func (d DurationCodec) Decode(r Reader) (protoreflect.Value, error) {
isNil, seconds, err := decodeSeconds(r)
if isNil || err != nil {
return protoreflect.Value{}, err
}
// we add the min duration value to get back the original value
seconds += DurationSecondsMin
negative := seconds < 0
msg := durationMsgType.New()
msg.Set(durationSecondsField, protoreflect.ValueOfInt64(seconds))
nanos, err := decodeNanos(r)
if err != nil {
return protoreflect.Value{}, err
}
if nanos == 0 {
return protoreflect.ValueOfMessage(msg), nil
}
if negative {
nanos = -nanos
}
msg.Set(durationNanosField, protoreflect.ValueOfInt32(nanos))
return protoreflect.ValueOfMessage(msg), nil
}
func (d DurationCodec) Compare(v1, v2 protoreflect.Value) int {
if !v1.IsValid() {
if !v2.IsValid() {
return 0
}
return 1
}
if !v2.IsValid() {
return -1
}
s1, n1 := getDurationSecondsAndNanos(v1)
s2, n2 := getDurationSecondsAndNanos(v2)
c := compareInt(s1, s2)
@ -61,9 +112,70 @@ func (d DurationCodec) IsOrdered() bool {
}
func (d DurationCodec) FixedBufferSize() int {
return 12
return timestampDurationBufferSize
}
func (d DurationCodec) ComputeBufferSize(protoreflect.Value) (int, error) {
return timestampDurationBufferSize, nil
}
var (
durationSecondsField = durationMsgType.Descriptor().Fields().ByName("seconds")
durationNanosField = durationMsgType.Descriptor().Fields().ByName("nanos")
)
func getDurationSecondsAndNanos(value protoreflect.Value) (protoreflect.Value, protoreflect.Value) {
msg := value.Message()
return msg.Get(durationSecondsField), msg.Get(durationNanosField)
}
// DurationV0Codec encodes a google.protobuf.Duration value as 12 bytes using
// Int64Codec for seconds followed by Int32Codec for nanos. This allows for
// sorted iteration.
type DurationV0Codec struct{}
func (d DurationV0Codec) Decode(r Reader) (protoreflect.Value, error) {
seconds, err := int64Codec.Decode(r)
if err != nil {
return protoreflect.Value{}, err
}
nanos, err := int32Codec.Decode(r)
if err != nil {
return protoreflect.Value{}, err
}
msg := durationMsgType.New()
msg.Set(durationSecondsField, seconds)
msg.Set(durationNanosField, nanos)
return protoreflect.ValueOfMessage(msg), nil
}
func (d DurationV0Codec) Encode(value protoreflect.Value, w io.Writer) error {
seconds, nanos := getDurationSecondsAndNanos(value)
err := int64Codec.Encode(seconds, w)
if err != nil {
return err
}
return int32Codec.Encode(nanos, w)
}
func (d DurationV0Codec) Compare(v1, v2 protoreflect.Value) int {
s1, n1 := getDurationSecondsAndNanos(v1)
s2, n2 := getDurationSecondsAndNanos(v2)
c := compareInt(s1, s2)
if c != 0 {
return c
}
return compareInt(n1, n2)
}
func (d DurationV0Codec) IsOrdered() bool {
return true
}
func (d DurationV0Codec) FixedBufferSize() int {
return 12
}
func (d DurationV0Codec) ComputeBufferSize(protoreflect.Value) (int, error) {
return d.FixedBufferSize(), nil
}

View File

@ -0,0 +1,158 @@
package ormfield_test
import (
"bytes"
"testing"
"time"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/types/known/durationpb"
"gotest.tools/v3/assert"
"github.com/cosmos/cosmos-sdk/orm/encoding/ormfield"
)
func TestDuration(t *testing.T) {
t.Parallel()
cdc := ormfield.DurationCodec{}
// nil value
t.Run("nil value", func(t *testing.T) {
t.Parallel()
buf := &bytes.Buffer{}
assert.NilError(t, cdc.Encode(protoreflect.Value{}, buf))
assert.Equal(t, 1, len(buf.Bytes()))
val, err := cdc.Decode(buf)
assert.NilError(t, err)
assert.Assert(t, !val.IsValid())
})
// no nanos
t.Run("no nanos", func(t *testing.T) {
t.Parallel()
dur, err := time.ParseDuration("100s")
assert.NilError(t, err)
durPb := durationpb.New(dur)
val := protoreflect.ValueOfMessage(durPb.ProtoReflect())
buf := &bytes.Buffer{}
assert.NilError(t, cdc.Encode(val, buf))
assert.Equal(t, 6, len(buf.Bytes()))
val2, err := cdc.Decode(buf)
assert.NilError(t, err)
assert.Equal(t, 0, cdc.Compare(val, val2))
})
t.Run("nanos", func(t *testing.T) {
t.Parallel()
dur, err := time.ParseDuration("3879468295ns")
assert.NilError(t, err)
durPb := durationpb.New(dur)
val := protoreflect.ValueOfMessage(durPb.ProtoReflect())
buf := &bytes.Buffer{}
assert.NilError(t, cdc.Encode(val, buf))
assert.Equal(t, 9, len(buf.Bytes()))
val2, err := cdc.Decode(buf)
assert.NilError(t, err)
assert.Equal(t, 0, cdc.Compare(val, val2))
})
t.Run("min value", func(t *testing.T) {
t.Parallel()
durPb := &durationpb.Duration{
Seconds: -315576000000,
Nanos: -999999999,
}
val := protoreflect.ValueOfMessage(durPb.ProtoReflect())
buf := &bytes.Buffer{}
assert.NilError(t, cdc.Encode(val, buf))
assert.Equal(t, 9, len(buf.Bytes()))
val2, err := cdc.Decode(buf)
assert.NilError(t, err)
assert.Equal(t, 0, cdc.Compare(val, val2))
})
t.Run("max value", func(t *testing.T) {
t.Parallel()
durPb := &durationpb.Duration{
Seconds: 315576000000,
Nanos: 999999999,
}
val := protoreflect.ValueOfMessage(durPb.ProtoReflect())
buf := &bytes.Buffer{}
assert.NilError(t, cdc.Encode(val, buf))
assert.Equal(t, 9, len(buf.Bytes()))
val2, err := cdc.Decode(buf)
assert.NilError(t, err)
assert.Equal(t, 0, cdc.Compare(val, val2))
})
}
func TestDurationOutOfRange(t *testing.T) {
t.Parallel()
cdc := ormfield.DurationCodec{}
tt := []struct {
name string
dur *durationpb.Duration
expectErr string
}{
{
name: "seconds too small",
dur: &durationpb.Duration{
Seconds: -315576000001,
Nanos: 0,
},
expectErr: "seconds is out of range",
},
{
name: "seconds too big",
dur: &durationpb.Duration{
Seconds: 315576000001,
Nanos: 0,
},
expectErr: "seconds is out of range",
},
{
name: "positive seconds negative nanos",
dur: &durationpb.Duration{
Seconds: 0,
Nanos: -1,
},
expectErr: "nanos is out of range",
},
{
name: "positive seconds nanos too big",
dur: &durationpb.Duration{
Seconds: 0,
Nanos: 1000000000,
},
expectErr: "nanos is out of range",
},
{
name: "negative seconds positive nanos",
dur: &durationpb.Duration{
Seconds: -1,
Nanos: 1,
},
expectErr: "negative duration nanos is out of range",
},
{
name: "negative seconds nanos too small",
dur: &durationpb.Duration{
Seconds: -1,
Nanos: -1000000000,
},
expectErr: "negative duration nanos is out of range",
},
}
for _, tc := range tt {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
val := protoreflect.ValueOfMessage(tc.dur.ProtoReflect())
buf := &bytes.Buffer{}
err := cdc.Encode(val, buf)
assert.ErrorContains(t, err, tc.expectErr)
})
}
}

View File

@ -21,38 +21,33 @@ import (
type TimestampCodec struct{}
const (
timestampNilValue = 0xFF
timestampZeroNanosValue = 0x0
timestampSecondsMin = -62135579038
timestampSecondsMax = 253402318799
timestampNanosMax = 999999999
timestampDurationNilValue = 0xFF
timestampDurationZeroNanosValue = 0x0
timestampDurationBufferSize = 9
TimestampSecondsMin = -62135596800
TimestampSecondsMax = 253402300799
TimestampNanosMax = 999999999
)
var (
timestampNilBz = []byte{timestampNilValue}
timestampZeroNanosBz = []byte{timestampZeroNanosValue}
timestampDurationNilBz = []byte{timestampDurationNilValue}
timestampZeroNanosBz = []byte{timestampDurationZeroNanosValue}
)
func (t TimestampCodec) Encode(value protoreflect.Value, w io.Writer) error {
// nil case
if !value.IsValid() {
_, err := w.Write(timestampNilBz)
_, err := w.Write(timestampDurationNilBz)
return err
}
seconds, nanos := getTimestampSecondsAndNanos(value)
secondsInt := seconds.Int()
if secondsInt < timestampSecondsMin || secondsInt > timestampSecondsMax {
return fmt.Errorf("seconds is out of range %d, must be between %d and %d", secondsInt, timestampSecondsMin, timestampSecondsMax)
if secondsInt < TimestampSecondsMin || secondsInt > TimestampSecondsMax {
return fmt.Errorf("timestamp seconds is out of range %d, must be between %d and %d", secondsInt, TimestampSecondsMin, TimestampSecondsMax)
}
secondsInt -= timestampSecondsMin
var secondsBz [5]byte
// write the seconds buffer from the end to the front
for i := 4; i >= 0; i-- {
secondsBz[i] = byte(secondsInt)
secondsInt >>= 8
}
_, err := w.Write(secondsBz[:])
secondsInt -= TimestampSecondsMin
err := encodeSeconds(secondsInt, w)
if err != nil {
return err
}
@ -63,65 +58,104 @@ func (t TimestampCodec) Encode(value protoreflect.Value, w io.Writer) error {
return err
}
if nanosInt < 0 || nanosInt > timestampNanosMax {
return fmt.Errorf("nanos is out of range %d, must be between %d and %d", secondsInt, 0, timestampNanosMax)
if nanosInt < 0 || nanosInt > TimestampNanosMax {
return fmt.Errorf("timestamp nanos is out of range %d, must be between %d and %d", secondsInt, 0, TimestampNanosMax)
}
return encodeNanos(nanosInt, w)
}
func encodeSeconds(secondsInt int64, w io.Writer) error {
var secondsBz [5]byte
// write the seconds buffer from the end to the front
for i := 4; i >= 0; i-- {
secondsBz[i] = byte(secondsInt)
secondsInt >>= 8
}
_, err := w.Write(secondsBz[:])
return err
}
func encodeNanos(nanosInt int64, w io.Writer) error {
var nanosBz [4]byte
for i := 3; i >= 0; i-- {
nanosBz[i] = byte(nanosInt)
nanosInt >>= 8
}
nanosBz[0] |= 0xC0
_, err = w.Write(nanosBz[:])
_, err := w.Write(nanosBz[:])
return err
}
func (t TimestampCodec) Decode(r Reader) (protoreflect.Value, error) {
b0, err := r.ReadByte()
isNil, seconds, err := decodeSeconds(r)
if isNil || err != nil {
return protoreflect.Value{}, err
}
seconds += TimestampSecondsMin
msg := timestampMsgType.New()
msg.Set(timestampSecondsField, protoreflect.ValueOfInt64(seconds))
nanos, err := decodeNanos(r)
if err != nil {
return protoreflect.Value{}, err
}
if b0 == timestampNilValue {
return protoreflect.Value{}, nil
if nanos == 0 {
return protoreflect.ValueOfMessage(msg), nil
}
msg.Set(timestampNanosField, protoreflect.ValueOfInt32(nanos))
return protoreflect.ValueOfMessage(msg), nil
}
func decodeSeconds(r Reader) (isNil bool, seconds int64, err error) {
b0, err := r.ReadByte()
if err != nil {
return false, 0, err
}
if b0 == timestampDurationNilValue {
return true, 0, nil
}
var secondsBz [4]byte
n, err := r.Read(secondsBz[:])
if err != nil {
return protoreflect.Value{}, err
return false, 0, err
}
if n < 4 {
return protoreflect.Value{}, io.EOF
return false, 0, io.EOF
}
seconds := int64(b0)
seconds = int64(b0)
for i := 0; i < 4; i++ {
seconds <<= 8
seconds |= int64(secondsBz[i])
}
seconds += timestampSecondsMin
msg := timestampMsgType.New()
msg.Set(timestampSecondsField, protoreflect.ValueOfInt64(seconds))
return false, seconds, nil
}
b0, err = r.ReadByte()
func decodeNanos(r Reader) (int32, error) {
b0, err := r.ReadByte()
if err != nil {
return protoreflect.Value{}, err
return 0, err
}
if b0 == timestampZeroNanosValue {
return protoreflect.ValueOfMessage(msg), nil
if b0 == timestampDurationZeroNanosValue {
return 0, nil
}
var nanosBz [3]byte
n, err = r.Read(nanosBz[:])
n, err := r.Read(nanosBz[:])
if err != nil {
return protoreflect.Value{}, err
return 0, err
}
if n < 3 {
return protoreflect.Value{}, io.EOF
return 0, io.EOF
}
nanos := int32(b0) & 0x3F // clear first two bits
@ -130,8 +164,7 @@ func (t TimestampCodec) Decode(r Reader) (protoreflect.Value, error) {
nanos |= int32(nanosBz[i])
}
msg.Set(timestampNanosField, protoreflect.ValueOfInt32(nanos))
return protoreflect.ValueOfMessage(msg), nil
return nanos, nil
}
func (t TimestampCodec) Compare(v1, v2 protoreflect.Value) int {
@ -161,11 +194,11 @@ func (t TimestampCodec) IsOrdered() bool {
}
func (t TimestampCodec) FixedBufferSize() int {
return 9
return timestampDurationBufferSize
}
func (t TimestampCodec) ComputeBufferSize(protoreflect.Value) (int, error) {
return 9, nil
return timestampDurationBufferSize, nil
}
// TimestampV0Codec encodes a google.protobuf.Timestamp value as 12 bytes using

View File

@ -0,0 +1,126 @@
package ormfield_test
import (
"bytes"
"testing"
"time"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/types/known/timestamppb"
"gotest.tools/v3/assert"
"github.com/cosmos/cosmos-sdk/orm/encoding/ormfield"
)
func TestTimestamp(t *testing.T) {
t.Parallel()
cdc := ormfield.TimestampCodec{}
t.Run("nil value", func(t *testing.T) {
t.Parallel()
buf := &bytes.Buffer{}
assert.NilError(t, cdc.Encode(protoreflect.Value{}, buf))
assert.Equal(t, 1, len(buf.Bytes()))
val, err := cdc.Decode(buf)
assert.NilError(t, err)
assert.Assert(t, !val.IsValid())
})
t.Run("no nanos", func(t *testing.T) {
t.Parallel()
ts := timestamppb.New(time.Date(2022, 1, 1, 12, 30, 15, 0, time.UTC))
val := protoreflect.ValueOfMessage(ts.ProtoReflect())
buf := &bytes.Buffer{}
assert.NilError(t, cdc.Encode(val, buf))
assert.Equal(t, 6, len(buf.Bytes()))
val2, err := cdc.Decode(buf)
assert.NilError(t, err)
assert.Equal(t, 0, cdc.Compare(val, val2))
})
t.Run("nanos", func(t *testing.T) {
t.Parallel()
ts := timestamppb.New(time.Date(2022, 1, 1, 12, 30, 15, 235809753, time.UTC))
val := protoreflect.ValueOfMessage(ts.ProtoReflect())
buf := &bytes.Buffer{}
assert.NilError(t, cdc.Encode(val, buf))
assert.Equal(t, 9, len(buf.Bytes()))
val2, err := cdc.Decode(buf)
assert.NilError(t, err)
assert.Equal(t, 0, cdc.Compare(val, val2))
})
t.Run("min value", func(t *testing.T) {
t.Parallel()
ts := timestamppb.New(time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC))
val := protoreflect.ValueOfMessage(ts.ProtoReflect())
buf := &bytes.Buffer{}
assert.NilError(t, cdc.Encode(val, buf))
assert.Equal(t, 6, len(buf.Bytes()))
assert.Assert(t, bytes.Equal(buf.Bytes(), []byte{0, 0, 0, 0, 0, 0})) // the minimum value should be all zeros
val2, err := cdc.Decode(buf)
assert.NilError(t, err)
assert.Equal(t, 0, cdc.Compare(val, val2))
})
t.Run("max value", func(t *testing.T) {
t.Parallel()
ts := timestamppb.New(time.Date(9999, 12, 31, 23, 59, 59, 999999999, time.UTC))
val := protoreflect.ValueOfMessage(ts.ProtoReflect())
buf := &bytes.Buffer{}
assert.NilError(t, cdc.Encode(val, buf))
assert.Equal(t, 9, len(buf.Bytes()))
val2, err := cdc.Decode(buf)
assert.NilError(t, err)
assert.Equal(t, 0, cdc.Compare(val, val2))
})
}
func TestTimestampOutOfRange(t *testing.T) {
t.Parallel()
cdc := ormfield.TimestampCodec{}
tt := []struct {
name string
ts *timestamppb.Timestamp
expectErr string
}{
{
name: "before min",
ts: timestamppb.New(time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC)),
expectErr: "timestamp seconds is out of range",
},
{
name: "after max",
ts: timestamppb.New(time.Date(10000, 1, 1, 0, 0, 0, 0, time.UTC)),
expectErr: "timestamp seconds is out of range",
},
{
name: "nanos too small",
ts: &timestamppb.Timestamp{
Seconds: 0,
Nanos: -1,
},
expectErr: "timestamp nanos is out of range",
},
{
name: "nanos too big",
ts: &timestamppb.Timestamp{
Seconds: 0,
Nanos: 1000000000,
},
expectErr: "timestamp nanos is out of range",
},
}
for _, tc := range tt {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
val := protoreflect.ValueOfMessage(tc.ts.ProtoReflect())
buf := &bytes.Buffer{}
err := cdc.Encode(val, buf)
assert.ErrorContains(t, err, tc.expectErr)
})
}
}

View File

@ -7,6 +7,7 @@ import (
ormlist "github.com/cosmos/cosmos-sdk/orm/model/ormlist"
ormtable "github.com/cosmos/cosmos-sdk/orm/model/ormtable"
ormerrors "github.com/cosmos/cosmos-sdk/orm/types/ormerrors"
durationpb "google.golang.org/protobuf/types/known/durationpb"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
)
@ -540,6 +541,138 @@ func NewExampleTimestampTable(db ormtable.Schema) (ExampleTimestampTable, error)
return exampleTimestampTable{table.(ormtable.AutoIncrementTable)}, nil
}
type ExampleDurationTable interface {
Insert(ctx context.Context, exampleDuration *ExampleDuration) error
InsertReturningId(ctx context.Context, exampleDuration *ExampleDuration) (uint64, error)
Update(ctx context.Context, exampleDuration *ExampleDuration) error
Save(ctx context.Context, exampleDuration *ExampleDuration) error
Delete(ctx context.Context, exampleDuration *ExampleDuration) error
Has(ctx context.Context, id uint64) (found bool, err error)
// Get returns nil and an error which responds true to ormerrors.IsNotFound() if the record was not found.
Get(ctx context.Context, id uint64) (*ExampleDuration, error)
List(ctx context.Context, prefixKey ExampleDurationIndexKey, opts ...ormlist.Option) (ExampleDurationIterator, error)
ListRange(ctx context.Context, from, to ExampleDurationIndexKey, opts ...ormlist.Option) (ExampleDurationIterator, error)
DeleteBy(ctx context.Context, prefixKey ExampleDurationIndexKey) error
DeleteRange(ctx context.Context, from, to ExampleDurationIndexKey) error
doNotImplement()
}
type ExampleDurationIterator struct {
ormtable.Iterator
}
func (i ExampleDurationIterator) Value() (*ExampleDuration, error) {
var exampleDuration ExampleDuration
err := i.UnmarshalMessage(&exampleDuration)
return &exampleDuration, err
}
type ExampleDurationIndexKey interface {
id() uint32
values() []interface{}
exampleDurationIndexKey()
}
// primary key starting index..
type ExampleDurationPrimaryKey = ExampleDurationIdIndexKey
type ExampleDurationIdIndexKey struct {
vs []interface{}
}
func (x ExampleDurationIdIndexKey) id() uint32 { return 0 }
func (x ExampleDurationIdIndexKey) values() []interface{} { return x.vs }
func (x ExampleDurationIdIndexKey) exampleDurationIndexKey() {}
func (this ExampleDurationIdIndexKey) WithId(id uint64) ExampleDurationIdIndexKey {
this.vs = []interface{}{id}
return this
}
type ExampleDurationDurIndexKey struct {
vs []interface{}
}
func (x ExampleDurationDurIndexKey) id() uint32 { return 1 }
func (x ExampleDurationDurIndexKey) values() []interface{} { return x.vs }
func (x ExampleDurationDurIndexKey) exampleDurationIndexKey() {}
func (this ExampleDurationDurIndexKey) WithDur(dur *durationpb.Duration) ExampleDurationDurIndexKey {
this.vs = []interface{}{dur}
return this
}
type exampleDurationTable struct {
table ormtable.AutoIncrementTable
}
func (this exampleDurationTable) Insert(ctx context.Context, exampleDuration *ExampleDuration) error {
return this.table.Insert(ctx, exampleDuration)
}
func (this exampleDurationTable) Update(ctx context.Context, exampleDuration *ExampleDuration) error {
return this.table.Update(ctx, exampleDuration)
}
func (this exampleDurationTable) Save(ctx context.Context, exampleDuration *ExampleDuration) error {
return this.table.Save(ctx, exampleDuration)
}
func (this exampleDurationTable) Delete(ctx context.Context, exampleDuration *ExampleDuration) error {
return this.table.Delete(ctx, exampleDuration)
}
func (this exampleDurationTable) InsertReturningId(ctx context.Context, exampleDuration *ExampleDuration) (uint64, error) {
return this.table.InsertReturningPKey(ctx, exampleDuration)
}
func (this exampleDurationTable) Has(ctx context.Context, id uint64) (found bool, err error) {
return this.table.PrimaryKey().Has(ctx, id)
}
func (this exampleDurationTable) Get(ctx context.Context, id uint64) (*ExampleDuration, error) {
var exampleDuration ExampleDuration
found, err := this.table.PrimaryKey().Get(ctx, &exampleDuration, id)
if err != nil {
return nil, err
}
if !found {
return nil, ormerrors.NotFound
}
return &exampleDuration, nil
}
func (this exampleDurationTable) List(ctx context.Context, prefixKey ExampleDurationIndexKey, opts ...ormlist.Option) (ExampleDurationIterator, error) {
it, err := this.table.GetIndexByID(prefixKey.id()).List(ctx, prefixKey.values(), opts...)
return ExampleDurationIterator{it}, err
}
func (this exampleDurationTable) ListRange(ctx context.Context, from, to ExampleDurationIndexKey, opts ...ormlist.Option) (ExampleDurationIterator, error) {
it, err := this.table.GetIndexByID(from.id()).ListRange(ctx, from.values(), to.values(), opts...)
return ExampleDurationIterator{it}, err
}
func (this exampleDurationTable) DeleteBy(ctx context.Context, prefixKey ExampleDurationIndexKey) error {
return this.table.GetIndexByID(prefixKey.id()).DeleteBy(ctx, prefixKey.values()...)
}
func (this exampleDurationTable) DeleteRange(ctx context.Context, from, to ExampleDurationIndexKey) error {
return this.table.GetIndexByID(from.id()).DeleteRange(ctx, from.values(), to.values())
}
func (this exampleDurationTable) doNotImplement() {}
var _ ExampleDurationTable = exampleDurationTable{}
func NewExampleDurationTable(db ormtable.Schema) (ExampleDurationTable, error) {
table := db.GetTable(&ExampleDuration{})
if table == nil {
return nil, ormerrors.TableNotFound.Wrap(string((&ExampleDuration{}).ProtoReflect().Descriptor().FullName()))
}
return exampleDurationTable{table.(ormtable.AutoIncrementTable)}, nil
}
type SimpleExampleTable interface {
Insert(ctx context.Context, simpleExample *SimpleExample) error
Update(ctx context.Context, simpleExample *SimpleExample) error
@ -819,6 +952,7 @@ type TestSchemaStore interface {
ExampleAutoIncrementTableTable() ExampleAutoIncrementTableTable
ExampleSingletonTable() ExampleSingletonTable
ExampleTimestampTable() ExampleTimestampTable
ExampleDurationTable() ExampleDurationTable
SimpleExampleTable() SimpleExampleTable
ExampleAutoIncFieldNameTable() ExampleAutoIncFieldNameTable
@ -830,6 +964,7 @@ type testSchemaStore struct {
exampleAutoIncrementTable ExampleAutoIncrementTableTable
exampleSingleton ExampleSingletonTable
exampleTimestamp ExampleTimestampTable
exampleDuration ExampleDurationTable
simpleExample SimpleExampleTable
exampleAutoIncFieldName ExampleAutoIncFieldNameTable
}
@ -850,6 +985,10 @@ func (x testSchemaStore) ExampleTimestampTable() ExampleTimestampTable {
return x.exampleTimestamp
}
func (x testSchemaStore) ExampleDurationTable() ExampleDurationTable {
return x.exampleDuration
}
func (x testSchemaStore) SimpleExampleTable() SimpleExampleTable {
return x.simpleExample
}
@ -883,6 +1022,11 @@ func NewTestSchemaStore(db ormtable.Schema) (TestSchemaStore, error) {
return nil, err
}
exampleDurationTable, err := NewExampleDurationTable(db)
if err != nil {
return nil, err
}
simpleExampleTable, err := NewSimpleExampleTable(db)
if err != nil {
return nil, err
@ -898,6 +1042,7 @@ func NewTestSchemaStore(db ormtable.Schema) (TestSchemaStore, error) {
exampleAutoIncrementTableTable,
exampleSingletonTable,
exampleTimestampTable,
exampleDurationTable,
simpleExampleTable,
exampleAutoIncFieldNameTable,
}, nil

View File

@ -480,6 +480,69 @@ func (x *ExampleTimestamp) GetTs() *timestamppb.Timestamp {
return nil
}
type ExampleDuration struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
Dur *durationpb.Duration `protobuf:"bytes,3,opt,name=dur,proto3" json:"dur,omitempty"`
}
func (x *ExampleDuration) Reset() {
*x = ExampleDuration{}
if protoimpl.UnsafeEnabled {
mi := &file_testpb_test_schema_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ExampleDuration) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ExampleDuration) ProtoMessage() {}
func (x *ExampleDuration) ProtoReflect() protoreflect.Message {
mi := &file_testpb_test_schema_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ExampleDuration.ProtoReflect.Descriptor instead.
func (*ExampleDuration) Descriptor() ([]byte, []int) {
return file_testpb_test_schema_proto_rawDescGZIP(), []int{4}
}
func (x *ExampleDuration) GetId() uint64 {
if x != nil {
return x.Id
}
return 0
}
func (x *ExampleDuration) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *ExampleDuration) GetDur() *durationpb.Duration {
if x != nil {
return x.Dur
}
return nil
}
type SimpleExample struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@ -493,7 +556,7 @@ type SimpleExample struct {
func (x *SimpleExample) Reset() {
*x = SimpleExample{}
if protoimpl.UnsafeEnabled {
mi := &file_testpb_test_schema_proto_msgTypes[4]
mi := &file_testpb_test_schema_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -506,7 +569,7 @@ func (x *SimpleExample) String() string {
func (*SimpleExample) ProtoMessage() {}
func (x *SimpleExample) ProtoReflect() protoreflect.Message {
mi := &file_testpb_test_schema_proto_msgTypes[4]
mi := &file_testpb_test_schema_proto_msgTypes[5]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -519,7 +582,7 @@ func (x *SimpleExample) ProtoReflect() protoreflect.Message {
// Deprecated: Use SimpleExample.ProtoReflect.Descriptor instead.
func (*SimpleExample) Descriptor() ([]byte, []int) {
return file_testpb_test_schema_proto_rawDescGZIP(), []int{4}
return file_testpb_test_schema_proto_rawDescGZIP(), []int{5}
}
func (x *SimpleExample) GetName() string {
@ -556,7 +619,7 @@ type ExampleAutoIncFieldName struct {
func (x *ExampleAutoIncFieldName) Reset() {
*x = ExampleAutoIncFieldName{}
if protoimpl.UnsafeEnabled {
mi := &file_testpb_test_schema_proto_msgTypes[5]
mi := &file_testpb_test_schema_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -569,7 +632,7 @@ func (x *ExampleAutoIncFieldName) String() string {
func (*ExampleAutoIncFieldName) ProtoMessage() {}
func (x *ExampleAutoIncFieldName) ProtoReflect() protoreflect.Message {
mi := &file_testpb_test_schema_proto_msgTypes[5]
mi := &file_testpb_test_schema_proto_msgTypes[6]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -582,7 +645,7 @@ func (x *ExampleAutoIncFieldName) ProtoReflect() protoreflect.Message {
// Deprecated: Use ExampleAutoIncFieldName.ProtoReflect.Descriptor instead.
func (*ExampleAutoIncFieldName) Descriptor() ([]byte, []int) {
return file_testpb_test_schema_proto_rawDescGZIP(), []int{5}
return file_testpb_test_schema_proto_rawDescGZIP(), []int{6}
}
func (x *ExampleAutoIncFieldName) GetFoo() uint64 {
@ -611,7 +674,7 @@ type ExampleTable_ExampleMessage struct {
func (x *ExampleTable_ExampleMessage) Reset() {
*x = ExampleTable_ExampleMessage{}
if protoimpl.UnsafeEnabled {
mi := &file_testpb_test_schema_proto_msgTypes[7]
mi := &file_testpb_test_schema_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -624,7 +687,7 @@ func (x *ExampleTable_ExampleMessage) String() string {
func (*ExampleTable_ExampleMessage) ProtoMessage() {}
func (x *ExampleTable_ExampleMessage) ProtoReflect() protoreflect.Message {
mi := &file_testpb_test_schema_proto_msgTypes[7]
mi := &file_testpb_test_schema_proto_msgTypes[8]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -727,35 +790,43 @@ var file_testpb_test_schema_proto_rawDesc = []byte{
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
0x6d, 0x70, 0x52, 0x02, 0x74, 0x73, 0x3a, 0x18, 0xf2, 0x9e, 0xd3, 0x8e, 0x03, 0x12, 0x0a, 0x06,
0x0a, 0x02, 0x69, 0x64, 0x10, 0x01, 0x12, 0x06, 0x0a, 0x02, 0x74, 0x73, 0x10, 0x01, 0x18, 0x04,
0x22, 0x7a, 0x0a, 0x0d, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c,
0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x18,
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x12, 0x1d, 0x0a,
0x0a, 0x6e, 0x6f, 0x74, 0x5f, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,
0x09, 0x52, 0x09, 0x6e, 0x6f, 0x74, 0x55, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x3a, 0x1e, 0xf2, 0x9e,
0xd3, 0x8e, 0x03, 0x18, 0x0a, 0x06, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x0c, 0x0a, 0x06,
0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x10, 0x01, 0x18, 0x01, 0x18, 0x05, 0x22, 0x50, 0x0a, 0x17,
0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x41, 0x75, 0x74, 0x6f, 0x49, 0x6e, 0x63, 0x46, 0x69,
0x65, 0x6c, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x66, 0x6f, 0x6f, 0x18, 0x01,
0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x66, 0x6f, 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x62, 0x61, 0x72,
0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x62, 0x61, 0x72, 0x3a, 0x11, 0xf2, 0x9e, 0xd3,
0x8e, 0x03, 0x0b, 0x0a, 0x07, 0x0a, 0x03, 0x66, 0x6f, 0x6f, 0x10, 0x01, 0x18, 0x06, 0x2a, 0x64,
0x0a, 0x04, 0x45, 0x6e, 0x75, 0x6d, 0x12, 0x14, 0x0a, 0x10, 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x55,
0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08,
0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x4f, 0x4e, 0x45, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x45, 0x4e,
0x55, 0x4d, 0x5f, 0x54, 0x57, 0x4f, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x45, 0x4e, 0x55, 0x4d,
0x5f, 0x46, 0x49, 0x56, 0x45, 0x10, 0x05, 0x12, 0x1b, 0x0a, 0x0e, 0x45, 0x4e, 0x55, 0x4d, 0x5f,
0x4e, 0x45, 0x47, 0x5f, 0x54, 0x48, 0x52, 0x45, 0x45, 0x10, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0x01, 0x42, 0x87, 0x01, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x2e, 0x74, 0x65, 0x73,
0x74, 0x70, 0x62, 0x42, 0x0f, 0x54, 0x65, 0x73, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x50,
0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73,
0x2d, 0x73, 0x64, 0x6b, 0x2f, 0x6f, 0x72, 0x6d, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61,
0x6c, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x70, 0x62, 0xa2, 0x02, 0x03, 0x54, 0x58, 0x58, 0xaa, 0x02,
0x06, 0x54, 0x65, 0x73, 0x74, 0x70, 0x62, 0xca, 0x02, 0x06, 0x54, 0x65, 0x73, 0x74, 0x70, 0x62,
0xe2, 0x02, 0x12, 0x54, 0x65, 0x73, 0x74, 0x70, 0x62, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74,
0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x06, 0x54, 0x65, 0x73, 0x74, 0x70, 0x62, 0x62, 0x06,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x22, 0x7d, 0x0a, 0x0f, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x44, 0x75, 0x72, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52,
0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x03, 0x64, 0x75, 0x72, 0x18, 0x03,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52,
0x03, 0x64, 0x75, 0x72, 0x3a, 0x19, 0xf2, 0x9e, 0xd3, 0x8e, 0x03, 0x13, 0x0a, 0x06, 0x0a, 0x02,
0x69, 0x64, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x64, 0x75, 0x72, 0x10, 0x01, 0x18, 0x04, 0x22,
0x7a, 0x0a, 0x0d, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x18, 0x02,
0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x12, 0x1d, 0x0a, 0x0a,
0x6e, 0x6f, 0x74, 0x5f, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
0x52, 0x09, 0x6e, 0x6f, 0x74, 0x55, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x3a, 0x1e, 0xf2, 0x9e, 0xd3,
0x8e, 0x03, 0x18, 0x0a, 0x06, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x0c, 0x0a, 0x06, 0x75,
0x6e, 0x69, 0x71, 0x75, 0x65, 0x10, 0x01, 0x18, 0x01, 0x18, 0x05, 0x22, 0x50, 0x0a, 0x17, 0x45,
0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x41, 0x75, 0x74, 0x6f, 0x49, 0x6e, 0x63, 0x46, 0x69, 0x65,
0x6c, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x66, 0x6f, 0x6f, 0x18, 0x01, 0x20,
0x01, 0x28, 0x04, 0x52, 0x03, 0x66, 0x6f, 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x62, 0x61, 0x72, 0x18,
0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x62, 0x61, 0x72, 0x3a, 0x11, 0xf2, 0x9e, 0xd3, 0x8e,
0x03, 0x0b, 0x0a, 0x07, 0x0a, 0x03, 0x66, 0x6f, 0x6f, 0x10, 0x01, 0x18, 0x06, 0x2a, 0x64, 0x0a,
0x04, 0x45, 0x6e, 0x75, 0x6d, 0x12, 0x14, 0x0a, 0x10, 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x55, 0x4e,
0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x45,
0x4e, 0x55, 0x4d, 0x5f, 0x4f, 0x4e, 0x45, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x45, 0x4e, 0x55,
0x4d, 0x5f, 0x54, 0x57, 0x4f, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x45, 0x4e, 0x55, 0x4d, 0x5f,
0x46, 0x49, 0x56, 0x45, 0x10, 0x05, 0x12, 0x1b, 0x0a, 0x0e, 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x4e,
0x45, 0x47, 0x5f, 0x54, 0x48, 0x52, 0x45, 0x45, 0x10, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x01, 0x42, 0x87, 0x01, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x2e, 0x74, 0x65, 0x73, 0x74,
0x70, 0x62, 0x42, 0x0f, 0x54, 0x65, 0x73, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x50, 0x72,
0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
0x6d, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2d,
0x73, 0x64, 0x6b, 0x2f, 0x6f, 0x72, 0x6d, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c,
0x2f, 0x74, 0x65, 0x73, 0x74, 0x70, 0x62, 0xa2, 0x02, 0x03, 0x54, 0x58, 0x58, 0xaa, 0x02, 0x06,
0x54, 0x65, 0x73, 0x74, 0x70, 0x62, 0xca, 0x02, 0x06, 0x54, 0x65, 0x73, 0x74, 0x70, 0x62, 0xe2,
0x02, 0x12, 0x54, 0x65, 0x73, 0x74, 0x70, 0x62, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61,
0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x06, 0x54, 0x65, 0x73, 0x74, 0x70, 0x62, 0x62, 0x06, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@ -771,32 +842,34 @@ func file_testpb_test_schema_proto_rawDescGZIP() []byte {
}
var file_testpb_test_schema_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_testpb_test_schema_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
var file_testpb_test_schema_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
var file_testpb_test_schema_proto_goTypes = []interface{}{
(Enum)(0), // 0: testpb.Enum
(*ExampleTable)(nil), // 1: testpb.ExampleTable
(*ExampleAutoIncrementTable)(nil), // 2: testpb.ExampleAutoIncrementTable
(*ExampleSingleton)(nil), // 3: testpb.ExampleSingleton
(*ExampleTimestamp)(nil), // 4: testpb.ExampleTimestamp
(*SimpleExample)(nil), // 5: testpb.SimpleExample
(*ExampleAutoIncFieldName)(nil), // 6: testpb.ExampleAutoIncFieldName
nil, // 7: testpb.ExampleTable.MapEntry
(*ExampleTable_ExampleMessage)(nil), // 8: testpb.ExampleTable.ExampleMessage
(*timestamppb.Timestamp)(nil), // 9: google.protobuf.Timestamp
(*durationpb.Duration)(nil), // 10: google.protobuf.Duration
(*ExampleDuration)(nil), // 5: testpb.ExampleDuration
(*SimpleExample)(nil), // 6: testpb.SimpleExample
(*ExampleAutoIncFieldName)(nil), // 7: testpb.ExampleAutoIncFieldName
nil, // 8: testpb.ExampleTable.MapEntry
(*ExampleTable_ExampleMessage)(nil), // 9: testpb.ExampleTable.ExampleMessage
(*timestamppb.Timestamp)(nil), // 10: google.protobuf.Timestamp
(*durationpb.Duration)(nil), // 11: google.protobuf.Duration
}
var file_testpb_test_schema_proto_depIdxs = []int32{
9, // 0: testpb.ExampleTable.ts:type_name -> google.protobuf.Timestamp
10, // 1: testpb.ExampleTable.dur:type_name -> google.protobuf.Duration
10, // 0: testpb.ExampleTable.ts:type_name -> google.protobuf.Timestamp
11, // 1: testpb.ExampleTable.dur:type_name -> google.protobuf.Duration
0, // 2: testpb.ExampleTable.e:type_name -> testpb.Enum
7, // 3: testpb.ExampleTable.map:type_name -> testpb.ExampleTable.MapEntry
8, // 4: testpb.ExampleTable.msg:type_name -> testpb.ExampleTable.ExampleMessage
9, // 5: testpb.ExampleTimestamp.ts:type_name -> google.protobuf.Timestamp
6, // [6:6] is the sub-list for method output_type
6, // [6:6] is the sub-list for method input_type
6, // [6:6] is the sub-list for extension type_name
6, // [6:6] is the sub-list for extension extendee
0, // [0:6] is the sub-list for field type_name
8, // 3: testpb.ExampleTable.map:type_name -> testpb.ExampleTable.MapEntry
9, // 4: testpb.ExampleTable.msg:type_name -> testpb.ExampleTable.ExampleMessage
10, // 5: testpb.ExampleTimestamp.ts:type_name -> google.protobuf.Timestamp
11, // 6: testpb.ExampleDuration.dur:type_name -> google.protobuf.Duration
7, // [7:7] is the sub-list for method output_type
7, // [7:7] is the sub-list for method input_type
7, // [7:7] is the sub-list for extension type_name
7, // [7:7] is the sub-list for extension extendee
0, // [0:7] is the sub-list for field type_name
}
func init() { file_testpb_test_schema_proto_init() }
@ -854,7 +927,7 @@ func file_testpb_test_schema_proto_init() {
}
}
file_testpb_test_schema_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SimpleExample); i {
switch v := v.(*ExampleDuration); i {
case 0:
return &v.state
case 1:
@ -866,6 +939,18 @@ func file_testpb_test_schema_proto_init() {
}
}
file_testpb_test_schema_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SimpleExample); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_testpb_test_schema_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ExampleAutoIncFieldName); i {
case 0:
return &v.state
@ -877,7 +962,7 @@ func file_testpb_test_schema_proto_init() {
return nil
}
}
file_testpb_test_schema_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
file_testpb_test_schema_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ExampleTable_ExampleMessage); i {
case 0:
return &v.state
@ -899,7 +984,7 @@ func file_testpb_test_schema_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_testpb_test_schema_proto_rawDesc,
NumEnums: 1,
NumMessages: 8,
NumMessages: 9,
NumExtensions: 0,
NumServices: 0,
},

View File

@ -107,6 +107,18 @@ message ExampleTimestamp {
google.protobuf.Timestamp ts = 3;
}
message ExampleDuration {
option (cosmos.orm.v1.table) = {
id: 4
primary_key: {fields: "id" auto_increment: true}
index: {id: 1 fields: "dur"}
};
uint64 id = 1;
string name = 2;
google.protobuf.Duration dur = 3;
}
message SimpleExample {
option (cosmos.orm.v1.table) = {
id: 5

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,7 @@ syntax = "proto3";
package testpb;
import "cosmos/base/query/v1beta1/pagination.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/timestamp.proto";
import "testpb/test_schema.proto";
@ -30,6 +31,10 @@ service TestSchemaQueryService {
rpc GetExampleTimestamp(GetExampleTimestampRequest) returns (GetExampleTimestampResponse) {}
// ListExampleTimestamp queries the ExampleTimestamp table using prefix and range queries against defined indexes.
rpc ListExampleTimestamp(ListExampleTimestampRequest) returns (ListExampleTimestampResponse) {}
// Get queries the ExampleDuration table by its primary key.
rpc GetExampleDuration(GetExampleDurationRequest) returns (GetExampleDurationResponse) {}
// ListExampleDuration queries the ExampleDuration table using prefix and range queries against defined indexes.
rpc ListExampleDuration(ListExampleDurationRequest) returns (ListExampleDurationResponse) {}
// Get queries the SimpleExample table by its primary key.
rpc GetSimpleExample(GetSimpleExampleRequest) returns (GetSimpleExampleResponse) {}
// GetSimpleExampleByUnique queries the SimpleExample table by its Unique index
@ -308,6 +313,73 @@ message ListExampleTimestampResponse {
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}
// GetExampleDurationRequest is the TestSchemaQuery/GetExampleDurationRequest request type.
message GetExampleDurationRequest {
// id specifies the value of the id field in the primary key.
uint64 id = 1;
}
// GetExampleDurationResponse is the TestSchemaQuery/GetExampleDurationResponse response type.
message GetExampleDurationResponse {
// value is the response value.
ExampleDuration value = 1;
}
// ListExampleDurationRequest is the TestSchemaQuery/ListExampleDurationRequest request type.
message ListExampleDurationRequest {
// IndexKey specifies the value of an index key to use in prefix and range queries.
message IndexKey {
// key specifies the index key value.
oneof key {
// id specifies the value of the Id index key to use in the query.
Id id = 1;
// dur specifies the value of the Dur index key to use in the query.
Dur dur = 2;
}
message Id {
// id is the value of the id field in the index.
// It can be omitted to query for all valid values of that field in this segment of the index.
optional uint64 id = 1;
}
message Dur {
// dur is the value of the dur field in the index.
// It can be omitted to query for all valid values of that field in this segment of the index.
optional google.protobuf.Duration dur = 1;
}
}
// query specifies the type of query - either a prefix or range query.
oneof query {
// prefix_query specifies the index key value to use for the prefix query.
IndexKey prefix_query = 1;
// range_query specifies the index key from/to values to use for the range query.
RangeQuery range_query = 2;
}
// pagination specifies optional pagination parameters.
cosmos.base.query.v1beta1.PageRequest pagination = 3;
// RangeQuery specifies the from/to index keys for a range query.
message RangeQuery {
// from is the index key to use for the start of the range query.
// To query from the start of an index, specify an index key for that index with empty values.
IndexKey from = 1;
// to is the index key to use for the end of the range query.
// The index key type MUST be the same as the index key type used for from.
// To query from to the end of an index it can be omitted.
IndexKey to = 2;
}
}
// ListExampleDurationResponse is the TestSchemaQuery/ListExampleDurationResponse response type.
message ListExampleDurationResponse {
// values are the results of the query.
repeated ExampleDuration values = 1;
// pagination is the pagination response.
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}
// GetSimpleExampleRequest is the TestSchemaQuery/GetSimpleExampleRequest request type.
message GetSimpleExampleRequest {
// name specifies the value of the name field in the primary key.

View File

@ -40,6 +40,10 @@ type TestSchemaQueryServiceClient interface {
GetExampleTimestamp(ctx context.Context, in *GetExampleTimestampRequest, opts ...grpc.CallOption) (*GetExampleTimestampResponse, error)
// ListExampleTimestamp queries the ExampleTimestamp table using prefix and range queries against defined indexes.
ListExampleTimestamp(ctx context.Context, in *ListExampleTimestampRequest, opts ...grpc.CallOption) (*ListExampleTimestampResponse, error)
// Get queries the ExampleDuration table by its primary key.
GetExampleDuration(ctx context.Context, in *GetExampleDurationRequest, opts ...grpc.CallOption) (*GetExampleDurationResponse, error)
// ListExampleDuration queries the ExampleDuration table using prefix and range queries against defined indexes.
ListExampleDuration(ctx context.Context, in *ListExampleDurationRequest, opts ...grpc.CallOption) (*ListExampleDurationResponse, error)
// Get queries the SimpleExample table by its primary key.
GetSimpleExample(ctx context.Context, in *GetSimpleExampleRequest, opts ...grpc.CallOption) (*GetSimpleExampleResponse, error)
// GetSimpleExampleByUnique queries the SimpleExample table by its Unique index
@ -141,6 +145,24 @@ func (c *testSchemaQueryServiceClient) ListExampleTimestamp(ctx context.Context,
return out, nil
}
func (c *testSchemaQueryServiceClient) GetExampleDuration(ctx context.Context, in *GetExampleDurationRequest, opts ...grpc.CallOption) (*GetExampleDurationResponse, error) {
out := new(GetExampleDurationResponse)
err := c.cc.Invoke(ctx, "/testpb.TestSchemaQueryService/GetExampleDuration", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *testSchemaQueryServiceClient) ListExampleDuration(ctx context.Context, in *ListExampleDurationRequest, opts ...grpc.CallOption) (*ListExampleDurationResponse, error) {
out := new(ListExampleDurationResponse)
err := c.cc.Invoke(ctx, "/testpb.TestSchemaQueryService/ListExampleDuration", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *testSchemaQueryServiceClient) GetSimpleExample(ctx context.Context, in *GetSimpleExampleRequest, opts ...grpc.CallOption) (*GetSimpleExampleResponse, error) {
out := new(GetSimpleExampleResponse)
err := c.cc.Invoke(ctx, "/testpb.TestSchemaQueryService/GetSimpleExample", in, out, opts...)
@ -208,6 +230,10 @@ type TestSchemaQueryServiceServer interface {
GetExampleTimestamp(context.Context, *GetExampleTimestampRequest) (*GetExampleTimestampResponse, error)
// ListExampleTimestamp queries the ExampleTimestamp table using prefix and range queries against defined indexes.
ListExampleTimestamp(context.Context, *ListExampleTimestampRequest) (*ListExampleTimestampResponse, error)
// Get queries the ExampleDuration table by its primary key.
GetExampleDuration(context.Context, *GetExampleDurationRequest) (*GetExampleDurationResponse, error)
// ListExampleDuration queries the ExampleDuration table using prefix and range queries against defined indexes.
ListExampleDuration(context.Context, *ListExampleDurationRequest) (*ListExampleDurationResponse, error)
// Get queries the SimpleExample table by its primary key.
GetSimpleExample(context.Context, *GetSimpleExampleRequest) (*GetSimpleExampleResponse, error)
// GetSimpleExampleByUnique queries the SimpleExample table by its Unique index
@ -252,6 +278,12 @@ func (UnimplementedTestSchemaQueryServiceServer) GetExampleTimestamp(context.Con
func (UnimplementedTestSchemaQueryServiceServer) ListExampleTimestamp(context.Context, *ListExampleTimestampRequest) (*ListExampleTimestampResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListExampleTimestamp not implemented")
}
func (UnimplementedTestSchemaQueryServiceServer) GetExampleDuration(context.Context, *GetExampleDurationRequest) (*GetExampleDurationResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetExampleDuration not implemented")
}
func (UnimplementedTestSchemaQueryServiceServer) ListExampleDuration(context.Context, *ListExampleDurationRequest) (*ListExampleDurationResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListExampleDuration not implemented")
}
func (UnimplementedTestSchemaQueryServiceServer) GetSimpleExample(context.Context, *GetSimpleExampleRequest) (*GetSimpleExampleResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetSimpleExample not implemented")
}
@ -443,6 +475,42 @@ func _TestSchemaQueryService_ListExampleTimestamp_Handler(srv interface{}, ctx c
return interceptor(ctx, in, info, handler)
}
func _TestSchemaQueryService_GetExampleDuration_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetExampleDurationRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(TestSchemaQueryServiceServer).GetExampleDuration(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/testpb.TestSchemaQueryService/GetExampleDuration",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(TestSchemaQueryServiceServer).GetExampleDuration(ctx, req.(*GetExampleDurationRequest))
}
return interceptor(ctx, in, info, handler)
}
func _TestSchemaQueryService_ListExampleDuration_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListExampleDurationRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(TestSchemaQueryServiceServer).ListExampleDuration(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/testpb.TestSchemaQueryService/ListExampleDuration",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(TestSchemaQueryServiceServer).ListExampleDuration(ctx, req.(*ListExampleDurationRequest))
}
return interceptor(ctx, in, info, handler)
}
func _TestSchemaQueryService_GetSimpleExample_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetSimpleExampleRequest)
if err := dec(in); err != nil {
@ -576,6 +644,14 @@ var TestSchemaQueryService_ServiceDesc = grpc.ServiceDesc{
MethodName: "ListExampleTimestamp",
Handler: _TestSchemaQueryService_ListExampleTimestamp_Handler,
},
{
MethodName: "GetExampleDuration",
Handler: _TestSchemaQueryService_GetExampleDuration_Handler,
},
{
MethodName: "ListExampleDuration",
Handler: _TestSchemaQueryService_ListExampleDuration_Handler,
},
{
MethodName: "GetSimpleExample",
Handler: _TestSchemaQueryService_GetSimpleExample_Handler,

View File

@ -84,8 +84,8 @@ var TestFieldSpecs = []TestFieldSpec{
if isNil >= 0.95 { // draw a nil 5% of the time
return nil
}
seconds := rapid.Int64Range(-9999999999, 9999999999).Draw(t, "seconds")
nanos := rapid.Int32Range(0, 999999999).Draw(t, "nanos")
seconds := rapid.Int64Range(ormfield.TimestampSecondsMin, ormfield.TimestampSecondsMax).Draw(t, "seconds")
nanos := rapid.Int32Range(0, ormfield.TimestampNanosMax).Draw(t, "nanos")
return (&timestamppb.Timestamp{
Seconds: seconds,
Nanos: nanos,
@ -95,8 +95,15 @@ var TestFieldSpecs = []TestFieldSpec{
{
"dur",
rapid.Custom(func(t *rapid.T) protoreflect.Message {
seconds := rapid.Int64Range(0, 315576000000).Draw(t, "seconds")
nanos := rapid.Int32Range(0, 999999999).Draw(t, "nanos")
isNil := rapid.Float32().Draw(t, "isNil")
if isNil >= 0.95 { // draw a nil 5% of the time
return nil
}
seconds := rapid.Int64Range(ormfield.DurationNanosMin, ormfield.DurationNanosMax).Draw(t, "seconds")
nanos := rapid.Int32Range(0, ormfield.DurationNanosMax).Draw(t, "nanos")
if seconds < 0 {
nanos = -nanos
}
return (&durationpb.Duration{
Seconds: seconds,
Nanos: nanos,

View File

@ -0,0 +1,103 @@
package ormtable_test
import (
"testing"
"time"
"google.golang.org/protobuf/types/known/durationpb"
"gotest.tools/v3/assert"
"github.com/cosmos/cosmos-sdk/orm/internal/testkv"
"github.com/cosmos/cosmos-sdk/orm/internal/testpb"
"github.com/cosmos/cosmos-sdk/orm/model/ormtable"
)
func TestDurationIndex(t *testing.T) {
table, err := ormtable.Build(ormtable.Options{
MessageType: (&testpb.ExampleDuration{}).ProtoReflect().Type(),
})
assert.NilError(t, err)
backend := testkv.NewDebugBackend(testkv.NewSplitMemBackend(), &testkv.EntryCodecDebugger{
EntryCodec: table,
})
ctx := ormtable.WrapContextDefault(backend)
store, err := testpb.NewExampleDurationTable(table)
assert.NilError(t, err)
neg, err := time.ParseDuration("-1h")
assert.NilError(t, err)
zero, err := time.ParseDuration("0")
assert.NilError(t, err)
pos, err := time.ParseDuration("11000ms")
assert.NilError(t, err)
negPb, zeroPb, posPb := durationpb.New(neg), durationpb.New(zero), durationpb.New(pos)
durOrder := []*durationpb.Duration{negPb, zeroPb, posPb}
assert.NilError(t, store.Insert(ctx, &testpb.ExampleDuration{
Name: "foo",
Dur: negPb,
}))
assert.NilError(t, store.Insert(ctx, &testpb.ExampleDuration{
Name: "bar",
Dur: zeroPb,
}))
assert.NilError(t, store.Insert(ctx, &testpb.ExampleDuration{
Name: "baz",
Dur: posPb,
}))
from, to := testpb.ExampleDurationDurIndexKey{}.WithDur(durationpb.New(neg)),
testpb.ExampleDurationDurIndexKey{}.WithDur(durationpb.New(pos))
it, err := store.ListRange(ctx, from, to)
assert.NilError(t, err)
i := 0
for it.Next() {
v, err := it.Value()
assert.NilError(t, err)
assert.Equal(t, durOrder[i].String(), v.Dur.String())
i++
}
// insert a nil entry
id, err := store.InsertReturningId(ctx, &testpb.ExampleDuration{
Name: "nil",
Dur: nil,
})
assert.NilError(t, err)
res, err := store.Get(ctx, id)
assert.NilError(t, err)
assert.Assert(t, res.Dur == nil)
it, err = store.List(ctx, testpb.ExampleDurationDurIndexKey{})
assert.NilError(t, err)
// make sure nils are ordered last
durOrder = append(durOrder, nil)
i = 0
for it.Next() {
v, err := it.Value()
assert.NilError(t, err)
assert.Assert(t, v != nil)
x := durOrder[i]
if x == nil {
assert.Assert(t, v.Dur == nil)
} else {
assert.Equal(t, x.String(), v.Dur.String())
}
i++
}
it.Close()
// try iterating over just nil timestamps
it, err = store.List(ctx, testpb.ExampleDurationDurIndexKey{}.WithDur(nil))
assert.NilError(t, err)
assert.Assert(t, it.Next())
res, err = it.Value()
assert.NilError(t, err)
assert.Assert(t, res.Dur == nil)
assert.Assert(t, !it.Next())
it.Close()
}

View File

@ -7,9 +7,6 @@ import (
"sort"
"strings"
"testing"
"time"
"google.golang.org/protobuf/types/known/timestamppb"
dbm "github.com/cosmos/cosmos-db"
@ -101,98 +98,6 @@ func TestPaginationLimitCountTotal(t *testing.T) {
assert.Equal(t, uint64(3), pr.Total)
}
func TestTimestampIndex(t *testing.T) {
table, err := ormtable.Build(ormtable.Options{
MessageType: (&testpb.ExampleTimestamp{}).ProtoReflect().Type(),
})
assert.NilError(t, err)
backend := testkv.NewDebugBackend(testkv.NewSplitMemBackend(), &testkv.EntryCodecDebugger{
EntryCodec: table,
Print: func(s string) {
t.Log(s)
},
})
ctx := ormtable.WrapContextDefault(backend)
store, err := testpb.NewExampleTimestampTable(table)
assert.NilError(t, err)
past, err := time.Parse("2006-01-02", "2000-01-01")
assert.NilError(t, err)
middle, err := time.Parse("2006-01-02", "2020-01-01")
assert.NilError(t, err)
future, err := time.Parse("2006-01-02", "2049-01-01")
assert.NilError(t, err)
pastPb, middlePb, futurePb := timestamppb.New(past), timestamppb.New(middle), timestamppb.New(future)
timeOrder := []*timestamppb.Timestamp{pastPb, middlePb, futurePb}
assert.NilError(t, store.Insert(ctx, &testpb.ExampleTimestamp{
Name: "foo",
Ts: pastPb,
}))
assert.NilError(t, store.Insert(ctx, &testpb.ExampleTimestamp{
Name: "bar",
Ts: middlePb,
}))
assert.NilError(t, store.Insert(ctx, &testpb.ExampleTimestamp{
Name: "baz",
Ts: futurePb,
}))
from, to := testpb.ExampleTimestampTsIndexKey{}.WithTs(timestamppb.New(past)), testpb.ExampleTimestampTsIndexKey{}.WithTs(timestamppb.New(future))
it, err := store.ListRange(ctx, from, to)
assert.NilError(t, err)
i := 0
for it.Next() {
v, err := it.Value()
assert.NilError(t, err)
assert.Equal(t, timeOrder[i].String(), v.Ts.String())
i++
}
// insert a nil entry
id, err := store.InsertReturningId(ctx, &testpb.ExampleTimestamp{
Name: "nil",
Ts: nil,
})
assert.NilError(t, err)
res, err := store.Get(ctx, id)
assert.NilError(t, err)
assert.Assert(t, res.Ts == nil)
it, err = store.List(ctx, testpb.ExampleTimestampTsIndexKey{})
assert.NilError(t, err)
// make sure nils are ordered last
timeOrder = append(timeOrder, nil)
i = 0
for it.Next() {
v, err := it.Value()
assert.NilError(t, err)
assert.Assert(t, v != nil)
x := timeOrder[i]
if x == nil {
assert.Assert(t, v.Ts == nil)
} else {
assert.Equal(t, x.String(), v.Ts.String())
}
i++
}
it.Close()
// try iterating over just nil timestamps
it, err = store.List(ctx, testpb.ExampleTimestampTsIndexKey{}.WithTs(nil))
assert.NilError(t, err)
assert.Assert(t, it.Next())
res, err = it.Value()
assert.NilError(t, err)
assert.Assert(t, res.Ts == nil)
assert.Assert(t, !it.Next())
it.Close()
}
// check that the ormkv.Entry's decode and encode to the same bytes
func checkEncodeDecodeEntries(t *testing.T, table ormtable.Table, store kv.ReadonlyStore) {
it, err := store.Iterator(nil, nil)

View File

@ -0,0 +1,103 @@
package ormtable_test
import (
"testing"
"time"
"google.golang.org/protobuf/types/known/timestamppb"
"gotest.tools/v3/assert"
"github.com/cosmos/cosmos-sdk/orm/internal/testkv"
"github.com/cosmos/cosmos-sdk/orm/internal/testpb"
"github.com/cosmos/cosmos-sdk/orm/model/ormtable"
)
func TestTimestampIndex(t *testing.T) {
table, err := ormtable.Build(ormtable.Options{
MessageType: (&testpb.ExampleTimestamp{}).ProtoReflect().Type(),
})
assert.NilError(t, err)
backend := testkv.NewDebugBackend(testkv.NewSplitMemBackend(), &testkv.EntryCodecDebugger{
EntryCodec: table,
})
ctx := ormtable.WrapContextDefault(backend)
store, err := testpb.NewExampleTimestampTable(table)
assert.NilError(t, err)
past, err := time.Parse("2006-01-02", "2000-01-01")
assert.NilError(t, err)
middle, err := time.Parse("2006-01-02", "2020-01-01")
assert.NilError(t, err)
future, err := time.Parse("2006-01-02", "2049-01-01")
assert.NilError(t, err)
pastPb, middlePb, futurePb := timestamppb.New(past), timestamppb.New(middle), timestamppb.New(future)
timeOrder := []*timestamppb.Timestamp{pastPb, middlePb, futurePb}
assert.NilError(t, store.Insert(ctx, &testpb.ExampleTimestamp{
Name: "foo",
Ts: pastPb,
}))
assert.NilError(t, store.Insert(ctx, &testpb.ExampleTimestamp{
Name: "bar",
Ts: middlePb,
}))
assert.NilError(t, store.Insert(ctx, &testpb.ExampleTimestamp{
Name: "baz",
Ts: futurePb,
}))
from, to := testpb.ExampleTimestampTsIndexKey{}.WithTs(timestamppb.New(past)), testpb.ExampleTimestampTsIndexKey{}.WithTs(timestamppb.New(future))
it, err := store.ListRange(ctx, from, to)
assert.NilError(t, err)
i := 0
for it.Next() {
v, err := it.Value()
assert.NilError(t, err)
assert.Equal(t, timeOrder[i].String(), v.Ts.String())
i++
}
// insert a nil entry
id, err := store.InsertReturningId(ctx, &testpb.ExampleTimestamp{
Name: "nil",
Ts: nil,
})
assert.NilError(t, err)
res, err := store.Get(ctx, id)
assert.NilError(t, err)
assert.Assert(t, res.Ts == nil)
it, err = store.List(ctx, testpb.ExampleTimestampTsIndexKey{})
assert.NilError(t, err)
// make sure nils are ordered last
timeOrder = append(timeOrder, nil)
i = 0
for it.Next() {
v, err := it.Value()
assert.NilError(t, err)
assert.Assert(t, v != nil)
x := timeOrder[i]
if x == nil {
assert.Assert(t, v.Ts == nil)
} else {
assert.Equal(t, x.String(), v.Ts.String())
}
i++
}
it.Close()
// try iterating over just nil timestamps
it, err = store.List(ctx, testpb.ExampleTimestampTsIndexKey{}.WithTs(nil))
assert.NilError(t, err)
assert.Assert(t, it.Next())
res, err = it.Value()
assert.NilError(t, err)
assert.Assert(t, res.Ts == nil)
assert.Assert(t, !it.Next())
it.Close()
}