Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

planner: support push part of order property down to the partition table #36108

Merged
merged 31 commits into from
Nov 29, 2022
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7c9e327
planner: support push part of order property down to the partition table
winoros Jul 7, 2022
3993e00
planner: support push part of order property to partition table
winoros Jul 11, 2022
3106166
fix fmt
winoros Jul 11, 2022
2c08dde
add tests
winoros Jul 13, 2022
39bca97
Merge branch 'master' into part-table-topn
hawkingrei Jul 16, 2022
896a4e0
Merge branch 'master' into part-table-topn
winoros Jul 18, 2022
9f57b9c
address comments
winoros Jul 18, 2022
6ccdc30
Merge remote-tracking branch 'yiding/part-table-topn' into part-table…
winoros Jul 19, 2022
423f599
add tests
winoros Jul 19, 2022
f940c71
make sure that the request is sent first by partition then by region
winoros Jul 20, 2022
2bc49ff
fix make check
winoros Jul 20, 2022
af0cf54
Merge branch 'master' into part-table-topn
winoros Aug 26, 2022
0575b4c
fix lint
winoros Aug 26, 2022
5006949
Merge branch 'master' into part-table-topn
winoros Aug 26, 2022
027eca6
fix tests
winoros Aug 28, 2022
4b00f0a
Merge remote-tracking branch 'yiding/part-table-topn' into part-table…
winoros Aug 28, 2022
174254c
Merge branch 'master' into part-table-topn
winoros Aug 28, 2022
27324cb
fix test
winoros Aug 28, 2022
bdf9854
Merge branch 'master' into part-table-topn
winoros Oct 28, 2022
51853c7
fix
winoros Oct 28, 2022
e9da563
Merge branch 'master' into part-table-topn
winoros Nov 23, 2022
921e702
wrap the ranges
winoros Nov 23, 2022
7a60b86
rename and cleanup
winoros Nov 23, 2022
09fb930
pass static check
winoros Nov 26, 2022
e1b4211
fix tiny error
winoros Nov 26, 2022
e285406
Merge branch 'master' into part-table-topn
winoros Nov 26, 2022
953273f
address comments
winoros Nov 28, 2022
6b0699f
Merge branch 'master' into part-table-topn
ti-chi-bot Nov 29, 2022
2794d3e
Merge branch 'master' into part-table-topn
ti-chi-bot Nov 29, 2022
1d38fcc
Merge branch 'master' into part-table-topn
ti-chi-bot Nov 29, 2022
743265c
Merge branch 'master' into part-table-topn
ti-chi-bot Nov 29, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions distsql/request_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,12 @@ func (builder *RequestBuilder) SetKeyRanges(keyRanges []kv.KeyRange) *RequestBui
return builder
}

// SetPartitionKeyRanges sets the "KeyRangesWithPartition" for "kv.Request".
func (builder *RequestBuilder) SetPartitionKeyRanges(keyRanges [][]kv.KeyRange) *RequestBuilder {
builder.Request.KeyRangesWithPartition = keyRanges
return builder
}

// SetStartTS sets "StartTS" for "kv.Request".
func (builder *RequestBuilder) SetStartTS(startTS uint64) *RequestBuilder {
builder.Request.StartTs = startTS
Expand Down
9 changes: 4 additions & 5 deletions executor/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -4045,17 +4045,16 @@ func (h kvRangeBuilderFromRangeAndPartition) buildKeyRangeSeparately(ranges []*r
return pids, ret, nil
}

func (h kvRangeBuilderFromRangeAndPartition) buildKeyRange(ranges []*ranger.Range) ([]kv.KeyRange, error) {
//nolint: prealloc
var ret []kv.KeyRange
for _, p := range h.partitions {
func (h kvRangeBuilderFromRangeAndPartition) buildKeyRange(ranges []*ranger.Range) ([][]kv.KeyRange, error) {
ret := make([][]kv.KeyRange, len(h.partitions))
for i, p := range h.partitions {
pid := p.GetPhysicalID()
meta := p.Meta()
kvRange, err := distsql.TableHandleRangesToKVRanges(h.sctx.GetSessionVars().StmtCtx, []int64{pid}, meta != nil && meta.IsCommonHandle, ranges, nil)
if err != nil {
return nil, err
}
ret = append(ret, kvRange...)
ret[i] = append(ret[i], kvRange...)
}
return ret, nil
}
Expand Down
3 changes: 0 additions & 3 deletions executor/distsql.go
Original file line number Diff line number Diff line change
Expand Up @@ -448,9 +448,6 @@ func (e *IndexLookUpExecutor) Open(ctx context.Context) error {
func (e *IndexLookUpExecutor) buildTableKeyRanges() (err error) {
sc := e.ctx.GetSessionVars().StmtCtx
if e.partitionTableMode {
if e.keepOrder { // this case should be prevented by the optimizer
return errors.New("invalid execution plan: cannot keep order when accessing a partition table by IndexLookUpReader")
}
e.feedback.Invalidate() // feedback for partition tables is not ready
e.partitionKVRanges = make([][]kv.KeyRange, 0, len(e.prunedPartitions))
for _, p := range e.prunedPartitions {
Expand Down
140 changes: 140 additions & 0 deletions executor/partition_table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -352,14 +352,67 @@ func TestOrderByandLimit(t *testing.T) {
// regular table
tk.MustExec("create table tregular(a int, b int, index idx_a(a))")

// range partition table with int pk
tk.MustExec(`create table trange_intpk(a int primary key, b int) partition by range(a) (
partition p0 values less than(300),
partition p1 values less than (500),
partition p2 values less than(1100));`)

// hash partition table with int pk
tk.MustExec("create table thash_intpk(a int primary key, b int) partition by hash(a) partitions 4;")

// regular table with int pk
tk.MustExec("create table tregular_intpk(a int primary key, b int)")

// range partition table with clustered index
tk.MustExec(`create table trange_clustered(a int, b int, primary key(a, b) clustered) partition by range(a) (
partition p0 values less than(300),
partition p1 values less than (500),
partition p2 values less than(1100));`)

// hash partition table with clustered index
tk.MustExec("create table thash_clustered(a int, b int, primary key(a, b) clustered) partition by hash(a) partitions 4;")

// regular table with clustered index
tk.MustExec("create table tregular_clustered(a int, b int, primary key(a, b) clustered)")

// generate some random data to be inserted
vals := make([]string, 0, 2000)
for i := 0; i < 2000; i++ {
vals = append(vals, fmt.Sprintf("(%v, %v)", rand.Intn(1100), rand.Intn(2000)))
}

dedupValsA := make([]string, 0, 2000)
dedupMapA := make(map[int]struct{}, 2000)
for i := 0; i < 2000; i++ {
valA := rand.Intn(1100)
if _, ok := dedupMapA[valA]; ok {
continue
}
dedupValsA = append(dedupValsA, fmt.Sprintf("(%v, %v)", valA, rand.Intn(2000)))
dedupMapA[valA] = struct{}{}
}

dedupValsAB := make([]string, 0, 2000)
dedupMapAB := make(map[string]struct{}, 2000)
for i := 0; i < 2000; i++ {
val := fmt.Sprintf("(%v, %v)", rand.Intn(1100), rand.Intn(2000))
if _, ok := dedupMapAB[val]; ok {
continue
}
dedupValsAB = append(dedupValsAB, val)
dedupMapAB[val] = struct{}{}
}

tk.MustExec("insert into trange values " + strings.Join(vals, ","))
tk.MustExec("insert into thash values " + strings.Join(vals, ","))
tk.MustExec("insert into tregular values " + strings.Join(vals, ","))
tk.MustExec("insert into trange_intpk values " + strings.Join(dedupValsA, ","))
tk.MustExec("insert into thash_intpk values " + strings.Join(dedupValsA, ","))
tk.MustExec("insert into tregular_intpk values " + strings.Join(dedupValsA, ","))
tk.MustExec("insert into trange_clustered values " + strings.Join(dedupValsAB, ","))
tk.MustExec("insert into thash_clustered values " + strings.Join(dedupValsAB, ","))
tk.MustExec("insert into tregular_clustered values " + strings.Join(dedupValsAB, ","))

// test indexLookUp
for i := 0; i < 100; i++ {
Expand All @@ -373,6 +426,29 @@ func TestOrderByandLimit(t *testing.T) {
tk.MustQuery(queryPartition).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows())
}

// test indexLookUp with order property pushed down.
for i := 0; i < 100; i++ {
// explain select * from t where a > {y} use index(idx_a) order by a limit {x}; // check if IndexLookUp is used
// select * from t where a > {y} use index(idx_a) order by a limit {x}; // it can return the correct result
x := rand.Intn(1099)
y := rand.Intn(2000) + 1
// Since we only use order by a not order by a, b, the result is not stable when we read both a and b.
// We cut the max element so that the result can be stable.
maxEle := tk.MustQuery(fmt.Sprintf("select ifnull(max(a), 1100) from (select * from tregular use index(idx_a) where a > %v order by a limit %v) t", x, y)).Rows()[0][0]
queryRangePartitionWithLimitHint := fmt.Sprintf("select /*+ LIMIT_TO_COP() */ * from trange use index(idx_a) where a > %v and a < greatest(%v+1, %v) order by a limit %v", x, x+1, maxEle, y)
queryHashPartitionWithLimitHint := fmt.Sprintf("select /*+ LIMIT_TO_COP() */ * from thash use index(idx_a) where a > %v and a < greatest(%v+1, %v) order by a limit %v", x, x+1, maxEle, y)
queryRegular := fmt.Sprintf("select * from tregular use index(idx_a) where a > %v and a < greatest(%v+1, %v) order by a limit %v;", x, x+1, maxEle, y)
require.True(t, tk.HasPlan(queryRangePartitionWithLimitHint, "Limit"))
require.True(t, tk.HasPlan(queryRangePartitionWithLimitHint, "IndexLookUp"))
require.True(t, tk.HasPlan(queryHashPartitionWithLimitHint, "Limit"))
require.True(t, tk.HasPlan(queryHashPartitionWithLimitHint, "IndexLookUp"))
require.True(t, tk.HasPlan(queryRangePartitionWithLimitHint, "TopN")) // but not fully pushed
require.True(t, tk.HasPlan(queryHashPartitionWithLimitHint, "TopN"))
regularResult := tk.MustQuery(queryRegular).Sort().Rows()
tk.MustQuery(queryRangePartitionWithLimitHint).Sort().Check(regularResult)
tk.MustQuery(queryHashPartitionWithLimitHint).Sort().Check(regularResult)
}

// test tableReader
for i := 0; i < 100; i++ {
// explain select * from t where a > {y} ignore index(idx_a) order by a limit {x}; // check if IndexLookUp is used
Expand All @@ -385,6 +461,51 @@ func TestOrderByandLimit(t *testing.T) {
tk.MustQuery(queryPartition).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows())
}

// test tableReader with order property pushed down.
for i := 0; i < 100; i++ {
// explain select * from t where a > {y} ignore index(idx_a) order by a limit {x}; // check if IndexLookUp is used
// select * from t where a > {y} ignore index(idx_a) order by a limit {x}; // it can return the correct result
x := rand.Intn(1099)
y := rand.Intn(2000) + 1
queryRangePartition := fmt.Sprintf("select /*+ LIMIT_TO_COP() */ * from trange ignore index(idx_a) where a > %v order by a, b limit %v;", x, y)
queryHashPartition := fmt.Sprintf("select /*+ LIMIT_TO_COP() */ * from thash ignore index(idx_a) where a > %v order by a, b limit %v;", x, y)
queryRegular := fmt.Sprintf("select * from tregular ignore index(idx_a) where a > %v order by a, b limit %v;", x, y)
require.True(t, tk.HasPlan(queryRangePartition, "TableReader")) // check if tableReader is used
require.True(t, tk.HasPlan(queryHashPartition, "TableReader"))
require.False(t, tk.HasPlan(queryRangePartition, "Limit")) // check if order property is not pushed
require.False(t, tk.HasPlan(queryHashPartition, "Limit"))
regularResult := tk.MustQuery(queryRegular).Sort().Rows()
tk.MustQuery(queryRangePartition).Sort().Check(regularResult)
tk.MustQuery(queryHashPartition).Sort().Check(regularResult)

// test int pk
// To be simplified, we only read column a.
queryRangePartition = fmt.Sprintf("select /*+ LIMIT_TO_COP() */ a from trange_intpk use index(primary) where a > %v order by a limit %v", x, y)
queryHashPartition = fmt.Sprintf("select /*+ LIMIT_TO_COP() */ a from thash_intpk use index(primary) where a > %v order by a limit %v", x, y)
queryRegular = fmt.Sprintf("select a from tregular_intpk where a > %v order by a limit %v", x, y)
require.True(t, tk.HasPlan(queryRangePartition, "TableReader"))
require.True(t, tk.HasPlan(queryHashPartition, "TableReader"))
require.True(t, tk.HasPlan(queryRangePartition, "Limit")) // check if order property is not pushed
require.True(t, tk.HasPlan(queryHashPartition, "Limit"))
regularResult = tk.MustQuery(queryRegular).Rows()
tk.MustQuery(queryRangePartition).Check(regularResult)
tk.MustQuery(queryHashPartition).Check(regularResult)

// test clustered index
queryRangePartition = fmt.Sprintf("select /*+ LIMIT_TO_COP() */ * from trange_clustered use index(primary) where a > %v order by a, b limit %v;", x, y)
queryHashPartition = fmt.Sprintf("select /*+ LIMIT_TO_COP() */ * from thash_clustered use index(primary) where a > %v order by a, b limit %v;", x, y)
queryRegular = fmt.Sprintf("select * from tregular_clustered where a > %v order by a, b limit %v;", x, y)
require.True(t, tk.HasPlan(queryRangePartition, "TableReader")) // check if tableReader is used
require.True(t, tk.HasPlan(queryHashPartition, "TableReader"))
require.True(t, tk.HasPlan(queryRangePartition, "Limit")) // check if order property is pushed
require.True(t, tk.HasPlan(queryHashPartition, "Limit"))
require.True(t, tk.HasPlan(queryRangePartition, "TopN")) // but not fully pushed
require.True(t, tk.HasPlan(queryHashPartition, "TopN"))
regularResult = tk.MustQuery(queryRegular).Rows()
tk.MustQuery(queryRangePartition).Check(regularResult)
tk.MustQuery(queryHashPartition).Check(regularResult)
}

// test indexReader
for i := 0; i < 100; i++ {
// explain select a from t where a > {y} use index(idx_a) order by a limit {x}; // check if IndexLookUp is used
Expand All @@ -397,6 +518,24 @@ func TestOrderByandLimit(t *testing.T) {
tk.MustQuery(queryPartition).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows())
}

// test indexReader with order property pushed down.
for i := 0; i < 100; i++ {
// explain select a from t where a > {y} use index(idx_a) order by a limit {x}; // check if IndexLookUp is used
// select a from t where a > {y} use index(idx_a) order by a limit {x}; // it can return the correct result
x := rand.Intn(1099)
y := rand.Intn(2000) + 1
queryRangePartition := fmt.Sprintf("select /*+ LIMIT_TO_COP() */ a from trange use index(idx_a) where a > %v order by a limit %v;", x, y)
queryHashPartition := fmt.Sprintf("select /*+ LIMIT_TO_COP() */ a from trange use index(idx_a) where a > %v order by a limit %v;", x, y)
queryRegular := fmt.Sprintf("select a from tregular use index(idx_a) where a > %v order by a limit %v;", x, y)
require.True(t, tk.HasPlan(queryRangePartition, "IndexReader")) // check if indexReader is used
require.True(t, tk.HasPlan(queryHashPartition, "IndexReader"))
require.True(t, tk.HasPlan(queryRangePartition, "Limit")) // check if order property is pushed
require.True(t, tk.HasPlan(queryHashPartition, "Limit"))
regularResult := tk.MustQuery(queryRegular).Sort().Rows()
tk.MustQuery(queryRangePartition).Sort().Check(regularResult)
tk.MustQuery(queryHashPartition).Sort().Check(regularResult)
}

// test indexMerge
for i := 0; i < 100; i++ {
// explain select /*+ use_index_merge(t) */ * from t where a > 2 or b < 5 order by a limit {x}; // check if IndexMerge is used
Expand All @@ -407,6 +546,7 @@ func TestOrderByandLimit(t *testing.T) {
require.True(t, tk.HasPlan(queryPartition, "IndexMerge")) // check if indexMerge is used
tk.MustQuery(queryPartition).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows())
}

}

func TestBatchGetandPointGetwithHashPartition(t *testing.T) {
Expand Down
4 changes: 2 additions & 2 deletions executor/table_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func (sr selectResultHook) SelectResult(ctx context.Context, sctx sessionctx.Con
}

type kvRangeBuilder interface {
buildKeyRange(ranges []*ranger.Range) ([]kv.KeyRange, error)
buildKeyRange(ranges []*ranger.Range) ([][]kv.KeyRange, error)
buildKeyRangeSeparately(ranges []*ranger.Range) ([]int64, [][]kv.KeyRange, error)
}

Expand Down Expand Up @@ -394,7 +394,7 @@ func (e *TableReaderExecutor) buildKVReq(ctx context.Context, ranges []*ranger.R
if err != nil {
return nil, err
}
reqBuilder = builder.SetKeyRanges(kvRange)
reqBuilder = builder.SetPartitionKeyRanges(kvRange)
} else {
reqBuilder = builder.SetHandleRanges(e.ctx.GetSessionVars().StmtCtx, getPhysicalTableID(e.table), e.table.Meta() != nil && e.table.Meta().IsCommonHandle, ranges, e.feedback)
}
Expand Down
4 changes: 4 additions & 0 deletions kv/kv.go
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,10 @@ type Request struct {
Data []byte
KeyRanges []KeyRange
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UnionScan / TemporaryTable / SelectForUpdateLock maybe depend on the key range ...
If for partition table, it's changed to use KeyRangesWithPartition, I'm not sure will this break something.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The union scan didn't rely on the KVRequest.KeyRanges directly, I'm trying to make the changes safer.


// KeyRangesWithPartition makes sure that the request is sent first by partition then by region.
// When the table is small, it's possible that multiple partitions are in the same region.
KeyRangesWithPartition [][]KeyRange

// For PartitionTableScan used by tiflash.
PartitionIDAndRanges []PartitionIDAndRanges

Expand Down
1 change: 1 addition & 0 deletions planner/core/find_best_task.go
Original file line number Diff line number Diff line change
Expand Up @@ -2268,6 +2268,7 @@ func (ds *DataSource) getOriginalPhysicalIndexScan(prop *property.PhysicalProper
physicalTableID: ds.physicalTableID,
tblColHists: ds.TblColHists,
pkIsHandleCol: ds.getPKIsHandleCol(),
constColsByCond: path.ConstCols,
prop: prop,
}.Init(ds.ctx, ds.blockOffset)
statsTbl := ds.statisticTable
Expand Down
7 changes: 6 additions & 1 deletion planner/core/physical_plans.go
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,12 @@ type PhysicalIndexScan struct {
// tblColHists contains all columns before pruning, which are used to calculate row-size
tblColHists *statistics.HistColl
pkIsHandleCol *expression.Column
prop *property.PhysicalProperty

// constColsByCond records the constant part of the index columns caused by the access conds.
// e.g. the index is (a, b, c) and there's filter a = 1 and b = 2, then the column a and b are const part.
constColsByCond []bool

prop *property.PhysicalProperty
}

// Clone implements PhysicalPlan interface.
Expand Down
Loading