Skip to content

fix(postgres): alter varchar char length in place on postgres 3357 - #12836

Open
BayaniCyberLabs wants to merge 1 commit into
typeorm:masterfrom
BayaniCyberLabs:fix/3357-postgres-varchar-length-alter
Open

fix(postgres): alter varchar char length in place on postgres 3357#12836
BayaniCyberLabs wants to merge 1 commit into
typeorm:masterfrom
BayaniCyberLabs:fix/3357-postgres-varchar-length-alter

Conversation

@BayaniCyberLabs

@BayaniCyberLabs BayaniCyberLabs commented Sep 4, 2026

Copy link
Copy Markdown

Fixes #3357.

We found this issue on a bounty board and wanted to send in a fix, even though the bounty is pretty old.

The Problem:
Changing a varchar/char length on Postgres (say varchar(50) to varchar(51)) currently makes TypeORM emit DROP COLUMN + ADD COLUMN, which throws away the data in that column.

What We Did:
Length-only changes on varchar, character varying, char and character now go through ALTER TABLE ... ALTER COLUMN ... TYPE instead, so the rows survive. varchar and character varying (and char/character) are compared as the same type, so just spelling it differently in the entity no longer triggers a drop/add. If the collation changes too, we stay on the old path. Collation-only ALTERs are unchanged.

Tests:
test/github-issues/3357/issue-3357.test.ts covers the generated SQL and a live Postgres run.

Other Found Bug:
Changing length and collation together still does DROP+ADD. On a nullable column you keep the rows but the values come back null. On NOT NULL without a transaction, the DROP can commit and the ADD then fails with 23502, so you end up with no column at all. #3357 was only about the length-only case so we left that path as is. If you want the mixed case fixed too, we can do a follow-up.

BayaniCyberLabs (Bayani Cyber Solutions LLC)
https://www.bayanicybersolutions.us

Happy to pick up other TypeORM/Postgres schema work if you have any.

Use ALTER COLUMN ... TYPE for length-only changes instead of DROP COLUMN + ADD COLUMN, which destroyed data.

Closes: typeorm#3357
@BayaniCyberLabs

Copy link
Copy Markdown
Author

/try

@BayaniCyberLabs

Copy link
Copy Markdown
Author

/claim

@github-actions github-actions Bot added linked-issue PR references an issue possible-duplicate PR may duplicate an existing open PR labels Sep 4, 2026
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown

Other open PRs also reference #3357: #12532,#12541,#12543,#12544,#12717,#12738,#12740,#12754,#12761,#12770,#12790,#12798,#12835. Maintainers may want to coordinate.

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (2) 📎 Requirement gaps (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Tests bypass functional suite 📘 Rule violation ⚙ Maintainability
Description
The issue coverage is added exclusively under test/github-issues, despite an existing PostgreSQL
column-length functional suite. This violates the requirement to place issue fixes in
test/functional without a clear reason for an exception.
Code

test/github-issues/3357/issue-3357.test.ts[16]

+describe("github issues > #3357 postgres character length SQL", () => {
Evidence
PR Compliance ID 3 requires issue fixes to add or update tests in test/functional. The new suite
explicitly identifies itself as a GitHub issue suite, while the repository already contains a
functional PostgreSQL column-length test covering size updates.

Rule 3: Prefer functional tests over per-issue tests
test/github-issues/3357/issue-3357.test.ts[16-16]
test/functional/database-schema/column-length/postgres/column-length-postgres.test.ts[44-74]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The PostgreSQL character-length regression tests were added as a per-issue suite instead of extending the relevant functional test suite.

## Issue Context
The repository already has a PostgreSQL column-length functional suite. Preserve the important generated-SQL and live data-retention scenarios, and identify the regression with an issue `#3357` comment where applicable.

## Fix Focus Areas
- test/github-issues/3357/issue-3357.test.ts[1-470]
- test/functional/database-schema/column-length/postgres/column-length-postgres.test.ts[44-74]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. any bypasses test typing 📘 Rule violation ⚙ Maintainability
Description
The new tests cast runner to any to stub internal methods, bypassing TypeScript's access and
signature checks. The checklist explicitly prohibits any casts used to evade type safety.
Code

test/github-issues/3357/issue-3357.test.ts[R59-60]

+            .stub(runner as any, "executeQueries")
+            .callsFake(async (up: Query | Query[], down: Query | Query[]) => {
Evidence
PR Compliance ID 4 lists any casts that bypass types as failure criteria. The test introduces both
any-typed driver fixtures and runner as any casts to access and stub implementation details.

Rule 4: Remove AI-generated noise
test/github-issues/3357/issue-3357.test.ts[18-18]
test/github-issues/3357/issue-3357.test.ts[58-64]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new tests use `any` annotations and casts to construct and manipulate test doubles, bypassing compile-time type checking.

## Issue Context
Prefer typed fixtures or public functional behavior. Relocating these scenarios into the existing functional suite may eliminate the need to stub internal `PostgresQueryRunner` methods entirely.

## Fix Focus Areas
- test/github-issues/3357/issue-3357.test.ts[17-38]
- test/github-issues/3357/issue-3357.test.ts[56-65]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Alias type cache stays stale 🐞 Bug ≡ Correctness
Description
After an in-place varcharcharacter varying or charcharacter length change,
changeColumn updates only the cached length and leaves the old type alias. A subsequent
same-length change using the new alias can therefore be misclassified as a type change and execute
DROP COLUMN plus ADD COLUMN, losing existing values.
Code

src/driver/postgres/PostgresQueryRunner.ts[R1388-1391]

+                const clonedColumn = clonedTable.columns.find(
+                    (column) => column.name === oldColumn.name,
+                )
+                if (clonedColumn) clonedColumn.length = newColumn.length
Evidence
The alias normalization permits canAlterLength when the concrete TableColumn.type values differ,
and the recreation condition is then suppressed. The new clone update changes only length;
replaceCachedTable subsequently installs that clone, so the old alias remains observable and can
trigger the existing unequal-type recreation condition on the next call.

src/driver/postgres/PostgresQueryRunner.ts[1329-1354]
src/driver/postgres/PostgresQueryRunner.ts[1356-1367]
src/driver/postgres/PostgresQueryRunner.ts[1388-1392]
src/driver/postgres/PostgresQueryRunner.ts[2515-2516]
src/query-runner/BaseQueryRunner.ts[374-399]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new in-place character-length alteration updates the cloned cached column's length but not its type. When the operation also changes between equivalent PostgreSQL aliases, the cache retains the old alias and a later same-length operation can incorrectly take the destructive drop/add path.

## Issue Context
`canAlterLength` deliberately treats `varchar`/`character varying` and `char`/`character` as equivalent, so the cached `TableColumn` must reflect both the new length and requested type after the ALTER succeeds.

## Fix Focus Areas
- src/driver/postgres/PostgresQueryRunner.ts[1388-1392]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


  • Author self-review: I have reviewed the code review findings, and addressed the relevant ones.

Grey Divider

Context sources

Grey Divider

Tip of the day
💡 Did you know, you can route each action level your way: inline, summary, both, or drop

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

@BayaniCyberLabs BayaniCyberLabs changed the title fix: alter varchar/char length in place on postgres (#3357) fix: alter varchar/char length in place on postgres Sep 4, 2026
@BayaniCyberLabs BayaniCyberLabs changed the title fix: alter varchar/char length in place on postgres fix: alter varchar/char length in place on postgres (3357) Sep 4, 2026
@BayaniCyberLabs

Copy link
Copy Markdown
Author
  • Author self-review: I have reviewed the code review findings, and addressed the relevant ones.

@BayaniCyberLabs BayaniCyberLabs changed the title fix: alter varchar/char length in place on postgres (3357) fix(postgres): alter varchar char length in place on postgres 3357 Sep 8, 2026
@BayaniCyberLabs

Copy link
Copy Markdown
Author

/claim

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Compliance violation linked-issue PR references an issue possible-duplicate PR may duplicate an existing open PR

Development

Successfully merging this pull request may close these issues.

Migration generation drops and creates columns instead of altering resulting in data loss

1 participant