New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
*: add buildin function CONV #2390
Changes from 3 commits
ca03c33
4056f8d
7830d0d
57f8671
bc0b980
d81ce96
76d023d
fc8e345
811868d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,8 @@ import ( | |
"hash/crc32" | ||
"math" | ||
"math/rand" | ||
"strconv" | ||
"strings" | ||
|
||
"github.com/juju/errors" | ||
"github.com/pingcap/tidb/context" | ||
|
@@ -190,8 +192,85 @@ func builtinRound(args []types.Datum, ctx context.Context) (d types.Datum, err e | |
|
||
// See http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html#function_conv | ||
func builtinConv(args []types.Datum, ctx context.Context) (d types.Datum, err error) { | ||
//TODO implement | ||
return d, errors.New("Function unimplement") | ||
var ( | ||
n string | ||
toBase int64 | ||
fromBase int64 | ||
signed bool | ||
negative bool | ||
touval bool | ||
) | ||
for _, arg := range args { | ||
if arg.IsNull() { | ||
return d, nil | ||
} | ||
} | ||
n, err = args[0].ToString() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use |
||
if err != nil { | ||
return d, errors.Trace(err) | ||
} | ||
sc := ctx.GetSessionVars().StmtCtx | ||
fromBase, err = args[1].ToInt64(sc) | ||
if err != nil { | ||
return d, errors.Trace(err) | ||
} | ||
toBase, err = args[2].ToInt64(sc) | ||
if err != nil { | ||
return d, errors.Trace(err) | ||
} | ||
|
||
if fromBase < 0 { | ||
fromBase = -fromBase | ||
signed = true | ||
} | ||
if toBase < 0 { | ||
touval = true | ||
toBase = -toBase | ||
} | ||
if fromBase > 36 || fromBase < 2 || toBase > 36 || toBase < 2 { | ||
return d, nil | ||
} | ||
n = getValidPrefix(n, fromBase) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should trim 'n' before calling getValidPrefix. |
||
if len(n) == 0 { | ||
return d, nil | ||
} | ||
if n[0] == '-' { | ||
negative = true | ||
n = n[1:] | ||
} | ||
|
||
val, err := strconv.ParseUint(n, int(fromBase), 64) | ||
if err != nil { | ||
return d, errors.Trace(types.ErrOverflow) | ||
} | ||
//Ref https://github.com/mysql/mysql-server/blob/5.7/strings/ctype-simple.c#L598 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add a space after the slash. And using |
||
if signed { | ||
if negative && val > -math.MinInt64 { | ||
val = -math.MinInt64 | ||
} | ||
if !negative && val > math.MaxInt64 { | ||
val = math.MaxInt64 | ||
} | ||
} | ||
if negative { | ||
val = -val | ||
} | ||
// Ref https://github.com/mysql/mysql-server/blob/5.7/strings/longlong2str.c#L58 | ||
if int64(val) < 0 { | ||
negative = true | ||
} else { | ||
negative = false | ||
} | ||
if touval && negative { | ||
val = 0 - val | ||
} | ||
|
||
s := strconv.FormatUint(val, int(toBase)) | ||
if negative && touval { | ||
s = "-" + s | ||
} | ||
d.SetString(strings.ToUpper(s)) | ||
return d, nil | ||
} | ||
|
||
// See http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html#function_crc32 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -191,3 +191,40 @@ func (s *testEvaluatorSuite) TestCRC32(c *C) { | |
c.Assert(v, testutil.DatumEquals, t["Ret"][0]) | ||
} | ||
} | ||
|
||
func (s *testEvaluatorSuite) TestConv(c *C) { | ||
defer testleak.AfterTest(c)() | ||
tbl := []struct { | ||
Arg []interface{} | ||
Ret interface{} | ||
}{ | ||
{[]interface{}{"a", 16, 2}, "1010"}, | ||
{[]interface{}{"6E", 18, 8}, "172"}, | ||
{[]interface{}{"-17", 10, -18}, "-H"}, | ||
{[]interface{}{"-17", 10, 18}, "2D3FGB0B9CG4BD1H"}, | ||
{[]interface{}{nil, 10, 10}, nil}, | ||
{[]interface{}{"+18aZ", 7, 36}, 1}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we handle this case: "SELECT CONV(10+'10'+'10'+X'0a',10,10);" ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes we can, constant folding will handle it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, please add this test case. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add a test with a negative |
||
} | ||
|
||
Dtbl := tblToDtbl(tbl) | ||
|
||
for _, t := range Dtbl { | ||
v, err := builtinConv(t["Arg"], s.ctx) | ||
c.Assert(err, IsNil) | ||
c.Assert(v, testutil.DatumEquals, t["Ret"][0]) | ||
} | ||
|
||
v := []struct { | ||
s string | ||
base int64 | ||
ret string | ||
}{ | ||
{"-123456D1f", 5, "-1234"}, | ||
{"+12azD", 16, "12a"}, | ||
{"+", 12, ""}, | ||
} | ||
for _, t := range v { | ||
r := getValidPrefix(t.s, t.base) | ||
c.Assert(r, Equals, t.ret) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,8 @@ | |
package expression | ||
|
||
import ( | ||
"unicode" | ||
|
||
"github.com/juju/errors" | ||
"github.com/pingcap/tidb/ast" | ||
"github.com/pingcap/tidb/sessionctx/variable" | ||
|
@@ -98,3 +100,42 @@ func calculateSum(sc *variable.StatementContext, sum, v types.Datum) (data types | |
return data, errors.Errorf("invalid value %v for aggregate", sum.Kind()) | ||
} | ||
} | ||
|
||
// getValidPrefix gets a prefix of string which can parsed to a number with base. the minimun base is 2 and the maximum is 36. | ||
func getValidPrefix(s string, base int64) string { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function can be write more elegant, Rest LGTM There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add some unit test cases for this function? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok |
||
var ( | ||
validLen int | ||
upper rune | ||
) | ||
switch { | ||
case base >= 2 && base <= 9: | ||
upper = rune('0' + base) | ||
case base <= 36: | ||
upper = rune('A' + base - 10) | ||
default: | ||
return "" | ||
} | ||
Loop: | ||
for i := 0; i < len(s); i++ { | ||
c := rune(s[i]) | ||
switch { | ||
case unicode.IsDigit(c) || unicode.IsLower(c) || unicode.IsUpper(c): | ||
c = unicode.ToUpper(c) | ||
if c < upper { | ||
validLen = i + 1 | ||
} else { | ||
break Loop | ||
} | ||
case c == '+' || c == '-': | ||
if i != 0 { | ||
break Loop | ||
} | ||
default: | ||
break Loop | ||
} | ||
} | ||
if validLen > 1 && s[0] == '+' { | ||
return s[1:validLen] | ||
} | ||
return s[:validLen] | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the meaning of
touval
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it means when tobase < 0 , we let n to unsigned ignore the sign. example -15 to 15, 15 to 15. if tobase > 0, n will be forced conversion.