diff --git a/expression/expression.go b/expression/expression.go index f81dcc919492d..cca14125c5740 100644 --- a/expression/expression.go +++ b/expression/expression.go @@ -131,9 +131,9 @@ type Expression interface { IsCorrelated() bool // ConstItem checks if this expression is constant item, regardless of query evaluation state. - // A constant item can be eval() when build a plan. // An expression is constant item if it: // refers no tables. + // refers no correlated column. // refers no subqueries that refers any tables. // refers no non-deterministic functions. // refers no statement parameters. diff --git a/expression/function_traits.go b/expression/function_traits.go index 9fbb299682c7a..d64b762b752ee 100644 --- a/expression/function_traits.go +++ b/expression/function_traits.go @@ -103,7 +103,8 @@ var IllegalFunctions4GeneratedColumns = map[string]struct{}{ ast.ReleaseAllLocks: {}, } -// DeferredFunctions stores non-deterministic functions, which can be deferred only when the plan cache is enabled. +// DeferredFunctions stores functions which are foldable but should be deferred as well when plan cache is enabled. +// Note that, these functions must be foldable at first place, i.e, they are not in `unFoldableFunctions`. var DeferredFunctions = map[string]struct{}{ ast.Now: {}, ast.RandomBytes: {}, @@ -113,12 +114,9 @@ var DeferredFunctions = map[string]struct{}{ ast.CurrentTime: {}, ast.UTCTimestamp: {}, ast.UnixTimestamp: {}, - ast.Sysdate: {}, ast.Curdate: {}, ast.CurrentDate: {}, ast.UTCDate: {}, - ast.Rand: {}, - ast.UUID: {}, } // inequalFunctions stores functions which cannot be propagated from column equal condition. diff --git a/expression/integration_test.go b/expression/integration_test.go index befe73c3aee44..1a94520d1a0cc 100755 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -5578,6 +5578,28 @@ func (s *testIntegrationSerialSuite) TestCacheRefineArgs(c *C) { tk.MustQuery("execute stmt using @p0").Check(testkit.Rows("0")) } +func (s *testIntegrationSuite) TestOrderByFuncPlanCache(c *C) { + tk := testkit.NewTestKit(c, s.store) + orgEnable := plannercore.PreparedPlanCacheEnabled() + defer func() { + plannercore.SetPreparedPlanCache(orgEnable) + }() + plannercore.SetPreparedPlanCache(true) + var err error + tk.Se, err = session.CreateSession4TestWithOpt(s.store, &session.Opt{ + PreparedPlanCache: kvcache.NewSimpleLRUCache(100, 0.1, math.MaxUint64), + }) + c.Assert(err, IsNil) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int)") + tk.MustExec("prepare stmt from 'SELECT * FROM t order by rand()'") + tk.MustQuery("execute stmt").Check(testkit.Rows()) + tk.MustExec("prepare stmt from 'SELECT * FROM t order by now()'") + tk.MustQuery("execute stmt").Check(testkit.Rows()) +} + func (s *testIntegrationSuite) TestCollation(c *C) { tk := testkit.NewTestKit(c, s.store) tk.MustExec("use test") diff --git a/expression/util.go b/expression/util.go index 41218b1bdb423..455477c59ff01 100644 --- a/expression/util.go +++ b/expression/util.go @@ -748,6 +748,27 @@ func BuildNotNullExpr(ctx sessionctx.Context, expr Expression) Expression { return notNull } +// IsRuntimeConstExpr checks if a expr can be treated as a constant in **executor**. +func IsRuntimeConstExpr(expr Expression) bool { + switch x := expr.(type) { + case *ScalarFunction: + if _, ok := unFoldableFunctions[x.FuncName.L]; ok { + return false + } + for _, arg := range x.GetArgs() { + if !IsRuntimeConstExpr(arg) { + return false + } + } + return true + case *Column: + return false + case *Constant, *CorrelatedColumn: + return true + } + return false +} + // IsMutableEffectsExpr checks if expr contains function which is mutable or has side effects. func IsMutableEffectsExpr(expr Expression) bool { switch x := expr.(type) { diff --git a/planner/core/rule_column_pruning.go b/planner/core/rule_column_pruning.go index 7d39fa96788ff..2ec433bdffa62 100644 --- a/planner/core/rule_column_pruning.go +++ b/planner/core/rule_column_pruning.go @@ -138,7 +138,7 @@ func (ls *LogicalSort) PruneColumns(parentUsedCols []*expression.Column) error { for i := len(ls.ByItems) - 1; i >= 0; i-- { cols := expression.ExtractColumns(ls.ByItems[i].Expr) if len(cols) == 0 { - if expression.IsMutableEffectsExpr(ls.ByItems[i].Expr) { + if !expression.IsRuntimeConstExpr(ls.ByItems[i].Expr) { continue } ls.ByItems = append(ls.ByItems[:i], ls.ByItems[i+1:]...)