PG.L7.TYPE_CHANGE_DROPS_STATISTICS — A type change that takes the column's statistics with it
- Category: safety
- Level: 7
- Confidence: deterministic
- Downtime class:
online - Stability: stable
- Suites: lint
- Applies to: PostgreSQL 18
ALTER COLUMN … TYPE removes the column's planner statistics in every form, the free ones
included: a binary-coercible change such as varchar(10) to varchar(20) or to text, and the
type the column already has, which Laravel's ->change() names on every call, also when only the
nullability or the default changes. The change adds nothing to n_mod_since_analyze, so autovacuum
has no reason to analyze the table again. Until ordinary writes cross its threshold, PostgreSQL
plans every query on the column with default estimates, and on a large table that is mostly read
that can take a long time.
Measured on PostgreSQL 18.0 over 50,000 rows, with autovacuum off for the table: a
binary-coercible varchar(10) to varchar(20), the type a column already had beside
DROP NOT NULL, and an integer to bigint rewrite each left the column without pg_stats rows.
The other columns kept theirs, n_mod_since_analyze did not move, and an ANALYZE of the table
put them back.
Flagged
// A free change: note was varchar, and text rewrites nothing.
DB::statement('ALTER TABLE orders ALTER COLUMN note TYPE text');
Preferred
DB::statement('ALTER TABLE orders ALTER COLUMN note TYPE text');
// The type change removed the column's planner statistics; this puts them back:
DB::statement('ANALYZE orders');
An ANALYZE of the table later in the same migration, or of the whole database, keeps the rule
silent. One with a column list counts when the list holds the changed column. ANALYZE runs inside
the migration's transaction and takes SHARE UPDATE EXCLUSIVE, which no read and no write waits
behind.
Where it stays silent
- The change rewrites the table, or might.
PG.L2.TYPE_CHANGE_REWRITEreports it, and its finding names the statistics as well, so a statement carries one finding about them. - The table is created in the same migration. It has no statistics to lose yet.
What it cannot see
The rule reads one migration. An ANALYZE in a later migration of the same deploy, or in a deploy
step that runs after migrate, puts the statistics back too, and the change is still reported.
Sources
- PostgreSQL 18 —
ALTER TABLE— "When this form is used, the column's statistics are removed, so running ANALYZE on the table afterwards is recommended."
The fix material this rule carries
A finding from this rule carries machine-readable fix material, using this sequence:
analyze_after— Leave the statement as written and follow it with anANALYZEof its table, in the same migration.
The payload is material for you or an agent to apply. SQLens writes no migration and runs no DDL. See the remediation payload for every field, the placeholder semantics, and the version rules a consumer has to follow.