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: INSERT #4414
Changes from 1 commit
c823377
b46927e
d0cdf95
b44f7ba
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 |
---|---|---|
|
@@ -78,7 +78,7 @@ var ( | |
_ functionClass = &formatFunctionClass{} | ||
_ functionClass = &fromBase64FunctionClass{} | ||
_ functionClass = &toBase64FunctionClass{} | ||
_ functionClass = &insertFuncFunctionClass{} | ||
_ functionClass = &insertFunctionClass{} | ||
_ functionClass = &instrFunctionClass{} | ||
_ functionClass = &loadFileFunctionClass{} | ||
) | ||
|
@@ -137,7 +137,8 @@ var ( | |
_ builtinFunc = &builtinFormatSig{} | ||
_ builtinFunc = &builtinFromBase64Sig{} | ||
_ builtinFunc = &builtinToBase64Sig{} | ||
_ builtinFunc = &builtinInsertFuncSig{} | ||
_ builtinFunc = &builtinInsertBinarySig{} | ||
_ builtinFunc = &builtinInsertSig{} | ||
_ builtinFunc = &builtinInstrSig{} | ||
_ builtinFunc = &builtinInstrBinarySig{} | ||
) | ||
|
@@ -2611,70 +2612,105 @@ func splitToSubN(s string, n int) []string { | |
return subs | ||
} | ||
|
||
type insertFuncFunctionClass struct { | ||
type insertFunctionClass struct { | ||
baseFunctionClass | ||
} | ||
|
||
func (c *insertFuncFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) { | ||
func (c *insertFunctionClass) getFunction(ctx context.Context, args []Expression) (builtinFunc, error) { | ||
if err := c.verifyArgs(args); err != nil { | ||
return nil, errors.Trace(err) | ||
} | ||
sig := &builtinInsertFuncSig{newBaseBuiltinFunc(args, ctx)} | ||
bf := newBaseBuiltinFuncWithTp(args, ctx, tpString, tpString, tpInt, tpInt, tpString) | ||
bf.tp.Flen = mysql.MaxBlobWidth | ||
SetBinFlagOrBinStr(args[0].GetType(), bf.tp) | ||
SetBinFlagOrBinStr(args[3].GetType(), bf.tp) | ||
var sig builtinFunc | ||
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. move the definition of sig and err to line 2619. |
||
if types.IsBinaryStr(args[0].GetType()) { | ||
sig = &builtinInsertBinarySig{baseStringBuiltinFunc{bf}} | ||
} else { | ||
sig = &builtinInsertSig{baseStringBuiltinFunc{bf}} | ||
} | ||
return sig.setSelf(sig), nil | ||
} | ||
|
||
type builtinInsertFuncSig struct { | ||
baseBuiltinFunc | ||
type builtinInsertBinarySig struct { | ||
baseStringBuiltinFunc | ||
} | ||
|
||
// eval evals a builtinInsertFuncSig. | ||
// evalString evals INSERT(str,pos,len,newstr). | ||
// See https://dev.mysql.com/doc/refman/5.6/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 { | ||
return d, errors.Trace(err) | ||
func (b *builtinInsertBinarySig) evalString(row []types.Datum) (string, bool, error) { | ||
sc := b.ctx.GetSessionVars().StmtCtx | ||
|
||
str, isNull, err := b.args[0].EvalString(row, sc) | ||
if isNull || err != nil { | ||
return "", true, errors.Trace(err) | ||
} | ||
|
||
// Returns NULL if any argument is NULL. | ||
if args[0].IsNull() || args[1].IsNull() || args[2].IsNull() || args[3].IsNull() { | ||
return | ||
pos, isNull, err := b.args[1].EvalInt(row, sc) | ||
if isNull || err != nil { | ||
return "", true, errors.Trace(err) | ||
} | ||
|
||
str0, err := args[0].ToString() | ||
if err != nil { | ||
return d, errors.Trace(err) | ||
length, isNull, err := b.args[2].EvalInt(row, sc) | ||
if isNull || err != nil { | ||
return "", true, errors.Trace(err) | ||
} | ||
str := []rune(str0) | ||
strLen := len(str) | ||
|
||
posInt64, err := args[1].ToInt64(b.ctx.GetSessionVars().StmtCtx) | ||
if err != nil { | ||
return d, errors.Trace(err) | ||
newstr, isNull, err := b.args[3].EvalString(row, sc) | ||
if isNull || err != nil { | ||
return "", true, errors.Trace(err) | ||
} | ||
pos := int(posInt64) | ||
|
||
lenInt64, err := args[2].ToInt64(b.ctx.GetSessionVars().StmtCtx) | ||
if err != nil { | ||
return d, errors.Trace(err) | ||
strLength := int64(len(str)) | ||
if pos < 1 || pos > strLength { | ||
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 check can be moved to line 2654? |
||
return str, false, nil | ||
} | ||
if length > strLength-pos+1 || length < 0 { | ||
return str[0:pos-1] + newstr, false, nil | ||
} | ||
length := int(lenInt64) | ||
return str[0:pos-1] + newstr + str[pos+length-1:], false, nil | ||
} | ||
|
||
newstr, err := args[3].ToString() | ||
if err != nil { | ||
return d, errors.Trace(err) | ||
type builtinInsertSig struct { | ||
baseStringBuiltinFunc | ||
} | ||
|
||
// evalString evals INSERT(str,pos,len,newstr). | ||
// See https://dev.mysql.com/doc/refman/5.6/en/string-functions.html#function_insert | ||
func (b *builtinInsertSig) evalString(row []types.Datum) (string, bool, error) { | ||
sc := b.ctx.GetSessionVars().StmtCtx | ||
|
||
str, isNull, err := b.args[0].EvalString(row, sc) | ||
if isNull || err != nil { | ||
return "", true, errors.Trace(err) | ||
} | ||
|
||
var s string | ||
if pos < 1 || pos > strLen { | ||
s = str0 | ||
} else if length > strLen-pos+1 || length < 0 { | ||
s = string(str[0:pos-1]) + newstr | ||
} else { | ||
s = string(str[0:pos-1]) + newstr + string(str[pos+length-1:]) | ||
pos, isNull, err := b.args[1].EvalInt(row, sc) | ||
if isNull || err != nil { | ||
return "", true, errors.Trace(err) | ||
} | ||
|
||
d.SetString(s) | ||
return d, nil | ||
length, isNull, err := b.args[2].EvalInt(row, sc) | ||
if isNull || err != nil { | ||
return "", true, errors.Trace(err) | ||
} | ||
|
||
newstr, isNull, err := b.args[3].EvalString(row, sc) | ||
if isNull || err != nil { | ||
return "", true, errors.Trace(err) | ||
} | ||
|
||
runes := []rune(str) | ||
runeLength := int64(len(runes)) | ||
|
||
if pos < 1 || pos > runeLength { | ||
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. ditto |
||
return str, false, nil | ||
} | ||
if length > runeLength-pos+1 || length < 0 { | ||
return string(runes[0:pos-1]) + newstr, false, nil | ||
} | ||
return string(runes[0:pos-1]) + newstr + string(runes[pos+length-1:]), false, nil | ||
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 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. package main
import "fmt"
import "time"
func main() {
str := ""
str1 := "aaaa"
str2 := "bbbb"
str3 := "cccc"
start := time.Now()
for i, round := 0, 1000000; i < round; i++ {
str = str1 + str2 + str3
}
fmt.Printf("%vns\n", time.Now().Sub(start).Nanoseconds())
fmt.Printf("%s\n", str)
start = time.Now()
for i, round := 0, 1000000; i < round; i++ {
str = fmt.Sprintf("%s%s%s", str1, str2, str3)
}
fmt.Printf("%vns\n", time.Now().Sub(start).Nanoseconds())
fmt.Printf("%s\n", str)
} $go build a.go
$./a
56394494ns
aaaabbbbcccc
279845006ns
aaaabbbbcccc It seems |
||
} | ||
|
||
type instrFunctionClass struct { | ||
|
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.
why?
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.
this is a bug fix: for
INSERT(str,pos,len,newstr)
, ifstr
is a binary string, we should treat it like a byte array, otherwise we should treat it like a code point array. seebuiltinInsertBinarySig
andbuiltinInsertSig
for more detailThere 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.
If we use args[0].GetType() after newBaseBuiltinFunc,
args[0] has already been wrapped with cast.
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.
when
arg[0]
is a binary string, it will not be wrapped with a cast.btw, I think wrapped cast functions should guarantee the correct type inference, and every function should do its type inference based on its children, no matter what expression the children is