Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

planner: restrict plan cache for decimal parameter types (#31769) #33543

Open
wants to merge 1 commit into
base: release-5.4
Choose a base branch
from
Open
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
5 changes: 0 additions & 5 deletions expression/builtin_arithmetic.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,6 @@ func numericContextResultType(ft *types.FieldType) types.EvalType {
// type according to the two input parameter's types.
func setFlenDecimal4RealOrDecimal(ctx sessionctx.Context, retTp *types.FieldType, arg0, arg1 Expression, isReal bool, isMultiply bool) {
a, b := arg0.GetType(), arg1.GetType()
if MaybeOverOptimized4PlanCache(ctx, []Expression{arg0, arg1}) {
// set length and decimal to unspecified if arguments depend on parameters
retTp.Flen, retTp.Decimal = types.UnspecifiedLength, types.UnspecifiedLength
return
}
if a.Decimal != types.UnspecifiedLength && b.Decimal != types.UnspecifiedLength {
retTp.Decimal = a.Decimal + b.Decimal
if !isMultiply {
Expand Down
13 changes: 10 additions & 3 deletions planner/core/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,10 @@ func NewPlanCacheKey(sessionVars *variable.SessionVars, pstmtID uint32, schemaVe
// FieldSlice is the slice of the types.FieldType
type FieldSlice []types.FieldType

// Equal compares FieldSlice with []*types.FieldType
// Currently this is only used in plan cache to invalidate cache when types of variables are different.
func (s FieldSlice) Equal(tps []*types.FieldType) bool {
// CheckTypesCompatibility4PC compares FieldSlice with []*types.FieldType
// Currently this is only used in plan cache to check whether the types of parameters are compatible.
// If the types of parameters are compatible, we can use the cached plan.
func (s FieldSlice) CheckTypesCompatibility4PC(tps []*types.FieldType) bool {
if len(s) != len(tps) {
return false
}
Expand All @@ -172,6 +173,12 @@ func (s FieldSlice) Equal(tps []*types.FieldType) bool {
(s[i].EvalType() == types.ETInt && mysql.HasUnsignedFlag(s[i].Flag) != mysql.HasUnsignedFlag(tps[i].Flag)) {
return false
}
// When the type is decimal, we should compare the Flen and Decimal.
// We can only use the plan when both Flen and Decimal should less equal than the cached one.
// We assume here that there is no correctness problem when the precision of the parameters is less than the precision of the parameters in the cache.
if tpEqual && s[i].Tp == mysql.TypeNewDecimal && !(s[i].Flen >= tps[i].Flen && s[i].Decimal >= tps[i].Decimal) {
return false
}
}
return true
}
Expand Down
4 changes: 2 additions & 2 deletions planner/core/common_plans.go
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,7 @@ func (e *Execute) getPhysicalPlan(ctx context.Context, sctx sessionctx.Context,
sctx.PreparedPlanCache().Delete(cacheKey)
break
}
if !cachedVal.UserVarTypes.Equal(tps) {
if !cachedVal.UserVarTypes.CheckTypesCompatibility4PC(tps) {
continue
}
planValid := true
Expand Down Expand Up @@ -572,7 +572,7 @@ REBUILD:
if cacheVals, exists := sctx.PreparedPlanCache().Get(cacheKey); exists {
hitVal := false
for i, cacheVal := range cacheVals.([]*PlanCacheValue) {
if cacheVal.UserVarTypes.Equal(tps) {
if cacheVal.UserVarTypes.CheckTypesCompatibility4PC(tps) {
hitVal = true
cacheVals.([]*PlanCacheValue)[i] = cached
break
Expand Down
26 changes: 26 additions & 0 deletions planner/core/prepare_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1733,9 +1733,13 @@ func (s *testPlanSerialSuite) TestIssue29565(c *C) {
tk.MustQuery(`execute stmt using @a,@b`).Check(testkit.Rows())
tk.MustExec(`set @a=5408499810319315618, @b=-9999999999999999999999999999999999999999999999999999999`)
tk.MustQuery(`execute stmt using @a,@b`).Check(testkit.Rows("-9999999999999999999999999999999999999999999999999999999"))
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("0"))
tk.MustExec(`set @a=7309027171262036496, @b=-9798213896406520625`)
tk.MustQuery(`execute stmt using @a,@b`).Check(testkit.Rows())
tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1"))
}

<<<<<<< HEAD
func (s *testPlanSerialSuite) TestIssue28828(c *C) {
store, dom, err := newStoreWithBootstrap()
c.Assert(err, IsNil)
Expand All @@ -1744,6 +1748,28 @@ func (s *testPlanSerialSuite) TestIssue28828(c *C) {
dom.Close()
store.Close()
}()
=======
func TestIssue31730(t *testing.T) {
store, clean := testkit.CreateMockStore(t)
defer clean()
orgEnable := core.PreparedPlanCacheEnabled()
defer core.SetPreparedPlanCache(orgEnable)
core.SetPreparedPlanCache(true)
tk := testkit.NewTestKit(t, store)

tk.MustExec(`use test`)
tk.MustExec(`drop table if exists PK_S_MULTI_37;`)
tk.MustExec(`CREATE TABLE PK_S_MULTI_37 (COL1 decimal(55,0) NOT NULL, COL2 decimal(55,0) NOT NULL,PRIMARY KEY (COL1, COL2) /*T![clustered_index] NONCLUSTERED */) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;`)
tk.MustExec(`insert into PK_S_MULTI_37 values(-9999999999999999999999999999999999999999999999, 1);`)
tk.MustExec(`prepare stmt from 'SELECT SUM(COL1+?), col2 FROM PK_S_MULTI_37 GROUP BY col2';`)
tk.MustExec(`set @a=1;`)
tk.MustQuery(`execute stmt using @a`).Check(testkit.Rows("-9999999999999999999999999999999999999999999998 1"))
}

func TestIssue28828(t *testing.T) {
store, clean := testkit.CreateMockStore(t)
defer clean()
>>>>>>> eec518648... planner: restrict plan cache for decimal parameter types (#31769)
orgEnable := core.PreparedPlanCacheEnabled()
defer func() {
core.SetPreparedPlanCache(orgEnable)
Expand Down