Skip to content

Commit da6dc8b

Browse files
authored
planner: fix the issue that plan cache may return wrong result when comparing datetime column with unix_timestamp (#48413) (#53266)
close #41918, close #48165
1 parent a3c139d commit da6dc8b

File tree

4 files changed

+41
-14
lines changed

4 files changed

+41
-14
lines changed

expression/builtin_compare.go

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1620,7 +1620,7 @@ func allowCmpArgsRefining4PlanCache(ctx sessionctx.Context, args []Expression) (
16201620
// 3. It also handles comparing datetime/timestamp column with numeric constant, try to cast numeric constant as timestamp type, do nothing if failed.
16211621
// This refining operation depends on the values of these args, but these values can change when using plan-cache.
16221622
// So we have to skip this operation or mark the plan as over-optimized when using plan-cache.
1623-
func (c *compareFunctionClass) refineArgs(ctx sessionctx.Context, args []Expression) []Expression {
1623+
func (c *compareFunctionClass) refineArgs(ctx sessionctx.Context, args []Expression) ([]Expression, error) {
16241624
arg0Type, arg1Type := args[0].GetType(), args[1].GetType()
16251625
arg0EvalType, arg1EvalType := arg0Type.EvalType(), arg1Type.EvalType()
16261626
arg0IsInt := arg0EvalType == types.ETInt
@@ -1631,17 +1631,19 @@ func (c *compareFunctionClass) refineArgs(ctx sessionctx.Context, args []Express
16311631
isPositiveInfinite, isNegativeInfinite := false, false
16321632

16331633
if !allowCmpArgsRefining4PlanCache(ctx, args) {
1634-
return args
1634+
return args, nil
16351635
}
16361636
// We should remove the mutable constant for correctness, because its value may be changed.
1637-
RemoveMutableConst(ctx, args)
1637+
if err := RemoveMutableConst(ctx, args); err != nil {
1638+
return nil, err
1639+
}
16381640

16391641
if arg0IsCon && !arg1IsCon && matchRefineRule3Pattern(arg0EvalType, arg1Type) {
1640-
return c.refineNumericConstantCmpDatetime(ctx, args, arg0, 0)
1642+
return c.refineNumericConstantCmpDatetime(ctx, args, arg0, 0), nil
16411643
}
16421644

16431645
if !arg0IsCon && arg1IsCon && matchRefineRule3Pattern(arg1EvalType, arg0Type) {
1644-
return c.refineNumericConstantCmpDatetime(ctx, args, arg1, 1)
1646+
return c.refineNumericConstantCmpDatetime(ctx, args, arg1, 1), nil
16451647
}
16461648

16471649
// int non-constant [cmp] non-int constant
@@ -1707,24 +1709,24 @@ func (c *compareFunctionClass) refineArgs(ctx sessionctx.Context, args []Express
17071709
}
17081710
if isExceptional && (c.op == opcode.EQ || c.op == opcode.NullEQ) {
17091711
// This will always be false.
1710-
return []Expression{NewZero(), NewOne()}
1712+
return []Expression{NewZero(), NewOne()}, nil
17111713
}
17121714
if isPositiveInfinite {
17131715
// If the op is opcode.LT, opcode.LE
17141716
// This will always be true.
17151717
// If the op is opcode.GT, opcode.GE
17161718
// This will always be false.
1717-
return []Expression{NewZero(), NewOne()}
1719+
return []Expression{NewZero(), NewOne()}, nil
17181720
}
17191721
if isNegativeInfinite {
17201722
// If the op is opcode.GT, opcode.GE
17211723
// This will always be true.
17221724
// If the op is opcode.LT, opcode.LE
17231725
// This will always be false.
1724-
return []Expression{NewOne(), NewZero()}
1726+
return []Expression{NewOne(), NewZero()}, nil
17251727
}
17261728

1727-
return c.refineArgsByUnsignedFlag(ctx, []Expression{finalArg0, finalArg1})
1729+
return c.refineArgsByUnsignedFlag(ctx, []Expression{finalArg0, finalArg1}), nil
17281730
}
17291731

17301732
// see https://github.com/pingcap/tidb/issues/38361 for more details
@@ -1817,7 +1819,10 @@ func (c *compareFunctionClass) getFunction(ctx sessionctx.Context, rawArgs []Exp
18171819
if err = c.verifyArgs(rawArgs); err != nil {
18181820
return nil, err
18191821
}
1820-
args := c.refineArgs(ctx, rawArgs)
1822+
args, err := c.refineArgs(ctx, rawArgs)
1823+
if err != nil {
1824+
return nil, err
1825+
}
18211826
cmpType := GetAccurateCmpType(args[0], args[1])
18221827
sig, err = c.generateCmpSigs(ctx, args, cmpType)
18231828
return sig, err

expression/util.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1485,16 +1485,25 @@ func containMutableConst(ctx sessionctx.Context, exprs []Expression) bool {
14851485
}
14861486

14871487
// RemoveMutableConst used to remove the `ParamMarker` and `DeferredExpr` in the `Constant` expr.
1488-
func RemoveMutableConst(ctx sessionctx.Context, exprs []Expression) {
1488+
func RemoveMutableConst(ctx sessionctx.Context, exprs []Expression) (err error) {
14891489
for _, expr := range exprs {
14901490
switch v := expr.(type) {
14911491
case *Constant:
14921492
v.ParamMarker = nil
1493-
v.DeferredExpr = nil
1493+
if v.DeferredExpr != nil { // evaluate and update v.Value to convert v to a complete immutable constant.
1494+
// TODO: remove or hide DefferedExpr since it's too dangerous (hard to be consistent with v.Value all the time).
1495+
v.Value, err = v.DeferredExpr.Eval(chunk.Row{})
1496+
if err != nil {
1497+
return err
1498+
}
1499+
v.DeferredExpr = nil
1500+
}
1501+
v.DeferredExpr = nil // do nothing since v.Value has already been evaluated in this case.
14941502
case *ScalarFunction:
1495-
RemoveMutableConst(ctx, v.GetArgs())
1503+
return RemoveMutableConst(ctx, v.GetArgs())
14961504
}
14971505
}
1506+
return nil
14981507
}
14991508

15001509
const (

planner/core/expression_rewriter.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1580,7 +1580,10 @@ func (er *expressionRewriter) inToExpression(lLen int, not bool, tp *types.Field
15801580
continue // no need to refine it
15811581
}
15821582
er.sctx.GetSessionVars().StmtCtx.SetSkipPlanCache(errors.Errorf("'%v' may be converted to INT", c.String()))
1583-
expression.RemoveMutableConst(er.sctx, []expression.Expression{c})
1583+
if err := expression.RemoveMutableConst(er.sctx, []expression.Expression{c}); err != nil {
1584+
er.err = err
1585+
return
1586+
}
15841587
}
15851588
args[i], isExceptional = expression.RefineComparedConstant(er.sctx, *leftFt, c, opcode.EQ)
15861589
if isExceptional {

planner/core/plan_cache_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2504,6 +2504,16 @@ func TestNonPreparedPlanCacheBuiltinFuncs(t *testing.T) {
25042504
}
25052505
}
25062506

2507+
func TestIssue48165(t *testing.T) {
2508+
store := testkit.CreateMockStore(t)
2509+
tk := testkit.NewTestKit(t, store)
2510+
tk.MustExec("use test")
2511+
tk.MustExec(`create table t(a int)`)
2512+
tk.MustExec(`insert into t values(1)`)
2513+
tk.MustExec(`prepare s from "select * from t where tidb_parse_tso(a) > unix_timestamp()"`)
2514+
tk.MustQuery(`execute s`).Check(testkit.Rows("1"))
2515+
}
2516+
25072517
func BenchmarkPlanCacheInsert(b *testing.B) {
25082518
store := testkit.CreateMockStore(b)
25092519
tk := testkit.NewTestKit(b, store)

0 commit comments

Comments
 (0)