From 1d641955d5a032e5d457f298dfc0dd62c3cb5041 Mon Sep 17 00:00:00 2001 From: Kenan Yao Date: Wed, 15 Jan 2020 18:42:20 +0800 Subject: [PATCH] expression: disable `int_col non-int const` folding for plan cache (#14120) --- expression/builtin_compare.go | 22 +++++++++++++-------- expression/integration_test.go | 35 ++++++++++++++++++++++++++++++++++ expression/util.go | 17 +++++++++++++++++ 3 files changed, 66 insertions(+), 8 deletions(-) diff --git a/expression/builtin_compare.go b/expression/builtin_compare.go index f74272fd9d799..211d544fccc0b 100644 --- a/expression/builtin_compare.go +++ b/expression/builtin_compare.go @@ -1098,8 +1098,10 @@ func tryToConvertConstantInt(ctx sessionctx.Context, targetFieldType *types.Fiel if err != nil { if terror.ErrorEqual(err, types.ErrOverflow) { return &Constant{ - Value: dt, - RetType: targetFieldType, + Value: dt, + RetType: targetFieldType, + DeferredExpr: con.DeferredExpr, + ParamMarker: con.ParamMarker, }, true } return con, false @@ -1134,8 +1136,10 @@ func RefineComparedConstant(ctx sessionctx.Context, targetFieldType types.FieldT if err != nil { if terror.ErrorEqual(err, types.ErrOverflow) { return &Constant{ - Value: intDatum, - RetType: &targetFieldType, + Value: intDatum, + RetType: &targetFieldType, + DeferredExpr: con.DeferredExpr, + ParamMarker: con.ParamMarker, }, true } return con, false @@ -1201,6 +1205,9 @@ func RefineComparedConstant(ctx sessionctx.Context, targetFieldType types.FieldT // refineArgs will rewrite the arguments if the compare expression is `int column non-int constant` or // `non-int constant int column`. E.g., `a < 1.1` will be rewritten to `a < 2`. func (c *compareFunctionClass) refineArgs(ctx sessionctx.Context, args []Expression) []Expression { + if ctx.GetSessionVars().StmtCtx.UseCache && ContainLazyConst(args) { + return args + } arg0Type, arg1Type := args[0].GetType(), args[1].GetType() arg0IsInt := arg0Type.EvalType() == types.ETInt arg1IsInt := arg1Type.EvalType() == types.ETInt @@ -1239,24 +1246,23 @@ func (c *compareFunctionClass) refineArgs(ctx sessionctx.Context, args []Express } } } - if isExceptional && (c.op == opcode.EQ || c.op == opcode.NullEQ) { // This will always be false. - return []Expression{Zero.Clone(), One.Clone()} + return []Expression{Zero, One} } if isPositiveInfinite { // If the op is opcode.LT, opcode.LE // This will always be true. // If the op is opcode.GT, opcode.GE // This will always be false. - return []Expression{Zero.Clone(), One.Clone()} + return []Expression{Zero, One} } if isNegativeInfinite { // If the op is opcode.GT, opcode.GE // This will always be true. // If the op is opcode.LT, opcode.LE // This will always be false. - return []Expression{One.Clone(), Zero.Clone()} + return []Expression{One, Zero} } return []Expression{finalArg0, finalArg1} diff --git a/expression/integration_test.go b/expression/integration_test.go index 2023f4d98bf3a..a99fcc6f0dd3b 100755 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -5277,3 +5277,38 @@ func (s *testIntegrationSuite) TestCacheRegexpr(c *C) { tk.MustExec("set @a='^R.*'") tk.MustQuery("execute stmt1 using @a").Check(testkit.Rows("R1")) } + +func (s *testIntegrationSuite) TestCacheRefineArgs(c *C) { + tk := testkit.NewTestKit(c, s.store) + orgEnable := plannercore.PreparedPlanCacheEnabled() + orgCapacity := plannercore.PreparedPlanCacheCapacity + orgMemGuardRatio := plannercore.PreparedPlanCacheMemoryGuardRatio + orgMaxMemory := plannercore.PreparedPlanCacheMaxMemory + defer func() { + plannercore.SetPreparedPlanCache(orgEnable) + plannercore.PreparedPlanCacheCapacity = orgCapacity + plannercore.PreparedPlanCacheMemoryGuardRatio = orgMemGuardRatio + plannercore.PreparedPlanCacheMaxMemory = orgMaxMemory + }() + plannercore.SetPreparedPlanCache(true) + plannercore.PreparedPlanCacheCapacity = 100 + plannercore.PreparedPlanCacheMemoryGuardRatio = 0.1 + // PreparedPlanCacheMaxMemory is set to MAX_UINT64 to make sure the cache + // behavior would not be effected by the uncertain memory utilization. + plannercore.PreparedPlanCacheMaxMemory.Store(math.MaxUint64) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(col_int int)") + tk.MustExec("insert into t values(null)") + tk.MustExec("prepare stmt from 'SELECT ((col_int is true) = ?) AS res FROM t'") + tk.MustExec("set @p0='0.8'") + tk.MustQuery("execute stmt using @p0").Check(testkit.Rows("0")) + tk.MustExec("set @p0='0'") + tk.MustQuery("execute stmt using @p0").Check(testkit.Rows("1")) + + tk.MustExec("delete from t") + tk.MustExec("insert into t values(1)") + tk.MustExec("prepare stmt from 'SELECT col_int < ? FROM t'") + tk.MustExec("set @p0='-184467440737095516167.1'") + tk.MustQuery("execute stmt using @p0").Check(testkit.Rows("0")) +} diff --git a/expression/util.go b/expression/util.go index 9634d51b75f7f..0656fe3780c63 100644 --- a/expression/util.go +++ b/expression/util.go @@ -803,3 +803,20 @@ func ContainVirtualColumn(exprs []Expression) bool { } return false } + +// ContainLazyConst checks if the expressions contain a lazy constant. +func ContainLazyConst(exprs []Expression) bool { + for _, expr := range exprs { + switch v := expr.(type) { + case *Constant: + if v.ParamMarker != nil || v.DeferredExpr != nil { + return true + } + case *ScalarFunction: + if ContainLazyConst(v.GetArgs()) { + return true + } + } + } + return false +}