From 27eb4ae3bc7aeb3ea6c9dc85de3fca1ac66f1e00 Mon Sep 17 00:00:00 2001 From: mailsmail <78419644+mailsmail@users.noreply.github.com> Date: Fri, 19 Mar 2021 23:14:38 +0700 Subject: [PATCH] fix: sql cast function when converting to float (#11817) --- pkg/s3select/select_test.go | 25 +++++++++++++++++++++++++ pkg/s3select/sql/evaluate.go | 9 +++++++-- pkg/s3select/sql/funceval.go | 2 +- pkg/s3select/sql/parser.go | 14 ++++++++------ pkg/s3select/sql/statement.go | 4 ++-- pkg/s3select/sql/value.go | 9 --------- 6 files changed, 43 insertions(+), 20 deletions(-) diff --git a/pkg/s3select/select_test.go b/pkg/s3select/select_test.go index cce521667..2ef6be2e0 100644 --- a/pkg/s3select/select_test.go +++ b/pkg/s3select/select_test.go @@ -378,6 +378,31 @@ func TestJSONQueries(t *testing.T) { query: `SELECT date_diff(day, '2010-01-01T23:00:00Z', '2010-01-02T23:00:00Z') FROM S3Object LIMIT 1`, wantResult: `{"_1":1}`, }, + { + name: "cast_from_int_to_float", + query: `SELECT cast(1 as float) FROM S3Object LIMIT 1`, + wantResult: `{"_1":1}`, + }, + { + name: "cast_from_float_to_float", + query: `SELECT cast(1.0 as float) FROM S3Object LIMIT 1`, + wantResult: `{"_1":1}`, + }, + { + name: "arithmetic_integer_operand", + query: `SELECT 1 / 2 FROM S3Object LIMIT 1`, + wantResult: `{"_1":0}`, + }, + { + name: "arithmetic_float_operand", + query: `SELECT 1.0 / 2.0 * .3 FROM S3Object LIMIT 1`, + wantResult: `{"_1":0.15}`, + }, + { + name: "arithmetic_integer_float_operand", + query: `SELECT 3.0 / 2, 5 / 2.0 FROM S3Object LIMIT 1`, + wantResult: `{"_1":1.5,"_2":2.5}`, + }, } defRequest := ` diff --git a/pkg/s3select/sql/evaluate.go b/pkg/s3select/sql/evaluate.go index 92e875cc1..50451c515 100644 --- a/pkg/s3select/sql/evaluate.go +++ b/pkg/s3select/sql/evaluate.go @@ -476,8 +476,13 @@ func (e *FuncExpr) evalNode(r Record) (res *Value, err error) { // aggregation or a row function - it always returns a value. func (e *LitValue) evalNode(_ Record) (res *Value, err error) { switch { - case e.Number != nil: - return floatToValue(*e.Number), nil + case e.Int != nil: + if *e.Int < math.MaxInt64 && *e.Int > math.MinInt64 { + return FromInt(int64(*e.Int)), nil + } + return FromFloat(*e.Int), nil + case e.Float != nil: + return FromFloat(*e.Float), nil case e.String != nil: return FromString(string(*e.String)), nil case e.Boolean != nil: diff --git a/pkg/s3select/sql/funceval.go b/pkg/s3select/sql/funceval.go index 3e8eebba6..0490535b2 100644 --- a/pkg/s3select/sql/funceval.go +++ b/pkg/s3select/sql/funceval.go @@ -492,7 +492,7 @@ func floatCast(v *Value) (float64, error) { switch x := v.value.(type) { case float64: return x, nil - case int: + case int64: return float64(x), nil case string: f, err := strconv.ParseFloat(strings.TrimSpace(x), 64) diff --git a/pkg/s3select/sql/parser.go b/pkg/s3select/sql/parser.go index 095008068..9b9aae7cd 100644 --- a/pkg/s3select/sql/parser.go +++ b/pkg/s3select/sql/parser.go @@ -106,10 +106,10 @@ type TableExpression struct { // JSONPathElement represents a keypath component type JSONPathElement struct { - Key *ObjectKey `parser:" @@"` // ['name'] and .name forms - Index *int `parser:"| \"[\" @Number \"]\""` // [3] form - ObjectWildcard bool `parser:"| @\".*\""` // .* form - ArrayWildcard bool `parser:"| @\"[*]\""` // [*] form + Key *ObjectKey `parser:" @@"` // ['name'] and .name forms + Index *int `parser:"| \"[\" @Int \"]\""` // [3] form + ObjectWildcard bool `parser:"| @\".*\""` // .* form + ArrayWildcard bool `parser:"| @\"[*]\""` // [*] form } // JSONPath represents a keypath. @@ -333,7 +333,8 @@ type DateDiffFunc struct { // LitValue represents a literal value parsed from the sql type LitValue struct { - Number *float64 `parser:"( @Number"` + Float *float64 `parser:"( @Float"` + Int *float64 `parser:" | @Int"` // To avoid value out of range, use float64 instead String *LiteralString `parser:" | @LitString"` Boolean *Boolean `parser:" | @(\"TRUE\" | \"FALSE\")"` Null bool `parser:" | @\"NULL\")"` @@ -351,7 +352,8 @@ var ( `|(?P(?i)\b(?:SELECT|FROM|TOP|DISTINCT|ALL|WHERE|GROUP|BY|HAVING|UNION|MINUS|EXCEPT|INTERSECT|ORDER|LIMIT|OFFSET|TRUE|FALSE|NULL|IS|NOT|ANY|SOME|BETWEEN|AND|OR|LIKE|ESCAPE|AS|IN|BOOL|INT|INTEGER|STRING|FLOAT|DECIMAL|NUMERIC|TIMESTAMP|AVG|COUNT|MAX|MIN|SUM|COALESCE|NULLIF|CAST|DATE_ADD|DATE_DIFF|EXTRACT|TO_STRING|TO_TIMESTAMP|UTCNOW|CHAR_LENGTH|CHARACTER_LENGTH|LOWER|SUBSTRING|TRIM|UPPER|LEADING|TRAILING|BOTH|FOR)\b)` + `|(?P[a-zA-Z_][a-zA-Z0-9_]*)` + `|(?P"([^"]*("")?)*")` + - `|(?P\d*\.?\d+([eE][-+]?\d+)?)` + + `|(?P\d*\.\d+([eE][-+]?\d+)?)` + + `|(?P\d+)` + `|(?P'([^']*('')?)*')` + `|(?P<>|!=|<=|>=|\.\*|\[\*\]|[-+*/%,.()=<>\[\]])`, )) diff --git a/pkg/s3select/sql/statement.go b/pkg/s3select/sql/statement.go index 5383221b2..e4fffc6b1 100644 --- a/pkg/s3select/sql/statement.go +++ b/pkg/s3select/sql/statement.go @@ -127,10 +127,10 @@ func parseLimit(v *LitValue) (int64, error) { switch { case v == nil: return -1, nil - case v.Number == nil: + case v.Int == nil: return -1, errBadLimitSpecified default: - r := int64(*v.Number) + r := int64(*v.Int) if r < 0 { return -1, errBadLimitSpecified } diff --git a/pkg/s3select/sql/value.go b/pkg/s3select/sql/value.go index a74b239f1..c49f46012 100644 --- a/pkg/s3select/sql/value.go +++ b/pkg/s3select/sql/value.go @@ -306,15 +306,6 @@ func (v Value) CSVString() string { } } -// floatToValue converts a float into int representation if needed. -func floatToValue(f float64) *Value { - intPart, fracPart := math.Modf(f) - if fracPart == 0 && intPart < math.MaxInt64 && intPart > math.MinInt64 { - return FromInt(int64(intPart)) - } - return FromFloat(f) -} - // negate negates a numeric value func (v *Value) negate() { switch x := v.value.(type) {