Skip to content

Commit

Permalink
This is an automated cherry-pick of pingcap#55724
Browse files Browse the repository at this point in the history
Signed-off-by: ti-chi-bot <ti-community-prow-bot@tidb.io>
  • Loading branch information
Defined2014 authored and ti-chi-bot committed Oct 28, 2024
1 parent cc04dd7 commit c7756e0
Show file tree
Hide file tree
Showing 13 changed files with 8,259 additions and 6 deletions.
2 changes: 1 addition & 1 deletion expression/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1452,7 +1452,7 @@ func genVecBuiltinFuncBenchCase(ctx sessionctx.Context, funcName string, testCas
case types.ETJson:
fc = &castAsJSONFunctionClass{baseFunctionClass{ast.Cast, 1, 1}, tp}
case types.ETString:
fc = &castAsStringFunctionClass{baseFunctionClass{ast.Cast, 1, 1}, tp}
fc = &castAsStringFunctionClass{baseFunctionClass{ast.Cast, 1, 1}, tp, false}
}
baseFunc, err = fc.getFunction(ctx, cols)
} else if funcName == ast.GetVar {
Expand Down
29 changes: 29 additions & 0 deletions expression/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,35 @@ func newBaseBuiltinCastFunc(builtinFunc baseBuiltinFunc, inUnion bool) baseBuilt
}
}

func newBaseBuiltinCastFunc4String(ctx BuildContext, funcName string, args []Expression, tp *types.FieldType, isExplicitCharset bool) (baseBuiltinFunc, error) {
var bf baseBuiltinFunc
var err error
if isExplicitCharset {
bf = baseBuiltinFunc{
bufAllocator: newLocalColumnPool(),
childrenVectorizedOnce: new(sync.Once),

args: args,
tp: tp,
}
bf.SetCharsetAndCollation(tp.GetCharset(), tp.GetCollate())
bf.setCollator(collate.GetCollator(tp.GetCollate()))
bf.SetCoercibility(CoercibilityExplicit)
bf.SetExplicitCharset(true)
if tp.GetCharset() == charset.CharsetASCII {
bf.SetRepertoire(ASCII)
} else {
bf.SetRepertoire(UNICODE)
}
} else {
bf, err = newBaseBuiltinFunc(ctx, funcName, args, tp)
if err != nil {
return baseBuiltinFunc{}, err
}
}
return bf, nil
}

// vecBuiltinFunc contains all vectorized methods for a builtin function.
type vecBuiltinFunc interface {
// vectorized returns if this builtin function itself supports vectorized evaluation.
Expand Down
28 changes: 26 additions & 2 deletions expression/builtin_cast.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,14 +272,15 @@ func (c *castAsDecimalFunctionClass) getFunction(ctx sessionctx.Context, args []
type castAsStringFunctionClass struct {
baseFunctionClass

tp *types.FieldType
tp *types.FieldType
isExplicitCharset bool
}

func (c *castAsStringFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (sig builtinFunc, err error) {
if err := c.verifyArgs(args); err != nil {
return nil, err
}
bf, err := newBaseBuiltinFunc(ctx, c.funcName, args, c.tp)
bf, err := newBaseBuiltinCastFunc4String(ctx, c.funcName, args, c.tp, c.isExplicitCharset)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -2039,12 +2040,19 @@ func CanImplicitEvalReal(expr Expression) bool {

// BuildCastFunction4Union build a implicitly CAST ScalarFunction from the Union
// Expression.
<<<<<<< HEAD:expression/builtin_cast.go
func BuildCastFunction4Union(ctx sessionctx.Context, expr Expression, tp *types.FieldType) (res Expression) {
ctx.SetValue(inUnionCastContext, struct{}{})
defer func() {
ctx.SetValue(inUnionCastContext, nil)
}()
return BuildCastFunction(ctx, expr, tp)
=======
func BuildCastFunction4Union(ctx BuildContext, expr Expression, tp *types.FieldType) (res Expression) {
res, err := BuildCastFunctionWithCheck(ctx, expr, tp, true, false)
terror.Log(err)
return
>>>>>>> e0864c6cf1d (expression: let `cast` function supports explicit set charset (#55724)):pkg/expression/builtin_cast.go
}

// BuildCastCollationFunction builds a ScalarFunction which casts the collation.
Expand Down Expand Up @@ -2079,15 +2087,25 @@ func BuildCastCollationFunction(ctx sessionctx.Context, expr Expression, ec *Exp
}

// BuildCastFunction builds a CAST ScalarFunction from the Expression.
<<<<<<< HEAD:expression/builtin_cast.go
func BuildCastFunction(ctx sessionctx.Context, expr Expression, tp *types.FieldType) (res Expression) {
res, err := BuildCastFunctionWithCheck(ctx, expr, tp)
=======
func BuildCastFunction(ctx BuildContext, expr Expression, tp *types.FieldType) (res Expression) {
res, err := BuildCastFunctionWithCheck(ctx, expr, tp, false, false)
>>>>>>> e0864c6cf1d (expression: let `cast` function supports explicit set charset (#55724)):pkg/expression/builtin_cast.go
terror.Log(err)
return
}

// BuildCastFunctionWithCheck builds a CAST ScalarFunction from the Expression and return error if any.
<<<<<<< HEAD:expression/builtin_cast.go
func BuildCastFunctionWithCheck(ctx sessionctx.Context, expr Expression, tp *types.FieldType) (res Expression, err error) {
argType := expr.GetType()
=======
func BuildCastFunctionWithCheck(ctx BuildContext, expr Expression, tp *types.FieldType, inUnion bool, isExplicitCharset bool) (res Expression, err error) {
argType := expr.GetType(ctx.GetEvalCtx())
>>>>>>> e0864c6cf1d (expression: let `cast` function supports explicit set charset (#55724)):pkg/expression/builtin_cast.go
// If source argument's nullable, then target type should be nullable
if !mysql.HasNotNullFlag(argType.GetFlag()) {
tp.DelFlag(mysql.NotNullFlag)
Expand All @@ -2112,9 +2130,15 @@ func BuildCastFunctionWithCheck(ctx sessionctx.Context, expr Expression, tp *typ
fc = &castAsJSONFunctionClass{baseFunctionClass{ast.Cast, 1, 1}, tp}
}
case types.ETString:
<<<<<<< HEAD:expression/builtin_cast.go
fc = &castAsStringFunctionClass{baseFunctionClass{ast.Cast, 1, 1}, tp}
if expr.GetType().GetType() == mysql.TypeBit {
tp.SetFlen((expr.GetType().GetFlen() + 7) / 8)
=======
fc = &castAsStringFunctionClass{baseFunctionClass{ast.Cast, 1, 1}, tp, isExplicitCharset}
if expr.GetType(ctx.GetEvalCtx()).GetType() == mysql.TypeBit {
tp.SetFlen((expr.GetType(ctx.GetEvalCtx()).GetFlen() + 7) / 8)
>>>>>>> e0864c6cf1d (expression: let `cast` function supports explicit set charset (#55724)):pkg/expression/builtin_cast.go
}
}
f, err := fc.getFunction(ctx, []Expression{expr})
Expand Down
11 changes: 8 additions & 3 deletions expression/builtin_cast_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -646,7 +646,7 @@ func TestCastFuncSig(t *testing.T) {
tp := types.NewFieldType(mysql.TypeVarString)
tp.SetCharset(charset.CharsetBin)
args := []Expression{c.before}
stringFunc, err := newBaseBuiltinFunc(ctx, "", args, tp)
stringFunc, err := newBaseBuiltinCastFunc4String(ctx, "", args, tp, false)
require.NoError(t, err)
switch i {
case 0:
Expand Down Expand Up @@ -732,7 +732,7 @@ func TestCastFuncSig(t *testing.T) {
tp := types.NewFieldType(mysql.TypeVarString)
tp.SetFlen(c.flen)
tp.SetCharset(charset.CharsetBin)
stringFunc, err := newBaseBuiltinFunc(ctx, "", args, tp)
stringFunc, err := newBaseBuiltinCastFunc4String(ctx, "", args, tp, false)
require.NoError(t, err)
switch i {
case 0:
Expand Down Expand Up @@ -1083,7 +1083,7 @@ func TestCastFuncSig(t *testing.T) {
// null case
args := []Expression{&Column{RetType: types.NewFieldType(mysql.TypeDouble), Index: 0}}
row := chunk.MutRowFromDatums([]types.Datum{types.NewDatum(nil)})
bf, err := newBaseBuiltinFunc(ctx, "", args, types.NewFieldType(mysql.TypeVarString))
bf, err := newBaseBuiltinCastFunc4String(ctx, "", args, types.NewFieldType(mysql.TypeVarString), false)
require.NoError(t, err)
sig = &builtinCastRealAsStringSig{bf}
sRes, isNull, err := sig.evalString(row.ToRow())
Expand Down Expand Up @@ -1677,10 +1677,15 @@ func TestCastArrayFunc(t *testing.T) {
},
}
for _, tt := range tbl {
<<<<<<< HEAD:expression/builtin_cast_test.go
f, err := BuildCastFunctionWithCheck(ctx, datumsToConstants(types.MakeDatums(types.CreateBinaryJSON(tt.input)))[0], tt.tp)
if tt.buildFuncSuccess {
require.NoError(t, err, tt.input)
} else {
=======
f, err := BuildCastFunctionWithCheck(ctx, datumsToConstants(types.MakeDatums(types.CreateBinaryJSON(tt.input)))[0], tt.tp, false, false)
if !tt.buildFuncSuccess {
>>>>>>> e0864c6cf1d (expression: let `cast` function supports explicit set charset (#55724)):pkg/expression/builtin_cast_test.go
require.Error(t, err, tt.input)
continue
}
Expand Down
59 changes: 59 additions & 0 deletions expression/collation.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,46 @@ type collationInfo struct {

charset string
collation string

isExplicitCharset bool
}

<<<<<<< HEAD:expression/collation.go
=======
// Hash64 implements the base.Hasher.<0th> interface.
func (c *collationInfo) Hash64(h base.Hasher) {
h.HashInt64(int64(c.coer))
h.HashBool(c.coerInit.Load())
h.HashInt(int(c.repertoire))
h.HashString(c.charset)
h.HashString(c.collation)
h.HashBool(c.isExplicitCharset)
}

// Equals implements the base.Hasher.<1th> interface.
func (c *collationInfo) Equals(other any) bool {
// the caller should care about c is nil or not.
if other == nil {
return false
}
var c2 *collationInfo
switch x := other.(type) {
case *collationInfo:
c2 = x
case collationInfo:
c2 = &x
default:
return false
}
return c.coer == c2.coer &&
c.coerInit.Load() == c2.coerInit.Load() &&
c.repertoire == c2.repertoire &&
c.charset == c2.charset &&
c.collation == c2.collation &&
c.isExplicitCharset == c2.isExplicitCharset
}

>>>>>>> e0864c6cf1d (expression: let `cast` function supports explicit set charset (#55724)):pkg/expression/collation.go
func (c *collationInfo) HasCoercibility() bool {
return c.coerInit.Load()
}
Expand Down Expand Up @@ -76,6 +114,14 @@ func (c *collationInfo) CharsetAndCollation() (string, string) {
return c.charset, c.collation
}

func (c *collationInfo) IsExplicitCharset() bool {
return c.isExplicitCharset
}

func (c *collationInfo) SetExplicitCharset(explicit bool) {
c.isExplicitCharset = explicit
}

// CollationInfo contains all interfaces about dealing with collation.
type CollationInfo interface {
// HasCoercibility returns if the Coercibility value is initialized.
Expand All @@ -98,6 +144,12 @@ type CollationInfo interface {

// SetCharsetAndCollation sets charset and collation.
SetCharsetAndCollation(chs, coll string)

// IsExplicitCharset return the charset is explicit set or not.
IsExplicitCharset() bool

// SetExplicitCharset set the charset is explicit or not.
SetExplicitCharset(bool)
}

// Coercibility values are used to check whether the collation of one item can be coerced to
Expand Down Expand Up @@ -245,11 +297,18 @@ func deriveCollation(ctx sessionctx.Context, funcName string, args []Expression,
return &ExprCollation{args[1].Coercibility(), args[1].Repertoire(), charsetInfo, collation}, nil
case ast.Cast:
// We assume all the cast are implicit.
<<<<<<< HEAD:expression/collation.go
ec = &ExprCollation{args[0].Coercibility(), args[0].Repertoire(), args[0].GetType().GetCharset(), args[0].GetType().GetCollate()}
// Non-string type cast to string type should use @@character_set_connection and @@collation_connection.
// String type cast to string type should keep its original charset and collation. It should not happen.
if retType == types.ETString && argTps[0] != types.ETString {
ec.Charset, ec.Collation = ctx.GetSessionVars().GetCharsetInfo()
=======
ec = &ExprCollation{args[0].Coercibility(), args[0].Repertoire(), args[0].GetType(ctx.GetEvalCtx()).GetCharset(), args[0].GetType(ctx.GetEvalCtx()).GetCollate()}
// Cast to string type should use @@character_set_connection and @@collation_connection.
if retType == types.ETString {
ec.Charset, ec.Collation = ctx.GetCharsetInfo()
>>>>>>> e0864c6cf1d (expression: let `cast` function supports explicit set charset (#55724)):pkg/expression/collation.go
}
return ec, nil
case ast.Case:
Expand Down
13 changes: 13 additions & 0 deletions expression/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,20 @@ func ColumnSubstituteImpl(expr Expression, schema *Schema, newExprs []Expression
}
if substituted {
flag := v.RetType.GetFlag()
<<<<<<< HEAD:expression/util.go
e := BuildCastFunction(v.GetCtx(), newArg, v.RetType)
=======
var e Expression
var err error
if v.FuncName.L == ast.Cast {
e, err = BuildCastFunctionWithCheck(ctx, newArg, v.RetType, false, v.Function.IsExplicitCharset())
terror.Log(err)
} else {
// for grouping function recreation, use clone (meta included) instead of newFunction
e = v.Clone()
e.(*ScalarFunction).Function.getArgs()[0] = newArg
}
>>>>>>> e0864c6cf1d (expression: let `cast` function supports explicit set charset (#55724)):pkg/expression/util.go
e.SetCoercibility(v.Coercibility())
e.GetType().SetFlag(flag)
return true, false, e
Expand Down
41 changes: 41 additions & 0 deletions expression/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,7 @@ func (m *MockExpr) EvalJSON(ctx sessionctx.Context, row chunk.Row) (val types.Bi
func (m *MockExpr) ReverseEval(sc *stmtctx.StatementContext, res types.Datum, rType types.RoundingType) (val types.Datum, err error) {
return types.Datum{}, m.err
}
<<<<<<< HEAD:expression/util_test.go
func (m *MockExpr) GetType() *types.FieldType { return m.t }
func (m *MockExpr) Clone() Expression { return nil }
func (m *MockExpr) Equal(ctx sessionctx.Context, e Expression) bool { return false }
Expand All @@ -588,6 +589,46 @@ func (m *MockExpr) Coercibility() Coercibility
func (m *MockExpr) SetCoercibility(Coercibility) {}
func (m *MockExpr) Repertoire() Repertoire { return UNICODE }
func (m *MockExpr) SetRepertoire(Repertoire) {}
=======
func (m *MockExpr) GetType(_ EvalContext) *types.FieldType { return m.t }

func (m *MockExpr) Clone() Expression {
cloned := new(MockExpr)
cloned.i = m.i
cloned.err = m.err
if m.t != nil {
cloned.t = m.t.Clone()
}
return cloned
}

func (m *MockExpr) Equal(ctx EvalContext, e Expression) bool { return false }
func (m *MockExpr) IsCorrelated() bool { return false }
func (m *MockExpr) ConstLevel() ConstLevel { return ConstNone }
func (m *MockExpr) Decorrelate(schema *Schema) Expression { return m }
func (m *MockExpr) ResolveIndices(schema *Schema) (Expression, error) { return m, nil }
func (m *MockExpr) resolveIndices(schema *Schema) error { return nil }
func (m *MockExpr) ResolveIndicesByVirtualExpr(ctx EvalContext, schema *Schema) (Expression, bool) {
return m, true
}
func (m *MockExpr) resolveIndicesByVirtualExpr(ctx EvalContext, schema *Schema) bool {
return true
}
func (m *MockExpr) RemapColumn(_ map[int64]*Column) (Expression, error) { return m, nil }
func (m *MockExpr) ExplainInfo(EvalContext) string { return "" }
func (m *MockExpr) ExplainNormalizedInfo() string { return "" }
func (m *MockExpr) ExplainNormalizedInfo4InList() string { return "" }
func (m *MockExpr) HashCode() []byte { return nil }
func (m *MockExpr) CanonicalHashCode() []byte { return nil }
func (m *MockExpr) Vectorized() bool { return false }
func (m *MockExpr) HasCoercibility() bool { return false }
func (m *MockExpr) Coercibility() Coercibility { return 0 }
func (m *MockExpr) SetCoercibility(Coercibility) {}
func (m *MockExpr) Repertoire() Repertoire { return UNICODE }
func (m *MockExpr) SetRepertoire(Repertoire) {}
func (m *MockExpr) IsExplicitCharset() bool { return false }
func (m *MockExpr) SetExplicitCharset(bool) {}
>>>>>>> e0864c6cf1d (expression: let `cast` function supports explicit set charset (#55724)):pkg/expression/util_test.go

func (m *MockExpr) CharsetAndCollation() (string, string) {
return "", ""
Expand Down
Loading

0 comments on commit c7756e0

Please sign in to comment.