MySQL: renameTable() updates foreign key constraint names but leaves supporting index names unchanged, causing invalid migrations
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 36.7k
- Forks
- 6.7k
- Avg merge
- 3d 1h
- Merged PRs (30d)
- 6
Description
Issue description
After renaming a table using a migration, TypeORM generates incorrect migrations that attempt to drop indexes required by foreign key constraints.
Expected Behavior
One of the following should happen:
1 . renameTable() should also rename the supporting indexes to match the updated schema metadata,
or
2 The schema diff should recognize that these indexes are required by the foreign keys and should not generate DROP INDEX statement
Actual Behavior
await queryRunner.renameTable('reports', 'circle_reports'); only run the following:
query: RENAME TABLE `reports` TO `circle_reports`
query: ALTER TABLE `circle_reports` DROP FOREIGN KEY `FK_90128aa0533cf113b418de0304f`, ADD CONSTRAINT `FK_e8b4f00e21f04a1e8de2cd99338` FOREIGN KEY (`comment_id`) REFERENCES `comments`(`id`) ON DELETE CASCADE ON UPDATE NO ACTION
query: ALTER TABLE `circle_reports` DROP FOREIGN KEY `FK_b84fcce5b84191fc4b8e5d0d6f9`, ADD CONSTRAINT `FK_3b4c6f59d3aeb0a197ad66dd48d` FOREIGN KEY (`post_id`) REFERENCES `posts`(`id`) ON DELETE CASCADE ON UPDATE NO ACTION
query: ALTER TABLE `circle_reports` DROP FOREIGN KEY `FK_ca7a21eb95ca4625bd5eaef7e0c`, ADD CONSTRAINT `FK_8e204e8dab7cb9702cc83fa7a4c` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE ON UPDATE NO ACTION
query: INSERT INTO `medchatter_typeorm`.`migrations`(`timestamp`, `name`) VALUES (?, ?) -- PARAMETERS: [1783342743130,"RenameReportsToCircleReports1783342743130"]
SHOW CREATE TABLE circle_reports;
'circle_reports', 'CREATE TABLE `circle_reports` (\n `id` int NOT NULL AUTO_INCREMENT,\n `reason` varchar(300) NOT NULL,\n `user_id` int NOT NULL,\n `post_id` int DEFAULT NULL,\n `created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),\n `status` enum(\'pending\',\'keep\',\'remove\') NOT NULL DEFAULT \'pending\',\n `resolved_at` datetime(3) DEFAULT NULL,\n `comment_id` int DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `FK_ca7a21eb95ca4625bd5eaef7e0c` (`user_id`),\n KEY `FK_b84fcce5b84191fc4b8e5d0d6f9` (`post_id`),\n KEY `FK_90128aa0533cf113b418de0304f` (`comment_id`),\n CONSTRAINT `FK_3b4c6f59d3aeb0a197ad66dd48d` FOREIGN KEY (`post_id`) REFERENCES `posts` (`id`) ON DELETE CASCADE,\n CONSTRAINT `FK_8e204e8dab7cb9702cc83fa7a4c` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,\n CONSTRAINT `FK_e8b4f00e21f04a1e8de2cd99338` FOREIGN KEY (`comment_id`) REFERENCES `comments` (`id`) ON DELETE CASCADE\n) ENGINE=InnoDB AUTO_INCREMENT=37 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci'
SHOW INDEX FROM circle_reports;
'circle_reports','0','PRIMARY','1','id','A','35',NULL,NULL,'','BTREE','','','YES',NULL
'circle_reports','1','FK_ca7a21eb95ca4625bd5eaef7e0c','1','user_id','A','7',NULL,NULL,'','BTREE','','','YES',NULL
'circle_reports','1','FK_b84fcce5b84191fc4b8e5d0d6f9','1','post_id','A','15',NULL,NULL,'YES','BTREE','','','YES',NULL
'circle_reports','1','FK_90128aa0533cf113b418de0304f','1','comment_id','A','10',NULL,NULL,'YES','BTREE','','','YES',NULL
Notice that the Index still using the old name even it was change
Steps to reproduce
- Create a table with foreign keys.
@Entity('reports')
export class Report {
@PrimaryGeneratedColumn({ name: 'id' })
id: number;
...
@ManyToOne(() => User, { onDelete: 'CASCADE' })
@JoinColumn({ name: 'user_id' })
user: User;
@ManyToOne(() => Post, { onDelete: 'CASCADE', nullable: true })
@JoinColumn({ name: 'post_id' })
post?: Post;
@ManyToOne(() => Comment, { onDelete: 'CASCADE', nullable: true })
@JoinColumn({ name: 'comment_id' })
comment?: Comment;
}
- rename table
@Entity('circle_reports')
export class CircleReport { ... }
- create migration using
renameTable
import { MigrationInterface, QueryRunner } from 'typeorm';
export class RenameReportsToCircleReports1783342743130
implements MigrationInterface
{
name = 'RenameReportsToCircleReports1783342743130';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.renameTable('reports', 'circle_reports');
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.renameTable('circle_reports', 'reports');
}
}
- run migration
5 generate new migration
DROP INDEX `FK_90128aa0533cf113b418de0304f` ON `circle_reports`;
DROP INDEX `FK_b84fcce5b84191fc4b8e5d0d6f9` ON `circle_reports`;
DROP INDEX `FK_ca7a21eb95ca4625bd5eaef7e0c` ON `circle_reports`;
My Environment
| Dependency | Version |
|---|---|
| Operating System | macOS |
| Node.js version | x.y.zzz |
| Typescript version | ^5.9.3 |
| TypeORM version | ^0.3.28 |
Additional Context
Im not sure if this is the reason, but i notice the name in indexes and foreign keys are different. I seems that the foreign keys are change but the name in index are not changed
Relevant Database Driver(s)
- aurora-mysql
- aurora-postgres
- better-sqlite3
- capacitor
- cockroachdb
- cordova
- expo
- mongodb
- mysql
- nativescript
- oracle
- postgres
- react-native
- sap
- spanner
- sqlite
- sqljs
- sqlserver
Are you willing to resolve this issue by submitting a Pull Request?
No, I don’t have the time and I’m okay to wait for the community / maintainers to resolve this issue.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the MySQL implementation of QueryRunner.renameTable and the schema-diff path that produces the DROP INDEX statements. Reproduce the migration and SHOW CREATE TABLE/SHOW INDEX results from the issue, then verify that a subsequent generated migration no longer tries to drop indexes required by the foreign keys.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- mysql, typescript
- Domain
- databases
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100