no-truncating-type-change
Reports a column type change whose USING expression keeps only part of each value, such as left() or a cast to varchar(n).
| Category | Default severity | Databases |
|---|---|---|
| data-loss | warn | PostgreSQL |
What happens
ALTER COLUMN ... TYPE ... USING <expression> replaces every value with the result of the expression. Some expressions drop part of a value, with no error:
left(),right(),substr(),substring(), andsplit_part()keep some charactersround(),trunc(), anddate_trunc()drop digits or parts of a date- an explicit cast to
varchar(n)orchar(n)cuts longer values toncharacters
The dropped part is gone once the migration commits. Without USING, PostgreSQL behaves differently: making a varchar shorter fails on the first value that is too long, so nothing is lost.
The type change also rewrites the table under a lock, which no-unsafe-column-type-change reports.
Bad
import { MigrationInterface, QueryRunner } from 'typeorm'
export class ShortenUserCode1727200000000 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "users" ALTER COLUMN "code" TYPE varchar(5) USING left("code", 5)`,
)
}
public async down(queryRunner: QueryRunner): Promise<void> {
// ...
}
}Safe
First check that no value would be cut:
SELECT count(*) FROM "users" WHERE "code" <> left("code", 5);Then change the type without USING. PostgreSQL fails instead of cutting a value that does not fit:
import { MigrationInterface, QueryRunner } from 'typeorm'
export class ShortenUserCode1727200000000 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "code" TYPE varchar(5)`)
}
public async down(queryRunner: QueryRunner): Promise<void> {
// ...
}
}To keep the original values, add a new column with the new type, copy the values, switch the code, and drop the old column in a later release.
When to suppress
When cutting the values is the point, for example to drop a known suffix, and you have checked which rows change.
// preflight safety-assured no-truncating-type-change -- codes were padded to 8 characters by the old importer; the last 3 are always spacesReferences
- PostgreSQL: character types ("if one explicitly casts a value to character varying(n) or character(n), then an over-length value will be truncated to n characters without raising an error")
- PostgreSQL: string functions (
left,right,substr,substring,split_part) - PostgreSQL: mathematical functions (
round,trunc) and date/time functions (date_trunc) - PostgreSQL: ALTER TABLE, SET DATA TYPE ("The optional USING clause specifies how to compute the new column value from the old")
- orm-preflight verification results ("varchar(3) USING left(name, 3)" keeps "n10" of "n1000", and the plain change fails)