From eab6174388feefc8c6dad3c67f1d5ddc027c6b9d Mon Sep 17 00:00:00 2001 From: reesporte Date: Mon, 6 Jun 2022 09:33:15 -0500 Subject: [PATCH] make pql.Decimal.Value a private big.Int field This ensures that we can't overflow when adding `pql.Decimal`s together. The only place we can possibly overflow is when converting pql.Decimal to an Int64, but that is a risk we have to take. Also, the only place we do this is in our ToRowser. We could maybe change that to strings, so the presentation of data doesn't indicate an overflow, but that is a later decision to make. It will also involve fixing the generate-proto-grpc make command, because that's broken rn. --- client/orm_test.go | 8 +- cmd/import.go | 36 ++++- ctl/import.go | 2 +- encoding/proto/proto.go | 22 +-- executor.go | 36 ++--- executor_test.go | 52 +++---- field_internal_test.go | 16 +- field_test.go | 21 ++- index.go | 16 +- index_test.go | 2 +- pb/public.pb.go | 289 +++++++++++++++++------------------ pb/public.proto | 3 +- pql/ast_test.go | 2 +- pql/decimal.go | 243 ++++++++++++++--------------- pql/decimal_internal_test.go | 133 ++++++++++++++++ pql/decimal_test.go | 184 ++++++---------------- pql/parser_test.go | 12 +- server/grpc.go | 6 +- server/handler_test.go | 26 ++-- server/pg.go | 8 +- server/server_test.go | 4 +- 21 files changed, 599 insertions(+), 522 deletions(-) create mode 100644 pql/decimal_internal_test.go diff --git a/client/orm_test.go b/client/orm_test.go index 57a70f1cc..988f6a4a4 100644 --- a/client/orm_test.go +++ b/client/orm_test.go @@ -1210,11 +1210,11 @@ func compareFieldOptions(t *testing.T, opts *FieldOptions, fieldType FieldType, if cacheSize != opts.CacheSize() { t.Fatalf("%d != %d", cacheSize, opts.CacheSize()) } - if min != opts.Min() { - t.Fatalf("%d != %d", min, opts.Min()) + if !min.EqualTo(opts.Min()) { + t.Fatalf("%v != %v", min, opts.Min()) } - if max != opts.Max() { - t.Fatalf("%d != %d", max, opts.Max()) + if !max.EqualTo(opts.Max()) { + t.Fatalf("%v != %v", max, opts.Max()) } if foreignIndex != opts.ForeignIndex() { t.Fatalf("%s != %s", foreignIndex, opts.ForeignIndex()) diff --git a/cmd/import.go b/cmd/import.go index 52f28a4aa..2f589fcf8 100644 --- a/cmd/import.go +++ b/cmd/import.go @@ -3,15 +3,45 @@ package cmd import ( "context" + "fmt" "io" + "strconv" pilosa "github.com/molecula/featurebase/v3" "github.com/molecula/featurebase/v3/ctl" + "github.com/molecula/featurebase/v3/pql" "github.com/spf13/cobra" ) var Importer *ctl.ImportCommand +// DecimalFlagValue is used to set the unexported value field in a decimal. It also +// fulfills the flag.Value interface. +type DecimalFlagValue struct { + dec *pql.Decimal +} + +func (dfv *DecimalFlagValue) String() string { + return fmt.Sprintf("%v", dfv.dec.Value()) +} + +func (dfv *DecimalFlagValue) Set(s string) error { + i, err := strconv.ParseInt(s, 10, 64) + if err != nil { + return err + } + if dfv.dec == nil { + d := pql.NewDecimal(0, 0) + dfv.dec = &d + } + dfv.dec.SetValue(i) + return nil +} + +func (dfv *DecimalFlagValue) Type() string { + return fmt.Sprintf("%T", int64(0)) +} + // newImportCommand runs the FeatureBase import subcommand for ingesting bulk data. func newImportCommand(stdin io.Reader, stdout, stderr io.Writer) *cobra.Command { Importer = ctl.NewImportCommand(stdin, stdout, stderr) @@ -34,6 +64,8 @@ omitted. If it is present then its format should be YYYY-MM-DDTHH:MM. }, } + fieldMin := DecimalFlagValue{dec: &Importer.FieldOptions.Min} + fieldMax := DecimalFlagValue{dec: &Importer.FieldOptions.Max} flags := importCmd.Flags() flags.StringVarP(&Importer.Host, "host", "", "localhost:10101", "host:port of FeatureBase.") flags.StringVarP(&Importer.Index, "index", "i", "", "FeatureBase index to import into.") @@ -41,8 +73,8 @@ omitted. If it is present then its format should be YYYY-MM-DDTHH:MM. flags.BoolVar(&Importer.IndexOptions.Keys, "index-keys", false, "Specify keys=true when creating an index") flags.BoolVar(&Importer.FieldOptions.Keys, "field-keys", false, "Specify keys=true when creating a field") flags.StringVar(&Importer.FieldOptions.Type, "field-type", "", "Specify the field type when creating a field. One of: set, int, decimal, time, bool, mutex") - flags.Int64Var(&Importer.FieldOptions.Min.Value, "field-min", 0, "Specify the minimum for an int field on creation") // TODO: noting that decimal field min/max are not supported here. - flags.Int64Var(&Importer.FieldOptions.Max.Value, "field-max", 0, "Specify the maximum for an int field on creation") + flags.Var(&fieldMin, "field-min", "Specify the minimum for an int field on creation") // TODO: noting that decimal field min/max are not supported here. + flags.Var(&fieldMax, "field-max", "Specify the maximum for an int field on creation") flags.StringVar(&Importer.FieldOptions.CacheType, "field-cache-type", pilosa.CacheTypeRanked, "Specify the cache type for a set field on creation. One of: none, lru, ranked") flags.Uint32Var(&Importer.FieldOptions.CacheSize, "field-cache-size", 50000, "Specify the cache size for a set field on creation") flags.Var(&Importer.FieldOptions.TimeQuantum, "field-time-quantum", "Specify the time quantum for a time field on creation. One of: D, DH, H, M, MD, MDH, Y, YM, YMD, YMDH") diff --git a/ctl/import.go b/ctl/import.go index 8d44fd98e..963aaaf52 100644 --- a/ctl/import.go +++ b/ctl/import.go @@ -102,7 +102,7 @@ func (cmd *ImportCommand) Run(ctx context.Context) error { // set the correct type for the field if cmd.FieldOptions.TimeQuantum != "" { cmd.FieldOptions.Type = pilosa.FieldTypeTime - } else if cmd.FieldOptions.Min != pql.NewDecimal(0, 0) || cmd.FieldOptions.Max != pql.NewDecimal(0, 0) { + } else if !cmd.FieldOptions.Min.EqualTo(pql.NewDecimal(0, 0)) || !cmd.FieldOptions.Max.EqualTo(pql.NewDecimal(0, 0)) { cmd.FieldOptions.Type = pilosa.FieldTypeInt } else { cmd.FieldOptions.Type = pilosa.FieldTypeSet diff --git a/encoding/proto/proto.go b/encoding/proto/proto.go index c8e1d0a04..81f15effd 100644 --- a/encoding/proto/proto.go +++ b/encoding/proto/proto.go @@ -704,8 +704,8 @@ func (s Serializer) encodeFieldOptions(o *pilosa.FieldOptions) *pb.FieldOptions Type: o.Type, CacheType: o.CacheType, CacheSize: o.CacheSize, - Min: &pb.Decimal{Value: o.Min.Value, Scale: o.Min.Scale}, - Max: &pb.Decimal{Value: o.Max.Value, Scale: o.Max.Scale}, + Min: s.encodeDecimal(&o.Min), + Max: s.encodeDecimal(&o.Max), Base: o.Base, Scale: o.Scale, BitDepth: uint64(o.BitDepth), @@ -1139,8 +1139,11 @@ func (s Serializer) decodeFieldOptions(options *pb.FieldOptions, m *pilosa.Field } func (s Serializer) decodeDecimal(d *pb.Decimal, m *pql.Decimal) { - m.Value = d.Value - m.Scale = d.Scale + // err should always be nil, unless there's a problem with the standard library + err := m.GobDecode(d.Gob) + if err != nil { + panic(err) + } } func (s Serializer) decodeNodes(a []*pb.Node, m []*topology.Node) { @@ -1743,10 +1746,9 @@ func (s Serializer) decodeDecimalStruct(pb *pb.Decimal) *pql.Decimal { if pb == nil { return nil } - return &pql.Decimal{ - Value: pb.Value, - Scale: pb.Scale, - } + d := &pql.Decimal{} + s.decodeDecimal(pb, d) + return d } func (s Serializer) encodeSignedRow(r pilosa.SignedRow) *pb.SignedRow { @@ -1962,9 +1964,9 @@ func (s Serializer) encodeDecimal(p *pql.Decimal) *pb.Decimal { if p == nil { return nil } + gob, _ := p.GobEncode() // ignore err on purpose return &pb.Decimal{ - Value: p.Value, - Scale: p.Scale, + Gob: gob, } } diff --git a/executor.go b/executor.go index 34eaffe66..6e46ad5b6 100644 --- a/executor.go +++ b/executor.go @@ -928,9 +928,8 @@ func (e *executor) executeFieldValueCallShard(ctx context.Context, qcx *Qcx, fie if field.Type() == FieldTypeInt { other.Val = value } else if field.Type() == FieldTypeDecimal { - other.DecimalVal = &pql.Decimal{ - Value: value, - Scale: field.Options().Scale} + dec := pql.NewDecimal(value, field.Options().Scale) + other.DecimalVal = &dec other.FloatVal = 0 other.Val = 0 } else if field.Type() == FieldTypeTimestamp { @@ -1076,9 +1075,8 @@ func (e *executor) executeSum(ctx context.Context, qcx *Qcx, index string, c *pq return ValCount{}, newNotFoundError(ErrFieldNotFound, fieldName) } if field.Type() == FieldTypeDecimal { - other.DecimalVal = &pql.Decimal{ - Value: other.Val, - Scale: field.Options().Scale} + dec := pql.NewDecimal(other.Val, field.Options().Scale) + other.DecimalVal = &dec other.FloatVal = 0 other.Val = 0 } @@ -1892,7 +1890,8 @@ func (e *executor) executeSumCountShard(ctx context.Context, qcx *Qcx, index str } if field.Type() == FieldTypeDecimal { out.FloatVal = float64(int64(vsum)+(int64(vcount)*bsig.Base)) / math.Pow(10, float64(bsig.Scale)) - out.DecimalVal = &pql.Decimal{Value: (int64(vsum) + (int64(vcount) * bsig.Base)), Scale: bsig.Scale} + dec := pql.NewDecimal((int64(vsum) + (int64(vcount) * bsig.Base)), bsig.Scale) + out.DecimalVal = &dec } return out, nil } @@ -3001,11 +3000,7 @@ func (e *executor) executeGroupBy(ctx context.Context, qcx *Qcx, index string, c if err := ctx.Err(); err != nil { return err } - merged, err := mergeGroupCounts(other, findGroupCounts(v), limit) - if err != nil { - return err - } - return merged + return mergeGroupCounts(other, findGroupCounts(v), limit) } // Get full result set. other, err := e.mapReduce(ctx, index, shards, c, opt, mapFn, reduceFn) @@ -3411,7 +3406,7 @@ func (g *GroupCount) Clone() (r *GroupCount) { // mergeGroupCounts merges two slices of GroupCounts throwing away any that go // beyond the limit. It assume that the two slices are sorted by the row ids in // the fields of the group counts. It may modify its arguments. -func mergeGroupCounts(a, b []GroupCount, limit int) ([]GroupCount, error) { +func mergeGroupCounts(a, b []GroupCount, limit int) []GroupCount { if limit > len(a)+len(b) { limit = len(a) + len(b) } @@ -3426,10 +3421,7 @@ func mergeGroupCounts(a, b []GroupCount, limit int) ([]GroupCount, error) { a[i].Count += b[j].Count a[i].Agg += b[j].Agg if a[i].DecimalAgg != nil && b[j].DecimalAgg != nil { - sum, ok := pql.AddDecimal(*a[i].DecimalAgg, *b[j].DecimalAgg) - if !ok { - return nil, fmt.Errorf("cannot add %s and %s, decimal overflow", a[i].DecimalAgg, b[j].DecimalAgg) - } + sum := pql.AddDecimal(*a[i].DecimalAgg, *b[j].DecimalAgg) a[i].DecimalAgg = &sum } ret = append(ret, a[i]) @@ -3446,7 +3438,7 @@ func mergeGroupCounts(a, b []GroupCount, limit int) ([]GroupCount, error) { for ; j < len(b) && len(ret) < limit; j++ { ret = append(ret, b[j]) } - return ret, nil + return ret } // Compare is used in ordering two GroupCount objects. @@ -3981,10 +3973,12 @@ func (t ExtractedTable) ToRows(callback func(*proto.RowResponse) error) error { }, } case pql.Decimal: + rValue := r.Value() + rValuePtr := &rValue col = &proto.ColumnResponse{ ColumnVal: &proto.ColumnResponse_DecimalVal{ DecimalVal: &proto.Decimal{ - Value: r.Value, + Value: rValuePtr.Int64(), Scale: r.Scale, }, }, @@ -7670,10 +7664,12 @@ func (v ValCount) ToRows(callback func(*proto.RowResponse) error) error { {Name: "value", Datatype: "decimal"}, {Name: "count", Datatype: "int64"}, } + vValue := v.DecimalVal.Value() + vValuePtr := &vValue if err := callback(&proto.RowResponse{ Headers: ci, Columns: []*proto.ColumnResponse{ - {ColumnVal: &proto.ColumnResponse_DecimalVal{DecimalVal: &proto.Decimal{Value: v.DecimalVal.Value, Scale: v.DecimalVal.Scale}}}, + {ColumnVal: &proto.ColumnResponse_DecimalVal{DecimalVal: &proto.Decimal{Value: vValuePtr.Int64(), Scale: v.DecimalVal.Scale}}}, {ColumnVal: &proto.ColumnResponse_Int64Val{Int64Val: v.Count}}, }}); err != nil { return errors.Wrap(err, "calling callback") diff --git a/executor_test.go b/executor_test.go index e2ce939fb..eba87d87a 100644 --- a/executor_test.go +++ b/executor_test.go @@ -2269,31 +2269,31 @@ func TestExecutor_Execute_MinMax(t *testing.T) { }{ { 2, - pql.Decimal{Value: 1, Scale: -1}, - pql.Decimal{Value: 2, Scale: -1}, - pql.Decimal{Value: 115, Scale: 1}, - pql.Decimal{Value: 1150, Scale: 2}, + pql.NewDecimal(1, -1), + pql.NewDecimal(2, -1), + pql.NewDecimal(115, 1), + pql.NewDecimal(1150, 2), }, { 2, - pql.Decimal{Value: -1, Scale: -1}, - pql.Decimal{Value: 2, Scale: -1}, - pql.Decimal{Value: 115, Scale: 1}, - pql.Decimal{Value: 1150, Scale: 2}, + pql.NewDecimal(-1, -1), + pql.NewDecimal(2, -1), + pql.NewDecimal(115, 1), + pql.NewDecimal(1150, 2), }, { 2, - pql.Decimal{Value: -1, Scale: -1}, - pql.Decimal{Value: 2, Scale: -1}, - pql.Decimal{Value: -95, Scale: 1}, - pql.Decimal{Value: -950, Scale: 2}, + pql.NewDecimal(-1, -1), + pql.NewDecimal(2, -1), + pql.NewDecimal(-95, 1), + pql.NewDecimal(-950, 2), }, { 2, - pql.Decimal{Value: -2, Scale: -1}, - pql.Decimal{Value: -1, Scale: -1}, - pql.Decimal{Value: -115, Scale: 1}, - pql.Decimal{Value: -1150, Scale: 2}, + pql.NewDecimal(-2, -1), + pql.NewDecimal(-1, -1), + pql.NewDecimal(-115, 1), + pql.NewDecimal(-1150, 2), }, } // This extra field exists to make there be shards which are present, @@ -2849,7 +2849,7 @@ func TestExecutor_Execute_Sum(t *testing.T) { t.Run("NoFilter", func(t *testing.T) { if result, err := c.GetNode(0).API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `Sum(field=dec)`}); err != nil { t.Fatal(err) - } else if !reflect.DeepEqual(result.Results[0], pilosa.ValCount{DecimalVal: &pql.Decimal{Value: 700007, Scale: 3}, Count: 3}) { + } else if !reflect.DeepEqual(result.Results[0], pilosa.ValCount{DecimalVal: pql.NewDecimal(700007, 3).Clone(), Count: 3}) { t.Fatalf("unexpected result: %s", spew.Sdump(result)) } }) @@ -2857,7 +2857,7 @@ func TestExecutor_Execute_Sum(t *testing.T) { t.Run("WithFilter", func(t *testing.T) { if result, err := c.GetNode(0).API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `Sum(Row(x=0), field=dec)`}); err != nil { t.Fatal(err) - } else if !reflect.DeepEqual(result.Results[0], pilosa.ValCount{DecimalVal: &pql.Decimal{Value: 500005, Scale: 3}, Count: 2}) { + } else if !reflect.DeepEqual(result.Results[0], pilosa.ValCount{DecimalVal: pql.NewDecimal(500005, 3).Clone(), Count: 2}) { t.Fatalf("unexpected result: %s", spew.Sdump(result)) } }) @@ -2865,7 +2865,7 @@ func TestExecutor_Execute_Sum(t *testing.T) { t.Run("NoFilter", func(t *testing.T) { if result, err := c.GetNode(0).API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `Sum(dec)`}); err != nil { t.Fatal(err) - } else if !reflect.DeepEqual(result.Results[0], pilosa.ValCount{DecimalVal: &pql.Decimal{Value: 700007, Scale: 3}, Count: 3}) { + } else if !reflect.DeepEqual(result.Results[0], pilosa.ValCount{DecimalVal: pql.NewDecimal(700007, 3).Clone(), Count: 3}) { t.Fatalf("unexpected result: %s", spew.Sdump(result)) } }) @@ -2873,7 +2873,7 @@ func TestExecutor_Execute_Sum(t *testing.T) { t.Run("WithFilter", func(t *testing.T) { if result, err := c.GetNode(0).API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `Sum(dec, Row(x=0))`}); err != nil { t.Fatal(err) - } else if !reflect.DeepEqual(result.Results[0], pilosa.ValCount{DecimalVal: &pql.Decimal{Value: 500005, Scale: 3}, Count: 2}) { + } else if !reflect.DeepEqual(result.Results[0], pilosa.ValCount{DecimalVal: pql.NewDecimal(500005, 3).Clone(), Count: 2}) { t.Fatalf("unexpected result: %s", spew.Sdump(result)) } }) @@ -4036,7 +4036,7 @@ func TestExecutor_Execute_FieldValue(t *testing.T) { } else { switch exp := test.expVal.(type) { case pql.Decimal: - if *vc.DecimalVal != exp { + if !vc.DecimalVal.EqualTo(exp) { t.Fatalf("test %d on node%d expected pql.Decimal(%s), but got: %s", i, n, exp, vc.DecimalVal) } case int64: @@ -5358,15 +5358,15 @@ func TestExecutor_GroupByStrings(t *testing.T) { { query: "GroupBy(Rows(generals), aggregate=Sum(field=dv))", expected: []pilosa.GroupCount{ - {Group: []pilosa.FieldRow{{Field: "generals", RowID: 1, RowKey: "r1"}}, Count: 5, Agg: 2775, DecimalAgg: &pql.Decimal{Value: 2775, Scale: 2}}, - {Group: []pilosa.FieldRow{{Field: "generals", RowID: 2, RowKey: "r2"}}, Count: 5, Agg: 3220, DecimalAgg: &pql.Decimal{Value: 3220, Scale: 2}}, + {Group: []pilosa.FieldRow{{Field: "generals", RowID: 1, RowKey: "r1"}}, Count: 5, Agg: 2775, DecimalAgg: pql.NewDecimal(2775, 2).Clone()}, + {Group: []pilosa.FieldRow{{Field: "generals", RowID: 2, RowKey: "r2"}}, Count: 5, Agg: 3220, DecimalAgg: pql.NewDecimal(3220, 2).Clone()}, }, }, { query: "GroupBy(Rows(generals), aggregate=Sum(field=ndv))", expected: []pilosa.GroupCount{ - {Group: []pilosa.FieldRow{{Field: "generals", RowID: 1, RowKey: "r1"}}, Count: 5, Agg: -2775, DecimalAgg: &pql.Decimal{Value: -2775, Scale: 1}}, - {Group: []pilosa.FieldRow{{Field: "generals", RowID: 2, RowKey: "r2"}}, Count: 5, Agg: -3220, DecimalAgg: &pql.Decimal{Value: -3220, Scale: 1}}, + {Group: []pilosa.FieldRow{{Field: "generals", RowID: 1, RowKey: "r1"}}, Count: 5, Agg: -2775, DecimalAgg: pql.NewDecimal(-2775, 1).Clone()}, + {Group: []pilosa.FieldRow{{Field: "generals", RowID: 2, RowKey: "r2"}}, Count: 5, Agg: -3220, DecimalAgg: pql.NewDecimal(-3220, 1).Clone()}, }, }, { @@ -8663,7 +8663,7 @@ func TestToRows(t *testing.T) { if e != nil { t.Fatal("Shouldn't be err ", e) } - v.DecimalVal = &pql.Decimal{Value: 1, Scale: 1} + v.DecimalVal = pql.NewDecimal(1, 1).Clone() e = v.ToRows(func(*proto.RowResponse) error { return nil }) diff --git a/field_internal_test.go b/field_internal_test.go index 01f0bed0d..04c996129 100644 --- a/field_internal_test.go +++ b/field_internal_test.go @@ -754,29 +754,29 @@ func TestDecimalField_MinMaxForShard(t *testing.T) { name: "single", columnIDs: []uint64{1}, values: []float64{10.1}, - expMax: ValCount{Val: 10100, DecimalVal: &pql.Decimal{Value: 10100, Scale: 3}, Count: 1}, - expMin: ValCount{Val: 10100, DecimalVal: &pql.Decimal{Value: 10100, Scale: 3}, Count: 1}, + expMax: ValCount{Val: 10100, DecimalVal: pql.NewDecimal(10100, 3).Clone(), Count: 1}, + expMin: ValCount{Val: 10100, DecimalVal: pql.NewDecimal(10100, 3).Clone(), Count: 1}, }, { name: "twovals", columnIDs: []uint64{1, 2}, values: []float64{10.1, 20.2}, - expMax: ValCount{Val: 20200, DecimalVal: &pql.Decimal{Value: 20200, Scale: 3}, Count: 1}, - expMin: ValCount{Val: 10100, DecimalVal: &pql.Decimal{Value: 10100, Scale: 3}, Count: 1}, + expMax: ValCount{Val: 20200, DecimalVal: pql.NewDecimal(20200, 3).Clone(), Count: 1}, + expMin: ValCount{Val: 10100, DecimalVal: pql.NewDecimal(10100, 3).Clone(), Count: 1}, }, { name: "multiplecounts", columnIDs: []uint64{1, 2, 3, 4, 5}, values: []float64{10.1, 20.2, 10.1, 10.1, 20.2}, - expMax: ValCount{Val: 20200, DecimalVal: &pql.Decimal{Value: 20200, Scale: 3}, Count: 2}, - expMin: ValCount{Val: 10100, DecimalVal: &pql.Decimal{Value: 10100, Scale: 3}, Count: 3}, + expMax: ValCount{Val: 20200, DecimalVal: pql.NewDecimal(20200, 3).Clone(), Count: 2}, + expMin: ValCount{Val: 10100, DecimalVal: pql.NewDecimal(10100, 3).Clone(), Count: 3}, }, { name: "middlevals", columnIDs: []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, values: []float64{10.1, 20.2, 10.1, 10.1, 20.2, 11, 12, 11, 13, 11}, - expMax: ValCount{Val: 20200, DecimalVal: &pql.Decimal{Value: 20200, Scale: 3}, Count: 2}, - expMin: ValCount{Val: 10100, DecimalVal: &pql.Decimal{Value: 10100, Scale: 3}, Count: 3}, + expMax: ValCount{Val: 20200, DecimalVal: pql.NewDecimal(20200, 3).Clone(), Count: 2}, + expMin: ValCount{Val: 10100, DecimalVal: pql.NewDecimal(10100, 3).Clone(), Count: 3}, }, } { t.Run(test.name+strconv.Itoa(i), func(t *testing.T) { diff --git a/field_test.go b/field_test.go index 635ab7e7c..1c602416a 100644 --- a/field_test.go +++ b/field_test.go @@ -279,10 +279,23 @@ func TestField_ClearValue(t *testing.T) { func TestFieldInfoMarshal(t *testing.T) { f := &pilosa.FieldInfo{Name: "timestamp", CreatedAt: 1649270079233541000, - Options: pilosa.FieldOptions{Base: 0, BitDepth: 0x0, Min: pql.Decimal{Value: -4294967296, - Scale: 0}, Max: pql.Decimal{Value: 4294967296, Scale: 0}, Scale: 0, Keys: false, - NoStandardView: false, CacheType: "", Type: "timestamp", TimeUnit: "s", - TimeQuantum: "", ForeignIndex: "", TTL: 0}, Cardinality: (*uint64)(nil)} + Options: pilosa.FieldOptions{ + Base: 0, + BitDepth: 0x0, + Min: pql.NewDecimal(-4294967296, 0), + Max: pql.NewDecimal(4294967296, 0), + Scale: 0, + Keys: false, + NoStandardView: false, + CacheType: "", + Type: "timestamp", + TimeUnit: "s", + TimeQuantum: "", + ForeignIndex: "", + TTL: 0, + }, + Cardinality: (*uint64)(nil), + } a, err := json.Marshal(f) if err != nil { t.Fatalf("unexpected error marshalling index info, %v", err) diff --git a/index.go b/index.go index 1c4bb268a..21c85b7fd 100644 --- a/index.go +++ b/index.go @@ -4,6 +4,7 @@ package pilosa import ( "context" "fmt" + "math/big" "os" "path/filepath" "sort" @@ -651,12 +652,15 @@ func (i *Index) CreateFieldIfNotExistsWithOptions(name string, opt *FieldOptions if opt != nil && (opt.Type == FieldTypeInt || opt.Type == FieldTypeTimestamp) { min, max := pql.MinMax(0) // ensure the provided bounds are valid - if opt.Max.Value == 0 { + zero := big.NewInt(0) + maxv := opt.Max.Value() + if maxv.Cmp(zero) == 0 { opt.Max = max } else if max.LessThan(opt.Max) { opt.Max = max } - if opt.Min.Value == 0 { + minv := opt.Min.Value() + if minv.Cmp(zero) == 0 { opt.Min = min } else if min.GreaterThan(opt.Min) { opt.Min = min @@ -665,14 +669,18 @@ func (i *Index) CreateFieldIfNotExistsWithOptions(name string, opt *FieldOptions // added for backward compatablity with old schemas if opt != nil && opt.Type == FieldTypeDecimal { min, max := pql.MinMax(opt.Scale) + zero := big.NewInt(0) + // ensure the provided bounds are valid - if opt.Max.Value == 0 { + maxv := opt.Max.Value() + if maxv.Cmp(zero) == 0 { opt.Max = max } else if max.LessThan(opt.Max) { opt.Max = max } - if opt.Min.Value == 0 { + minv := opt.Min.Value() + if minv.Cmp(zero) == 0 { opt.Min = min } else if min.GreaterThan(opt.Min) { opt.Min = min diff --git a/index_test.go b/index_test.go index a979cb487..cf4955a67 100644 --- a/index_test.go +++ b/index_test.go @@ -203,7 +203,7 @@ func TestIndex_CreateField(t *testing.T) { t.Run("DecimalField", func(t *testing.T) { index := test.MustOpenIndex(t) - _, err := index.CreateField("f", pilosa.OptFieldTypeDecimal(1, pql.Decimal{Value: -1}, pql.Decimal{Value: 1}), pilosa.OptFieldKeys()) + _, err := index.CreateField("f", pilosa.OptFieldTypeDecimal(1, pql.NewDecimal(-1, 0), pql.NewDecimal(1, 0)), pilosa.OptFieldKeys()) if errors.Cause(err) != pilosa.ErrDecimalFieldWithKeys { t.Fatal("decimal field cannot be created with keys=true") } diff --git a/pb/public.pb.go b/pb/public.pb.go index 818b26892..f4bab252a 100644 --- a/pb/public.pb.go +++ b/pb/public.pb.go @@ -1243,8 +1243,7 @@ func (m *ValCount) GetTimestampVal() string { } type Decimal struct { - Value int64 `protobuf:"varint,1,opt,name=Value,proto3" json:"Value,omitempty"` - Scale int64 `protobuf:"varint,2,opt,name=Scale,proto3" json:"Scale,omitempty"` + Gob []byte `protobuf:"bytes,1,opt,name=Gob,proto3" json:"Gob,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1283,18 +1282,11 @@ func (m *Decimal) XXX_DiscardUnknown() { var xxx_messageInfo_Decimal proto.InternalMessageInfo -func (m *Decimal) GetValue() int64 { +func (m *Decimal) GetGob() []byte { if m != nil { - return m.Value + return m.Gob } - return 0 -} - -func (m *Decimal) GetScale() int64 { - if m != nil { - return m.Scale - } - return 0 + return nil } type DistinctTimestamp struct { @@ -2657,113 +2649,113 @@ func init() { func init() { proto.RegisterFile("public.proto", fileDescriptor_413a91106d7bcce8) } var fileDescriptor_413a91106d7bcce8 = []byte{ - // 1694 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x58, 0x5b, 0x6f, 0x23, 0x49, - 0x15, 0x4e, 0x5f, 0x7c, 0x3b, 0x76, 0x9c, 0xa4, 0x36, 0xbb, 0xf4, 0x0e, 0x59, 0xe3, 0x6d, 0xa1, - 0xc5, 0x4b, 0x50, 0x56, 0x18, 0x18, 0xa1, 0x91, 0x60, 0x14, 0xc7, 0x19, 0x62, 0xcd, 0x24, 0x33, - 0x94, 0x43, 0xe0, 0x61, 0x5e, 0x3a, 0x76, 0xe1, 0x69, 0xd1, 0x76, 0x9b, 0xee, 0xf6, 0x38, 0xf9, - 0x01, 0x08, 0x7e, 0x02, 0x6f, 0xfc, 0x1a, 0x04, 0x6f, 0xf0, 0xc8, 0x23, 0x1a, 0xde, 0xf8, 0x15, - 0xe8, 0x9c, 0xaa, 0xea, 0x9b, 0x9d, 0xd1, 0x68, 0xc4, 0x5b, 0x9f, 0x4b, 0x9d, 0xaa, 0xf3, 0x9d, - 0xab, 0x0d, 0xad, 0xe5, 0xea, 0x36, 0xf0, 0x27, 0x27, 0xcb, 0x28, 0x4c, 0x42, 0x66, 0x2e, 0x6f, - 0xdd, 0x7b, 0xb0, 0x78, 0xb8, 0x66, 0x0e, 0xd4, 0xce, 0xc2, 0x60, 0x35, 0x5f, 0xc4, 0x8e, 0xd1, - 0xb5, 0x7a, 0x36, 0xd7, 0x24, 0x63, 0x60, 0x3f, 0x17, 0xf7, 0xb1, 0x63, 0x75, 0xad, 0x5e, 0x83, - 0xd3, 0x37, 0x6a, 0xf3, 0xd0, 0x8b, 0xfc, 0xc5, 0xcc, 0xb1, 0xbb, 0x46, 0xaf, 0xc5, 0x35, 0xc9, - 0x0e, 0xa1, 0x32, 0x5a, 0x4c, 0xc5, 0x9d, 0x53, 0xe9, 0x1a, 0xbd, 0x06, 0x97, 0x04, 0x72, 0x9f, - 0xf9, 0x22, 0x98, 0x3a, 0x55, 0xc9, 0x25, 0xc2, 0xed, 0x41, 0x83, 0x87, 0xeb, 0x4b, 0x2f, 0x89, - 0xfc, 0x3b, 0xf6, 0x6d, 0xb0, 0x79, 0xb8, 0x96, 0xb7, 0x37, 0xfb, 0xb5, 0x93, 0xe5, 0xed, 0x09, - 0x0f, 0xd7, 0x9c, 0x98, 0xee, 0x29, 0x34, 0xc6, 0xfe, 0x6c, 0x21, 0xa6, 0xf8, 0xd4, 0xcf, 0xc1, - 0x7a, 0x15, 0xa2, 0xa2, 0x91, 0x57, 0x44, 0x1e, 0x8a, 0xae, 0xc4, 0xcc, 0x31, 0x4b, 0xa2, 0x2b, - 0x31, 0x73, 0x7f, 0x0a, 0x6d, 0x1e, 0xae, 0x47, 0x53, 0xb1, 0x48, 0xfc, 0xdf, 0xfa, 0x22, 0x22, - 0xc7, 0xd2, 0x1b, 0x6d, 0x79, 0x51, 0xea, 0xac, 0x99, 0x39, 0xeb, 0x3e, 0x82, 0xea, 0x68, 0xf8, - 0xc2, 0x8f, 0x13, 0xb6, 0x0f, 0xd6, 0x68, 0xa8, 0x0f, 0xe0, 0xa7, 0x7b, 0x06, 0x07, 0xe7, 0x77, - 0x49, 0xe4, 0x4d, 0x12, 0x31, 0x1d, 0x0d, 0x25, 0x64, 0xac, 0x0d, 0xe6, 0x68, 0x48, 0xef, 0xb3, - 0xb9, 0x39, 0x1a, 0xb2, 0x0e, 0xd8, 0x37, 0x5e, 0x20, 0x8d, 0x36, 0xfb, 0x80, 0xcf, 0x92, 0x06, - 0x39, 0xf1, 0xdd, 0xd7, 0x05, 0x23, 0x0a, 0x8f, 0xcf, 0xa0, 0x4a, 0x28, 0xc9, 0xeb, 0x1a, 0x5c, - 0x51, 0xec, 0x9b, 0x2c, 0x50, 0xd2, 0xde, 0xa7, 0x68, 0x6f, 0xe3, 0x11, 0x69, 0xfc, 0xdc, 0x2f, - 0xa0, 0xf6, 0x5c, 0xdc, 0xd3, 0xfb, 0xb5, 0x77, 0x46, 0xce, 0xbb, 0x7f, 0x18, 0xf0, 0x49, 0x7a, - 0xfa, 0xda, 0xbb, 0x0d, 0xc4, 0x8d, 0x17, 0xac, 0x04, 0xeb, 0x68, 0x5f, 0x8d, 0xe2, 0x9b, 0x2f, - 0x76, 0xc8, 0x73, 0xf6, 0x65, 0x8a, 0x14, 0x2a, 0x34, 0x51, 0x41, 0x5d, 0x73, 0xb1, 0xa3, 0xb2, - 0xe4, 0x08, 0xea, 0x83, 0xf1, 0x88, 0xcc, 0x39, 0x56, 0xd7, 0xe8, 0x59, 0x17, 0x3b, 0x3c, 0xe5, - 0xb0, 0x47, 0x50, 0xbb, 0x5c, 0x25, 0xe2, 0x6e, 0x34, 0xa4, 0x1c, 0xb2, 0x2f, 0x76, 0xb8, 0x66, - 0xe0, 0x49, 0xfa, 0x7c, 0x2e, 0xee, 0x65, 0x22, 0xe1, 0x49, 0xcd, 0x61, 0x87, 0x60, 0x0f, 0xc2, - 0x30, 0xa0, 0x64, 0xaa, 0xe3, 0x6d, 0x48, 0x0d, 0x6a, 0x50, 0x21, 0xc3, 0xee, 0x1d, 0x1c, 0x16, - 0x1d, 0x52, 0x61, 0x61, 0x60, 0xa1, 0x3d, 0x43, 0xd9, 0x43, 0x82, 0xed, 0x53, 0xa8, 0x4c, 0x75, - 0x3f, 0x06, 0xeb, 0x1b, 0xa8, 0x92, 0x19, 0x99, 0xf0, 0xcd, 0xfe, 0xb7, 0x0a, 0xf0, 0x66, 0x00, - 0x71, 0xa5, 0x36, 0x68, 0x10, 0xbe, 0x2f, 0xa3, 0xd1, 0xd0, 0xfd, 0x59, 0x19, 0x4a, 0x8a, 0x19, - 0xc2, 0x7e, 0xe5, 0xcd, 0x85, 0xbc, 0x99, 0xd3, 0x37, 0xf2, 0xae, 0xef, 0x97, 0x82, 0xae, 0x6e, - 0x70, 0xfa, 0x76, 0x57, 0xd0, 0x2e, 0x1e, 0xc7, 0xc7, 0xe4, 0x92, 0x60, 0xeb, 0x63, 0x48, 0x9e, - 0x66, 0x47, 0xbf, 0x9c, 0x1d, 0xce, 0xe6, 0x89, 0x72, 0x82, 0xfc, 0x1c, 0xec, 0x57, 0x9e, 0x1f, - 0x6d, 0xa4, 0xed, 0xbe, 0xc4, 0xcb, 0xa2, 0x17, 0x5a, 0x12, 0xf8, 0xca, 0x59, 0xb8, 0x5a, 0x24, - 0x12, 0x30, 0x2e, 0x09, 0xf7, 0x29, 0x34, 0xf0, 0xbc, 0xf4, 0xf5, 0x48, 0x1a, 0x53, 0x79, 0x53, - 0xc7, 0xdb, 0x91, 0xe6, 0xf2, 0x8a, 0xb4, 0x0f, 0x98, 0xf9, 0x3e, 0x30, 0x00, 0x40, 0x69, 0x2c, - 0x2d, 0x74, 0xa0, 0x42, 0x94, 0x72, 0x39, 0x33, 0x21, 0xd9, 0x0f, 0xd8, 0xf8, 0x02, 0xfb, 0x4e, - 0xf2, 0xf8, 0xc7, 0x28, 0x96, 0x19, 0x87, 0x2f, 0xb0, 0xb8, 0xca, 0x89, 0x10, 0xea, 0x12, 0xa8, - 0x70, 0x9d, 0x19, 0x30, 0x72, 0x06, 0x90, 0x8b, 0xfd, 0x61, 0xa8, 0x7d, 0x23, 0x02, 0xab, 0x90, - 0x87, 0xeb, 0x0c, 0x06, 0x45, 0xb1, 0xef, 0xe8, 0x5b, 0x6c, 0xf2, 0xb3, 0x41, 0xf5, 0x81, 0xf7, - 0xeb, 0x0b, 0x7f, 0x03, 0xf0, 0x8b, 0x28, 0x5c, 0x2d, 0x09, 0x22, 0xe6, 0x42, 0x85, 0x28, 0xe5, - 0x53, 0x0b, 0xd5, 0xf5, 0x7b, 0xb8, 0x14, 0x6d, 0x07, 0x17, 0x83, 0x70, 0x3a, 0x9b, 0xc9, 0xf2, - 0xe1, 0xf8, 0xe9, 0xfe, 0xc5, 0x80, 0xfa, 0x8d, 0x17, 0xa4, 0xe2, 0x1b, 0x2f, 0x50, 0xbe, 0xe2, - 0x67, 0xd1, 0x8c, 0xa5, 0xcd, 0x3c, 0x82, 0xfa, 0xb3, 0x20, 0xf4, 0x12, 0x54, 0x46, 0x5b, 0x06, - 0x4f, 0x69, 0x76, 0x0c, 0x30, 0x14, 0x13, 0x7f, 0xee, 0x05, 0x28, 0xb5, 0xb3, 0x7a, 0x56, 0x5c, - 0x9e, 0x13, 0x33, 0x17, 0x5a, 0xd7, 0xfe, 0x5c, 0xc4, 0x89, 0x37, 0x5f, 0xa2, 0xba, 0x6c, 0xf3, - 0x05, 0x9e, 0xfb, 0x13, 0xa8, 0xa9, 0x13, 0xdb, 0xa3, 0x81, 0xdc, 0xf1, 0xc4, 0x0b, 0x84, 0x7e, - 0x23, 0x11, 0xee, 0x53, 0x38, 0x18, 0xfa, 0x71, 0xe2, 0x2f, 0x26, 0x49, 0x6a, 0x0e, 0x03, 0xa0, - 0xca, 0x51, 0xb5, 0x41, 0x49, 0xa5, 0x35, 0x65, 0x66, 0x35, 0xe5, 0xfe, 0xd5, 0x80, 0xd6, 0x2f, - 0x57, 0x22, 0xba, 0xe7, 0xe2, 0xf7, 0x2b, 0x11, 0x27, 0x78, 0x0f, 0xd1, 0x3a, 0xd2, 0x44, 0xa0, - 0xc9, 0xf1, 0x1b, 0x2f, 0x9a, 0xca, 0x12, 0xb1, 0xb9, 0xa2, 0x28, 0xd6, 0x62, 0x1e, 0x26, 0x82, - 0x9c, 0xaa, 0x73, 0x45, 0xb1, 0x63, 0x68, 0x9d, 0xcf, 0x6f, 0xc5, 0x74, 0x2a, 0xa6, 0x43, 0x2f, - 0xf1, 0x9c, 0x7a, 0x71, 0x42, 0x15, 0x84, 0xec, 0xbb, 0xb0, 0xfb, 0x2a, 0x12, 0xd7, 0x91, 0xb7, - 0x88, 0x03, 0x2f, 0x11, 0x53, 0xa7, 0x41, 0xb6, 0x8a, 0x4c, 0x76, 0x04, 0x8d, 0x4b, 0xef, 0xee, - 0x52, 0xcc, 0xc3, 0xe8, 0xde, 0x01, 0x02, 0x21, 0x63, 0xb8, 0x2f, 0x60, 0x57, 0xb9, 0x11, 0x2f, - 0xc3, 0x45, 0x2c, 0x30, 0xca, 0xe7, 0x51, 0xa4, 0xbc, 0xc0, 0x4f, 0xf6, 0x35, 0xd4, 0xb8, 0x88, - 0x57, 0x41, 0xa2, 0xeb, 0x7c, 0x0f, 0x9f, 0xa3, 0x4f, 0xad, 0x82, 0x84, 0x6b, 0xb9, 0xfb, 0xdf, - 0x0a, 0x34, 0x73, 0x82, 0xb4, 0xf3, 0x60, 0xf7, 0xdc, 0x95, 0x9d, 0x07, 0xe7, 0x26, 0x0f, 0xd7, - 0x1b, 0x23, 0x15, 0xab, 0xa5, 0x05, 0xc6, 0x95, 0x4a, 0x49, 0xe3, 0x2a, 0x2b, 0x4e, 0x6b, 0x7b, - 0x71, 0xe2, 0x1a, 0xf1, 0xc6, 0x5b, 0xcc, 0xc4, 0x94, 0x12, 0xa9, 0xce, 0x35, 0xc9, 0x7a, 0x59, - 0xd6, 0x12, 0xbe, 0xaa, 0x0a, 0x34, 0x8f, 0x67, 0x39, 0x2d, 0x6b, 0x0e, 0x87, 0x4f, 0x4d, 0xc6, - 0x47, 0x52, 0xec, 0x31, 0xb4, 0x5f, 0x06, 0xd3, 0xac, 0xaa, 0x62, 0x15, 0x89, 0x36, 0xda, 0xc9, - 0xd8, 0xbc, 0xa4, 0xc5, 0x9e, 0x94, 0x27, 0x3f, 0xc5, 0xa4, 0xd9, 0x67, 0xca, 0xcf, 0x9c, 0x84, - 0x97, 0x77, 0x84, 0xe3, 0xdc, 0xe2, 0x41, 0x81, 0x6a, 0xf6, 0x77, 0xf1, 0x58, 0xca, 0xe4, 0xb9, - 0xc5, 0xe4, 0x24, 0xdf, 0xc7, 0x9c, 0x26, 0x69, 0xb7, 0x35, 0x42, 0x92, 0xcb, 0xf3, 0x9d, 0xee, - 0x38, 0xd7, 0x38, 0x9d, 0x56, 0x66, 0x3c, 0x65, 0xf2, 0x5c, 0x63, 0x3d, 0xdb, 0xb2, 0x24, 0x38, - 0xbb, 0x74, 0xa8, 0xbc, 0x01, 0x48, 0x21, 0xdf, 0xb2, 0x54, 0x3c, 0x29, 0x4f, 0x18, 0xa7, 0x9d, - 0x41, 0x51, 0x94, 0xf0, 0xf2, 0x2c, 0x3a, 0xce, 0x6d, 0x6b, 0xce, 0x5e, 0xf6, 0xda, 0x94, 0xc9, - 0x73, 0xdb, 0xdc, 0x0f, 0xa1, 0x99, 0x0f, 0xd4, 0x3e, 0xa9, 0xef, 0x15, 0x03, 0x15, 0xf3, 0xbc, - 0x0e, 0x3a, 0xb8, 0x51, 0xfe, 0xce, 0x41, 0xe6, 0xe0, 0x86, 0x90, 0x6f, 0xea, 0xbb, 0x7f, 0x33, - 0x61, 0x77, 0x34, 0x5f, 0x86, 0x51, 0x92, 0xeb, 0x01, 0x72, 0x21, 0x35, 0xb6, 0x2e, 0xa4, 0x66, - 0x69, 0x06, 0x50, 0x2f, 0xa0, 0x16, 0x69, 0x73, 0x49, 0xe4, 0xf2, 0xd1, 0x2e, 0xe4, 0xe3, 0x11, - 0x34, 0xe4, 0x08, 0x45, 0x51, 0x85, 0x44, 0x19, 0x43, 0xae, 0xc8, 0x6b, 0x5a, 0x91, 0x6a, 0xd4, - 0xb9, 0x34, 0xc9, 0x3a, 0x00, 0x52, 0x8d, 0x84, 0x75, 0x12, 0xe6, 0x38, 0x28, 0x4f, 0x1d, 0x8a, - 0x9d, 0x6a, 0xd7, 0xea, 0x59, 0x3c, 0xc7, 0x61, 0x5f, 0x41, 0x9b, 0x9c, 0x38, 0x8b, 0x04, 0x36, - 0x93, 0xd3, 0x84, 0xf2, 0xd9, 0xe2, 0x25, 0x2e, 0xea, 0x91, 0x5b, 0x99, 0x9e, 0xec, 0x34, 0x25, - 0x2e, 0x4d, 0x8c, 0x40, 0x78, 0x11, 0x65, 0x6c, 0x9d, 0x4b, 0xc2, 0xfd, 0x97, 0x09, 0x4c, 0x22, - 0x29, 0xd7, 0x9d, 0xff, 0x1b, 0x9c, 0xef, 0x87, 0xad, 0x08, 0x4e, 0x6d, 0x03, 0x9c, 0x6c, 0x1e, - 0x48, 0x60, 0xf4, 0x3c, 0xe8, 0x42, 0x53, 0x0f, 0x34, 0x14, 0x22, 0xaa, 0x06, 0xcf, 0xb3, 0x70, - 0x72, 0x8d, 0x13, 0xfc, 0x8d, 0xa2, 0x54, 0x1a, 0x64, 0xbb, 0xc0, 0xdb, 0x02, 0x2d, 0x7c, 0x20, - 0xb4, 0xcd, 0xf7, 0x43, 0xdb, 0xca, 0x43, 0xfb, 0x47, 0x03, 0x5a, 0xa7, 0x49, 0x38, 0xf7, 0x27, - 0x5c, 0x4c, 0xc2, 0x68, 0xfa, 0x30, 0xa8, 0x12, 0x3e, 0x33, 0x0f, 0x5f, 0x0f, 0xac, 0xd1, 0xdb, - 0x48, 0xf5, 0xdf, 0xcf, 0x68, 0xef, 0xd8, 0x88, 0x12, 0x47, 0x15, 0xf6, 0x25, 0x98, 0xa3, 0x88, - 0x72, 0xb6, 0xd9, 0x3f, 0xc8, 0x14, 0xb5, 0x8e, 0x39, 0x8a, 0xdc, 0x1f, 0xc0, 0xa1, 0x7c, 0x88, - 0x16, 0xa9, 0x81, 0x73, 0x08, 0x95, 0xf3, 0x28, 0x0a, 0xf5, 0xc8, 0x91, 0x04, 0x2e, 0xd6, 0xe9, - 0x0c, 0xc3, 0x60, 0x7c, 0x4c, 0x4e, 0x6c, 0xfb, 0x35, 0xd9, 0x85, 0xe6, 0x55, 0x98, 0xfc, 0x3a, - 0xf2, 0x13, 0x6a, 0x49, 0x72, 0x70, 0xe4, 0x59, 0xee, 0xd7, 0xf0, 0x69, 0xe9, 0xe6, 0x6c, 0x32, - 0x62, 0x1a, 0x59, 0xd9, 0x2f, 0xb2, 0x31, 0x7c, 0x92, 0xaa, 0x8e, 0x86, 0x1f, 0xf5, 0xc6, 0x4d, - 0xa3, 0xdf, 0xcf, 0x79, 0x4e, 0x46, 0xd5, 0xf5, 0x5b, 0xbc, 0x71, 0x07, 0xe0, 0x28, 0x34, 0xe5, - 0x4f, 0x62, 0xf5, 0x82, 0x1b, 0x5f, 0xac, 0x1f, 0xfa, 0x25, 0x40, 0x6b, 0x85, 0x49, 0x3f, 0xa4, - 0xe9, 0xdb, 0xfd, 0x93, 0x09, 0x87, 0xdb, 0x8c, 0x64, 0x09, 0x65, 0xe4, 0x12, 0x8a, 0xf5, 0xa1, - 0xf2, 0xd6, 0x17, 0x6b, 0xbd, 0x0b, 0x1c, 0xe5, 0x82, 0xbd, 0xf1, 0x06, 0x2e, 0x55, 0xb1, 0x90, - 0x4e, 0x27, 0x89, 0x1f, 0x2e, 0xf4, 0x66, 0x2b, 0x29, 0xbc, 0x61, 0x10, 0x84, 0x93, 0xdf, 0xc9, - 0x1f, 0x65, 0x5c, 0x12, 0x5b, 0x0a, 0xa3, 0xf2, 0x81, 0x85, 0x51, 0xdd, 0x5a, 0x18, 0x3d, 0xd8, - 0xfb, 0xd5, 0x72, 0xea, 0x25, 0xe2, 0xfc, 0xce, 0x8f, 0x13, 0xb1, 0x98, 0x08, 0xa7, 0x46, 0x1e, - 0x95, 0xd9, 0xee, 0x1f, 0x0c, 0xd8, 0x55, 0x5e, 0x48, 0xd1, 0x03, 0xfb, 0x3b, 0x03, 0x1b, 0xdd, - 0xd3, 0x0b, 0x21, 0xc1, 0x9d, 0xa2, 0x65, 0x11, 0xb6, 0x0a, 0xad, 0x7d, 0xb0, 0xc6, 0x22, 0x51, - 0x7f, 0x5c, 0xe0, 0x27, 0xb6, 0x06, 0x12, 0xc9, 0x72, 0x8c, 0xd5, 0xfe, 0x57, 0xe0, 0xb9, 0xaf, - 0xe1, 0xf3, 0x02, 0xa4, 0x54, 0x8d, 0x3a, 0x2c, 0xd9, 0xea, 0x68, 0x14, 0x56, 0xc7, 0xef, 0x41, - 0xe5, 0x26, 0x17, 0x98, 0x03, 0x39, 0x2f, 0x73, 0xce, 0x70, 0x29, 0x77, 0xc7, 0x85, 0x79, 0x89, - 0x3d, 0xf2, 0x74, 0x36, 0x8b, 0xc4, 0xcc, 0x4b, 0x74, 0xb2, 0x64, 0x0c, 0xf6, 0x15, 0x54, 0x49, - 0x59, 0x9b, 0x2d, 0x2f, 0x40, 0x4a, 0x3a, 0xd8, 0xff, 0xfb, 0xbb, 0x8e, 0xf1, 0xcf, 0x77, 0x1d, - 0xe3, 0xdf, 0xef, 0x3a, 0xc6, 0x9f, 0xff, 0xd3, 0xd9, 0xb9, 0xad, 0xd2, 0xff, 0x3e, 0x3f, 0xfa, - 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x48, 0x84, 0x6e, 0xe4, 0x07, 0x12, 0x00, 0x00, + // 1692 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x58, 0x4b, 0x6f, 0x1b, 0xd7, + 0x15, 0xd6, 0x3c, 0xf8, 0x3a, 0xa4, 0x28, 0xe9, 0x5a, 0x76, 0xc7, 0xb6, 0xcc, 0xd2, 0x83, 0xc2, + 0xa5, 0xab, 0x42, 0x46, 0xd9, 0xc2, 0x28, 0x0c, 0xb4, 0x86, 0x28, 0xca, 0x16, 0x61, 0x4b, 0x76, + 0xaf, 0x54, 0xb5, 0x0b, 0x6f, 0x46, 0xe4, 0x2d, 0x3d, 0xe8, 0x90, 0xc3, 0xcc, 0x0c, 0x4d, 0xe9, + 0x07, 0x04, 0xc9, 0x4f, 0xc8, 0x2e, 0xbf, 0x26, 0x48, 0x76, 0xc9, 0x32, 0xcb, 0xc0, 0xd9, 0xe5, + 0x57, 0x04, 0xe7, 0xdc, 0x3b, 0x4f, 0x52, 0x86, 0x61, 0x64, 0x37, 0xe7, 0x71, 0xcf, 0xbd, 0xe7, + 0x3b, 0x4f, 0x12, 0x1a, 0xb3, 0xf9, 0x85, 0xe7, 0x0e, 0xf7, 0x66, 0x81, 0x1f, 0xf9, 0x4c, 0x9f, + 0x5d, 0xd8, 0x57, 0x60, 0x70, 0x7f, 0xc1, 0x2c, 0xa8, 0x1c, 0xf8, 0xde, 0x7c, 0x32, 0x0d, 0x2d, + 0xad, 0x6d, 0x74, 0x4c, 0x1e, 0x93, 0x8c, 0x81, 0xf9, 0x42, 0x5c, 0x85, 0x96, 0xd1, 0x36, 0x3a, + 0x35, 0x4e, 0xdf, 0xa8, 0xcd, 0x7d, 0x27, 0x70, 0xa7, 0x63, 0xcb, 0x6c, 0x6b, 0x9d, 0x06, 0x8f, + 0x49, 0xb6, 0x0d, 0xa5, 0xc1, 0x74, 0x24, 0x2e, 0xad, 0x52, 0x5b, 0xeb, 0xd4, 0xb8, 0x24, 0x90, + 0xfb, 0xcc, 0x15, 0xde, 0xc8, 0x2a, 0x4b, 0x2e, 0x11, 0x76, 0x07, 0x6a, 0xdc, 0x5f, 0x1c, 0x3b, + 0x51, 0xe0, 0x5e, 0xb2, 0xbb, 0x60, 0x72, 0x7f, 0x21, 0x6f, 0xaf, 0x77, 0x2b, 0x7b, 0xb3, 0x8b, + 0x3d, 0xee, 0x2f, 0x38, 0x31, 0xed, 0x7d, 0xa8, 0x9d, 0xba, 0xe3, 0xa9, 0x18, 0xe1, 0x53, 0x6f, + 0x83, 0xf1, 0xda, 0x47, 0x45, 0x2d, 0xab, 0x88, 0x3c, 0x14, 0x9d, 0x88, 0xb1, 0xa5, 0x17, 0x44, + 0x27, 0x62, 0x6c, 0xff, 0x1d, 0x9a, 0xdc, 0x5f, 0x0c, 0x46, 0x62, 0x1a, 0xb9, 0xff, 0x73, 0x45, + 0x40, 0x8e, 0x25, 0x37, 0x9a, 0xf2, 0xa2, 0xc4, 0x59, 0x3d, 0x75, 0xd6, 0xbe, 0x03, 0xe5, 0x41, + 0xff, 0xa5, 0x1b, 0x46, 0x6c, 0x13, 0x8c, 0x41, 0x3f, 0x3e, 0x80, 0x9f, 0xf6, 0x01, 0x6c, 0x1d, + 0x5e, 0x46, 0x81, 0x33, 0x8c, 0xc4, 0x68, 0xd0, 0x97, 0x90, 0xb1, 0x26, 0xe8, 0x83, 0x3e, 0xbd, + 0xcf, 0xe4, 0xfa, 0xa0, 0xcf, 0x5a, 0x60, 0x9e, 0x3b, 0x9e, 0x34, 0x5a, 0xef, 0x02, 0x3e, 0x4b, + 0x1a, 0xe4, 0xc4, 0xb7, 0xdf, 0xe4, 0x8c, 0x28, 0x3c, 0x6e, 0x41, 0x99, 0x50, 0x92, 0xd7, 0xd5, + 0xb8, 0xa2, 0xd8, 0xa3, 0x34, 0x50, 0xd2, 0xde, 0x4d, 0xb4, 0xb7, 0xf4, 0x88, 0x24, 0x7e, 0xf6, + 0x3d, 0xa8, 0xbc, 0x10, 0x57, 0xf4, 0xfe, 0xd8, 0x3b, 0x2d, 0xe3, 0xdd, 0xf7, 0x1a, 0xdc, 0x48, + 0x4e, 0x9f, 0x39, 0x17, 0x9e, 0x38, 0x77, 0xbc, 0xb9, 0x60, 0xad, 0xd8, 0x57, 0x2d, 0xff, 0xe6, + 0xa3, 0x35, 0xf2, 0x9c, 0xdd, 0x4f, 0x90, 0x42, 0x85, 0x3a, 0x2a, 0xa8, 0x6b, 0x8e, 0xd6, 0x54, + 0x96, 0xec, 0x40, 0xb5, 0x77, 0x3a, 0x20, 0x73, 0x96, 0xd1, 0xd6, 0x3a, 0xc6, 0xd1, 0x1a, 0x4f, + 0x38, 0xec, 0x0e, 0x54, 0x8e, 0xe7, 0x91, 0xb8, 0x1c, 0xf4, 0x29, 0x87, 0xcc, 0xa3, 0x35, 0x1e, + 0x33, 0xf0, 0x24, 0x7d, 0xbe, 0x10, 0x57, 0x32, 0x91, 0xf0, 0x64, 0xcc, 0x61, 0xdb, 0x60, 0xf6, + 0x7c, 0xdf, 0xa3, 0x64, 0xaa, 0xe2, 0x6d, 0x48, 0xf5, 0x2a, 0x50, 0x22, 0xc3, 0xf6, 0x25, 0x6c, + 0xe7, 0x1d, 0x52, 0x61, 0x61, 0x60, 0xa0, 0x3d, 0x4d, 0xd9, 0x43, 0x82, 0x6d, 0x52, 0xa8, 0x74, + 0x75, 0x3f, 0x06, 0xeb, 0x11, 0x94, 0xc9, 0x8c, 0x4c, 0xf8, 0x7a, 0xf7, 0x77, 0x39, 0x78, 0x53, + 0x80, 0xb8, 0x52, 0xeb, 0xd5, 0x08, 0xdf, 0x57, 0xc1, 0xa0, 0x6f, 0xff, 0xa3, 0x08, 0x25, 0xc5, + 0x0c, 0x61, 0x3f, 0x71, 0x26, 0x42, 0xde, 0xcc, 0xe9, 0x1b, 0x79, 0x67, 0x57, 0x33, 0x41, 0x57, + 0xd7, 0x38, 0x7d, 0xdb, 0x73, 0x68, 0xe6, 0x8f, 0xe3, 0x63, 0x32, 0x49, 0xb0, 0xf2, 0x31, 0x24, + 0x4f, 0xb2, 0xa3, 0x5b, 0xcc, 0x0e, 0x6b, 0xf9, 0x44, 0x31, 0x41, 0xfe, 0x09, 0xe6, 0x6b, 0xc7, + 0x0d, 0x96, 0xd2, 0x76, 0x53, 0xe2, 0x65, 0xd0, 0x0b, 0x0d, 0x09, 0x7c, 0xe9, 0xc0, 0x9f, 0x4f, + 0x23, 0x09, 0x18, 0x97, 0x84, 0xfd, 0x14, 0x6a, 0x78, 0x5e, 0xfa, 0xba, 0x23, 0x8d, 0xa9, 0xbc, + 0xa9, 0xe2, 0xed, 0x48, 0x73, 0x79, 0x45, 0xd2, 0x07, 0xf4, 0x6c, 0x1f, 0xe8, 0x01, 0xa0, 0x34, + 0x94, 0x16, 0x5a, 0x50, 0x22, 0x4a, 0xb9, 0x9c, 0x9a, 0x90, 0xec, 0x6b, 0x6c, 0xdc, 0xc3, 0xbe, + 0x13, 0x3d, 0xfe, 0x1b, 0x8a, 0x65, 0xc6, 0xe1, 0x0b, 0x0c, 0xae, 0x72, 0xc2, 0x87, 0xaa, 0x04, + 0xca, 0x5f, 0xa4, 0x06, 0xb4, 0x8c, 0x01, 0xe4, 0x62, 0x7f, 0xe8, 0xc7, 0xbe, 0x11, 0x81, 0x55, + 0xc8, 0xfd, 0x45, 0x0a, 0x83, 0xa2, 0xd8, 0xef, 0xe3, 0x5b, 0x4c, 0xf2, 0xb3, 0x46, 0xf5, 0x81, + 0xf7, 0xc7, 0x17, 0xfe, 0x17, 0xe0, 0x79, 0xe0, 0xcf, 0x67, 0x04, 0x11, 0xb3, 0xa1, 0x44, 0x94, + 0xf2, 0xa9, 0x81, 0xea, 0xf1, 0x7b, 0xb8, 0x14, 0xad, 0x06, 0x17, 0x83, 0xb0, 0x3f, 0x1e, 0xcb, + 0xf2, 0xe1, 0xf8, 0x69, 0x7f, 0xad, 0x41, 0xf5, 0xdc, 0xf1, 0x12, 0xf1, 0xb9, 0xe3, 0x29, 0x5f, + 0xf1, 0x33, 0x6f, 0xc6, 0x88, 0xcd, 0xdc, 0x81, 0xea, 0x33, 0xcf, 0x77, 0x22, 0x54, 0x46, 0x5b, + 0x1a, 0x4f, 0x68, 0xb6, 0x0b, 0xd0, 0x17, 0x43, 0x77, 0xe2, 0x78, 0x28, 0x35, 0xd3, 0x7a, 0x56, + 0x5c, 0x9e, 0x11, 0x33, 0x1b, 0x1a, 0x67, 0xee, 0x44, 0x84, 0x91, 0x33, 0x99, 0xa1, 0xba, 0x6c, + 0xf3, 0x39, 0x9e, 0x7d, 0x17, 0x2a, 0xea, 0x04, 0xbe, 0xef, 0xb9, 0x7f, 0x41, 0xef, 0x6b, 0x70, + 0xfc, 0xb4, 0x9f, 0xc2, 0x56, 0xdf, 0x0d, 0x23, 0x77, 0x3a, 0x8c, 0x92, 0x43, 0x08, 0xb3, 0x2a, + 0x3a, 0xd5, 0xec, 0x24, 0x95, 0x54, 0x8e, 0x9e, 0x56, 0x8e, 0xfd, 0x8d, 0x06, 0x8d, 0x7f, 0xcd, + 0x45, 0x70, 0xc5, 0xc5, 0x67, 0x73, 0x11, 0x46, 0xe8, 0x31, 0xd1, 0x71, 0x3c, 0x89, 0x40, 0x93, + 0xa7, 0x6f, 0x9d, 0x60, 0x24, 0x0b, 0xc1, 0xe4, 0x8a, 0xa2, 0x88, 0x8a, 0x89, 0x1f, 0x09, 0x7a, + 0x7a, 0x95, 0x2b, 0x8a, 0xed, 0x42, 0xe3, 0x70, 0x72, 0x21, 0x46, 0x23, 0x31, 0xea, 0x3b, 0x91, + 0x63, 0x55, 0xf3, 0x73, 0x28, 0x27, 0x64, 0x7f, 0x80, 0xf5, 0xd7, 0x81, 0x38, 0x0b, 0x9c, 0x69, + 0xe8, 0x39, 0x91, 0x18, 0x59, 0x35, 0xb2, 0x95, 0x67, 0xb2, 0x1d, 0xa8, 0x1d, 0x3b, 0x97, 0xc7, + 0x62, 0xe2, 0x07, 0x57, 0x16, 0x50, 0x38, 0x52, 0x86, 0xfd, 0x12, 0xd6, 0x95, 0x1b, 0xe1, 0xcc, + 0x9f, 0x86, 0x02, 0xb1, 0x3a, 0x0c, 0x02, 0xe5, 0x05, 0x7e, 0xb2, 0x87, 0x50, 0xe1, 0x22, 0x9c, + 0x7b, 0x51, 0x5c, 0xcd, 0x1b, 0xf8, 0x9c, 0xf8, 0xd4, 0xdc, 0x8b, 0x78, 0x2c, 0xb7, 0x7f, 0x29, + 0x41, 0x3d, 0x23, 0x48, 0xfa, 0x0b, 0xf6, 0xc8, 0x75, 0xd9, 0x5f, 0x70, 0x3a, 0x72, 0x7f, 0xb1, + 0x34, 0x38, 0xb1, 0x26, 0x1a, 0xa0, 0x9d, 0xa8, 0xc4, 0xd3, 0x4e, 0xd2, 0x12, 0x34, 0x56, 0x97, + 0x20, 0x2e, 0x0b, 0x6f, 0x9d, 0xe9, 0x58, 0x8c, 0x28, 0x5d, 0xaa, 0x3c, 0x26, 0x59, 0x27, 0xcd, + 0x4d, 0xc2, 0x57, 0xe5, 0x7a, 0xcc, 0xe3, 0x69, 0xe6, 0xca, 0xca, 0xc2, 0x11, 0x53, 0x91, 0xf1, + 0x91, 0x14, 0x7b, 0x0c, 0xcd, 0x57, 0xde, 0x28, 0xad, 0x9d, 0x50, 0x45, 0xa2, 0x89, 0x76, 0x52, + 0x36, 0x2f, 0x68, 0xb1, 0x27, 0xc5, 0xf9, 0x4e, 0x31, 0xa9, 0x77, 0x99, 0xf2, 0x33, 0x23, 0xe1, + 0xc5, 0x4d, 0x60, 0x37, 0xb3, 0x5e, 0x50, 0xa0, 0xea, 0xdd, 0x75, 0x3c, 0x96, 0x30, 0x79, 0x66, + 0xfd, 0xd8, 0xcb, 0x76, 0x2b, 0xab, 0x4e, 0xda, 0xcd, 0x18, 0x21, 0xc9, 0xe5, 0xd9, 0x7e, 0xb6, + 0x9b, 0x69, 0x8f, 0x56, 0x23, 0x35, 0x9e, 0x30, 0x79, 0xa6, 0x7d, 0x1e, 0xac, 0x58, 0x05, 0xac, + 0x75, 0x3a, 0x54, 0x9c, 0xf3, 0x52, 0xc8, 0x57, 0xac, 0x0e, 0x4f, 0x8a, 0x73, 0xc4, 0x6a, 0xa6, + 0x50, 0xe4, 0x25, 0xbc, 0x38, 0x71, 0x76, 0x33, 0x3b, 0x99, 0xb5, 0x91, 0xbe, 0x36, 0x61, 0xf2, + 0xcc, 0xce, 0xf6, 0x17, 0xa8, 0x67, 0x03, 0xb5, 0x49, 0xea, 0x1b, 0xf9, 0x40, 0x85, 0x3c, 0xab, + 0x83, 0x0e, 0x2e, 0x95, 0xbf, 0xb5, 0x95, 0x3a, 0xb8, 0x24, 0xe4, 0xcb, 0xfa, 0xf6, 0xb7, 0x3a, + 0xac, 0x0f, 0x26, 0x33, 0x3f, 0x88, 0x32, 0x3d, 0x40, 0xae, 0x9d, 0xda, 0xca, 0xb5, 0x53, 0x2f, + 0x74, 0x7a, 0xea, 0x05, 0xd4, 0x08, 0x4d, 0x2e, 0x89, 0x4c, 0x3e, 0x9a, 0xb9, 0x7c, 0xdc, 0x81, + 0x9a, 0x1c, 0x94, 0x28, 0x2a, 0x91, 0x28, 0x65, 0xc8, 0x45, 0x78, 0x41, 0x8b, 0x50, 0x85, 0x3a, + 0x57, 0x4c, 0xb2, 0x16, 0x80, 0x54, 0x23, 0x61, 0x95, 0x84, 0x19, 0x0e, 0xca, 0x13, 0x87, 0x42, + 0xab, 0xdc, 0x36, 0x3a, 0x06, 0xcf, 0x70, 0xd8, 0x03, 0x68, 0x92, 0x13, 0x07, 0x81, 0xc0, 0x66, + 0xb2, 0x1f, 0x51, 0x3e, 0x1b, 0xbc, 0xc0, 0x45, 0x3d, 0x72, 0x2b, 0xd5, 0x93, 0x9d, 0xa6, 0xc0, + 0xa5, 0xb9, 0xe0, 0x09, 0x27, 0xa0, 0x8c, 0xad, 0x72, 0x49, 0xd8, 0x3f, 0xea, 0xc0, 0x24, 0x92, + 0x72, 0xa9, 0xf9, 0xcd, 0xe0, 0xfc, 0x30, 0x6c, 0x79, 0x70, 0x2a, 0x4b, 0xe0, 0xa4, 0xf3, 0x40, + 0x02, 0x13, 0xcf, 0x83, 0x36, 0xd4, 0xe3, 0xb1, 0x85, 0x42, 0x44, 0x55, 0xe3, 0x59, 0x16, 0xce, + 0xa7, 0xd3, 0x08, 0x7f, 0x89, 0x28, 0x95, 0x1a, 0xd9, 0xce, 0xf1, 0x56, 0x40, 0x0b, 0x1f, 0x09, + 0x6d, 0xfd, 0xc3, 0xd0, 0x36, 0xb2, 0xd0, 0x7e, 0xa1, 0x41, 0x63, 0x3f, 0xf2, 0x27, 0xee, 0x90, + 0x8b, 0xa1, 0x1f, 0x8c, 0xae, 0x07, 0x55, 0xc2, 0xa7, 0x67, 0xe1, 0xeb, 0x80, 0x31, 0x78, 0x17, + 0xa8, 0xfe, 0x7b, 0x8b, 0xb6, 0x8b, 0xa5, 0x28, 0x71, 0x54, 0x61, 0xf7, 0x41, 0x1f, 0x04, 0x94, + 0xb3, 0xf5, 0xee, 0x56, 0xaa, 0x18, 0xeb, 0xe8, 0x83, 0xc0, 0xfe, 0x33, 0x6c, 0xcb, 0x87, 0xc4, + 0x22, 0x35, 0x70, 0xb6, 0xa1, 0x74, 0x18, 0x04, 0x7e, 0x3c, 0x72, 0x24, 0x81, 0xeb, 0x73, 0x32, + 0xc3, 0x30, 0x18, 0x9f, 0x92, 0x13, 0xab, 0x7e, 0x33, 0xb6, 0xa1, 0x7e, 0xe2, 0x47, 0xff, 0x09, + 0xdc, 0x88, 0x5a, 0x92, 0x1c, 0x1c, 0x59, 0x96, 0xfd, 0x10, 0x6e, 0x16, 0x6e, 0x4e, 0x27, 0x23, + 0xa6, 0x91, 0x91, 0xfe, 0xee, 0x3a, 0x85, 0x1b, 0x89, 0xea, 0xa0, 0xff, 0x49, 0x6f, 0x5c, 0x36, + 0xfa, 0xa7, 0x8c, 0xe7, 0x64, 0x54, 0x5d, 0xbf, 0xc2, 0x1b, 0xbb, 0x07, 0x96, 0x42, 0x53, 0xfe, + 0xf0, 0x55, 0x2f, 0x38, 0x77, 0xc5, 0xe2, 0xba, 0x7d, 0x9f, 0xd6, 0x0a, 0x9d, 0x36, 0x21, 0xfa, + 0xb6, 0xbf, 0xd4, 0x61, 0x7b, 0x95, 0x91, 0x34, 0xa1, 0xb4, 0x4c, 0x42, 0xb1, 0x2e, 0x94, 0xde, + 0xb9, 0x62, 0x11, 0xef, 0x02, 0x3b, 0x99, 0x60, 0x2f, 0xbd, 0x81, 0x4b, 0x55, 0x2c, 0xa4, 0xfd, + 0x61, 0xe4, 0xfa, 0xd3, 0x78, 0x7f, 0x95, 0x14, 0xde, 0xd0, 0xf3, 0xfc, 0xe1, 0xff, 0xe5, 0x4f, + 0x2f, 0x2e, 0x89, 0x15, 0x85, 0x51, 0xfa, 0xc8, 0xc2, 0x28, 0xaf, 0x2c, 0x8c, 0x0e, 0x6c, 0xfc, + 0x7b, 0x36, 0x72, 0x22, 0x71, 0x78, 0xe9, 0x86, 0x91, 0x98, 0x0e, 0x85, 0x55, 0x21, 0x8f, 0x8a, + 0x6c, 0xfb, 0x73, 0x0d, 0xd6, 0x95, 0x17, 0x52, 0x74, 0xcd, 0x96, 0xce, 0xc0, 0x44, 0xf7, 0xe2, + 0x85, 0x90, 0xe0, 0x4e, 0xd0, 0x32, 0x08, 0x5b, 0x85, 0xd6, 0x26, 0x18, 0xa7, 0x22, 0x52, 0x7f, + 0x4f, 0xe0, 0x27, 0xb6, 0x06, 0x12, 0xc9, 0x72, 0x0c, 0xd5, 0xfe, 0x97, 0xe3, 0xd9, 0x6f, 0xe0, + 0x76, 0x0e, 0x52, 0xaa, 0xc6, 0x38, 0x2c, 0xe9, 0xea, 0xa8, 0xe5, 0x56, 0xc7, 0x3f, 0x42, 0xe9, + 0x3c, 0x13, 0x98, 0x2d, 0x39, 0x2f, 0x33, 0xce, 0x70, 0x29, 0xb7, 0x4f, 0x73, 0xf3, 0x12, 0x7b, + 0xe4, 0xfe, 0x78, 0x1c, 0x88, 0xb1, 0x13, 0xc5, 0xc9, 0x92, 0x32, 0xd8, 0x03, 0x28, 0x93, 0x72, + 0x6c, 0xb6, 0xb8, 0x00, 0x29, 0x69, 0x6f, 0xf3, 0xbb, 0xf7, 0x2d, 0xed, 0x87, 0xf7, 0x2d, 0xed, + 0xa7, 0xf7, 0x2d, 0xed, 0xab, 0x9f, 0x5b, 0x6b, 0x17, 0x65, 0xfa, 0x77, 0xe7, 0xaf, 0xbf, 0x06, + 0x00, 0x00, 0xff, 0xff, 0x51, 0x2f, 0x5d, 0x6f, 0xed, 0x11, 0x00, 0x00, } func (m *Row) Marshal() (dAtA []byte, err error) { @@ -3834,15 +3826,12 @@ func (m *Decimal) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - if m.Scale != 0 { - i = encodeVarintPublic(dAtA, i, uint64(m.Scale)) + if len(m.Gob) > 0 { + i -= len(m.Gob) + copy(dAtA[i:], m.Gob) + i = encodeVarintPublic(dAtA, i, uint64(len(m.Gob))) i-- - dAtA[i] = 0x10 - } - if m.Value != 0 { - i = encodeVarintPublic(dAtA, i, uint64(m.Value)) - i-- - dAtA[i] = 0x8 + dAtA[i] = 0xa } return len(dAtA) - i, nil } @@ -5635,11 +5624,9 @@ func (m *Decimal) Size() (n int) { } var l int _ = l - if m.Value != 0 { - n += 1 + sovPublic(uint64(m.Value)) - } - if m.Scale != 0 { - n += 1 + sovPublic(uint64(m.Scale)) + l = len(m.Gob) + if l > 0 { + n += 1 + l + sovPublic(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) @@ -8774,10 +8761,10 @@ func (m *Decimal) Unmarshal(dAtA []byte) error { } switch fieldNum { case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Gob", wireType) } - m.Value = 0 + var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowPublic @@ -8787,30 +8774,26 @@ func (m *Decimal) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Value |= int64(b&0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Scale", wireType) + if byteLen < 0 { + return ErrInvalidLengthPublic } - m.Scale = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowPublic - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Scale |= int64(b&0x7F) << shift - if b < 0x80 { - break - } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthPublic } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Gob = append(m.Gob[:0], dAtA[iNdEx:postIndex]...) + if m.Gob == nil { + m.Gob = []byte{} + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipPublic(dAtA[iNdEx:]) diff --git a/pb/public.proto b/pb/public.proto index d5dbf06c6..d22f12e74 100644 --- a/pb/public.proto +++ b/pb/public.proto @@ -113,8 +113,7 @@ message ValCount { } message Decimal { - int64 Value = 1; - int64 Scale = 2; + bytes Gob = 1; } message DistinctTimestamp { diff --git a/pql/ast_test.go b/pql/ast_test.go index 2311053f7..375d93405 100644 --- a/pql/ast_test.go +++ b/pql/ast_test.go @@ -40,7 +40,7 @@ func TestCondition_StringWithSubj(t *testing.T) { }{ {pql.BETWEEN, []interface{}{int64(4), int64(8)}, "4<=subj<=8"}, {pql.BETWEEN, []interface{}{uint64(5), uint64(9)}, "5<=subj<=9"}, - {pql.BETWEEN, []interface{}{pql.Decimal{Value: -401, Scale: 2}, pql.Decimal{Value: 802, Scale: 1}}, "-4.01<=subj<=80.2"}, + {pql.BETWEEN, []interface{}{pql.NewDecimal(-401, 2), pql.NewDecimal(802, 1)}, "-4.01<=subj<=80.2"}, {pql.EQ, nil, "subj==null"}, {pql.NEQ, nil, "subj!=null"}, } { diff --git a/pql/decimal.go b/pql/decimal.go index c9a59f4e3..6dda17f22 100644 --- a/pql/decimal.go +++ b/pql/decimal.go @@ -2,7 +2,7 @@ package pql import ( - "fmt" + "encoding/binary" "math" "math/big" "strconv" @@ -52,22 +52,74 @@ func Pow10(p int64) int64 { // Precision is currently not considered; precision, for // our purposes is implied to be the complete, known value. type Decimal struct { - Value int64 + value big.Int Scale int64 } +func (d *Decimal) Value() big.Int { + val := big.NewInt(0) + val.Set(&d.value) + return *val +} + +func (d *Decimal) SetValue(v int64) { + val := big.NewInt(v) + d.value = *val +} + func (d Decimal) Clone() (r *Decimal) { + val := big.NewInt(0) + val.Set(&d.value) r = &Decimal{ - Value: d.Value, + value: *val, Scale: d.Scale, } return } +func (d *Decimal) GobEncode() ([]byte, error) { + if d == nil { + return nil, nil + } + valBuf, err := d.value.GobEncode() + if err != nil { + return nil, err + } + valSz := len(valBuf) + + // make a buffer the size of our Decimal, plus our 8 byte Scale, + // plus 1 byte for sign of scale + buf := make([]byte, valSz+8) + copy(buf[:valSz], valBuf) + + binary.LittleEndian.PutUint64(buf[valSz:valSz+8], uint64(d.Scale)) + + return buf, nil +} + +func (d *Decimal) GobDecode(buf []byte) error { + if len(buf) == 0 { + // they sent a nil or default value + *d = Decimal{} + return nil + } + + valPart := len(buf) - 8 + value := big.NewInt(0) + if err := value.GobDecode(buf[:valPart]); err != nil { + return err + } + d.value = *value + + d.Scale = int64(binary.LittleEndian.Uint64(buf[valPart : valPart+8])) + return nil +} + // NewDecimal returns a Decimal based on the provided arguments. func NewDecimal(value, scale int64) Decimal { + v := big.NewInt(value) return Decimal{ - Value: value, + value: *v, Scale: scale, } } @@ -84,34 +136,14 @@ func MinMax(scale int64) (Decimal, Decimal) { // // If the Scale of a and b don't match, the returned Decimal will have the // smallest Scale needed to precisely represent the sum. -func AddDecimal(a, b Decimal) (Decimal, bool) { - av, bv := big.NewInt(a.Value), big.NewInt(b.Value) - - // if the scales dont match, - // we add zeros to the end of the one with the smaller scale until they match - // or we overflow - // then we add the values and return the decimal - as, bs := a.Scale, b.Scale - var ok bool - if a.Scale > b.Scale { - av, bv = bv, av - as, bs = bs, as - } - - for as < bs { - av = av.Mul(av, big.NewInt(10)) - as++ - } - - av = av.Add(av, bv) - ret, ok := av.Int64(), av.IsInt64() - if !ok { - return Decimal{}, ok - } +func AddDecimal(a, b Decimal) Decimal { + ac, bc := sameScalify(a, b) + apv, bpv := &ac.value, &bc.value + apv.Add(apv, bpv) return Decimal{ - Value: ret, - Scale: as, - }, ok + value: *apv, + Scale: ac.Scale, + } } // LessThan returns true if d < d2. @@ -134,84 +166,56 @@ func (d Decimal) GreaterThanOrEqualTo(d2 Decimal) bool { return d.greaterThan(d2, true) } -// EqualTo returns true if d == d2. -func (d Decimal) EqualTo(d2 Decimal) bool { - if d.Scale == d2.Scale { - return d.Value == d2.Value +func (d *Decimal) withLargerScale(scale int64) *Decimal { + dc := d.Clone() + val := &dc.value + ten := big.NewInt(10) + for dc.Scale < scale { + val = val.Mul(val, ten) + dc.Scale++ } - quotientD := quotient(d) - quotientD2 := quotient(d2) - if quotientD != quotientD2 { - return false + return dc +} + +func sameScalify(d, d2 Decimal) (*Decimal, *Decimal) { + dc := d.Clone() + d2c := d2.Clone() + + if dc.Scale < d2c.Scale { + dc = dc.withLargerScale(d2c.Scale) + } else { + d2c = d2c.withLargerScale(dc.Scale) } - remainderD, remainderD2 := remainder(d), remainder(d2) - if d.Scale < d2.Scale { - scaleDiff := d2.Scale - d.Scale - return (remainderD * pow10[scaleDiff]) == remainderD2 + + return dc, d2c +} + +func (d Decimal) cmp(d2 Decimal) int { + if d.Scale == d2.Scale { + return (&d.value).Cmp(&d2.value) } - scaleDiff := d.Scale - d2.Scale - return remainderD == (remainderD2 * pow10[scaleDiff]) + dc, d2c := sameScalify(d, d2) + return (&dc.value).Cmp(&d2c.value) +} + +// EqualTo returns true if d == d2. +func (d Decimal) EqualTo(d2 Decimal) bool { + return d.cmp(d2) == 0 } func (d Decimal) lessThan(d2 Decimal, eq bool) bool { - if d.Scale == d2.Scale { - if eq { - return d.Value <= d2.Value - } - return d.Value < d2.Value + if eq { + return d.cmp(d2) <= 0 } - - quotientD, quotientD2 := quotient(d), quotient(d2) - if quotientD < quotientD2 { - return true - } else if quotientD == quotientD2 { - remainderD, remainderD2 := remainder(d), remainder(d2) - if d.Scale < d2.Scale { - scaleDiff := d2.Scale - d.Scale - if eq { - return (remainderD * pow10[scaleDiff]) <= remainderD2 - } - return (remainderD * pow10[scaleDiff]) < remainderD2 - } - scaleDiff := d.Scale - d2.Scale - if eq { - return remainderD <= (remainderD2 * pow10[scaleDiff]) - } - return remainderD < (remainderD2 * pow10[scaleDiff]) - } - - return false + return d.cmp(d2) < 0 } func (d Decimal) greaterThan(d2 Decimal, eq bool) bool { - if d.Scale == d2.Scale { - if eq { - return d.Value >= d2.Value - } - return d.Value > d2.Value + if eq { + return d.cmp(d2) >= 0 } - - quotientD, quotientD2 := quotient(d), quotient(d2) - if quotientD > quotientD2 { - return true - } else if quotientD == quotientD2 { - remainderD, remainderD2 := remainder(d), remainder(d2) - if d.Scale < d2.Scale { - scaleDiff := d2.Scale - d.Scale - if eq { - return (remainderD * pow10[scaleDiff]) >= remainderD2 - } - return (remainderD * pow10[scaleDiff]) > remainderD2 - } - scaleDiff := d.Scale - d2.Scale - if eq { - return remainderD >= (remainderD2 * pow10[scaleDiff]) - } - return remainderD > (remainderD2 * pow10[scaleDiff]) - } - - return false + return d.cmp(d2) > 0 } // SupportedByScale returns true if d can be represented @@ -240,16 +244,17 @@ func (d Decimal) IsValid() bool { } // ToInt64 returns d as an int64 adjusted to the -// provided scale. +// provided scale. If d.value cannot be represented +// as an int64, results are undefined. func (d Decimal) ToInt64(scale int64) int64 { var ret int64 scaleDiff := scale - d.Scale if scaleDiff == 0 { - ret = d.Value + ret = d.value.Int64() } else if scaleDiff < 0 { - ret = d.Value / Pow10(-1*scaleDiff) + ret = d.value.Int64() / Pow10(-1*scaleDiff) } else { - ret = d.Value * Pow10(scaleDiff) + ret = d.value.Int64() * Pow10(scaleDiff) } return ret } @@ -257,12 +262,14 @@ func (d Decimal) ToInt64(scale int64) int64 { // Float64 returns d as a float64. // TODO: this could potentially lose precision; we should audit // its use and protect against unexpected results. +// If d.value cannot be represented as an int64, +// results are undefined. func (d Decimal) Float64() float64 { var ret float64 if d.Scale == 0 { - ret = float64(d.Value) + ret = float64(d.value.Int64()) } else { - ret = float64(d.Value) / math.Pow10(int(d.Scale)) + ret = float64(d.value.Int64()) / math.Pow10(int(d.Scale)) } return ret } @@ -272,7 +279,10 @@ func (d Decimal) String() string { var s string var neg bool - sval := fmt.Sprintf("%d", d.Value) + sval := d.value.String() + if len(sval) == 0 { + return "" + } // Strip the negative sign off for now, and // re-apply it at the end. @@ -441,8 +451,9 @@ func ParseDecimal(s string) (Decimal, error) { value *= -1 } + bigVal := big.NewInt(value) return Decimal{ - Value: value, + value: *bigVal, Scale: scale, }, nil } @@ -489,24 +500,6 @@ func reducePrecision(sign bool, mantissa []byte, scale int64) ([]byte, int64, bo return reducePrecision(sign, mantissa[:len(mantissa)-1], scale-1) } -func quotient(d Decimal) int64 { - if d.Scale == 0 { - return d.Value - } else if d.Scale > 0 && d.Scale < 19 { - return d.Value / pow10[d.Scale] - } else if d.Scale < 0 && d.Scale > -19 { - return d.Value * pow10[-1*d.Scale] - } - return 0 -} - -func remainder(d Decimal) int64 { - if d.Scale >= 0 && d.Scale < 19 { - return d.Value % pow10[d.Scale] - } - return 0 -} - // UnmarshalJSON is a custom unmarshaller for the Decimal // type. The intention is to avoid the use of float64 // anywhere, so this unmarhaller parses the decimal out @@ -516,7 +509,7 @@ func (d *Decimal) UnmarshalJSON(data []byte) error { if err != nil { return errors.Wrapf(err, "parsing decimal: %s", string(data)) } - d.Value = o.Value + d.value = o.value d.Scale = o.Scale return nil @@ -539,7 +532,7 @@ func (d *Decimal) UnmarshalYAML(unmarshal func(interface{}) error) error { if err != nil { return errors.Wrapf(err, "parsing decimal: %s", data) } - d.Value = o.Value + d.value = o.value d.Scale = o.Scale return nil diff --git a/pql/decimal_internal_test.go b/pql/decimal_internal_test.go new file mode 100644 index 000000000..5b3b10ea2 --- /dev/null +++ b/pql/decimal_internal_test.go @@ -0,0 +1,133 @@ +package pql + +import ( + "fmt" + "math" + "math/big" + "reflect" + "testing" +) + +type testAddDecimalCase struct { + a Decimal + b Decimal + exp Decimal +} + +func TestAddDecimal(t *testing.T) { + toDecimal := func(a interface{}) Decimal { + switch ac := a.(type) { + case string: + return mustParse(t, ac) + case Decimal: + return ac + default: + t.Fatalf("cannot support type %T", ac) + } + return Decimal{} + } + + newTestAddDecimalCase := func(a, b, exp interface{}) testAddDecimalCase { + return testAddDecimalCase{ + a: toDecimal(a), + b: toDecimal(b), + exp: toDecimal(exp), + } + } + + tests := []testAddDecimalCase{ + newTestAddDecimalCase("40.37", "40.37", "80.74"), + newTestAddDecimalCase("18.5", "9.25", "27.75"), + newTestAddDecimalCase("18.50", "9.25", "27.75"), + newTestAddDecimalCase("-18.50", "-9.25", "-27.75"), + newTestAddDecimalCase("-40.37", "40.37", NewDecimal(0, 0)), + newTestAddDecimalCase("-50.38", "-50.38", "-100.76"), + newTestAddDecimalCase("-40.381", "-40.38", "-80.761"), + newTestAddDecimalCase("-40.3700000000000000000001", "-50.38", "-90.7500000000000000000001"), + newTestAddDecimalCase("10.37000000000000001", "-10", "0.37000000000000001"), + // this is a weird one. because we reduce precision when we parse strings, we + // expect the value to be smaller than it really should be if you did + // the math yourself. + // + // there's not really a good way around this unless we use math/big.Int to represent + // our Value/Scale. + // + // but if we did this, to quote seebs: "i suspect that + // our performance would tank so badly by the time we were doing >63-bit + // numbers that there's no real-world benefit to us." + newTestAddDecimalCase("10.370000000000000001", "-10", "0.37"), + newTestAddDecimalCase("-9223372036854775807", "-9223372036854775807", "-18446744073709551614"), + newTestAddDecimalCase("9223372036854775807", "9223372036854775807", "18446744073709551614"), + newTestAddDecimalCase(NewDecimal(0, 0), NewDecimal(1, 4), NewDecimal(1, 4)), + } + + for i, test := range tests { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + t.Logf("first %#v %#v", test.a, test.b) + if got := AddDecimal(test.a, test.b); !got.EqualTo(test.exp) { + t.Logf("LOG: %#v + %#v, expected %#v, got %#v", test.a, test.b, test.exp, got) + t.Errorf("%v + %v, expected %v, got %v", test.a, test.b, test.exp, got) + } + + t.Logf("second %v %v", test.a, test.b) + if got := AddDecimal(test.b, test.a); !got.EqualTo(test.exp) { + t.Logf("LOG: %#v + %#v, expected %#v, got %#v", test.a, test.b, test.exp, got) + t.Errorf("%v + %v, expected %v, got %v", test.b, test.a, test.exp, got) + } + }) + } +} + +func mustParse(t *testing.T, num string) Decimal { + t.Helper() + d, err := ParseDecimal(num) + if err != nil { + v := big.NewInt(0) + if _, ok := v.SetString(num, 10); !ok { + t.Fatalf("unexpected error parsing %s to Decimal: %v", num, err) + } + d.value = *v + } + return d +} + +func TestGobEncodeDecode(t *testing.T) { + for i := int64(-10); i < 10; i++ { + for j := int64(-10); j < 10; j++ { + decimal := &Decimal{value: *(big.NewInt(i)), Scale: j} + t.Run(fmt.Sprintf("v%ds%d", i, j), func(t *testing.T) { + encoded, err := decimal.GobEncode() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + decoded := &Decimal{} + err = decoded.GobDecode(encoded) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if !reflect.DeepEqual(decimal, decoded) { + t.Errorf("%v != %v", decimal, encoded) + } + }) + } + } + + i := int64(math.MaxInt64) + j := int64(math.MinInt64) + decimal := &Decimal{value: *(big.NewInt(i)), Scale: j} + t.Run(fmt.Sprintf("v%ds%d", i, j), func(t *testing.T) { + encoded, err := decimal.GobEncode() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + decoded := &Decimal{} + err = decoded.GobDecode(encoded) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if !reflect.DeepEqual(decimal, decoded) { + t.Errorf("%v != %v", decimal, encoded) + } + }) + +} diff --git a/pql/decimal_test.go b/pql/decimal_test.go index b17a9a1db..dd45fceb2 100644 --- a/pql/decimal_test.go +++ b/pql/decimal_test.go @@ -3,7 +3,6 @@ package pql_test import ( "encoding/json" - "fmt" "reflect" "strings" "testing" @@ -19,48 +18,48 @@ func TestDecimal(t *testing.T) { exp pql.Decimal expErr string }{ - {"0", pql.Decimal{0, 0}, ""}, - {"-0", pql.Decimal{0, 0}, ""}, - {"0.0", pql.Decimal{0, 0}, ""}, - {"0.", pql.Decimal{0, 0}, ""}, - {"-0.00", pql.Decimal{0, 0}, ""}, - {"123.4567", pql.Decimal{1234567, 4}, ""}, - {"123.456700", pql.Decimal{1234567, 4}, ""}, - {"00123.4567", pql.Decimal{1234567, 4}, ""}, - {"+123.4567", pql.Decimal{1234567, 4}, ""}, - {"-123.4567", pql.Decimal{-1234567, 4}, ""}, - {"-00123.4567", pql.Decimal{-1234567, 4}, ""}, - {"-12.25", pql.Decimal{-1225, 2}, ""}, + {"0", pql.NewDecimal(0, 0), ""}, + {"-0", pql.NewDecimal(0, 0), ""}, + {"0.0", pql.NewDecimal(0, 0), ""}, + {"0.", pql.NewDecimal(0, 0), ""}, + {"-0.00", pql.NewDecimal(0, 0), ""}, + {"123.4567", pql.NewDecimal(1234567, 4), ""}, + {"123.456700", pql.NewDecimal(1234567, 4), ""}, + {"00123.4567", pql.NewDecimal(1234567, 4), ""}, + {"+123.4567", pql.NewDecimal(1234567, 4), ""}, + {"-123.4567", pql.NewDecimal(-1234567, 4), ""}, + {"-00123.4567", pql.NewDecimal(-1234567, 4), ""}, + {"-12.25", pql.NewDecimal(-1225, 2), ""}, - {"123", pql.Decimal{123, 0}, ""}, - {"-12300", pql.Decimal{-123, -2}, ""}, - {"+012300", pql.Decimal{123, -2}, ""}, - {"12300", pql.Decimal{123, -2}, ""}, - {"12300.", pql.Decimal{123, -2}, ""}, - {"12300.0", pql.Decimal{123, -2}, ""}, - {"123.0", pql.Decimal{123, 0}, ""}, + {"123", pql.NewDecimal(123, 0), ""}, + {"-12300", pql.NewDecimal(-123, -2), ""}, + {"+012300", pql.NewDecimal(123, -2), ""}, + {"12300", pql.NewDecimal(123, -2), ""}, + {"12300.", pql.NewDecimal(123, -2), ""}, + {"12300.0", pql.NewDecimal(123, -2), ""}, + {"123.0", pql.NewDecimal(123, 0), ""}, - {".123", pql.Decimal{123, 3}, ""}, - {"0.123", pql.Decimal{123, 3}, ""}, - {"0.001230", pql.Decimal{123, 5}, ""}, - {"-0.001230", pql.Decimal{-123, 5}, ""}, + {".123", pql.NewDecimal(123, 3), ""}, + {"0.123", pql.NewDecimal(123, 3), ""}, + {"0.001230", pql.NewDecimal(123, 5), ""}, + {"-0.001230", pql.NewDecimal(-123, 5), ""}, // int64 edges. - {".000009223372036854775807", pql.Decimal{9223372036854775807, 24}, ""}, - {"-.000009223372036854775808", pql.Decimal{-9223372036854775808, 24}, ""}, - {"92233720368547.75807", pql.Decimal{9223372036854775807, 5}, ""}, - {"-92233720368547.75807", pql.Decimal{-9223372036854775807, 5}, ""}, - {"9223372036854775807000", pql.Decimal{9223372036854775807, -3}, ""}, - {"-9223372036854775807000", pql.Decimal{-9223372036854775807, -3}, ""}, + {".000009223372036854775807", pql.NewDecimal(9223372036854775807, 24), ""}, + {"-.000009223372036854775808", pql.NewDecimal(-9223372036854775808, 24), ""}, + {"92233720368547.75807", pql.NewDecimal(9223372036854775807, 5), ""}, + {"-92233720368547.75807", pql.NewDecimal(-9223372036854775807, 5), ""}, + {"9223372036854775807000", pql.NewDecimal(9223372036854775807, -3), ""}, + {"-9223372036854775807000", pql.NewDecimal(-9223372036854775807, -3), ""}, // precision adjustment - {"2.666666666666666667", pql.Decimal{2666666666666666667, 18}, ""}, - {"2.6666666666666666667", pql.Decimal{2666666666666666666, 18}, ""}, - {"2.6666666666666666666667", pql.Decimal{2666666666666666666, 18}, ""}, - {"-9.223372036854775808", pql.Decimal{-9223372036854775808, 18}, ""}, - {"-9.223372036854775809", pql.Decimal{-922337203685477580, 17}, ""}, - {"9.223372036854775807", pql.Decimal{9223372036854775807, 18}, ""}, - {"9.223372036854775808", pql.Decimal{922337203685477580, 17}, ""}, + {"2.666666666666666667", pql.NewDecimal(2666666666666666667, 18), ""}, + {"2.6666666666666666667", pql.NewDecimal(2666666666666666666, 18), ""}, + {"2.6666666666666666666667", pql.NewDecimal(2666666666666666666, 18), ""}, + {"-9.223372036854775808", pql.NewDecimal(-9223372036854775808, 18), ""}, + {"-9.223372036854775809", pql.NewDecimal(-922337203685477580, 17), ""}, + {"9.223372036854775807", pql.NewDecimal(9223372036854775807, 18), ""}, + {"9.223372036854775808", pql.NewDecimal(922337203685477580, 17), ""}, // Error cases. {"", pql.Decimal{}, "decimal string is empty"}, @@ -88,7 +87,7 @@ func TestDecimal(t *testing.T) { } } else if err != nil { t.Fatalf("test %d parsing string `%s`: %s", i, test.s, err) - } else if dec != test.exp { + } else if !dec.EqualTo(test.exp) { t.Fatalf("test %d parsing string `%s`: expected: %v, but got: %v", i, test.s, test.exp, dec) } } @@ -100,22 +99,22 @@ func TestDecimal(t *testing.T) { scale int64 exp int64 }{ - {pql.Decimal{0, 0}, 0, 0}, // 0 : 0 - {pql.Decimal{0, 0}, 1, 0}, // 0 : 0.0 - {pql.Decimal{0, 0}, -1, 0}, // 0 : 0 + {pql.NewDecimal(0, 0), 0, 0}, // 0 : 0 + {pql.NewDecimal(0, 0), 1, 0}, // 0 : 0.0 + {pql.NewDecimal(0, 0), -1, 0}, // 0 : 0 - {pql.Decimal{1234567, 4}, 5, 12345670}, // 123.4567 : 123.45670 - {pql.Decimal{1234567, 4}, 4, 1234567}, // 123.4567 : 123.4567 - {pql.Decimal{1234567, 4}, 3, 123456}, // 123.4567 : 123.456 + {pql.NewDecimal(1234567, 4), 5, 12345670}, // 123.4567 : 123.45670 + {pql.NewDecimal(1234567, 4), 4, 1234567}, // 123.4567 : 123.4567 + {pql.NewDecimal(1234567, 4), 3, 123456}, // 123.4567 : 123.456 - {pql.Decimal{-1234567, 4}, 5, -12345670}, // -123.4567 : -123.45670 - {pql.Decimal{-1234567, 4}, 4, -1234567}, // -123.4567 : -123.4567 - {pql.Decimal{-1234567, 4}, 3, -123456}, // -123.4567 : -123.456 + {pql.NewDecimal(-1234567, 4), 5, -12345670}, // -123.4567 : -123.45670 + {pql.NewDecimal(-1234567, 4), 4, -1234567}, // -123.4567 : -123.4567 + {pql.NewDecimal(-1234567, 4), 3, -123456}, // -123.4567 : -123.456 - {pql.Decimal{123, -2}, 5, 1230000000}, // 12300 : 12300.00000 - {pql.Decimal{123, -2}, -1, 1230}, // 12300 : 1230 - {pql.Decimal{123, 1}, -1, 1}, // 12.3 : 1 - {pql.Decimal{123, 1}, -2, 0}, // 12.3 : 0 + {pql.NewDecimal(123, -2), 5, 1230000000}, // 12300 : 12300.00000 + {pql.NewDecimal(123, -2), -1, 1230}, // 12300 : 1230 + {pql.NewDecimal(123, 1), -1, 1}, // 12.3 : 1 + {pql.NewDecimal(123, 1), -2, 0}, // 12.3 : 0 } for i, test := range tests { v := test.dec.ToInt64(test.scale) @@ -248,86 +247,3 @@ func TestDecimal(t *testing.T) { }) }) } - -type testAddDecimalCase struct { - a pql.Decimal - b pql.Decimal - exp pql.Decimal - notOk bool -} - -func TestAddDecimal(t *testing.T) { - toDecimal := func(a interface{}) pql.Decimal { - switch ac := a.(type) { - case string: - return mustParse(t, ac) - case pql.Decimal: - return ac - default: - t.Fatalf("cannot support type %T", ac) - } - return pql.Decimal{} - } - - newTestAddDecimalCase := func(a, b, exp interface{}, notOk bool) testAddDecimalCase { - return testAddDecimalCase{ - a: toDecimal(a), - b: toDecimal(b), - exp: toDecimal(exp), - notOk: notOk, - } - } - - tests := []testAddDecimalCase{ - newTestAddDecimalCase("40.37", "40.37", "80.74", false), - newTestAddDecimalCase("18.5", "9.25", "27.75", false), - newTestAddDecimalCase("18.50", "9.25", "27.75", false), - newTestAddDecimalCase("-18.50", "-9.25", "-27.75", false), - newTestAddDecimalCase("-40.37", "40.37", "0", false), - newTestAddDecimalCase("-50.38", "-50.38", "-100.76", false), - newTestAddDecimalCase("-40.381", "-40.38", "-80.761", false), - newTestAddDecimalCase("-40.3700000000000000000001", "-50.38", "-90.7500000000000000000001", false), - newTestAddDecimalCase("10.37000000000000001", "-10", "0.37000000000000001", false), - // this is a weird one. because we reduce precision when we parse strings, we - // expect the value to be smaller than it really should be if you did - // the math yourself. - // - // there's not really a good way around this unless we use math/big.Int to represent - // our Value/Scale. - // - // but if we did this, to quote seebs: "i suspect that - // our performance would tank so badly by the time we were doing >63-bit - // numbers that there's no real-world benefit to us." - newTestAddDecimalCase("10.370000000000000001", "-10", "0.37", false), - newTestAddDecimalCase("-9223372036854775807", "-9223372036854775807", pql.Decimal{}, true), - newTestAddDecimalCase("9223372036854775807", "9223372036854775807", pql.Decimal{}, true), - newTestAddDecimalCase(pql.Decimal{Value: 0, Scale: 0}, pql.Decimal{Value: 1, Scale: 4}, pql.Decimal{Value: 1, Scale: 4}, false), - } - - for i, test := range tests { - t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { - if got, ok := pql.AddDecimal(test.a, test.b); !got.EqualTo(test.exp) { - t.Logf("%#v + %#v, expected %#v, got %#v", test.a, test.b, test.exp, got) - t.Errorf("%v + %v, expected %v, got %v", test.a, test.b, test.exp, got) - } else if !ok != test.notOk { - t.Errorf("expected %v, got %v", test.notOk, ok) - } - - if got, ok := pql.AddDecimal(test.b, test.a); !got.EqualTo(test.exp) { - t.Logf("%#v + %#v, expected %#v, got %#v", test.a, test.b, test.exp, got) - t.Errorf("%v + %v, expected %v, got %v", test.b, test.a, test.exp, got) - } else if !ok != test.notOk { - t.Errorf("expected %v, got %v", test.notOk, ok) - } - }) - } -} - -func mustParse(t *testing.T, num string) pql.Decimal { - t.Helper() - d, err := pql.ParseDecimal(num) - if err != nil { - t.Fatalf("unexpected error parsing %s to pql.Decimal: %v", num, err) - } - return d -} diff --git a/pql/parser_test.go b/pql/parser_test.go index b829d6079..ebadbbeff 100644 --- a/pql/parser_test.go +++ b/pql/parser_test.go @@ -94,10 +94,10 @@ func TestParser_Parse(t *testing.T) { &pql.Call{ Name: "Row", Args: map[string]interface{}{ - "key": pql.Decimal{1225, 2}, - "foo": pql.Decimal{13167, 3}, - "bar": pql.Decimal{2, 0}, - "baz": pql.Decimal{9, 1}, + "key": pql.NewDecimal(1225, 2), + "foo": pql.NewDecimal(13167, 3), + "bar": pql.NewDecimal(2, 0), + "baz": pql.NewDecimal(9, 1), }, }, ) { @@ -114,7 +114,7 @@ func TestParser_Parse(t *testing.T) { &pql.Call{ Name: "Row", Args: map[string]interface{}{ - "key": pql.Decimal{-1225, 2}, + "key": pql.NewDecimal(-1225, 2), "foo": int64(-13), }, }, @@ -170,7 +170,7 @@ func TestParser_Parse(t *testing.T) { Name: "Row", Args: map[string]interface{}{ "key": "foo", - "x": &pql.Condition{Op: pql.EQ, Value: pql.Decimal{1225, 2}}, + "x": &pql.Condition{Op: pql.EQ, Value: pql.NewDecimal(1225, 2)}, "y": &pql.Condition{Op: pql.GTE, Value: int64(100)}, "z": &pql.Condition{Op: pql.BETWEEN, Value: []interface{}{int64(4), int64(8)}}, "m": &pql.Condition{Op: pql.NEQ, Value: nil}, diff --git a/server/grpc.go b/server/grpc.go index 451817e58..ed2954b87 100644 --- a/server/grpc.go +++ b/server/grpc.go @@ -1044,8 +1044,9 @@ func (h *GRPCHandler) Inspect(req *pb.InspectRequest, stream pb.Pilosa_InspectSe if len(resp.Results) > 0 { valCount, ok := resp.Results[0].(pilosa.ValCount) if ok && valCount.Count == 1 { + valVal := valCount.DecimalVal.Value() rowResp.Columns = append(rowResp.Columns, - &pb.ColumnResponse{ColumnVal: &pb.ColumnResponse_DecimalVal{DecimalVal: &pb.Decimal{Value: valCount.DecimalVal.Value, Scale: valCount.DecimalVal.Scale}}}) + &pb.ColumnResponse{ColumnVal: &pb.ColumnResponse_DecimalVal{DecimalVal: &pb.Decimal{Value: (&valVal).Int64(), Scale: valCount.DecimalVal.Scale}}}) colAdded++ } else { rowResp.Columns = append(rowResp.Columns, @@ -1371,8 +1372,9 @@ func (h *GRPCHandler) Inspect(req *pb.InspectRequest, stream pb.Pilosa_InspectSe if len(resp.Results) > 0 { valCount, ok := resp.Results[0].(pilosa.ValCount) if ok && valCount.Count == 1 { + valVal := valCount.DecimalVal.Value() rowResp.Columns = append(rowResp.Columns, - &pb.ColumnResponse{ColumnVal: &pb.ColumnResponse_DecimalVal{DecimalVal: &pb.Decimal{Value: valCount.DecimalVal.Value, Scale: valCount.DecimalVal.Scale}}}) + &pb.ColumnResponse{ColumnVal: &pb.ColumnResponse_DecimalVal{DecimalVal: &pb.Decimal{Value: (&valVal).Int64(), Scale: valCount.DecimalVal.Scale}}}) colAdded++ } else { rowResp.Columns = append(rowResp.Columns, diff --git a/server/handler_test.go b/server/handler_test.go index 1f8225bba..72d10ae04 100644 --- a/server/handler_test.go +++ b/server/handler_test.go @@ -263,7 +263,7 @@ func TestHandler_Endpoints(t *testing.T) { } } - f, err = i2.CreateFieldIfNotExists("f2", pilosa.OptFieldTypeDecimal(1, pql.Decimal{Value: -10}, pql.Decimal{Value: 10})) + f, err = i2.CreateFieldIfNotExists("f2", pilosa.OptFieldTypeDecimal(1, pql.NewDecimal(-10, 0), pql.NewDecimal(10, 0))) if err != nil { t.Fatal(err) } @@ -744,10 +744,10 @@ func TestHandler_Endpoints(t *testing.T) { } if field != nil { // happy linter if !reflect.DeepEqual(pql.NewDecimal(math.MinInt64, 0), field.Options.Min) { - t.Fatalf("field min %d != %d", int64(math.MinInt64), field.Options.Min) + t.Fatalf("field min %v != %v", int64(math.MinInt64), field.Options.Min) } if !reflect.DeepEqual(pql.NewDecimal(math.MaxInt64, 0), field.Options.Max) { - t.Fatalf("field max %d != %d", int64(math.MaxInt64), field.Options.Max) + t.Fatalf("field max %v != %v", int64(math.MaxInt64), field.Options.Max) } } }) @@ -775,10 +775,10 @@ func TestHandler_Endpoints(t *testing.T) { } if field != nil { // happy linter if !reflect.DeepEqual(pql.NewDecimal(math.MinInt64, 0), field.Options.Min) { - t.Fatalf("field min %d != %d", int64(math.MinInt64), field.Options.Min) + t.Fatalf("field min %v != %v", int64(math.MinInt64), field.Options.Min) } if !reflect.DeepEqual(pql.NewDecimal(1, -1), field.Options.Max) { - t.Fatalf("field max %d != %d", 10, field.Options.Max) + t.Fatalf("field max %v != %v", 10, field.Options.Max) } } }) @@ -806,10 +806,10 @@ func TestHandler_Endpoints(t *testing.T) { } if field != nil { // happy linter if !reflect.DeepEqual(pql.NewDecimal(-1, -1), field.Options.Min) { - t.Fatalf("field min %d != %d", 10, field.Options.Min) + t.Fatalf("field min %v != %v", 10, field.Options.Min) } if !reflect.DeepEqual(pql.NewDecimal(math.MaxInt64, 0), field.Options.Max) { - t.Fatalf("field max %d != %d", int64(math.MaxInt64), field.Options.Max) + t.Fatalf("field max %v != %v", int64(math.MaxInt64), field.Options.Max) } } }) @@ -847,10 +847,10 @@ func TestHandler_Endpoints(t *testing.T) { } if field != nil { // happy linter if !reflect.DeepEqual(pql.NewDecimal(math.MinInt64, 0), field.Options.Min) { - t.Fatalf("field min %d != %d", int64(math.MinInt64), field.Options.Min) + t.Fatalf("field min %v != %v", int64(math.MinInt64), field.Options.Min) } if !reflect.DeepEqual(pql.NewDecimal(math.MaxInt64, 0), field.Options.Max) { - t.Fatalf("field max %d != %d", int64(math.MaxInt64), field.Options.Max) + t.Fatalf("field max %v != %v", int64(math.MaxInt64), field.Options.Max) } } }) @@ -879,10 +879,10 @@ func TestHandler_Endpoints(t *testing.T) { } if field != nil { // happy linter if !reflect.DeepEqual(pql.NewDecimal(math.MinInt64, 1), field.Options.Min) { - t.Fatalf("field min %d != %d", pql.NewDecimal(math.MinInt64, 1), field.Options.Min) + t.Fatalf("field min %v != %v", pql.NewDecimal(math.MinInt64, 1), field.Options.Min) } if !reflect.DeepEqual(pql.NewDecimal(105, 1), field.Options.Max) { - t.Fatalf("field max %s != %d", pql.NewDecimal(105, 1), field.Options.Max) + t.Fatalf("field max %v != %v", pql.NewDecimal(105, 1), field.Options.Max) } } }) @@ -910,10 +910,10 @@ func TestHandler_Endpoints(t *testing.T) { } if field != nil { // happy linter if !reflect.DeepEqual(pql.NewDecimal(math.MinInt64, 2), field.Options.Min) { - t.Fatalf("field min %d != %d", pql.NewDecimal(math.MinInt64, 1), field.Options.Min) + t.Fatalf("field min %v != %v", pql.NewDecimal(math.MinInt64, 1), field.Options.Min) } if !reflect.DeepEqual(pql.NewDecimal(math.MaxInt64, 2), field.Options.Max) { - t.Fatalf("field min %d != %d", pql.NewDecimal(math.MaxInt64, 2), field.Options.Max) + t.Fatalf("field min %v != %v", pql.NewDecimal(math.MaxInt64, 2), field.Options.Max) } } }) diff --git a/server/pg.go b/server/pg.go index 87b1f8fa8..d529b6254 100644 --- a/server/pg.go +++ b/server/pg.go @@ -467,10 +467,10 @@ func pgWriteRowser(w pg.QueryResultWriter, result pb.ToRowser) error { case *pb.ColumnResponse_BoolVal: v = strconv.FormatBool(col.BoolVal) case *pb.ColumnResponse_DecimalVal: - v = pql.Decimal{ - Value: col.DecimalVal.Value, - Scale: col.DecimalVal.Scale, - }.String() + v = pql.NewDecimal( + col.DecimalVal.Value, + col.DecimalVal.Scale, + ).String() case *pb.ColumnResponse_Float64Val: v = strconv.FormatFloat(col.Float64Val, 'g', -1, 64) case *pb.ColumnResponse_Int64Val: diff --git a/server/server_test.go b/server/server_test.go index 0c5970381..8ef2290da 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -197,8 +197,8 @@ func TestMain_MinMaxFloat(t *testing.T) { } // Query row. - exp0 := pilosa.ValCount{DecimalVal: &pql.Decimal{Value: 4440, Scale: 3}, Count: 1} - exp1 := pilosa.ValCount{DecimalVal: &pql.Decimal{Value: 1320, Scale: 3}, Count: 1} + exp0 := pilosa.ValCount{DecimalVal: pql.NewDecimal(4440, 3).Clone(), Count: 1} + exp1 := pilosa.ValCount{DecimalVal: pql.NewDecimal(1320, 3).Clone(), Count: 1} if res, err := m.QueryProtobuf("i", `Max(field=dec) Min(field=dec)`); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(res.Results[0], exp0) || !reflect.DeepEqual(res.Results[1], exp1) {