// AcquireLease acquires a schema change lease on the table if // an unexpired lease doesn't exist. It returns the lease. func (sc *SchemaChanger) AcquireLease() (sqlbase.TableDescriptor_SchemaChangeLease, error) { var lease sqlbase.TableDescriptor_SchemaChangeLease err := sc.db.Txn(func(txn *client.Txn) error { txn.SetSystemConfigTrigger() tableDesc, err := sqlbase.GetTableDescFromID(txn, sc.tableID) if err != nil { return err } // A second to deal with the time uncertainty across nodes. // It is perfectly valid for two or more goroutines to hold a valid // lease and execute a schema change in parallel, because schema // changes are executed using transactions that run sequentially. // This just reduces the probability of a write collision. expirationTimeUncertainty := time.Second if tableDesc.Lease != nil { if time.Unix(0, tableDesc.Lease.ExpirationTime).Add(expirationTimeUncertainty).After(timeutil.Now()) { return errExistingSchemaChangeLease } log.Infof(txn.Context, "Overriding existing expired lease %v", tableDesc.Lease) } lease = sc.createSchemaChangeLease() tableDesc.Lease = &lease return txn.Put(sqlbase.MakeDescMetadataKey(tableDesc.ID), sqlbase.WrapDescriptor(tableDesc)) }) return lease, err }
func (sc *SchemaChanger) findTableWithLease( txn *client.Txn, lease sqlbase.TableDescriptor_SchemaChangeLease, ) (*sqlbase.TableDescriptor, error) { tableDesc, err := sqlbase.GetTableDescFromID(txn, sc.tableID) if err != nil { return nil, err } if tableDesc.Lease == nil { return nil, errors.Errorf("no lease present for tableID: %d", sc.tableID) } if *tableDesc.Lease != lease { return nil, errors.Errorf("table: %d has lease: %v, expected: %v", sc.tableID, tableDesc.Lease, lease) } return tableDesc, nil }
// getTableSpan returns a span containing the start and end key for a table. func (sc *SchemaChanger) getTableSpan() (sqlbase.Span, error) { var tableDesc *sqlbase.TableDescriptor if err := sc.db.Txn(context.TODO(), func(txn *client.Txn) error { var err error tableDesc, err = sqlbase.GetTableDescFromID(txn, sc.tableID) return err }); err != nil { return sqlbase.Span{}, err } prefix := roachpb.Key(sqlbase.MakeIndexKeyPrefix(tableDesc, tableDesc.PrimaryIndex.ID)) return sqlbase.Span{ Start: prefix, End: prefix.PrefixEnd(), }, nil }
// getTableLeaseByID is a by-ID variant of getTableLease (i.e. uses same cache). func (p *planner) getTableLeaseByID(tableID sqlbase.ID) (*sqlbase.TableDescriptor, error) { if log.V(2) { log.Infof(p.ctx(), "planner acquiring lease on table ID %d", tableID) } if testDisableTableLeases { table, err := sqlbase.GetTableDescFromID(p.txn, tableID) if err != nil { return nil, err } if err := filterTableState(table); err != nil { return nil, err } return table, nil } // First, look to see if we already have a lease for this table -- including // leases acquired via `getTableLease`. var lease *LeaseState for _, l := range p.leases { if l.ID == tableID { lease = l if log.V(2) { log.Infof(p.ctx(), "found lease in planner cache for table %d", tableID) } break } } // If we didn't find a lease or the lease is about to expire, acquire one. if lease == nil || p.removeLeaseIfExpiring(lease) { var err error lease, err = p.leaseMgr.Acquire(p.txn, tableID, 0) if err != nil { if err == sqlbase.ErrDescriptorNotFound { // Transform the descriptor error into an error that references the // table's ID. return nil, sqlbase.NewUndefinedTableError(fmt.Sprintf("<id=%d>", tableID)) } return nil, err } p.leases = append(p.leases, lease) // If the lease we just acquired expires before the txn's deadline, reduce // the deadline. p.txn.UpdateDeadlineMaybe(hlc.Timestamp{WallTime: lease.Expiration().UnixNano()}) } return &lease.TableDescriptor, nil }
// showCreateInterleave returns an INTERLEAVE IN PARENT clause for the specified // index, if applicable. func (p *planner) showCreateInterleave(idx *sqlbase.IndexDescriptor) (string, error) { if len(idx.Interleave.Ancestors) == 0 { return "", nil } intl := idx.Interleave parentTable, err := sqlbase.GetTableDescFromID(p.txn, intl.Ancestors[len(intl.Ancestors)-1].TableID) if err != nil { return "", err } var sharedPrefixLen int for _, ancestor := range intl.Ancestors { sharedPrefixLen += int(ancestor.SharedPrefixLen) } interleavedColumnNames := quoteNames(idx.ColumnNames[:sharedPrefixLen]...) s := fmt.Sprintf(" INTERLEAVE IN PARENT %s (%s)", parentTable.Name, interleavedColumnNames) return s, nil }
// IsDone returns true if the work scheduled for the schema changer // is complete. func (sc *SchemaChanger) IsDone() (bool, error) { var done bool err := sc.db.Txn(func(txn *client.Txn) error { done = true tableDesc, err := sqlbase.GetTableDescFromID(txn, sc.tableID) if err != nil { return err } if sc.mutationID == sqlbase.InvalidMutationID { if tableDesc.UpVersion { done = false } } else { for _, mutation := range tableDesc.Mutations { if mutation.MutationID == sc.mutationID { done = false break } } } return nil }) return done, err }
func (sc *SchemaChanger) truncateIndexes( lease *sqlbase.TableDescriptor_SchemaChangeLease, dropped []sqlbase.IndexDescriptor, ) error { for _, desc := range dropped { // First extend the schema change lease. l, err := sc.ExtendLease(*lease) if err != nil { return err } *lease = l if err := sc.db.Txn(context.TODO(), func(txn *client.Txn) error { tableDesc, err := sqlbase.GetTableDescFromID(txn, sc.tableID) if err != nil { return err } // Short circuit the truncation if the table has been deleted. if tableDesc.Deleted() { return nil } rd, err := makeRowDeleter(txn, tableDesc, nil, nil, false) if err != nil { return err } td := tableDeleter{rd: rd} if err := td.init(txn); err != nil { return err } return td.deleteIndex(context.TODO(), &desc) }); err != nil { return err } } return nil }
func (sc *SchemaChanger) backfillIndexesChunk( added []sqlbase.IndexDescriptor, sp sqlbase.Span, ) (roachpb.Key, bool, error) { var nextKey roachpb.Key done := false err := sc.db.Txn(context.TODO(), func(txn *client.Txn) error { tableDesc, err := sqlbase.GetTableDescFromID(txn, sc.tableID) if err != nil { return err } // Short circuit the backfill if the table has been deleted. if tableDesc.Deleted() { done = true 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() planner.setTxn(txn) scan := planner.Scan() scan.desc = *tableDesc scan.spans = []sqlbase.Span{sp} 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 := 0 for ; numRows < IndexBackfillChunkSize; numRows++ { if next, err := rows.Next(); !next { if err != nil { return err } break } rowVals := rows.Values() for _, desc := range added { secondaryIndexEntries := make([]sqlbase.IndexEntry, 1) err := sqlbase.EncodeSecondaryIndexes( tableDesc, []sqlbase.IndexDescriptor{desc}, colIDtoRowIndex, rowVals, secondaryIndexEntries) if err != nil { return err } for _, secondaryIndexEntry := range secondaryIndexEntries { if log.V(2) { log.Infof(context.TODO(), "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 numRows < IndexBackfillChunkSize { done = true return nil } // Keep track of the next key. nextKey = scan.fetcher.Key() return nil }) return nextKey, done, err }
func (sc *SchemaChanger) truncateAndBackfillColumnsChunk( added []sqlbase.ColumnDescriptor, dropped []sqlbase.ColumnDescriptor, defaultExprs []parser.TypedExpr, evalCtx *parser.EvalContext, sp sqlbase.Span, ) (roachpb.Key, bool, error) { var curIndexKey roachpb.Key done := false err := sc.db.Txn(context.TODO(), func(txn *client.Txn) error { tableDesc, err := sqlbase.GetTableDescFromID(txn, sc.tableID) if err != nil { return err } // Short circuit the backfill if the table has been deleted. if tableDesc.Deleted() { done = true return nil } updateCols := append(added, dropped...) fkTables := tablesNeededForFKs(*tableDesc, CheckUpdates) for k := range fkTables { table, err := sqlbase.GetTableDescFromID(txn, k) if err != nil { return err } fkTables[k] = tableLookup{table: table} } // TODO(dan): Tighten up the bound on the requestedCols parameter to // makeRowUpdater. requestedCols := make([]sqlbase.ColumnDescriptor, 0, len(tableDesc.Columns)+len(added)) requestedCols = append(requestedCols, tableDesc.Columns...) requestedCols = append(requestedCols, added...) ru, err := makeRowUpdater( txn, tableDesc, fkTables, updateCols, requestedCols, rowUpdaterOnlyColumns, ) if err != nil { return err } // TODO(dan): This check is an unfortunate bleeding of the internals of // rowUpdater. Extract the sql row to k/v mapping logic out into something // usable here. if !ru.isColumnOnlyUpdate() { panic("only column data should be modified, but the rowUpdater is configured otherwise") } // Run a scan across the table using the primary key. 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. var rf sqlbase.RowFetcher colIDtoRowIndex := colIDtoRowIndexFromCols(tableDesc.Columns) valNeededForCol := make([]bool, len(tableDesc.Columns)) for i := range valNeededForCol { _, valNeededForCol[i] = ru.fetchColIDtoRowIndex[tableDesc.Columns[i].ID] } err = rf.Init(tableDesc, colIDtoRowIndex, &tableDesc.PrimaryIndex, false, false, tableDesc.Columns, valNeededForCol) if err != nil { return err } // StartScan uses 0 as a sentinal for the default limit of entries scanned. if err := rf.StartScan(txn, sqlbase.Spans{sp}, 0); err != nil { return err } indexKeyPrefix := sqlbase.MakeIndexKeyPrefix(tableDesc, tableDesc.PrimaryIndex.ID) oldValues := make(parser.DTuple, len(ru.fetchCols)) updateValues := make(parser.DTuple, len(updateCols)) writeBatch := &client.Batch{} var i int for ; i < ColumnTruncateAndBackfillChunkSize; i++ { row, err := rf.NextRow() if err != nil { return err } if row == nil { break // Done } curIndexKey, _, err = sqlbase.EncodeIndexKey( tableDesc, &tableDesc.PrimaryIndex, colIDtoRowIndex, row, indexKeyPrefix) for j, col := range added { if defaultExprs == nil || defaultExprs[j] == nil { updateValues[j] = parser.DNull } else { updateValues[j], err = defaultExprs[j].Eval(evalCtx) if err != nil { return err } } if !col.Nullable && updateValues[j].Compare(parser.DNull) == 0 { return sqlbase.NewNonNullViolationError(col.Name) } } for j := range dropped { updateValues[j+len(added)] = parser.DNull } copy(oldValues, row) for j := len(row); j < len(oldValues); j++ { oldValues[j] = parser.DNull } if _, err := ru.updateRow(context.TODO(), writeBatch, oldValues, updateValues); err != nil { return err } } if i < ColumnTruncateAndBackfillChunkSize { done = true } if err := txn.Run(writeBatch); err != nil { return convertBackfillError(tableDesc, writeBatch) } return nil }) return curIndexKey.PrefixEnd(), done, err }
// runBackfill runs the backfill for the schema changer. func (sc *SchemaChanger) runBackfill(lease *sqlbase.TableDescriptor_SchemaChangeLease) error { l, err := sc.ExtendLease(*lease) if err != nil { return err } *lease = l // Mutations are applied in a FIFO order. Only apply the first set of // mutations. Collect the elements that are part of the mutation. var droppedColumnDescs []sqlbase.ColumnDescriptor var droppedIndexDescs []sqlbase.IndexDescriptor var addedColumnDescs []sqlbase.ColumnDescriptor var addedIndexDescs []sqlbase.IndexDescriptor if err := sc.db.Txn(context.TODO(), func(txn *client.Txn) error { tableDesc, err := sqlbase.GetTableDescFromID(txn, sc.tableID) if err != nil { return err } for _, m := range tableDesc.Mutations { if m.MutationID != sc.mutationID { break } switch m.Direction { case sqlbase.DescriptorMutation_ADD: switch t := m.Descriptor_.(type) { case *sqlbase.DescriptorMutation_Column: addedColumnDescs = append(addedColumnDescs, *t.Column) case *sqlbase.DescriptorMutation_Index: addedIndexDescs = append(addedIndexDescs, *t.Index) default: return errors.Errorf("unsupported mutation: %+v", m) } case sqlbase.DescriptorMutation_DROP: switch t := m.Descriptor_.(type) { case *sqlbase.DescriptorMutation_Column: droppedColumnDescs = append(droppedColumnDescs, *t.Column) case *sqlbase.DescriptorMutation_Index: droppedIndexDescs = append(droppedIndexDescs, *t.Index) default: return errors.Errorf("unsupported mutation: %+v", m) } } } return nil }); err != nil { return err } // First drop indexes, then add/drop columns, and only then add indexes. // Drop indexes. if err := sc.truncateIndexes(lease, droppedIndexDescs); err != nil { return err } // Add and drop columns. if err := sc.truncateAndBackfillColumns( lease, addedColumnDescs, droppedColumnDescs, ); err != nil { return err } // Add new indexes. if err := sc.backfillIndexes(lease, addedIndexDescs); err != nil { return err } return nil }