示例#1
0
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
}
示例#2
0
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
}