func (sc *SchemaChanger) truncateAndBackfillColumns( lease *sqlbase.TableDescriptor_SchemaChangeLease, added []sqlbase.ColumnDescriptor, dropped []sqlbase.ColumnDescriptor, ) error { evalCtx := parser.EvalContext{} // Set the eval context timestamps. pTime := timeutil.Now() evalCtx.SetTxnTimestamp(pTime) evalCtx.SetStmtTimestamp(pTime) defaultExprs, err := makeDefaultExprs(added, &parser.Parser{}, evalCtx) if err != nil { return err } // Remember any new non nullable column with no default value. nonNullableColumn := "" for _, columnDesc := range added { if columnDesc.DefaultExpr == nil && !columnDesc.Nullable { nonNullableColumn = columnDesc.Name } } // Add or Drop a column. if len(dropped) > 0 || nonNullableColumn != "" || len(defaultExprs) > 0 { // Initialize start and end to represent a span of keys. sp, err := sc.getTableSpan() if err != nil { return err } // Run through the entire table key space adding and deleting columns. for done := false; !done; { // First extend the schema change lease. l, err := sc.ExtendLease(*lease) if err != nil { return err } *lease = l // Add and delete columns for a chunk of the key space. sp.Start, done, err = sc.truncateAndBackfillColumnsChunk( added, dropped, nonNullableColumn, defaultExprs, evalCtx, sp, ) if err != nil { return err } } } return nil }
func (sc *SchemaChanger) truncateAndBackfillColumns( lease *TableDescriptor_SchemaChangeLease, added []ColumnDescriptor, dropped []ColumnDescriptor, version DescriptorVersion, ) *roachpb.Error { evalCtx := parser.EvalContext{} // Set the eval context timestamps. pTime := timeutil.Now() evalCtx.SetTxnTimestamp(pTime) evalCtx.SetStmtTimestamp(pTime) defaultExprs, err := makeDefaultExprs(added, &parser.Parser{}, evalCtx) if err != nil { return roachpb.NewError(err) } // Remember any new non nullable column with no default value. nonNullableColumn := "" for _, columnDesc := range added { if columnDesc.DefaultExpr == nil && !columnDesc.Nullable { nonNullableColumn = columnDesc.Name } } // Add or Drop a column. if len(dropped) > 0 || nonNullableColumn != "" || len(defaultExprs) > 0 { // First extend the schema change lease. l, pErr := sc.ExtendLease(*lease) if pErr != nil { return pErr } *lease = l pErr = sc.db.Txn(func(txn *client.Txn) *roachpb.Error { tableDesc, pErr := getTableDescAtVersion(txn, sc.tableID, version) if pErr != nil { return pErr } // Run a scan across the table using the primary key. start := roachpb.Key(MakeIndexKeyPrefix(tableDesc.ID, tableDesc.PrimaryIndex.ID)) b := &client.Batch{} b.Scan(start, start.PrefixEnd(), 0) if pErr := txn.Run(b); pErr != nil { return pErr } if nonNullableColumn != "" { for _, result := range b.Results { if len(result.Rows) > 0 { return roachpb.NewErrorf("column %s contains null values", nonNullableColumn) } } } // Use a different batch to truncate/backfill columns. writeBatch := &client.Batch{} for _, result := range b.Results { var sentinelKey roachpb.Key for _, kv := range result.Rows { if sentinelKey == nil || !bytes.HasPrefix(kv.Key, sentinelKey) { // Sentinel keys have a 0 suffix indicating 0 bytes of column // ID. Strip off that suffix to determine the prefix shared with the // other keys for the row. sentinelKey = stripColumnIDLength(kv.Key) // Delete the entire dropped columns. // This used to use SQL UPDATE in the past to update the dropped // column to NULL; but a column in the process of being // dropped is placed in the table descriptor mutations, and // a SQL UPDATE of a column in mutations will fail. for _, columnDesc := range dropped { // Delete the dropped column. colKey := keys.MakeColumnKey(sentinelKey, uint32(columnDesc.ID)) if log.V(2) { log.Infof("Del %s", colKey) } writeBatch.Del(colKey) } // Add the new columns and backfill the values. for i, expr := range defaultExprs { if expr == nil { continue } col := added[i] colKey := keys.MakeColumnKey(sentinelKey, uint32(col.ID)) d, err := expr.Eval(evalCtx) if err != nil { return roachpb.NewError(err) } val, err := marshalColumnValue(col, d, evalCtx.Args) if err != nil { return roachpb.NewError(err) } if log.V(2) { log.Infof("Put %s -> %v", colKey, val) } // Insert default value into the column. If this row // was recently added the default value might have // already been populated, because the // ColumnDescriptor is in the WRITE_ONLY state. // Reinserting the default value is not a big deal. // // Note: a column in the WRITE_ONLY state cannot be // populated directly through SQL. A SQL INSERT cannot // directly reference the column, and the INSERT // populates the column with the default value. writeBatch.Put(colKey, val) } } } } if pErr := txn.Run(writeBatch); pErr != nil { return convertBackfillError(tableDesc, writeBatch, pErr) } return nil }) return pErr } return nil }