Fix Set operation for float numbers on decimal fields. (#101)

This commit is contained in:
Kuba Podgórski 2020-02-03 19:29:15 +01:00 committed by GitHub
parent 7c395ac4d1
commit 3bb45ea2c0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 94 additions and 25 deletions

View file

@ -2941,38 +2941,41 @@ func (e *executor) executeSet(ctx context.Context, index string, c *pql.Call, op
}
}
// Int field.
if f.Type() == FieldTypeInt || f.Type() == FieldTypeDecimal {
switch f.Type() {
case FieldTypeInt, FieldTypeDecimal:
// Int or Decimal field.
v, ok := c.Arg(fieldName)
if !ok {
return false, fmt.Errorf("Set() row argument '%v' required", rowLabel)
}
// Read row value.
rowVal, ok, err := c.IntArg(fieldName)
rowVal, err := getScaledInt(f, v)
if err != nil {
return false, fmt.Errorf("reading Set() row: %v", err)
}
return e.executeSetValueField(ctx, index, c, f, colID, rowVal, opt)
default:
// Read row ID.
rowID, ok, err := c.UintArg(fieldName)
if err != nil {
return false, fmt.Errorf("reading Set() row: %v", err)
} else if !ok {
return false, fmt.Errorf("Set() row argument '%v' required", rowLabel)
}
return e.executeSetValueField(ctx, index, c, f, colID, rowVal, opt)
}
// Read row ID.
rowID, ok, err := c.UintArg(fieldName)
if err != nil {
return false, fmt.Errorf("reading Set() row: %v", err)
} else if !ok {
return false, fmt.Errorf("Set() row argument '%v' required", rowLabel)
}
var timestamp *time.Time
sTimestamp, ok := c.Args["_timestamp"].(string)
if ok {
t, err := time.Parse(TimeFormat, sTimestamp)
if err != nil {
return false, fmt.Errorf("invalid date: %s", sTimestamp)
var timestamp *time.Time
sTimestamp, ok := c.Args["_timestamp"].(string)
if ok {
t, err := time.Parse(TimeFormat, sTimestamp)
if err != nil {
return false, fmt.Errorf("invalid date: %s", sTimestamp)
}
timestamp = &t
}
timestamp = &t
}
return e.executeSetBitField(ctx, index, c, f, colID, rowID, timestamp, opt)
return e.executeSetBitField(ctx, index, c, f, colID, rowID, timestamp, opt)
}
}
// executeSetBitField executes a Set() call for a specific field.
@ -4448,8 +4451,10 @@ func getCondIntSlice(f *Field, cond *pql.Condition) ([]int64, error) {
// the field type.
func getScaledInt(f *Field, v interface{}) (int64, error) {
var value int64
if f.Options().Type == FieldTypeDecimal {
scale := f.Options().Scale
opt := f.Options()
if opt.Type == FieldTypeDecimal {
scale := opt.Scale
switch tv := v.(type) {
case int64:
value = int64(float64(tv) * math.Pow10(int(scale)))

View file

@ -766,6 +766,63 @@ func TestExecutor_Execute_SetBool(t *testing.T) {
})
}
// Ensure a set query can be executed on a decimal field.
func TestExecutor_Execute_SetDecimal(t *testing.T) {
t.Run("Basic", func(t *testing.T) {
c := test.MustRunCluster(t, 1)
defer c.Close()
hldr := test.Holder{Holder: c[0].Server.Holder()}
// Create fields.
index := hldr.MustCreateIndexIfNotExists("i", pilosa.IndexOptions{})
if _, err := index.CreateFieldIfNotExists("f", pilosa.OptFieldTypeDecimal(2)); err != nil {
t.Fatal(err)
}
// Set a value.
if res, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `Set(1000, f=1.5)`}); err != nil {
t.Fatal(err)
} else if !res.Results[0].(bool) {
t.Fatalf("expected column changed")
}
// Set the same value again verify nothing changed.
if res, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `Set(1000, f=1.5)`}); err != nil {
t.Fatal(err)
} else if res.Results[0].(bool) {
t.Fatalf("expected column to be unchanged")
}
if result, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `Row(f == 1.5)`}); err != nil {
t.Fatal(err)
} else if columns := result.Results[0].(*pilosa.Row).Columns(); !reflect.DeepEqual(columns, []uint64{1000}) {
t.Fatalf("unexpected colums: %+v", columns)
}
if result, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `Row(f > 1.4999)`}); err != nil {
t.Fatal(err)
} else if columns := result.Results[0].(*pilosa.Row).Columns(); !reflect.DeepEqual(columns, []uint64{1000}) {
t.Fatalf("unexpected colums: %+v", columns)
}
})
t.Run("Error", func(t *testing.T) {
c := test.MustRunCluster(t, 1)
defer c.Close()
hldr := test.Holder{Holder: c[0].Server.Holder()}
// Create fields.
index := hldr.MustCreateIndexIfNotExists("i", pilosa.IndexOptions{})
if _, err := index.CreateFieldIfNotExists("f", pilosa.OptFieldTypeDecimal(2)); err != nil {
t.Fatal(err)
}
// Set decimal using a string value.
if _, err := c[0].API.Query(context.Background(), &pilosa.QueryRequest{Index: "i", Query: `Set(1000, f="1.5")`}); err == nil {
t.Fatalf("expected invalid decimal type error")
}
})
}
// Ensure old PQL syntax doesn't break anything too badly.
func TestExecutor_Execute_OldPQL(t *testing.T) {
c := test.MustRunCluster(t, 1)

View file

@ -557,6 +557,13 @@ func (c *Call) CallIndex() string {
return ""
}
// Arg is for reading the value at key from call.Args.
// If the key is not in Call.Args, the value of the returned bool will be false.
func (c *Call) Arg(key string) (interface{}, bool) {
v, ok := c.Args[key]
return v, ok
}
// BoolArg is for reading the value at key from call.Args as a bool. If the
// key is not in Call.Args, the value of the returned bool will be false, and
// the error will be nil. The value is assumed to be a bool. An error is