Skip to content
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

expression: rewrite builtin function: EXPORT_SET #4434

Merged
merged 14 commits into from Sep 7, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
207 changes: 138 additions & 69 deletions expression/builtin_string.go
Expand Up @@ -133,7 +133,9 @@ var (
_ builtinFunc = &builtinQuoteSig{}
_ builtinFunc = &builtinBinSig{}
_ builtinFunc = &builtinEltSig{}
_ builtinFunc = &builtinExportSetSig{}
_ builtinFunc = &builtinExportSet3ArgSig{}
_ builtinFunc = &builtinExportSet4ArgSig{}
_ builtinFunc = &builtinExportSet5ArgSig{}
_ builtinFunc = &builtinFormatWithLocaleSig{}
_ builtinFunc = &builtinFormatSig{}
_ builtinFunc = &builtinFromBase64Sig{}
Expand Down Expand Up @@ -1599,7 +1601,7 @@ type builtinLpadBinarySig struct {
}

// evalString evals LPAD(str,len,padstr).
// See https://dev.mysql.com/doc/refman/5.6/en/string-functions.html#function_lpad
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_lpad
func (b *builtinLpadBinarySig) evalString(row []types.Datum) (string, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx

Expand Down Expand Up @@ -1637,7 +1639,7 @@ type builtinLpadSig struct {
}

// evalString evals LPAD(str,len,padstr).
// See https://dev.mysql.com/doc/refman/5.6/en/string-functions.html#function_lpad
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_lpad
func (b *builtinLpadSig) evalString(row []types.Datum) (string, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx

Expand Down Expand Up @@ -2308,7 +2310,7 @@ type builtinBinSig struct {
}

// evalString evals BIN(N).
// See https://dev.mysql.com/doc/refman/5.6/en/string-functions.html#function_bin
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_bin
func (b *builtinBinSig) evalString(row []types.Datum) (string, bool, error) {
val, isNull, err := b.args[0].EvalInt(row, b.ctx.GetSessionVars().StmtCtx)
if isNull || err != nil {
Expand Down Expand Up @@ -2349,7 +2351,7 @@ type builtinEltSig struct {
}

// evalString evals a builtinEltSig.
// See https://dev.mysql.com/doc/refman/5.6/en/string-functions.html#function_elt
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_elt
func (b *builtinEltSig) evalString(row []types.Datum) (string, bool, error) {
idx, isNull, err := b.args[0].EvalInt(row, b.ctx.GetSessionVars().StmtCtx)
if isNull || err != nil {
Expand All @@ -2369,78 +2371,145 @@ type exportSetFunctionClass struct {
baseFunctionClass
}

func (c *exportSetFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
func (c *exportSetFunctionClass) getFunction(ctx context.Context, args []Expression) (sig builtinFunc, err error) {
if err = c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
sig := &builtinExportSetSig{newBaseBuiltinFunc(args, ctx)}
return sig.setSelf(sig), nil
}

type builtinExportSetSig struct {
baseBuiltinFunc
}

// eval evals a builtinExportSetSig.
// See https://dev.mysql.com/doc/refman/5.6/en/string-functions.html#function_export-set
func (b *builtinExportSetSig) eval(row []types.Datum) (d types.Datum, err error) {
args, err := b.evalArgs(row)
if err != nil {
return d, errors.Trace(err)
argTps := make([]evalTp, 0, 5)
argTps = append(argTps, tpInt, tpString, tpString)
if len(args) > 3 {
argTps = append(argTps, tpString)
}
var (
bits uint64
on string
off string
separator = ","
numberOfBits = 64
)
if len(args) > 4 {
argTps = append(argTps, tpInt)
}
bf := newBaseBuiltinFuncWithTp(args, ctx, tpString, argTps...)
bf.tp.Flen = mysql.MaxBlobWidth
switch len(args) {
case 5:
var arg int64
arg, err = args[4].ToInt64(b.ctx.GetSessionVars().StmtCtx)
if err != nil {
return d, errors.Trace(err)
}
if arg >= 0 && arg < 64 {
numberOfBits = int(arg)
}
fallthrough
case 4:
separator, err = args[3].ToString()
if err != nil {
return d, errors.Trace(err)
}
fallthrough
case 3:
arg, err := args[0].ToInt64(b.ctx.GetSessionVars().StmtCtx)
if err != nil {
return d, errors.Trace(err)
}
bits = uint64(arg)
on, err = args[1].ToString()
if err != nil {
return d, errors.Trace(err)
}
off, err = args[2].ToString()
if err != nil {
return d, errors.Trace(err)
}
sig = &builtinExportSet3ArgSig{baseStringBuiltinFunc{bf}}
case 4:
sig = &builtinExportSet4ArgSig{baseStringBuiltinFunc{bf}}
case 5:
sig = &builtinExportSet5ArgSig{baseStringBuiltinFunc{bf}}
}
var result string
for i := 0; i < numberOfBits; i++ {
if bits&1 > 0 {
return sig.setSelf(sig), nil
}

// exportSet evals EXPORT_SET(bits,on,off,separator,number_of_bits).
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_export-set
func exportSet(bits int64, on, off, separator string, numberOfBits int64) string {
result := ""
for i := uint64(0); i < uint64(numberOfBits); i++ {
if (bits & (1 << i)) > 0 {
result += on
} else {
result += off
}
bits >>= 1
if i < numberOfBits-1 {
if i < uint64(numberOfBits)-1 {
result += separator
}
}
d.SetString(result)
return d, nil
return result
}

type builtinExportSet3ArgSig struct {
baseStringBuiltinFunc
}

// evalString evals EXPORT_SET(bits,on,off).
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_export-set
func (b *builtinExportSet3ArgSig) evalString(row []types.Datum) (string, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx

bits, isNull, err := b.args[0].EvalInt(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}

on, isNull, err := b.args[1].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}

off, isNull, err := b.args[2].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}

return exportSet(bits, on, off, ",", 64), false, nil
}

type builtinExportSet4ArgSig struct {
baseStringBuiltinFunc
}

// evalString evals EXPORT_SET(bits,on,off,separator).
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_export-set
func (b *builtinExportSet4ArgSig) evalString(row []types.Datum) (string, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx

bits, isNull, err := b.args[0].EvalInt(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}

on, isNull, err := b.args[1].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}

off, isNull, err := b.args[2].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}

separator, isNull, err := b.args[3].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}

return exportSet(bits, on, off, separator, 64), false, nil
}

type builtinExportSet5ArgSig struct {
baseStringBuiltinFunc
}

// evalString evals EXPORT_SET(bits,on,off,separator,number_of_bits).
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_export-set
func (b *builtinExportSet5ArgSig) evalString(row []types.Datum) (string, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx

bits, isNull, err := b.args[0].EvalInt(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}

on, isNull, err := b.args[1].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}

off, isNull, err := b.args[2].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}

separator, isNull, err := b.args[3].EvalString(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}

numberOfBits, isNull, err := b.args[4].EvalInt(row, sc)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
if numberOfBits < 0 || numberOfBits > 64 {
numberOfBits = 64
}

return exportSet(bits, on, off, separator, numberOfBits), false, nil
}

type formatFunctionClass struct {
Expand Down Expand Up @@ -2472,7 +2541,7 @@ type builtinFormatWithLocaleSig struct {
}

// evalString evals FORMAT(X,D,locale).
// See https://dev.mysql.com/doc/refman/5.6/en/string-functions.html#function_format
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_format
func (b *builtinFormatWithLocaleSig) evalString(row []types.Datum) (string, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx

Expand Down Expand Up @@ -2538,7 +2607,7 @@ type builtinFromBase64Sig struct {
}

// evalString evals FROM_BASE64(str).
// See https://dev.mysql.com/doc/refman/5.6/en/string-functions.html#function_from-base64
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_from-base64
func (b *builtinFromBase64Sig) evalString(row []types.Datum) (string, bool, error) {
str, isNull, err := b.args[0].EvalString(row, b.ctx.GetSessionVars().StmtCtx)
if isNull || err != nil {
Expand Down Expand Up @@ -2649,7 +2718,7 @@ type builtinInsertFuncSig struct {
}

// eval evals a builtinInsertFuncSig.
// See https://dev.mysql.com/doc/refman/5.6/en/string-functions.html#function_insert
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_insert
func (b *builtinInsertFuncSig) eval(row []types.Datum) (d types.Datum, err error) {
args, err := b.evalArgs(row)
if err != nil {
Expand Down Expand Up @@ -2720,7 +2789,7 @@ type builtinInstrSig struct{ baseIntBuiltinFunc }
type builtinInstrBinarySig struct{ baseIntBuiltinFunc }

// evalInt evals INSTR(str,substr), case insensitive
// See https://dev.mysql.com/doc/refman/5.6/en/string-functions.html#function_instr
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_instr
func (b *builtinInstrSig) evalInt(row []types.Datum) (int64, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx

Expand All @@ -2744,7 +2813,7 @@ func (b *builtinInstrSig) evalInt(row []types.Datum) (int64, bool, error) {
}

// evalInt evals INSTR(str,substr), case sensitive
// See https://dev.mysql.com/doc/refman/5.6/en/string-functions.html#function_instr
// See https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_instr
func (b *builtinInstrBinarySig) evalInt(row []types.Datum) (int64, bool, error) {
sc := b.ctx.GetSessionVars().StmtCtx

Expand Down
2 changes: 2 additions & 0 deletions expression/builtin_string_test.go
Expand Up @@ -1685,6 +1685,8 @@ func (s *testEvaluatorSuite) TestExportSet(c *C) {
for _, t := range estd {
f, err := fc.getFunction(s.ctx, datumsToConstants(types.MakeDatums(t.argLst...)))
c.Assert(err, IsNil)
c.Assert(f, NotNil)
c.Assert(f.canBeFolded(), IsTrue)
exportSetRes, err := f.eval(nil)
res, err := exportSetRes.ToString()
c.Assert(err, IsNil)
Expand Down
14 changes: 14 additions & 0 deletions expression/integration_test.go
Expand Up @@ -835,6 +835,20 @@ func (s *testIntegrationSuite) TestStringBuiltin(c *C) {
result = tk.MustQuery(`select quote(0121), quote(0000), quote("中文"), quote(NULL);`)
result.Check(testkit.Rows("'121' '0' '中文' <nil>"))

// for export_set
result = tk.MustQuery(`select export_set(7, "1", "0", ",", 65);`)
result.Check(testkit.Rows("1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"))
result = tk.MustQuery(`select export_set(7, "1", "0", ",", -1);`)
result.Check(testkit.Rows("1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"))
result = tk.MustQuery(`select export_set(7, "1", "0", ",");`)
result.Check(testkit.Rows("1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"))
result = tk.MustQuery(`select export_set(7, "1", "0");`)
result.Check(testkit.Rows("1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"))
result = tk.MustQuery(`select export_set(NULL, "1", "0", ",", 65);`)
result.Check(testkit.Rows("<nil>"))
result = tk.MustQuery(`select export_set(7, "1", "0", ",", 1);`)
result.Check(testkit.Rows("1"))

// for format
result = tk.MustQuery(`select format(12332.1, 4), format(12332.2, 0), format(12332.2, 2,'en_US');`)
result.Check(testkit.Rows("12,332.1000 12,332 12,332.20"))
Expand Down
4 changes: 4 additions & 0 deletions plan/typeinfer_test.go
Expand Up @@ -434,6 +434,10 @@ func (s *testPlanSuite) createTestCase4StrFuncs() []typeInferTestCase {
{"quote(c_float_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 26, types.UnspecifiedLength},
{"quote(c_double_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 46, types.UnspecifiedLength},

{"export_set(c_double_d, c_text_d, c_text_d)", mysql.TypeLongBlob, charset.CharsetUTF8, 0, mysql.MaxBlobWidth, types.UnspecifiedLength},
{"export_set(c_double_d, c_text_d, c_text_d, c_text_d)", mysql.TypeLongBlob, charset.CharsetUTF8, 0, mysql.MaxBlobWidth, types.UnspecifiedLength},
{"export_set(c_double_d, c_text_d, c_text_d, c_text_d, c_int_d)", mysql.TypeLongBlob, charset.CharsetUTF8, 0, mysql.MaxBlobWidth, types.UnspecifiedLength},

{"format(c_double_d, c_double_d)", mysql.TypeLongBlob, charset.CharsetUTF8, 0, mysql.MaxBlobWidth, types.UnspecifiedLength},
{"format(c_double_d, c_double_d, c_binary)", mysql.TypeLongBlob, charset.CharsetUTF8, 0, mysql.MaxBlobWidth, types.UnspecifiedLength},
}
Expand Down