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 2 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,67 @@ 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 | ||
r string | ||
toBase int64 | ||
fromBase int64 | ||
signed bool | ||
) | ||
for _, arg := range args { | ||
if arg.IsNull() { | ||
return d, nil | ||
} | ||
} | ||
|
||
n, err = args[0].ToString() | ||
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 | ||
} | ||
if toBase < 0 { | ||
signed = true | ||
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. "If from_base is a negative number, N is regarded as a signed number. Otherwise, N is treated as unsigned. " 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. the docs seems have a error , I test in mysql, just toBase affect signed. 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. Please refer to mysql source code: https://github.com/mysql/mysql-server/blob/5.7/sql/item_strfunc.cc#L3959 |
||
toBase = -toBase | ||
} | ||
if fromBase > 36 || fromBase < 2 { | ||
return d, nil | ||
} | ||
if 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 !signed { | ||
c, err := strconv.ParseUint(n, int(fromBase), 64) | ||
if err != nil { | ||
return d, errors.Trace(types.ErrOverflow) | ||
} | ||
r = strconv.FormatUint(c, int(toBase)) | ||
} else { | ||
c, err := strconv.ParseInt(n, int(fromBase), 64) | ||
if err != nil { | ||
return d, errors.Trace(types.ErrOverflow) | ||
} | ||
r = strconv.FormatInt(c, int(toBase)) | ||
} | ||
|
||
d.SetString(strings.ToUpper(r)) | ||
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,25 @@ 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{}{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]) | ||
} | ||
} |
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 "" | ||
} | ||
|
||
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 | ||
} | ||
case c == '+' || c == '-': | ||
if i != 0 { | ||
break | ||
} | ||
default: | ||
break | ||
} | ||
} | ||
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.
Use
:=
instead of=
, and remove line 196.The same as line 213, line 217 and line 233.