enhancement
major
minor
defect
critical
major
minor
major
#30163
SQLFactory: Generate column changes (modifyColumnName/Type/Mandatory) only with a complete column definition
Note
Some dialects re-issue the complete column definition when a column is modified, even if only one aspect changes:
- MySQL (standard implementation in DBHelper): MODIFY / CHANGE with the complete column definition when renaming, changing the data type, or modifying the required status.
- MS SQL Server: ALTER COLUMN <c> <column definition> when changing the data type or setting a NOT NULL constraint.
- H2: ALTER COLUMN <c> <column definition> when changing the data type (type with size, precision, and binary property, NOT NULL).
However, the factory methods SQLFactory.modifyColumnName(..), modifyColumnType(..), and modifyColumnMandatory(..) only require the data type. Length, precision, binary property, and mandatory status are only applied if the caller voluntarily sets them via setters on SQLModifyColumn. If a specification is missing, the result is, for example, VARCHAR(0), or a NOT NULL column becomes nullable—and this is only noticeable in the affected dialects.
Examples:
- When changing the mandatory status to H2,AlterColumnProcessor generates ` ALTER COLUMN "FORMAT" VARCHAR_IGNORECASE(0)`; when renaming to MySQL, it creates a column of size 0; when changing the type, it omits the mandatory status.
- RecollateStringColumnsProcessor omits the required value when changing the data type, causing NOT NULL columns to become nullable in MySQL and MS SQL.
- DBSchemaUtils and UpdateFastListTable manually update the size and binary property using setters. This is correct, but it depends entirely on the caller remembering to do so.
Database defaults no longer play a role here: they were removed from SQL generation in #30167. This ticket builds on #30167.
Proposal
Similar to SQLFactory.addColumn(..), the factory methods will be extended to include variants that require the complete column definition after the change (type, required status, binary property, size, precision), as well as convenient variants using a DBColumn. The previous shorthand forms, which require only the type, will be removed so that the compiler enforces the complete specification. All callers will be updated (AlterColumnProcessor, RecollateStringColumnsProcessor, Ticket23376UpdateReferenceIdColumns, DBSchemaUtils, UpdateFastListTable, SQLCopy, TestAlterTable, as well as the calls added with #30167 in AddMOAttributeProcessor and AddDatabaseColumnProcessor).
Additional bugs (to be fixed at the same time)
- AlterColumnProcessor: If the column is renamed and its data type or mandatory status is changed in the same step, subsequent statements still refer to the column by its old name and fail.
- PostgreSQLHelper.appendChangeMandatory: Removing the mandatory constraint generates ` ALTER COLUMN c SET ` (invalid SQL) instead of ` DROP NOT NULL`.
- H2Helper: Removing the mandatory constraint generates a redefinition with ` DEFAULT NULL`, which does not remove the ` NOT NULL `. The correct action is ` SET NULL`.
- (Resolved by #30167: PostgreSQLHelper.appendChangeColumnType incorrectly appended a default value to the TYPE clause; this issue is resolved by removing the database defaults.)
The fixes are in the CWS/CWS_30163_modify_column_definition branch, which is based on the #30167 branch.
Migration
The methods SQLFactory.modifyColumnName(SQLTable, String, DBType, String), SQLFactory.modifyColumnType(SQLTable, String, DBType), and SQLFactory.modifyColumnMandatory(SQLTable, String, DBType, boolean) have been removed. Calls (e.g., in custom migration processors) now pass the complete definition of the column after the change:
{{{#!java modifyColumnName(table, columnName, newName, type, mandatory, binary, size, precision) modifyColumnType(table, columnName, type, mandatory, binary, size, precision) modifyColumnMandatory(table, columnName, type, mandatory, binary, size, precision) }}}
Alternatively, there are variants that use a DBColumn (modifyColumnName(table, column, newName), modifyColumnType(table, column), modifyColumnMandatory(table, column)). Settings that were previously set via setters on ` SQLModifyColumn ` (`setSize(..)`, `setBinary(..)`, `setPrecision(..)`, `setMandatory(..)`) should now be included in these parameters. Example:
{{{#!java // before modifyColumnType(table("T"), "C", DBType.STRING).setSize(150).setBinary(true) // after modifyColumnType(table("T"), "C", DBType.STRING, mandatory, true, 150, 0) }}}
You must specify the mandatory status of the column as it should be after the change; for a simple type change, this means its previous status. A default value is no longer available since #30167.