Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pkg/planner/core/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ go_test(
"//pkg/testkit/testsetup",
"//pkg/testkit/testutil",
"//pkg/types",
"//pkg/types/parser_driver",
"//pkg/util",
"//pkg/util/chunk",
"//pkg/util/collate",
Expand Down
40 changes: 21 additions & 19 deletions pkg/planner/core/expression_rewriter.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ func buildSimpleExpr(ctx expression.BuildContext, node ast.ExprNode, opts ...exp
}

if ft := options.TargetFieldType; ft != nil {
expr = expression.BuildCastFunction(ctx, expr, ft)
expr = expression.BuildCastFunction(ctx, expr, ft.DeepCopy())
}

return expr, err
Expand Down Expand Up @@ -1612,7 +1612,8 @@ func (er *expressionRewriter) Leave(originInNode ast.Node) (retNode ast.Node, ok
return retNode, false
}

castFunction, err := expression.BuildCastFunctionWithCheck(er.sctx, arg, v.Tp, false, v.ExplicitCharSet)
targetTp := v.Tp.DeepCopy()
castFunction, err := expression.BuildCastFunctionWithCheck(er.sctx, arg, targetTp, false, v.ExplicitCharSet)
if err != nil {
er.err = err
return retNode, false
Expand All @@ -1633,7 +1634,8 @@ func (er *expressionRewriter) Leave(originInNode ast.Node) (retNode ast.Node, ok
er.ctxNameStk[len(er.ctxNameStk)-1] = types.EmptyName
case *ast.JSONSumCrc32Expr:
arg := er.ctxStack[len(er.ctxStack)-1]
jsonSumFunction, err := expression.BuildJSONSumCrc32FunctionWithCheck(er.sctx, arg, v.Tp)
targetTp := v.Tp.DeepCopy()
jsonSumFunction, err := expression.BuildJSONSumCrc32FunctionWithCheck(er.sctx, arg, targetTp)
if err != nil {
er.err = err
return retNode, false
Expand All @@ -1651,7 +1653,7 @@ func (er *expressionRewriter) Leave(originInNode ast.Node) (retNode ast.Node, ok
er.rowToScalarFunc(v)
case *ast.PatternInExpr:
if v.Sel == nil {
er.inToExpression(len(v.List), v.Not, &v.Type)
er.inToExpression(len(v.List), v.Not, v.Type.DeepCopy())
}
case *ast.PositionExpr:
withPlanCtx(func(planCtx *exprRewriterPlanCtx) {
Expand Down Expand Up @@ -1913,7 +1915,7 @@ func (er *expressionRewriter) unaryOpToExpression(v *ast.UnaryOperationExpr) {
er.err = expression.ErrOperandColumns.GenWithStackByArgs(1)
return
}
er.ctxStack[stkLen-1], er.err = er.newFunction(op, &v.Type, er.ctxStack[stkLen-1])
er.ctxStack[stkLen-1], er.err = er.newFunction(op, v.Type.DeepCopy(), er.ctxStack[stkLen-1])
er.ctxNameStk[stkLen-1] = types.EmptyName
}

Expand Down Expand Up @@ -1965,7 +1967,7 @@ func (er *expressionRewriter) isNullToExpression(v *ast.IsNullExpr) {
er.err = expression.ErrOperandColumns.GenWithStackByArgs(1)
return
}
function := er.notToExpression(v.Not, ast.IsNull, &v.Type, er.ctxStack[stkLen-1])
function := er.notToExpression(v.Not, ast.IsNull, v.Type.DeepCopy(), er.ctxStack[stkLen-1])
er.ctxStackPop(1)
er.ctxStackAppend(function, types.EmptyName)
}
Expand Down Expand Up @@ -2005,7 +2007,7 @@ func (er *expressionRewriter) isTrueToScalarFunc(v *ast.IsTruthExpr) {
er.err = expression.ErrOperandColumns.GenWithStackByArgs(1)
return
}
function := er.notToExpression(v.Not, op, &v.Type, er.ctxStack[stkLen-1])
function := er.notToExpression(v.Not, op, v.Type.DeepCopy(), er.ctxStack[stkLen-1])
er.ctxStackPop(1)
er.ctxStackAppend(function, types.EmptyName)
}
Expand Down Expand Up @@ -2196,7 +2198,7 @@ func (er *expressionRewriter) caseToExpression(v *ast.CaseExpr) {
// else clause
args = er.ctxStack[stkLen-argsLen:]
}
function, err := er.newFunction(ast.Case, &v.Type, args...)
function, err := er.newFunction(ast.Case, v.Type.DeepCopy(), args...)
if err != nil {
er.err = err
return
Expand Down Expand Up @@ -2244,7 +2246,7 @@ func (er *expressionRewriter) patternLikeOrIlikeToExpression(v *ast.PatternLikeO
funcName = ast.Ilike
}
types.DefaultTypeForValue(int(v.Escape), fieldType, char, col)
function = er.notToExpression(v.Not, funcName, &v.Type,
function = er.notToExpression(v.Not, funcName, v.Type.DeepCopy(),
er.ctxStack[l-2], er.ctxStack[l-1], &expression.Constant{Value: types.NewIntDatum(int64(v.Escape)), RetType: fieldType})
}

Expand All @@ -2258,7 +2260,7 @@ func (er *expressionRewriter) regexpToScalarFunc(v *ast.PatternRegexpExpr) {
if er.err != nil {
return
}
function := er.notToExpression(v.Not, ast.Regexp, &v.Type, er.ctxStack[l-2], er.ctxStack[l-1])
function := er.notToExpression(v.Not, ast.Regexp, v.Type.DeepCopy(), er.ctxStack[l-2], er.ctxStack[l-1])
er.ctxStackPop(2)
er.ctxStackAppend(function, types.EmptyName)
}
Expand Down Expand Up @@ -2346,21 +2348,21 @@ func (er *expressionRewriter) betweenToExpression(v *ast.BetweenExpr) {
rexp = expression.BuildCastCollationFunction(er.sctx, rexp, coll, enumOrSetRealTypeIsStr)

var l, r expression.Expression
l, er.err = expression.NewFunction(er.sctx, ast.GE, &v.Type, expr, lexp)
l, er.err = expression.NewFunction(er.sctx, ast.GE, v.Type.DeepCopy(), expr, lexp)
if er.err != nil {
return
}
r, er.err = expression.NewFunction(er.sctx, ast.LE, &v.Type, expr, rexp)
r, er.err = expression.NewFunction(er.sctx, ast.LE, v.Type.DeepCopy(), expr, rexp)
if er.err != nil {
return
}
function, err := er.newFunction(ast.LogicAnd, &v.Type, l, r)
function, err := er.newFunction(ast.LogicAnd, v.Type.DeepCopy(), l, r)
if err != nil {
er.err = err
return
}
if v.Not {
function, err = er.newFunction(ast.UnaryNot, &v.Type, function)
function, err = er.newFunction(ast.UnaryNot, v.Type.DeepCopy(), function)
if err != nil {
er.err = err
return
Expand Down Expand Up @@ -2434,7 +2436,7 @@ func (er *expressionRewriter) rewriteFuncCall(v *ast.FuncCallExpr) bool {
RetType: nullTp,
}
// if(param1 = param2, NULL, param1)
funcIf, err := er.newFunction(ast.If, &v.Type, funcCompare, paramNull, param1)
funcIf, err := er.newFunction(ast.If, v.Type.DeepCopy(), funcCompare, paramNull, param1)
if err != nil {
er.err = err
return true
Expand Down Expand Up @@ -2495,7 +2497,7 @@ func (er *expressionRewriter) funcCallToExpressionWithPlanCtx(planCtx *exprRewri
err = groupingFunc.Function.(*expression.BuiltinGroupingImplSig).SetMetadata(planCtx.rollExpand.GroupingMode, planCtx.rollExpand.GenerateGroupingMarks(resolvedCols))
return groupingFunc, err
}
function, er.err = er.newFunctionWithInit(v.FnName.L, &v.Type, init, newArg)
function, er.err = er.newFunctionWithInit(v.FnName.L, v.Type.DeepCopy(), init, newArg)
er.ctxStackAppend(function, types.EmptyName)
}
default:
Expand All @@ -2522,15 +2524,15 @@ func (er *expressionRewriter) funcCallToExpression(v *ast.FuncCallExpr) {
// When the expression is unix_timestamp and the number of argument is not zero,
// we deal with it as normal expression.
if v.FnName.L == ast.UnixTimestamp && len(v.Args) != 0 {
function, er.err = er.newFunction(v.FnName.L, &v.Type, args...)
function, er.err = er.newFunction(v.FnName.L, v.Type.DeepCopy(), args...)
er.ctxStackAppend(function, types.EmptyName)
} else {
function, er.err = expression.NewFunctionBase(er.sctx, v.FnName.L, &v.Type, args...)
function, er.err = expression.NewFunctionBase(er.sctx, v.FnName.L, v.Type.DeepCopy(), args...)
c := &expression.Constant{Value: types.NewDatum(nil), RetType: function.GetType(er.sctx.GetEvalCtx()).Clone(), DeferredExpr: function}
er.ctxStackAppend(c, types.EmptyName)
}
} else {
function, er.err = er.newFunction(v.FnName.L, &v.Type, args...)
function, er.err = er.newFunction(v.FnName.L, v.Type.DeepCopy(), args...)
er.ctxStackAppend(function, types.EmptyName)
}
}
Expand Down
46 changes: 46 additions & 0 deletions pkg/planner/core/expression_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,52 @@ func TestCast(t *testing.T) {
require.Equal(t, types.KindNull, v.Kind())
}

func TestCastRetTypeDoesNotShareASTFieldType(t *testing.T) {
targetTp := types.NewFieldType(mysql.TypeLonglong)
targetTp.AddFlag(mysql.NotNullFlag)

ctx := coretestsdk.MockContext()
defer func() {
do := domain.GetDomain(ctx)
do.StatsHandle().Close()
}()

tbl := &model.TableInfo{
Name: ast.NewCIStr("t"),
Columns: []*model.ColumnInfo{
{
Name: ast.NewCIStr("a"),
Offset: 0,
State: model.StatePublic,
FieldType: *types.NewFieldType(mysql.TypeLonglong),
},
},
}
expr := parseExpr(t, "cast(a as signed)").(*ast.FuncCastExpr)
expr.Tp = targetTp

built1, err := buildExpr(t, ctx, expr, expression.WithTableInfo("", tbl))
require.NoError(t, err)
sf1, ok := built1.(*expression.ScalarFunction)
require.True(t, ok)
require.NotSame(t, targetTp, sf1.RetType)
require.True(t, mysql.HasNotNullFlag(targetTp.GetFlag()))

sf1.RetType.SetType(mysql.TypeString)
sf1.RetType.AddFlag(mysql.UnsignedFlag)

built2, err := buildExpr(t, ctx, expr, expression.WithTableInfo("", tbl))
require.NoError(t, err)
sf2, ok := built2.(*expression.ScalarFunction)
require.True(t, ok)
require.NotSame(t, sf1.RetType, sf2.RetType)
require.Equal(t, mysql.TypeLonglong, targetTp.GetType())
require.True(t, mysql.HasNotNullFlag(targetTp.GetFlag()))
require.Equal(t, mysql.TypeLonglong, sf2.RetType.GetType())
require.False(t, mysql.HasNotNullFlag(sf2.RetType.GetFlag()))
require.False(t, mysql.HasUnsignedFlag(sf2.RetType.GetFlag()))
}

func TestPatternIn(t *testing.T) {
tests := []testCase{
{
Expand Down
13 changes: 8 additions & 5 deletions pkg/planner/core/logical_plan_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -3685,7 +3685,7 @@ func (b *PlanBuilder) addAliasName(ctx context.Context, selectStmt *ast.SelectSt
}

func (b *PlanBuilder) pushTableHints(hints []*ast.TableOptimizerHint, currentLevel int) {
hints = b.hintProcessor.GetCurrentStmtHints(hints, currentLevel)
hints = b.hintProcessor.GetCurrentStmtHints(hints, currentLevel, b.hintState)
sessionVars := b.ctx.GetSessionVars()
currentDB := sessionVars.CurrentDB
warnHandler := sessionVars.StmtCtx
Expand Down Expand Up @@ -4495,7 +4495,7 @@ func (b *PlanBuilder) buildDataSource(ctx context.Context, tn *ast.TableName, as
// Because of the nested views, so we should check the left table list in hint when build the data source from the view inside the current view.
currentQBNameMap4View[qbName] = viewQBNameHintTable[1:]
currentViewHints[qbName] = b.hintProcessor.ViewQBNameToHints[qbName]
b.hintProcessor.ViewQBNameUsed[qbName] = struct{}{}
b.hintProcessor.MarkViewQBNameUsed(qbName, b.hintState)
}
}
return b.BuildDataSourceFromView(ctx, dbName, tableInfo, currentQBNameMap4View, currentViewHints)
Expand Down Expand Up @@ -4995,18 +4995,21 @@ func (b *PlanBuilder) BuildDataSourceFromView(ctx context.Context, dbName ast.CI

hintProcessor.ViewQBNameToTable = qbNameMap4View
hintProcessor.ViewQBNameToHints = viewHints
hintProcessor.ViewQBNameUsed = make(map[string]struct{})
hintProcessor.QBOffsetToHints = currentQbHints
hintProcessor.QBNameToSelOffset = currentQbNameMap
hintState := hintProcessor.NewBuildState()
hintState.QBOffsetToHints = currentQbHints

originHintProcessor := b.hintProcessor
originHintState := b.hintState
originPlannerSelectBlockAsName := b.ctx.GetSessionVars().PlannerSelectBlockAsName.Load()
b.hintProcessor = hintProcessor
b.hintState = hintState
newPlannerSelectBlockAsName := make([]ast.HintTable, hintProcessor.MaxSelectStmtOffset()+1)
b.ctx.GetSessionVars().PlannerSelectBlockAsName.Store(&newPlannerSelectBlockAsName)
defer func() {
b.hintProcessor.HandleUnusedViewHints()
b.hintProcessor.SetWarns(b.hintProcessor.HandleUnusedViewHints(b.hintState, nil))
b.hintProcessor = originHintProcessor
b.hintState = originHintState
b.ctx.GetSessionVars().PlannerSelectBlockAsName.Store(originPlannerSelectBlockAsName)
}()
nodeW := resolve.NewNodeWWithCtx(selectNode, b.resolveCtx)
Expand Down
6 changes: 5 additions & 1 deletion pkg/planner/core/operator/logicalop/logical_datasource.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,16 @@ import (
type DataSource struct {
LogicalSchemaProducer `hash64-equals:"true"`

// AstIndexHints keeps the original AST hints for later access-path selection.
// It is treated as read-only after DataSource build and may be shared by plans rebuilt from the same AST.
AstIndexHints []*ast.IndexHint
IndexHints []h.HintedIndex
Table table.Table
TableInfo *model.TableInfo `hash64-equals:"true"`
Columns []*model.ColumnInfo
DBName ast.CIStr

// TableAsName points to the AST alias and is only used as read-only metadata after plan build.
TableAsName *ast.CIStr `hash64-equals:"true"`
// IndexMergeHints are the hint for indexmerge.
IndexMergeHints []h.HintedIndex
Expand Down Expand Up @@ -84,7 +87,8 @@ type DataSource struct {
// The data source may be a partition, rather than a real table.
PartitionDefIdx *int
PhysicalTableID int64 `hash64-equals:"true"`
PartitionNames []ast.CIStr
// PartitionNames records the explicit partition list from AST and is treated as read-only after plan build.
PartitionNames []ast.CIStr

// handleCol represents the handle column for the datasource, either the
// int primary key column or extra handle column.
Expand Down
2 changes: 2 additions & 0 deletions pkg/planner/core/operator/logicalop/logical_lock.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import (
type LogicalLock struct {
BaseLogicalPlan `hash64-equals:"true"`

// Lock points to AST lock metadata. Preprocess may normalize the AST before planning,
// but after LogicalLock is built this field is treated as read-only.
Lock *ast.SelectLockInfo `hash64-equals:"true"`
TblID2Handle map[int64][]util.HandleCols

Expand Down
20 changes: 11 additions & 9 deletions pkg/planner/core/operator/logicalop/logical_show.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,12 @@ type LogicalShow struct {

// ShowContents stores the contents for the `SHOW` statement.
type ShowContents struct {
Tp ast.ShowStmtType // Databases/Tables/Columns/....
DBName string
Table *resolve.TableNameW // Used for showing columns.
Partition ast.CIStr // Use for showing partition
Column *ast.ColumnName // Used for `desc table column`.
Tp ast.ShowStmtType // Databases/Tables/Columns/....
DBName string
Table *resolve.TableNameW // Used for showing columns.
Partition ast.CIStr // Use for showing partition
// Column points to the AST selector for `desc table column` and is treated as read-only after plan build.
Column *ast.ColumnName
IndexName ast.CIStr
ResourceGroupName string // Used for showing resource group
Flag int // Some flag parsed from sql, such as FULL.
Expand All @@ -51,10 +52,11 @@ type ShowContents struct {
CountWarningsOrErrors bool // Used for showing count(*) warnings | errors

Full bool
IfNotExists bool // Used for `show create database if not exists`.
GlobalScope bool // Used by show variables.
Extended bool // Used for `show extended columns from ...`
Limit *ast.Limit // Used for limit Result Set row number.
IfNotExists bool // Used for `show create database if not exists`.
GlobalScope bool // Used by show variables.
Extended bool // Used for `show extended columns from ...`
// Limit points to the AST SHOW limit clause. It is only read during planner build and should stay immutable.
Limit *ast.Limit

ImportJobID *int64 // Used for SHOW LOAD DATA JOB <jobID>
ImportGroupKey string // Used for SHOW IMPORT GROUP <GROUP_KEY>
Expand Down
1 change: 1 addition & 0 deletions pkg/planner/core/operator/physicalop/physical_lock.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
type PhysicalLock struct {
BasePhysicalPlan

// Lock shares the read-only AST lock metadata forwarded from LogicalLock.
Lock *ast.SelectLockInfo `plan-cache-clone:"shallow"`

TblID2Handle map[int64][]util.HandleCols
Expand Down
Loading