Merge pull request #17745 from influxdata/sgc/issues/17449

feat(storage): New data types for measurement schema gRPC APIs
pull/17792/head
Stuart Carnie 2020-04-17 12:44:25 -07:00 committed by GitHub
commit 602d56f763
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 3056 additions and 1063 deletions

2
go.mod
View File

@ -29,7 +29,7 @@ require (
github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2 // indirect
github.com/glycerine/goconvey v0.0.0-20180728074245-46e3a41ad493 // indirect
github.com/go-chi/chi v4.1.0+incompatible
github.com/gogo/protobuf v1.2.1
github.com/gogo/protobuf v1.3.1
github.com/golang/gddo v0.0.0-20181116215533-9bd4a3295021
github.com/golang/protobuf v1.3.2
github.com/golang/snappy v0.0.1

4
go.sum
View File

@ -137,6 +137,8 @@ github.com/gogo/protobuf v1.2.0 h1:xU6/SpYbvkNYiptHJYEDRseDLvYE7wSqhYYNy0QSUzI=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/gddo v0.0.0-20181116215533-9bd4a3295021 h1:HYV500jCgk+IC68L5sWrLFIWMpaUFfXXpJSAb7XOoBk=
github.com/golang/gddo v0.0.0-20181116215533-9bd4a3295021/go.mod h1:xEhNfoBDX1hzLm2Nf80qUvZ2sVwoMZ8d6IE2SrsQfh4=
github.com/golang/geo v0.0.0-20190916061304-5b978397cfec h1:lJwO/92dFXWeXOZdoGXgptLmNLwynMSHUmU6besqtiw=
@ -279,6 +281,7 @@ github.com/kamilsk/retry v0.0.0-20181229152359-495c1d672c93/go.mod h1:vW4uuVWZOG
github.com/kevinburke/go-bindata v3.11.0+incompatible h1:RcC+GJNmrBHbGaOpQ9MBD8z22rdzlIm0esDRDkyxd4s=
github.com/kevinburke/go-bindata v3.11.0+incompatible/go.mod h1:/pEEZ72flUW2p0yi30bslSp9YqD9pysLxunQDdb2CPM=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
@ -574,6 +577,7 @@ golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=

View File

@ -45,7 +45,7 @@ func (e *Engine) MeasurementTagValues(ctx context.Context, orgID, bucketID influ
// MeasurementTagKeys returns an iterator which enumerates the tag keys for the given
// bucket and measurement, filtered using the optional the predicate and limited to the
//// time range [start, end].
// time range [start, end].
//
// MeasurementTagKeys will always return a StringIterator if there is no error.
//
@ -63,7 +63,7 @@ func (e *Engine) MeasurementTagKeys(ctx context.Context, orgID, bucketID influxd
// MeasurementFields returns an iterator which enumerates the field schema for the given
// bucket and measurement, filtered using the optional the predicate and limited to the
//// time range [start, end].
// time range [start, end].
//
// MeasurementFields will always return a MeasurementFieldsIterator if there is no error.
//

View File

@ -10,6 +10,7 @@ import (
proto "github.com/gogo/protobuf/proto"
io "io"
math "math"
math_bits "math/bits"
)
// Reference imports to suppress errors if they are not otherwise used.
@ -21,7 +22,7 @@ var _ = math.Inf
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
type Node_Type int32
@ -163,7 +164,7 @@ func (m *Node) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Node.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalTo(b)
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
@ -189,34 +190,34 @@ type isNode_Value interface {
}
type Node_StringValue struct {
StringValue string `protobuf:"bytes,3,opt,name=string_value,json=stringValue,proto3,oneof"`
StringValue string `protobuf:"bytes,3,opt,name=string_value,json=stringValue,proto3,oneof" json:"string_value,omitempty"`
}
type Node_BooleanValue struct {
BooleanValue bool `protobuf:"varint,4,opt,name=bool_value,json=boolValue,proto3,oneof"`
BooleanValue bool `protobuf:"varint,4,opt,name=bool_value,json=boolValue,proto3,oneof" json:"bool_value,omitempty"`
}
type Node_IntegerValue struct {
IntegerValue int64 `protobuf:"varint,5,opt,name=int_value,json=intValue,proto3,oneof"`
IntegerValue int64 `protobuf:"varint,5,opt,name=int_value,json=intValue,proto3,oneof" json:"int_value,omitempty"`
}
type Node_UnsignedValue struct {
UnsignedValue uint64 `protobuf:"varint,6,opt,name=uint_value,json=uintValue,proto3,oneof"`
UnsignedValue uint64 `protobuf:"varint,6,opt,name=uint_value,json=uintValue,proto3,oneof" json:"uint_value,omitempty"`
}
type Node_FloatValue struct {
FloatValue float64 `protobuf:"fixed64,7,opt,name=float_value,json=floatValue,proto3,oneof"`
FloatValue float64 `protobuf:"fixed64,7,opt,name=float_value,json=floatValue,proto3,oneof" json:"float_value,omitempty"`
}
type Node_RegexValue struct {
RegexValue string `protobuf:"bytes,8,opt,name=regex_value,json=regexValue,proto3,oneof"`
RegexValue string `protobuf:"bytes,8,opt,name=regex_value,json=regexValue,proto3,oneof" json:"regex_value,omitempty"`
}
type Node_TagRefValue struct {
TagRefValue string `protobuf:"bytes,9,opt,name=tag_ref_value,json=tagRefValue,proto3,oneof"`
TagRefValue string `protobuf:"bytes,9,opt,name=tag_ref_value,json=tagRefValue,proto3,oneof" json:"tag_ref_value,omitempty"`
}
type Node_FieldRefValue struct {
FieldRefValue string `protobuf:"bytes,10,opt,name=field_ref_value,json=fieldRefValue,proto3,oneof"`
FieldRefValue string `protobuf:"bytes,10,opt,name=field_ref_value,json=fieldRefValue,proto3,oneof" json:"field_ref_value,omitempty"`
}
type Node_Logical_ struct {
Logical Node_Logical `protobuf:"varint,11,opt,name=logical,proto3,enum=influxdata.platform.storage.Node_Logical,oneof"`
Logical Node_Logical `protobuf:"varint,11,opt,name=logical,proto3,enum=influxdata.platform.storage.Node_Logical,oneof" json:"logical,omitempty"`
}
type Node_Comparison_ struct {
Comparison Node_Comparison `protobuf:"varint,12,opt,name=comparison,proto3,enum=influxdata.platform.storage.Node_Comparison,oneof"`
Comparison Node_Comparison `protobuf:"varint,12,opt,name=comparison,proto3,enum=influxdata.platform.storage.Node_Comparison,oneof" json:"comparison,omitempty"`
}
func (*Node_StringValue) isNode_Value() {}
@ -321,9 +322,9 @@ func (m *Node) GetComparison() Node_Comparison {
return ComparisonEqual
}
// XXX_OneofFuncs is for the internal use of the proto package.
func (*Node) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) {
return _Node_OneofMarshaler, _Node_OneofUnmarshaler, _Node_OneofSizer, []interface{}{
// XXX_OneofWrappers is for the internal use of the proto package.
func (*Node) XXX_OneofWrappers() []interface{} {
return []interface{}{
(*Node_StringValue)(nil),
(*Node_BooleanValue)(nil),
(*Node_IntegerValue)(nil),
@ -337,174 +338,6 @@ func (*Node) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, f
}
}
func _Node_OneofMarshaler(msg proto.Message, b *proto.Buffer) error {
m := msg.(*Node)
// value
switch x := m.Value.(type) {
case *Node_StringValue:
_ = b.EncodeVarint(3<<3 | proto.WireBytes)
_ = b.EncodeStringBytes(x.StringValue)
case *Node_BooleanValue:
t := uint64(0)
if x.BooleanValue {
t = 1
}
_ = b.EncodeVarint(4<<3 | proto.WireVarint)
_ = b.EncodeVarint(t)
case *Node_IntegerValue:
_ = b.EncodeVarint(5<<3 | proto.WireVarint)
_ = b.EncodeVarint(uint64(x.IntegerValue))
case *Node_UnsignedValue:
_ = b.EncodeVarint(6<<3 | proto.WireVarint)
_ = b.EncodeVarint(uint64(x.UnsignedValue))
case *Node_FloatValue:
_ = b.EncodeVarint(7<<3 | proto.WireFixed64)
_ = b.EncodeFixed64(math.Float64bits(x.FloatValue))
case *Node_RegexValue:
_ = b.EncodeVarint(8<<3 | proto.WireBytes)
_ = b.EncodeStringBytes(x.RegexValue)
case *Node_TagRefValue:
_ = b.EncodeVarint(9<<3 | proto.WireBytes)
_ = b.EncodeStringBytes(x.TagRefValue)
case *Node_FieldRefValue:
_ = b.EncodeVarint(10<<3 | proto.WireBytes)
_ = b.EncodeStringBytes(x.FieldRefValue)
case *Node_Logical_:
_ = b.EncodeVarint(11<<3 | proto.WireVarint)
_ = b.EncodeVarint(uint64(x.Logical))
case *Node_Comparison_:
_ = b.EncodeVarint(12<<3 | proto.WireVarint)
_ = b.EncodeVarint(uint64(x.Comparison))
case nil:
default:
return fmt.Errorf("Node.Value has unexpected type %T", x)
}
return nil
}
func _Node_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) {
m := msg.(*Node)
switch tag {
case 3: // value.string_value
if wire != proto.WireBytes {
return true, proto.ErrInternalBadWireType
}
x, err := b.DecodeStringBytes()
m.Value = &Node_StringValue{x}
return true, err
case 4: // value.bool_value
if wire != proto.WireVarint {
return true, proto.ErrInternalBadWireType
}
x, err := b.DecodeVarint()
m.Value = &Node_BooleanValue{x != 0}
return true, err
case 5: // value.int_value
if wire != proto.WireVarint {
return true, proto.ErrInternalBadWireType
}
x, err := b.DecodeVarint()
m.Value = &Node_IntegerValue{int64(x)}
return true, err
case 6: // value.uint_value
if wire != proto.WireVarint {
return true, proto.ErrInternalBadWireType
}
x, err := b.DecodeVarint()
m.Value = &Node_UnsignedValue{x}
return true, err
case 7: // value.float_value
if wire != proto.WireFixed64 {
return true, proto.ErrInternalBadWireType
}
x, err := b.DecodeFixed64()
m.Value = &Node_FloatValue{math.Float64frombits(x)}
return true, err
case 8: // value.regex_value
if wire != proto.WireBytes {
return true, proto.ErrInternalBadWireType
}
x, err := b.DecodeStringBytes()
m.Value = &Node_RegexValue{x}
return true, err
case 9: // value.tag_ref_value
if wire != proto.WireBytes {
return true, proto.ErrInternalBadWireType
}
x, err := b.DecodeStringBytes()
m.Value = &Node_TagRefValue{x}
return true, err
case 10: // value.field_ref_value
if wire != proto.WireBytes {
return true, proto.ErrInternalBadWireType
}
x, err := b.DecodeStringBytes()
m.Value = &Node_FieldRefValue{x}
return true, err
case 11: // value.logical
if wire != proto.WireVarint {
return true, proto.ErrInternalBadWireType
}
x, err := b.DecodeVarint()
m.Value = &Node_Logical_{Node_Logical(x)}
return true, err
case 12: // value.comparison
if wire != proto.WireVarint {
return true, proto.ErrInternalBadWireType
}
x, err := b.DecodeVarint()
m.Value = &Node_Comparison_{Node_Comparison(x)}
return true, err
default:
return false, nil
}
}
func _Node_OneofSizer(msg proto.Message) (n int) {
m := msg.(*Node)
// value
switch x := m.Value.(type) {
case *Node_StringValue:
n += 1 // tag and wire
n += proto.SizeVarint(uint64(len(x.StringValue)))
n += len(x.StringValue)
case *Node_BooleanValue:
n += 1 // tag and wire
n += 1
case *Node_IntegerValue:
n += 1 // tag and wire
n += proto.SizeVarint(uint64(x.IntegerValue))
case *Node_UnsignedValue:
n += 1 // tag and wire
n += proto.SizeVarint(uint64(x.UnsignedValue))
case *Node_FloatValue:
n += 1 // tag and wire
n += 8
case *Node_RegexValue:
n += 1 // tag and wire
n += proto.SizeVarint(uint64(len(x.RegexValue)))
n += len(x.RegexValue)
case *Node_TagRefValue:
n += 1 // tag and wire
n += proto.SizeVarint(uint64(len(x.TagRefValue)))
n += len(x.TagRefValue)
case *Node_FieldRefValue:
n += 1 // tag and wire
n += proto.SizeVarint(uint64(len(x.FieldRefValue)))
n += len(x.FieldRefValue)
case *Node_Logical_:
n += 1 // tag and wire
n += proto.SizeVarint(uint64(x.Logical))
case *Node_Comparison_:
n += 1 // tag and wire
n += proto.SizeVarint(uint64(x.Comparison))
case nil:
default:
panic(fmt.Sprintf("proto: unexpected type %T in oneof", x))
}
return n
}
type Predicate struct {
Root *Node `protobuf:"bytes,1,opt,name=root,proto3" json:"root,omitempty"`
}
@ -523,7 +356,7 @@ func (m *Predicate) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Predicate.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalTo(b)
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
@ -621,7 +454,7 @@ var fileDescriptor_87cba9804b436f42 = []byte{
func (m *Node) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
@ -629,121 +462,184 @@ func (m *Node) Marshal() (dAtA []byte, err error) {
}
func (m *Node) MarshalTo(dAtA []byte) (int, error) {
var i int
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Node) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.NodeType != 0 {
dAtA[i] = 0x8
i++
i = encodeVarintPredicate(dAtA, i, uint64(m.NodeType))
}
if len(m.Children) > 0 {
for _, msg := range m.Children {
dAtA[i] = 0x12
i++
i = encodeVarintPredicate(dAtA, i, uint64(msg.Size()))
n, err := msg.MarshalTo(dAtA[i:])
if err != nil {
if m.Value != nil {
{
size := m.Value.Size()
i -= size
if _, err := m.Value.MarshalTo(dAtA[i:]); err != nil {
return 0, err
}
i += n
}
}
if m.Value != nil {
nn1, err := m.Value.MarshalTo(dAtA[i:])
if err != nil {
return 0, err
if len(m.Children) > 0 {
for iNdEx := len(m.Children) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.Children[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintPredicate(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x12
}
i += nn1
}
return i, nil
if m.NodeType != 0 {
i = encodeVarintPredicate(dAtA, i, uint64(m.NodeType))
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *Node_StringValue) MarshalTo(dAtA []byte) (int, error) {
i := 0
dAtA[i] = 0x1a
i++
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Node_StringValue) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
i -= len(m.StringValue)
copy(dAtA[i:], m.StringValue)
i = encodeVarintPredicate(dAtA, i, uint64(len(m.StringValue)))
i += copy(dAtA[i:], m.StringValue)
return i, nil
i--
dAtA[i] = 0x1a
return len(dAtA) - i, nil
}
func (m *Node_BooleanValue) MarshalTo(dAtA []byte) (int, error) {
i := 0
dAtA[i] = 0x20
i++
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Node_BooleanValue) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
i--
if m.BooleanValue {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i++
return i, nil
i--
dAtA[i] = 0x20
return len(dAtA) - i, nil
}
func (m *Node_IntegerValue) MarshalTo(dAtA []byte) (int, error) {
i := 0
dAtA[i] = 0x28
i++
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Node_IntegerValue) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
i = encodeVarintPredicate(dAtA, i, uint64(m.IntegerValue))
return i, nil
i--
dAtA[i] = 0x28
return len(dAtA) - i, nil
}
func (m *Node_UnsignedValue) MarshalTo(dAtA []byte) (int, error) {
i := 0
dAtA[i] = 0x30
i++
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Node_UnsignedValue) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
i = encodeVarintPredicate(dAtA, i, uint64(m.UnsignedValue))
return i, nil
i--
dAtA[i] = 0x30
return len(dAtA) - i, nil
}
func (m *Node_FloatValue) MarshalTo(dAtA []byte) (int, error) {
i := 0
dAtA[i] = 0x39
i++
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Node_FloatValue) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
i -= 8
encoding_binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.FloatValue))))
i += 8
return i, nil
i--
dAtA[i] = 0x39
return len(dAtA) - i, nil
}
func (m *Node_RegexValue) MarshalTo(dAtA []byte) (int, error) {
i := 0
dAtA[i] = 0x42
i++
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Node_RegexValue) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
i -= len(m.RegexValue)
copy(dAtA[i:], m.RegexValue)
i = encodeVarintPredicate(dAtA, i, uint64(len(m.RegexValue)))
i += copy(dAtA[i:], m.RegexValue)
return i, nil
i--
dAtA[i] = 0x42
return len(dAtA) - i, nil
}
func (m *Node_TagRefValue) MarshalTo(dAtA []byte) (int, error) {
i := 0
dAtA[i] = 0x4a
i++
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Node_TagRefValue) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
i -= len(m.TagRefValue)
copy(dAtA[i:], m.TagRefValue)
i = encodeVarintPredicate(dAtA, i, uint64(len(m.TagRefValue)))
i += copy(dAtA[i:], m.TagRefValue)
return i, nil
i--
dAtA[i] = 0x4a
return len(dAtA) - i, nil
}
func (m *Node_FieldRefValue) MarshalTo(dAtA []byte) (int, error) {
i := 0
dAtA[i] = 0x52
i++
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Node_FieldRefValue) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
i -= len(m.FieldRefValue)
copy(dAtA[i:], m.FieldRefValue)
i = encodeVarintPredicate(dAtA, i, uint64(len(m.FieldRefValue)))
i += copy(dAtA[i:], m.FieldRefValue)
return i, nil
i--
dAtA[i] = 0x52
return len(dAtA) - i, nil
}
func (m *Node_Logical_) MarshalTo(dAtA []byte) (int, error) {
i := 0
dAtA[i] = 0x58
i++
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Node_Logical_) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
i = encodeVarintPredicate(dAtA, i, uint64(m.Logical))
return i, nil
i--
dAtA[i] = 0x58
return len(dAtA) - i, nil
}
func (m *Node_Comparison_) MarshalTo(dAtA []byte) (int, error) {
i := 0
dAtA[i] = 0x60
i++
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Node_Comparison_) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
i = encodeVarintPredicate(dAtA, i, uint64(m.Comparison))
return i, nil
i--
dAtA[i] = 0x60
return len(dAtA) - i, nil
}
func (m *Predicate) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
@ -751,31 +647,40 @@ func (m *Predicate) Marshal() (dAtA []byte, err error) {
}
func (m *Predicate) MarshalTo(dAtA []byte) (int, error) {
var i int
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Predicate) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.Root != nil {
dAtA[i] = 0xa
i++
i = encodeVarintPredicate(dAtA, i, uint64(m.Root.Size()))
n2, err := m.Root.MarshalTo(dAtA[i:])
if err != nil {
return 0, err
{
size, err := m.Root.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintPredicate(dAtA, i, uint64(size))
}
i += n2
i--
dAtA[i] = 0xa
}
return i, nil
return len(dAtA) - i, nil
}
func encodeVarintPredicate(dAtA []byte, offset int, v uint64) int {
offset -= sovPredicate(v)
base := offset
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return offset + 1
return base
}
func (m *Node) Size() (n int) {
if m == nil {
@ -906,14 +811,7 @@ func (m *Predicate) Size() (n int) {
}
func sovPredicate(x uint64) (n int) {
for {
n++
x >>= 7
if x == 0 {
break
}
}
return n
return (math_bits.Len64(x|1) + 6) / 7
}
func sozPredicate(x uint64) (n int) {
return sovPredicate(uint64((x << 1) ^ uint64((int64(x) >> 63))))
@ -1356,6 +1254,7 @@ func (m *Predicate) Unmarshal(dAtA []byte) error {
func skipPredicate(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
depth := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
@ -1387,10 +1286,8 @@ func skipPredicate(dAtA []byte) (n int, err error) {
break
}
}
return iNdEx, nil
case 1:
iNdEx += 8
return iNdEx, nil
case 2:
var length int
for shift := uint(0); ; shift += 7 {
@ -1411,55 +1308,30 @@ func skipPredicate(dAtA []byte) (n int, err error) {
return 0, ErrInvalidLengthPredicate
}
iNdEx += length
if iNdEx < 0 {
return 0, ErrInvalidLengthPredicate
}
return iNdEx, nil
case 3:
for {
var innerWire uint64
var start int = iNdEx
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowPredicate
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
innerWire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
innerWireType := int(innerWire & 0x7)
if innerWireType == 4 {
break
}
next, err := skipPredicate(dAtA[start:])
if err != nil {
return 0, err
}
iNdEx = start + next
if iNdEx < 0 {
return 0, ErrInvalidLengthPredicate
}
}
return iNdEx, nil
depth++
case 4:
return iNdEx, nil
if depth == 0 {
return 0, ErrUnexpectedEndOfGroupPredicate
}
depth--
case 5:
iNdEx += 4
return iNdEx, nil
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
if iNdEx < 0 {
return 0, ErrInvalidLengthPredicate
}
if depth == 0 {
return iNdEx, nil
}
}
panic("unreachable")
return 0, io.ErrUnexpectedEOF
}
var (
ErrInvalidLengthPredicate = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowPredicate = fmt.Errorf("proto: integer overflow")
ErrInvalidLengthPredicate = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowPredicate = fmt.Errorf("proto: integer overflow")
ErrUnexpectedEndOfGroupPredicate = fmt.Errorf("proto: unexpected end of group")
)

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,6 @@ package influxdata.platform.storage;
option go_package = "datatypes";
import "gogoproto/gogo.proto";
import "google/protobuf/empty.proto";
import "google/protobuf/any.proto";
import "predicate.proto";
@ -175,7 +174,61 @@ message TagValuesRequest {
string tag_key = 4;
}
// Response message for Storage.TagKeys and Storage.TagValues.
// Response message for Storage.TagKeys, Storage.TagValues Storage.MeasurementNames,
// Storage.MeasurementTagKeys and Storage.MeasurementTagValues.
message StringValuesResponse {
repeated bytes values = 1;
}
// MeasurementNamesRequest is the request message for Storage.MeasurementNames.
message MeasurementNamesRequest {
google.protobuf.Any source = 1;
TimestampRange range = 2 [(gogoproto.nullable) = false];
}
// MeasurementTagKeysRequest is the request message for Storage.MeasurementTagKeys.
message MeasurementTagKeysRequest {
google.protobuf.Any source = 1;
string measurement = 2;
TimestampRange range = 3 [(gogoproto.nullable) = false];
Predicate predicate = 4;
}
// MeasurementTagValuesRequest is the request message for Storage.MeasurementTagValues.
message MeasurementTagValuesRequest {
google.protobuf.Any source = 1;
string measurement = 2;
string tag_key = 3;
TimestampRange range = 4 [(gogoproto.nullable) = false];
Predicate predicate = 5;
}
// MeasurementFieldsRequest is the request message for Storage.MeasurementFields.
message MeasurementFieldsRequest {
google.protobuf.Any source = 1;
string measurement = 2;
TimestampRange range = 3 [(gogoproto.nullable) = false];
Predicate predicate = 4;
}
// MeasurementFieldsResponse is the response message for Storage.MeasurementFields.
message MeasurementFieldsResponse {
enum FieldType {
option (gogoproto.goproto_enum_prefix) = false;
FLOAT = 0 [(gogoproto.enumvalue_customname) = "FieldTypeFloat"];
INTEGER = 1 [(gogoproto.enumvalue_customname) = "FieldTypeInteger"];
UNSIGNED = 2 [(gogoproto.enumvalue_customname) = "FieldTypeUnsigned"];
STRING = 3 [(gogoproto.enumvalue_customname) = "FieldTypeString"];
BOOLEAN = 4 [(gogoproto.enumvalue_customname) = "FieldTypeBoolean"];
UNDEFINED = 5 [(gogoproto.enumvalue_customname) = "FieldTypeUndefined"];
}
message MessageField {
string key = 1;
FieldType type = 2;
sfixed64 timestamp = 3;
}
repeated MessageField fields = 1 [(gogoproto.nullable) = false];
}

View File

@ -0,0 +1,28 @@
// Code generated by "stringer -type FieldType"; DO NOT EDIT.
package cursors
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[Float-0]
_ = x[Integer-1]
_ = x[Unsigned-2]
_ = x[String-3]
_ = x[Boolean-4]
_ = x[Undefined-5]
}
const _FieldType_name = "FloatIntegerUnsignedStringBooleanUndefined"
var _FieldType_index = [...]uint8{0, 5, 12, 20, 26, 33, 42}
func (i FieldType) String() string {
if i < 0 || i >= FieldType(len(_FieldType_index)-1) {
return "FieldType(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _FieldType_name[_FieldType_index[i]:_FieldType_index[i+1]]
}

View File

@ -1,3 +1,4 @@
package cursors
//go:generate env GO111MODULE=on go run github.com/benbjohnson/tmpl -data=@arrayvalues.gen.go.tmpldata arrayvalues.gen.go.tmpl
//go:generate stringer -type FieldType

View File

@ -1,5 +1,7 @@
package cursors
import "github.com/influxdata/influxql"
// FieldType represents the primitive field data types available in tsm.
type FieldType int
@ -7,14 +9,82 @@ const (
Float FieldType = iota // means the data type is a float
Integer // means the data type is an integer
Unsigned // means the data type is an unsigned integer
Boolean // means the data type is a boolean
String // means the data type is a string of text
Boolean // means the data type is a boolean
Undefined // means the data type in unknown or undefined
)
var (
fieldTypeToDataTypeMapping = [8]influxql.DataType{
Float: influxql.Float,
Integer: influxql.Integer,
Unsigned: influxql.Unsigned,
String: influxql.String,
Boolean: influxql.Boolean,
Undefined: influxql.Unknown,
6: influxql.Unknown,
7: influxql.Unknown,
}
)
// FieldTypeToDataType returns the equivalent influxql DataType for the field type ft.
// If ft is an invalid FieldType, the results are undefined.
func FieldTypeToDataType(ft FieldType) influxql.DataType {
return fieldTypeToDataTypeMapping[ft&7]
}
// IsLower returns true if the other FieldType has greater precedence than the
// current value. Undefined has the lowest precedence.
func (ft FieldType) IsLower(other FieldType) bool { return other < ft }
type MeasurementField struct {
Key string
Type FieldType
Key string // Key is the name of the field
Type FieldType // Type is field type
Timestamp int64 // Timestamp refers to the maximum timestamp observed for the given field
}
// MeasurementFieldSlice implements sort.Interface and sorts
// the slice from lowest to highest precedence. Use sort.Reverse
// to sort from highest to lowest.
type MeasurementFieldSlice []MeasurementField
func (m MeasurementFieldSlice) Len() int {
return len(m)
}
func (m MeasurementFieldSlice) Less(i, j int) bool {
ii, jj := &m[i], &m[j]
return ii.Key < jj.Key ||
(ii.Key == jj.Key &&
(ii.Timestamp < jj.Timestamp ||
(ii.Timestamp == jj.Timestamp && ii.Type.IsLower(jj.Type))))
}
func (m MeasurementFieldSlice) Swap(i, j int) {
m[i], m[j] = m[j], m[i]
}
// UniqueByKey performs an in-place update of m, removing duplicate elements
// by Key, keeping the first occurrence of each. If the slice is not sorted,
// the behavior of UniqueByKey is undefined.
func (m *MeasurementFieldSlice) UniqueByKey() {
mm := *m
if len(mm) < 2 {
return
}
j := 0
for i := 1; i < len(mm); i++ {
if mm[j].Key != mm[i].Key {
j++
if j != i {
// optimization: skip copy if j == i
mm[j] = mm[i]
}
}
}
*m = mm[:j+1]
}
type MeasurementFields struct {

310
tsdb/cursors/schema_test.go Normal file
View File

@ -0,0 +1,310 @@
package cursors_test
import (
"math/rand"
"sort"
"testing"
"github.com/influxdata/influxdb/v2/pkg/testing/assert"
"github.com/influxdata/influxdb/v2/tsdb/cursors"
)
// Verifies FieldType precedence behavior is equivalent to influxql.DataType#LessThan
func TestFieldTypeDataTypePrecedenceEquivalence(t *testing.T) {
var fieldTypes = []cursors.FieldType{
cursors.Float,
cursors.Integer,
cursors.Unsigned,
cursors.Boolean,
cursors.String,
cursors.Undefined,
}
for _, fta := range fieldTypes {
for _, ftb := range fieldTypes {
if fta == ftb {
continue
}
got := fta.IsLower(ftb)
exp := cursors.FieldTypeToDataType(fta).LessThan(cursors.FieldTypeToDataType(ftb))
assert.Equal(t, got, exp, "failed %s.LessThan(%s)", fta.String(), ftb.String())
}
}
}
// Verifies sorting behavior of MeasurementFieldSlice
func TestMeasurementFieldSliceSort(t *testing.T) {
mfs := func(d ...cursors.MeasurementField) cursors.MeasurementFieldSlice {
return d
}
mf := func(key string, timestamp int64, ft cursors.FieldType) cursors.MeasurementField {
return cursors.MeasurementField{
Key: key,
Type: ft,
Timestamp: timestamp,
}
}
fltF := func(key string, ts int64) cursors.MeasurementField {
return mf(key, ts, cursors.Float)
}
intF := func(key string, ts int64) cursors.MeasurementField {
return mf(key, ts, cursors.Integer)
}
strF := func(key string, ts int64) cursors.MeasurementField {
return mf(key, ts, cursors.String)
}
blnF := func(key string, ts int64) cursors.MeasurementField {
return mf(key, ts, cursors.Boolean)
}
cases := []struct {
name string
in cursors.MeasurementFieldSlice
exp cursors.MeasurementFieldSlice
}{
{
name: "keys:diff types:same ts:same",
in: mfs(
fltF("bbb", 0),
fltF("aaa", 0),
fltF("ccc", 0),
),
exp: mfs(
fltF("aaa", 0),
fltF("bbb", 0),
fltF("ccc", 0),
),
},
{
name: "keys:same types:same ts:diff",
in: mfs(
fltF("aaa", 10),
fltF("ccc", 20),
fltF("aaa", 0),
fltF("ccc", 0),
),
exp: mfs(
fltF("aaa", 0),
fltF("aaa", 10),
fltF("ccc", 0),
fltF("ccc", 20),
),
},
{
name: "keys:same types:diff ts:same",
in: mfs(
strF("aaa", 0),
intF("aaa", 0),
fltF("aaa", 0),
blnF("aaa", 0),
),
exp: mfs(
blnF("aaa", 0),
strF("aaa", 0),
intF("aaa", 0),
fltF("aaa", 0),
),
},
{
name: "keys:same types:diff ts:diff",
in: mfs(
strF("aaa", 20),
intF("aaa", 10),
fltF("aaa", 0),
blnF("aaa", 30),
),
exp: mfs(
fltF("aaa", 0),
intF("aaa", 10),
strF("aaa", 20),
blnF("aaa", 30),
),
},
{
name: "keys:diff types:diff ts:diff",
in: mfs(
intF("ccc", 10),
blnF("fff", 30),
strF("aaa", 20),
fltF("ddd", 0),
),
exp: mfs(
strF("aaa", 20),
intF("ccc", 10),
fltF("ddd", 0),
blnF("fff", 30),
),
},
{
name: "keys:many types:many ts:same",
in: mfs(
intF("ccc", 10),
blnF("fff", 30),
strF("aaa", 20),
fltF("ddd", 0),
fltF("ccc", 10),
strF("fff", 30),
intF("aaa", 20),
blnF("ddd", 0),
),
exp: mfs(
strF("aaa", 20),
intF("aaa", 20),
intF("ccc", 10),
fltF("ccc", 10),
blnF("ddd", 0),
fltF("ddd", 0),
blnF("fff", 30),
strF("fff", 30),
),
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
got := tc.in
// randomize order using fixed seed to
// ensure tests are deterministic on a given platform
rand.Seed(100)
for i := 0; i < 5; i++ {
rand.Shuffle(len(got), func(i, j int) {
got[i], got[j] = got[j], got[i]
})
sort.Sort(got)
assert.Equal(t, got, tc.exp, "failed at index", i)
}
})
}
}
func TestMeasurementFieldSlice_UniqueByKey(t *testing.T) {
mfs := func(d ...cursors.MeasurementField) cursors.MeasurementFieldSlice {
return d
}
mf := func(key string, timestamp int64, ft cursors.FieldType) cursors.MeasurementField {
return cursors.MeasurementField{
Key: key,
Type: ft,
Timestamp: timestamp,
}
}
fltF := func(key string, ts int64) cursors.MeasurementField {
return mf(key, ts, cursors.Float)
}
t.Run("multiple start end", func(t *testing.T) {
got := mfs(
fltF("aaa", 0),
fltF("aaa", 10),
fltF("bbb", 10),
fltF("ccc", 10),
fltF("ccc", 20),
)
exp := mfs(
fltF("aaa", 0),
fltF("bbb", 10),
fltF("ccc", 10),
)
got.UniqueByKey()
assert.Equal(t, got, exp)
})
t.Run("multiple at end", func(t *testing.T) {
got := mfs(
fltF("aaa", 0),
fltF("bbb", 10),
fltF("ccc", 10),
fltF("ccc", 20),
fltF("ccc", 30),
)
exp := mfs(
fltF("aaa", 0),
fltF("bbb", 10),
fltF("ccc", 10),
)
got.UniqueByKey()
assert.Equal(t, got, exp)
})
t.Run("no duplicates many", func(t *testing.T) {
got := mfs(
fltF("aaa", 0),
fltF("bbb", 10),
fltF("ccc", 20),
)
exp := mfs(
fltF("aaa", 0),
fltF("bbb", 10),
fltF("ccc", 20),
)
got.UniqueByKey()
assert.Equal(t, got, exp)
})
t.Run("no duplicates two elements", func(t *testing.T) {
got := mfs(
fltF("aaa", 0),
fltF("bbb", 10),
)
exp := mfs(
fltF("aaa", 0),
fltF("bbb", 10),
)
got.UniqueByKey()
assert.Equal(t, got, exp)
})
t.Run("duplicates one key", func(t *testing.T) {
got := mfs(
fltF("aaa", 0),
fltF("aaa", 10),
fltF("aaa", 10),
fltF("aaa", 10),
fltF("aaa", 10),
fltF("aaa", 10),
)
exp := mfs(
fltF("aaa", 0),
)
got.UniqueByKey()
assert.Equal(t, got, exp)
})
t.Run("one element", func(t *testing.T) {
got := mfs(
fltF("aaa", 0),
)
exp := mfs(
fltF("aaa", 0),
)
got.UniqueByKey()
assert.Equal(t, got, exp)
})
t.Run("empty", func(t *testing.T) {
got := mfs()
exp := mfs()
got.UniqueByKey()
assert.Equal(t, got, exp)
})
}

View File

@ -301,7 +301,7 @@ func (e *Engine) fieldsPredicate(ctx context.Context, orgID influxdb.ID, bucketI
vals := make([]cursors.MeasurementField, 0, len(tsmValues))
for key, val := range tsmValues {
vals = append(vals, cursors.MeasurementField{Key: key, Type: val.typ})
vals = append(vals, cursors.MeasurementField{Key: key, Type: val.typ, Timestamp: val.max})
}
return cursors.NewMeasurementFieldsSliceIteratorWithStats([]cursors.MeasurementFields{{Fields: vals}}, stats), nil
@ -403,7 +403,7 @@ func (e *Engine) fieldsNoPredicate(ctx context.Context, orgID influxdb.ID, bucke
vals := make([]cursors.MeasurementField, 0, len(tsmValues))
for key, val := range tsmValues {
vals = append(vals, cursors.MeasurementField{Key: key, Type: val.typ})
vals = append(vals, cursors.MeasurementField{Key: key, Type: val.typ, Timestamp: val.max})
}
return cursors.NewMeasurementFieldsSliceIteratorWithStats([]cursors.MeasurementFields{{Fields: vals}}, stats), nil

View File

@ -877,7 +877,7 @@ m10,foo=v barS="60" 501
min: 0,
max: 300,
},
exp: []cursors.MeasurementField{{Key: "i", Type: cursors.String}, {Key: "f", Type: cursors.Float}},
exp: []cursors.MeasurementField{{Key: "i", Type: cursors.String, Timestamp: 209}, {Key: "f", Type: cursors.Float, Timestamp: 201}},
expStats: makeStats(12),
},
{
@ -888,7 +888,7 @@ m10,foo=v barS="60" 501
min: 0,
max: 199,
},
exp: []cursors.MeasurementField{{Key: "i", Type: cursors.Integer}, {Key: "f", Type: cursors.Float}},
exp: []cursors.MeasurementField{{Key: "i", Type: cursors.Integer, Timestamp: 109}, {Key: "f", Type: cursors.Float, Timestamp: 109}},
expStats: makeStats(12),
},
{
@ -899,7 +899,7 @@ m10,foo=v barS="60" 501
min: 0,
max: 1000,
},
exp: []cursors.MeasurementField{{Key: "b", Type: cursors.Boolean}},
exp: []cursors.MeasurementField{{Key: "b", Type: cursors.Boolean, Timestamp: 201}},
expStats: makeStats(1),
},
{
@ -910,7 +910,7 @@ m10,foo=v barS="60" 501
min: 0,
max: 199,
},
exp: []cursors.MeasurementField{{Key: "barF", Type: cursors.Float}},
exp: []cursors.MeasurementField{{Key: "barF", Type: cursors.Float, Timestamp: 101}},
expStats: makeStats(1),
},
{
@ -921,7 +921,7 @@ m10,foo=v barS="60" 501
min: 200,
max: 299,
},
exp: []cursors.MeasurementField{{Key: "i", Type: cursors.String}, {Key: "f", Type: cursors.Float}},
exp: []cursors.MeasurementField{{Key: "i", Type: cursors.String, Timestamp: 209}, {Key: "f", Type: cursors.Float, Timestamp: 201}},
expStats: makeStats(6),
},
{
@ -932,7 +932,7 @@ m10,foo=v barS="60" 501
min: 109,
max: 109,
},
exp: []cursors.MeasurementField{{Key: "i", Type: cursors.Integer}, {Key: "f", Type: cursors.Float}},
exp: []cursors.MeasurementField{{Key: "i", Type: cursors.Integer, Timestamp: 109}, {Key: "f", Type: cursors.Float, Timestamp: 109}},
expStats: makeStats(6),
},
{
@ -943,7 +943,7 @@ m10,foo=v barS="60" 501
min: 201,
max: 201,
},
exp: []cursors.MeasurementField{{Key: "i", Type: cursors.Integer}, {Key: "f", Type: cursors.Float}},
exp: []cursors.MeasurementField{{Key: "i", Type: cursors.Integer, Timestamp: 202}, {Key: "f", Type: cursors.Float, Timestamp: 201}},
expStats: makeStats(6),
},
{
@ -954,7 +954,7 @@ m10,foo=v barS="60" 501
min: 202,
max: 202,
},
exp: []cursors.MeasurementField{{Key: "i", Type: cursors.String}},
exp: []cursors.MeasurementField{{Key: "i", Type: cursors.String, Timestamp: 209}},
expStats: makeStats(6),
},
{
@ -990,7 +990,7 @@ m10,foo=v barS="60" 501
max: 300,
expr: `tag10 = 'v10'`,
},
exp: []cursors.MeasurementField{{Key: "i", Type: cursors.Integer}, {Key: "f", Type: cursors.Float}},
exp: []cursors.MeasurementField{{Key: "i", Type: cursors.Integer, Timestamp: 202}, {Key: "f", Type: cursors.Float, Timestamp: 201}},
expStats: makeStats(3),
},
{
@ -1002,7 +1002,7 @@ m10,foo=v barS="60" 501
max: 300,
expr: `tag10 = 'v11'`,
},
exp: []cursors.MeasurementField{{Key: "i", Type: cursors.String}},
exp: []cursors.MeasurementField{{Key: "i", Type: cursors.String, Timestamp: 209}},
expStats: makeStats(3),
},
@ -1017,7 +1017,7 @@ m10,foo=v barS="60" 501
min: 0,
max: 1000,
},
exp: []cursors.MeasurementField{{Key: "barF", Type: cursors.Float}, {Key: "barS", Type: cursors.String}},
exp: []cursors.MeasurementField{{Key: "barF", Type: cursors.Float, Timestamp: 101}, {Key: "barS", Type: cursors.String, Timestamp: 501}},
expStats: makeStats(1),
},