planner: generate BatchPointGet for hash table partitions (#23733)
This commit is contained in:
@ -49,6 +49,8 @@ type BatchPointGetExec struct {
|
||||
handles []kv.Handle
|
||||
physIDs []int64
|
||||
partPos int
|
||||
singlePart bool
|
||||
partTblID int64
|
||||
idxVals [][]types.Datum
|
||||
startTS uint64
|
||||
snapshotTS uint64
|
||||
@ -211,6 +213,10 @@ func (e *BatchPointGetExec) initialize(ctx context.Context) error {
|
||||
}
|
||||
|
||||
physID := getPhysID(e.tblInfo, idxVals[e.partPos].GetInt64())
|
||||
// If this BatchPointGetExec is built only for the specific table partition, skip those filters not matching this partition.
|
||||
if e.singlePart && e.partTblID != physID {
|
||||
continue
|
||||
}
|
||||
idxKey, err1 := EncodeUniqueIndexKey(e.ctx, e.tblInfo, e.idxInfo, idxVals, physID)
|
||||
if err1 != nil && !kv.ErrNotExist.Equal(err1) {
|
||||
return err1
|
||||
@ -326,7 +332,8 @@ func (e *BatchPointGetExec) initialize(ctx context.Context) error {
|
||||
sort.Slice(e.handles, less)
|
||||
}
|
||||
|
||||
keys := make([]kv.Key, len(e.handles))
|
||||
keys := make([]kv.Key, 0, len(e.handles))
|
||||
newHandles := make([]kv.Handle, 0, len(e.handles))
|
||||
for i, handle := range e.handles {
|
||||
var tID int64
|
||||
if len(e.physIDs) > 0 {
|
||||
@ -342,9 +349,15 @@ func (e *BatchPointGetExec) initialize(ctx context.Context) error {
|
||||
tID = getPhysID(e.tblInfo, d.GetInt64())
|
||||
}
|
||||
}
|
||||
// If this BatchPointGetExec is built only for the specific table partition, skip those handles not matching this partition.
|
||||
if e.singlePart && e.partTblID != tID {
|
||||
continue
|
||||
}
|
||||
key := tablecodec.EncodeRowKeyWithHandle(tID, handle)
|
||||
keys[i] = key
|
||||
keys = append(keys, key)
|
||||
newHandles = append(newHandles, handle)
|
||||
}
|
||||
e.handles = newHandles
|
||||
|
||||
var values map[string][]byte
|
||||
// Lock keys (include exists and non-exists keys) before fetch all values for Repeatable Read Isolation.
|
||||
|
||||
@ -3901,6 +3901,8 @@ func (b *executorBuilder) buildBatchPointGet(plan *plannercore.BatchPointGetPlan
|
||||
lock: plan.Lock,
|
||||
waitTime: plan.LockWaitTime,
|
||||
partPos: plan.PartitionColPos,
|
||||
singlePart: plan.SinglePart,
|
||||
partTblID: plan.PartTblID,
|
||||
columns: plan.Columns,
|
||||
}
|
||||
if e.lock {
|
||||
|
||||
@ -251,30 +251,3 @@ func (s *globalIndexSuite) TestIssue21731(c *C) {
|
||||
tk.MustExec("drop table if exists p, t")
|
||||
tk.MustExec("create table t (a int, b int, unique index idx(a)) partition by list columns(b) (partition p0 values in (1), partition p1 values in (2));")
|
||||
}
|
||||
|
||||
func (s *globalIndexSuite) TestBatchPointGetTablePartition(c *C) {
|
||||
testKit := testkit.NewTestKitWithInit(c, s.store)
|
||||
testKit.MustExec("use test")
|
||||
testKit.MustExec("drop table if exists t")
|
||||
testKit.MustExec("create table t(a int, b int, primary key(a,b)) partition by hash(b) partitions 2")
|
||||
testKit.MustExec("insert into t values(1,1),(1,2),(2,1),(2,2)")
|
||||
|
||||
testKit.MustExec("set @@tidb_partition_prune_mode = 'static'")
|
||||
testKit.MustQuery("select * from t where a in (1,2) and b = 1").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"2 1",
|
||||
))
|
||||
testKit.MustQuery("select * from t where a = 1 and b in (1,2)").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"1 2",
|
||||
))
|
||||
testKit.MustExec("set @@tidb_partition_prune_mode = 'dynamic'")
|
||||
testKit.MustQuery("select * from t where a in (1,2) and b = 1").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"2 1",
|
||||
))
|
||||
testKit.MustQuery("select * from t where a = 1 and b in (1,2)").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"1 2",
|
||||
))
|
||||
}
|
||||
|
||||
@ -996,3 +996,248 @@ func (s *testAnalyzeSuite) TestLimitIndexEstimation(c *C) {
|
||||
tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *testAnalyzeSuite) TestBatchPointGetTablePartition(c *C) {
|
||||
store, dom, err := newStoreWithBootstrap()
|
||||
c.Assert(err, IsNil)
|
||||
testKit := testkit.NewTestKit(c, store)
|
||||
defer func() {
|
||||
dom.Close()
|
||||
store.Close()
|
||||
}()
|
||||
testKit.MustExec("use test")
|
||||
testKit.MustExec("drop table if exists t1,t2,t3,t4,t5,t6")
|
||||
|
||||
testKit.MustExec("create table t1(a int, b int, primary key(a,b)) partition by hash(b) partitions 2")
|
||||
testKit.MustExec("insert into t1 values(1,1),(1,2),(2,1),(2,2)")
|
||||
testKit.MustExec("set @@tidb_partition_prune_mode = 'static'")
|
||||
testKit.MustQuery("explain format = 'brief' select * from t1 where a in (1,2) and b = 1").Check(testkit.Rows(
|
||||
"Batch_Point_Get 2.00 root table:t1, index:PRIMARY(a, b) keep order:false, desc:false",
|
||||
))
|
||||
testKit.MustQuery("select * from t1 where a in (1,2) and b = 1").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"2 1",
|
||||
))
|
||||
testKit.MustQuery("explain format = 'brief' select * from t1 where a = 1 and b in (1,2)").Check(testkit.Rows(
|
||||
"PartitionUnion 4.00 root ",
|
||||
"├─Batch_Point_Get 2.00 root table:t1, index:PRIMARY(a, b) keep order:false, desc:false",
|
||||
"└─Batch_Point_Get 2.00 root table:t1, index:PRIMARY(a, b) keep order:false, desc:false",
|
||||
))
|
||||
testKit.MustQuery("select * from t1 where a = 1 and b in (1,2)").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"1 2",
|
||||
))
|
||||
testKit.MustExec("set @@tidb_partition_prune_mode = 'dynamic'")
|
||||
testKit.MustQuery("explain format = 'brief' select * from t1 where a in (1,2) and b = 1").Check(testkit.Rows(
|
||||
"IndexReader 2.00 root partition:p1 index:IndexRangeScan",
|
||||
"└─IndexRangeScan 2.00 cop[tikv] table:t1, index:PRIMARY(a, b) range:[1 1,1 1], [2 1,2 1], keep order:false, stats:pseudo",
|
||||
))
|
||||
testKit.MustQuery("select * from t1 where a in (1,2) and b = 1").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"2 1",
|
||||
))
|
||||
testKit.MustQuery("explain format = 'brief' select * from t1 where a = 1 and b in (1,2)").Check(testkit.Rows(
|
||||
"IndexReader 2.00 root partition:p0,p1 index:IndexRangeScan",
|
||||
"└─IndexRangeScan 2.00 cop[tikv] table:t1, index:PRIMARY(a, b) range:[1 1,1 1], [1 2,1 2], keep order:false, stats:pseudo",
|
||||
))
|
||||
testKit.MustQuery("select * from t1 where a = 1 and b in (1,2)").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"1 2",
|
||||
))
|
||||
|
||||
testKit.MustExec("create table t2(a int, b int, primary key(a,b)) partition by range(b) (partition p0 values less than (2), partition p1 values less than maxvalue)")
|
||||
testKit.MustExec("insert into t2 values(1,1),(1,2),(2,1),(2,2)")
|
||||
testKit.MustExec("set @@tidb_partition_prune_mode = 'static'")
|
||||
testKit.MustQuery("explain format = 'brief' select * from t2 where a in (1,2) and b = 1").Check(testkit.Rows(
|
||||
"IndexReader 2.00 root index:IndexRangeScan",
|
||||
"└─IndexRangeScan 2.00 cop[tikv] table:t2, partition:p0, index:PRIMARY(a, b) range:[1 1,1 1], [2 1,2 1], keep order:false, stats:pseudo",
|
||||
))
|
||||
testKit.MustQuery("select * from t2 where a in (1,2) and b = 1").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"2 1",
|
||||
))
|
||||
testKit.MustQuery("explain format = 'brief' select * from t2 where a = 1 and b in (1,2)").Check(testkit.Rows(
|
||||
"PartitionUnion 4.00 root ",
|
||||
"├─IndexReader 2.00 root index:IndexRangeScan",
|
||||
"│ └─IndexRangeScan 2.00 cop[tikv] table:t2, partition:p0, index:PRIMARY(a, b) range:[1 1,1 1], [1 2,1 2], keep order:false, stats:pseudo",
|
||||
"└─IndexReader 2.00 root index:IndexRangeScan",
|
||||
" └─IndexRangeScan 2.00 cop[tikv] table:t2, partition:p1, index:PRIMARY(a, b) range:[1 1,1 1], [1 2,1 2], keep order:false, stats:pseudo",
|
||||
))
|
||||
testKit.MustQuery("select * from t2 where a = 1 and b in (1,2)").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"1 2",
|
||||
))
|
||||
testKit.MustExec("set @@tidb_partition_prune_mode = 'dynamic'")
|
||||
testKit.MustQuery("explain format = 'brief' select * from t2 where a in (1,2) and b = 1").Check(testkit.Rows(
|
||||
"IndexReader 2.00 root partition:p0 index:IndexRangeScan",
|
||||
"└─IndexRangeScan 2.00 cop[tikv] table:t2, index:PRIMARY(a, b) range:[1 1,1 1], [2 1,2 1], keep order:false, stats:pseudo",
|
||||
))
|
||||
testKit.MustQuery("select * from t2 where a in (1,2) and b = 1").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"2 1",
|
||||
))
|
||||
testKit.MustQuery("explain format = 'brief' select * from t2 where a = 1 and b in (1,2)").Check(testkit.Rows(
|
||||
"IndexReader 2.00 root partition:all index:IndexRangeScan",
|
||||
"└─IndexRangeScan 2.00 cop[tikv] table:t2, index:PRIMARY(a, b) range:[1 1,1 1], [1 2,1 2], keep order:false, stats:pseudo",
|
||||
))
|
||||
testKit.MustQuery("select * from t2 where a = 1 and b in (1,2)").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"1 2",
|
||||
))
|
||||
|
||||
testKit.Se.GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeOn
|
||||
|
||||
testKit.MustExec("create table t3(a int, b int, primary key(a,b)) partition by hash(b) partitions 2")
|
||||
testKit.MustExec("insert into t3 values(1,1),(1,2),(2,1),(2,2)")
|
||||
testKit.MustExec("set @@tidb_partition_prune_mode = 'static'")
|
||||
testKit.MustQuery("explain format = 'brief' select * from t3 where a in (1,2) and b = 1").Check(testkit.Rows(
|
||||
"Batch_Point_Get 2.00 root table:t3, clustered index:PRIMARY(a, b) keep order:false, desc:false",
|
||||
))
|
||||
testKit.MustQuery("select * from t3 where a in (1,2) and b = 1").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"2 1",
|
||||
))
|
||||
testKit.MustQuery("explain format = 'brief' select * from t3 where a = 1 and b in (1,2)").Check(testkit.Rows(
|
||||
"PartitionUnion 4.00 root ",
|
||||
"├─Batch_Point_Get 2.00 root table:t3, clustered index:PRIMARY(a, b) keep order:false, desc:false",
|
||||
"└─Batch_Point_Get 2.00 root table:t3, clustered index:PRIMARY(a, b) keep order:false, desc:false",
|
||||
))
|
||||
testKit.MustQuery("select * from t3 where a = 1 and b in (1,2)").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"1 2",
|
||||
))
|
||||
testKit.MustExec("set @@tidb_partition_prune_mode = 'dynamic'")
|
||||
testKit.MustQuery("explain format = 'brief' select * from t3 where a in (1,2) and b = 1").Check(testkit.Rows(
|
||||
"TableReader 2.00 root partition:p1 data:TableRangeScan",
|
||||
"└─TableRangeScan 2.00 cop[tikv] table:t3 range:[1 1,1 1], [2 1,2 1], keep order:false, stats:pseudo",
|
||||
))
|
||||
testKit.MustQuery("select * from t3 where a in (1,2) and b = 1").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"2 1",
|
||||
))
|
||||
testKit.MustQuery("explain format = 'brief' select * from t3 where a = 1 and b in (1,2)").Check(testkit.Rows(
|
||||
"TableReader 2.00 root partition:p0,p1 data:TableRangeScan",
|
||||
"└─TableRangeScan 2.00 cop[tikv] table:t3 range:[1 1,1 1], [1 2,1 2], keep order:false, stats:pseudo",
|
||||
))
|
||||
testKit.MustQuery("select * from t3 where a = 1 and b in (1,2)").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"1 2",
|
||||
))
|
||||
|
||||
testKit.MustExec("create table t4(a int, b int, primary key(a,b)) partition by range(b) (partition p0 values less than (2), partition p1 values less than maxvalue)")
|
||||
testKit.MustExec("insert into t4 values(1,1),(1,2),(2,1),(2,2)")
|
||||
testKit.MustExec("set @@tidb_partition_prune_mode = 'static'")
|
||||
testKit.MustQuery("explain format = 'brief' select * from t4 where a in (1,2) and b = 1").Check(testkit.Rows(
|
||||
"TableReader 2.00 root data:TableRangeScan",
|
||||
"└─TableRangeScan 2.00 cop[tikv] table:t4, partition:p0 range:[1 1,1 1], [2 1,2 1], keep order:false, stats:pseudo",
|
||||
))
|
||||
testKit.MustQuery("select * from t4 where a in (1,2) and b = 1").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"2 1",
|
||||
))
|
||||
testKit.MustQuery("explain format = 'brief' select * from t4 where a = 1 and b in (1,2)").Check(testkit.Rows(
|
||||
"PartitionUnion 4.00 root ",
|
||||
"├─TableReader 2.00 root data:TableRangeScan",
|
||||
"│ └─TableRangeScan 2.00 cop[tikv] table:t4, partition:p0 range:[1 1,1 1], [1 2,1 2], keep order:false, stats:pseudo",
|
||||
"└─TableReader 2.00 root data:TableRangeScan",
|
||||
" └─TableRangeScan 2.00 cop[tikv] table:t4, partition:p1 range:[1 1,1 1], [1 2,1 2], keep order:false, stats:pseudo",
|
||||
))
|
||||
testKit.MustQuery("select * from t4 where a = 1 and b in (1,2)").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"1 2",
|
||||
))
|
||||
testKit.MustExec("set @@tidb_partition_prune_mode = 'dynamic'")
|
||||
testKit.MustQuery("explain format = 'brief' select * from t4 where a in (1,2) and b = 1").Check(testkit.Rows(
|
||||
"TableReader 2.00 root partition:p0 data:TableRangeScan",
|
||||
"└─TableRangeScan 2.00 cop[tikv] table:t4 range:[1 1,1 1], [2 1,2 1], keep order:false, stats:pseudo",
|
||||
))
|
||||
testKit.MustQuery("select * from t4 where a in (1,2) and b = 1").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"2 1",
|
||||
))
|
||||
testKit.MustQuery("explain format = 'brief' select * from t4 where a = 1 and b in (1,2)").Check(testkit.Rows(
|
||||
"TableReader 2.00 root partition:all data:TableRangeScan",
|
||||
"└─TableRangeScan 2.00 cop[tikv] table:t4 range:[1 1,1 1], [1 2,1 2], keep order:false, stats:pseudo",
|
||||
))
|
||||
testKit.MustQuery("select * from t4 where a = 1 and b in (1,2)").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"1 2",
|
||||
))
|
||||
|
||||
testKit.MustExec("create table t5(a int, b int, primary key(a)) partition by hash(a) partitions 2")
|
||||
testKit.MustExec("insert into t5 values(1,0),(2,0),(3,0),(4,0)")
|
||||
testKit.MustExec("set @@tidb_partition_prune_mode = 'static'")
|
||||
testKit.MustQuery("explain format = 'brief' select * from t5 where a in (1,2) and 1 = 1").Check(testkit.Rows(
|
||||
"PartitionUnion 4.00 root ",
|
||||
"├─Batch_Point_Get 2.00 root table:t5 handle:[1 2], keep order:false, desc:false",
|
||||
"└─Batch_Point_Get 2.00 root table:t5 handle:[1 2], keep order:false, desc:false",
|
||||
))
|
||||
testKit.MustQuery("select * from t5 where a in (1,2) and 1 = 1").Sort().Check(testkit.Rows(
|
||||
"1 0",
|
||||
"2 0",
|
||||
))
|
||||
testKit.MustQuery("explain format = 'brief' select * from t5 where a in (1,3) and 1 = 1").Check(testkit.Rows(
|
||||
"Batch_Point_Get 2.00 root table:t5 handle:[1 3], keep order:false, desc:false",
|
||||
))
|
||||
testKit.MustQuery("select * from t5 where a in (1,3) and 1 = 1").Sort().Check(testkit.Rows(
|
||||
"1 0",
|
||||
"3 0",
|
||||
))
|
||||
testKit.MustExec("set @@tidb_partition_prune_mode = 'dynamic'")
|
||||
testKit.MustQuery("explain format = 'brief' select * from t5 where a in (1,2) and 1 = 1").Check(testkit.Rows(
|
||||
"TableReader 2.00 root partition:p0,p1 data:TableRangeScan",
|
||||
"└─TableRangeScan 2.00 cop[tikv] table:t5 range:[1,1], [2,2], keep order:false, stats:pseudo",
|
||||
))
|
||||
testKit.MustQuery("select * from t5 where a in (1,2) and 1 = 1").Sort().Check(testkit.Rows(
|
||||
"1 0",
|
||||
"2 0",
|
||||
))
|
||||
testKit.MustQuery("explain format = 'brief' select * from t5 where a in (1,3) and 1 = 1").Check(testkit.Rows(
|
||||
"TableReader 2.00 root partition:p1 data:TableRangeScan",
|
||||
"└─TableRangeScan 2.00 cop[tikv] table:t5 range:[1,1], [3,3], keep order:false, stats:pseudo",
|
||||
))
|
||||
testKit.MustQuery("select * from t5 where a in (1,3) and 1 = 1").Sort().Check(testkit.Rows(
|
||||
"1 0",
|
||||
"3 0",
|
||||
))
|
||||
|
||||
testKit.MustExec("create table t6(a int, b int, primary key(a)) partition by range(a) (partition p0 values less than (3), partition p1 values less than maxvalue)")
|
||||
testKit.MustExec("insert into t6 values(1,0),(2,0),(3,0),(4,0)")
|
||||
testKit.MustExec("set @@tidb_partition_prune_mode = 'static'")
|
||||
testKit.MustQuery("explain format = 'brief' select * from t6 where a in (1,2) and 1 = 1").Check(testkit.Rows(
|
||||
"TableReader 2.00 root data:TableRangeScan",
|
||||
"└─TableRangeScan 2.00 cop[tikv] table:t6, partition:p0 range:[1,1], [2,2], keep order:false, stats:pseudo",
|
||||
))
|
||||
testKit.MustQuery("select * from t6 where a in (1,2) and 1 = 1").Sort().Check(testkit.Rows(
|
||||
"1 0",
|
||||
"2 0",
|
||||
))
|
||||
testKit.MustQuery("explain format = 'brief' select * from t6 where a in (1,3) and 1 = 1").Check(testkit.Rows(
|
||||
"PartitionUnion 4.00 root ",
|
||||
"├─TableReader 2.00 root data:TableRangeScan",
|
||||
"│ └─TableRangeScan 2.00 cop[tikv] table:t6, partition:p0 range:[1,1], [3,3], keep order:false, stats:pseudo",
|
||||
"└─TableReader 2.00 root data:TableRangeScan",
|
||||
" └─TableRangeScan 2.00 cop[tikv] table:t6, partition:p1 range:[1,1], [3,3], keep order:false, stats:pseudo",
|
||||
))
|
||||
testKit.MustQuery("select * from t6 where a in (1,3) and 1 = 1").Sort().Check(testkit.Rows(
|
||||
"1 0",
|
||||
"3 0",
|
||||
))
|
||||
testKit.MustExec("set @@tidb_partition_prune_mode = 'dynamic'")
|
||||
testKit.MustQuery("explain format = 'brief' select * from t6 where a in (1,2) and 1 = 1").Check(testkit.Rows(
|
||||
"TableReader 2.00 root partition:p0 data:TableRangeScan",
|
||||
"└─TableRangeScan 2.00 cop[tikv] table:t6 range:[1,1], [2,2], keep order:false, stats:pseudo",
|
||||
))
|
||||
testKit.MustQuery("select * from t6 where a in (1,2) and 1 = 1").Sort().Check(testkit.Rows(
|
||||
"1 0",
|
||||
"2 0",
|
||||
))
|
||||
testKit.MustQuery("explain format = 'brief' select * from t6 where a in (1,3) and 1 = 1").Check(testkit.Rows(
|
||||
"TableReader 2.00 root partition:all data:TableRangeScan",
|
||||
"└─TableRangeScan 2.00 cop[tikv] table:t6 range:[1,1], [3,3], keep order:false, stats:pseudo",
|
||||
))
|
||||
testKit.MustQuery("select * from t6 where a in (1,3) and 1 = 1").Sort().Check(testkit.Rows(
|
||||
"1 0",
|
||||
"3 0",
|
||||
))
|
||||
}
|
||||
|
||||
@ -694,21 +694,34 @@ func (ds *DataSource) findBestTask(prop *property.PhysicalProperty, planCounter
|
||||
p: dual,
|
||||
}, cntPlan, nil
|
||||
}
|
||||
canConvertPointGet := (!ds.isPartition && len(path.Ranges) > 0) || (ds.isPartition && len(path.Ranges) == 1)
|
||||
canConvertPointGet = canConvertPointGet && candidate.path.StoreType != kv.TiFlash
|
||||
if !candidate.path.IsIntHandlePath {
|
||||
canConvertPointGet = canConvertPointGet &&
|
||||
candidate.path.Index.Unique && !candidate.path.Index.HasPrefixIndex()
|
||||
idxColsLen := len(candidate.path.Index.Columns)
|
||||
for _, ran := range candidate.path.Ranges {
|
||||
canConvertPointGet := len(path.Ranges) > 0 && path.StoreType != kv.TiFlash
|
||||
if canConvertPointGet && !path.IsIntHandlePath {
|
||||
// We simply do not build [batch] point get for prefix indexes. This can be optimized.
|
||||
canConvertPointGet = path.Index.Unique && !path.Index.HasPrefixIndex()
|
||||
// If any range cannot cover all columns of the index, we cannot build [batch] point get.
|
||||
idxColsLen := len(path.Index.Columns)
|
||||
for _, ran := range path.Ranges {
|
||||
if len(ran.LowVal) != idxColsLen {
|
||||
canConvertPointGet = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if ds.table.Meta().GetPartitionInfo() != nil && ds.ctx.GetSessionVars().UseDynamicPartitionPrune() {
|
||||
canConvertPointGet = false
|
||||
var hashPartColName *ast.ColumnName
|
||||
if tblInfo := ds.table.Meta(); canConvertPointGet && tblInfo.GetPartitionInfo() != nil {
|
||||
// We do not build [batch] point get for dynamic table partitions now. This can be optimized.
|
||||
if ds.ctx.GetSessionVars().UseDynamicPartitionPrune() {
|
||||
canConvertPointGet = false
|
||||
} else if len(path.Ranges) > 1 {
|
||||
// We can only build batch point get for hash partitions on a simple column now. This is
|
||||
// decided by the current implementation of `BatchPointGetExec::initialize()`, specifically,
|
||||
// the `getPhysID()` function. Once we optimize that part, we can come back and enable
|
||||
// BatchPointGet plan for more cases.
|
||||
hashPartColName = getHashPartitionColumnName(ds.ctx, tblInfo)
|
||||
if hashPartColName == nil {
|
||||
canConvertPointGet = false
|
||||
}
|
||||
}
|
||||
}
|
||||
if canConvertPointGet {
|
||||
allRangeIsPoint := true
|
||||
@ -723,7 +736,7 @@ func (ds *DataSource) findBestTask(prop *property.PhysicalProperty, planCounter
|
||||
if len(path.Ranges) == 1 {
|
||||
pointGetTask = ds.convertToPointGet(prop, candidate)
|
||||
} else {
|
||||
pointGetTask = ds.convertToBatchPointGet(prop, candidate)
|
||||
pointGetTask = ds.convertToBatchPointGet(prop, candidate, hashPartColName)
|
||||
}
|
||||
if !pointGetTask.invalid() {
|
||||
cntPlan += 1
|
||||
@ -1642,7 +1655,7 @@ func (ds *DataSource) convertToPointGet(prop *property.PhysicalProperty, candida
|
||||
return rTsk
|
||||
}
|
||||
|
||||
func (ds *DataSource) convertToBatchPointGet(prop *property.PhysicalProperty, candidate *candidatePath) task {
|
||||
func (ds *DataSource) convertToBatchPointGet(prop *property.PhysicalProperty, candidate *candidatePath, hashPartColName *ast.ColumnName) task {
|
||||
if !prop.IsEmpty() && !candidate.isMatchProp {
|
||||
return invalidTask
|
||||
}
|
||||
@ -1658,6 +1671,8 @@ func (ds *DataSource) convertToBatchPointGet(prop *property.PhysicalProperty, ca
|
||||
TblInfo: ds.TableInfo(),
|
||||
KeepOrder: !prop.IsEmpty(),
|
||||
Columns: ds.Columns,
|
||||
SinglePart: ds.isPartition,
|
||||
PartTblID: ds.physicalTableID,
|
||||
}.Init(ds.ctx, ds.tableStats.ScaleByExpectCnt(accessCnt), ds.schema.Clone(), ds.names, ds.blockOffset)
|
||||
if batchPointGetPlan.KeepOrder {
|
||||
batchPointGetPlan.Desc = prop.SortItems[0].Desc
|
||||
@ -1683,6 +1698,7 @@ func (ds *DataSource) convertToBatchPointGet(prop *property.PhysicalProperty, ca
|
||||
batchPointGetPlan.IndexInfo = candidate.path.Index
|
||||
batchPointGetPlan.IdxCols = candidate.path.IdxCols
|
||||
batchPointGetPlan.IdxColLens = candidate.path.IdxColLens
|
||||
batchPointGetPlan.PartitionColPos = getPartitionColumnPos(candidate.path.Index, hashPartColName)
|
||||
for _, ran := range candidate.path.Ranges {
|
||||
batchPointGetPlan.IndexValues = append(batchPointGetPlan.IndexValues, ran.LowVal)
|
||||
}
|
||||
|
||||
@ -262,6 +262,13 @@ type BatchPointGetPlan struct {
|
||||
Lock bool
|
||||
LockWaitTime int64
|
||||
Columns []*model.ColumnInfo
|
||||
// SinglePart indicates whether this BatchPointGetPlan is just for a single partition, instead of the whole partition table.
|
||||
// If the BatchPointGetPlan is built in fast path, this value if false; if the plan is generated in physical optimization for a partition,
|
||||
// this value would be true. This value would decide the behavior of BatchPointGetExec, i.e, whether to compute the table ID of the partition
|
||||
// on the fly.
|
||||
SinglePart bool
|
||||
// PartTblID is the table ID for the specific table partition.
|
||||
PartTblID int64
|
||||
}
|
||||
|
||||
// Clone implements PhysicalPlan interface.
|
||||
|
||||
@ -354,6 +354,224 @@ func (s *testPointGetSuite) TestCBOPointGet(c *C) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *testPointGetSuite) TestPartitionBatchPointGetPlanCache(c *C) {
|
||||
testKit := testkit.NewTestKit(c, s.store)
|
||||
orgEnable := core.PreparedPlanCacheEnabled()
|
||||
defer func() {
|
||||
core.SetPreparedPlanCache(orgEnable)
|
||||
}()
|
||||
core.SetPreparedPlanCache(true)
|
||||
|
||||
var err error
|
||||
testKit.Se, err = session.CreateSession4TestWithOpt(s.store, &session.Opt{
|
||||
PreparedPlanCache: kvcache.NewSimpleLRUCache(100, 0.1, math.MaxUint64),
|
||||
})
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
testKit.MustExec("use test")
|
||||
testKit.MustExec("drop table if exists t")
|
||||
testKit.MustExec("create table t(a int, b int, unique key(a))")
|
||||
testKit.MustExec("insert into t values(1,1),(2,2),(3,3)")
|
||||
testKit.MustExec("prepare stmt from 'select * from t use index(a) where (a >= ? and a <= ?) or a = 3'")
|
||||
testKit.MustExec("set @p=1,@q=2,@u=3")
|
||||
testKit.MustQuery("execute stmt using @p,@p").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"3 3",
|
||||
))
|
||||
testKit.MustQuery("execute stmt using @u,@q").Sort().Check(testkit.Rows(
|
||||
"3 3",
|
||||
))
|
||||
|
||||
testKit.MustExec("drop table t")
|
||||
testKit.MustExec("create table t(a int, b int, primary key(a,b)) partition by hash(b) partitions 2")
|
||||
testKit.MustExec("insert into t values(1,1),(1,2),(1,3),(2,1),(2,2),(2,3),(3,1),(3,2),(3,3)")
|
||||
testKit.MustExec("set @@tidb_partition_prune_mode = 'static'")
|
||||
testKit.MustExec("prepare stmt from 'select * from t where ((a >= ? and a <= ?) or a = 2) and b = ?'")
|
||||
testKit.MustQuery("execute stmt using @p,@p,@p").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"2 1",
|
||||
))
|
||||
testKit.MustQuery("execute stmt using @q,@q,@p").Sort().Check(testkit.Rows(
|
||||
"2 1",
|
||||
))
|
||||
testKit.MustQuery("execute stmt using @q,@q,@q").Sort().Check(testkit.Rows(
|
||||
"2 2",
|
||||
))
|
||||
testKit.MustQuery("execute stmt using @p,@u,@p").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"2 1",
|
||||
"3 1",
|
||||
))
|
||||
testKit.MustQuery("execute stmt using @u,@p,@p").Sort().Check(testkit.Rows(
|
||||
"2 1",
|
||||
))
|
||||
|
||||
testKit.MustExec("prepare stmt from 'select * from t where a in (?,?) and b = ?'")
|
||||
testKit.MustQuery("execute stmt using @p,@q,@p").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"2 1",
|
||||
))
|
||||
testKit.MustQuery("execute stmt using @q,@p,@p").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"2 1",
|
||||
))
|
||||
testKit.MustQuery("execute stmt using @q,@q,@p").Sort().Check(testkit.Rows(
|
||||
"2 1",
|
||||
))
|
||||
testKit.MustQuery("execute stmt using @p,@q,@q").Sort().Check(testkit.Rows(
|
||||
"1 2",
|
||||
"2 2",
|
||||
))
|
||||
|
||||
testKit.MustExec("prepare stmt from 'select * from t where a = ? and ((b >= ? and b <= ?) or b = 2)'")
|
||||
testKit.MustQuery("execute stmt using @p,@p,@p").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"1 2",
|
||||
))
|
||||
testKit.MustQuery("execute stmt using @p,@q,@q").Sort().Check(testkit.Rows(
|
||||
"1 2",
|
||||
))
|
||||
testKit.MustQuery("execute stmt using @q,@q,@q").Sort().Check(testkit.Rows(
|
||||
"2 2",
|
||||
))
|
||||
testKit.MustQuery("execute stmt using @p,@p,@u").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"1 2",
|
||||
"1 3",
|
||||
))
|
||||
testKit.MustQuery("execute stmt using @p,@u,@p").Sort().Check(testkit.Rows(
|
||||
"1 2",
|
||||
))
|
||||
|
||||
testKit.MustExec("prepare stmt from 'select * from t where a = ? and b in (?,?)'")
|
||||
testKit.MustQuery("execute stmt using @p,@p,@q").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"1 2",
|
||||
))
|
||||
testKit.MustQuery("execute stmt using @p,@q,@p").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"1 2",
|
||||
))
|
||||
testKit.MustQuery("execute stmt using @p,@q,@q").Sort().Check(testkit.Rows(
|
||||
"1 2",
|
||||
))
|
||||
testKit.MustQuery("execute stmt using @q,@p,@q").Sort().Check(testkit.Rows(
|
||||
"2 1",
|
||||
"2 2",
|
||||
))
|
||||
|
||||
testKit.Se.GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeOn
|
||||
testKit.MustExec("drop table t")
|
||||
testKit.MustExec("create table t(a int, b int, primary key(a,b)) partition by hash(b) partitions 2")
|
||||
testKit.MustExec("insert into t values(1,1),(1,2),(1,3),(2,1),(2,2),(2,3),(3,1),(3,2),(3,3)")
|
||||
testKit.MustExec("prepare stmt from 'select * from t where ((a >= ? and a <= ?) or a = 2) and b = ?'")
|
||||
testKit.MustQuery("execute stmt using @p,@p,@p").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"2 1",
|
||||
))
|
||||
testKit.MustQuery("execute stmt using @q,@q,@p").Sort().Check(testkit.Rows(
|
||||
"2 1",
|
||||
))
|
||||
testKit.MustQuery("execute stmt using @q,@q,@q").Sort().Check(testkit.Rows(
|
||||
"2 2",
|
||||
))
|
||||
testKit.MustQuery("execute stmt using @p,@u,@p").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"2 1",
|
||||
"3 1",
|
||||
))
|
||||
testKit.MustQuery("execute stmt using @u,@p,@p").Sort().Check(testkit.Rows(
|
||||
"2 1",
|
||||
))
|
||||
|
||||
testKit.MustExec("prepare stmt from 'select * from t where a in (?,?) and b = ?'")
|
||||
testKit.MustQuery("execute stmt using @p,@q,@p").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"2 1",
|
||||
))
|
||||
testKit.MustQuery("execute stmt using @q,@p,@p").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"2 1",
|
||||
))
|
||||
testKit.MustQuery("execute stmt using @q,@q,@p").Sort().Check(testkit.Rows(
|
||||
"2 1",
|
||||
))
|
||||
testKit.MustQuery("execute stmt using @p,@q,@q").Sort().Check(testkit.Rows(
|
||||
"1 2",
|
||||
"2 2",
|
||||
))
|
||||
|
||||
testKit.MustExec("prepare stmt from 'select * from t where a = ? and ((b >= ? and b <= ?) or b = 2)'")
|
||||
testKit.MustQuery("execute stmt using @p,@p,@p").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"1 2",
|
||||
))
|
||||
testKit.MustQuery("execute stmt using @p,@q,@q").Sort().Check(testkit.Rows(
|
||||
"1 2",
|
||||
))
|
||||
testKit.MustQuery("execute stmt using @q,@q,@q").Sort().Check(testkit.Rows(
|
||||
"2 2",
|
||||
))
|
||||
testKit.MustQuery("execute stmt using @p,@p,@u").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"1 2",
|
||||
"1 3",
|
||||
))
|
||||
testKit.MustQuery("execute stmt using @p,@u,@p").Sort().Check(testkit.Rows(
|
||||
"1 2",
|
||||
))
|
||||
|
||||
testKit.MustExec("prepare stmt from 'select * from t where a = ? and b in (?,?)'")
|
||||
testKit.MustQuery("execute stmt using @p,@p,@q").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"1 2",
|
||||
))
|
||||
testKit.MustQuery("execute stmt using @p,@q,@p").Sort().Check(testkit.Rows(
|
||||
"1 1",
|
||||
"1 2",
|
||||
))
|
||||
testKit.MustQuery("execute stmt using @p,@q,@q").Sort().Check(testkit.Rows(
|
||||
"1 2",
|
||||
))
|
||||
testKit.MustQuery("execute stmt using @q,@p,@q").Sort().Check(testkit.Rows(
|
||||
"2 1",
|
||||
"2 2",
|
||||
))
|
||||
|
||||
testKit.MustExec("drop table t")
|
||||
testKit.MustExec("create table t(a int, b int, primary key(a)) partition by hash(a) partitions 2")
|
||||
testKit.MustExec("insert into t values(1,0),(2,0),(3,0),(4,0)")
|
||||
testKit.MustExec("prepare stmt from 'select * from t where ((a >= ? and a <= ?) or a = 2) and 1 = 1'")
|
||||
testKit.MustQuery("execute stmt using @p,@p").Sort().Check(testkit.Rows(
|
||||
"1 0",
|
||||
"2 0",
|
||||
))
|
||||
testKit.MustQuery("execute stmt using @q,@q").Sort().Check(testkit.Rows(
|
||||
"2 0",
|
||||
))
|
||||
testKit.MustQuery("execute stmt using @p,@u").Sort().Check(testkit.Rows(
|
||||
"1 0",
|
||||
"2 0",
|
||||
"3 0",
|
||||
))
|
||||
testKit.MustQuery("execute stmt using @u,@p").Sort().Check(testkit.Rows(
|
||||
"2 0",
|
||||
))
|
||||
|
||||
testKit.MustExec("prepare stmt from 'select * from t where a in (?,?) and 1 = 1'")
|
||||
testKit.MustQuery("execute stmt using @p,@q").Sort().Check(testkit.Rows(
|
||||
"1 0",
|
||||
"2 0",
|
||||
))
|
||||
testKit.MustQuery("execute stmt using @q,@p").Sort().Check(testkit.Rows(
|
||||
"1 0",
|
||||
"2 0",
|
||||
))
|
||||
testKit.MustQuery("execute stmt using @q,@q").Sort().Check(testkit.Rows(
|
||||
"2 0",
|
||||
))
|
||||
}
|
||||
|
||||
func (s *testPointGetSuite) TestBatchPointGetPlanCache(c *C) {
|
||||
tk := testkit.NewTestKit(c, s.store)
|
||||
orgEnable := core.PreparedPlanCacheEnabled()
|
||||
|
||||
Reference in New Issue
Block a user