From c6a93def28245e4b770c993e52b0630ec95a46ee Mon Sep 17 00:00:00 2001 From: pingcap-github-bot Date: Mon, 1 Jun 2020 17:46:32 +0800 Subject: [PATCH] util/admin: support admin check index on partition table (#17183) (#17390) --- executor/admin_test.go | 26 +++++++++ executor/builder.go | 23 +------- executor/executor.go | 63 ++------------------- planner/core/common_plans.go | 10 +--- planner/core/planbuilder.go | 105 +++++++++++++++++++---------------- 5 files changed, 92 insertions(+), 135 deletions(-) diff --git a/executor/admin_test.go b/executor/admin_test.go index b6bd236925231..d42979b2c54dc 100644 --- a/executor/admin_test.go +++ b/executor/admin_test.go @@ -45,6 +45,32 @@ func (s *testSuite1) TestAdminCheckIndexRange(c *C) { result.Check(testkit.Rows("-1 hi 4", "2 cd 2")) } +func (s *testSuite2) TestAdminCheckIndex(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + check := func() { + tk.MustExec("insert admin_test (c1, c2) values (1, 1), (2, 2), (5, 5), (10, 10), (11, 11), (NULL, NULL)") + tk.MustExec("admin check index admin_test c1") + tk.MustExec("admin check index admin_test c2") + } + tk.MustExec("drop table if exists admin_test") + tk.MustExec("create table admin_test (c1 int, c2 int, c3 int default 1, index (c1), unique key(c2))") + check() + + // Test for hash partition table. + tk.MustExec("drop table if exists admin_test") + tk.MustExec("create table admin_test (c1 int, c2 int, c3 int default 1, index (c1), unique key(c2)) partition by hash(c2) partitions 5;") + check() + + // Test for range partition table. + tk.MustExec("drop table if exists admin_test") + tk.MustExec(`create table admin_test (c1 int, c2 int, c3 int default 1, index (c1), unique key(c2)) PARTITION BY RANGE ( c2 ) ( + PARTITION p0 VALUES LESS THAN (5), + PARTITION p1 VALUES LESS THAN (10), + PARTITION p2 VALUES LESS THAN (MAXVALUE))`) + check() +} + func (s *testSuite2) TestAdminRecoverIndex(c *C) { tk := testkit.NewTestKit(c, s.store) tk.MustExec("use test") diff --git a/executor/builder.go b/executor/builder.go index 69731d389ee19..a42858cd64c16 100644 --- a/executor/builder.go +++ b/executor/builder.go @@ -97,8 +97,6 @@ func (b *executorBuilder) build(p plannercore.Plan) Executor { return b.buildChange(v) case *plannercore.CheckTable: return b.buildCheckTable(v) - case *plannercore.CheckIndex: - return b.buildCheckIndex(v) case *plannercore.RecoverIndex: return b.buildRecoverIndex(v) case *plannercore.CleanupIndex: @@ -304,26 +302,6 @@ func (b *executorBuilder) buildShowSlow(v *plannercore.ShowSlow) Executor { return e } -func (b *executorBuilder) buildCheckIndex(v *plannercore.CheckIndex) Executor { - readerExec, err := buildNoRangeIndexLookUpReader(b, v.IndexLookUpReader) - if err != nil { - b.err = err - return nil - } - - buildIndexLookUpChecker(b, v.IndexLookUpReader, readerExec) - - e := &CheckIndexExec{ - baseExecutor: newBaseExecutor(b.ctx, v.Schema(), v.ExplainID()), - dbName: v.DBName, - tableName: readerExec.table.Meta().Name.L, - idxName: v.IdxName, - is: b.is, - src: readerExec, - } - return e -} - // buildIndexLookUpChecker builds check information to IndexLookUpReader. func buildIndexLookUpChecker(b *executorBuilder, readerPlan *plannercore.PhysicalIndexLookUpReader, readerExec *IndexLookUpExecutor) { @@ -377,6 +355,7 @@ func (b *executorBuilder) buildCheckTable(v *plannercore.CheckTable) Executor { srcs: readerExecs, exitCh: make(chan struct{}), retCh: make(chan error, len(readerExecs)), + checkIndex: v.CheckIndex, } return e } diff --git a/executor/executor.go b/executor/executor.go index e82cf5a55b910..7beaa637830a8 100644 --- a/executor/executor.go +++ b/executor/executor.go @@ -71,7 +71,6 @@ var ( _ Executor = &TableScanExec{} _ Executor = &TopNExec{} _ Executor = &UnionExec{} - _ Executor = &CheckIndexExec{} _ Executor = &HashJoinExec{} _ Executor = &IndexLookUpExecutor{} _ Executor = &MergeJoinExec{} @@ -481,6 +480,7 @@ type CheckTableExec struct { is infoschema.InfoSchema exitCh chan struct{} retCh chan error + checkIndex bool } // Open implements the Executor Open interface. @@ -568,6 +568,10 @@ func (e *CheckTableExec) Next(ctx context.Context, req *chunk.Chunk) error { } greater, idxOffset, err := admin.CheckIndicesCount(e.ctx, e.dbName, e.table.Meta().Name.O, idxNames) if err != nil { + // For admin check index statement, for speed up and compatibility, doesn't do below checks. + if e.checkIndex { + return errors.Trace(err) + } if greater == admin.IdxCntGreater { err = e.checkTableIndexHandle(ctx, e.indexInfos[idxOffset]) } else if greater == admin.TblCntGreater { @@ -590,7 +594,7 @@ func (e *CheckTableExec) Next(ctx context.Context, req *chunk.Chunk) error { util.WithRecovery(func() { err1 := e.checkIndexHandle(ctx, e.srcs[num]) if err1 != nil { - logutil.Logger(ctx).Info("check index handle failed", zap.Error(err)) + logutil.Logger(ctx).Info("check index handle failed", zap.Error(err1)) } }, e.handlePanic) }(i) @@ -634,61 +638,6 @@ func (e *CheckTableExec) checkTableRecord(idxOffset int) error { return nil } -// CheckIndexExec represents the executor of checking an index. -// It is built from the "admin check index" statement, and it checks -// the consistency of the index data with the records of the table. -type CheckIndexExec struct { - baseExecutor - - dbName string - tableName string - idxName string - src *IndexLookUpExecutor - done bool - is infoschema.InfoSchema -} - -// Open implements the Executor Open interface. -func (e *CheckIndexExec) Open(ctx context.Context) error { - if err := e.baseExecutor.Open(ctx); err != nil { - return err - } - if err := e.src.Open(ctx); err != nil { - return err - } - e.done = false - return nil -} - -// Close implements the Executor Close interface. -func (e *CheckIndexExec) Close() error { - return e.src.Close() -} - -// Next implements the Executor Next interface. -func (e *CheckIndexExec) Next(ctx context.Context, req *chunk.Chunk) error { - if e.done { - return nil - } - defer func() { e.done = true }() - - _, _, err := admin.CheckIndicesCount(e.ctx, e.dbName, e.tableName, []string{e.idxName}) - if err != nil { - return err - } - chk := newFirstChunk(e.src) - for { - err := Next(ctx, e.src, chk) - if err != nil { - return err - } - if chk.NumRows() == 0 { - break - } - } - return nil -} - // ShowSlowExec represents the executor of showing the slow queries. // It is build from the "admin show slow" statement: // admin show slow top [internal | all] N diff --git a/planner/core/common_plans.go b/planner/core/common_plans.go index ed536f5bae2c1..a612dbad9ccb7 100644 --- a/planner/core/common_plans.go +++ b/planner/core/common_plans.go @@ -80,6 +80,7 @@ type CheckTable struct { Table table.Table IndexInfos []*model.IndexInfo IndexLookUpReaders []*PhysicalIndexLookUpReader + CheckIndex bool } // RecoverIndex is used for backfilling corrupted index data. @@ -98,15 +99,6 @@ type CleanupIndex struct { IndexName string } -// CheckIndex is used for checking index data, built from the 'admin check index' statement. -type CheckIndex struct { - baseSchemaProducer - - IndexLookUpReader *PhysicalIndexLookUpReader - DBName string - IdxName string -} - // CheckIndexRange is used for checking index data, output the index values that handle within begin and end. type CheckIndexRange struct { baseSchemaProducer diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index 52d5530e08d63..6b0929d26d578 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -605,53 +605,15 @@ func (b *PlanBuilder) buildPrepare(x *ast.PrepareStmt) Plan { return p } -func (b *PlanBuilder) buildCheckIndex(ctx context.Context, dbName model.CIStr, as *ast.AdminStmt) (Plan, error) { - tblName := as.Tables[0] - tbl, err := b.is.TableByName(dbName, tblName.Name) - if err != nil { - return nil, err - } - tblInfo := tbl.Meta() - - // get index information - var idx *model.IndexInfo - for _, index := range tblInfo.Indices { - if index.Name.L == strings.ToLower(as.Index) { - idx = index - break - } - } - if idx == nil { - return nil, errors.Errorf("index %s do not exist", as.Index) - } - if idx.State != model.StatePublic { - return nil, errors.Errorf("index %s state %s isn't public", as.Index, idx.State) - } - - return b.buildPhysicalIndexLookUpReader(ctx, dbName, tbl, idx) -} - func (b *PlanBuilder) buildAdmin(ctx context.Context, as *ast.AdminStmt) (Plan, error) { var ret Plan var err error switch as.Tp { - case ast.AdminCheckTable: + case ast.AdminCheckTable, ast.AdminCheckIndex: ret, err = b.buildAdminCheckTable(ctx, as) if err != nil { return ret, err } - case ast.AdminCheckIndex: - dbName := as.Tables[0].Schema - readerPlan, err := b.buildCheckIndex(ctx, dbName, as) - if err != nil { - return ret, err - } - - ret = &CheckIndex{ - DBName: dbName.L, - IdxName: as.Index, - IndexLookUpReader: readerPlan.(*PhysicalIndexLookUpReader), - } case ast.AdminRecoverIndex: p := &RecoverIndex{Table: as.Tables[0], IndexName: as.Index} p.SetSchema(buildRecoverIndexFields()) @@ -857,12 +819,12 @@ func (b *PlanBuilder) buildPhysicalIndexLookUpReader(ctx context.Context, dbName return rootT.p, nil } -func (b *PlanBuilder) buildPhysicalIndexLookUpReaders(ctx context.Context, dbName model.CIStr, tbl table.Table) ([]Plan, []*model.IndexInfo, error) { +func (b *PlanBuilder) buildPhysicalIndexLookUpReaders(ctx context.Context, dbName model.CIStr, tbl table.Table, indices []table.Index) ([]Plan, []*model.IndexInfo, error) { tblInfo := tbl.Meta() // get index information indexInfos := make([]*model.IndexInfo, 0, len(tblInfo.Indices)) indexLookUpReaders := make([]Plan, 0, len(tblInfo.Indices)) - for _, idx := range tbl.Indices() { + for _, idx := range indices { idxInfo := idx.Meta() if idxInfo.State != model.StatePublic { logutil.Logger(context.Background()).Info("build physical index lookup reader, the index isn't public", @@ -896,17 +858,40 @@ func (b *PlanBuilder) buildPhysicalIndexLookUpReaders(ctx context.Context, dbNam } func (b *PlanBuilder) buildAdminCheckTable(ctx context.Context, as *ast.AdminStmt) (*CheckTable, error) { - tbl := as.Tables[0] + tblName := as.Tables[0] tableInfo := as.Tables[0].TableInfo - table, ok := b.is.TableByID(tableInfo.ID) + tbl, ok := b.is.TableByID(tableInfo.ID) if !ok { - return nil, infoschema.ErrTableNotExists.GenWithStackByArgs(tbl.DBInfo.Name.O, tableInfo.Name.O) + return nil, infoschema.ErrTableNotExists.GenWithStackByArgs(tblName.DBInfo.Name.O, tableInfo.Name.O) } p := &CheckTable{ - DBName: tbl.Schema.O, - Table: table, + DBName: tblName.Schema.O, + Table: tbl, + } + var readerPlans []Plan + var indexInfos []*model.IndexInfo + var err error + if as.Tp == ast.AdminCheckIndex { + // get index information + var idx table.Index + idxName := strings.ToLower(as.Index) + for _, index := range tbl.Indices() { + if index.Meta().Name.L == idxName { + idx = index + break + } + } + if idx == nil { + return nil, errors.Errorf("index %s do not exist", as.Index) + } + if idx.Meta().State != model.StatePublic { + return nil, errors.Errorf("index %s state %s isn't public", as.Index, idx.Meta().State) + } + p.CheckIndex = true + readerPlans, indexInfos, err = b.buildPhysicalIndexLookUpReaders(ctx, tblName.Schema, tbl, []table.Index{idx}) + } else { + readerPlans, indexInfos, err = b.buildPhysicalIndexLookUpReaders(ctx, tblName.Schema, tbl, tbl.Indices()) } - readerPlans, indexInfos, err := b.buildPhysicalIndexLookUpReaders(ctx, tbl.Schema, table) if err != nil { return nil, errors.Trace(err) } @@ -919,6 +904,32 @@ func (b *PlanBuilder) buildAdminCheckTable(ctx context.Context, as *ast.AdminStm return p, nil } +func (b *PlanBuilder) buildCheckIndex(ctx context.Context, dbName model.CIStr, as *ast.AdminStmt) (Plan, error) { + tblName := as.Tables[0] + tbl, err := b.is.TableByName(dbName, tblName.Name) + if err != nil { + return nil, err + } + tblInfo := tbl.Meta() + + // get index information + var idx *model.IndexInfo + for _, index := range tblInfo.Indices { + if index.Name.L == strings.ToLower(as.Index) { + idx = index + break + } + } + if idx == nil { + return nil, errors.Errorf("index %s do not exist", as.Index) + } + if idx.State != model.StatePublic { + return nil, errors.Errorf("index %s state %s isn't public", as.Index, idx.State) + } + + return b.buildPhysicalIndexLookUpReader(ctx, dbName, tbl, idx) +} + func (b *PlanBuilder) buildCheckIndexSchema(tn *ast.TableName, indexName string) (*expression.Schema, error) { schema := expression.NewSchema() indexName = strings.ToLower(indexName)