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 }