Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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