diff --git a/pkg/expression/expression.go b/pkg/expression/expression.go index ad8f9bf01b..55bac11e75 100644 --- a/pkg/expression/expression.go +++ b/pkg/expression/expression.go @@ -811,6 +811,16 @@ type Assignment struct { LazyErr error } +// Clone clones the Assignment. +func (a *Assignment) Clone() *Assignment { + return &Assignment{ + Col: a.Col.Clone().(*Column), + ColName: a.ColName, + Expr: a.Expr.Clone(), + LazyErr: a.LazyErr, + } +} + // MemoryUsage return the memory usage of Assignment func (a *Assignment) MemoryUsage() (sum int64) { if a == nil { diff --git a/pkg/planner/core/common_plans.go b/pkg/planner/core/common_plans.go index 2d67625ffc..4c78407c83 100644 --- a/pkg/planner/core/common_plans.go +++ b/pkg/planner/core/common_plans.go @@ -324,17 +324,24 @@ func (p *PhysicalSimpleWrapper) MemoryUsage() (sum int64) { // InsertGeneratedColumns is for completing generated columns in Insert. // We resolve generation expressions in plan, and eval those in executor. type InsertGeneratedColumns struct { - Columns []*ast.ColumnName Exprs []expression.Expression OnDuplicates []*expression.Assignment } +// Copy clones InsertGeneratedColumns. +func (i InsertGeneratedColumns) Copy() InsertGeneratedColumns { + return InsertGeneratedColumns{ + Exprs: util.CloneExpressions(i.Exprs), + OnDuplicates: util.CloneAssignments(i.OnDuplicates), + } +} + // MemoryUsage return the memory usage of InsertGeneratedColumns func (i *InsertGeneratedColumns) MemoryUsage() (sum int64) { if i == nil { return } - sum = size.SizeOfSlice*3 + int64(cap(i.Columns)+cap(i.OnDuplicates))*size.SizeOfPointer + int64(cap(i.Exprs))*size.SizeOfInterface + sum = size.SizeOfSlice*3 + int64(cap(i.OnDuplicates))*size.SizeOfPointer + int64(cap(i.Exprs))*size.SizeOfInterface for _, expr := range i.Exprs { sum += expr.MemoryUsage() @@ -349,15 +356,15 @@ func (i *InsertGeneratedColumns) MemoryUsage() (sum int64) { type Insert struct { baseSchemaProducer - Table table.Table - tableSchema *expression.Schema - tableColNames types.NameSlice - Columns []*ast.ColumnName + Table table.Table `plan-cache-clone:"shallow"` + tableSchema *expression.Schema `plan-cache-clone:"shallow"` + tableColNames types.NameSlice `plan-cache-clone:"shallow"` + Columns []*ast.ColumnName `plan-cache-clone:"shallow"` Lists [][]expression.Expression OnDuplicate []*expression.Assignment - Schema4OnDuplicate *expression.Schema - names4OnDuplicate types.NameSlice + Schema4OnDuplicate *expression.Schema `plan-cache-clone:"shallow"` + names4OnDuplicate types.NameSlice `plan-cache-clone:"shallow"` GenCols InsertGeneratedColumns @@ -372,8 +379,8 @@ type Insert struct { RowLen int - FKChecks []*FKCheck - FKCascades []*FKCascade + FKChecks []*FKCheck `plan-cache-clone:"must-nil"` + FKCascades []*FKCascade `plan-cache-clone:"must-nil"` } // MemoryUsage return the memory usage of Insert @@ -429,16 +436,19 @@ type Update struct { SelectPlan base.PhysicalPlan - TblColPosInfos TblColPosInfoSlice + // TblColPosInfos is for multi-table update statement. + // It records the column position of each related table. + TblColPosInfos TblColPosInfoSlice `plan-cache-clone:"shallow"` // Used when partition sets are given. // e.g. update t partition(p0) set a = 1; - PartitionedTable []table.PartitionedTable + PartitionedTable []table.PartitionedTable `plan-cache-clone:"must-nil"` - tblID2Table map[int64]table.Table + // tblID2Table stores related tables' info of this Update statement. + tblID2Table map[int64]table.Table `plan-cache-clone:"shallow"` - FKChecks map[int64][]*FKCheck - FKCascades map[int64][]*FKCascade + FKChecks map[int64][]*FKCheck `plan-cache-clone:"must-nil"` + FKCascades map[int64][]*FKCascade `plan-cache-clone:"must-nil"` } // MemoryUsage return the memory usage of Update @@ -477,10 +487,10 @@ type Delete struct { SelectPlan base.PhysicalPlan - TblColPosInfos TblColPosInfoSlice + TblColPosInfos TblColPosInfoSlice `plan-cache-clone:"shallow"` - FKChecks map[int64][]*FKCheck - FKCascades map[int64][]*FKCascade + FKChecks map[int64][]*FKCheck `plan-cache-clone:"must-nil"` + FKCascades map[int64][]*FKCascade `plan-cache-clone:"must-nil"` } // MemoryUsage return the memory usage of Delete diff --git a/pkg/planner/core/foreign_key.go b/pkg/planner/core/foreign_key.go index 241204dbaf..5728e274c4 100644 --- a/pkg/planner/core/foreign_key.go +++ b/pkg/planner/core/foreign_key.go @@ -186,8 +186,10 @@ func (p *Insert) buildOnInsertFKTriggers(ctx base.PlanContext, is infoschema.Inf fkChecks = append(fkChecks, fkCheck) } } - p.FKChecks = fkChecks - p.FKCascades = fkCascades + if len(fkChecks) > 0 || len(fkCascades) > 0 { + p.FKChecks = fkChecks + p.FKCascades = fkCascades + } return nil } @@ -254,8 +256,10 @@ func (updt *Update) buildOnUpdateFKTriggers(ctx base.PlanContext, is infoschema. fkChecks[tid] = append(fkChecks[tid], childFKChecks...) } } - updt.FKChecks = fkChecks - updt.FKCascades = fkCascades + if len(fkChecks) > 0 || len(fkCascades) > 0 { + updt.FKChecks = fkChecks + updt.FKCascades = fkCascades + } return nil } @@ -285,8 +289,10 @@ func (del *Delete) buildOnDeleteFKTriggers(ctx base.PlanContext, is infoschema.I } } } - del.FKChecks = fkChecks - del.FKCascades = fkCascades + if len(fkChecks) > 0 || len(fkCascades) > 0 { + del.FKChecks = fkChecks + del.FKCascades = fkCascades + } return nil } diff --git a/pkg/planner/core/index_join_path.go b/pkg/planner/core/index_join_path.go index 883c74285f..eeea1bacee 100644 --- a/pkg/planner/core/index_join_path.go +++ b/pkg/planner/core/index_join_path.go @@ -641,6 +641,9 @@ type ColWithCmpFuncManager struct { // Copy clones the ColWithCmpFuncManager. func (cwc *ColWithCmpFuncManager) Copy() *ColWithCmpFuncManager { + if cwc == nil { + return nil + } cloned := new(ColWithCmpFuncManager) if cwc.TargetCol != nil { cloned.TargetCol = cwc.TargetCol.Clone().(*expression.Column) diff --git a/pkg/planner/core/plan_cache_rebuild_test.go b/pkg/planner/core/plan_cache_rebuild_test.go index 96acf2d393..ae48fcd277 100644 --- a/pkg/planner/core/plan_cache_rebuild_test.go +++ b/pkg/planner/core/plan_cache_rebuild_test.go @@ -39,6 +39,7 @@ func TestPlanCacheClone(t *testing.T) { tk1.MustExec(`use test`) tk2.MustExec(`use test`) tk1.MustExec(`create table t (a int, b int, c int, d int, primary key(a), key(b), unique key(d))`) + tk1.MustExec(`create table t1 (a int, b int, c int, d int)`) for i := -20; i < 20; i++ { tk1.MustExec(fmt.Sprintf("insert into t values (%v,%v,%v,%v)", i, rand.Intn(20), rand.Intn(20), -i)) @@ -181,13 +182,41 @@ func TestPlanCacheClone(t *testing.T) { `set @a1=1,@b1=1, @a2=2,@b2=2`, `execute st using @a1,@b1`, `execute st using @a2,@b2`) testCachedPlanClone(t, tk1, tk2, `prepare st from 'select * from t where d in (?,?)'`, `set @a1=1,@b1=1, @a2=2,@b2=2`, `execute st using @a1,@b1`, `execute st using @a2,@b2`) + + // Insert + testCachedPlanClone(t, tk1, tk2, `prepare st from 'insert into t1 values (?, ?, ?, ?)'`, + `set @a=1, @b=2`, `execute st using @a, @a, @a, @a`, `execute st using @b, @b, @b, @b`) + testCachedPlanClone(t, tk1, tk2, `prepare st from 'insert into t1 select * from t where a