mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-09-07 09:05:55 +00:00
Merge pull request #227 from travisturner/backout-decimal
back out the pql.Decimal changes
This commit is contained in:
commit
e77bf45643
18 changed files with 401 additions and 1148 deletions
34
api_test.go
34
api_test.go
|
|
@ -447,9 +447,39 @@ func TestAPI_ImportValue(t *testing.T) {
|
|||
t.Fatalf("creating index: %v", err)
|
||||
}
|
||||
_, err = m0.API.CreateField(ctx, index, field, pilosa.OptFieldTypeDecimal(-1))
|
||||
if err == nil {
|
||||
t.Fatal("expected error creating field")
|
||||
if err != nil {
|
||||
t.Fatalf("creating field: %v", err)
|
||||
}
|
||||
|
||||
// Generate some keyed records.
|
||||
values := []float64{}
|
||||
colIDs := []uint64{}
|
||||
for i := 0; i < 10; i++ {
|
||||
values = append(values, float64(i)*100+10)
|
||||
colIDs = append(colIDs, uint64(i))
|
||||
}
|
||||
|
||||
// Import data with keys to the coordinator (node0) and verify that it gets
|
||||
// translated and forwarded to the owner of shard 0 (node1; because of offsetModHasher)
|
||||
req := &pilosa.ImportValueRequest{
|
||||
Index: index,
|
||||
Field: field,
|
||||
ColumnIDs: colIDs,
|
||||
FloatValues: values,
|
||||
}
|
||||
if err := m1.API.ImportValue(ctx, req); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
pql := fmt.Sprintf("Row(%s>600)", field)
|
||||
|
||||
// Query node0.
|
||||
if res, err := m0.API.Query(ctx, &pilosa.QueryRequest{Index: index, Query: pql}); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if ids := res.Results[0].(*pilosa.Row).Columns(); !reflect.DeepEqual(ids, colIDs[6:]) {
|
||||
t.Fatalf("unexpected column keys: %+v", ids)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
t.Run("ValStringField", func(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -54,8 +54,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.Int64Var(&Importer.FieldOptions.Min, "field-min", 0, "Specify the minimum for an int field on creation")
|
||||
flags.Int64Var(&Importer.FieldOptions.Max, "field-max", 0, "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")
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ import (
|
|||
"github.com/pilosa/pilosa/v2"
|
||||
|
||||
"github.com/pilosa/pilosa/v2/cmd"
|
||||
"github.com/pilosa/pilosa/v2/pql"
|
||||
)
|
||||
|
||||
func TestImportHelp(t *testing.T) {
|
||||
|
|
@ -59,8 +58,8 @@ field = "f1"
|
|||
v.Check(cmd.Importer.Field, "f1")
|
||||
v.Check(cmd.Importer.FieldOptions, pilosa.FieldOptions{
|
||||
Keys: true,
|
||||
Max: pql.NewDecimal(100, 0),
|
||||
Min: pql.NewDecimal(-10, 0),
|
||||
Max: 100,
|
||||
Min: -10,
|
||||
CacheType: pilosa.CacheTypeRanked,
|
||||
CacheSize: 50000,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ import (
|
|||
|
||||
"github.com/pilosa/pilosa/v2"
|
||||
"github.com/pilosa/pilosa/v2/http"
|
||||
"github.com/pilosa/pilosa/v2/pql"
|
||||
"github.com/pilosa/pilosa/v2/server"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
|
@ -105,7 +104,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 != 0 || cmd.FieldOptions.Max != 0 {
|
||||
cmd.FieldOptions.Type = pilosa.FieldTypeInt
|
||||
} else {
|
||||
cmd.FieldOptions.Type = pilosa.FieldTypeSet
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ import (
|
|||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/pilosa/pilosa/v2"
|
||||
"github.com/pilosa/pilosa/v2/internal"
|
||||
"github.com/pilosa/pilosa/v2/pql"
|
||||
"github.com/pilosa/pilosa/v2/roaring"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
|
@ -612,8 +611,8 @@ func encodeFieldOptions(o *pilosa.FieldOptions) *internal.FieldOptions {
|
|||
Type: o.Type,
|
||||
CacheType: o.CacheType,
|
||||
CacheSize: o.CacheSize,
|
||||
Min: &internal.Decimal{Value: o.Min.Value, Scale: o.Min.Scale},
|
||||
Max: &internal.Decimal{Value: o.Max.Value, Scale: o.Max.Scale},
|
||||
Min: o.Min,
|
||||
Max: o.Max,
|
||||
Base: o.Base,
|
||||
Scale: o.Scale,
|
||||
BitDepth: uint64(o.BitDepth),
|
||||
|
|
@ -916,8 +915,8 @@ func decodeFieldOptions(options *internal.FieldOptions, m *pilosa.FieldOptions)
|
|||
m.Type = options.Type
|
||||
m.CacheType = options.CacheType
|
||||
m.CacheSize = options.CacheSize
|
||||
decodeDecimal(options.Min, &m.Min)
|
||||
decodeDecimal(options.Max, &m.Max)
|
||||
m.Min = options.Min
|
||||
m.Max = options.Max
|
||||
m.Base = options.Base
|
||||
m.Scale = options.Scale
|
||||
m.BitDepth = uint(options.BitDepth)
|
||||
|
|
@ -926,11 +925,6 @@ func decodeFieldOptions(options *internal.FieldOptions, m *pilosa.FieldOptions)
|
|||
m.ForeignIndex = options.ForeignIndex
|
||||
}
|
||||
|
||||
func decodeDecimal(d *internal.Decimal, m *pql.Decimal) {
|
||||
m.Value = d.Value
|
||||
m.Scale = d.Scale
|
||||
}
|
||||
|
||||
func decodeNodes(a []*internal.Node, m []*pilosa.Node) {
|
||||
for i := range a {
|
||||
m[i] = &pilosa.Node{}
|
||||
|
|
@ -1245,7 +1239,7 @@ func decodeQueryResult(pb *internal.QueryResult) interface{} {
|
|||
panic(fmt.Sprintf("unknown type: %d", pb.Type))
|
||||
}
|
||||
|
||||
// decodeRow converts r from its internal representation.
|
||||
// DecodeRow converts r from its internal representation.
|
||||
func decodeRow(pr *internal.Row) *pilosa.Row {
|
||||
if pr == nil {
|
||||
return pilosa.NewRow()
|
||||
|
|
|
|||
|
|
@ -1478,14 +1478,14 @@ func TestExecutor_Execute_MinMax(t *testing.T) {
|
|||
|
||||
tests := []struct {
|
||||
scale int64
|
||||
min pql.Decimal
|
||||
max pql.Decimal
|
||||
min int64
|
||||
max int64
|
||||
set pql.Decimal
|
||||
}{
|
||||
{2, pql.Decimal{Value: 1, Scale: -1}, pql.Decimal{Value: 2, Scale: -1}, pql.Decimal{Value: 115, Scale: 1}},
|
||||
{2, pql.Decimal{Value: -1, Scale: -1}, pql.Decimal{Value: 2, Scale: -1}, pql.Decimal{Value: 115, Scale: 1}},
|
||||
{2, pql.Decimal{Value: -1, Scale: -1}, pql.Decimal{Value: 2, Scale: -1}, pql.Decimal{Value: -95, Scale: 1}},
|
||||
{2, pql.Decimal{Value: -2, Scale: -1}, pql.Decimal{Value: -1, Scale: -1}, pql.Decimal{Value: -115, Scale: 1}},
|
||||
{2, 10, 20, pql.Decimal{Value: 115, Scale: 1}},
|
||||
{2, -10, 20, pql.Decimal{Value: 115, Scale: 1}},
|
||||
{2, -10, 20, pql.Decimal{Value: -95, Scale: 1}},
|
||||
{2, -20, -10, pql.Decimal{Value: -115, Scale: 1}},
|
||||
}
|
||||
for i, test := range tests {
|
||||
fld := fmt.Sprintf("f%d", i)
|
||||
|
|
@ -2249,37 +2249,6 @@ func TestExecutor_Execute_Range_Deprecated(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
// Ensure decimal args are supported for Decimal fields.
|
||||
func TestExecutor_DecimalArgs(t *testing.T) {
|
||||
c := test.MustRunCluster(t, 1)
|
||||
defer c.Close()
|
||||
hldr := test.Holder{Holder: c[0].Server.Holder()}
|
||||
|
||||
idx, err := hldr.CreateIndex("i", pilosa.IndexOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
min, err := pql.ParseDecimal("-10.5")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
max, err := pql.ParseDecimal("10.5")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := idx.CreateField("f", pilosa.OptFieldTypeDecimal(2, min, max)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `
|
||||
Set(0, f=0)
|
||||
`}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure a Row(bsiGroup) query can be executed.
|
||||
func TestExecutor_Execute_Row_BSIGroup(t *testing.T) {
|
||||
c := test.MustRunCluster(t, 1)
|
||||
|
|
|
|||
149
field.go
149
field.go
|
|
@ -178,80 +178,55 @@ func OptFieldTypeInt(min, max int64) FieldOption {
|
|||
return errors.New("int field min cannot be greater than max")
|
||||
}
|
||||
fo.Type = FieldTypeInt
|
||||
fo.Min = pql.NewDecimal(min, 0)
|
||||
fo.Max = pql.NewDecimal(max, 0)
|
||||
fo.Min = min
|
||||
fo.Max = max
|
||||
fo.Base = bsiBase(min, max)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// OptFieldTypeDecimal is a functional option for creating a `decimal` field.
|
||||
// Unless we decide to expand the range of supported values, `scale` is
|
||||
// restricted to the range [0,19]. This supports anything from:
|
||||
//
|
||||
// scale = 0:
|
||||
// min: -9223372036854775808.
|
||||
// max: 9223372036854775807.
|
||||
//
|
||||
// to:
|
||||
//
|
||||
// scale = 19:
|
||||
// min: -0.9223372036854775808
|
||||
// max: 0.9223372036854775807
|
||||
//
|
||||
// While it's possible to support scale values outside of this range,
|
||||
// the coverage for those scales are no longer continuous. For example,
|
||||
//
|
||||
// scale = -2:
|
||||
// min : [-922337203685477580800, -100]
|
||||
// GAPs: [-99, -1], [-199, -101] ... [-922337203685477580799, -922337203685477580701]
|
||||
// 0
|
||||
// max : [100, 922337203685477580700]
|
||||
// GAPs: [1, 99], [101, 199] ... [922337203685477580601, 922337203685477580699]
|
||||
//
|
||||
// An alternative to this gap strategy would be to scale the supported range
|
||||
// to a continuous 64-bit space (which is not unreasonable using bsiGroup.Base).
|
||||
// The issue with this approach is that we would need to know which direction
|
||||
// to favor. For example, there are two possible ranges for `scale = -2`:
|
||||
//
|
||||
// min : [-922337203685477580800, -922337203685477580800+(2^64)]
|
||||
// max : [922337203685477580700-(2^64), 922337203685477580700]
|
||||
//
|
||||
func OptFieldTypeDecimal(scale int64, minmax ...pql.Decimal) FieldOption {
|
||||
func OptFieldTypeDecimal(scale int64, minmax ...int64) FieldOption {
|
||||
return func(fo *FieldOptions) error {
|
||||
if fo.Type != "" {
|
||||
return errors.Errorf("can't set field type to 'decimal', already set to: %s", fo.Type)
|
||||
}
|
||||
if scale < 0 || scale > 19 {
|
||||
return errors.Errorf("scale values outside the range [0,19] are not supported: %d", scale)
|
||||
}
|
||||
|
||||
fo.Min, fo.Max = pql.MinMax(scale)
|
||||
fo.Min = math.MinInt64
|
||||
fo.Max = math.MaxInt64
|
||||
if len(minmax) == 2 {
|
||||
min := minmax[0]
|
||||
max := minmax[1]
|
||||
if !min.IsValid() || !max.IsValid() {
|
||||
return errors.Errorf("min/max range %s-%s is not supported", min, max)
|
||||
} else if !min.SupportedByScale(scale) || !max.SupportedByScale(scale) {
|
||||
return errors.Errorf("min/max range %s-%s is not supported by scale %d", min, max, scale)
|
||||
} else if min.GreaterThan(max) {
|
||||
return errors.Errorf("decimal field min cannot be greater than max, got %s, %s", min, max)
|
||||
min, max := minmax[0], minmax[1]
|
||||
if scale != 0 {
|
||||
// If the min/max provided are already on the boundary of int64,
|
||||
// then we don't want to operate on them and cause overflow.
|
||||
// There are still overflow scenarios where a user provides a
|
||||
// min/max which is not on the boundary, but overflow once the
|
||||
// scale is applied. This does not address those cases, but at
|
||||
// least it addresses the default case (where a min/max is not
|
||||
// provided).
|
||||
if min != math.MinInt64 {
|
||||
min = int64(float64(min) * math.Pow10(int(scale)))
|
||||
}
|
||||
if max != math.MaxInt64 {
|
||||
max = int64(float64(max) * math.Pow10(int(scale)))
|
||||
}
|
||||
}
|
||||
if min > max {
|
||||
return errors.Errorf("decimal field min cannot be greater than max, got %d, %d", min, max)
|
||||
}
|
||||
fo.Min = min
|
||||
fo.Max = max
|
||||
} else if len(minmax) > 2 {
|
||||
return errors.Errorf("unknown extra parameters beyond min and max: %v", minmax)
|
||||
} else if len(minmax) == 1 {
|
||||
min := minmax[0]
|
||||
if !min.IsValid() {
|
||||
return errors.Errorf("min %s is not supported", min)
|
||||
} else if !min.SupportedByScale(scale) {
|
||||
return errors.Errorf("min %s is not supported by scale %d", min, scale)
|
||||
// It's not necessary to handle the scale==0 case separately,
|
||||
// but it avoids the type conversion.
|
||||
if scale == 0 || minmax[0] == math.MinInt64 {
|
||||
fo.Min = minmax[0]
|
||||
} else {
|
||||
fo.Min = int64(float64(minmax[0]) * math.Pow10(int(scale)))
|
||||
}
|
||||
fo.Min = min
|
||||
}
|
||||
fo.Type = FieldTypeDecimal
|
||||
fo.Base = bsiBase(fo.Min.ToInt64(scale), fo.Max.ToInt64(scale))
|
||||
fo.Base = bsiBase(fo.Min, fo.Max)
|
||||
fo.Scale = scale
|
||||
return nil
|
||||
}
|
||||
|
|
@ -717,14 +692,10 @@ func (f *Field) loadMeta() error {
|
|||
}
|
||||
}
|
||||
|
||||
min := pql.NewDecimal(pb.Min.Value, pb.Min.Scale)
|
||||
max := pql.NewDecimal(pb.Max.Value, pb.Max.Scale)
|
||||
|
||||
// Initialize "base" to "min" when upgrading from v1 BSI format.
|
||||
if pb.BitDepth == 0 {
|
||||
minInt64, maxInt64 := min.ToInt64(0), max.ToInt64(0)
|
||||
pb.Base = bsiBase(minInt64, maxInt64)
|
||||
pb.BitDepth = uint64(bitDepthInt64(maxInt64 - minInt64))
|
||||
pb.Base = bsiBase(pb.Min, pb.Max)
|
||||
pb.BitDepth = uint64(bitDepthInt64(pb.Max - pb.Min))
|
||||
if pb.BitDepth == 0 {
|
||||
pb.BitDepth = 1
|
||||
}
|
||||
|
|
@ -734,8 +705,8 @@ func (f *Field) loadMeta() error {
|
|||
f.options.Type = pb.Type
|
||||
f.options.CacheType = pb.CacheType
|
||||
f.options.CacheSize = pb.CacheSize
|
||||
f.options.Min = min
|
||||
f.options.Max = max
|
||||
f.options.Min = pb.Min
|
||||
f.options.Max = pb.Max
|
||||
f.options.Base = pb.Base
|
||||
f.options.Scale = pb.Scale
|
||||
f.options.BitDepth = uint(pb.BitDepth)
|
||||
|
|
@ -795,8 +766,8 @@ func (f *Field) applyOptions(opt FieldOptions) error {
|
|||
} else if opt.CacheSize != 0 {
|
||||
f.options.CacheSize = opt.CacheSize
|
||||
}
|
||||
f.options.Min = pql.Decimal{}
|
||||
f.options.Max = pql.Decimal{}
|
||||
f.options.Min = 0
|
||||
f.options.Max = 0
|
||||
f.options.Base = 0
|
||||
f.options.BitDepth = 0
|
||||
f.options.TimeQuantum = ""
|
||||
|
|
@ -819,8 +790,8 @@ func (f *Field) applyOptions(opt FieldOptions) error {
|
|||
bsig := &bsiGroup{
|
||||
Name: f.name,
|
||||
Type: bsiGroupTypeInt,
|
||||
Min: opt.Min.ToInt64(opt.Scale),
|
||||
Max: opt.Max.ToInt64(opt.Scale),
|
||||
Min: opt.Min,
|
||||
Max: opt.Max,
|
||||
Base: opt.Base,
|
||||
Scale: opt.Scale,
|
||||
BitDepth: opt.BitDepth,
|
||||
|
|
@ -836,8 +807,8 @@ func (f *Field) applyOptions(opt FieldOptions) error {
|
|||
f.options.Type = opt.Type
|
||||
f.options.CacheType = CacheTypeNone
|
||||
f.options.CacheSize = 0
|
||||
f.options.Min = pql.Decimal{}
|
||||
f.options.Max = pql.Decimal{}
|
||||
f.options.Min = 0
|
||||
f.options.Max = 0
|
||||
f.options.Base = 0
|
||||
f.options.BitDepth = 0
|
||||
f.options.Keys = opt.Keys
|
||||
|
|
@ -852,8 +823,8 @@ func (f *Field) applyOptions(opt FieldOptions) error {
|
|||
f.options.Type = FieldTypeBool
|
||||
f.options.CacheType = CacheTypeNone
|
||||
f.options.CacheSize = 0
|
||||
f.options.Min = pql.Decimal{}
|
||||
f.options.Max = pql.Decimal{}
|
||||
f.options.Min = 0
|
||||
f.options.Max = 0
|
||||
f.options.Base = 0
|
||||
f.options.BitDepth = 0
|
||||
f.options.TimeQuantum = ""
|
||||
|
|
@ -1847,8 +1818,8 @@ func (p fieldInfoSlice) Less(i, j int) bool { return p[i].Name < p[j].Name }
|
|||
type FieldOptions struct {
|
||||
Base int64 `json:"base,omitempty"`
|
||||
BitDepth uint `json:"bitDepth,omitempty"`
|
||||
Min pql.Decimal `json:"min,omitempty"`
|
||||
Max pql.Decimal `json:"max,omitempty"`
|
||||
Min int64 `json:"min,omitempty"`
|
||||
Max int64 `json:"max,omitempty"`
|
||||
Scale int64 `json:"scale,omitempty"`
|
||||
Keys bool `json:"keys"`
|
||||
NoStandardView bool `json:"noStandardView,omitempty"`
|
||||
|
|
@ -1910,8 +1881,8 @@ func encodeFieldOptions(o *FieldOptions) *internal.FieldOptions {
|
|||
Base: o.Base,
|
||||
Scale: o.Scale,
|
||||
BitDepth: uint64(o.BitDepth),
|
||||
Min: &internal.Decimal{Value: o.Min.Value, Scale: o.Min.Scale},
|
||||
Max: &internal.Decimal{Value: o.Max.Value, Scale: o.Max.Scale},
|
||||
Min: o.Min,
|
||||
Max: o.Max,
|
||||
TimeQuantum: string(o.TimeQuantum),
|
||||
Keys: o.Keys,
|
||||
NoStandardView: o.NoStandardView,
|
||||
|
|
@ -1938,13 +1909,13 @@ func (o *FieldOptions) MarshalJSON() ([]byte, error) {
|
|||
})
|
||||
case FieldTypeInt:
|
||||
return json.Marshal(struct {
|
||||
Type string `json:"type"`
|
||||
Base int64 `json:"base"`
|
||||
BitDepth uint `json:"bitDepth"`
|
||||
Min pql.Decimal `json:"min"`
|
||||
Max pql.Decimal `json:"max"`
|
||||
Keys bool `json:"keys"`
|
||||
ForeignIndex string `json:"foreignIndex"`
|
||||
Type string `json:"type"`
|
||||
Base int64 `json:"base"`
|
||||
BitDepth uint `json:"bitDepth"`
|
||||
Min int64 `json:"min"`
|
||||
Max int64 `json:"max"`
|
||||
Keys bool `json:"keys"`
|
||||
ForeignIndex string `json:"foreignIndex"`
|
||||
}{
|
||||
o.Type,
|
||||
o.Base,
|
||||
|
|
@ -1956,13 +1927,13 @@ func (o *FieldOptions) MarshalJSON() ([]byte, error) {
|
|||
})
|
||||
case FieldTypeDecimal:
|
||||
return json.Marshal(struct {
|
||||
Type string `json:"type"`
|
||||
Base int64 `json:"base"`
|
||||
Scale int64 `json:"scale"`
|
||||
BitDepth uint `json:"bitDepth"`
|
||||
Min pql.Decimal `json:"min"`
|
||||
Max pql.Decimal `json:"max"`
|
||||
Keys bool `json:"keys"`
|
||||
Type string `json:"type"`
|
||||
Base int64 `json:"base"`
|
||||
Scale int64 `json:"scale"`
|
||||
BitDepth uint `json:"bitDepth"`
|
||||
Min int64 `json:"min"`
|
||||
Max int64 `json:"max"`
|
||||
Keys bool `json:"keys"`
|
||||
}{
|
||||
o.Type,
|
||||
o.Base,
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import (
|
|||
"path/filepath"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
|
@ -161,7 +160,7 @@ func TestBSIGroup_BaseValue(t *testing.T) {
|
|||
|
||||
// Ensure field can open and retrieve a view.
|
||||
func TestField_DeleteView(t *testing.T) {
|
||||
f := OpenField(t, OptFieldTypeDefault())
|
||||
f := MustOpenField(OptFieldTypeDefault())
|
||||
defer f.Close()
|
||||
|
||||
viewName := viewStandard + "_v"
|
||||
|
|
@ -198,23 +197,23 @@ type TestField struct {
|
|||
}
|
||||
|
||||
// NewTestField returns a new instance of TestField d/0.
|
||||
func NewTestField(t *testing.T, opts FieldOption) *TestField {
|
||||
func NewTestField(opts FieldOption) *TestField {
|
||||
path, err := ioutil.TempDir(*TempDir, "pilosa-field-")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
panic(err)
|
||||
}
|
||||
field, err := NewField(path, "i", "f", opts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
panic(err)
|
||||
}
|
||||
return &TestField{Field: field}
|
||||
}
|
||||
|
||||
// OpenField returns a new, opened field at a temporary path.
|
||||
func OpenField(t *testing.T, opts FieldOption) *TestField {
|
||||
f := NewTestField(t, opts)
|
||||
// MustOpenField returns a new, opened field at a temporary path. Panic on error.
|
||||
func MustOpenField(opts FieldOption) *TestField {
|
||||
f := NewTestField(opts)
|
||||
if err := f.Open(); err != nil {
|
||||
t.Fatal(err)
|
||||
panic(err)
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
|
@ -261,7 +260,7 @@ func (f *TestField) MustSetBit(row, col uint64, ts ...time.Time) {
|
|||
|
||||
// Ensure field can open and retrieve a view.
|
||||
func TestField_CreateViewIfNotExists(t *testing.T) {
|
||||
f := OpenField(t, OptFieldTypeDefault())
|
||||
f := MustOpenField(OptFieldTypeDefault())
|
||||
defer f.Close()
|
||||
|
||||
// Create view.
|
||||
|
|
@ -286,7 +285,7 @@ func TestField_CreateViewIfNotExists(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestField_SetTimeQuantum(t *testing.T) {
|
||||
f := OpenField(t, OptFieldTypeTime(TimeQuantum("")))
|
||||
f := MustOpenField(OptFieldTypeTime(TimeQuantum("")))
|
||||
defer f.Close()
|
||||
|
||||
// Set & retrieve time quantum.
|
||||
|
|
@ -305,7 +304,7 @@ func TestField_SetTimeQuantum(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestField_RowTime(t *testing.T) {
|
||||
f := OpenField(t, OptFieldTypeTime(TimeQuantum("")))
|
||||
f := MustOpenField(OptFieldTypeTime(TimeQuantum("")))
|
||||
defer f.Close()
|
||||
|
||||
if err := f.setTimeQuantum(TimeQuantum("YMDH")); err != nil {
|
||||
|
|
@ -351,7 +350,7 @@ func TestField_RowTime(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestField_PersistAvailableShards(t *testing.T) {
|
||||
f := OpenField(t, OptFieldTypeDefault())
|
||||
f := MustOpenField(OptFieldTypeDefault())
|
||||
|
||||
// bm represents remote available shards.
|
||||
bm := roaring.NewBitmap(1, 2, 3)
|
||||
|
|
@ -370,7 +369,7 @@ func TestField_PersistAvailableShards(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestField_CorruptAvailableShards(t *testing.T) {
|
||||
f := OpenField(t, OptFieldTypeDefault())
|
||||
f := MustOpenField(OptFieldTypeDefault())
|
||||
|
||||
// bm represents remote available shards.
|
||||
bm := roaring.NewBitmap(1, 2, 3)
|
||||
|
|
@ -400,7 +399,7 @@ func TestField_CorruptAvailableShards(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestField_TruncatedAvailableShards(t *testing.T) {
|
||||
f := OpenField(t, OptFieldTypeDefault())
|
||||
f := MustOpenField(OptFieldTypeDefault())
|
||||
|
||||
// bm represents remote available shards.
|
||||
bm := roaring.NewBitmap(1, 2, 3)
|
||||
|
|
@ -428,7 +427,7 @@ func TestField_TruncatedAvailableShards(t *testing.T) {
|
|||
// Ensure that persisting available shards having a smaller footprint (for example,
|
||||
// when going from a bitmap to a smaller, RLE representation) succeeds.
|
||||
func TestField_PersistAvailableShardsFootprint(t *testing.T) {
|
||||
f := OpenField(t, OptFieldTypeDefault())
|
||||
f := MustOpenField(OptFieldTypeDefault())
|
||||
|
||||
// bm represents remote available shards.
|
||||
bm := roaring.NewBitmap()
|
||||
|
|
@ -539,7 +538,7 @@ func TestField_ApplyOptions(t *testing.T) {
|
|||
// into consideration. This would cause an import of 1/8/1
|
||||
// to result in a value of 9 instead of 1.
|
||||
func TestBSIGroup_importValue(t *testing.T) {
|
||||
f := OpenField(t, OptFieldTypeInt(-100, 200))
|
||||
f := MustOpenField(OptFieldTypeInt(-100, 200))
|
||||
|
||||
options := &ImportOptions{}
|
||||
for i, tt := range []struct {
|
||||
|
|
@ -580,7 +579,7 @@ func TestBSIGroup_importValue(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestIntField_MinMaxForShard(t *testing.T) {
|
||||
f := OpenField(t, OptFieldTypeInt(-100, 200))
|
||||
f := MustOpenField(OptFieldTypeInt(-100, 200))
|
||||
|
||||
options := &ImportOptions{}
|
||||
for i, test := range []struct {
|
||||
|
|
@ -655,86 +654,33 @@ func TestIntField_MinMaxForShard(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// Ensure we get errors when they are expected.
|
||||
func TestDecimalField_MinMaxBoundaries(t *testing.T) {
|
||||
for i, test := range []struct {
|
||||
min int64
|
||||
max int64
|
||||
scale int64
|
||||
min pql.Decimal
|
||||
max pql.Decimal
|
||||
expErr bool
|
||||
expmin int64
|
||||
expmax int64
|
||||
}{
|
||||
{
|
||||
scale: 3,
|
||||
min: pql.NewDecimal(math.MinInt64, 0),
|
||||
max: pql.NewDecimal(math.MaxInt64, 0),
|
||||
expErr: true,
|
||||
},
|
||||
{
|
||||
scale: 3,
|
||||
min: pql.NewDecimal(math.MinInt64, 3),
|
||||
max: pql.NewDecimal(math.MaxInt64, 3),
|
||||
expErr: false,
|
||||
},
|
||||
{
|
||||
scale: 3,
|
||||
min: pql.NewDecimal(44, 0),
|
||||
max: pql.NewDecimal(88, 0),
|
||||
expErr: false,
|
||||
},
|
||||
{
|
||||
scale: 3,
|
||||
min: pql.NewDecimal(-44, 0),
|
||||
max: pql.NewDecimal(88, 0),
|
||||
expErr: false,
|
||||
},
|
||||
{
|
||||
scale: 19,
|
||||
min: pql.NewDecimal(1, 0),
|
||||
max: pql.NewDecimal(2, 0),
|
||||
expErr: true,
|
||||
},
|
||||
{
|
||||
scale: 19,
|
||||
min: pql.NewDecimal(math.MinInt64, 18),
|
||||
max: pql.NewDecimal(math.MaxInt64, 18),
|
||||
expErr: true,
|
||||
},
|
||||
{
|
||||
scale: 0,
|
||||
min: pql.NewDecimal(1, 20),
|
||||
max: pql.NewDecimal(2, 20),
|
||||
expErr: true,
|
||||
},
|
||||
{
|
||||
scale: 0,
|
||||
min: pql.NewDecimal(1, -1),
|
||||
max: pql.NewDecimal(2, -1),
|
||||
expErr: false,
|
||||
},
|
||||
{
|
||||
scale: 0,
|
||||
min: pql.NewDecimal(1, -19),
|
||||
max: pql.NewDecimal(2, -19),
|
||||
expErr: true,
|
||||
},
|
||||
{min: math.MinInt64, max: math.MaxInt64, scale: 3, expmin: math.MinInt64, expmax: math.MaxInt64},
|
||||
{min: 44, max: 88, scale: 3, expmin: 44000, expmax: 88000},
|
||||
{min: -44, max: 88, scale: 3, expmin: -44000, expmax: 88000},
|
||||
} {
|
||||
t.Run("minmax"+strconv.Itoa(i), func(t *testing.T) {
|
||||
_, err := NewField("no-path", "i", "f", OptFieldTypeDecimal(test.scale, test.min, test.max))
|
||||
if err != nil && test.expErr {
|
||||
if !strings.Contains(err.Error(), "is not supported") {
|
||||
t.Fatal(err)
|
||||
}
|
||||
} else if err != nil && !test.expErr {
|
||||
t.Fatalf("did not expect error, but got: %s", err)
|
||||
} else if err == nil && test.expErr {
|
||||
t.Fatal("expected error, but got none")
|
||||
f := MustOpenField(OptFieldTypeDecimal(test.scale, test.min, test.max))
|
||||
|
||||
if f.Options().Min != test.expmin {
|
||||
t.Fatalf("expected min: %v, but got: %v", test.expmin, f.Options().Min)
|
||||
}
|
||||
if f.Options().Max != test.expmax {
|
||||
t.Fatalf("expected max: %v, but got: %v", test.expmax, f.Options().Max)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecimalField_MinMaxForShard(t *testing.T) {
|
||||
f := OpenField(t, OptFieldTypeDecimal(3))
|
||||
f := MustOpenField(OptFieldTypeDecimal(3))
|
||||
|
||||
options := &ImportOptions{}
|
||||
for i, test := range []struct {
|
||||
|
|
|
|||
|
|
@ -1147,7 +1147,7 @@ func TestClient_CreateDecimalField(t *testing.T) {
|
|||
t.Fatalf("creating index: %v", err)
|
||||
}
|
||||
field := "dfield"
|
||||
err = c.CreateFieldWithOptions(context.Background(), index, field, pilosa.FieldOptions{Type: pilosa.FieldTypeDecimal, Scale: 1, Min: pql.NewDecimal(-1000, 0), Max: pql.NewDecimal(1000, 0)})
|
||||
err = c.CreateFieldWithOptions(context.Background(), index, field, pilosa.FieldOptions{Type: pilosa.FieldTypeDecimal, Scale: 1, Min: -1000, Max: 1000})
|
||||
if err != nil {
|
||||
t.Fatalf("creating field: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ import (
|
|||
"github.com/gorilla/mux"
|
||||
"github.com/pilosa/pilosa/v2"
|
||||
"github.com/pilosa/pilosa/v2/logger"
|
||||
"github.com/pilosa/pilosa/v2/pql"
|
||||
"github.com/pilosa/pilosa/v2/tracing"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
|
|
@ -798,39 +797,24 @@ func (h *Handler) handlePostField(w http.ResponseWriter, r *http.Request) {
|
|||
switch req.Options.Type {
|
||||
case pilosa.FieldTypeSet:
|
||||
fos = append(fos, pilosa.OptFieldTypeSet(*req.Options.CacheType, *req.Options.CacheSize))
|
||||
case pilosa.FieldTypeInt:
|
||||
case pilosa.FieldTypeInt, pilosa.FieldTypeDecimal:
|
||||
if req.Options.Min == nil {
|
||||
min := pql.NewDecimal(int64(math.MinInt64), 0)
|
||||
min := int64(math.MinInt64)
|
||||
req.Options.Min = &min
|
||||
}
|
||||
if req.Options.Max == nil {
|
||||
max := pql.NewDecimal(int64(math.MaxInt64), 0)
|
||||
max := int64(math.MaxInt64)
|
||||
req.Options.Max = &max
|
||||
}
|
||||
fos = append(fos, pilosa.OptFieldTypeInt(req.Options.Min.ToInt64(0), req.Options.Max.ToInt64(0)))
|
||||
case pilosa.FieldTypeDecimal:
|
||||
scale := int64(0)
|
||||
if req.Options.Scale != nil {
|
||||
scale = *req.Options.Scale
|
||||
}
|
||||
if req.Options.Min == nil {
|
||||
min := pql.NewDecimal(int64(math.MinInt64), scale)
|
||||
req.Options.Min = &min
|
||||
}
|
||||
if req.Options.Max == nil {
|
||||
max := pql.NewDecimal(int64(math.MaxInt64), scale)
|
||||
req.Options.Max = &max
|
||||
}
|
||||
var minmax []pql.Decimal
|
||||
if req.Options.Min != nil {
|
||||
minmax = []pql.Decimal{
|
||||
*req.Options.Min,
|
||||
}
|
||||
if req.Options.Max != nil {
|
||||
minmax = append(minmax, *req.Options.Max)
|
||||
if req.Options.Type == pilosa.FieldTypeDecimal {
|
||||
scale := int64(0)
|
||||
if req.Options.Scale != nil {
|
||||
scale = *req.Options.Scale
|
||||
}
|
||||
fos = append(fos, pilosa.OptFieldTypeDecimal(scale, *req.Options.Min, *req.Options.Max))
|
||||
} else {
|
||||
fos = append(fos, pilosa.OptFieldTypeInt(*req.Options.Min, *req.Options.Max))
|
||||
}
|
||||
fos = append(fos, pilosa.OptFieldTypeDecimal(scale, minmax...))
|
||||
case pilosa.FieldTypeTime:
|
||||
fos = append(fos, pilosa.OptFieldTypeTime(*req.Options.TimeQuantum, req.Options.NoStandardView))
|
||||
case pilosa.FieldTypeMutex:
|
||||
|
|
@ -865,8 +849,8 @@ type fieldOptions struct {
|
|||
Type string `json:"type,omitempty"`
|
||||
CacheType *string `json:"cacheType,omitempty"`
|
||||
CacheSize *uint32 `json:"cacheSize,omitempty"`
|
||||
Min *pql.Decimal `json:"min,omitempty"`
|
||||
Max *pql.Decimal `json:"max,omitempty"`
|
||||
Min *int64 `json:"min,omitempty"`
|
||||
Max *int64 `json:"max,omitempty"`
|
||||
Scale *int64 `json:"scale,omitempty"`
|
||||
TimeQuantum *pilosa.TimeQuantum `json:"timeQuantum,omitempty"`
|
||||
Keys *bool `json:"keys,omitempty"`
|
||||
|
|
@ -902,7 +886,7 @@ func (o *fieldOptions) validate() error {
|
|||
} else if o.ForeignIndex != nil {
|
||||
return pilosa.NewBadRequestError(errors.New("set field cannot be a foreign key"))
|
||||
}
|
||||
case pilosa.FieldTypeInt:
|
||||
case pilosa.FieldTypeInt, pilosa.FieldTypeDecimal:
|
||||
if o.CacheType != nil {
|
||||
return pilosa.NewBadRequestError(errors.New("cacheType does not apply to field type int"))
|
||||
} else if o.CacheSize != nil {
|
||||
|
|
@ -912,18 +896,6 @@ func (o *fieldOptions) validate() error {
|
|||
} else if o.ForeignIndex != nil && o.Type == pilosa.FieldTypeDecimal {
|
||||
return pilosa.NewBadRequestError(errors.New("decimal field cannot be a foreign key"))
|
||||
}
|
||||
case pilosa.FieldTypeDecimal:
|
||||
if o.Scale == nil {
|
||||
return pilosa.NewBadRequestError(errors.New("decimal field requires a scale argument"))
|
||||
} else if o.CacheType != nil {
|
||||
return pilosa.NewBadRequestError(errors.New("cacheType does not apply to field type int"))
|
||||
} else if o.CacheSize != nil {
|
||||
return pilosa.NewBadRequestError(errors.New("cacheSize does not apply to field type int"))
|
||||
} else if o.TimeQuantum != nil {
|
||||
return pilosa.NewBadRequestError(errors.New("timeQuantum does not apply to field type int"))
|
||||
} else if o.ForeignIndex != nil && o.Type == pilosa.FieldTypeDecimal {
|
||||
return pilosa.NewBadRequestError(errors.New("decimal field cannot be a foreign key"))
|
||||
}
|
||||
case pilosa.FieldTypeTime:
|
||||
if o.CacheType != nil {
|
||||
return pilosa.NewBadRequestError(errors.New("cacheType does not apply to field type time"))
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/pilosa/pilosa/v2"
|
||||
"github.com/pilosa/pilosa/v2/pql"
|
||||
)
|
||||
|
||||
// Test custom UnmarshalJSON for postIndexRequest object
|
||||
|
|
@ -100,8 +99,8 @@ func stringPtr(s string) *string {
|
|||
return &s
|
||||
}
|
||||
|
||||
func decimalPtr(d pql.Decimal) *pql.Decimal {
|
||||
return &d
|
||||
func int64Ptr(i int64) *int64 {
|
||||
return &i
|
||||
}
|
||||
|
||||
// Test fieldOption validation.
|
||||
|
|
@ -136,10 +135,10 @@ func TestFieldOptionValidation(t *testing.T) {
|
|||
// FieldType: Int
|
||||
{json: `{"options": {"type": "int"}}`, err: "min is required for field type int"},
|
||||
{json: `{"options": {"type": "int", "min": 0}}`, err: "max is required for field type int"},
|
||||
{json: `{"options": {"type": "int", "min": 0, "max": 1001}}`, expected: postFieldRequest{Options: fieldOptions{
|
||||
{json: `{"options": {"type": "int", "min": 0, "max": 1000}}`, expected: postFieldRequest{Options: fieldOptions{
|
||||
Type: pilosa.FieldTypeInt,
|
||||
Min: decimalPtr(pql.NewDecimal(0, 0)),
|
||||
Max: decimalPtr(pql.NewDecimal(1001, 0)),
|
||||
Min: int64Ptr(0),
|
||||
Max: int64Ptr(1000),
|
||||
}}},
|
||||
{json: `{"options": {"type": "int", "min": 0, "max": 1000, "cacheType": "ranked"}}`, err: "cacheType does not apply to field type int"},
|
||||
{json: `{"options": {"type": "int", "min": 0, "max": 1000, "cacheSize": 1000}}`, err: "cacheSize does not apply to field type int"},
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/pilosa/pilosa/v2"
|
||||
"github.com/pilosa/pilosa/v2/pql"
|
||||
"github.com/pilosa/pilosa/v2/test"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
|
@ -204,7 +203,7 @@ func TestIndex_CreateField(t *testing.T) {
|
|||
index := test.MustOpenIndex()
|
||||
defer index.Close()
|
||||
|
||||
_, err := index.CreateField("f", pilosa.OptFieldTypeDecimal(1, pql.Decimal{Value: -1}, pql.Decimal{Value: 1}), pilosa.OptFieldKeys())
|
||||
_, err := index.CreateField("f", pilosa.OptFieldTypeDecimal(1, -1, 1), pilosa.OptFieldKeys())
|
||||
if errors.Cause(err) != pilosa.ErrDecimalFieldWithKeys {
|
||||
t.Fatal("decimal field cannot be created with keys=true")
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -12,19 +12,14 @@ message FieldOptions {
|
|||
string CacheType = 3;
|
||||
uint32 CacheSize = 4;
|
||||
string TimeQuantum = 5;
|
||||
int64 Min = 9;
|
||||
int64 Max = 10;
|
||||
bool Keys = 11;
|
||||
bool NoStandardView = 12;
|
||||
int64 Base = 13;
|
||||
uint64 BitDepth = 14;
|
||||
int64 Scale = 15;
|
||||
string ForeignIndex = 16;
|
||||
Decimal Min = 17;
|
||||
Decimal Max = 18;
|
||||
}
|
||||
|
||||
message Decimal {
|
||||
int64 Value = 1;
|
||||
int64 Scale = 2;
|
||||
}
|
||||
|
||||
message ImportResponse {
|
||||
|
|
|
|||
211
pql/decimal.go
211
pql/decimal.go
|
|
@ -23,38 +23,6 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// pow10 is a map used to avoid the float64 required by math.Pow10()
|
||||
var pow10 = map[int64]int64{
|
||||
0: 1,
|
||||
1: 10,
|
||||
2: 100,
|
||||
3: 1000,
|
||||
4: 10000,
|
||||
5: 100000,
|
||||
6: 1000000,
|
||||
7: 10000000,
|
||||
8: 100000000,
|
||||
9: 1000000000,
|
||||
10: 10000000000,
|
||||
11: 100000000000,
|
||||
12: 1000000000000,
|
||||
13: 10000000000000,
|
||||
14: 100000000000000,
|
||||
15: 1000000000000000,
|
||||
16: 10000000000000000,
|
||||
17: 100000000000000000,
|
||||
18: 1000000000000000000,
|
||||
//19: 10000000000000000000,
|
||||
}
|
||||
|
||||
// Pow10 is a function which can be used in place of math.Pow10()
|
||||
// to avoid the float64 logic. Note that only powers 0-18 are
|
||||
// currently supported; anything else will return 0, which is
|
||||
// probably going to result in incorrect values.
|
||||
func Pow10(p int64) int64 {
|
||||
return pow10[p]
|
||||
}
|
||||
|
||||
// Decimal represents a decimal value; the intention
|
||||
// is to avoid relying on float64, and the primary
|
||||
// purpose is to have a predictable way to encode such
|
||||
|
|
@ -68,147 +36,6 @@ type Decimal struct {
|
|||
Scale int64
|
||||
}
|
||||
|
||||
// NewDecimal returns a Decimal based on the provided arguments.
|
||||
func NewDecimal(value, scale int64) Decimal {
|
||||
return Decimal{
|
||||
Value: value,
|
||||
Scale: scale,
|
||||
}
|
||||
}
|
||||
|
||||
// MinMax returns the minimum and maximum values
|
||||
// supported by the provided scale.
|
||||
func MinMax(scale int64) (Decimal, Decimal) {
|
||||
min := NewDecimal(math.MinInt64, scale)
|
||||
max := NewDecimal(math.MaxInt64, scale)
|
||||
return min, max
|
||||
}
|
||||
|
||||
// LessThan returns true if d < d2.
|
||||
func (d Decimal) LessThan(d2 Decimal) bool {
|
||||
return d.lessThan(d2, false)
|
||||
}
|
||||
|
||||
// LessThanOrEqualTo returns true if d <= d2.
|
||||
func (d Decimal) LessThanOrEqualTo(d2 Decimal) bool {
|
||||
return d.lessThan(d2, true)
|
||||
}
|
||||
|
||||
// GreaterThan returns true if d > d2.
|
||||
func (d Decimal) GreaterThan(d2 Decimal) bool {
|
||||
return d.greaterThan(d2, false)
|
||||
}
|
||||
|
||||
// GreaterThanOrEqualTo returns true if d >= d2.
|
||||
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
|
||||
}
|
||||
|
||||
quotientD := quotient(d)
|
||||
quotientD2 := quotient(d2)
|
||||
if quotientD != quotientD2 {
|
||||
return false
|
||||
}
|
||||
remainderD, remainderD2 := remainder(d), remainder(d2)
|
||||
if d.Scale < d2.Scale {
|
||||
scaleDiff := d2.Scale - d.Scale
|
||||
return (remainderD * pow10[scaleDiff]) == remainderD2
|
||||
}
|
||||
scaleDiff := d.Scale - d2.Scale
|
||||
return remainderD == (remainderD2 * pow10[scaleDiff])
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// SupportedByScale returns true if d can be represented
|
||||
// by a decimal based on scale.
|
||||
// For example:
|
||||
// scale = 2:
|
||||
// min: -92233720368547758.08
|
||||
// max: 92233720368547758.07
|
||||
// would not support: NewDecimal(9223372036854775807, 0)
|
||||
func (d Decimal) SupportedByScale(scale int64) bool {
|
||||
min, max := MinMax(scale)
|
||||
|
||||
if d.GreaterThanOrEqualTo(min) && d.LessThanOrEqualTo(max) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsValid returns true if the decimal does not break
|
||||
// any assumption or resrictions on input.
|
||||
func (d Decimal) IsValid() bool {
|
||||
if d.Scale < -18 || d.Scale > 19 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ToInt64 returns d as an int64 adjusted to the
|
||||
// provided scale.
|
||||
func (d Decimal) ToInt64(scale int64) int64 {
|
||||
|
|
@ -216,17 +43,13 @@ func (d Decimal) ToInt64(scale int64) int64 {
|
|||
scaleDiff := scale - d.Scale
|
||||
if scaleDiff == 0 {
|
||||
ret = d.Value
|
||||
} else if scaleDiff < 0 {
|
||||
ret = d.Value / Pow10(-1*scaleDiff)
|
||||
} else {
|
||||
ret = d.Value * Pow10(scaleDiff)
|
||||
ret = int64(float64(d.Value) * math.Pow10(int(scaleDiff)))
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Float64 returns d as a float64.
|
||||
// TODO: this could potentially lose precision; we should audit
|
||||
// its use and protect against unexpected results.
|
||||
func (d Decimal) Float64() float64 {
|
||||
var ret float64
|
||||
if d.Scale == 0 {
|
||||
|
|
@ -396,19 +219,15 @@ func ParseDecimal(s string) (Decimal, error) {
|
|||
scale = 0
|
||||
}
|
||||
|
||||
// We have to use ParseUint here (as opposed to ParseInt) because
|
||||
// math.MinInt64 is a valid value, but its absolute value is not.
|
||||
// So this allows us to handle that one value without overflow, and
|
||||
// then we check for the uint bounds in the next step.
|
||||
uvalue, err := strconv.ParseUint(string(mantissa), 10, 64)
|
||||
value, err = strconv.ParseInt(string(mantissa), 10, 64)
|
||||
if err != nil {
|
||||
return Decimal{}, errors.Wrap(err, "converting mantissa string to uint64")
|
||||
return Decimal{}, errors.Wrap(err, "converting mantissa to uint32")
|
||||
}
|
||||
|
||||
if (sign && uvalue > -1*math.MinInt64) || (!sign && uvalue > math.MaxInt64) {
|
||||
return Decimal{}, errors.New("value out of range")
|
||||
// Because we pulled the sign off at the beginning, if value is
|
||||
// negative here, it likely means the string had two "-"" characters.
|
||||
if value < 0 {
|
||||
return Decimal{}, errors.New("invalid negative value")
|
||||
}
|
||||
value = int64(uvalue)
|
||||
|
||||
if sign {
|
||||
value *= -1
|
||||
|
|
@ -420,22 +239,6 @@ func ParseDecimal(s string) (Decimal, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
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]
|
||||
}
|
||||
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
|
||||
|
|
|
|||
|
|
@ -15,8 +15,6 @@
|
|||
package pql_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
|
@ -61,7 +59,7 @@ func TestDecimal(t *testing.T) {
|
|||
|
||||
// int64 edges.
|
||||
{".000009223372036854775807", pql.Decimal{9223372036854775807, 24}, ""},
|
||||
{"-.000009223372036854775808", pql.Decimal{-9223372036854775808, 24}, ""},
|
||||
{"-.000009223372036854775807", pql.Decimal{-9223372036854775807, 24}, ""},
|
||||
{"92233720368547.75807", pql.Decimal{9223372036854775807, 5}, ""},
|
||||
{"-92233720368547.75807", pql.Decimal{-9223372036854775807, 5}, ""},
|
||||
{"9223372036854775807000", pql.Decimal{9223372036854775807, -3}, ""},
|
||||
|
|
@ -73,12 +71,11 @@ func TestDecimal(t *testing.T) {
|
|||
{"*0.123", pql.Decimal{}, "invalid syntax"},
|
||||
{"abc", pql.Decimal{}, "invalid syntax"},
|
||||
{"0.12.3", pql.Decimal{}, "invalid decimal string"},
|
||||
{"--12300", pql.Decimal{}, "invalid syntax"},
|
||||
|
||||
{"922337203685477580.9", pql.Decimal{}, "value out of range"},
|
||||
{"-922337203685477580.9", pql.Decimal{}, "value out of range"},
|
||||
{"--12300", pql.Decimal{}, "invalid negative value"},
|
||||
{"922337203685477580.8", pql.Decimal{}, "value out of range"},
|
||||
{"-922337203685477580.8", pql.Decimal{}, "value out of range"},
|
||||
{"9223372036854775808000", pql.Decimal{}, "value out of range"},
|
||||
{"-9223372036854775809000", pql.Decimal{}, "value out of range"},
|
||||
{"-9223372036854775808000", pql.Decimal{}, "value out of range"},
|
||||
}
|
||||
for i, test := range tests {
|
||||
dec, err := pql.ParseDecimal(test.s)
|
||||
|
|
@ -164,89 +161,4 @@ func TestDecimal(t *testing.T) {
|
|||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Comparisons", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
d1 pql.Decimal
|
||||
d2 pql.Decimal
|
||||
expLT bool
|
||||
expLTE bool
|
||||
expGT bool
|
||||
expGTE bool
|
||||
expEQ bool
|
||||
}{
|
||||
{pql.NewDecimal(0, 0), pql.NewDecimal(0, 0), false, true, false, true, true},
|
||||
{pql.NewDecimal(0, 0), pql.NewDecimal(10, 0), true, true, false, false, false},
|
||||
{pql.NewDecimal(10, 0), pql.NewDecimal(0, 0), false, false, true, true, false},
|
||||
{pql.NewDecimal(123456, 3), pql.NewDecimal(123456, 3), false, true, false, true, true},
|
||||
{pql.NewDecimal(123456, 3), pql.NewDecimal(123456, 4), false, false, true, true, false},
|
||||
{pql.NewDecimal(123456, 4), pql.NewDecimal(123456, 3), true, true, false, false, false},
|
||||
{pql.NewDecimal(1233456, 4), pql.NewDecimal(123456, 3), true, true, false, false, false},
|
||||
|
||||
{pql.NewDecimal(0, 0), pql.NewDecimal(-10, 0), false, false, true, true, false},
|
||||
{pql.NewDecimal(-10, 0), pql.NewDecimal(0, 0), true, true, false, false, false},
|
||||
{pql.NewDecimal(-123456, 3), pql.NewDecimal(-123456, 3), false, true, false, true, true},
|
||||
{pql.NewDecimal(-123456, 3), pql.NewDecimal(-123456, 4), true, true, false, false, false},
|
||||
{pql.NewDecimal(-123456, 4), pql.NewDecimal(-123456, 3), false, false, true, true, false},
|
||||
{pql.NewDecimal(-1233456, 4), pql.NewDecimal(-123456, 3), false, false, true, true, false},
|
||||
|
||||
{pql.NewDecimal(10, 0), pql.NewDecimal(-10, 0), false, false, true, true, false},
|
||||
{pql.NewDecimal(-10, 0), pql.NewDecimal(10, 0), true, true, false, false, false},
|
||||
{pql.NewDecimal(-123456, 3), pql.NewDecimal(123456, 3), true, true, false, false, false},
|
||||
{pql.NewDecimal(123456, 3), pql.NewDecimal(-123456, 3), false, false, true, true, false},
|
||||
{pql.NewDecimal(-123456, 3), pql.NewDecimal(123456, 4), true, true, false, false, false},
|
||||
{pql.NewDecimal(123456, 3), pql.NewDecimal(-123456, 4), false, false, true, true, false},
|
||||
{pql.NewDecimal(-123456, 4), pql.NewDecimal(123456, 3), true, true, false, false, false},
|
||||
{pql.NewDecimal(123456, 4), pql.NewDecimal(-123456, 3), false, false, true, true, false},
|
||||
{pql.NewDecimal(-1233456, 4), pql.NewDecimal(123456, 3), true, true, false, false, false},
|
||||
{pql.NewDecimal(1233456, 4), pql.NewDecimal(-123456, 3), false, false, true, true, false},
|
||||
|
||||
{pql.NewDecimal(9223372036854775807, 0), pql.NewDecimal(9223372036854775807, 0), false, true, false, true, true},
|
||||
{pql.NewDecimal(9223372036854775807, 2), pql.NewDecimal(9223372036854775807, 0), true, true, false, false, false},
|
||||
{pql.NewDecimal(9223372036854775807, 19), pql.NewDecimal(9223372036854775807, 0), true, true, false, false, false},
|
||||
|
||||
{pql.NewDecimal(-9223372036854775808, 0), pql.NewDecimal(-9223372036854775808, 0), false, true, false, true, true},
|
||||
{pql.NewDecimal(-9223372036854775808, 0), pql.NewDecimal(-9223372036854775807, 0), true, true, false, false, false},
|
||||
{pql.NewDecimal(-9223372036854775808, 2), pql.NewDecimal(-9223372036854775808, 0), false, false, true, true, false},
|
||||
{pql.NewDecimal(-9223372036854775808, 19), pql.NewDecimal(-9223372036854775807, 0), false, false, true, true, false},
|
||||
}
|
||||
for i, test := range tests {
|
||||
if got := test.d1.LessThan(test.d2); got != test.expLT {
|
||||
t.Fatalf("test LT %d expected %s < %s to be %v, but got: %v", i, test.d1, test.d2, test.expLT, got)
|
||||
}
|
||||
if got := test.d1.LessThanOrEqualTo(test.d2); got != test.expLTE {
|
||||
t.Fatalf("test LTE %d expected %s <= %s to be %v, but got: %v", i, test.d1, test.d2, test.expLTE, got)
|
||||
}
|
||||
if got := test.d1.GreaterThan(test.d2); got != test.expGT {
|
||||
t.Fatalf("test GT %d expected %s > %s to be %v, but got: %v", i, test.d1, test.d2, test.expGT, got)
|
||||
}
|
||||
if got := test.d1.GreaterThanOrEqualTo(test.d2); got != test.expGTE {
|
||||
t.Fatalf("test GTE %d expected %s >= %s to be %v, but got: %v", i, test.d1, test.d2, test.expGTE, got)
|
||||
}
|
||||
if got := test.d1.EqualTo(test.d2); got != test.expEQ {
|
||||
t.Fatalf("test EQ %d expected %s == %s to be %v, but got: %v", i, test.d1, test.d2, test.expEQ, got)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("JSON", func(t *testing.T) {
|
||||
t.Run("Unmarshal", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
json string
|
||||
exp pql.Decimal
|
||||
}{
|
||||
{"1234.56", pql.NewDecimal(123456, 2)},
|
||||
}
|
||||
for i, test := range tests {
|
||||
b := []byte(test.json)
|
||||
dec := &pql.Decimal{}
|
||||
if err := json.Unmarshal(b, &dec); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if !reflect.DeepEqual(*dec, test.exp) {
|
||||
t.Fatalf("test %d expected: %T, but got: %T", i, test.exp, dec)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ import (
|
|||
"github.com/pilosa/pilosa/v2/boltdb"
|
||||
"github.com/pilosa/pilosa/v2/encoding/proto"
|
||||
"github.com/pilosa/pilosa/v2/http"
|
||||
"github.com/pilosa/pilosa/v2/pql"
|
||||
"github.com/pilosa/pilosa/v2/server"
|
||||
"github.com/pilosa/pilosa/v2/test"
|
||||
)
|
||||
|
|
@ -575,10 +574,10 @@ func TestHandler_Endpoints(t *testing.T) {
|
|||
if field == nil {
|
||||
t.Fatalf("field not found: %s", fieldName)
|
||||
}
|
||||
if !reflect.DeepEqual(pql.NewDecimal(math.MinInt64, 0), field.Options.Min) {
|
||||
if math.MinInt64 != field.Options.Min {
|
||||
t.Fatalf("field min %d != %d", int64(math.MinInt64), field.Options.Min)
|
||||
}
|
||||
if !reflect.DeepEqual(pql.NewDecimal(math.MaxInt64, 0), field.Options.Max) {
|
||||
if math.MaxInt64 != field.Options.Max {
|
||||
t.Fatalf("field max %d != %d", int64(math.MaxInt64), field.Options.Max)
|
||||
}
|
||||
})
|
||||
|
|
@ -604,10 +603,10 @@ func TestHandler_Endpoints(t *testing.T) {
|
|||
if field == nil {
|
||||
t.Fatalf("field not found: %s", fieldName)
|
||||
}
|
||||
if !reflect.DeepEqual(pql.NewDecimal(math.MinInt64, 0), field.Options.Min) {
|
||||
if math.MinInt64 != field.Options.Min {
|
||||
t.Fatalf("field min %d != %d", int64(math.MinInt64), field.Options.Min)
|
||||
}
|
||||
if !reflect.DeepEqual(pql.NewDecimal(1, -1), field.Options.Max) {
|
||||
if 10 != field.Options.Max {
|
||||
t.Fatalf("field max %d != %d", 10, field.Options.Max)
|
||||
}
|
||||
})
|
||||
|
|
@ -633,10 +632,10 @@ func TestHandler_Endpoints(t *testing.T) {
|
|||
if field == nil {
|
||||
t.Fatalf("field not found: %s", fieldName)
|
||||
}
|
||||
if !reflect.DeepEqual(pql.NewDecimal(-1, -1), field.Options.Min) {
|
||||
if -10 != field.Options.Min {
|
||||
t.Fatalf("field min %d != %d", 10, field.Options.Min)
|
||||
}
|
||||
if !reflect.DeepEqual(pql.NewDecimal(math.MaxInt64, 0), field.Options.Max) {
|
||||
if math.MaxInt64 != field.Options.Max {
|
||||
t.Fatalf("field max %d != %d", int64(math.MaxInt64), field.Options.Max)
|
||||
}
|
||||
})
|
||||
|
|
@ -651,79 +650,6 @@ func TestHandler_Endpoints(t *testing.T) {
|
|||
}
|
||||
})
|
||||
|
||||
t.Run("Query decimal field unbounded", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
fieldName := "f-decimal-ubound"
|
||||
h.ServeHTTP(w, test.MustNewHTTPRequest("POST", fmt.Sprintf("/index/i0/field/%s", fieldName),
|
||||
strings.NewReader(`{"options":{"type":"decimal", "scale": 0}}`)))
|
||||
if w.Code != gohttp.StatusOK {
|
||||
t.Fatalf("unexpected status code: %d", w.Code)
|
||||
}
|
||||
w = httptest.NewRecorder()
|
||||
h.ServeHTTP(w, test.MustNewHTTPRequest("GET", "/schema", strings.NewReader("")))
|
||||
if w.Code != gohttp.StatusOK {
|
||||
t.Fatalf("unexpected status code: %d", w.Code)
|
||||
}
|
||||
rsp := getSchemaResponse{}
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &rsp); err != nil {
|
||||
t.Fatalf("json decode: %s", err)
|
||||
}
|
||||
field := rsp.findField("i0", fieldName)
|
||||
if field == nil {
|
||||
t.Fatalf("field not found: %s", fieldName)
|
||||
}
|
||||
if !reflect.DeepEqual(pql.NewDecimal(math.MinInt64, 0), field.Options.Min) {
|
||||
t.Fatalf("field min %d != %d", 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.Run("Query decimal field unbounded min", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
fieldName := "f-decimal-ubound-min"
|
||||
h.ServeHTTP(w, test.MustNewHTTPRequest("POST", fmt.Sprintf("/index/i0/field/%s", fieldName),
|
||||
strings.NewReader(`{"options":{"type":"decimal", "scale": 1, "max": 10.5}}`)))
|
||||
if w.Code != gohttp.StatusOK {
|
||||
fmt.Println(w.Body.String())
|
||||
t.Fatalf("unexpected status code: %d", w.Code)
|
||||
}
|
||||
w = httptest.NewRecorder()
|
||||
h.ServeHTTP(w, test.MustNewHTTPRequest("GET", "/schema", strings.NewReader("")))
|
||||
if w.Code != gohttp.StatusOK {
|
||||
t.Fatalf("unexpected status code: %d", w.Code)
|
||||
}
|
||||
rsp := getSchemaResponse{}
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &rsp); err != nil {
|
||||
t.Fatalf("json decode: %s", err)
|
||||
}
|
||||
field := rsp.findField("i0", fieldName)
|
||||
if field == nil {
|
||||
t.Fatalf("field not found: %s", fieldName)
|
||||
}
|
||||
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)
|
||||
}
|
||||
if !reflect.DeepEqual(pql.NewDecimal(105, 1), field.Options.Max) {
|
||||
t.Fatalf("field max %s != %d", pql.NewDecimal(105, 1), field.Options.Max)
|
||||
}
|
||||
})
|
||||
|
||||
// Ensure that decimal fields error when scale is not provided.
|
||||
t.Run("Query decimal field scale error", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
fieldName := "f-decimal-ubound"
|
||||
h.ServeHTTP(w, test.MustNewHTTPRequest("POST", fmt.Sprintf("/index/i0/field/%s", fieldName),
|
||||
strings.NewReader(`{"options":{"type":"decimal"}}`)))
|
||||
expErr := "decimal field requires a scale argument"
|
||||
if w.Code != gohttp.StatusBadRequest {
|
||||
t.Fatalf("unexpected status code: %d", w.Code)
|
||||
} else if !strings.Contains(w.Body.String(), expErr) {
|
||||
t.Fatalf("expected error to contain: %s, but got: %s", expErr, w.Body.String())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Method not allowed", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
h.ServeHTTP(w, test.MustNewHTTPRequest("GET", "/index/i0/query", nil))
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ import (
|
|||
"github.com/pelletier/go-toml"
|
||||
"github.com/pilosa/pilosa/v2"
|
||||
"github.com/pilosa/pilosa/v2/http"
|
||||
"github.com/pilosa/pilosa/v2/pql"
|
||||
"github.com/pilosa/pilosa/v2/roaring"
|
||||
"github.com/pilosa/pilosa/v2/server"
|
||||
"github.com/pilosa/pilosa/v2/test"
|
||||
|
|
@ -308,7 +307,7 @@ func TestMain_MinMaxFloat(t *testing.T) {
|
|||
if err := client.CreateIndex(context.Background(), "i", pilosa.IndexOptions{}); err != nil && err != pilosa.ErrIndexExists {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := client.CreateFieldWithOptions(context.Background(), "i", "dec", pilosa.FieldOptions{Type: pilosa.FieldTypeDecimal, Scale: 3, Max: pql.NewDecimal(100000, 0)}); err != nil {
|
||||
if err := client.CreateFieldWithOptions(context.Background(), "i", "dec", pilosa.FieldOptions{Type: pilosa.FieldTypeDecimal, Scale: 3, Max: 100000}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue