Skip to content

Commit

Permalink
cherry pick pingcap#16375 to release-3.0
Browse files Browse the repository at this point in the history
Signed-off-by: sre-bot <sre-bot@pingcap.com>
  • Loading branch information
Lingyu Song authored and sre-bot committed Apr 23, 2020
1 parent 445e69b commit 53057ad
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 30 deletions.
5 changes: 5 additions & 0 deletions executor/prepared.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
49 changes: 49 additions & 0 deletions planner/core/cacheable_checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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
Expand Down
122 changes: 92 additions & 30 deletions planner/core/cacheable_checker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{})
Expand All @@ -27,42 +30,55 @@ 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{}
stmt = &ast.DeleteStmt{
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{},
Expand All @@ -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{},
Expand All @@ -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{},
Expand All @@ -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{},
Expand All @@ -127,71 +151,109 @@ 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{},
}
stmt = &ast.SelectStmt{
Limit: limitStmt,
}
c.Assert(Cacheable(stmt), IsFalse)
c.Assert(core.Cacheable(stmt, is), IsFalse)

limitStmt = &ast.Limit{
Offset: &driver.ParamMarkerExpr{},
}
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)
}
Loading

0 comments on commit 53057ad

Please sign in to comment.