typeorm/no-changecolumn-recreate
Reports queryRunner.changeColumn() calls that make TypeORM drop and re-add the column, which deletes every value in it.
| Category | Default severity | Databases |
|---|---|---|
| data-loss | error | PostgreSQL, MySQL |
What happens
changeColumn() does not always alter a column in place. When the change needs a conversion, TypeORM drops the column and adds it again ("To avoid data conversion, we just recreate column"), and every existing value is lost.
- PostgreSQL: when the type, length, or array flag changes, when the column becomes a stored generated column, or when the expression of a stored generated column changes.
- MySQL: when the type or length changes, when the generated type changes, or when generation is turned on or off (except for
uuid).
orm-preflight compares the old and new TableColumn. When the old column is passed by name, as a string, it cannot compare them, and reports a warning instead of an error.
A changeColumn() that only renames is reported by no-rename-column, and one that sets isNullable: false by no-set-not-null.
Bad
import { MigrationInterface, QueryRunner, TableColumn } from 'typeorm'
export class WidenUserName1727200000000 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.changeColumn(
'users',
new TableColumn({ name: 'name', type: 'varchar', length: '100' }),
new TableColumn({ name: 'name', type: 'varchar', length: '255' }),
)
}
public async down(queryRunner: QueryRunner): Promise<void> {
// ...
}
}Safe
Write the change as SQL, which alters the column in place:
import { MigrationInterface, QueryRunner } from 'typeorm'
export class WidenUserName1727200000000 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "name" TYPE character varying(255)`)
}
public async down(queryRunner: QueryRunner): Promise<void> {
// ...
}
}A longer varchar, or varchar to text, keeps the data and does not rewrite the table on PostgreSQL. For other type changes, use expand and contract: add a new column, backfill it, switch the code, and drop the old column in a later release.
When to suppress
When losing the column's data is intended, or the table is known to be empty in every environment.
// preflight safety-assured typeorm/no-changecolumn-recreate -- column holds derived data, recomputed by the jobReferences
- TypeORM 1.x:
PostgresQueryRunner.changeColumn,MysqlQueryRunner.changeColumn - TypeORM 0.3.x:
PostgresQueryRunner.changeColumn,MysqlQueryRunner.changeColumn - TypeORM issue #3357
- PostgreSQL: ALTER TABLE, SET DATA TYPE
- MySQL: ALTER TABLE, renaming, redefining, and reordering columns