292 lines
7.7 KiB
Go
292 lines
7.7 KiB
Go
// Copyright 2015 PingCAP, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package expression
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/juju/errors"
|
|
"github.com/pingcap/tidb/context"
|
|
"github.com/pingcap/tidb/util/types"
|
|
)
|
|
|
|
var (
|
|
_ Expression = (*FunctionSubstring)(nil)
|
|
_ Expression = (*FunctionSubstringIndex)(nil)
|
|
_ Expression = (*FunctionLocate)(nil)
|
|
)
|
|
|
|
// FunctionSubstring returns the substring as specified.
|
|
// See: https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_substring
|
|
type FunctionSubstring struct {
|
|
StrExpr Expression
|
|
Pos Expression
|
|
Len Expression
|
|
}
|
|
|
|
// Clone implements the Expression Clone interface.
|
|
func (f *FunctionSubstring) Clone() Expression {
|
|
expr := f.StrExpr.Clone()
|
|
nf := &FunctionSubstring{
|
|
StrExpr: expr,
|
|
Pos: f.Pos,
|
|
Len: f.Len,
|
|
}
|
|
return nf
|
|
}
|
|
|
|
// IsStatic implements the Expression IsStatic interface.
|
|
func (f *FunctionSubstring) IsStatic() bool {
|
|
return f.StrExpr.IsStatic()
|
|
}
|
|
|
|
// String implements the Expression String interface.
|
|
func (f *FunctionSubstring) String() string {
|
|
if f.Len != nil {
|
|
return fmt.Sprintf("SUBSTRING(%s, %s, %s)", f.StrExpr.String(), f.Pos.String(), f.Len.String())
|
|
}
|
|
return fmt.Sprintf("SUBSTRING(%s, %s)", f.StrExpr.String(), f.Pos.String())
|
|
}
|
|
|
|
// Eval implements the Expression Eval interface.
|
|
func (f *FunctionSubstring) Eval(ctx context.Context, args map[interface{}]interface{}) (interface{}, error) {
|
|
fs, err := f.StrExpr.Eval(ctx, args)
|
|
if err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
str, ok := fs.(string)
|
|
if !ok {
|
|
return nil, errors.Errorf("Substring invalid args, need string but get %T", fs)
|
|
}
|
|
|
|
t, err := f.Pos.Eval(ctx, args)
|
|
if err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
p, ok := t.(int64)
|
|
if !ok {
|
|
return nil, errors.Errorf("Substring invalid pos args, need int but get %T", t)
|
|
}
|
|
pos := int(p)
|
|
|
|
length := -1
|
|
if f.Len != nil {
|
|
t, err := f.Len.Eval(ctx, args)
|
|
if err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
p, ok := t.(int64)
|
|
if !ok {
|
|
return nil, errors.Errorf("Substring invalid len args, need int but get %T", t)
|
|
}
|
|
length = int(p)
|
|
}
|
|
// The forms without a len argument return a substring from string str starting at position pos.
|
|
// The forms with a len argument return a substring len characters long from string str, starting at position pos.
|
|
// The forms that use FROM are standard SQL syntax. It is also possible to use a negative value for pos.
|
|
// In this case, the beginning of the substring is pos characters from the end of the string, rather than the beginning.
|
|
// A negative value may be used for pos in any of the forms of this function.
|
|
if pos < 0 {
|
|
pos = len(str) + pos
|
|
} else {
|
|
pos--
|
|
}
|
|
if pos > len(str) || pos <= 0 {
|
|
pos = len(str)
|
|
}
|
|
end := len(str)
|
|
if length != -1 {
|
|
end = pos + length
|
|
}
|
|
if end > len(str) {
|
|
end = len(str)
|
|
}
|
|
return str[pos:end], nil
|
|
}
|
|
|
|
// Accept implements Expression Accept interface.
|
|
func (f *FunctionSubstring) Accept(v Visitor) (Expression, error) {
|
|
return v.VisitFunctionSubstring(f)
|
|
}
|
|
|
|
// FunctionSubstringIndex returns the substring as specified.
|
|
// See: https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_substring-index
|
|
type FunctionSubstringIndex struct {
|
|
StrExpr Expression
|
|
Delim Expression
|
|
Count Expression
|
|
}
|
|
|
|
// Clone implements the Expression Clone interface.
|
|
func (f *FunctionSubstringIndex) Clone() Expression {
|
|
nf := &FunctionSubstringIndex{
|
|
StrExpr: f.StrExpr.Clone(),
|
|
Delim: f.Delim.Clone(),
|
|
Count: f.Count.Clone(),
|
|
}
|
|
return nf
|
|
}
|
|
|
|
// IsStatic implements the Expression IsStatic interface.
|
|
func (f *FunctionSubstringIndex) IsStatic() bool {
|
|
return f.StrExpr.IsStatic() && f.Delim.IsStatic() && f.Count.IsStatic()
|
|
}
|
|
|
|
// String implements the Expression String interface.
|
|
func (f *FunctionSubstringIndex) String() string {
|
|
return fmt.Sprintf("SUBSTRING_INDEX(%s, %s, %s)", f.StrExpr, f.Delim, f.Count)
|
|
}
|
|
|
|
// Eval implements the Expression Eval interface.
|
|
func (f *FunctionSubstringIndex) Eval(ctx context.Context, args map[interface{}]interface{}) (interface{}, error) {
|
|
fs, err := f.StrExpr.Eval(ctx, args)
|
|
if err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
str, ok := fs.(string)
|
|
if !ok {
|
|
return nil, errors.Errorf("Substring_Index invalid args, need string but get %T", fs)
|
|
}
|
|
|
|
t, err := f.Delim.Eval(ctx, args)
|
|
if err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
delim, ok := t.(string)
|
|
if !ok {
|
|
return nil, errors.Errorf("Substring_Index invalid delim, need string but get %T", t)
|
|
}
|
|
|
|
t, err = f.Count.Eval(ctx, args)
|
|
if err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
c, err := types.ToInt64(t)
|
|
if err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
count := int(c)
|
|
strs := strings.Split(str, delim)
|
|
var (
|
|
start = 0
|
|
end = len(strs)
|
|
)
|
|
if count > 0 {
|
|
// If count is positive, everything to the left of the final delimiter (counting from the left) is returned.
|
|
if count < end {
|
|
end = count
|
|
}
|
|
} else {
|
|
// If count is negative, everything to the right of the final delimiter (counting from the right) is returned.
|
|
count = -count
|
|
if count < end {
|
|
start = end - count
|
|
}
|
|
}
|
|
substrs := strs[start:end]
|
|
return strings.Join(substrs, delim), nil
|
|
}
|
|
|
|
// Accept implements Expression Accept interface.
|
|
func (f *FunctionSubstringIndex) Accept(v Visitor) (Expression, error) {
|
|
return v.VisitFunctionSubstringIndex(f)
|
|
}
|
|
|
|
// FunctionLocate returns the position of the first occurrence of substring.
|
|
// See: https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_locate
|
|
type FunctionLocate struct {
|
|
Str Expression
|
|
SubStr Expression
|
|
Pos Expression
|
|
}
|
|
|
|
// Clone implements the Expression Clone interface.
|
|
func (f *FunctionLocate) Clone() Expression {
|
|
nf := &FunctionLocate{
|
|
Str: f.Str.Clone(),
|
|
SubStr: f.SubStr.Clone(),
|
|
}
|
|
if f.Pos != nil {
|
|
nf.Pos = f.Pos.Clone()
|
|
}
|
|
return nf
|
|
}
|
|
|
|
// IsStatic implements the Expression IsStatic interface.
|
|
func (f *FunctionLocate) IsStatic() bool {
|
|
return f.Str.IsStatic() && f.SubStr.IsStatic() && (f.Pos == nil || f.Pos.IsStatic())
|
|
}
|
|
|
|
// String implements the Expression String interface.
|
|
func (f *FunctionLocate) String() string {
|
|
if f.Pos != nil {
|
|
return fmt.Sprintf("LOCATE(%s, %s, %s)", f.SubStr, f.Str, f.Pos)
|
|
}
|
|
return fmt.Sprintf("LOCATE(%s, %s)", f.SubStr, f.Str)
|
|
}
|
|
|
|
// Eval implements the Expression Eval interface.
|
|
func (f *FunctionLocate) Eval(ctx context.Context, args map[interface{}]interface{}) (interface{}, error) {
|
|
// eval str
|
|
fs, err := f.Str.Eval(ctx, args)
|
|
if err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
if fs == nil {
|
|
return nil, nil
|
|
}
|
|
str, err := types.ToString(fs)
|
|
if err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
// eval substr
|
|
fs, err = f.SubStr.Eval(ctx, args)
|
|
if err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
if fs == nil {
|
|
return nil, nil
|
|
}
|
|
substr, err := types.ToString(fs)
|
|
if err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
// eval pos
|
|
pos := 0
|
|
if f.Pos != nil {
|
|
t, err := f.Pos.Eval(ctx, args)
|
|
if err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
p, err := types.ToInt64(t)
|
|
if err != nil {
|
|
return nil, errors.Trace(err)
|
|
}
|
|
pos = int(p)
|
|
}
|
|
// eval locate
|
|
if pos < 0 || pos > len(str) {
|
|
return 0, errors.Errorf("Locate invalid pos args: %d", pos)
|
|
}
|
|
str = str[pos:]
|
|
i := strings.Index(str, substr)
|
|
return i + 1 + pos, nil
|
|
}
|
|
|
|
// Accept implements Expression Accept interface.
|
|
func (f *FunctionLocate) Accept(v Visitor) (Expression, error) {
|
|
return v.VisitFunctionLocate(f)
|
|
}
|