Repository: pingcap/tidb
File: pkg/executor/admin.go (Go)

Code before:
			break
		}
		iter := chunk.NewIterator4Chunk(e.srcChunk)
		for row := iter.Begin(); row != iter.End(); row = iter.Next() {
			// TODO: it's possible to push down the condition.
			if e.index.Meta().HasCondition() {
				ok, err := e.index.MeetPartialConditionWithChunk(row)
				if err != nil {
					return nil, err
				}
				if !ok {
					// Skip partial index not match row
					continue
				}
			}

			if result.scanRowCount >= int64(e.batchSize) {
				return e.recoverRows, nil
			}
			handle, err := e.handleCols.BuildHandle(e.Ctx().GetSessionVars().StmtCtx, row)
			if err != nil {
				return nil, err
			}
			if e.index.Meta().Global {
				handle = kv.NewPartitionHandle(e.physicalID, handle)
			}
			idxVals, err := e.buildIndexedValues(row, e.idxValsBufs[result.scanRowCount], e.colFieldTypes, idxValLen)
			if err != nil {
				return nil, err
			}
			e.idxValsBufs[result.scanRowCount] = idxVals
			rsData := tables.TryGetHandleRestoredDataWrapper(e.table.Meta(), plannercore.GetCommonHandleDatum(e.handleCols, row), nil, e.index.Meta())
			e.recoverRows = append(e.recoverRows, recoverRows{handle: handle, idxVals: idxVals, rsData: rsData, skip: true})
			result.scanRowCount++
			result.currentHandle = handle
		}
	}

	return e.recoverRows, nil
}

func (e *RecoverIndexExec) buildIndexedValues(row chunk.Row, idxVals []types.Datum, fieldTypes []*types.FieldType, idxValLen int) ([]types.Datum, error) {
	if !e.containsGenedCol {
		return extractIdxVals(row, idxVals, fieldTypes, idxValLen), nil
	}

	if e.cols == nil {
		columns, _, err := expression.ColumnInfos2ColumnsAndNames(e.Ctx().GetExprCtx(), ast.NewCIStr("mock"), e.table.Meta().Name, e.table.Meta().Columns, e.table.Meta())
		if err != nil {
			return nil, err
		}

Review comment (performance):
If all rows in one batch are skipped, the `result.scanRowCount` is `0`, the recovery process will be terminated by `backfillIndex`, the later rows won't be handled.
```
		// no more rows
		if result.scanRowCount == 0 {
			break
		}
```

Code after:
			break
		}
		iter := chunk.NewIterator4Chunk(e.srcChunk)
		for row := iter.Begin(); row != iter.End(); row = iter.Next() {
			if result.scanRowCount >= int64(e.batchSize) {
				return e.recoverRows, nil
			}
			handle, err := e.handleCols.BuildHandle(e.Ctx().GetSessionVars().StmtCtx, row)
			if err != nil {
				return nil, err
			}
			if e.index.Meta().Global {
				handle = kv.NewPartitionHandle(e.physicalID, handle)
			}

			// TODO: it's possible to push down the condition.
			if e.index.Meta().HasCondition() {
				ok, err := e.index.MeetPartialConditionWithChunk(row)
				if err != nil {
					return nil, err
				}
				if !ok {
					// Skip partial index not match row. Still add the `scanRowCount` to avoid stopping the recovery too early.
					result.scanRowCount++
					result.currentHandle = handle
					continue
				}
			}

			idxVals, err := e.buildIndexedValues(row, e.idxValsBufs[result.scanRowCount], e.colFieldTypes, idxValLen)
			if err != nil {
				return nil, err
			}
			e.idxValsBufs[result.scanRowCount] = idxVals
			rsData := tables.TryGetHandleRestoredDataWrapper(e.table.Meta(), plannercore.GetCommonHandleDatum(e.handleCols, row), nil, e.index.Meta())
			e.recoverRows = append(e.recoverRows, recoverRows{handle: handle, idxVals: idxVals, rsData: rsData, skip: true})
			result.scanRowCount++
			result.currentHandle = handle
		}
	}

	return e.recoverRows, nil
}

func (e *RecoverIndexExec) buildIndexedValues(row chunk.Row, idxVals []types.Datum, fieldTypes []*types.FieldType, idxValLen int) ([]types.Datum, error) {
	if !e.containsGenedColOrPartialIndex {
		return extractIdxVals(row, idxVals, fieldTypes, idxValLen), nil
	}

	if e.cols == nil {
		columns, _, err := expression.ColumnInfos2ColumnsAndNames(e.Ctx().GetExprCtx(), ast.NewCIStr("mock"), e.table.Meta().Name, e.table.Meta().Columns, e.table.Meta())