From c237781cad40acb059e8513103e515b01625d59d Mon Sep 17 00:00:00 2001 From: Haibin Xie Date: Wed, 26 Apr 2017 10:36:44 +0800 Subject: [PATCH] plan, executor: make Analyze a Plan (#3130) * executor: remove indexExec's depedency on indexPlan * plan, executor: make Analyze a Plan * address comment * address comment * fix gofmt --- executor/analyze.go | 2 +- executor/builder.go | 112 +++++++++++++++++++++++------- executor/distsql.go | 76 ++++++++++---------- executor/executor.go | 13 ++-- executor/union_scan.go | 2 +- plan/initialize.go | 9 --- plan/logical_plans.go | 1 - plan/match_property.go | 11 --- plan/new_physical_plan_builder.go | 2 +- plan/physical_plan_builder.go | 69 +----------------- plan/physical_plans.go | 10 --- plan/planbuilder.go | 10 +-- plan/plans.go | 20 +++--- plan/refiner.go | 50 ++++++------- 14 files changed, 179 insertions(+), 208 deletions(-) diff --git a/executor/analyze.go b/executor/analyze.go index 49b6400f224c5..6ae4792f1a379 100644 --- a/executor/analyze.go +++ b/executor/analyze.go @@ -174,7 +174,7 @@ func analyzeColumns(exec *XSelectTableExec) analyzeResult { } func analyzeIndex(exec *XSelectIndexExec) analyzeResult { - count, hg, err := statistics.BuildIndex(exec.ctx, defaultBucketCount, exec.indexPlan.Index.ID, &recordSet{executor: exec}) + count, hg, err := statistics.BuildIndex(exec.ctx, defaultBucketCount, exec.index.ID, &recordSet{executor: exec}) return analyzeResult{tableID: exec.tableInfo.ID, hist: []*statistics.Histogram{hg}, count: count, isIndex: 1, err: err} } diff --git a/executor/builder.go b/executor/builder.go index 93e7822a8730f..154460cdc430c 100644 --- a/executor/builder.go +++ b/executor/builder.go @@ -343,9 +343,9 @@ func (b *executorBuilder) buildUnionScanExec(v *plan.PhysicalUnionScan) Executor us.conditions = v.Conditions us.buildAndSortAddedRows(x.table, x.asName) case *XSelectIndexExec: - us.desc = x.indexPlan.Desc - for _, ic := range x.indexPlan.Index.Columns { - for i, col := range x.indexPlan.Schema().Columns { + us.desc = x.desc + for _, ic := range x.index.Columns { + for i, col := range x.schema.Columns { if col.ColName.L == ic.Name.L { us.usedIndex = append(us.usedIndex, i) break @@ -582,18 +582,26 @@ func (b *executorBuilder) buildIndexScan(v *plan.PhysicalIndexScan) Executor { client := b.ctx.GetClient() supportDesc := client.SupportRequestType(kv.ReqTypeIndex, kv.ReqSubTypeDesc) e := &XSelectIndexExec{ - tableInfo: v.Table, - ctx: b.ctx, - supportDesc: supportDesc, - asName: v.TableAsName, - table: table, - indexPlan: v, - singleReadMode: !v.DoubleRead, - startTS: startTS, - where: v.TableConditionPBExpr, - aggregate: v.Aggregated, - aggFuncs: v.AggFuncsPB, - byItems: v.GbyItemsPB, + tableInfo: v.Table, + ctx: b.ctx, + supportDesc: supportDesc, + asName: v.TableAsName, + table: table, + singleReadMode: !v.DoubleRead, + startTS: startTS, + where: v.TableConditionPBExpr, + schema: v.Schema(), + ranges: v.Ranges, + limitCount: v.LimitCount, + sortItemsPB: v.SortItemsPB, + columns: v.Columns, + index: v.Index, + desc: v.Desc, + outOfOrder: v.OutOfOrder, + indexConditionPBExpr: v.IndexConditionPBExpr, + aggregate: v.Aggregated, + aggFuncs: v.AggFuncsPB, + byItems: v.GbyItemsPB, } vars := b.ctx.GetSessionVars() if v.OutOfOrder { @@ -605,9 +613,9 @@ func (b *executorBuilder) buildIndexScan(v *plan.PhysicalIndexScan) Executor { } if !e.aggregate && e.singleReadMode { // Single read index result has the schema of full index columns. - schemaColumns := make([]*expression.Column, len(e.indexPlan.Index.Columns)) - for i, col := range e.indexPlan.Index.Columns { - colInfo := e.indexPlan.Table.Columns[col.Offset] + schemaColumns := make([]*expression.Column, len(e.index.Columns)) + for i, col := range e.index.Columns { + colInfo := e.tableInfo.Columns[col.Offset] schemaColumns[i] = &expression.Column{ Index: i, ColName: col.Name, @@ -743,22 +751,72 @@ func (b *executorBuilder) buildCache(v *plan.Cache) Executor { } } +func (b *executorBuilder) buildTableScanForAnalyze(tblInfo *model.TableInfo, cols []*model.ColumnInfo) Executor { + startTS := b.getStartTS() + if b.err != nil { + return nil + } + table, _ := b.is.TableByID(tblInfo.ID) + schema := expression.NewSchema(expression.ColumnInfos2Columns(tblInfo.Name, cols)...) + ranges := []types.IntColumnRange{{math.MinInt64, math.MaxInt64}} + e := &XSelectTableExec{ + tableInfo: tblInfo, + ctx: b.ctx, + startTS: startTS, + table: table, + schema: schema, + Columns: cols, + ranges: ranges, + } + return e +} + +func (b *executorBuilder) buildIndexScanForAnalyze(tblInfo *model.TableInfo, idxInfo *model.IndexInfo) Executor { + startTS := b.getStartTS() + if b.err != nil { + return nil + } + table, _ := b.is.TableByID(tblInfo.ID) + cols := make([]*model.ColumnInfo, len(idxInfo.Columns)) + for i, col := range idxInfo.Columns { + cols[i] = tblInfo.Columns[col.Offset] + } + schema := expression.NewSchema(expression.ColumnInfos2Columns(tblInfo.Name, cols)...) + idxRange := &types.IndexRange{LowVal: []types.Datum{types.MinNotNullDatum()}, HighVal: []types.Datum{types.MaxValueDatum()}} + scanConcurrency := b.ctx.GetSessionVars().IndexSerialScanConcurrency + e := &XSelectIndexExec{ + tableInfo: tblInfo, + ctx: b.ctx, + table: table, + singleReadMode: true, + startTS: startTS, + idxColsSchema: schema, + schema: schema, + ranges: []*types.IndexRange{idxRange}, + columns: cols, + index: idxInfo, + outOfOrder: false, + scanConcurrency: scanConcurrency, + } + return e +} + func (b *executorBuilder) buildAnalyze(v *plan.Analyze) Executor { e := &AnalyzeExec{ ctx: b.ctx, tasks: make([]analyzeTask, 0, len(v.Children())), } - pkTasks := v.Children()[:len(v.PkTasks)] - for _, task := range pkTasks { - e.tasks = append(e.tasks, analyzeTask{taskType: pkTask, src: b.build(task)}) + for _, task := range v.PkTasks { + e.tasks = append(e.tasks, analyzeTask{taskType: pkTask, + src: b.buildTableScanForAnalyze(task.TableInfo, []*model.ColumnInfo{task.PKInfo})}) } - colTasks := v.Children()[len(v.PkTasks) : len(v.PkTasks)+len(v.ColTasks)] - for _, task := range colTasks { - e.tasks = append(e.tasks, analyzeTask{taskType: colTask, src: b.build(task)}) + for _, task := range v.ColTasks { + e.tasks = append(e.tasks, analyzeTask{taskType: colTask, + src: b.buildTableScanForAnalyze(task.TableInfo, task.ColsInfo)}) } - idxTasks := v.Children()[len(v.PkTasks)+len(v.ColTasks):] - for _, task := range idxTasks { - e.tasks = append(e.tasks, analyzeTask{taskType: idxTask, src: b.build(task)}) + for _, task := range v.IdxTasks { + e.tasks = append(e.tasks, analyzeTask{taskType: idxTask, + src: b.buildIndexScanForAnalyze(task.TableInfo, task.IndexInfo)}) } return e } diff --git a/executor/distsql.go b/executor/distsql.go index 27f8e5bb44a85..3091680cbc9a3 100644 --- a/executor/distsql.go +++ b/executor/distsql.go @@ -28,7 +28,6 @@ import ( "github.com/pingcap/tidb/kv" "github.com/pingcap/tidb/model" "github.com/pingcap/tidb/mysql" - "github.com/pingcap/tidb/plan" "github.com/pingcap/tidb/sessionctx/variable" "github.com/pingcap/tidb/table" "github.com/pingcap/tidb/tablecodec" @@ -338,8 +337,6 @@ type XSelectIndexExec struct { isMemDB bool singleReadMode bool - indexPlan *plan.PhysicalIndexScan - // Variables only used for single read. result distsql.SelectResult partialResult distsql.PartialResult @@ -351,9 +348,18 @@ type XSelectIndexExec struct { taskCurr *lookupTableTask handleCount uint64 // returned handle count in double read. - where *tipb.Expr - startTS uint64 - returnedRows uint64 // returned row count + where *tipb.Expr + startTS uint64 + returnedRows uint64 // returned row count + schema *expression.Schema + ranges []*types.IndexRange + limitCount *int64 + sortItemsPB []*tipb.ByItem + columns []*model.ColumnInfo + index *model.IndexInfo + desc bool + outOfOrder bool + indexConditionPBExpr *tipb.Expr /* The following attributes are used for aggregation push down. @@ -373,7 +379,7 @@ type XSelectIndexExec struct { // Schema implements Exec Schema interface. func (e *XSelectIndexExec) Schema() *expression.Schema { - return e.indexPlan.Schema() + return e.schema } // Close implements Exec Close interface. @@ -396,7 +402,7 @@ func (e *XSelectIndexExec) Close() error { // Next implements the Executor Next interface. func (e *XSelectIndexExec) Next() (*Row, error) { - if e.indexPlan.LimitCount != nil && len(e.indexPlan.SortItemsPB) == 0 && e.returnedRows >= uint64(*e.indexPlan.LimitCount) { + if e.limitCount != nil && len(e.sortItemsPB) == 0 && e.returnedRows >= uint64(*e.limitCount) { return nil, nil } e.returnedRows++ @@ -483,9 +489,9 @@ func decodeRawValues(values []types.Datum, schema *expression.Schema) error { } func (e *XSelectIndexExec) indexRowToTableRow(handle int64, indexRow []types.Datum) []types.Datum { - tableRow := make([]types.Datum, len(e.indexPlan.Columns)) - for i, tblCol := range e.indexPlan.Columns { - if table.ToColumn(tblCol).IsPKHandleColumn(e.indexPlan.Table) { + tableRow := make([]types.Datum, len(e.columns)) + for i, tblCol := range e.columns { + if table.ToColumn(tblCol).IsPKHandleColumn(e.tableInfo) { if mysql.HasUnsignedFlag(tblCol.FieldType.Flag) { tableRow[i] = types.NewUintDatum(uint64(handle)) } else { @@ -493,7 +499,7 @@ func (e *XSelectIndexExec) indexRowToTableRow(handle int64, indexRow []types.Dat } continue } - for j, idxCol := range e.indexPlan.Index.Columns { + for j, idxCol := range e.index.Columns { if tblCol.Name.L == idxCol.Name.L { tableRow[i] = indexRow[j] break @@ -546,7 +552,7 @@ func (e *XSelectIndexExec) nextForDoubleRead() (*Row, error) { func (e *XSelectIndexExec) slowQueryInfo(duration time.Duration) string { return fmt.Sprintf("time: %v, table: %s(%d), index: %s(%d), partials: %d, concurrency: %d, rows: %d, handles: %d", - duration, e.tableInfo.Name, e.tableInfo.ID, e.indexPlan.Index.Name, e.indexPlan.Index.ID, + duration, e.tableInfo.Name, e.tableInfo.ID, e.index.Name, e.index.ID, e.partialCount, e.scanConcurrency, e.returnedRows, e.handleCount) } @@ -601,36 +607,36 @@ func (e *XSelectIndexExec) doIndexRequest() (distsql.SelectResult, error) { selIdxReq.StartTs = e.startTS selIdxReq.TimeZoneOffset = timeZoneOffset() selIdxReq.Flags = statementContextToFlags(e.ctx.GetSessionVars().StmtCtx) - selIdxReq.IndexInfo = distsql.IndexToProto(e.table.Meta(), e.indexPlan.Index) - if e.indexPlan.Desc { - selIdxReq.OrderBy = []*tipb.ByItem{{Desc: e.indexPlan.Desc}} + selIdxReq.IndexInfo = distsql.IndexToProto(e.table.Meta(), e.index) + if e.desc { + selIdxReq.OrderBy = []*tipb.ByItem{{Desc: e.desc}} } // If the index is single read, we can push topn down. if e.singleReadMode { - selIdxReq.Limit = e.indexPlan.LimitCount + selIdxReq.Limit = e.limitCount // If sortItemsPB is empty, the Desc may be true and we shouldn't overwrite it. - if len(e.indexPlan.SortItemsPB) > 0 { - selIdxReq.OrderBy = e.indexPlan.SortItemsPB + if len(e.sortItemsPB) > 0 { + selIdxReq.OrderBy = e.sortItemsPB } - } else if e.where == nil && len(e.indexPlan.SortItemsPB) == 0 { + } else if e.where == nil && len(e.sortItemsPB) == 0 { // If the index is double read but table scan has no filter or topn, we can push limit down to index. - selIdxReq.Limit = e.indexPlan.LimitCount + selIdxReq.Limit = e.limitCount } - selIdxReq.Where = e.indexPlan.IndexConditionPBExpr + selIdxReq.Where = e.indexConditionPBExpr if e.singleReadMode { selIdxReq.Aggregates = e.aggFuncs selIdxReq.GroupBy = e.byItems } - fieldTypes := make([]*types.FieldType, len(e.indexPlan.Index.Columns)) - for i, v := range e.indexPlan.Index.Columns { + fieldTypes := make([]*types.FieldType, len(e.index.Columns)) + for i, v := range e.index.Columns { fieldTypes[i] = &(e.table.Cols()[v.Offset].FieldType) } sc := e.ctx.GetSessionVars().StmtCtx - keyRanges, err := indexRangesToKVRanges(sc, e.table.Meta().ID, e.indexPlan.Index.ID, e.indexPlan.Ranges, fieldTypes) + keyRanges, err := indexRangesToKVRanges(sc, e.table.Meta().ID, e.index.ID, e.ranges, fieldTypes) if err != nil { return nil, errors.Trace(err) } - return distsql.Select(e.ctx.GetClient(), e.ctx.GoCtx(), selIdxReq, keyRanges, e.scanConcurrency, !e.indexPlan.OutOfOrder) + return distsql.Select(e.ctx.GetClient(), e.ctx.GoCtx(), selIdxReq, keyRanges, e.scanConcurrency, !e.outOfOrder) } func (e *XSelectIndexExec) buildTableTasks(handles []int64) []*lookupTableTask { @@ -647,7 +653,7 @@ func (e *XSelectIndexExec) buildTableTasks(handles []int64) []*lookupTableTask { } var indexOrder map[int64]int - if !e.indexPlan.OutOfOrder { + if !e.outOfOrder { // Save the index order. indexOrder = make(map[int64]int, len(handles)) for i, h := range handles { @@ -692,10 +698,10 @@ func (e *XSelectIndexExec) executeTask(task *lookupTableTask) error { if err != nil { return errors.Trace(err) } - if !e.indexPlan.OutOfOrder { + if !e.outOfOrder { // Restore the index order. sorter := &rowsSorter{order: task.indexOrder, rows: task.rows} - if e.indexPlan.Desc && !e.supportDesc { + if e.desc && !e.supportDesc { sort.Sort(sort.Reverse(sorter)) } else { sort.Sort(sorter) @@ -744,7 +750,7 @@ func (e *XSelectIndexExec) extractRowsFromPartialResult(t table.Table, partialRe if err != nil { return nil, errors.Trace(err) } - row := resultRowToRow(t, h, values, e.indexPlan.TableAsName) + row := resultRowToRow(t, h, values, e.asName) rows = append(rows, row) } return rows, nil @@ -753,9 +759,9 @@ func (e *XSelectIndexExec) extractRowsFromPartialResult(t table.Table, partialRe func (e *XSelectIndexExec) doTableRequest(handles []int64) (distsql.SelectResult, error) { // The handles are not in original index order, so we can't push limit here. selTableReq := new(tipb.SelectRequest) - if e.indexPlan.OutOfOrder { - selTableReq.Limit = e.indexPlan.LimitCount - selTableReq.OrderBy = e.indexPlan.SortItemsPB + if e.outOfOrder { + selTableReq.Limit = e.limitCount + selTableReq.OrderBy = e.sortItemsPB } selTableReq.StartTs = e.startTS selTableReq.TimeZoneOffset = timeZoneOffset() @@ -763,8 +769,8 @@ func (e *XSelectIndexExec) doTableRequest(handles []int64) (distsql.SelectResult selTableReq.TableInfo = &tipb.TableInfo{ TableId: e.table.Meta().ID, } - selTableReq.TableInfo.Columns = distsql.ColumnsToProto(e.indexPlan.Columns, e.table.Meta().PKIsHandle) - err := setPBColumnsDefaultValue(e.ctx, selTableReq.TableInfo.Columns, e.indexPlan.Columns) + selTableReq.TableInfo.Columns = distsql.ColumnsToProto(e.columns, e.table.Meta().PKIsHandle) + err := setPBColumnsDefaultValue(e.ctx, selTableReq.TableInfo.Columns, e.columns) if err != nil { return nil, errors.Trace(err) } diff --git a/executor/executor.go b/executor/executor.go index 9b68a38cd5fb0..ca8ea95602ead 100644 --- a/executor/executor.go +++ b/executor/executor.go @@ -512,15 +512,16 @@ func (e *SelectionExec) initController() error { } x.ranges = ranges case *XSelectIndexExec: - x.indexPlan.AccessCondition, newConds, _, _ = plan.DetachIndexScanConditions(newConds, x.indexPlan.Index) - idxConds, tblConds := plan.DetachIndexFilterConditions(newConds, x.indexPlan.Index.Columns, x.indexPlan.Table) - x.indexPlan.IndexConditionPBExpr, _, _ = plan.ExpressionsToPB(sc, idxConds, client) - x.indexPlan.TableConditionPBExpr, _, _ = plan.ExpressionsToPB(sc, tblConds, client) - err := plan.BuildIndexRange(sc, x.indexPlan) + accessCondition, newConds, _, accessInAndEqCount := plan.DetachIndexScanConditions(newConds, x.index) + idxConds, tblConds := plan.DetachIndexFilterConditions(newConds, x.index.Columns, x.tableInfo) + x.indexConditionPBExpr, _, _ = plan.ExpressionsToPB(sc, idxConds, client) + tableConditionPBExpr, _, _ := plan.ExpressionsToPB(sc, tblConds, client) + var err error + x.ranges, err = plan.BuildIndexRange(sc, x.tableInfo, x.index, accessInAndEqCount, accessCondition) if err != nil { return errors.Trace(err) } - x.where = x.indexPlan.TableConditionPBExpr + x.where = tableConditionPBExpr default: return errors.Errorf("Error type of Executor: %T", x) } diff --git a/executor/union_scan.go b/executor/union_scan.go index 4fa4ed80891ed..98ec0b0adb66e 100644 --- a/executor/union_scan.go +++ b/executor/union_scan.go @@ -243,7 +243,7 @@ func (us *UnionScanExec) buildAndSortAddedRows(t table.Table, asName *model.CISt if t, ok := us.Src.(*XSelectTableExec); ok { columns = t.Columns } else { - columns = us.Src.(*XSelectIndexExec).indexPlan.Columns + columns = us.Src.(*XSelectIndexExec).columns } for _, col := range columns { newData = append(newData, data[col.Offset]) diff --git a/plan/initialize.go b/plan/initialize.go index b76f6c180781d..a798c6e221a12 100644 --- a/plan/initialize.go +++ b/plan/initialize.go @@ -72,8 +72,6 @@ const ( TypeUpate = "Update" // TypeDelete is the type of Delete. TypeDelete = "Delete" - // TypeAnalyze is the type of Analyze. - TypeAnalyze = "Analyze" // TypeIndexLookUp is the type of IndexLookUp. TypeIndexLookUp = "IndexLookUp" // TypeTableReader is the type of TableReader. @@ -190,13 +188,6 @@ func (p Show) init(allocator *idAllocator, ctx context.Context) *Show { return &p } -func (p Analyze) init(allocator *idAllocator, ctx context.Context) *Analyze { - p.basePlan = newBasePlan(TypeAnalyze, allocator, ctx, &p) - p.baseLogicalPlan = newBaseLogicalPlan(p.basePlan) - p.basePhysicalPlan = newBasePhysicalPlan(p.basePlan) - return &p -} - func (p SelectLock) init(allocator *idAllocator, ctx context.Context) *SelectLock { p.basePlan = newBasePlan(TypeLock, allocator, ctx, &p) p.baseLogicalPlan = newBaseLogicalPlan(p.basePlan) diff --git a/plan/logical_plans.go b/plan/logical_plans.go index ed2d019941700..eba52a7fc0e1c 100644 --- a/plan/logical_plans.go +++ b/plan/logical_plans.go @@ -40,7 +40,6 @@ var ( _ LogicalPlan = &Limit{} _ LogicalPlan = &Show{} _ LogicalPlan = &Insert{} - _ LogicalPlan = &Analyze{} ) // JoinType contains CrossJoin, InnerJoin, LeftOuterJoin, RightOuterJoin, FullOuterJoin, SemiJoin. diff --git a/plan/match_property.go b/plan/match_property.go index 8c66946068955..d6dcbabf52020 100644 --- a/plan/match_property.go +++ b/plan/match_property.go @@ -279,14 +279,3 @@ func (p *Projection) matchProperty(_ *requiredProperty, childPlanInfo ...*physic np.SetChildren(childPlanInfo[0].p) return &physicalPlanInfo{p: np, cost: childPlanInfo[0].cost, reliable: childPlanInfo[0].reliable} } - -// matchProperty implements PhysicalPlan matchProperty interface. -func (p *Analyze) matchProperty(_ *requiredProperty, childPlanInfo ...*physicalPlanInfo) *physicalPlanInfo { - children := make([]Plan, 0, len(childPlanInfo)) - for _, res := range childPlanInfo { - children = append(children, res.p) - } - np := p.Copy() - np.SetChildren(children...) - return &physicalPlanInfo{p: np} -} diff --git a/plan/new_physical_plan_builder.go b/plan/new_physical_plan_builder.go index 68ab7f6e1c3f3..5e034fa10c8b9 100644 --- a/plan/new_physical_plan_builder.go +++ b/plan/new_physical_plan_builder.go @@ -378,7 +378,7 @@ func (p *DataSource) convertToIndexScan(prop *requiredProp, idx *model.IndexInfo conds = append(conds, cond.Clone()) } is.AccessCondition, is.filterCondition, is.accessEqualCount, is.accessInAndEqCount = DetachIndexScanConditions(conds, idx) - err = BuildIndexRange(sc, is) + is.Ranges, err = BuildIndexRange(sc, is.Table, is.Index, is.accessInAndEqCount, is.AccessCondition) if err != nil { return nil, errors.Trace(err) } diff --git a/plan/physical_plan_builder.go b/plan/physical_plan_builder.go index 25c79b9a5c824..eed2b79138f33 100644 --- a/plan/physical_plan_builder.go +++ b/plan/physical_plan_builder.go @@ -143,7 +143,8 @@ func (p *DataSource) convert2IndexScan(prop *requiredProperty, index *model.Inde is.TableConditionPBExpr, is.tableFilterConditions, tblConds = ExpressionsToPB(sc, tblConds, client) newSel.Conditions = append(idxConds, tblConds...) } - err := BuildIndexRange(p.ctx.GetSessionVars().StmtCtx, is) + var err error + is.Ranges, err = BuildIndexRange(p.ctx.GetSessionVars().StmtCtx, is.Table, is.Index, is.accessInAndEqCount, is.AccessCondition) if err != nil { if !terror.ErrorEqual(err, types.ErrTruncated) { return nil, errors.Trace(err) @@ -1552,72 +1553,6 @@ func (p *LogicalApply) convert2PhysicalPlan(prop *requiredProperty) (*physicalPl return info, nil } -func (p *Analyze) prepareSimpleTableScan(tblInfo *model.TableInfo, cols []*model.ColumnInfo) *PhysicalTableScan { - ts := PhysicalTableScan{ - Table: tblInfo, - Columns: cols, - physicalTableSource: physicalTableSource{client: p.ctx.GetClient()}, - }.init(p.allocator, p.ctx) - ts.SetSchema(expression.NewSchema(expression.ColumnInfos2Columns(ts.Table.Name, cols)...)) - ts.readOnly = true - ts.Ranges = []types.IntColumnRange{{math.MinInt64, math.MaxInt64}} - return ts -} - -func (p *Analyze) prepareSimpleIndexScan(tblInfo *model.TableInfo, idxInfo *model.IndexInfo, cols []*model.ColumnInfo) *PhysicalIndexScan { - is := PhysicalIndexScan{ - Index: idxInfo, - Table: tblInfo, - Columns: cols, - OutOfOrder: false, - physicalTableSource: physicalTableSource{client: p.ctx.GetClient()}, - DoubleRead: false, - }.init(p.allocator, p.ctx) - is.SetSchema(expression.NewSchema(expression.ColumnInfos2Columns(tblInfo.Name, cols)...)) - is.readOnly = true - rb := rangeBuilder{sc: p.ctx.GetSessionVars().StmtCtx} - is.Ranges = rb.buildIndexRanges(fullRange, types.NewFieldType(mysql.TypeNull)) - return is -} - -// convert2PhysicalPlan implements the LogicalPlan convert2PhysicalPlan interface. -func (p *Analyze) convert2PhysicalPlan(prop *requiredProperty) (*physicalPlanInfo, error) { - info, err := p.getPlanInfo(prop) - if err != nil { - return nil, errors.Trace(err) - } - if info != nil { - return info, nil - } - var childInfos []*physicalPlanInfo - // TODO: It's inefficient to do table scan two times for massive data. - for _, task := range p.PkTasks { - ts := p.prepareSimpleTableScan(task.TableInfo, []*model.ColumnInfo{task.PKInfo}) - childInfos = append(childInfos, ts.matchProperty(prop, &physicalPlanInfo{count: 0})) - } - for _, task := range p.ColTasks { - ts := p.prepareSimpleTableScan(task.TableInfo, task.ColsInfo) - childInfos = append(childInfos, ts.matchProperty(prop, &physicalPlanInfo{count: 0})) - } - for _, task := range p.IdxTasks { - idx := task.IndexInfo - columns := make([]*model.ColumnInfo, 0, len(idx.Columns)) - for _, idxCol := range idx.Columns { - for _, col := range task.TableInfo.Columns { - if col.Name.L == idxCol.Name.L { - columns = append(columns, col) - break - } - } - } - is := p.prepareSimpleIndexScan(task.TableInfo, idx, columns) - childInfos = append(childInfos, is.matchProperty(prop, &physicalPlanInfo{count: 0})) - } - info = p.matchProperty(prop, childInfos...) - p.storePlanInfo(prop, info) - return info, nil -} - // addCachePlan will add a Cache plan above the plan whose father's IsCorrelated() is true but its own IsCorrelated() is false. func addCachePlan(p PhysicalPlan, allocator *idAllocator) []*expression.CorrelatedColumn { if len(p.Children()) == 0 { diff --git a/plan/physical_plans.go b/plan/physical_plans.go index da6cdcc647606..cac01a515a7d2 100644 --- a/plan/physical_plans.go +++ b/plan/physical_plans.go @@ -56,7 +56,6 @@ var ( _ PhysicalPlan = &Limit{} _ PhysicalPlan = &Show{} _ PhysicalPlan = &Insert{} - _ PhysicalPlan = &Analyze{} _ PhysicalPlan = &PhysicalIndexScan{} _ PhysicalPlan = &PhysicalTableScan{} _ PhysicalPlan = &PhysicalAggregation{} @@ -1064,12 +1063,3 @@ func (p *Cache) Copy() PhysicalPlan { np.basePhysicalPlan = newBasePhysicalPlan(np.basePlan) return &np } - -// Copy implements the PhysicalPlan Copy interface. -func (p *Analyze) Copy() PhysicalPlan { - np := *p - np.basePlan = p.basePlan.copy() - np.baseLogicalPlan = newBaseLogicalPlan(np.basePlan) - np.basePhysicalPlan = newBasePhysicalPlan(np.basePlan) - return &np -} diff --git a/plan/planbuilder.go b/plan/planbuilder.go index da792730991a3..45c1d0136d5ce 100644 --- a/plan/planbuilder.go +++ b/plan/planbuilder.go @@ -404,18 +404,18 @@ func getColsInfo(tn *ast.TableName) (indicesInfo []*model.IndexInfo, colsInfo [] return } -func (b *planBuilder) buildAnalyze(as *ast.AnalyzeTableStmt) LogicalPlan { - p := Analyze{}.init(b.allocator, b.ctx) +func (b *planBuilder) buildAnalyze(as *ast.AnalyzeTableStmt) Plan { + p := &Analyze{} for _, tbl := range as.TableNames { idxInfo, colInfo, pkInfo := getColsInfo(tbl) for _, idx := range idxInfo { - p.IdxTasks = append(p.IdxTasks, analyzeIndexTask{TableInfo: tbl.TableInfo, IndexInfo: idx}) + p.IdxTasks = append(p.IdxTasks, AnalyzeIndexTask{TableInfo: tbl.TableInfo, IndexInfo: idx}) } if len(colInfo) > 0 { - p.ColTasks = append(p.ColTasks, analyzeColumnsTask{TableInfo: tbl.TableInfo, ColsInfo: colInfo}) + p.ColTasks = append(p.ColTasks, AnalyzeColumnsTask{TableInfo: tbl.TableInfo, ColsInfo: colInfo}) } if pkInfo != nil { - p.PkTasks = append(p.PkTasks, analyzePKTask{TableInfo: tbl.TableInfo, PKInfo: pkInfo}) + p.PkTasks = append(p.PkTasks, AnalyzePKTask{TableInfo: tbl.TableInfo, PKInfo: pkInfo}) } } p.SetSchema(&expression.Schema{}) diff --git a/plan/plans.go b/plan/plans.go index d6c6632be36cf..ec396ccbc154a 100644 --- a/plan/plans.go +++ b/plan/plans.go @@ -125,31 +125,31 @@ type Insert struct { Ignore bool } -// Used only when pk is handle. -type analyzePKTask struct { +// AnalyzePKTask is used for analyze pk. Used only when pk is handle. +type AnalyzePKTask struct { TableInfo *model.TableInfo PKInfo *model.ColumnInfo } -type analyzeColumnsTask struct { +// AnalyzeColumnsTask is used for analyze columns. +type AnalyzeColumnsTask struct { TableInfo *model.TableInfo ColsInfo []*model.ColumnInfo } -type analyzeIndexTask struct { +// AnalyzeIndexTask is used for analyze index. +type AnalyzeIndexTask struct { TableInfo *model.TableInfo IndexInfo *model.IndexInfo } // Analyze represents an analyze plan type Analyze struct { - *basePlan - baseLogicalPlan - basePhysicalPlan + basePlan - PkTasks []analyzePKTask - ColTasks []analyzeColumnsTask - IdxTasks []analyzeIndexTask + PkTasks []AnalyzePKTask + ColTasks []AnalyzeColumnsTask + IdxTasks []AnalyzeIndexTask } // LoadData represents a loaddata plan. diff --git a/plan/refiner.go b/plan/refiner.go index 08b1c4ab8053c..9c29ec8330dbe 100644 --- a/plan/refiner.go +++ b/plan/refiner.go @@ -32,43 +32,45 @@ var fullRange = []rangePoint{ } // BuildIndexRange will build range of index for PhysicalIndexScan -func BuildIndexRange(sc *variable.StatementContext, p *PhysicalIndexScan) error { +func BuildIndexRange(sc *variable.StatementContext, tblInfo *model.TableInfo, index *model.IndexInfo, + accessInAndEqCount int, accessCondition []expression.Expression) ([]*types.IndexRange, error) { rb := rangeBuilder{sc: sc} - for i := 0; i < p.accessInAndEqCount; i++ { + var ranges []*types.IndexRange + for i := 0; i < accessInAndEqCount; i++ { // Build ranges for equal or in access conditions. - point := rb.build(p.AccessCondition[i]) - colOff := p.Index.Columns[i].Offset - tp := &p.Table.Columns[colOff].FieldType + point := rb.build(accessCondition[i]) + colOff := index.Columns[i].Offset + tp := &tblInfo.Columns[colOff].FieldType if i == 0 { - p.Ranges = rb.buildIndexRanges(point, tp) + ranges = rb.buildIndexRanges(point, tp) } else { - p.Ranges = rb.appendIndexRanges(p.Ranges, point, tp) + ranges = rb.appendIndexRanges(ranges, point, tp) } } rangePoints := fullRange // Build rangePoints for non-equal access conditions. - for i := p.accessInAndEqCount; i < len(p.AccessCondition); i++ { - rangePoints = rb.intersection(rangePoints, rb.build(p.AccessCondition[i])) - } - if p.accessInAndEqCount == 0 { - colOff := p.Index.Columns[0].Offset - tp := &p.Table.Columns[colOff].FieldType - p.Ranges = rb.buildIndexRanges(rangePoints, tp) - } else if p.accessInAndEqCount < len(p.AccessCondition) { - colOff := p.Index.Columns[p.accessInAndEqCount].Offset - tp := &p.Table.Columns[colOff].FieldType - p.Ranges = rb.appendIndexRanges(p.Ranges, rangePoints, tp) + for i := accessInAndEqCount; i < len(accessCondition); i++ { + rangePoints = rb.intersection(rangePoints, rb.build(accessCondition[i])) + } + if accessInAndEqCount == 0 { + colOff := index.Columns[0].Offset + tp := &tblInfo.Columns[colOff].FieldType + ranges = rb.buildIndexRanges(rangePoints, tp) + } else if accessInAndEqCount < len(accessCondition) { + colOff := index.Columns[accessInAndEqCount].Offset + tp := &tblInfo.Columns[colOff].FieldType + ranges = rb.appendIndexRanges(ranges, rangePoints, tp) } // Take prefix index into consideration. - if p.Index.HasPrefixIndex() { - for i := 0; i < len(p.Ranges); i++ { - refineRange(p.Ranges[i], p.Index) + if index.HasPrefixIndex() { + for i := 0; i < len(ranges); i++ { + refineRange(ranges[i], index) } } - if len(p.Ranges) > 0 && len(p.Ranges[0].LowVal) < len(p.Index.Columns) { - for _, ran := range p.Ranges { + if len(ranges) > 0 && len(ranges[0].LowVal) < len(index.Columns) { + for _, ran := range ranges { if ran.HighExclude || ran.LowExclude { if ran.HighExclude { ran.HighVal = append(ran.HighVal, types.NewDatum(nil)) @@ -83,7 +85,7 @@ func BuildIndexRange(sc *variable.StatementContext, p *PhysicalIndexScan) error } } } - return errors.Trace(rb.err) + return ranges, errors.Trace(rb.err) } // refineRange changes the IndexRange taking prefix index length into consideration.