Issue description
PostgresQueryRunner.loadTables() re-scans the full catalog arrays once per table, so schema loading grows as tables × rows.
After the four catalog queries return, dbTables.map(...) walks every table and, for each one, runs a full .filter() over dbColumns, dbConstraints (three times), dbForeignKeys and dbIndices. One of them is nested a level deeper: for every column of the table it scans all of dbConstraints again to find that column's constraints.
Roughly:
tables × columns + (total columns) × constraints + tables × (3×constraints + fks + indices)
loadTables() runs on synchronize, on migration runs, and whenever the schema is introspected, so this shows up as start-up / deploy latency on larger schemas.
Expected Behavior
Schema loading time grows roughly linearly with the size of the schema.
Actual Behavior
It grows quadratically. Measured on a faithful extraction of the current filter structure (same predicates, synthetic catalog rows of 20 columns / 8 constraints / 3 FKs / 4 indices per table), median of repeated runs, output compared for equality on every size:
| tables |
columns |
current |
grouped first |
|
| 5 |
100 |
0.036 ms |
0.024 ms |
1.5x |
| 20 |
400 |
0.410 ms |
0.096 ms |
4.3x |
| 50 |
1,000 |
2.40 ms |
0.244 ms |
9.8x |
| 100 |
2,000 |
10.2 ms |
0.450 ms |
22.7x |
| 300 |
6,000 |
77.7 ms |
1.41 ms |
55x |
| 800 |
16,000 |
630 ms |
4.5 ms |
140x |
Small schemas are not penalised — grouping is already cheaper at 5 tables, so there is no break-even to worry about.
Steps to reproduce
Connect to a Postgres schema with a few hundred tables and run anything that triggers loadTables() (synchronize, migration:run, or queryRunner.getTables()), and profile the time spent after the catalog queries return.
My Environment
| Dependency |
Version |
| Operating System |
macOS |
| Node.js version |
v22 |
| TypeORM version |
master |
| Database |
Postgres |
Additional Context
The same pattern exists in the other drivers' loadTables() (cockroachdb, aurora-mysql, mysql, sqlserver, sap, oracle, spanner). Happy to follow up per-driver if this direction looks right — keeping the first change to Postgres so it stays reviewable.
Relevant Database Driver(s)
Are you willing to resolve this issue by submitting a Pull Request?
Yes, I have the time, and I know how to start.
Issue description
PostgresQueryRunner.loadTables()re-scans the full catalog arrays once per table, so schema loading grows astables × rows.After the four catalog queries return,
dbTables.map(...)walks every table and, for each one, runs a full.filter()overdbColumns,dbConstraints(three times),dbForeignKeysanddbIndices. One of them is nested a level deeper: for every column of the table it scans all ofdbConstraintsagain to find that column's constraints.Roughly:
loadTables()runs onsynchronize, on migration runs, and whenever the schema is introspected, so this shows up as start-up / deploy latency on larger schemas.Expected Behavior
Schema loading time grows roughly linearly with the size of the schema.
Actual Behavior
It grows quadratically. Measured on a faithful extraction of the current filter structure (same predicates, synthetic catalog rows of 20 columns / 8 constraints / 3 FKs / 4 indices per table), median of repeated runs, output compared for equality on every size:
Small schemas are not penalised — grouping is already cheaper at 5 tables, so there is no break-even to worry about.
Steps to reproduce
Connect to a Postgres schema with a few hundred tables and run anything that triggers
loadTables()(synchronize,migration:run, orqueryRunner.getTables()), and profile the time spent after the catalog queries return.My Environment
Additional Context
The same pattern exists in the other drivers'
loadTables()(cockroachdb, aurora-mysql, mysql, sqlserver, sap, oracle, spanner). Happy to follow up per-driver if this direction looks right — keeping the first change to Postgres so it stays reviewable.Relevant Database Driver(s)
Are you willing to resolve this issue by submitting a Pull Request?
Yes, I have the time, and I know how to start.