From 7f9b4fd288db68a635ed71368f272a86b328c705 Mon Sep 17 00:00:00 2001 From: Ti Chi Robot Date: Fri, 24 Mar 2023 09:20:42 +0800 Subject: [PATCH] *: Update the UniqueID of partition expression columns in LIST partitioning (#42193) (#42222) ref pingcap/tidb#42135 --- planner/core/partition_prune.go | 2 +- planner/core/partition_pruner_test.go | 37 +++++++++++++++++++ planner/core/rule_partition_processor.go | 47 +++++++++++++----------- table/tables/partition.go | 23 ++++++++++++ 4 files changed, 87 insertions(+), 22 deletions(-) diff --git a/planner/core/partition_prune.go b/planner/core/partition_prune.go index 3ab266340829d..2dec9c62d19e2 100644 --- a/planner/core/partition_prune.go +++ b/planner/core/partition_prune.go @@ -40,7 +40,7 @@ func PartitionPruning(ctx sessionctx.Context, tbl table.PartitionedTable, conds ret := s.convertToIntSlice(rangeOr, pi, partitionNames) return ret, nil case model.PartitionTypeList: - return s.pruneListPartition(ctx, tbl, partitionNames, conds) + return s.pruneListPartition(ctx, tbl, partitionNames, conds, columns) } return []int{FullRange}, nil } diff --git a/planner/core/partition_pruner_test.go b/planner/core/partition_pruner_test.go index 6fdf04ea5a873..59d44cdfdf9de 100644 --- a/planner/core/partition_pruner_test.go +++ b/planner/core/partition_pruner_test.go @@ -681,6 +681,43 @@ func TestRangePartitionPredicatePruner(t *testing.T) { } } +func TestIssue42135(t *testing.T) { + store, clean := testkit.CreateMockStore(t) + defer clean() + tk := testkit.NewTestKit(t, store) + tk.MustExec(`create database issue42135`) + tk.MustExec(`use issue42135`) + tk.MustExec("CREATE TABLE `tx1` (`ID` varchar(13), `a` varchar(13), `b` varchar(4000), `ltype` int(5) NOT NULL)") + tk.MustExec("CREATE TABLE `tx2` (`ID` varchar(13), `rid` varchar(12), `a` varchar(9), `b` varchar(8), `c` longtext, `d` varchar(12), `ltype` int(5) NOT NULL) PARTITION BY LIST (`ltype`) (PARTITION `p1` VALUES IN (501), PARTITION `p2` VALUES IN (502))") + tk.MustExec("insert into tx1 values(1,1,1,501)") + tk.MustExec("insert into tx2 values(1,1,1,1,1,1,501)") + tk.MustExec(`analyze table tx1`) + tk.MustExec(`analyze table tx2`) + tk.MustQuery(`select * from tx1 inner join tx2 on tx1.ID=tx2.ID and tx1.ltype=tx2.ltype where tx2.rid='1'`).Check(testkit.Rows("1 1 1 501 1 1 1 1 1 1 501")) + tk.MustQuery(`explain format='brief' select * from tx1 inner join tx2 on tx1.ID=tx2.ID and tx1.ltype=tx2.ltype where tx2.rid='1'`).Check(testkit.Rows(""+ + "HashJoin 1.00 root inner join, equal:[eq(issue42135.tx1.id, issue42135.tx2.id) eq(issue42135.tx1.ltype, issue42135.tx2.ltype)]", + `├─TableReader(Build) 1.00 root partition:all data:Selection`, + `│ └─Selection 1.00 cop[tikv] eq(issue42135.tx2.rid, "1"), not(isnull(issue42135.tx2.id))`, + `│ └─TableFullScan 1.00 cop[tikv] table:tx2 keep order:false`, + `└─TableReader(Probe) 1.00 root data:Selection`, + ` └─Selection 1.00 cop[tikv] not(isnull(issue42135.tx1.id))`, + ` └─TableFullScan 1.00 cop[tikv] table:tx1 keep order:false`)) + + tk.MustExec(`drop table tx2`) + tk.MustExec("CREATE TABLE `tx2` (`ID` varchar(13), `rid` varchar(12), `a` varchar(9), `b` varchar(8), `c` longtext, `d` varchar(12), `ltype` int(5) NOT NULL) PARTITION BY LIST COLUMNS (`ltype`,d) (PARTITION `p1` VALUES IN ((501,1)), PARTITION `p2` VALUES IN ((502,1)))") + tk.MustExec("insert into tx2 values(1,1,1,1,1,1,501)") + tk.MustExec(`analyze table tx2`) + tk.MustQuery(`select * from tx1 inner join tx2 on tx1.ID=tx2.ID and tx1.ltype=tx2.ltype where tx2.rid='1'`).Check(testkit.Rows("1 1 1 501 1 1 1 1 1 1 501")) + tk.MustQuery(`explain format='brief' select * from tx1 inner join tx2 on tx1.ID=tx2.ID and tx1.ltype=tx2.ltype where tx2.rid='1'`).Check(testkit.Rows(""+ + "HashJoin 1.00 root inner join, equal:[eq(issue42135.tx1.id, issue42135.tx2.id) eq(issue42135.tx1.ltype, issue42135.tx2.ltype)]", + "├─TableReader(Build) 1.00 root partition:all data:Selection", + "│ └─Selection 1.00 cop[tikv] eq(issue42135.tx2.rid, \"1\"), not(isnull(issue42135.tx2.id))", + "│ └─TableFullScan 1.00 cop[tikv] table:tx2 keep order:false", + "└─TableReader(Probe) 1.00 root data:Selection", + " └─Selection 1.00 cop[tikv] not(isnull(issue42135.tx1.id))", + " └─TableFullScan 1.00 cop[tikv] table:tx1 keep order:false")) +} + func TestHashPartitionPruning(t *testing.T) { store, clean := testkit.CreateMockStore(t) defer clean() diff --git a/planner/core/rule_partition_processor.go b/planner/core/rule_partition_processor.go index 3ab21cee1403a..07eb87681904d 100644 --- a/planner/core/rule_partition_processor.go +++ b/planner/core/rule_partition_processor.go @@ -359,21 +359,30 @@ func (s *partitionProcessor) processHashPartition(ds *DataSource, pi *model.Part // listPartitionPruner uses to prune partition for list partition. type listPartitionPruner struct { *partitionProcessor - ctx sessionctx.Context - pi *model.PartitionInfo - partitionNames []model.CIStr - colIDToUniqueID map[int64]int64 - fullRange map[int]struct{} - listPrune *tables.ForListPruning + ctx sessionctx.Context + pi *model.PartitionInfo + partitionNames []model.CIStr + fullRange map[int]struct{} + listPrune *tables.ForListPruning } func newListPartitionPruner(ctx sessionctx.Context, tbl table.Table, partitionNames []model.CIStr, - s *partitionProcessor, conds []expression.Expression, pruneList *tables.ForListPruning) *listPartitionPruner { - colIDToUniqueID := make(map[int64]int64) - for _, cond := range conds { - condCols := expression.ExtractColumns(cond) - for _, c := range condCols { - colIDToUniqueID[c.ID] = c.UniqueID + s *partitionProcessor, conds []expression.Expression, pruneList *tables.ForListPruning, columns []*expression.Column) *listPartitionPruner { + pruneList = pruneList.Clone() + for i := range pruneList.PruneExprCols { + for j := range columns { + if columns[j].ID == pruneList.PruneExprCols[i].ID { + pruneList.PruneExprCols[i].UniqueID = columns[j].UniqueID + break + } + } + } + for i := range pruneList.ColPrunes { + for j := range columns { + if columns[j].ID == pruneList.ColPrunes[i].ExprCol.ID { + pruneList.ColPrunes[i].ExprCol.UniqueID = columns[j].UniqueID + break + } } } fullRange := make(map[int]struct{}) @@ -383,7 +392,6 @@ func newListPartitionPruner(ctx sessionctx.Context, tbl table.Table, partitionNa ctx: ctx, pi: tbl.Meta().Partition, partitionNames: partitionNames, - colIDToUniqueID: colIDToUniqueID, fullRange: fullRange, listPrune: pruneList, } @@ -526,9 +534,6 @@ func (l *listPartitionPruner) detachCondAndBuildRange(conds []expression.Express colLen := make([]int, 0, len(exprCols)) for _, c := range exprCols { c = c.Clone().(*expression.Column) - if uniqueID, ok := l.colIDToUniqueID[c.ID]; ok { - c.UniqueID = uniqueID - } cols = append(cols, c) colLen = append(colLen, types.UnspecifiedLength) } @@ -594,14 +599,14 @@ func (l *listPartitionPruner) findUsedListPartitions(conds []expression.Expressi } func (s *partitionProcessor) findUsedListPartitions(ctx sessionctx.Context, tbl table.Table, partitionNames []model.CIStr, - conds []expression.Expression) ([]int, error) { + conds []expression.Expression, columns []*expression.Column) ([]int, error) { pi := tbl.Meta().Partition partExpr, err := tbl.(partitionTable).PartitionExpr() if err != nil { return nil, err } - listPruner := newListPartitionPruner(ctx, tbl, partitionNames, s, conds, partExpr.ForListPruning) + listPruner := newListPartitionPruner(ctx, tbl, partitionNames, s, conds, partExpr.ForListPruning, columns) var used map[int]struct{} if partExpr.ForListPruning.ColPrunes == nil { used, err = listPruner.findUsedListPartitions(conds) @@ -624,8 +629,8 @@ func (s *partitionProcessor) findUsedListPartitions(ctx sessionctx.Context, tbl } func (s *partitionProcessor) pruneListPartition(ctx sessionctx.Context, tbl table.Table, partitionNames []model.CIStr, - conds []expression.Expression) ([]int, error) { - used, err := s.findUsedListPartitions(ctx, tbl, partitionNames, conds) + conds []expression.Expression, columns []*expression.Column) ([]int, error) { + used, err := s.findUsedListPartitions(ctx, tbl, partitionNames, conds, columns) if err != nil { return nil, err } @@ -872,7 +877,7 @@ func (s *partitionProcessor) processRangePartition(ds *DataSource, pi *model.Par } func (s *partitionProcessor) processListPartition(ds *DataSource, pi *model.PartitionInfo, opt *logicalOptimizeOp) (LogicalPlan, error) { - used, err := s.pruneListPartition(ds.SCtx(), ds.table, ds.partitionNames, ds.allConds) + used, err := s.pruneListPartition(ds.SCtx(), ds.table, ds.partitionNames, ds.allConds, ds.TblCols) if err != nil { return nil, err } diff --git a/table/tables/partition.go b/table/tables/partition.go index 5c961094740f4..e68c82a72771e 100644 --- a/table/tables/partition.go +++ b/table/tables/partition.go @@ -624,6 +624,29 @@ func generateListPartitionExpr(ctx sessionctx.Context, tblInfo *model.TableInfo, return ret, nil } +// Clone a copy of ForListPruning +func (lp *ForListPruning) Clone() *ForListPruning { + ret := *lp + if ret.LocateExpr != nil { + ret.LocateExpr = lp.LocateExpr.Clone() + } + if ret.PruneExpr != nil { + ret.PruneExpr = lp.PruneExpr.Clone() + } + ret.PruneExprCols = make([]*expression.Column, 0, len(lp.PruneExprCols)) + for i := range lp.PruneExprCols { + c := lp.PruneExprCols[i].Clone().(*expression.Column) + ret.PruneExprCols = append(ret.PruneExprCols, c) + } + ret.ColPrunes = make([]*ForListColumnPruning, 0, len(lp.ColPrunes)) + for i := range lp.ColPrunes { + l := *lp.ColPrunes[i] + l.ExprCol = l.ExprCol.Clone().(*expression.Column) + ret.ColPrunes = append(ret.ColPrunes, &l) + } + return &ret +} + func (lp *ForListPruning) buildListPruner(ctx sessionctx.Context, tblInfo *model.TableInfo, exprCols []*expression.Column, columns []*expression.Column, names types.NameSlice) error { pi := tblInfo.GetPartitionInfo()