enhancement
major
minor
defect
critical
major
minor
major
#30163
SQLFactory: Spaltenänderungen (modifyColumnName/Type/Mandatory) nur mit vollständiger Spaltendefinition erzeugen
Beobachtung
Einige Dialekte geben beim Ändern einer Spalte die komplette Spaltendefinition neu aus, auch wenn sich nur ein Aspekt ändert:
- MySQL (Standard-Implementierung in DBHelper): MODIFY / CHANGE mit der vollständigen Spaltendefinition beim Umbenennen, bei der Typänderung und beim Ändern der Pflichtangabe.
- MS SQL Server: ALTER COLUMN <c> <Spaltendefinition> bei Typänderung und Pflichtangabe.
- H2: ALTER COLUMN <c> <Spaltendefinition> bei der Typänderung (Typ mit Größe, Genauigkeit und Binär-Eigenschaft, NOT NULL).
Die Factory-Methoden SQLFactory.modifyColumnName(..), modifyColumnType(..) und modifyColumnMandatory(..) verlangen aber nur den Typ. Größe, Genauigkeit, Binär-Eigenschaft und Pflichtangabe werden nur übernommen, wenn der Aufrufer sie freiwillig über Setter an SQLModifyColumn setzt. Fehlt eine Angabe, entsteht z. B. VARCHAR(0) oder eine NOT NULL-Spalte wird nullbar – und das fällt nur auf den betroffenen Dialekten auf.
Beispiele:
- AlterColumnProcessor erzeugt beim Ändern der Pflichtangabe auf H2 ALTER COLUMN "FORMAT" VARCHAR_IGNORECASE(0), beim Umbenennen auf MySQL eine Spalte der Größe 0; bei der Typänderung lässt er die Pflichtangabe weg.
- RecollateStringColumnsProcessor lässt bei der Typänderung die Pflichtangabe weg, sodass NOT NULL-Spalten auf MySQL und MS SQL nullbar werden.
- DBSchemaUtils und UpdateFastListTable setzen Größe und Binär-Eigenschaft von Hand per Setter nach. Das ist korrekt, hängt aber allein davon ab, dass der Aufrufer daran denkt.
Datenbank-Defaults spielen hier keine Rolle mehr: Sie sind mit #30167 aus der SQL-Erzeugung entfernt. Dieses Ticket baut auf #30167 auf.
Vorschlag
Analog zu SQLFactory.addColumn(..) erhalten die Factory-Methoden Varianten, die die vollständige Definition der Spalte nach der Änderung verlangen (Typ, Pflichtangabe, Binär-Eigenschaft, Größe, Genauigkeit), zusätzlich bequeme Varianten mit einem DBColumn. Die bisherigen Kurzformen, die nur den Typ verlangen, werden entfernt, damit der Compiler die vollständige Angabe erzwingt. Alle Aufrufer werden umgestellt (AlterColumnProcessor, RecollateStringColumnsProcessor, Ticket23376UpdateReferenceIdColumns, DBSchemaUtils, UpdateFastListTable, SQLCopy, TestAlterTable sowie die mit #30167 hinzugekommenen Aufrufe in AddMOAttributeProcessor und AddDatabaseColumnProcessor).
Weitere Fehler (im selben Zug zu beheben)
- AlterColumnProcessor: Wird umbenannt und im selben Schritt Typ oder Pflichtangabe geändert, sprechen die späteren Anweisungen die Spalte noch unter dem alten Namen an und scheitern.
- PostgreSQLHelper.appendChangeMandatory: Das Aufheben der Pflichtangabe erzeugt ALTER COLUMN c SET (ungültiges SQL) statt DROP NOT NULL.
- H2Helper: Das Aufheben der Pflichtangabe erzeugt eine Neudefinition mit DEFAULT NULL, die das NOT NULL nicht entfernt. Richtig ist SET NULL.
- (Erledigt durch #30167: PostgreSQLHelper.appendChangeColumnType hängte einen Default-Wert ungültig an die TYPE-Klausel; mit dem Entfernen der Datenbank-Defaults entfällt das.)
Die Korrekturen liegen auf dem Branch CWS/CWS_30163_modify_column_definition, der auf dem Branch von #30167 aufsetzt.
Migration
Die Methoden SQLFactory.modifyColumnName(SQLTable, String, DBType, String), SQLFactory.modifyColumnType(SQLTable, String, DBType) und SQLFactory.modifyColumnMandatory(SQLTable, String, DBType, boolean) wurden entfernt. Aufrufe (z. B. in eigenen Migrationsprozessoren) übergeben jetzt die vollständige Definition der Spalte nach der Änderung:
{{{#!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) }}}
Alternativ gibt es Varianten mit einem DBColumn (modifyColumnName(table, column, newName), modifyColumnType(table, column), modifyColumnMandatory(table, column)). Angaben, die bisher per Setter an SQLModifyColumn gesetzt wurden (setSize(..), setBinary(..), setPrecision(..), setMandatory(..)), gehören in diese Parameter. Beispiel:
{{{#!java // vorher modifyColumnType(table("T"), "C", DBType.STRING).setSize(150).setBinary(true) // nachher modifyColumnType(table("T"), "C", DBType.STRING, mandatory, true, 150, 0) }}}
Dabei ist die Pflichtangabe der Spalte anzugeben, die sie nach der Änderung haben soll; bei einer reinen Typänderung also ihre bisherige. Einen Default-Wert gibt es seit #30167 nicht mehr.