From 5300a32ef194696b7c5e99d9acc654f99412a3fd Mon Sep 17 00:00:00 2001 From: Lingyu Song Date: Thu, 26 Mar 2020 22:58:09 +0800 Subject: [PATCH] plan: do not cache plan for query on range partition table (#15697) --- planner/core/common_plans.go | 39 ++++++++++++++++++++++++++++---- planner/core/prepare_test.go | 44 +++++++++++++++++++++++++++++------- 2 files changed, 71 insertions(+), 12 deletions(-) diff --git a/planner/core/common_plans.go b/planner/core/common_plans.go index 5d179f24fa1c9..03f540f8f6faa 100644 --- a/planner/core/common_plans.go +++ b/planner/core/common_plans.go @@ -320,8 +320,9 @@ func (e *Execute) getPhysicalPlan(ctx context.Context, sctx sessionctx.Context, } e.names = names e.Plan = p + isRange := e.isRangePartition(p) _, isTableDual := p.(*PhysicalTableDual) - if !isTableDual && prepared.UseCache { + if !isTableDual && prepared.UseCache && !isRange { cached := NewPSTMTPlanCacheValue(p, names) preparedStmt.NormalizedPlan, preparedStmt.PlanDigest = NormalizePlan(p) stmtCtx.SetPlanDigest(preparedStmt.NormalizedPlan, preparedStmt.PlanDigest) @@ -386,7 +387,7 @@ func (e *Execute) rebuildRange(p Plan) error { if err != nil { return err } - if ts.Table.Partition != nil { + if ts.Table.Partition != nil && ts.Table.Partition.Type == model.PartitionTypeHash { pID, err := rebuildNewTableIDFromTable(e.ctx, ts, sc, pkCol) if err != nil { return err @@ -404,7 +405,7 @@ func (e *Execute) rebuildRange(p Plan) error { if err != nil { return err } - if is.Table.Partition != nil { + if is.Table.Partition != nil && is.Table.Partition.Type == model.PartitionTypeHash { pID, err := rebuildNewTableIDFromIndex(e.ctx, is, sc) if err != nil { return err @@ -419,7 +420,7 @@ func (e *Execute) rebuildRange(p Plan) error { if err != nil { return err } - if is.Table.Partition != nil { + if is.Table.Partition != nil && is.Table.Partition.Type == model.PartitionTypeHash { pID, err := rebuildNewTableIDFromIndex(e.ctx, is, sc) if err != nil { return err @@ -496,6 +497,36 @@ func (e *Execute) rebuildRange(p Plan) error { return nil } +func checkRangePartitionInfo(pi *model.PartitionInfo) bool { + if pi != nil && pi.Type == model.PartitionTypeRange { + return true + } + return false +} + +// Prepare plan cache is not support query plan on range partition table. +func (e *Execute) isRangePartition(p Plan) bool { + isRange := false + switch x := p.(type) { + case *PhysicalTableReader: + ts := x.TablePlans[0].(*PhysicalTableScan) + return checkRangePartitionInfo(ts.Table.Partition) + case *PhysicalIndexLookUpReader: + is := x.IndexPlans[0].(*PhysicalIndexScan) + return checkRangePartitionInfo(is.Table.Partition) + case *PhysicalIndexReader: + is := x.IndexPlans[0].(*PhysicalIndexScan) + return checkRangePartitionInfo(is.Table.Partition) + case PhysicalPlan: + for _, child := range x.Children() { + if e.isRangePartition(child) { + isRange = true + } + } + } + return isRange +} + func (e *Execute) buildRangeForIndexScan(sctx sessionctx.Context, is *PhysicalIndexScan) ([]*ranger.Range, error) { if len(is.IdxCols) == 0 { return ranger.FullRange(), nil diff --git a/planner/core/prepare_test.go b/planner/core/prepare_test.go index 94e07f5c629ae..17202584c5f83 100644 --- a/planner/core/prepare_test.go +++ b/planner/core/prepare_test.go @@ -431,19 +431,47 @@ func (s *testPrepareSuite) TestPrepareCacheForPartition(c *C) { tk.MustExec("drop table if exists t_table_read") tk.MustExec("create table t_table_read (id int, k int, c varchar(10), primary key(id)) partition by hash(id) partitions 10") tk.MustExec("insert into t_table_read values (1, 2, 'abc'), (3, 4, 'def'), (5, 6, 'xyz')") - tk.MustExec("prepare stmt1 from 'select c from t_index_read where id = ?;'") + tk.MustExec("prepare stmt3 from 'select c from t_index_read where id = ?;'") tk.MustExec("set @id=1") // When executing one statement at the first time, we don't use cache, so we need to execute it at least twice to test the cache. - tk.MustQuery("execute stmt1 using @id").Check(testkit.Rows("abc")) - tk.MustQuery("execute stmt1 using @id").Check(testkit.Rows("abc")) + tk.MustQuery("execute stmt3 using @id").Check(testkit.Rows("abc")) + tk.MustQuery("execute stmt3 using @id").Check(testkit.Rows("abc")) tk.MustExec("set @id=5") - tk.MustQuery("execute stmt1 using @id").Check(testkit.Rows("xyz")) - tk.MustExec("prepare stmt2 from 'select c from t_index_read where id = ? and k = ?'") + tk.MustQuery("execute stmt3 using @id").Check(testkit.Rows("xyz")) + tk.MustExec("prepare stmt4 from 'select c from t_index_read where id = ? and k = ?'") tk.MustExec("set @id=1, @k=2") - tk.MustQuery("execute stmt2 using @id, @k").Check(testkit.Rows("abc")) - tk.MustQuery("execute stmt2 using @id, @k").Check(testkit.Rows("abc")) + tk.MustQuery("execute stmt4 using @id, @k").Check(testkit.Rows("abc")) + tk.MustQuery("execute stmt4 using @id, @k").Check(testkit.Rows("abc")) tk.MustExec("set @id=5, @k=6") - tk.MustQuery("execute stmt2 using @id, @k").Check(testkit.Rows("xyz")) + tk.MustQuery("execute stmt4 using @id, @k").Check(testkit.Rows("xyz")) + // Query on range partition tables should not raise error. + tk.MustExec("create table t_range_index (id int, k int, c varchar(10), primary key(id)) partition by range(id) ( PARTITION p0 VALUES LESS THAN (4), PARTITION p1 VALUES LESS THAN (14),PARTITION p2 VALUES LESS THAN (20) )") + tk.MustExec("insert into t_range_index values (1, 2, 'abc'), (5, 4, 'def'), (13, 6, 'xyz'), (17, 6, 'hij')") + tk.MustExec("prepare stmt5 from 'select c from t_range_index where id = ?'") + tk.MustExec("set @id=1") + tk.MustQuery("execute stmt5 using @id").Check(testkit.Rows("abc")) + tk.MustQuery("execute stmt5 using @id").Check(testkit.Rows("abc")) + tk.MustExec("set @id=5") + tk.MustQuery("execute stmt5 using @id").Check(testkit.Rows("def")) + tk.MustQuery("execute stmt5 using @id").Check(testkit.Rows("def")) + tk.MustExec("set @id=13") + tk.MustQuery("execute stmt5 using @id").Check(testkit.Rows("xyz")) + tk.MustExec("set @id=17") + tk.MustQuery("execute stmt5 using @id").Check(testkit.Rows("hij")) + + tk.MustExec("create table t_range_table (id int, k int, c varchar(10)) partition by range(id) ( PARTITION p0 VALUES LESS THAN (4), PARTITION p1 VALUES LESS THAN (14),PARTITION p2 VALUES LESS THAN (20) )") + tk.MustExec("insert into t_range_table values (1, 2, 'abc'), (5, 4, 'def'), (13, 6, 'xyz'), (17, 6, 'hij')") + tk.MustExec("prepare stmt6 from 'select c from t_range_table where id = ?'") + tk.MustExec("set @id=1") + tk.MustQuery("execute stmt6 using @id").Check(testkit.Rows("abc")) + tk.MustQuery("execute stmt6 using @id").Check(testkit.Rows("abc")) + tk.MustExec("set @id=5") + tk.MustQuery("execute stmt6 using @id").Check(testkit.Rows("def")) + tk.MustQuery("execute stmt6 using @id").Check(testkit.Rows("def")) + tk.MustExec("set @id=13") + tk.MustQuery("execute stmt6 using @id").Check(testkit.Rows("xyz")) + tk.MustExec("set @id=17") + tk.MustQuery("execute stmt6 using @id").Check(testkit.Rows("hij")) } func newSession(c *C, store kv.Storage, dbName string) session.Session {