Skip to content

Commit 0391820

Browse files
coocoodngaut
authored andcommitted
experssion: implement MD5 builtin function (pingcap#2780)
* experssion: implement MD5 builtin function
1 parent 8a212e4 commit 0391820

6 files changed

Lines changed: 88 additions & 1 deletion

File tree

‎expression/builtin_encryption.go‎

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
package expression
1515

1616
import (
17+
"crypto/md5"
18+
"fmt"
19+
1720
"github.com/juju/errors"
1821
"github.com/pingcap/tidb/context"
1922
"github.com/pingcap/tidb/util/encrypt"
@@ -447,7 +450,23 @@ type builtinMD5Sig struct {
447450

448451
// See https://dev.mysql.com/doc/refman/5.7/en/encryption-functions.html#function_md5
449452
func (b *builtinMD5Sig) eval(row []types.Datum) (d types.Datum, err error) {
450-
return d, errFunctionNotExists.GenByArgs("MD5")
453+
args, err := b.evalArgs(row)
454+
if err != nil {
455+
return types.Datum{}, errors.Trace(err)
456+
}
457+
// This function takes one argument.
458+
arg := args[0]
459+
if arg.IsNull() {
460+
return
461+
}
462+
bin, err := arg.ToBytes()
463+
if err != nil {
464+
return d, errors.Trace(err)
465+
}
466+
sum := md5.Sum(bin)
467+
hexStr := fmt.Sprintf("%x", sum)
468+
d.SetString(hexStr)
469+
return d, nil
451470
}
452471

453472
type oldPasswordFunctionClass struct {

‎expression/builtin_encryption_test.go‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,22 @@ var aesCases = []struct {
3535
{"pingcap", "123", "996E0CA8688D7AD20819B90B273E01C6"},
3636
}
3737

38+
type md5Test struct {
39+
out interface{}
40+
in interface{}
41+
}
42+
43+
var md5Cases = []md5Test{
44+
{"d41d8cd98f00b204e9800998ecf8427e", ""},
45+
{"0cc175b9c0f1b6a831c399e269772661", "a"},
46+
{"187ef4436122d1cc2f40dc2b92f0eba0", "ab"},
47+
{"900150983cd24fb0d6963f7d28e17f72", "abc"},
48+
{"202cb962ac59075b964b07152d234b70", 123},
49+
{"202cb962ac59075b964b07152d234b70", "123"},
50+
{"46ddc40585caa8abc07c460b3485781e", 123.123},
51+
{nil, nil},
52+
}
53+
3854
func (s *testEvaluatorSuite) TestAESEncrypt(c *C) {
3955
defer testleak.AfterTest(c)()
4056
fc := funcs[ast.AesEncrypt]
@@ -88,3 +104,17 @@ func fromHex(str string) (d types.Datum) {
88104
d.SetBytes(h)
89105
return d
90106
}
107+
108+
func (s *testEvaluatorSuite) TestMD5(c *C) {
109+
defer testleak.AfterTest(c)()
110+
fc := funcs[ast.MD5]
111+
for _, test := range md5Cases {
112+
arg := types.NewDatum(test.in)
113+
f, err := fc.getFunction(datumsToConstants([]types.Datum{arg}), s.ctx)
114+
c.Assert(err, IsNil)
115+
out, err := f.eval(nil)
116+
c.Assert(err, IsNil)
117+
c.Assert(out, DeepEquals, types.NewDatum(test.out))
118+
}
119+
s.testNullInput(c, ast.AesDecrypt)
120+
}

‎plan/typeinferer.go‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,10 @@ func (v *typeInferrer) handleFuncCallExpr(x *ast.FuncCallExpr) {
396396
case ast.AesEncrypt, ast.AesDecrypt:
397397
tp = types.NewFieldType(mysql.TypeVarString)
398398
chs = v.defaultCharset
399+
case ast.MD5:
400+
tp = types.NewFieldType(mysql.TypeVarString)
401+
chs = v.defaultCharset
402+
tp.Flen = 32
399403
default:
400404
tp = types.NewFieldType(mysql.TypeUnspecified)
401405
}

‎plan/typeinferer_test.go‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ func (ts *testTypeInferrerSuite) TestInferType(c *C) {
234234
{"timestamp('2003-12-31 12:00:00','12:00:00')", mysql.TypeDatetime, charset.CharsetBin},
235235
{`aes_encrypt("pingcap", "fit2cloud@2014")`, mysql.TypeVarString, "utf8"},
236236
{`aes_decrypt("pingcap", "fit2cloud@2014")`, mysql.TypeVarString, "utf8"},
237+
{`md5(123)`, mysql.TypeVarString, "utf8"},
237238
}
238239
for _, ca := range cases {
239240
ctx := testKit.Se.(context.Context)

‎util/types/datum.go‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1333,6 +1333,20 @@ func (d *Datum) ToString() (string, error) {
13331333
}
13341334
}
13351335

1336+
// ToBytes gets the bytes representation of the datum.
1337+
func (d *Datum) ToBytes() ([]byte, error) {
1338+
switch d.k {
1339+
case KindString, KindBytes:
1340+
return d.GetBytes(), nil
1341+
default:
1342+
str, err := d.ToString()
1343+
if err != nil {
1344+
return nil, errors.Trace(err)
1345+
}
1346+
return []byte(str), nil
1347+
}
1348+
}
1349+
13361350
func invalidConv(d *Datum, tp byte) (Datum, error) {
13371351
return Datum{}, errors.Errorf("cannot convert %v to type %s", d, TypeStr(tp))
13381352
}

‎util/types/datum_test.go‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,3 +300,22 @@ func (ts *testDatumSuite) TestBitOps(c *C) {
300300
c.Assert(result.GetUint64(), Equals, ca.result.GetUint64())
301301
}
302302
}
303+
304+
func (ts *testDatumSuite) TestToBytes(c *C) {
305+
testCases := []struct {
306+
a Datum
307+
out []byte
308+
}{
309+
{NewIntDatum(1), []byte("1")},
310+
{NewDecimalDatum(NewDecFromInt(1)), []byte("1")},
311+
{NewFloat64Datum(1.23), []byte("1.23")},
312+
{NewStringDatum("abc"), []byte("abc")},
313+
}
314+
sc := new(variable.StatementContext)
315+
sc.IgnoreTruncate = true
316+
for _, ca := range testCases {
317+
bin, err := ca.a.ToBytes()
318+
c.Assert(err, IsNil)
319+
c.Assert(bin, BytesEquals, ca.out)
320+
}
321+
}

0 commit comments

Comments
 (0)