From 53057adc9e0d5290a4d2feec4077dbc618ce1aea Mon Sep 17 00:00:00 2001 From: Lingyu Song Date: Wed, 22 Apr 2020 21:58:20 +0800 Subject: [PATCH] cherry pick #16375 to release-3.0 Signed-off-by: sre-bot --- executor/prepared.go | 5 + planner/core/cacheable_checker.go | 49 ++++++++++ planner/core/cacheable_checker_test.go | 122 +++++++++++++++++++------ planner/core/common_plans.go | 18 ++++ 4 files changed, 164 insertions(+), 30 deletions(-) diff --git a/executor/prepared.go b/executor/prepared.go index 20c1d6321ef48..d69d94c2c94e2 100644 --- a/executor/prepared.go +++ b/executor/prepared.go @@ -173,7 +173,12 @@ func (e *PrepareExec) Next(ctx context.Context, req *chunk.Chunk) error { Params: sorter.markers, SchemaVersion: e.is.SchemaMetaVersion(), } +<<<<<<< HEAD prepared.UseCache = plannercore.PreparedPlanCacheEnabled() && (vars.LightningMode || plannercore.Cacheable(stmt)) +======= + + prepared.UseCache = plannercore.PreparedPlanCacheEnabled() && plannercore.Cacheable(stmt, e.is) +>>>>>>> 79211fe... plan: make query on partition table not cacheable (#16375) // We try to build the real statement of preparedStmt. for i := range prepared.Params { diff --git a/planner/core/cacheable_checker.go b/planner/core/cacheable_checker.go index f4ef4f9c22b6d..cf8e640d4bac0 100644 --- a/planner/core/cacheable_checker.go +++ b/planner/core/cacheable_checker.go @@ -16,20 +16,51 @@ package core import ( "github.com/pingcap/parser/ast" "github.com/pingcap/tidb/expression" + "github.com/pingcap/tidb/infoschema" "github.com/pingcap/tidb/types/parser_driver" + "github.com/pingcap/tidb/util/logutil" + "go.uber.org/zap" ) // Cacheable checks whether the input ast is cacheable. +<<<<<<< HEAD func Cacheable(node ast.Node) bool { _, isSelect := node.(*ast.SelectStmt) _, isUpdate := node.(*ast.UpdateStmt) _, isInsert := node.(*ast.InsertStmt) _, isDelete := node.(*ast.DeleteStmt) if !(isSelect || isUpdate || isInsert || isDelete) { +======= +// Handle "ignore_plan_cache()" hint +// If there are multiple hints, only one will take effect +func Cacheable(node ast.Node, is infoschema.InfoSchema) bool { + switch node.(type) { + case *ast.SelectStmt: + for _, hints := range (node.(*ast.SelectStmt)).TableHints { + if hints.HintName.L == HintIgnorePlanCache { + return false + } + } + case *ast.DeleteStmt: + for _, hints := range (node.(*ast.DeleteStmt)).TableHints { + if hints.HintName.L == HintIgnorePlanCache { + return false + } + } + case *ast.UpdateStmt: + for _, hints := range (node.(*ast.UpdateStmt)).TableHints { + if hints.HintName.L == HintIgnorePlanCache { + return false + } + } + case *ast.InsertStmt: + default: +>>>>>>> 79211fe... plan: make query on partition table not cacheable (#16375) return false } checker := cacheableChecker{ cacheable: true, + schema: is, } node.Accept(&checker) return checker.cacheable @@ -42,6 +73,7 @@ func Cacheable(node ast.Node) bool { // NOTE: we can add more rules in the future. type cacheableChecker struct { cacheable bool + schema infoschema.InfoSchema } // Enter implements Visitor interface. @@ -87,10 +119,27 @@ func (checker *cacheableChecker) Enter(in ast.Node) (out ast.Node, skipChildren checker.cacheable = false return in, true } + case *ast.TableName: + if checker.isPartitionTable(node) { + checker.cacheable = false + return in, true + } } return in, false } +func (checker *cacheableChecker) isPartitionTable(tn *ast.TableName) bool { + tb, err := checker.schema.TableByName(tn.Schema, tn.Name) + if err != nil { + logutil.BgLogger().Error("Error occur in checking cacheable", zap.Error(err)) + return false + } + if tb.Meta().Partition != nil { + return true + } + return false +} + // Leave implements Visitor interface. func (checker *cacheableChecker) Leave(in ast.Node) (out ast.Node, ok bool) { return in, checker.cacheable diff --git a/planner/core/cacheable_checker_test.go b/planner/core/cacheable_checker_test.go index 6d195c25f4b27..5caff6f8d6f90 100644 --- a/planner/core/cacheable_checker_test.go +++ b/planner/core/cacheable_checker_test.go @@ -11,14 +11,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -package core +package core_test import ( . "github.com/pingcap/check" "github.com/pingcap/parser/ast" "github.com/pingcap/parser/model" "github.com/pingcap/tidb/expression" + "github.com/pingcap/tidb/infoschema" + "github.com/pingcap/tidb/planner/core" "github.com/pingcap/tidb/types/parser_driver" + "github.com/pingcap/tidb/util/testkit" ) var _ = Suite(&testCacheableSuite{}) @@ -27,20 +30,33 @@ type testCacheableSuite struct { } func (s *testCacheableSuite) TestCacheable(c *C) { + store, dom, err := newStoreWithBootstrap() + c.Assert(err, IsNil) + tk := testkit.NewTestKit(c, store) + defer func() { + dom.Close() + store.Close() + }() + tk.MustExec("use test") + tk.MustExec("create table t1(a int, b int) partition by range(a) ( partition p0 values less than (6), partition p1 values less than (11) )") + tk.MustExec("create table t2(a int, b int) partition by hash(a) partitions 11") + tk.MustExec("create table t3(a int, b int)") + tbl := &ast.TableName{Schema: model.NewCIStr("test"), Name: model.NewCIStr("t3")} + is := infoschema.GetInfoSchema(tk.Se) // test non-SelectStmt/-InsertStmt/-DeleteStmt/-UpdateStmt/-SelectStmt var stmt ast.Node = &ast.UnionStmt{} - c.Assert(Cacheable(stmt), IsFalse) + c.Assert(core.Cacheable(stmt, is), IsFalse) stmt = &ast.ShowStmt{} - c.Assert(Cacheable(stmt), IsFalse) + c.Assert(core.Cacheable(stmt, is), IsFalse) stmt = &ast.LoadDataStmt{} - c.Assert(Cacheable(stmt), IsFalse) + c.Assert(core.Cacheable(stmt, is), IsFalse) - tableRefsClause := &ast.TableRefsClause{TableRefs: &ast.Join{Left: &ast.TableSource{Source: &ast.TableName{}}}} + tableRefsClause := &ast.TableRefsClause{TableRefs: &ast.Join{Left: &ast.TableSource{Source: tbl}}} // test InsertStmt stmt = &ast.InsertStmt{Table: tableRefsClause} - c.Assert(Cacheable(stmt), IsTrue) + c.Assert(core.Cacheable(stmt, is), IsTrue) // test DeleteStmt whereExpr := &ast.FuncCallExpr{} @@ -48,21 +64,21 @@ func (s *testCacheableSuite) TestCacheable(c *C) { TableRefs: tableRefsClause, Where: whereExpr, } - c.Assert(Cacheable(stmt), IsTrue) + c.Assert(core.Cacheable(stmt, is), IsTrue) for funcName := range expression.UnCacheableFunctions { whereExpr.FnName = model.NewCIStr(funcName) - c.Assert(Cacheable(stmt), IsFalse) + c.Assert(core.Cacheable(stmt, is), IsFalse) } whereExpr.FnName = model.NewCIStr(ast.Rand) - c.Assert(Cacheable(stmt), IsTrue) + c.Assert(core.Cacheable(stmt, is), IsTrue) stmt = &ast.DeleteStmt{ TableRefs: tableRefsClause, Where: &ast.ExistsSubqueryExpr{}, } - c.Assert(Cacheable(stmt), IsFalse) + c.Assert(core.Cacheable(stmt, is), IsFalse) limitStmt := &ast.Limit{ Count: &driver.ParamMarkerExpr{}, @@ -71,7 +87,7 @@ func (s *testCacheableSuite) TestCacheable(c *C) { TableRefs: tableRefsClause, Limit: limitStmt, } - c.Assert(Cacheable(stmt), IsFalse) + c.Assert(core.Cacheable(stmt, is), IsFalse) limitStmt = &ast.Limit{ Offset: &driver.ParamMarkerExpr{}, @@ -80,36 +96,44 @@ func (s *testCacheableSuite) TestCacheable(c *C) { TableRefs: tableRefsClause, Limit: limitStmt, } - c.Assert(Cacheable(stmt), IsFalse) + c.Assert(core.Cacheable(stmt, is), IsFalse) limitStmt = &ast.Limit{} stmt = &ast.DeleteStmt{ TableRefs: tableRefsClause, Limit: limitStmt, } - c.Assert(Cacheable(stmt), IsTrue) + c.Assert(core.Cacheable(stmt, is), IsTrue) +<<<<<<< HEAD +======= + stmt.(*ast.DeleteStmt).TableHints = append(stmt.(*ast.DeleteStmt).TableHints, &ast.TableOptimizerHint{ + HintName: model.NewCIStr(core.HintIgnorePlanCache), + }) + c.Assert(core.Cacheable(stmt, is), IsFalse) + +>>>>>>> 79211fe... plan: make query on partition table not cacheable (#16375) // test UpdateStmt whereExpr = &ast.FuncCallExpr{} stmt = &ast.UpdateStmt{ TableRefs: tableRefsClause, Where: whereExpr, } - c.Assert(Cacheable(stmt), IsTrue) + c.Assert(core.Cacheable(stmt, is), IsTrue) for funcName := range expression.UnCacheableFunctions { whereExpr.FnName = model.NewCIStr(funcName) - c.Assert(Cacheable(stmt), IsFalse) + c.Assert(core.Cacheable(stmt, is), IsFalse) } whereExpr.FnName = model.NewCIStr(ast.Rand) - c.Assert(Cacheable(stmt), IsTrue) + c.Assert(core.Cacheable(stmt, is), IsTrue) stmt = &ast.UpdateStmt{ TableRefs: tableRefsClause, Where: &ast.ExistsSubqueryExpr{}, } - c.Assert(Cacheable(stmt), IsFalse) + c.Assert(core.Cacheable(stmt, is), IsFalse) limitStmt = &ast.Limit{ Count: &driver.ParamMarkerExpr{}, @@ -118,7 +142,7 @@ func (s *testCacheableSuite) TestCacheable(c *C) { TableRefs: tableRefsClause, Limit: limitStmt, } - c.Assert(Cacheable(stmt), IsFalse) + c.Assert(core.Cacheable(stmt, is), IsFalse) limitStmt = &ast.Limit{ Offset: &driver.ParamMarkerExpr{}, @@ -127,34 +151,42 @@ func (s *testCacheableSuite) TestCacheable(c *C) { TableRefs: tableRefsClause, Limit: limitStmt, } - c.Assert(Cacheable(stmt), IsFalse) + c.Assert(core.Cacheable(stmt, is), IsFalse) limitStmt = &ast.Limit{} stmt = &ast.UpdateStmt{ TableRefs: tableRefsClause, Limit: limitStmt, } - c.Assert(Cacheable(stmt), IsTrue) + c.Assert(core.Cacheable(stmt, is), IsTrue) + +<<<<<<< HEAD +======= + stmt.(*ast.UpdateStmt).TableHints = append(stmt.(*ast.UpdateStmt).TableHints, &ast.TableOptimizerHint{ + HintName: model.NewCIStr(core.HintIgnorePlanCache), + }) + c.Assert(core.Cacheable(stmt, is), IsFalse) +>>>>>>> 79211fe... plan: make query on partition table not cacheable (#16375) // test SelectStmt whereExpr = &ast.FuncCallExpr{} stmt = &ast.SelectStmt{ Where: whereExpr, } - c.Assert(Cacheable(stmt), IsTrue) + c.Assert(core.Cacheable(stmt, is), IsTrue) for funcName := range expression.UnCacheableFunctions { whereExpr.FnName = model.NewCIStr(funcName) - c.Assert(Cacheable(stmt), IsFalse) + c.Assert(core.Cacheable(stmt, is), IsFalse) } whereExpr.FnName = model.NewCIStr(ast.Rand) - c.Assert(Cacheable(stmt), IsTrue) + c.Assert(core.Cacheable(stmt, is), IsTrue) stmt = &ast.SelectStmt{ Where: &ast.ExistsSubqueryExpr{}, } - c.Assert(Cacheable(stmt), IsFalse) + c.Assert(core.Cacheable(stmt, is), IsFalse) limitStmt = &ast.Limit{ Count: &driver.ParamMarkerExpr{}, @@ -162,7 +194,7 @@ func (s *testCacheableSuite) TestCacheable(c *C) { stmt = &ast.SelectStmt{ Limit: limitStmt, } - c.Assert(Cacheable(stmt), IsFalse) + c.Assert(core.Cacheable(stmt, is), IsFalse) limitStmt = &ast.Limit{ Offset: &driver.ParamMarkerExpr{}, @@ -170,28 +202,58 @@ func (s *testCacheableSuite) TestCacheable(c *C) { stmt = &ast.SelectStmt{ Limit: limitStmt, } - c.Assert(Cacheable(stmt), IsFalse) + c.Assert(core.Cacheable(stmt, is), IsFalse) limitStmt = &ast.Limit{} stmt = &ast.SelectStmt{ Limit: limitStmt, } - c.Assert(Cacheable(stmt), IsTrue) + c.Assert(core.Cacheable(stmt, is), IsTrue) paramExpr := &driver.ParamMarkerExpr{} orderByClause := &ast.OrderByClause{Items: []*ast.ByItem{{Expr: paramExpr}}} stmt = &ast.SelectStmt{ OrderBy: orderByClause, } - c.Assert(Cacheable(stmt), IsFalse) + c.Assert(core.Cacheable(stmt, is), IsFalse) valExpr := &driver.ValueExpr{} orderByClause = &ast.OrderByClause{Items: []*ast.ByItem{{Expr: valExpr}}} stmt = &ast.SelectStmt{ OrderBy: orderByClause, } - c.Assert(Cacheable(stmt), IsTrue) + c.Assert(core.Cacheable(stmt, is), IsTrue) + +<<<<<<< HEAD +======= + stmt.(*ast.SelectStmt).TableHints = append(stmt.(*ast.SelectStmt).TableHints, &ast.TableOptimizerHint{ + HintName: model.NewCIStr(core.HintIgnorePlanCache), + }) + c.Assert(core.Cacheable(stmt, is), IsFalse) +>>>>>>> 79211fe... plan: make query on partition table not cacheable (#16375) boundExpr := &ast.FrameBound{Expr: &driver.ParamMarkerExpr{}} - c.Assert(Cacheable(boundExpr), IsFalse) + c.Assert(core.Cacheable(boundExpr, is), IsFalse) + + // Partition table can not be cached. + join := &ast.Join{ + Left: &ast.TableName{Schema: model.NewCIStr("test"), Name: model.NewCIStr("t1")}, + Right: &ast.TableName{Schema: model.NewCIStr("test"), Name: model.NewCIStr("t2")}, + } + stmt = &ast.SelectStmt{ + From: &ast.TableRefsClause{ + TableRefs: join, + }, + } + c.Assert(core.Cacheable(stmt, is), IsFalse) + + join = &ast.Join{ + Left: &ast.TableName{Schema: model.NewCIStr("test"), Name: model.NewCIStr("t3")}, + } + stmt = &ast.SelectStmt{ + From: &ast.TableRefsClause{ + TableRefs: join, + }, + } + c.Assert(core.Cacheable(stmt, is), IsTrue) } diff --git a/planner/core/common_plans.go b/planner/core/common_plans.go index 4283cf0a26665..901ac9c9264ba 100644 --- a/planner/core/common_plans.go +++ b/planner/core/common_plans.go @@ -252,10 +252,25 @@ func (e *Execute) getPhysicalPlan(ctx context.Context, sctx sessionctx.Context, if err != nil { return nil, err } +<<<<<<< HEAD _, isTableDual := p.(*PhysicalTableDual) isPartition := e.isPartition(p) if !isTableDual && prepared.UseCache && !isPartition { sctx.PreparedPlanCache().Put(cacheKey, NewPSTMTPlanCacheValue(p)) +======= + e.names = names + e.Plan = p + _, isTableDual := p.(*PhysicalTableDual) + if !isTableDual && prepared.UseCache { + err = e.setFoundInPlanCache(sctx, true) + if err != nil { + return err + } + cached := NewPSTMTPlanCacheValue(p, names, stmtCtx.TblInfo2UnionScan) + preparedStmt.NormalizedPlan, preparedStmt.PlanDigest = NormalizePlan(p) + stmtCtx.SetPlanDigest(preparedStmt.NormalizedPlan, preparedStmt.PlanDigest) + sctx.PreparedPlanCache().Put(cacheKey, cached) +>>>>>>> 79211fe... plan: make query on partition table not cacheable (#16375) } return p, err } @@ -330,6 +345,7 @@ func (e *Execute) rebuildRange(p Plan) error { return nil } +<<<<<<< HEAD func checkPartitionInfo(pi *model.PartitionInfo) bool { if pi != nil { return true @@ -360,6 +376,8 @@ func (e *Execute) isPartition(p Plan) bool { return isRange } +======= +>>>>>>> 79211fe... plan: make query on partition table not cacheable (#16375) func (e *Execute) buildRangeForIndexScan(sctx sessionctx.Context, is *PhysicalIndexScan) ([]*ranger.Range, error) { idxCols, colLengths := expression.IndexInfo2Cols(is.schema.Columns, is.Index) if len(idxCols) == 0 {