Files
tidb/pkg/expression/builtin_info.go

1792 lines
52 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,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Copyright 2013 The ql Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSES/QL-LICENSE file.
package expression
import (
"context"
"encoding/hex"
"encoding/json"
"slices"
"strings"
"time"
"github.com/pingcap/errors"
"github.com/pingcap/kvproto/pkg/kvrpcpb"
"github.com/pingcap/tidb/pkg/expression/expropt"
infoschema "github.com/pingcap/tidb/pkg/infoschema/context"
"github.com/pingcap/tidb/pkg/parser"
"github.com/pingcap/tidb/pkg/parser/ast"
"github.com/pingcap/tidb/pkg/parser/mysql"
"github.com/pingcap/tidb/pkg/sessionctx/variable"
"github.com/pingcap/tidb/pkg/store/helper"
"github.com/pingcap/tidb/pkg/tablecodec"
"github.com/pingcap/tidb/pkg/types"
"github.com/pingcap/tidb/pkg/util/chunk"
"github.com/pingcap/tidb/pkg/util/dbterror/plannererrors"
"github.com/pingcap/tidb/pkg/util/plancodec"
"github.com/pingcap/tidb/pkg/util/printer"
"github.com/pingcap/tipb/go-tipb"
)
var (
_ functionClass = &databaseFunctionClass{}
_ functionClass = &foundRowsFunctionClass{}
_ functionClass = &currentUserFunctionClass{}
_ functionClass = &currentRoleFunctionClass{}
_ functionClass = &currentResourceGroupFunctionClass{}
_ functionClass = &userFunctionClass{}
_ functionClass = &connectionIDFunctionClass{}
_ functionClass = &lastInsertIDFunctionClass{}
_ functionClass = &versionFunctionClass{}
_ functionClass = &benchmarkFunctionClass{}
_ functionClass = &charsetFunctionClass{}
_ functionClass = &coercibilityFunctionClass{}
_ functionClass = &collationFunctionClass{}
_ functionClass = &rowCountFunctionClass{}
_ functionClass = &tidbVersionFunctionClass{}
_ functionClass = &tidbIsDDLOwnerFunctionClass{}
_ functionClass = &tidbDecodePlanFunctionClass{}
_ functionClass = &tidbMVCCInfoFunctionClass{}
_ functionClass = &tidbEncodeRecordKeyClass{}
_ functionClass = &tidbEncodeIndexKeyClass{}
_ functionClass = &tidbDecodeKeyFunctionClass{}
_ functionClass = &tidbDecodeSQLDigestsFunctionClass{}
_ functionClass = &nextValFunctionClass{}
_ functionClass = &lastValFunctionClass{}
_ functionClass = &setValFunctionClass{}
_ functionClass = &formatBytesFunctionClass{}
_ functionClass = &formatNanoTimeFunctionClass{}
)
var (
_ builtinFunc = &builtinDatabaseSig{}
_ builtinFunc = &builtinFoundRowsSig{}
_ builtinFunc = &builtinCurrentUserSig{}
_ builtinFunc = &builtinCurrentResourceGroupSig{}
_ builtinFunc = &builtinUserSig{}
_ builtinFunc = &builtinConnectionIDSig{}
_ builtinFunc = &builtinLastInsertIDSig{}
_ builtinFunc = &builtinLastInsertIDWithIDSig{}
_ builtinFunc = &builtinVersionSig{}
_ builtinFunc = &builtinTiDBVersionSig{}
_ builtinFunc = &builtinRowCountSig{}
_ builtinFunc = &builtinTiDBMVCCInfoSig{}
_ builtinFunc = &builtinTiDBEncodeRecordKeySig{}
_ builtinFunc = &builtinTiDBEncodeIndexKeySig{}
_ builtinFunc = &builtinTiDBDecodeKeySig{}
_ builtinFunc = &builtinTiDBDecodeSQLDigestsSig{}
_ builtinFunc = &builtinNextValSig{}
_ builtinFunc = &builtinLastValSig{}
_ builtinFunc = &builtinSetValSig{}
_ builtinFunc = &builtinFormatBytesSig{}
_ builtinFunc = &builtinFormatNanoTimeSig{}
)
type databaseFunctionClass struct {
baseFunctionClass
}
func (c *databaseFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, err
}
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString)
if err != nil {
return nil, err
}
bf.tp.SetFlen(64)
sig := &builtinDatabaseSig{bf}
return sig, nil
}
type builtinDatabaseSig struct {
baseBuiltinFunc
// NOTE: Any new fields added here must be thread-safe or immutable during execution,
// as this expression may be shared across sessions.
// If a field does not meet these requirements, set SafeToShareAcrossSession to false.
}
func (b *builtinDatabaseSig) Clone() builtinFunc {
newSig := &builtinDatabaseSig{}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}
// evalString evals a builtinDatabaseSig.
// See https://dev.mysql.com/doc/refman/5.7/en/information-functions.html
func (b *builtinDatabaseSig) evalString(ctx EvalContext, row chunk.Row) (string, bool, error) {
currentDB := ctx.CurrentDB()
return currentDB, currentDB == "", nil
}
type foundRowsFunctionClass struct {
baseFunctionClass
}
func (c *foundRowsFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, err
}
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt)
if err != nil {
return nil, err
}
bf.tp.AddFlag(mysql.UnsignedFlag)
sig := &builtinFoundRowsSig{baseBuiltinFunc: bf}
return sig, nil
}
type builtinFoundRowsSig struct {
baseBuiltinFunc
expropt.SessionVarsPropReader
}
func (b *builtinFoundRowsSig) Clone() builtinFunc {
newSig := &builtinFoundRowsSig{}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}
func (b *builtinFoundRowsSig) RequiredOptionalEvalProps() OptionalEvalPropKeySet {
return b.SessionVarsPropReader.RequiredOptionalEvalProps()
}
// evalInt evals a builtinFoundRowsSig.
// See https://dev.mysql.com/doc/refman/5.7/en/information-functions.html#function_found-rows
// TODO: SQL_CALC_FOUND_ROWS and LIMIT not support for now, We will finish in another PR.
func (b *builtinFoundRowsSig) evalInt(ctx EvalContext, row chunk.Row) (int64, bool, error) {
data, err := b.GetSessionVars(ctx)
if err != nil {
return 0, true, err
}
if data == nil {
return 0, true, errors.Errorf("Missing session variable when eval builtin")
}
return int64(data.LastFoundRows), false, nil
}
type currentUserFunctionClass struct {
baseFunctionClass
}
func (c *currentUserFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, err
}
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString)
if err != nil {
return nil, err
}
bf.tp.SetFlen(64)
sig := &builtinCurrentUserSig{baseBuiltinFunc: bf}
return sig, nil
}
type builtinCurrentUserSig struct {
baseBuiltinFunc
expropt.CurrentUserPropReader
}
func (b *builtinCurrentUserSig) Clone() builtinFunc {
newSig := &builtinCurrentUserSig{}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}
// RequiredOptionalEvalProps implements the RequireOptionalEvalProps interface.
func (b *builtinCurrentUserSig) RequiredOptionalEvalProps() (set OptionalEvalPropKeySet) {
return b.CurrentUserPropReader.RequiredOptionalEvalProps()
}
// evalString evals a builtinCurrentUserSig.
// See https://dev.mysql.com/doc/refman/5.7/en/information-functions.html#function_current-user
func (b *builtinCurrentUserSig) evalString(ctx EvalContext, _ chunk.Row) (string, bool, error) {
user, err := b.CurrentUser(ctx)
if err != nil {
return "", true, err
}
if user == nil {
return "", true, errors.Errorf("Missing session variable when eval builtin")
}
return user.String(), false, nil
}
type currentRoleFunctionClass struct {
baseFunctionClass
}
func (c *currentRoleFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, err
}
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString)
if err != nil {
return nil, err
}
bf.tp.SetFlen(64)
sig := &builtinCurrentRoleSig{baseBuiltinFunc: bf}
return sig, nil
}
type builtinCurrentRoleSig struct {
baseBuiltinFunc
expropt.CurrentUserPropReader
}
func (b *builtinCurrentRoleSig) Clone() builtinFunc {
newSig := &builtinCurrentRoleSig{}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}
// RequiredOptionalEvalProps implements the RequireOptionalEvalProps interface.
func (b *builtinCurrentRoleSig) RequiredOptionalEvalProps() OptionalEvalPropKeySet {
return b.CurrentUserPropReader.RequiredOptionalEvalProps()
}
// evalString evals a builtinCurrentUserSig.
// See https://dev.mysql.com/doc/refman/8.0/en/information-functions.html#function_current-role
func (b *builtinCurrentRoleSig) evalString(ctx EvalContext, row chunk.Row) (res string, isNull bool, err error) {
roles, err := b.ActiveRoles(ctx)
if err != nil {
return "", true, err
}
if roles == nil {
return "", true, errors.Errorf("Missing session variable when eval builtin")
}
if len(roles) == 0 {
return "NONE", false, nil
}
sortedRes := make([]string, 0, 10)
for _, r := range roles {
sortedRes = append(sortedRes, r.String())
}
slices.Sort(sortedRes)
for i, r := range sortedRes {
res += r
if i != len(roles)-1 {
res += ","
}
}
return res, false, nil
}
type currentResourceGroupFunctionClass struct {
baseFunctionClass
}
func (c *currentResourceGroupFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, err
}
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString)
if err != nil {
return nil, err
}
bf.tp.SetFlen(64)
sig := &builtinCurrentResourceGroupSig{baseBuiltinFunc: bf}
return sig, nil
}
type builtinCurrentResourceGroupSig struct {
baseBuiltinFunc
expropt.SessionVarsPropReader
}
func (b *builtinCurrentResourceGroupSig) Clone() builtinFunc {
newSig := &builtinCurrentResourceGroupSig{}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}
func (b *builtinCurrentResourceGroupSig) RequiredOptionalEvalProps() OptionalEvalPropKeySet {
return b.SessionVarsPropReader.RequiredOptionalEvalProps()
}
func (b *builtinCurrentResourceGroupSig) evalString(ctx EvalContext, row chunk.Row) (val string, isNull bool, err error) {
data, err := b.GetSessionVars(ctx)
if err != nil {
return "", true, err
}
if data == nil {
return "", true, errors.Errorf("Missing session variable when eval builtin")
}
return getHintResourceGroupName(data), false, nil
}
// get statement resource group name with hint in consideration
// NOTE: because function `CURRENT_RESOURCE_GROUP()` maybe evaluated in optimizer
// before we assign the hint value to StmtCtx.ResourceGroupName, so we have to
// explicitly check the hint here.
func getHintResourceGroupName(vars *variable.SessionVars) string {
groupName := vars.StmtCtx.ResourceGroupName
if vars.StmtCtx.HasResourceGroup {
groupName = vars.StmtCtx.StmtHints.ResourceGroup
}
return groupName
}
type userFunctionClass struct {
baseFunctionClass
}
func (c *userFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, err
}
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString)
if err != nil {
return nil, err
}
bf.tp.SetFlen(64)
sig := &builtinUserSig{baseBuiltinFunc: bf}
return sig, nil
}
type builtinUserSig struct {
baseBuiltinFunc
expropt.CurrentUserPropReader
}
func (b *builtinUserSig) Clone() builtinFunc {
newSig := &builtinUserSig{}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}
// RequiredOptionalEvalProps implements the RequireOptionalEvalProps interface.
func (b *builtinUserSig) RequiredOptionalEvalProps() OptionalEvalPropKeySet {
return b.CurrentUserPropReader.RequiredOptionalEvalProps()
}
// evalString evals a builtinUserSig.
// See https://dev.mysql.com/doc/refman/5.7/en/information-functions.html#function_user
func (b *builtinUserSig) evalString(ctx EvalContext, _ chunk.Row) (string, bool, error) {
user, err := b.CurrentUser(ctx)
if err != nil {
return "", true, err
}
if user == nil {
return "", true, errors.Errorf("Missing session variable when eval builtin")
}
return user.LoginString(), false, nil
}
type connectionIDFunctionClass struct {
baseFunctionClass
}
func (c *connectionIDFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, err
}
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt)
if err != nil {
return nil, err
}
bf.tp.AddFlag(mysql.UnsignedFlag)
sig := &builtinConnectionIDSig{baseBuiltinFunc: bf}
return sig, nil
}
type builtinConnectionIDSig struct {
baseBuiltinFunc
expropt.SessionVarsPropReader
}
func (b *builtinConnectionIDSig) Clone() builtinFunc {
newSig := &builtinConnectionIDSig{}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}
func (b *builtinConnectionIDSig) RequiredOptionalEvalProps() OptionalEvalPropKeySet {
return b.SessionVarsPropReader.RequiredOptionalEvalProps()
}
func (b *builtinConnectionIDSig) evalInt(ctx EvalContext, row chunk.Row) (int64, bool, error) {
data, err := b.GetSessionVars(ctx)
if err != nil {
return 0, true, err
}
if data == nil {
return 0, true, errors.Errorf("Missing session variable `builtinConnectionIDSig.evalInt`")
}
return int64(data.ConnectionID), false, nil
}
type lastInsertIDFunctionClass struct {
baseFunctionClass
}
func (c *lastInsertIDFunctionClass) getFunction(ctx BuildContext, args []Expression) (sig builtinFunc, err error) {
if err = c.verifyArgs(args); err != nil {
return nil, err
}
var argsTp []types.EvalType
if len(args) == 1 {
argsTp = append(argsTp, types.ETInt)
}
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, argsTp...)
if err != nil {
return nil, err
}
bf.tp.AddFlag(mysql.UnsignedFlag)
if len(args) == 1 {
sig = &builtinLastInsertIDWithIDSig{baseBuiltinFunc: bf}
sig.setPbCode(tipb.ScalarFuncSig_LastInsertIDWithID)
} else {
sig = &builtinLastInsertIDSig{baseBuiltinFunc: bf}
sig.setPbCode(tipb.ScalarFuncSig_LastInsertID)
}
return sig, err
}
type builtinLastInsertIDSig struct {
baseBuiltinFunc
expropt.SessionVarsPropReader
}
func (b *builtinLastInsertIDSig) Clone() builtinFunc {
newSig := &builtinLastInsertIDSig{}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}
func (b *builtinLastInsertIDSig) RequiredOptionalEvalProps() OptionalEvalPropKeySet {
return b.SessionVarsPropReader.RequiredOptionalEvalProps()
}
// evalInt evals LAST_INSERT_ID().
// See https://dev.mysql.com/doc/refman/5.7/en/information-functions.html#function_last-insert-id.
func (b *builtinLastInsertIDSig) evalInt(ctx EvalContext, row chunk.Row) (res int64, isNull bool, err error) {
vars, err := b.GetSessionVars(ctx)
if err != nil {
return 0, true, err
}
res = int64(vars.StmtCtx.PrevLastInsertID)
return res, false, nil
}
type builtinLastInsertIDWithIDSig struct {
baseBuiltinFunc
expropt.SessionVarsPropReader
}
func (b *builtinLastInsertIDWithIDSig) Clone() builtinFunc {
newSig := &builtinLastInsertIDWithIDSig{}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}
func (b *builtinLastInsertIDWithIDSig) RequiredOptionalEvalProps() OptionalEvalPropKeySet {
return b.SessionVarsPropReader.RequiredOptionalEvalProps()
}
// evalInt evals LAST_INSERT_ID(expr).
// See https://dev.mysql.com/doc/refman/5.7/en/information-functions.html#function_last-insert-id.
func (b *builtinLastInsertIDWithIDSig) evalInt(ctx EvalContext, row chunk.Row) (res int64, isNull bool, err error) {
vars, err := b.GetSessionVars(ctx)
if err != nil {
return 0, true, err
}
res, isNull, err = b.args[0].EvalInt(ctx, row)
if isNull || err != nil {
return res, isNull, err
}
vars.SetLastInsertID(uint64(res))
return res, false, nil
}
type versionFunctionClass struct {
baseFunctionClass
}
func (c *versionFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, err
}
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString)
if err != nil {
return nil, err
}
bf.tp.SetFlen(64)
sig := &builtinVersionSig{bf}
return sig, nil
}
type builtinVersionSig struct {
baseBuiltinFunc
// NOTE: Any new fields added here must be thread-safe or immutable during execution,
// as this expression may be shared across sessions.
// If a field does not meet these requirements, set SafeToShareAcrossSession to false.
}
func (b *builtinVersionSig) Clone() builtinFunc {
newSig := &builtinVersionSig{}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}
// evalString evals a builtinVersionSig.
// See https://dev.mysql.com/doc/refman/5.7/en/information-functions.html#function_version
func (b *builtinVersionSig) evalString(ctx EvalContext, row chunk.Row) (string, bool, error) {
return mysql.ServerVersion, false, nil
}
type tidbVersionFunctionClass struct {
baseFunctionClass
}
func (c *tidbVersionFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, err
}
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString)
if err != nil {
return nil, err
}
bf.tp.SetFlen(len(printer.GetTiDBInfo()))
sig := &builtinTiDBVersionSig{bf}
return sig, nil
}
type builtinTiDBVersionSig struct {
baseBuiltinFunc
// NOTE: Any new fields added here must be thread-safe or immutable during execution,
// as this expression may be shared across sessions.
// If a field does not meet these requirements, set SafeToShareAcrossSession to false.
}
func (b *builtinTiDBVersionSig) Clone() builtinFunc {
newSig := &builtinTiDBVersionSig{}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}
// evalString evals a builtinTiDBVersionSig.
// This will show git hash and build time for tidb-server.
func (b *builtinTiDBVersionSig) evalString(ctx EvalContext, row chunk.Row) (string, bool, error) {
return printer.GetTiDBInfo(), false, nil
}
type tidbIsDDLOwnerFunctionClass struct {
baseFunctionClass
}
func (c *tidbIsDDLOwnerFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, err
}
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt)
if err != nil {
return nil, err
}
sig := &builtinTiDBIsDDLOwnerSig{baseBuiltinFunc: bf}
return sig, nil
}
type builtinTiDBIsDDLOwnerSig struct {
baseBuiltinFunc
expropt.DDLOwnerPropReader
}
func (b *builtinTiDBIsDDLOwnerSig) Clone() builtinFunc {
newSig := &builtinTiDBIsDDLOwnerSig{}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}
func (b *builtinTiDBIsDDLOwnerSig) RequiredOptionalEvalProps() OptionalEvalPropKeySet {
return b.DDLOwnerPropReader.RequiredOptionalEvalProps()
}
// evalInt evals a builtinTiDBIsDDLOwnerSig.
func (b *builtinTiDBIsDDLOwnerSig) evalInt(ctx EvalContext, row chunk.Row) (res int64, isNull bool, err error) {
isOwner, err := b.IsDDLOwner(ctx)
if err != nil {
return 0, true, err
}
if isOwner {
res = 1
}
return res, false, nil
}
type benchmarkFunctionClass struct {
baseFunctionClass
}
func (c *benchmarkFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, err
}
// Syntax: BENCHMARK(loop_count, expression)
// Define with same eval type of input arg to avoid unnecessary cast function.
sameEvalType := args[1].GetType(ctx.GetEvalCtx()).EvalType()
// constLoopCount is used by VecEvalInt
// since non-constant loop count would be different between rows, and cannot be vectorized.
var constLoopCount int64
con, ok := args[0].(*Constant)
if ok && con.Value.Kind() == types.KindInt64 {
if lc, isNull, err := con.EvalInt(ctx.GetEvalCtx(), chunk.Row{}); err == nil && !isNull {
constLoopCount = lc
}
}
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETInt, sameEvalType)
if err != nil {
return nil, err
}
sig := &builtinBenchmarkSig{bf, constLoopCount}
return sig, nil
}
type builtinBenchmarkSig struct {
baseBuiltinFunc
constLoopCount int64
}
func (b *builtinBenchmarkSig) Clone() builtinFunc {
newSig := &builtinBenchmarkSig{constLoopCount: b.constLoopCount}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}
// evalInt evals a builtinBenchmarkSig. It will execute expression repeatedly count times.
// See https://dev.mysql.com/doc/refman/5.7/en/information-functions.html#function_benchmark
func (b *builtinBenchmarkSig) evalInt(ctx EvalContext, row chunk.Row) (int64, bool, error) {
// Get loop count.
var loopCount int64
var isNull bool
var err error
if b.constLoopCount > 0 {
loopCount = b.constLoopCount
} else {
loopCount, isNull, err = b.args[0].EvalInt(ctx, row)
if isNull || err != nil {
return 0, isNull, err
}
}
// BENCHMARK() will return NULL if loop count < 0,
// behavior observed on MySQL 5.7.24.
if loopCount < 0 {
return 0, true, nil
}
// Eval loop count times based on arg type.
// BENCHMARK() will pass-through the eval error,
// behavior observed on MySQL 5.7.24.
var i int64
arg := b.args[1]
switch evalType := arg.GetType(ctx).EvalType(); evalType {
case types.ETInt:
for ; i < loopCount; i++ {
_, isNull, err = arg.EvalInt(ctx, row)
if err != nil {
return 0, isNull, err
}
}
case types.ETReal:
for ; i < loopCount; i++ {
_, isNull, err = arg.EvalReal(ctx, row)
if err != nil {
return 0, isNull, err
}
}
case types.ETDecimal:
for ; i < loopCount; i++ {
_, isNull, err = arg.EvalDecimal(ctx, row)
if err != nil {
return 0, isNull, err
}
}
case types.ETString:
for ; i < loopCount; i++ {
_, isNull, err = arg.EvalString(ctx, row)
if err != nil {
return 0, isNull, err
}
}
case types.ETDatetime, types.ETTimestamp:
for ; i < loopCount; i++ {
_, isNull, err = arg.EvalTime(ctx, row)
if err != nil {
return 0, isNull, err
}
}
case types.ETDuration:
for ; i < loopCount; i++ {
_, isNull, err = arg.EvalDuration(ctx, row)
if err != nil {
return 0, isNull, err
}
}
case types.ETJson:
for ; i < loopCount; i++ {
_, isNull, err = arg.EvalJSON(ctx, row)
if err != nil {
return 0, isNull, err
}
}
default: // Should never go into here.
return 0, true, errors.Errorf("%s is not supported for BENCHMARK()", evalType)
}
// Return value of BENCHMARK() is always 0.
return 0, false, nil
}
type charsetFunctionClass struct {
baseFunctionClass
}
func (c *charsetFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, err
}
argsTps := make([]types.EvalType, 0, len(args))
for _, arg := range args {
argsTps = append(argsTps, arg.GetType(ctx.GetEvalCtx()).EvalType())
}
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, argsTps...)
if err != nil {
return nil, err
}
charset, collate := ctx.GetCharsetInfo()
bf.tp.SetCharset(charset)
bf.tp.SetCollate(collate)
bf.tp.SetFlen(64)
sig := &builtinCharsetSig{bf}
return sig, nil
}
type builtinCharsetSig struct {
baseBuiltinFunc
// NOTE: Any new fields added here must be thread-safe or immutable during execution,
// as this expression may be shared across sessions.
// If a field does not meet these requirements, set SafeToShareAcrossSession to false.
}
func (b *builtinCharsetSig) Clone() builtinFunc {
newSig := &builtinCharsetSig{}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}
func (b *builtinCharsetSig) evalString(ctx EvalContext, row chunk.Row) (string, bool, error) {
return b.args[0].GetType(ctx).GetCharset(), false, nil
}
type coercibilityFunctionClass struct {
baseFunctionClass
}
func (c *coercibilityFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, err
}
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, args[0].GetType(ctx.GetEvalCtx()).EvalType())
if err != nil {
return nil, err
}
sig := &builtinCoercibilitySig{bf}
sig.setPbCode(tipb.ScalarFuncSig_Unspecified)
return sig, nil
}
type builtinCoercibilitySig struct {
baseBuiltinFunc
// NOTE: Any new fields added here must be thread-safe or immutable during execution,
// as this expression may be shared across sessions.
// If a field does not meet these requirements, set SafeToShareAcrossSession to false.
}
func (c *builtinCoercibilitySig) evalInt(ctx EvalContext, row chunk.Row) (val int64, isNull bool, err error) {
return int64(c.args[0].Coercibility()), false, nil
}
func (c *builtinCoercibilitySig) Clone() builtinFunc {
newSig := &builtinCoercibilitySig{}
newSig.cloneFrom(&c.baseBuiltinFunc)
return newSig
}
type collationFunctionClass struct {
baseFunctionClass
}
func (c *collationFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, err
}
argsTps := make([]types.EvalType, 0, len(args))
for _, arg := range args {
argsTps = append(argsTps, arg.GetType(ctx.GetEvalCtx()).EvalType())
}
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, argsTps...)
if err != nil {
return nil, err
}
charset, collate := ctx.GetCharsetInfo()
bf.tp.SetCharset(charset)
bf.tp.SetCollate(collate)
bf.tp.SetFlen(64)
sig := &builtinCollationSig{bf}
return sig, nil
}
type builtinCollationSig struct {
baseBuiltinFunc
// NOTE: Any new fields added here must be thread-safe or immutable during execution,
// as this expression may be shared across sessions.
// If a field does not meet these requirements, set SafeToShareAcrossSession to false.
}
func (b *builtinCollationSig) Clone() builtinFunc {
newSig := &builtinCollationSig{}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}
func (b *builtinCollationSig) evalString(ctx EvalContext, row chunk.Row) (string, bool, error) {
return b.args[0].GetType(ctx).GetCollate(), false, nil
}
type rowCountFunctionClass struct {
baseFunctionClass
}
func (c *rowCountFunctionClass) getFunction(ctx BuildContext, args []Expression) (sig builtinFunc, err error) {
if err = c.verifyArgs(args); err != nil {
return nil, err
}
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt)
if err != nil {
return nil, err
}
sig = &builtinRowCountSig{baseBuiltinFunc: bf}
sig.setPbCode(tipb.ScalarFuncSig_RowCount)
return sig, nil
}
type builtinRowCountSig struct {
baseBuiltinFunc
expropt.SessionVarsPropReader
}
func (b *builtinRowCountSig) Clone() builtinFunc {
newSig := &builtinRowCountSig{}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}
func (b *builtinRowCountSig) RequiredOptionalEvalProps() OptionalEvalPropKeySet {
return b.SessionVarsPropReader.RequiredOptionalEvalProps()
}
// evalInt evals ROW_COUNT().
// See https://dev.mysql.com/doc/refman/5.7/en/information-functions.html#function_row-count.
func (b *builtinRowCountSig) evalInt(ctx EvalContext, row chunk.Row) (res int64, isNull bool, err error) {
vars, err := b.GetSessionVars(ctx)
if err != nil {
return 0, true, err
}
res = vars.StmtCtx.PrevAffectedRows
return res, false, nil
}
type tidbMVCCInfoFunctionClass struct {
baseFunctionClass
}
func (c *tidbMVCCInfoFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, err
}
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETString)
if err != nil {
return nil, err
}
sig := &builtinTiDBMVCCInfoSig{baseBuiltinFunc: bf}
return sig, nil
}
type builtinTiDBMVCCInfoSig struct {
baseBuiltinFunc
expropt.KVStorePropReader
expropt.PrivilegeCheckerPropReader
}
// RequiredOptionalEvalProps implements the RequireOptionalEvalProps interface.
func (b *builtinTiDBMVCCInfoSig) RequiredOptionalEvalProps() OptionalEvalPropKeySet {
return b.KVStorePropReader.RequiredOptionalEvalProps() |
b.PrivilegeCheckerPropReader.RequiredOptionalEvalProps()
}
func (b *builtinTiDBMVCCInfoSig) Clone() builtinFunc {
newSig := &builtinTiDBMVCCInfoSig{}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}
// evalString evals a builtinTiDBMVCCInfoSig.
func (b *builtinTiDBMVCCInfoSig) evalString(ctx EvalContext, row chunk.Row) (string, bool, error) {
privChecker, err := b.GetPrivilegeChecker(ctx)
if err != nil {
return "", false, err
}
if !privChecker.RequestVerification("", "", "", mysql.SuperPriv) {
return "", false, plannererrors.ErrSpecificAccessDenied.FastGenByArgs("SUPER")
}
s, isNull, err := b.args[0].EvalString(ctx, row)
if isNull || err != nil {
return "", isNull, err
}
encodedKey, err := hex.DecodeString(s)
if err != nil {
return "", false, err
}
store, err := b.GetKVStore(ctx)
if err != nil {
return "", isNull, err
}
hStore, ok := store.(helper.Storage)
if !ok {
return "", isNull, errors.New("storage is not a helper.Storage")
}
h := helper.NewHelper(hStore)
resp, err := h.GetMvccByEncodedKey(encodedKey)
if err != nil {
return "", false, err
}
type mvccInfoResult struct {
Key string `json:"key"`
Resp *kvrpcpb.MvccGetByKeyResponse `json:"mvcc"`
}
mvccInfo := []*mvccInfoResult{{s, resp}}
if tablecodec.IsIndexKey(encodedKey) && !tablecodec.IsTempIndexKey(encodedKey) {
tablecodec.IndexKey2TempIndexKey(encodedKey)
hexStr := hex.EncodeToString(encodedKey)
res, err := h.GetMvccByEncodedKey(encodedKey)
if err != nil {
return "", false, err
}
if res.Info != nil && (len(res.Info.Writes) > 0 || len(res.Info.Values) > 0 || res.Info.Lock != nil) {
mvccInfo = append(mvccInfo, &mvccInfoResult{hexStr, res})
}
}
js, err := json.Marshal(mvccInfo)
if err != nil {
return "", false, err
}
return string(js), false, nil
}
type tidbEncodeRecordKeyClass struct {
baseFunctionClass
}
func (c *tidbEncodeRecordKeyClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, err
}
evalTps := make([]types.EvalType, 0, len(args))
evalTps = append(evalTps, types.ETString, types.ETString)
for _, arg := range args[2:] {
evalTps = append(evalTps, arg.GetType(ctx.GetEvalCtx()).EvalType())
}
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, evalTps...)
if err != nil {
return nil, err
}
sig := &builtinTiDBEncodeRecordKeySig{baseBuiltinFunc: bf}
return sig, nil
}
type builtinTiDBEncodeRecordKeySig struct {
baseBuiltinFunc
expropt.InfoSchemaPropReader
expropt.SessionVarsPropReader
expropt.PrivilegeCheckerPropReader
}
// RequiredOptionalEvalProps implements the RequireOptionalEvalProps interface.
func (b *builtinTiDBEncodeRecordKeySig) RequiredOptionalEvalProps() OptionalEvalPropKeySet {
return b.InfoSchemaPropReader.RequiredOptionalEvalProps() |
b.SessionVarsPropReader.RequiredOptionalEvalProps() |
b.PrivilegeCheckerPropReader.RequiredOptionalEvalProps()
}
func (b *builtinTiDBEncodeRecordKeySig) Clone() builtinFunc {
newSig := &builtinTiDBEncodeRecordKeySig{}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}
// evalString evals a builtinTiDBEncodeRecordKeySig.
func (b *builtinTiDBEncodeRecordKeySig) evalString(ctx EvalContext, row chunk.Row) (string, bool, error) {
is, err := b.GetLatestInfoSchema(ctx)
if err != nil {
return "", true, err
}
if EncodeRecordKeyFromRow == nil {
return "", false, errors.New("EncodeRecordKeyFromRow is not initialized")
}
privChecker, err := b.GetPrivilegeChecker(ctx)
if err != nil {
return "", false, err
}
recordKey, isNull, err := EncodeRecordKeyFromRow(ctx, privChecker, is, b.args, row)
if isNull || err != nil {
if errors.ErrorEqual(err, plannererrors.ErrSpecificAccessDenied) {
sv, err2 := b.GetSessionVars(ctx)
if err2 != nil {
return "", isNull, err
}
tblName, isNull, err2 := b.args[1].EvalString(ctx, row)
if err2 != nil || isNull {
return "", isNull, err
}
return "", isNull, plannererrors.ErrTableaccessDenied.FastGenByArgs("SELECT", sv.User.AuthUsername, sv.User.AuthHostname, tblName)
}
return "", isNull, err
}
return hex.EncodeToString(recordKey), false, nil
}
type tidbEncodeIndexKeyClass struct {
baseFunctionClass
}
func (c *tidbEncodeIndexKeyClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, err
}
evalTps := make([]types.EvalType, 0, len(args))
evalTps = append(evalTps, types.ETString, types.ETString, types.ETString)
for _, arg := range args[3:] {
evalTps = append(evalTps, arg.GetType(ctx.GetEvalCtx()).EvalType())
}
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, evalTps...)
if err != nil {
return nil, err
}
sig := &builtinTiDBEncodeIndexKeySig{baseBuiltinFunc: bf}
return sig, nil
}
type builtinTiDBEncodeIndexKeySig struct {
baseBuiltinFunc
expropt.InfoSchemaPropReader
expropt.SessionVarsPropReader
expropt.PrivilegeCheckerPropReader
}
// RequiredOptionalEvalProps implements the RequireOptionalEvalProps interface.
func (b *builtinTiDBEncodeIndexKeySig) RequiredOptionalEvalProps() OptionalEvalPropKeySet {
return b.InfoSchemaPropReader.RequiredOptionalEvalProps() |
b.SessionVarsPropReader.RequiredOptionalEvalProps() |
b.PrivilegeCheckerPropReader.RequiredOptionalEvalProps()
}
func (b *builtinTiDBEncodeIndexKeySig) Clone() builtinFunc {
newSig := &builtinTiDBEncodeIndexKeySig{}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}
// evalString evals a builtinTiDBEncodeIndexKeySig.
func (b *builtinTiDBEncodeIndexKeySig) evalString(ctx EvalContext, row chunk.Row) (string, bool, error) {
is, err := b.GetLatestInfoSchema(ctx)
if err != nil {
return "", true, err
}
if EncodeIndexKeyFromRow == nil {
return "", false, errors.New("EncodeIndexKeyFromRow is not initialized")
}
privChecker, err := b.GetPrivilegeChecker(ctx)
if err != nil {
return "", false, err
}
idxKey, isNull, err := EncodeIndexKeyFromRow(ctx, privChecker, is, b.args, row)
if isNull || err != nil {
if errors.ErrorEqual(err, plannererrors.ErrSpecificAccessDenied) {
sv, err2 := b.GetSessionVars(ctx)
if err2 != nil {
return "", isNull, err
}
tblName, isNull, err2 := b.args[1].EvalString(ctx, row)
if err2 != nil || isNull {
return "", isNull, err
}
return "", isNull, plannererrors.ErrTableaccessDenied.FastGenByArgs("SELECT", sv.User.AuthUsername, sv.User.AuthHostname, tblName)
}
return "", isNull, err
}
return hex.EncodeToString(idxKey), false, nil
}
type tidbDecodeKeyFunctionClass struct {
baseFunctionClass
}
func (c *tidbDecodeKeyFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, err
}
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETString)
if err != nil {
return nil, err
}
sig := &builtinTiDBDecodeKeySig{baseBuiltinFunc: bf}
return sig, nil
}
// DecodeKeyFromString is used to decode key by expressions
var DecodeKeyFromString func(types.Context, infoschema.MetaOnlyInfoSchema, string) string
// EncodeRecordKeyFromRow is used to encode record key by expressions.
var EncodeRecordKeyFromRow func(ctx EvalContext, checker expropt.PrivilegeChecker, is infoschema.MetaOnlyInfoSchema, args []Expression, row chunk.Row) ([]byte, bool, error)
// EncodeIndexKeyFromRow is used to encode index key by expressions.
var EncodeIndexKeyFromRow func(ctx EvalContext, checker expropt.PrivilegeChecker, is infoschema.MetaOnlyInfoSchema, args []Expression, row chunk.Row) ([]byte, bool, error)
type builtinTiDBDecodeKeySig struct {
baseBuiltinFunc
expropt.InfoSchemaPropReader
}
// RequiredOptionalEvalProps implements the RequireOptionalEvalProps interface.
func (b *builtinTiDBDecodeKeySig) RequiredOptionalEvalProps() OptionalEvalPropKeySet {
return b.InfoSchemaPropReader.RequiredOptionalEvalProps()
}
func (b *builtinTiDBDecodeKeySig) Clone() builtinFunc {
newSig := &builtinTiDBDecodeKeySig{}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}
// evalInt evals a builtinTiDBDecodeKeySig.
func (b *builtinTiDBDecodeKeySig) evalString(ctx EvalContext, row chunk.Row) (string, bool, error) {
s, isNull, err := b.args[0].EvalString(ctx, row)
if isNull || err != nil {
return "", isNull, err
}
is, err := b.GetLatestInfoSchema(ctx)
if err != nil {
return "", true, err
}
if fn := DecodeKeyFromString; fn != nil {
s = fn(ctx.TypeCtx(), is, s)
}
return s, false, nil
}
type tidbDecodeSQLDigestsFunctionClass struct {
baseFunctionClass
expropt.PrivilegeCheckerPropReader
}
func (c *tidbDecodeSQLDigestsFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, err
}
privChecker, err := c.GetPrivilegeChecker(ctx.GetEvalCtx())
if err != nil {
return nil, err
}
if !privChecker.RequestVerification("", "", "", mysql.ProcessPriv) {
return nil, errSpecificAccessDenied.GenWithStackByArgs("PROCESS")
}
var argTps []types.EvalType
if len(args) > 1 {
argTps = []types.EvalType{types.ETString, types.ETInt}
} else {
argTps = []types.EvalType{types.ETString}
}
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, argTps...)
if err != nil {
return nil, err
}
sig := &builtinTiDBDecodeSQLDigestsSig{baseBuiltinFunc: bf}
return sig, nil
}
type builtinTiDBDecodeSQLDigestsSig struct {
baseBuiltinFunc
expropt.SessionVarsPropReader
expropt.SQLExecutorPropReader
expropt.PrivilegeCheckerPropReader
}
// RequiredOptionalEvalProps implements the RequireOptionalEvalProps interface.
func (b *builtinTiDBDecodeSQLDigestsSig) RequiredOptionalEvalProps() OptionalEvalPropKeySet {
return b.SessionVarsPropReader.RequiredOptionalEvalProps() |
b.SQLExecutorPropReader.RequiredOptionalEvalProps() |
b.PrivilegeCheckerPropReader.RequiredOptionalEvalProps()
}
func (b *builtinTiDBDecodeSQLDigestsSig) Clone() builtinFunc {
newSig := &builtinTiDBDecodeSQLDigestsSig{}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}
func (b *builtinTiDBDecodeSQLDigestsSig) evalString(ctx EvalContext, row chunk.Row) (string, bool, error) {
privChecker, err := b.GetPrivilegeChecker(ctx)
if err != nil {
return "", true, err
}
if !privChecker.RequestVerification("", "", "", mysql.ProcessPriv) {
return "", true, errSpecificAccessDenied.GenWithStackByArgs("PROCESS")
}
args := b.getArgs()
digestsStr, isNull, err := args[0].EvalString(ctx, row)
if err != nil {
return "", true, err
}
if isNull {
return "", true, nil
}
stmtTruncateLength := int64(0)
if len(args) > 1 {
stmtTruncateLength, isNull, err = args[1].EvalInt(ctx, row)
if err != nil {
return "", true, err
}
if isNull {
stmtTruncateLength = 0
}
}
var digests []any
err = json.Unmarshal([]byte(digestsStr), &digests)
if err != nil {
const errMsgMaxLength = 32
if len(digestsStr) > errMsgMaxLength {
digestsStr = digestsStr[:errMsgMaxLength] + "..."
}
tc := typeCtx(ctx)
tc.AppendWarning(errIncorrectArgs.FastGen("The argument can't be unmarshalled as JSON array: '%s'", digestsStr))
return "", true, nil
}
// Query the SQL Statements by digests.
retriever := NewSQLDigestTextRetriever()
for _, item := range digests {
if item != nil {
digest, ok := item.(string)
if ok {
retriever.SQLDigestsMap[digest] = ""
}
}
}
vars, err := b.GetSessionVars(ctx)
if err != nil {
return "", true, err
}
// Querying may take some time and it takes a context.Context as argument, which is not available here.
// We simply create a context with a timeout here.
timeout := time.Duration(vars.GetMaxExecutionTime()) * time.Millisecond
if timeout == 0 || timeout > 20*time.Second {
timeout = 20 * time.Second
}
goCtx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
exec, err := b.GetSQLExecutor(ctx)
if err != nil {
return "", true, err
}
err = retriever.RetrieveGlobal(goCtx, exec)
if err != nil {
if errors.Cause(err) == context.DeadlineExceeded || errors.Cause(err) == context.Canceled {
return "", true, errUnknown.GenWithStack("Retrieving cancelled internally with error: %v", err)
}
tc := typeCtx(ctx)
tc.AppendWarning(errUnknown.FastGen("Retrieving statements information failed with error: %v", err))
return "", true, nil
}
// Collect the result.
result := make([]any, len(digests))
for i, item := range digests {
if item == nil {
continue
}
if digest, ok := item.(string); ok {
if stmt, ok := retriever.SQLDigestsMap[digest]; ok && len(stmt) > 0 {
// Truncate too-long statements if necessary.
if stmtTruncateLength > 0 && int64(len(stmt)) > stmtTruncateLength {
stmt = stmt[:stmtTruncateLength] + "..."
}
result[i] = stmt
}
}
}
resultStr, err := json.Marshal(result)
if err != nil {
tc := typeCtx(ctx)
tc.AppendWarning(errUnknown.FastGen("Marshalling result as JSON failed with error: %v", err))
return "", true, nil
}
return string(resultStr), false, nil
}
type tidbEncodeSQLDigestFunctionClass struct {
baseFunctionClass
}
func (c *tidbEncodeSQLDigestFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, err
}
argTps := []types.EvalType{types.ETString}
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, argTps...)
if err != nil {
return nil, err
}
sig := &builtinTiDBEncodeSQLDigestSig{bf}
return sig, nil
}
type builtinTiDBEncodeSQLDigestSig struct {
baseBuiltinFunc
// NOTE: Any new fields added here must be thread-safe or immutable during execution,
// as this expression may be shared across sessions.
// If a field does not meet these requirements, set SafeToShareAcrossSession to false.
}
func (b *builtinTiDBEncodeSQLDigestSig) Clone() builtinFunc {
newSig := &builtinTiDBEncodeSQLDigestSig{}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}
func (b *builtinTiDBEncodeSQLDigestSig) evalString(ctx EvalContext, row chunk.Row) (string, bool, error) {
orgSQLStr, isNull, err := b.getArgs()[0].EvalString(ctx, row)
if err != nil {
return "", true, err
}
if isNull {
return "", true, nil
}
return parser.DigestHash(orgSQLStr).String(), false, nil
}
type tidbDecodePlanFunctionClass struct {
baseFunctionClass
}
func (c *tidbDecodePlanFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, err
}
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETString)
if err != nil {
return nil, err
}
if c.funcName == ast.TiDBDecodePlan {
return &builtinTiDBDecodePlanSig{bf}, nil
} else if c.funcName == ast.TiDBDecodeBinaryPlan {
return &builtinTiDBDecodeBinaryPlanSig{bf}, nil
}
return nil, errors.New("unknown decode plan function")
}
type builtinTiDBDecodePlanSig struct {
baseBuiltinFunc
// NOTE: Any new fields added here must be thread-safe or immutable during execution,
// as this expression may be shared across sessions.
// If a field does not meet these requirements, set SafeToShareAcrossSession to false.
}
func (b *builtinTiDBDecodePlanSig) Clone() builtinFunc {
newSig := &builtinTiDBDecodePlanSig{}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}
func (b *builtinTiDBDecodePlanSig) evalString(ctx EvalContext, row chunk.Row) (string, bool, error) {
planString, isNull, err := b.args[0].EvalString(ctx, row)
if isNull || err != nil {
return "", isNull, err
}
planTree, err := plancodec.DecodePlan(planString)
if err != nil {
return planString, false, nil
}
return planTree, false, nil
}
type builtinTiDBDecodeBinaryPlanSig struct {
baseBuiltinFunc
// NOTE: Any new fields added here must be thread-safe or immutable during execution,
// as this expression may be shared across sessions.
// If a field does not meet these requirements, set SafeToShareAcrossSession to false.
}
func (b *builtinTiDBDecodeBinaryPlanSig) Clone() builtinFunc {
newSig := &builtinTiDBDecodeBinaryPlanSig{}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}
func (b *builtinTiDBDecodeBinaryPlanSig) evalString(ctx EvalContext, row chunk.Row) (string, bool, error) {
planString, isNull, err := b.args[0].EvalString(ctx, row)
if isNull || err != nil {
return "", isNull, err
}
planTree, err := plancodec.DecodeBinaryPlan(planString)
if err != nil {
tc := typeCtx(ctx)
tc.AppendWarning(err)
return "", false, nil
}
return planTree, false, nil
}
type nextValFunctionClass struct {
baseFunctionClass
}
func (c *nextValFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, err
}
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETString)
if err != nil {
return nil, err
}
sig := &builtinNextValSig{baseBuiltinFunc: bf}
bf.tp.SetFlen(10)
return sig, nil
}
type builtinNextValSig struct {
baseBuiltinFunc
expropt.SequenceOperatorPropReader
expropt.SessionVarsPropReader
expropt.PrivilegeCheckerPropReader
}
func (b *builtinNextValSig) RequiredOptionalEvalProps() OptionalEvalPropKeySet {
return b.SequenceOperatorPropReader.RequiredOptionalEvalProps() |
b.SessionVarsPropReader.RequiredOptionalEvalProps() |
b.PrivilegeCheckerPropReader.RequiredOptionalEvalProps()
}
func (b *builtinNextValSig) Clone() builtinFunc {
newSig := &builtinNextValSig{}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}
func (b *builtinNextValSig) evalInt(ctx EvalContext, row chunk.Row) (int64, bool, error) {
sequenceName, isNull, err := b.args[0].EvalString(ctx, row)
if isNull || err != nil {
return 0, isNull, err
}
db, seq := getSchemaAndSequence(sequenceName)
if len(db) == 0 {
db = ctx.CurrentDB()
}
// Check the tableName valid.
sequence, err := b.GetSequenceOperator(ctx, db, seq)
if err != nil {
return 0, false, err
}
// Get session vars
vars, err := b.GetSessionVars(ctx)
if err != nil {
return 0, false, err
}
// Do the privilege check.
user := vars.User
privChecker, err := b.GetPrivilegeChecker(ctx)
if err != nil {
return 0, false, err
}
if !privChecker.RequestVerification(db, seq, "", mysql.InsertPriv) {
return 0, false, errSequenceAccessDenied.GenWithStackByArgs("INSERT", user.AuthUsername, user.AuthHostname, seq)
}
nextVal, err := sequence.GetSequenceNextVal()
if err != nil {
return 0, false, err
}
// update the sequenceState.
vars.SequenceState.UpdateState(sequence.GetSequenceID(), nextVal)
return nextVal, false, nil
}
type lastValFunctionClass struct {
baseFunctionClass
}
func (c *lastValFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, err
}
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETString)
if err != nil {
return nil, err
}
sig := &builtinLastValSig{baseBuiltinFunc: bf}
bf.tp.SetFlen(10)
return sig, nil
}
type builtinLastValSig struct {
baseBuiltinFunc
expropt.SequenceOperatorPropReader
expropt.SessionVarsPropReader
expropt.PrivilegeCheckerPropReader
}
func (b *builtinLastValSig) RequiredOptionalEvalProps() OptionalEvalPropKeySet {
return b.SequenceOperatorPropReader.RequiredOptionalEvalProps() |
b.SessionVarsPropReader.RequiredOptionalEvalProps() |
b.PrivilegeCheckerPropReader.RequiredOptionalEvalProps()
}
func (b *builtinLastValSig) Clone() builtinFunc {
newSig := &builtinLastValSig{}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}
func (b *builtinLastValSig) evalInt(ctx EvalContext, row chunk.Row) (int64, bool, error) {
sequenceName, isNull, err := b.args[0].EvalString(ctx, row)
if isNull || err != nil {
return 0, isNull, err
}
db, seq := getSchemaAndSequence(sequenceName)
if len(db) == 0 {
db = ctx.CurrentDB()
}
// Check the tableName valid.
sequence, err := b.GetSequenceOperator(ctx, db, seq)
if err != nil {
return 0, false, err
}
// Get session vars
vars, err := b.GetSessionVars(ctx)
if err != nil {
return 0, false, err
}
// Do the privilege check.
user := vars.User
privChecker, err := b.GetPrivilegeChecker(ctx)
if err != nil {
return 0, false, err
}
if !privChecker.RequestVerification(db, seq, "", mysql.SelectPriv) {
return 0, false, errSequenceAccessDenied.GenWithStackByArgs("SELECT", user.AuthUsername, user.AuthHostname, seq)
}
return vars.SequenceState.GetLastValue(sequence.GetSequenceID())
}
type setValFunctionClass struct {
baseFunctionClass
}
func (c *setValFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, err
}
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETString, types.ETInt)
if err != nil {
return nil, err
}
sig := &builtinSetValSig{baseBuiltinFunc: bf}
bf.tp.SetFlen(args[1].GetType(ctx.GetEvalCtx()).GetFlen())
return sig, nil
}
type builtinSetValSig struct {
baseBuiltinFunc
expropt.SequenceOperatorPropReader
expropt.SessionVarsPropReader
expropt.PrivilegeCheckerPropReader
}
func (b *builtinSetValSig) RequiredOptionalEvalProps() OptionalEvalPropKeySet {
return b.SequenceOperatorPropReader.RequiredOptionalEvalProps() |
b.SessionVarsPropReader.RequiredOptionalEvalProps() |
b.PrivilegeCheckerPropReader.RequiredOptionalEvalProps()
}
func (b *builtinSetValSig) Clone() builtinFunc {
newSig := &builtinSetValSig{}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}
func (b *builtinSetValSig) evalInt(ctx EvalContext, row chunk.Row) (int64, bool, error) {
sequenceName, isNull, err := b.args[0].EvalString(ctx, row)
if isNull || err != nil {
return 0, isNull, err
}
db, seq := getSchemaAndSequence(sequenceName)
if len(db) == 0 {
db = ctx.CurrentDB()
}
// Check the tableName valid.
sequence, err := b.GetSequenceOperator(ctx, db, seq)
if err != nil {
return 0, false, err
}
// Get session vars
vars, err := b.GetSessionVars(ctx)
if err != nil {
return 0, false, err
}
// Do the privilege check.
user := vars.User
privChecker, err := b.GetPrivilegeChecker(ctx)
if err != nil {
return 0, false, err
}
if !privChecker.RequestVerification(db, seq, "", mysql.InsertPriv) {
return 0, false, errSequenceAccessDenied.GenWithStackByArgs("INSERT", user.AuthUsername, user.AuthHostname, seq)
}
setValue, isNull, err := b.args[1].EvalInt(ctx, row)
if isNull || err != nil {
return 0, isNull, err
}
return sequence.SetSequenceVal(setValue)
}
func getSchemaAndSequence(sequenceName string) (string, string) {
res := strings.Split(sequenceName, ".")
if len(res) == 1 {
return "", res[0]
}
return res[0], res[1]
}
type formatBytesFunctionClass struct {
baseFunctionClass
}
func (c *formatBytesFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, err
}
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETReal)
if err != nil {
return nil, err
}
charset, collate := ctx.GetCharsetInfo()
bf.tp.SetCharset(charset)
bf.tp.SetCollate(collate)
sig := &builtinFormatBytesSig{bf}
return sig, nil
}
type builtinFormatBytesSig struct {
baseBuiltinFunc
// NOTE: Any new fields added here must be thread-safe or immutable during execution,
// as this expression may be shared across sessions.
// If a field does not meet these requirements, set SafeToShareAcrossSession to false.
}
func (b *builtinFormatBytesSig) Clone() builtinFunc {
newSig := &builtinFormatBytesSig{}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}
// formatBytes evals a builtinFormatBytesSig.
// See https://dev.mysql.com/doc/refman/8.0/en/performance-schema-functions.html#function_format-bytes
func (b *builtinFormatBytesSig) evalString(ctx EvalContext, row chunk.Row) (string, bool, error) {
val, isNull, err := b.args[0].EvalReal(ctx, row)
if isNull || err != nil {
return "", isNull, err
}
return GetFormatBytes(val), false, nil
}
type formatNanoTimeFunctionClass struct {
baseFunctionClass
}
func (c *formatNanoTimeFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, err
}
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETReal)
if err != nil {
return nil, err
}
charset, collate := ctx.GetCharsetInfo()
bf.tp.SetCharset(charset)
bf.tp.SetCollate(collate)
sig := &builtinFormatNanoTimeSig{bf}
return sig, nil
}
type builtinFormatNanoTimeSig struct {
baseBuiltinFunc
// NOTE: Any new fields added here must be thread-safe or immutable during execution,
// as this expression may be shared across sessions.
// If a field does not meet these requirements, set SafeToShareAcrossSession to false.
}
func (b *builtinFormatNanoTimeSig) Clone() builtinFunc {
newSig := &builtinFormatNanoTimeSig{}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}
// formatNanoTime evals a builtinFormatNanoTimeSig, as time unit in TiDB is always nanosecond, not picosecond.
// See https://dev.mysql.com/doc/refman/8.0/en/performance-schema-functions.html#function_format-pico-time
func (b *builtinFormatNanoTimeSig) evalString(ctx EvalContext, row chunk.Row) (string, bool, error) {
val, isNull, err := b.args[0].EvalReal(ctx, row)
if isNull || err != nil {
return "", isNull, err
}
return GetFormatNanoTime(val), false, nil
}