Skip to content

Commit 184aa7e

Browse files
authored
planner: fix the issue that plan cache may return wrong result when comparing datetime column with unix_timestamp (#48413)
close #48165
1 parent 92f071a commit 184aa7e

File tree

4 files changed

+41
-14
lines changed

4 files changed

+41
-14
lines changed

pkg/expression/builtin_compare.go

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

15831583
if !allowCmpArgsRefining4PlanCache(ctx, args) {
1584-
return args
1584+
return args, nil
15851585
}
15861586
// We should remove the mutable constant for correctness, because its value may be changed.
1587-
RemoveMutableConst(ctx, args)
1587+
if err := RemoveMutableConst(ctx, args); err != nil {
1588+
return nil, err
1589+
}
15881590

15891591
if arg0IsCon && !arg1IsCon && matchRefineRule3Pattern(arg0EvalType, arg1Type) {
1590-
return c.refineNumericConstantCmpDatetime(ctx, args, arg0, 0)
1592+
return c.refineNumericConstantCmpDatetime(ctx, args, arg0, 0), nil
15911593
}
15921594

15931595
if !arg0IsCon && arg1IsCon && matchRefineRule3Pattern(arg1EvalType, arg0Type) {
1594-
return c.refineNumericConstantCmpDatetime(ctx, args, arg1, 1)
1596+
return c.refineNumericConstantCmpDatetime(ctx, args, arg1, 1), nil
15951597
}
15961598

15971599
// int non-constant [cmp] non-int constant
@@ -1657,24 +1659,24 @@ func (c *compareFunctionClass) refineArgs(ctx sessionctx.Context, args []Express
16571659
}
16581660
if isExceptional && (c.op == opcode.EQ || c.op == opcode.NullEQ) {
16591661
// This will always be false.
1660-
return []Expression{NewZero(), NewOne()}
1662+
return []Expression{NewZero(), NewOne()}, nil
16611663
}
16621664
if isPositiveInfinite {
16631665
// If the op is opcode.LT, opcode.LE
16641666
// This will always be true.
16651667
// If the op is opcode.GT, opcode.GE
16661668
// This will always be false.
1667-
return []Expression{NewZero(), NewOne()}
1669+
return []Expression{NewZero(), NewOne()}, nil
16681670
}
16691671
if isNegativeInfinite {
16701672
// If the op is opcode.GT, opcode.GE
16711673
// This will always be true.
16721674
// If the op is opcode.LT, opcode.LE
16731675
// This will always be false.
1674-
return []Expression{NewOne(), NewZero()}
1676+
return []Expression{NewOne(), NewZero()}, nil
16751677
}
16761678

1677-
return c.refineArgsByUnsignedFlag(ctx, []Expression{finalArg0, finalArg1})
1679+
return c.refineArgsByUnsignedFlag(ctx, []Expression{finalArg0, finalArg1}), nil
16781680
}
16791681

16801682
// see https://github.com/pingcap/tidb/issues/38361 for more details
@@ -1767,7 +1769,10 @@ func (c *compareFunctionClass) getFunction(ctx sessionctx.Context, rawArgs []Exp
17671769
if err = c.verifyArgs(rawArgs); err != nil {
17681770
return nil, err
17691771
}
1770-
args := c.refineArgs(ctx, rawArgs)
1772+
args, err := c.refineArgs(ctx, rawArgs)
1773+
if err != nil {
1774+
return nil, err
1775+
}
17711776
cmpType := GetAccurateCmpType(args[0], args[1])
17721777
sig, err = c.generateCmpSigs(ctx, args, cmpType)
17731778
return sig, err

pkg/expression/util.go

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

14981498
// RemoveMutableConst used to remove the `ParamMarker` and `DeferredExpr` in the `Constant` expr.
1499-
func RemoveMutableConst(ctx sessionctx.Context, exprs []Expression) {
1499+
func RemoveMutableConst(ctx sessionctx.Context, exprs []Expression) (err error) {
15001500
for _, expr := range exprs {
15011501
switch v := expr.(type) {
15021502
case *Constant:
15031503
v.ParamMarker = nil
1504-
v.DeferredExpr = nil
1504+
if v.DeferredExpr != nil { // evaluate and update v.Value to convert v to a complete immutable constant.
1505+
// TODO: remove or hide DefferedExpr since it's too dangerous (hard to be consistent with v.Value all the time).
1506+
v.Value, err = v.DeferredExpr.Eval(chunk.Row{})
1507+
if err != nil {
1508+
return err
1509+
}
1510+
v.DeferredExpr = nil
1511+
}
1512+
v.DeferredExpr = nil // do nothing since v.Value has already been evaluated in this case.
15051513
case *ScalarFunction:
1506-
RemoveMutableConst(ctx, v.GetArgs())
1514+
return RemoveMutableConst(ctx, v.GetArgs())
15071515
}
15081516
}
1517+
return nil
15091518
}
15101519

15111520
const (

pkg/planner/core/expression_rewriter.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1677,7 +1677,10 @@ func (er *expressionRewriter) inToExpression(lLen int, not bool, tp *types.Field
16771677
continue // no need to refine it
16781678
}
16791679
er.sctx.GetSessionVars().StmtCtx.SetSkipPlanCache(errors.Errorf("'%v' may be converted to INT", c.String()))
1680-
expression.RemoveMutableConst(er.sctx, []expression.Expression{c})
1680+
if err := expression.RemoveMutableConst(er.sctx, []expression.Expression{c}); err != nil {
1681+
er.err = err
1682+
return
1683+
}
16811684
}
16821685
args[i], isExceptional = expression.RefineComparedConstant(er.sctx, *leftFt, c, opcode.EQ)
16831686
if isExceptional {

pkg/planner/core/plan_cache_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1437,6 +1437,16 @@ func TestNonPreparedPlanCacheBuiltinFuncs(t *testing.T) {
14371437
}
14381438
}
14391439

1440+
func TestIssue48165(t *testing.T) {
1441+
store := testkit.CreateMockStore(t)
1442+
tk := testkit.NewTestKit(t, store)
1443+
tk.MustExec("use test")
1444+
tk.MustExec(`create table t(a int)`)
1445+
tk.MustExec(`insert into t values(1)`)
1446+
tk.MustExec(`prepare s from "select * from t where tidb_parse_tso(a) > unix_timestamp()"`)
1447+
tk.MustQuery(`execute s`).Check(testkit.Rows("1"))
1448+
}
1449+
14401450
func BenchmarkPlanCacheInsert(b *testing.B) {
14411451
store := testkit.CreateMockStore(b)
14421452
tk := testkit.NewTestKit(b, store)

0 commit comments

Comments
 (0)