Repository: pingcap/tidb
File: pkg/ddl/modify_column.go (Go)

Code before:
				break
			}
		}
		if partCol != nil {
			return checkPartitionColumnModifiable(sctx, tblInfo, col, newCol)
		}
	}
	return nil
}

func checkPartitionColumnModifiable(sctx sessionctx.Context, tblInfo *model.TableInfo, col, newCol *model.ColumnInfo) error {
	if col.Name.L != newCol.Name.L {
		return dbterror.ErrDependentByPartitionFunctional.GenWithStackByArgs(col.Name.L)
	}
	if !isColTypeAllowedAsPartitioningCol(tblInfo.Partition.Type, newCol.FieldType) {
		return dbterror.ErrNotAllowedTypeInPartition.GenWithStackByArgs(newCol.Name.O)
	}
	pi := tblInfo.GetPartitionInfo()
	if len(pi.Columns) == 0 {
		// non COLUMNS partitioning, only checks INTs, not their actual range
		// There are many edge cases, like when truncating SQL Mode is allowed
		// which will change the partitioning expression value resulting in a
		// different partition. Better be safe and not allow decreasing of length.
		// TODO: Should we allow it in strict mode? Wait for a use case / request.
		if newCol.FieldType.GetFlen() < col.FieldType.GetFlen() {
			return dbterror.ErrUnsupportedModifyCollation.GenWithStack("Unsupported modify column, decreasing length of int may result in truncation and change of partition")
		}
	}
	// Basically only allow changes of the length/decimals for the column
	// Note that enum is not allowed, so elems are not checked
	// TODO: support partition by ENUM
	if newCol.FieldType.EvalType() != col.FieldType.EvalType() ||
		newCol.FieldType.GetFlag() != col.FieldType.GetFlag() ||
		newCol.FieldType.GetCollate() != col.FieldType.GetCollate() ||
		newCol.FieldType.GetCharset() != col.FieldType.GetCharset() {
		return dbterror.ErrUnsupportedModifyColumn.GenWithStackByArgs("can't change the partitioning column, since it would require reorganize all partitions")
	}
	// Generate a new PartitionInfo and validate it together with the new column definition
	// Checks if all partition definition values are compatible.
	// Similar to what buildRangePartitionDefinitions would do in terms of checks.

	newTblInfo := *tblInfo
	// Replace col with newCol and see if we can generate a new SHOW CREATE TABLE
	// and reparse it and build new partition definitions (which will do additional
	// checks columns vs partition definition values
	newCols := make([]*model.ColumnInfo, 0, len(newTblInfo.Columns))
	for _, c := range newTblInfo.Columns {
		if c.ID == col.ID {
			newCols = append(newCols, newCol)
			continue
		}

Review comment (bug):
Using `ErrUnsupportedModifyCollation` error type for a message about int length truncation is semantically incorrect. This error type is meant for collation-related issues, not integer field length changes. Consider using `ErrUnsupportedModifyColumn` instead, which is more appropriate for this context.
```suggestion
			return dbterror.ErrUnsupportedModifyColumn.GenWithStack("Unsupported modify column, decreasing length of int may result in truncation and change of partition")
```

Code after:
		}
		var partCol *model.ColumnInfo
		for _, pc := range partCols {
			if strings.EqualFold(pc.Name.L, col.Name.L) {
				partCol = pc
				break
			}
		}
		if partCol != nil {
			return checkPartitionColumnModifiable(sctx, tblInfo, col, newCol)
		}
	}
	return nil
}

func checkPartitionColumnModifiable(sctx sessionctx.Context, tblInfo *model.TableInfo, col, newCol *model.ColumnInfo) error {
	if col.Name.L != newCol.Name.L {
		return dbterror.ErrDependentByPartitionFunctional.GenWithStackByArgs(col.Name.L)
	}
	if !isColTypeAllowedAsPartitioningCol(tblInfo.Partition.Type, newCol.FieldType) {
		return dbterror.ErrNotAllowedTypeInPartition.GenWithStackByArgs(newCol.Name.O)
	}
	pi := tblInfo.GetPartitionInfo()
	if len(pi.Columns) == 0 {
		// non COLUMNS partitioning, only checks INTs, not their actual range
		// There are many edge cases, like when truncating SQL Mode is allowed
		// which will change the partitioning expression value resulting in a
		// different partition. Better be safe and not allow decreasing of length.
		// TODO: Should we allow it in strict mode? Wait for a use case / request.
		if newCol.FieldType.GetFlen() < col.FieldType.GetFlen() {
			return dbterror.ErrUnsupportedModifyColumn.GenWithStack("Unsupported modify column, decreasing length of int may result in truncation and change of partition")
		}
	}
	// Basically only allow changes of the length/decimals for the column
	// Note that enum is not allowed, so elems are not checked
	// TODO: support partition by ENUM
	if newCol.FieldType.EvalType() != col.FieldType.EvalType() ||
		newCol.FieldType.GetFlag() != col.FieldType.GetFlag() ||
		newCol.FieldType.GetCollate() != col.FieldType.GetCollate() ||
		newCol.FieldType.GetCharset() != col.FieldType.GetCharset() {
		return dbterror.ErrUnsupportedModifyColumn.GenWithStackByArgs("can't change the partitioning column, since it would require reorganize all partitions")
	}
	// Generate a new PartitionInfo and validate it together with the new column definition
	// Checks if all partition definition values are compatible.
	// Similar to what buildRangePartitionDefinitions would do in terms of checks.

	newTblInfo := *tblInfo
	// Replace col with newCol and see if we can generate a new SHOW CREATE TABLE
	// and reparse it and build new partition definitions (which will do additional
	// checks columns vs partition definition values
	newCols := make([]*model.ColumnInfo, 0, len(newTblInfo.Columns))