mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 10:54:59 +00:00
Fix Set operation for float numbers on decimal fields. (#101)
This commit is contained in:
parent
7c395ac4d1
commit
3bb45ea2c0
3 changed files with 94 additions and 25 deletions
55
executor.go
55
executor.go
|
|
@ -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)))
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue