github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/scrub_physical.go (about)

     1  // Copyright 2017 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package sql
    12  
    13  import (
    14  	"context"
    15  	"time"
    16  
    17  	"github.com/cockroachdb/cockroach/pkg/sql/rowcontainer"
    18  	"github.com/cockroachdb/cockroach/pkg/sql/rowexec"
    19  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    20  	"github.com/cockroachdb/cockroach/pkg/sql/span"
    21  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    22  	"github.com/cockroachdb/errors"
    23  )
    24  
    25  var _ checkOperation = &physicalCheckOperation{}
    26  
    27  // physicalCheckOperation is a check on an indexes physical data.
    28  type physicalCheckOperation struct {
    29  	tableName *tree.TableName
    30  	tableDesc *sqlbase.ImmutableTableDescriptor
    31  	indexDesc *sqlbase.IndexDescriptor
    32  
    33  	// columns is a list of the columns returned in the query result
    34  	// tree.Datums.
    35  	columns []*sqlbase.ColumnDescriptor
    36  	// primaryColIdxs maps PrimaryIndex.Columns to the row
    37  	// indexes in the query result tree.Datums.
    38  	primaryColIdxs []int
    39  
    40  	run physicalCheckRun
    41  }
    42  
    43  // physicalCheckRun contains the run-time state for
    44  // physicalCheckOperation during local execution.
    45  type physicalCheckRun struct {
    46  	started  bool
    47  	rows     *rowcontainer.RowContainer
    48  	rowIndex int
    49  }
    50  
    51  func newPhysicalCheckOperation(
    52  	tableName *tree.TableName,
    53  	tableDesc *sqlbase.ImmutableTableDescriptor,
    54  	indexDesc *sqlbase.IndexDescriptor,
    55  ) *physicalCheckOperation {
    56  	return &physicalCheckOperation{
    57  		tableName: tableName,
    58  		tableDesc: tableDesc,
    59  		indexDesc: indexDesc,
    60  	}
    61  }
    62  
    63  // Start implements the checkOperation interface.
    64  // It will plan and run the physical data check using the distSQL
    65  // execution engine.
    66  func (o *physicalCheckOperation) Start(params runParams) error {
    67  	ctx := params.ctx
    68  	// Collect all of the columns, their types, and their IDs.
    69  	var columnIDs []tree.ColumnID
    70  	colIDToIdx := make(map[sqlbase.ColumnID]int, len(o.tableDesc.Columns))
    71  	columns := make([]*sqlbase.ColumnDescriptor, len(columnIDs))
    72  	for i := range o.tableDesc.Columns {
    73  		colIDToIdx[o.tableDesc.Columns[i].ID] = i
    74  	}
    75  
    76  	// Collect all of the columns being scanned.
    77  	if o.indexDesc.ID == o.tableDesc.PrimaryIndex.ID {
    78  		for i := range o.tableDesc.Columns {
    79  			columnIDs = append(columnIDs, tree.ColumnID(o.tableDesc.Columns[i].ID))
    80  		}
    81  	} else {
    82  		for _, id := range o.indexDesc.ColumnIDs {
    83  			columnIDs = append(columnIDs, tree.ColumnID(id))
    84  		}
    85  		for _, id := range o.indexDesc.ExtraColumnIDs {
    86  			columnIDs = append(columnIDs, tree.ColumnID(id))
    87  		}
    88  		for _, id := range o.indexDesc.StoreColumnIDs {
    89  			columnIDs = append(columnIDs, tree.ColumnID(id))
    90  		}
    91  	}
    92  
    93  	for i := range columnIDs {
    94  		idx := colIDToIdx[sqlbase.ColumnID(columnIDs[i])]
    95  		columns = append(columns, &o.tableDesc.Columns[idx])
    96  	}
    97  
    98  	// Find the row indexes for all of the primary index columns.
    99  	primaryColIdxs, err := getPrimaryColIdxs(o.tableDesc, columns)
   100  	if err != nil {
   101  		return err
   102  	}
   103  
   104  	indexFlags := &tree.IndexFlags{
   105  		IndexID:     tree.IndexID(o.indexDesc.ID),
   106  		NoIndexJoin: true,
   107  	}
   108  	scan := params.p.Scan()
   109  	scan.isCheck = true
   110  	colCfg := scanColumnsConfig{wantedColumns: columnIDs, addUnwantedAsHidden: true}
   111  	if err := scan.initTable(ctx, params.p, o.tableDesc, indexFlags, colCfg); err != nil {
   112  		return err
   113  	}
   114  	scan.index = scan.specifiedIndex
   115  	sb := span.MakeBuilder(params.ExecCfg().Codec, o.tableDesc.TableDesc(), o.indexDesc)
   116  	scan.spans, err = sb.UnconstrainedSpans()
   117  	if err != nil {
   118  		return err
   119  	}
   120  	scan.isFull = true
   121  
   122  	planCtx := params.extendedEvalCtx.DistSQLPlanner.NewPlanningCtx(ctx, params.extendedEvalCtx, params.p.txn, true /* distribute */)
   123  	physPlan, err := params.extendedEvalCtx.DistSQLPlanner.createScrubPhysicalCheck(
   124  		planCtx, scan, *o.tableDesc.TableDesc(), *o.indexDesc, params.p.ExecCfg().Clock.Now())
   125  	if err != nil {
   126  		return err
   127  	}
   128  
   129  	o.primaryColIdxs = primaryColIdxs
   130  	o.columns = columns
   131  	o.run.started = true
   132  	rows, err := scrubRunDistSQL(ctx, planCtx, params.p, &physPlan, rowexec.ScrubTypes)
   133  	if err != nil {
   134  		rows.Close(ctx)
   135  		return err
   136  	}
   137  	o.run.rows = rows
   138  	return nil
   139  }
   140  
   141  // Next implements the checkOperation interface.
   142  func (o *physicalCheckOperation) Next(params runParams) (tree.Datums, error) {
   143  	row := o.run.rows.At(o.run.rowIndex)
   144  	o.run.rowIndex++
   145  
   146  	timestamp, err := tree.MakeDTimestamp(
   147  		params.extendedEvalCtx.GetStmtTimestamp(), time.Nanosecond)
   148  	if err != nil {
   149  		return nil, err
   150  	}
   151  
   152  	details, ok := row[2].(*tree.DJSON)
   153  	if !ok {
   154  		return nil, errors.Errorf("expected row value 3 to be DJSON, got: %T", row[2])
   155  	}
   156  
   157  	return tree.Datums{
   158  		// TODO(joey): Add the job UUID once the SCRUB command uses jobs.
   159  		tree.DNull, /* job_uuid */
   160  		row[0],     /* errorType */
   161  		tree.NewDString(o.tableName.Catalog()),
   162  		tree.NewDString(o.tableName.Table()),
   163  		row[1], /* primaryKey */
   164  		timestamp,
   165  		tree.DBoolFalse,
   166  		details,
   167  	}, nil
   168  }
   169  
   170  // Started implements the checkOperation interface.
   171  func (o *physicalCheckOperation) Started() bool {
   172  	return o.run.started
   173  }
   174  
   175  // Done implements the checkOperation interface.
   176  func (o *physicalCheckOperation) Done(ctx context.Context) bool {
   177  	return o.run.rows == nil || o.run.rowIndex >= o.run.rows.Len()
   178  }
   179  
   180  // Close implements the checkOperation interface.
   181  func (o *physicalCheckOperation) Close(ctx context.Context) {
   182  	if o.run.rows != nil {
   183  		o.run.rows.Close(ctx)
   184  	}
   185  }