From e2a05cb39417714f4d61fd1270d9e4b6b1261e2d Mon Sep 17 00:00:00 2001 From: Kenan Yao Date: Tue, 21 Apr 2020 20:04:23 +0800 Subject: [PATCH] =?UTF-8?q?expression:=20avoid=20`Order=20By`=20constant?= =?UTF-8?q?=20expression=20error=20when=20pl=E2=80=A6=20(#16261)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- expression/expression.go | 2 +- expression/function_traits.go | 6 ++---- expression/integration_test.go | 22 ++++++++++++++++++++++ expression/util.go | 21 +++++++++++++++++++++ planner/core/rule_column_pruning.go | 2 +- 5 files changed, 47 insertions(+), 6 deletions(-) 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 528414ba3b373..f1f00ba4aa8a2 100644 --- a/expression/function_traits.go +++ b/expression/function_traits.go @@ -102,7 +102,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: {}, @@ -112,12 +113,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 6469f26b1b21f..55e2b81f28cb7 100755 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -5544,6 +5544,28 @@ func (s *testIntegrationSuite) 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 c35f17a6a0cdf..0f27ba8a697d6 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:]...)