Skip to content

Commit

Permalink
expression: introduce optional properties for EvalContext (#51487)
Browse files Browse the repository at this point in the history
close #51477
  • Loading branch information
lcwangchao authored Mar 11, 2024
1 parent f2ae698 commit 5f3fc33
Show file tree
Hide file tree
Showing 17 changed files with 611 additions and 32 deletions.
1 change: 1 addition & 0 deletions pkg/expression/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ go_library(
"//pkg/errctx",
"//pkg/errno",
"//pkg/expression/context",
"//pkg/expression/contextopt",
"//pkg/extension",
"//pkg/kv",
"//pkg/parser",
Expand Down
6 changes: 6 additions & 0 deletions pkg/expression/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (

"github.com/gogo/protobuf/proto"
"github.com/pingcap/errors"
"github.com/pingcap/tidb/pkg/expression/contextopt"
"github.com/pingcap/tidb/pkg/parser/ast"
"github.com/pingcap/tidb/pkg/parser/charset"
"github.com/pingcap/tidb/pkg/parser/mysql"
Expand Down Expand Up @@ -63,6 +64,10 @@ func (b *baseBuiltinFunc) PbCode() tipb.ScalarFuncSig {
return b.pbCode
}

func (*baseBuiltinFunc) RequiredOptionalEvalProps() (set OptionalEvalPropKeySet) {
return
}

// metadata returns the metadata of a function.
// metadata means some functions contain extra inner fields which will not
// contain in `tipb.Expr.children` but must be pushed down to coprocessor
Expand Down Expand Up @@ -475,6 +480,7 @@ type vecBuiltinFunc interface {

// builtinFunc stands for a particular function signature.
type builtinFunc interface {
contextopt.RequireOptionalEvalProps
vecBuiltinFunc

// evalInt evaluates int result of builtinFunc by given row.
Expand Down
60 changes: 44 additions & 16 deletions pkg/expression/builtin_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"time"

"github.com/pingcap/errors"
"github.com/pingcap/tidb/pkg/expression/contextopt"
"github.com/pingcap/tidb/pkg/parser"
"github.com/pingcap/tidb/pkg/parser/ast"
"github.com/pingcap/tidb/pkg/parser/model"
Expand Down Expand Up @@ -173,12 +174,13 @@ func (c *currentUserFunctionClass) getFunction(ctx BuildContext, args []Expressi
return nil, err
}
bf.tp.SetFlen(64)
sig := &builtinCurrentUserSig{bf}
sig := &builtinCurrentUserSig{baseBuiltinFunc: bf}
return sig, nil
}

type builtinCurrentUserSig struct {
baseBuiltinFunc
contextopt.CurrentUserPropReader
}

func (b *builtinCurrentUserSig) Clone() builtinFunc {
Expand All @@ -187,14 +189,22 @@ func (b *builtinCurrentUserSig) Clone() builtinFunc {
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, row chunk.Row) (string, bool, error) {
data := ctx.GetSessionVars()
if data == nil || data.User == nil {
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 data.User.String(), false, nil
return user.String(), false, nil
}

type currentRoleFunctionClass struct {
Expand All @@ -210,12 +220,13 @@ func (c *currentRoleFunctionClass) getFunction(ctx BuildContext, args []Expressi
return nil, err
}
bf.tp.SetFlen(64)
sig := &builtinCurrentRoleSig{bf}
sig := &builtinCurrentRoleSig{baseBuiltinFunc: bf}
return sig, nil
}

type builtinCurrentRoleSig struct {
baseBuiltinFunc
contextopt.CurrentUserPropReader
}

func (b *builtinCurrentRoleSig) Clone() builtinFunc {
Expand All @@ -224,24 +235,32 @@ func (b *builtinCurrentRoleSig) Clone() builtinFunc {
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) {
data := ctx.GetSessionVars()
if data == nil || data.ActiveRoles == nil {
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(data.ActiveRoles) == 0 {
if len(roles) == 0 {
return "NONE", false, nil
}
sortedRes := make([]string, 0, 10)
for _, r := range data.ActiveRoles {
for _, r := range roles {
sortedRes = append(sortedRes, r.String())
}
slices.Sort(sortedRes)
for i, r := range sortedRes {
res += r
if i != len(data.ActiveRoles)-1 {
if i != len(roles)-1 {
res += ","
}
}
Expand Down Expand Up @@ -309,12 +328,13 @@ func (c *userFunctionClass) getFunction(ctx BuildContext, args []Expression) (bu
return nil, err
}
bf.tp.SetFlen(64)
sig := &builtinUserSig{bf}
sig := &builtinUserSig{baseBuiltinFunc: bf}
return sig, nil
}

type builtinUserSig struct {
baseBuiltinFunc
contextopt.CurrentUserPropReader
}

func (b *builtinUserSig) Clone() builtinFunc {
Expand All @@ -323,14 +343,22 @@ func (b *builtinUserSig) Clone() builtinFunc {
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, row chunk.Row) (string, bool, error) {
data := ctx.GetSessionVars()
if data == nil || data.User == nil {
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 data.User.LoginString(), false, nil
return user.LoginString(), false, nil
}

type connectionIDFunctionClass struct {
Expand Down
30 changes: 20 additions & 10 deletions pkg/expression/builtin_info_vec.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,14 +106,17 @@ func (b *builtinCurrentUserSig) vectorized() bool {
// See https://dev.mysql.com/doc/refman/5.7/en/information-functions.html#function_current-user
func (b *builtinCurrentUserSig) vecEvalString(ctx EvalContext, input *chunk.Chunk, result *chunk.Column) error {
n := input.NumRows()
user, err := b.CurrentUser(ctx)
if err != nil {
return err
}

data := ctx.GetSessionVars()
result.ReserveString(n)
if data == nil || data.User == nil {
if user == nil {
return errors.Errorf("Missing session variable when eval builtin")
}
for i := 0; i < n; i++ {
result.AppendString(data.User.String())
result.AppendString(user.String())
}
return nil
}
Expand Down Expand Up @@ -145,21 +148,25 @@ func (b *builtinCurrentRoleSig) vectorized() bool {
func (b *builtinCurrentRoleSig) vecEvalString(ctx EvalContext, input *chunk.Chunk, result *chunk.Column) error {
n := input.NumRows()

data := ctx.GetSessionVars()
if data == nil || data.ActiveRoles == nil {
roles, err := b.ActiveRoles(ctx)
if err != nil {
return err
}

if roles == nil {
return errors.Errorf("Missing session variable when eval builtin")
}

result.ReserveString(n)
if len(data.ActiveRoles) == 0 {
if len(roles) == 0 {
for i := 0; i < n; i++ {
result.AppendString("NONE")
}
return nil
}

sortedRes := make([]string, 0, 10)
for _, r := range data.ActiveRoles {
for _, r := range roles {
sortedRes = append(sortedRes, r.String())
}
slices.Sort(sortedRes)
Expand All @@ -178,14 +185,17 @@ func (b *builtinUserSig) vectorized() bool {
// See https://dev.mysql.com/doc/refman/5.7/en/information-functions.html#function_user
func (b *builtinUserSig) vecEvalString(ctx EvalContext, input *chunk.Chunk, result *chunk.Column) error {
n := input.NumRows()
data := ctx.GetSessionVars()
if data == nil || data.User == nil {
user, err := b.CurrentUser(ctx)
if err != nil {
return err
}
if user == nil {
return errors.Errorf("Missing session variable when eval builtin")
}

result.ReserveString(n)
for i := 0; i < n; i++ {
result.AppendString(data.User.LoginString())
result.AppendString(user.LoginString())
}
return nil
}
Expand Down
12 changes: 12 additions & 0 deletions pkg/expression/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,18 @@ type EvalContext = context.EvalContext
// BuildContext is used to build an expression
type BuildContext = context.BuildContext

// OptionalEvalPropKey is an alias of context.OptionalEvalPropKey
type OptionalEvalPropKey = context.OptionalEvalPropKey

// OptionalEvalPropProvider is an alias of context.OptionalEvalPropProvider
type OptionalEvalPropProvider = context.OptionalEvalPropProvider

// OptionalEvalPropKeySet is an alias of context.OptionalEvalPropKeySet
type OptionalEvalPropKeySet = context.OptionalEvalPropKeySet

// OptionalEvalPropDesc is an alias of context.OptionalEvalPropDesc
type OptionalEvalPropDesc = context.OptionalEvalPropDesc

func sqlMode(ctx EvalContext) mysql.SQLMode {
return ctx.SQLMode()
}
Expand Down
17 changes: 15 additions & 2 deletions pkg/expression/context/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")

go_library(
name = "context",
srcs = ["context.go"],
srcs = [
"context.go",
"optional.go",
],
importpath = "github.com/pingcap/tidb/pkg/expression/context",
visibility = ["//visibility:public"],
deps = [
Expand All @@ -15,3 +18,13 @@ go_library(
"//pkg/types",
],
)

go_test(
name = "context_test",
timeout = "short",
srcs = ["optional_test.go"],
embed = [":context"],
flaky = True,
shard_count = 4,
deps = ["@com_github_stretchr_testify//require"],
)
2 changes: 2 additions & 0 deletions pkg/expression/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ type EvalContext interface {
GetInfoSchema() infoschema.InfoSchemaMetaVersion
// GetDomainInfoSchema returns the latest information schema in domain
GetDomainInfoSchema() infoschema.InfoSchemaMetaVersion
// GetOptionalPropProvider gets the optional property provider by key
GetOptionalPropProvider(OptionalEvalPropKey) (OptionalEvalPropProvider, bool)
}

// BuildContext is used to build an expression
Expand Down
Loading

0 comments on commit 5f3fc33

Please sign in to comment.