You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1067 lines
30 KiB
1067 lines
30 KiB
// 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.
|
|
|
|
// 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 (
|
|
"encoding/hex"
|
|
"fmt"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/pingcap/errors"
|
|
"github.com/pingcap/parser/model"
|
|
"github.com/pingcap/parser/mysql"
|
|
"github.com/pingcap/tidb/privilege"
|
|
"github.com/pingcap/tidb/sessionctx"
|
|
"github.com/pingcap/tidb/tablecodec"
|
|
"github.com/pingcap/tidb/types"
|
|
"github.com/pingcap/tidb/util"
|
|
"github.com/pingcap/tidb/util/chunk"
|
|
"github.com/pingcap/tidb/util/codec"
|
|
"github.com/pingcap/tidb/util/plancodec"
|
|
"github.com/pingcap/tidb/util/printer"
|
|
"github.com/pingcap/tipb/go-tipb"
|
|
)
|
|
|
|
var (
|
|
_ functionClass = &databaseFunctionClass{}
|
|
_ functionClass = &foundRowsFunctionClass{}
|
|
_ functionClass = ¤tUserFunctionClass{}
|
|
_ functionClass = ¤tRoleFunctionClass{}
|
|
_ 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 = &tidbDecodeKeyFunctionClass{}
|
|
_ functionClass = &nextValFunctionClass{}
|
|
_ functionClass = &lastValFunctionClass{}
|
|
_ functionClass = &setValFunctionClass{}
|
|
_ functionClass = &formatBytesFunctionClass{}
|
|
_ functionClass = &formatNanoTimeFunctionClass{}
|
|
)
|
|
|
|
var (
|
|
_ builtinFunc = &builtinDatabaseSig{}
|
|
_ builtinFunc = &builtinFoundRowsSig{}
|
|
_ builtinFunc = &builtinCurrentUserSig{}
|
|
_ builtinFunc = &builtinUserSig{}
|
|
_ builtinFunc = &builtinConnectionIDSig{}
|
|
_ builtinFunc = &builtinLastInsertIDSig{}
|
|
_ builtinFunc = &builtinLastInsertIDWithIDSig{}
|
|
_ builtinFunc = &builtinVersionSig{}
|
|
_ builtinFunc = &builtinTiDBVersionSig{}
|
|
_ builtinFunc = &builtinRowCountSig{}
|
|
_ builtinFunc = &builtinTiDBDecodeKeySig{}
|
|
_ builtinFunc = &builtinNextValSig{}
|
|
_ builtinFunc = &builtinLastValSig{}
|
|
_ builtinFunc = &builtinSetValSig{}
|
|
_ builtinFunc = &builtinFormatBytesSig{}
|
|
_ builtinFunc = &builtinFormatNanoTimeSig{}
|
|
)
|
|
|
|
type databaseFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *databaseFunctionClass) getFunction(ctx sessionctx.Context, 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.Charset, bf.tp.Collate = ctx.GetSessionVars().GetCharsetInfo()
|
|
bf.tp.Flen = 64
|
|
sig := &builtinDatabaseSig{bf}
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinDatabaseSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
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(row chunk.Row) (string, bool, error) {
|
|
currentDB := b.ctx.GetSessionVars().CurrentDB
|
|
return currentDB, currentDB == "", nil
|
|
}
|
|
|
|
type foundRowsFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *foundRowsFunctionClass) getFunction(ctx sessionctx.Context, 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.Flag |= mysql.UnsignedFlag
|
|
sig := &builtinFoundRowsSig{bf}
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinFoundRowsSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinFoundRowsSig) Clone() builtinFunc {
|
|
newSig := &builtinFoundRowsSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// 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(row chunk.Row) (int64, bool, error) {
|
|
data := b.ctx.GetSessionVars()
|
|
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 sessionctx.Context, 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.Charset, bf.tp.Collate = ctx.GetSessionVars().GetCharsetInfo()
|
|
bf.tp.Flen = 64
|
|
sig := &builtinCurrentUserSig{bf}
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinCurrentUserSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinCurrentUserSig) Clone() builtinFunc {
|
|
newSig := &builtinCurrentUserSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalString evals a builtinCurrentUserSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/information-functions.html#function_current-user
|
|
func (b *builtinCurrentUserSig) evalString(row chunk.Row) (string, bool, error) {
|
|
data := b.ctx.GetSessionVars()
|
|
if data == nil || data.User == nil {
|
|
return "", true, errors.Errorf("Missing session variable when eval builtin")
|
|
}
|
|
return data.User.AuthIdentityString(), false, nil
|
|
}
|
|
|
|
type currentRoleFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *currentRoleFunctionClass) getFunction(ctx sessionctx.Context, 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.Charset, bf.tp.Collate = ctx.GetSessionVars().GetCharsetInfo()
|
|
bf.tp.Flen = 64
|
|
sig := &builtinCurrentRoleSig{bf}
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinCurrentRoleSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinCurrentRoleSig) Clone() builtinFunc {
|
|
newSig := &builtinCurrentRoleSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalString evals a builtinCurrentUserSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/information-functions.html#function_current-user
|
|
func (b *builtinCurrentRoleSig) evalString(row chunk.Row) (string, bool, error) {
|
|
data := b.ctx.GetSessionVars()
|
|
if data == nil || data.ActiveRoles == nil {
|
|
return "", true, errors.Errorf("Missing session variable when eval builtin")
|
|
}
|
|
if len(data.ActiveRoles) == 0 {
|
|
return "", false, nil
|
|
}
|
|
res := ""
|
|
sortedRes := make([]string, 0, 10)
|
|
for _, r := range data.ActiveRoles {
|
|
sortedRes = append(sortedRes, r.String())
|
|
}
|
|
sort.Strings(sortedRes)
|
|
for i, r := range sortedRes {
|
|
res += r
|
|
if i != len(data.ActiveRoles)-1 {
|
|
res += ","
|
|
}
|
|
}
|
|
return res, false, nil
|
|
}
|
|
|
|
type userFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *userFunctionClass) getFunction(ctx sessionctx.Context, 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.Charset, bf.tp.Collate = ctx.GetSessionVars().GetCharsetInfo()
|
|
bf.tp.Flen = 64
|
|
sig := &builtinUserSig{bf}
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinUserSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinUserSig) Clone() builtinFunc {
|
|
newSig := &builtinUserSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalString evals a builtinUserSig.
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/information-functions.html#function_user
|
|
func (b *builtinUserSig) evalString(row chunk.Row) (string, bool, error) {
|
|
data := b.ctx.GetSessionVars()
|
|
if data == nil || data.User == nil {
|
|
return "", true, errors.Errorf("Missing session variable when eval builtin")
|
|
}
|
|
|
|
return data.User.String(), false, nil
|
|
}
|
|
|
|
type connectionIDFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *connectionIDFunctionClass) getFunction(ctx sessionctx.Context, 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.Flag |= mysql.UnsignedFlag
|
|
sig := &builtinConnectionIDSig{bf}
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinConnectionIDSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinConnectionIDSig) Clone() builtinFunc {
|
|
newSig := &builtinConnectionIDSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
func (b *builtinConnectionIDSig) evalInt(_ chunk.Row) (int64, bool, error) {
|
|
data := b.ctx.GetSessionVars()
|
|
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 sessionctx.Context, 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.Flag |= mysql.UnsignedFlag
|
|
|
|
if len(args) == 1 {
|
|
sig = &builtinLastInsertIDWithIDSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_LastInsertIDWithID)
|
|
} else {
|
|
sig = &builtinLastInsertIDSig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_LastInsertID)
|
|
}
|
|
return sig, err
|
|
}
|
|
|
|
type builtinLastInsertIDSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinLastInsertIDSig) Clone() builtinFunc {
|
|
newSig := &builtinLastInsertIDSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// 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(row chunk.Row) (res int64, isNull bool, err error) {
|
|
res = int64(b.ctx.GetSessionVars().StmtCtx.PrevLastInsertID)
|
|
return res, false, nil
|
|
}
|
|
|
|
type builtinLastInsertIDWithIDSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinLastInsertIDWithIDSig) Clone() builtinFunc {
|
|
newSig := &builtinLastInsertIDWithIDSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// 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(row chunk.Row) (res int64, isNull bool, err error) {
|
|
res, isNull, err = b.args[0].EvalInt(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return res, isNull, err
|
|
}
|
|
|
|
b.ctx.GetSessionVars().SetLastInsertID(uint64(res))
|
|
return res, false, nil
|
|
}
|
|
|
|
type versionFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *versionFunctionClass) getFunction(ctx sessionctx.Context, 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.Charset, bf.tp.Collate = ctx.GetSessionVars().GetCharsetInfo()
|
|
bf.tp.Flen = 64
|
|
sig := &builtinVersionSig{bf}
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinVersionSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
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(row chunk.Row) (string, bool, error) {
|
|
return mysql.ServerVersion, false, nil
|
|
}
|
|
|
|
type tidbVersionFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *tidbVersionFunctionClass) getFunction(ctx sessionctx.Context, 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.Charset, bf.tp.Collate = ctx.GetSessionVars().GetCharsetInfo()
|
|
bf.tp.Flen = len(printer.GetTiDBInfo())
|
|
sig := &builtinTiDBVersionSig{bf}
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinTiDBVersionSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
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(_ chunk.Row) (string, bool, error) {
|
|
return printer.GetTiDBInfo(), false, nil
|
|
}
|
|
|
|
type tidbIsDDLOwnerFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *tidbIsDDLOwnerFunctionClass) getFunction(ctx sessionctx.Context, 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{bf}
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinTiDBIsDDLOwnerSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinTiDBIsDDLOwnerSig) Clone() builtinFunc {
|
|
newSig := &builtinTiDBIsDDLOwnerSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalInt evals a builtinTiDBIsDDLOwnerSig.
|
|
func (b *builtinTiDBIsDDLOwnerSig) evalInt(_ chunk.Row) (res int64, isNull bool, err error) {
|
|
ddlOwnerChecker := b.ctx.DDLOwnerChecker()
|
|
if ddlOwnerChecker.IsOwner() {
|
|
res = 1
|
|
}
|
|
|
|
return res, false, nil
|
|
}
|
|
|
|
type benchmarkFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *benchmarkFunctionClass) getFunction(ctx sessionctx.Context, 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().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, 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(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(b.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, ctx := b.args[1], b.ctx
|
|
switch evalType := arg.GetType().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("EvalType %v not implemented for builtin BENCHMARK()", evalType)
|
|
}
|
|
|
|
// Return value of BENCHMARK() is always 0.
|
|
return 0, false, nil
|
|
}
|
|
|
|
type charsetFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *charsetFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
|
|
return nil, errFunctionNotExists.GenWithStackByArgs("FUNCTION", "CHARSET")
|
|
}
|
|
|
|
type coercibilityFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *coercibilityFunctionClass) getFunction(ctx sessionctx.Context, 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().EvalType())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sig := &builtinCoercibilitySig{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_Unspecified)
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinCoercibilitySig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (c *builtinCoercibilitySig) evalInt(_ chunk.Row) (res 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 sessionctx.Context, 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().EvalType())
|
|
}
|
|
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, argsTps...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bf.tp.Charset, bf.tp.Collate = ctx.GetSessionVars().GetCharsetInfo()
|
|
sig := &builtinCollationSig{bf}
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinCollationSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinCollationSig) Clone() builtinFunc {
|
|
newSig := &builtinCollationSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
func (b *builtinCollationSig) evalString(_ chunk.Row) (string, bool, error) {
|
|
return b.args[0].GetType().Collate, false, nil
|
|
}
|
|
|
|
type rowCountFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *rowCountFunctionClass) getFunction(ctx sessionctx.Context, 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{bf}
|
|
sig.setPbCode(tipb.ScalarFuncSig_RowCount)
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinRowCountSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinRowCountSig) Clone() builtinFunc {
|
|
newSig := &builtinRowCountSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalInt evals ROW_COUNT().
|
|
// See https://dev.mysql.com/doc/refman/5.7/en/information-functions.html#function_row-count.
|
|
func (b *builtinRowCountSig) evalInt(_ chunk.Row) (res int64, isNull bool, err error) {
|
|
res = int64(b.ctx.GetSessionVars().StmtCtx.PrevAffectedRows)
|
|
return res, false, nil
|
|
}
|
|
|
|
type tidbDecodeKeyFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *tidbDecodeKeyFunctionClass) getFunction(ctx sessionctx.Context, 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{bf}
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinTiDBDecodeKeySig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinTiDBDecodeKeySig) Clone() builtinFunc {
|
|
newSig := &builtinTiDBDecodeKeySig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
// evalInt evals a builtinTiDBIsDDLOwnerSig.
|
|
func (b *builtinTiDBDecodeKeySig) evalString(row chunk.Row) (string, bool, error) {
|
|
s, isNull, err := b.args[0].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return "", isNull, err
|
|
}
|
|
return decodeKey(b.ctx, s), false, nil
|
|
}
|
|
|
|
func decodeKey(ctx sessionctx.Context, s string) string {
|
|
key, err := hex.DecodeString(s)
|
|
if err != nil {
|
|
ctx.GetSessionVars().StmtCtx.AppendWarning(errors.Errorf("invalid record/index key: %X", key))
|
|
return s
|
|
}
|
|
// Auto decode byte if needed.
|
|
_, bs, err := codec.DecodeBytes([]byte(key), nil)
|
|
if err == nil {
|
|
key = bs
|
|
}
|
|
// Try to decode it as a record key.
|
|
tableID, handle, err := tablecodec.DecodeRecordKey(key)
|
|
if err == nil {
|
|
return "tableID=" + strconv.FormatInt(tableID, 10) + ", _tidb_rowid=" + strconv.FormatInt(handle, 10)
|
|
}
|
|
// Try decode as table index key.
|
|
tableID, indexID, indexValues, err := tablecodec.DecodeIndexKeyPrefix(key)
|
|
if err == nil {
|
|
idxValueStr := fmt.Sprintf("%X", indexValues)
|
|
return "tableID=" + strconv.FormatInt(tableID, 10) + ", indexID=" + strconv.FormatInt(indexID, 10) + ", indexValues=" + idxValueStr
|
|
}
|
|
|
|
// TODO: try to decode other type key.
|
|
ctx.GetSessionVars().StmtCtx.AppendWarning(errors.Errorf("invalid record/index key: %X", key))
|
|
return s
|
|
}
|
|
|
|
type tidbDecodePlanFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *tidbDecodePlanFunctionClass) getFunction(ctx sessionctx.Context, 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 := &builtinTiDBDecodePlanSig{bf}
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinTiDBDecodePlanSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinTiDBDecodePlanSig) Clone() builtinFunc {
|
|
newSig := &builtinTiDBDecodePlanSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
func (b *builtinTiDBDecodePlanSig) evalString(row chunk.Row) (string, bool, error) {
|
|
planString, isNull, err := b.args[0].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return "", isNull, err
|
|
}
|
|
planTree, err := plancodec.DecodePlan(planString)
|
|
return planTree, false, err
|
|
}
|
|
|
|
type nextValFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *nextValFunctionClass) getFunction(ctx sessionctx.Context, 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{bf}
|
|
bf.tp.Flen = 10
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinNextValSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinNextValSig) Clone() builtinFunc {
|
|
newSig := &builtinNextValSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
func (b *builtinNextValSig) evalInt(row chunk.Row) (int64, bool, error) {
|
|
sequenceName, isNull, err := b.args[0].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return 0, isNull, err
|
|
}
|
|
db, seq := getSchemaAndSequence(sequenceName)
|
|
if len(db) == 0 {
|
|
db = b.ctx.GetSessionVars().CurrentDB
|
|
}
|
|
// Check the tableName valid.
|
|
sequence, err := b.ctx.GetSessionVars().TxnCtx.InfoSchema.(util.SequenceSchema).SequenceByName(model.NewCIStr(db), model.NewCIStr(seq))
|
|
if err != nil {
|
|
return 0, false, err
|
|
}
|
|
// Do the privilege check.
|
|
checker := privilege.GetPrivilegeManager(b.ctx)
|
|
user := b.ctx.GetSessionVars().User
|
|
if checker != nil && !checker.RequestVerification(b.ctx.GetSessionVars().ActiveRoles, db, seq, "", mysql.InsertPriv) {
|
|
return 0, false, errSequenceAccessDenied.GenWithStackByArgs("INSERT", user.AuthUsername, user.AuthHostname, seq)
|
|
}
|
|
nextVal, err := sequence.GetSequenceNextVal(b.ctx, db, seq)
|
|
if err != nil {
|
|
return 0, false, err
|
|
}
|
|
// update the sequenceState.
|
|
b.ctx.GetSessionVars().SequenceState.UpdateState(sequence.GetSequenceID(), nextVal)
|
|
return nextVal, false, nil
|
|
}
|
|
|
|
type lastValFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *lastValFunctionClass) getFunction(ctx sessionctx.Context, 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{bf}
|
|
bf.tp.Flen = 10
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinLastValSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinLastValSig) Clone() builtinFunc {
|
|
newSig := &builtinLastValSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
func (b *builtinLastValSig) evalInt(row chunk.Row) (int64, bool, error) {
|
|
sequenceName, isNull, err := b.args[0].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return 0, isNull, err
|
|
}
|
|
db, seq := getSchemaAndSequence(sequenceName)
|
|
if len(db) == 0 {
|
|
db = b.ctx.GetSessionVars().CurrentDB
|
|
}
|
|
// Check the tableName valid.
|
|
sequence, err := b.ctx.GetSessionVars().TxnCtx.InfoSchema.(util.SequenceSchema).SequenceByName(model.NewCIStr(db), model.NewCIStr(seq))
|
|
if err != nil {
|
|
return 0, false, err
|
|
}
|
|
// Do the privilege check.
|
|
checker := privilege.GetPrivilegeManager(b.ctx)
|
|
user := b.ctx.GetSessionVars().User
|
|
if checker != nil && !checker.RequestVerification(b.ctx.GetSessionVars().ActiveRoles, db, seq, "", mysql.SelectPriv) {
|
|
return 0, false, errSequenceAccessDenied.GenWithStackByArgs("SELECT", user.AuthUsername, user.AuthHostname, seq)
|
|
}
|
|
return b.ctx.GetSessionVars().SequenceState.GetLastValue(sequence.GetSequenceID())
|
|
}
|
|
|
|
type setValFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *setValFunctionClass) getFunction(ctx sessionctx.Context, 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{bf}
|
|
bf.tp.Flen = args[1].GetType().Flen
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinSetValSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
func (b *builtinSetValSig) Clone() builtinFunc {
|
|
newSig := &builtinSetValSig{}
|
|
newSig.cloneFrom(&b.baseBuiltinFunc)
|
|
return newSig
|
|
}
|
|
|
|
func (b *builtinSetValSig) evalInt(row chunk.Row) (int64, bool, error) {
|
|
sequenceName, isNull, err := b.args[0].EvalString(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return 0, isNull, err
|
|
}
|
|
db, seq := getSchemaAndSequence(sequenceName)
|
|
if len(db) == 0 {
|
|
db = b.ctx.GetSessionVars().CurrentDB
|
|
}
|
|
// Check the tableName valid.
|
|
sequence, err := b.ctx.GetSessionVars().TxnCtx.InfoSchema.(util.SequenceSchema).SequenceByName(model.NewCIStr(db), model.NewCIStr(seq))
|
|
if err != nil {
|
|
return 0, false, err
|
|
}
|
|
// Do the privilege check.
|
|
checker := privilege.GetPrivilegeManager(b.ctx)
|
|
user := b.ctx.GetSessionVars().User
|
|
if checker != nil && !checker.RequestVerification(b.ctx.GetSessionVars().ActiveRoles, db, seq, "", mysql.InsertPriv) {
|
|
return 0, false, errSequenceAccessDenied.GenWithStackByArgs("INSERT", user.AuthUsername, user.AuthHostname, seq)
|
|
}
|
|
setValue, isNull, err := b.args[1].EvalInt(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return 0, isNull, err
|
|
}
|
|
return sequence.SetSequenceVal(b.ctx, setValue, db, seq)
|
|
}
|
|
|
|
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 sessionctx.Context, 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
|
|
}
|
|
bf.tp.Flag |= mysql.UnsignedFlag
|
|
sig := &builtinFormatBytesSig{bf}
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinFormatBytesSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
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(row chunk.Row) (string, bool, error) {
|
|
val, isNull, err := b.args[0].EvalReal(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return "", isNull, err
|
|
}
|
|
return GetFormatBytes(val), false, nil
|
|
}
|
|
|
|
type formatNanoTimeFunctionClass struct {
|
|
baseFunctionClass
|
|
}
|
|
|
|
func (c *formatNanoTimeFunctionClass) getFunction(ctx sessionctx.Context, 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
|
|
}
|
|
bf.tp.Flag |= mysql.UnsignedFlag
|
|
sig := &builtinFormatNanoTimeSig{bf}
|
|
return sig, nil
|
|
}
|
|
|
|
type builtinFormatNanoTimeSig struct {
|
|
baseBuiltinFunc
|
|
}
|
|
|
|
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(row chunk.Row) (string, bool, error) {
|
|
val, isNull, err := b.args[0].EvalReal(b.ctx, row)
|
|
if isNull || err != nil {
|
|
return "", isNull, err
|
|
}
|
|
return GetFormatNanoTime(val), false, nil
|
|
}
|
|
|