// encodeSecondaryIndexes encodes the secondary index keys. The // secondaryIndexEntries are only valid until the next call to encodeIndexes or // encodeSecondaryIndexes. func (rh *rowHelper) encodeSecondaryIndexes( colIDtoRowIndex map[sqlbase.ColumnID]int, values []parser.Datum, ) (secondaryIndexEntries []sqlbase.IndexEntry, err error) { if len(rh.indexEntries) != len(rh.indexes) { rh.indexEntries = make([]sqlbase.IndexEntry, len(rh.indexes)) } err = sqlbase.EncodeSecondaryIndexes( rh.tableDesc, rh.indexes, colIDtoRowIndex, values, rh.indexEntries) if err != nil { return nil, err } return rh.indexEntries, nil }
// backfillIndexesChunk returns the next-key, done and an error. next-key and // done are invalid if error != nil. next-key is invalid if done is true. func (sc *SchemaChanger) backfillIndexesChunk( added []sqlbase.IndexDescriptor, sp roachpb.Span, chunkSize int64, mutationIdx int, lastCheckpoint *time.Time, ) (roachpb.Key, bool, error) { var nextKey roachpb.Key done := false secondaryIndexEntries := make([]sqlbase.IndexEntry, len(added)) err := sc.db.Txn(context.TODO(), func(txn *client.Txn) error { if sc.testingKnobs.RunBeforeBackfillChunk != nil { if err := sc.testingKnobs.RunBeforeBackfillChunk(sp); err != nil { return err } } if sc.testingKnobs.RunAfterBackfillChunk != nil { defer sc.testingKnobs.RunAfterBackfillChunk() } tableDesc, err := sqlbase.GetTableDescFromID(txn, sc.tableID) if err != nil { return err } // Short circuit the backfill if the table has been deleted. if done = tableDesc.Dropped(); done { return nil } // Get the next set of rows. // TODO(tamird): Support partial indexes? // // Use a scanNode with SELECT to pass in a sqlbase.TableDescriptor // to the SELECT without needing to go through table name // resolution, because we want to run schema changes from a gossip // feed of table IDs. Running the scan and applying the changes in // many transactions is fine because the schema change is in the // correct state to handle intermediate OLTP commands which delete // and add values during the scan. planner := makePlanner("backfill") planner.setTxn(txn) scan := planner.Scan() scan.desc = *tableDesc scan.spans = []roachpb.Span{sp} scan.SetLimitHint(chunkSize, false) scan.initDescDefaults(publicAndNonPublicColumns) rows, err := selectIndex(scan, nil, false) if err != nil { return err } if err := rows.Start(); err != nil { return err } // Construct a map from column ID to the index the value appears at // within a row. colIDtoRowIndex, err := makeColIDtoRowIndex(rows, tableDesc) if err != nil { return err } b := &client.Batch{} numRows := int64(0) for ; numRows < chunkSize; numRows++ { if next, err := rows.Next(); !next { if err != nil { return err } break } rowVals := rows.Values() err := sqlbase.EncodeSecondaryIndexes( tableDesc, added, colIDtoRowIndex, rowVals, secondaryIndexEntries) if err != nil { return err } for _, secondaryIndexEntry := range secondaryIndexEntries { if log.V(2) { log.Infof(txn.Context, "InitPut %s -> %v", secondaryIndexEntry.Key, secondaryIndexEntry.Value) } b.InitPut(secondaryIndexEntry.Key, &secondaryIndexEntry.Value) } } // Write the new index values. if err := txn.Run(b); err != nil { return convertBackfillError(tableDesc, b) } // Have we processed all the table rows? if done = numRows < chunkSize; done { return nil } // Keep track of the next key. resume := roachpb.Span{Key: scan.fetcher.Key(), EndKey: sp.EndKey} if err := sc.maybeWriteResumeSpan(txn, tableDesc, resume, mutationIdx, lastCheckpoint); err != nil { return err } nextKey = resume.Key return nil }) return nextKey, done, err }