Skip to content

Conversation

taina0407
Copy link
Contributor

@taina0407 taina0407 commented Jul 8, 2025

Description of change

This PR fixes a critical array modification bug in QueryRunner implementations where iterating over arrays while simultaneously modifying them during iteration causes elements to be skipped.

Fixes:

Problem:
Multiple QueryRunner drop methods (dropColumns, dropIndices, dropForeignKeys, dropUniqueConstraints) had a classic "iteration while modifying" bug. When these methods iterate over arrays using for (const item of array) and call individual drop methods, the individual methods internally remove items from the same array being iterated over. This causes the iterator to skip elements because array indices shift during iteration.

Example of the bug:
When dropping columns [A, B, C, D]:

  1. Iterator starts at index 0, drops column A
  2. Array becomes [B, C, D], but iterator moves to index 1
  3. Iterator now points to column C (skipping B)
  4. Only columns A and C are dropped, B and D remain

Solution:
Changed all affected methods to iterate over a shallow copy of the input array using the spread operator [...array]. This ensures that modifications to the original array don't affect the iteration.

Before (buggy):

for (const column of columns) {
    await this.dropColumn(tableOrName, column)
}

After (fixed):

for (const column of [...columns]) {
    await this.dropColumn(tableOrName, column)
}

Affected drivers and methods:

  • dropColumns: SpannerQueryRunner, PostgresQueryRunner, MysqlQueryRunner, SqlServerQueryRunner, OracleQueryRunner, CockroachQueryRunner, SapQueryRunner, AuroraMysqlQueryRunner
  • dropIndices: SpannerQueryRunner, PostgresQueryRunner, MysqlQueryRunner, SqlServerQueryRunner, OracleQueryRunner, CockroachQueryRunner, SapQueryRunner, AuroraMysqlQueryRunner
  • dropForeignKeys: SpannerQueryRunner, PostgresQueryRunner, CockroachQueryRunner, SapQueryRunner
  • dropUniqueConstraints: PostgresQueryRunner, SqlServerQueryRunner, OracleQueryRunner, CockroachQueryRunner

Verification:

Impact:
This fix prevents potential data loss scenarios where users expect all specified database objects to be dropped but some are silently left behind due to the iteration bug.

Pull-Request Checklist

  • Code is up-to-date with the master branch
  • This pull request links relevant issues as Fixes #11563
  • There are new or updated unit tests validating the change
  • Documentation has been updated to reflect this change

Summary by CodeRabbit

  • Bug Fixes

    • Improved reliability when dropping multiple columns, foreign keys, indices, or unique constraints at once by preventing skipped elements due to array modifications during iteration.
  • Tests

    • Added new test cases to verify batch dropping of columns, foreign keys, indices, and unique constraints processes all items correctly without skipping any.

Fix iteration bug in QueryRunner implementations where dropping multiple
database objects (columns, indices, foreign keys, unique constraints) would
skip elements due to in-place array modification during iteration.

The issue occurred when methods like dropColumns(), dropIndices(),
dropForeignKeys(), and dropUniqueConstraints() iterated over arrays while
simultaneously modifying them by removing elements. This caused a classic
"off-by-one" iteration bug where alternate elements would be skipped.

Changes:
- Update all affected QueryRunner drop methods to iterate over a shallow
  copy of the input array using [...array] spread syntax
- Add comprehensive regression tests in test/github-issues/11563/
- Test coverage includes all affected drivers: Postgres, MySQL, SQL Server,
  Oracle, CockroachDB, Spanner, SAP, and Aurora MySQL

Affected drivers:
- SpannerQueryRunner
- PostgresQueryRunner
- MysqlQueryRunner
- SqlServerQueryRunner
- OracleQueryRunner
- CockroachQueryRunner
- SapQueryRunner
- AuroraMysqlQueryRunner

Closes typeorm#11563
Copy link

coderabbitai bot commented Jul 8, 2025

Walkthrough

This update modifies several query runner methods for various database drivers to iterate over shallow copies of arrays when dropping columns, indices, foreign keys, and unique constraints. This prevents issues from array mutation during iteration. New tests are added to verify that all specified schema elements are dropped without skipping any due to array modification.

Changes

File(s) Change Summary
src/driver/aurora-mysql/AuroraMysqlQueryRunner.ts
src/driver/mysql/MysqlQueryRunner.ts
src/driver/oracle/OracleQueryRunner.ts
src/driver/sap/SapQueryRunner.ts
src/driver/sqlserver/SqlServerQueryRunner.ts
Updated dropColumns to iterate over a shallow copy of the input array instead of the original.
src/driver/cockroachdb/CockroachQueryRunner.ts
src/driver/postgres/PostgresQueryRunner.ts
Updated methods for dropping columns, unique constraints, foreign keys, and indices to use shallow copies for iteration.
src/driver/spanner/SpannerQueryRunner.ts Updated dropColumns, dropForeignKeys, and dropIndices to iterate over shallow copies.
test/functional/query-runner/drop-column.ts Added a test to verify dropping multiple columns does not skip any due to array modification.
test/functional/query-runner/drop-foreign-key.ts Added a test to verify dropping multiple foreign keys does not skip any due to array modification.
test/functional/query-runner/drop-index.ts Added a test to verify dropping multiple indices does not skip any due to array modification.
test/functional/query-runner/drop-unique-constraint.ts Added a test to verify dropping multiple unique constraints does not skip any due to array modification.

Sequence Diagram(s)

sequenceDiagram
    participant Test as Test Suite
    participant QR as QueryRunner
    participant DB as Database

    Test->>QR: dropColumns(table, [col1, col2, col3, col4])
    loop For each column in [...columns]
        QR->>DB: DROP COLUMN colX
        DB-->>QR: Success
    end
    QR-->>Test: Operation complete
Loading

Possibly related issues

Suggested labels

comp: relations, need review help

Suggested reviewers

  • alumni
  • gioboa
  • sgarner
  • mguida22

Poem

In the garden of code where columns once grew,
Rabbits hop by, array copies in view.
Dropping constraints, indices, and keys—
No more skipping, just smooth expertise!
With tests to ensure all bugs are through,
The schema is tidy, and so are you! 🐇✨

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

npm error Exit handler never called!
npm error This is an error with npm itself. Please report this error at:
npm error https://github.com/npm/cli/issues
npm error A complete log of this run can be found in: /.npm/_logs/2025-07-09T02_49_34_767Z-debug-0.log


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between df1f6a5 and 99e366c.

📒 Files selected for processing (4)
  • test/functional/query-runner/drop-column.ts (2 hunks)
  • test/functional/query-runner/drop-foreign-key.ts (2 hunks)
  • test/functional/query-runner/drop-index.ts (2 hunks)
  • test/functional/query-runner/drop-unique-constraint.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • test/functional/query-runner/drop-index.ts
  • test/functional/query-runner/drop-column.ts
  • test/functional/query-runner/drop-unique-constraint.ts
  • test/functional/query-runner/drop-foreign-key.ts
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/driver/postgres/PostgresQueryRunner.ts (2)

2969-2972: Foreign-key batch drop safeguarded

Using a spread copy eliminates the mutation hazard. 👍
Minor nit: if the array is extremely large, slice() might be marginally faster than the spread operator, but that’s non-blocking and stylistic.


3097-3099: Indices loop fixed likewise

The same issue is resolved here; implementation is sound and mirrors other methods.
Consider adding a small helper like for (const i of toArray(indices)) to DRY this pattern across drivers, but optional.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3c26cf1 and 10edcd5.

📒 Files selected for processing (10)
  • src/driver/aurora-mysql/AuroraMysqlQueryRunner.ts (1 hunks)
  • src/driver/cockroachdb/CockroachQueryRunner.ts (4 hunks)
  • src/driver/mysql/MysqlQueryRunner.ts (1 hunks)
  • src/driver/oracle/OracleQueryRunner.ts (1 hunks)
  • src/driver/postgres/PostgresQueryRunner.ts (4 hunks)
  • src/driver/sap/SapQueryRunner.ts (1 hunks)
  • src/driver/spanner/SpannerQueryRunner.ts (3 hunks)
  • src/driver/sqlserver/SqlServerQueryRunner.ts (1 hunks)
  • test/github-issues/11563/entity/TestEntity.ts (1 hunks)
  • test/github-issues/11563/issue-11563.ts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
test/github-issues/11563/entity/TestEntity.ts (2)
src/decorator/columns/PrimaryGeneratedColumn.ts (1)
  • PrimaryGeneratedColumn (55-119)
src/decorator/columns/Column.ts (1)
  • Column (134-220)
🔇 Additional comments (20)
test/github-issues/11563/entity/TestEntity.ts (1)

1-10: Test entity is well-structured for testing the array mutation fix.

The TestEntity properly uses TypeORM decorators and provides a simple but effective schema for testing the batch drop operations that were affected by the array iteration bug. The entity structure supports comprehensive testing of dropColumns, dropIndices, dropForeignKeys, and dropUniqueConstraints methods.

src/driver/aurora-mysql/AuroraMysqlQueryRunner.ts (1)

1417-1417: Excellent fix for the array mutation during iteration bug.

The use of the spread operator [...columns] creates a shallow copy that prevents the iteration from being affected when dropColumn internally modifies the original array. This ensures all columns are dropped without skipping elements, which was the core issue described in #11563.

This change is minimal, targeted, and consistent with the fix pattern applied across other database drivers in this PR.

src/driver/sqlserver/SqlServerQueryRunner.ts (1)

2118-2118: Excellent fix for the array modification during iteration bug!

The use of the spread operator [...columns] correctly creates a shallow copy of the array, preventing the iteration from being affected when individual dropColumn calls modify the original array. This ensures all columns are properly dropped without any being skipped due to index shifts during iteration.

src/driver/mysql/MysqlQueryRunner.ts (1)

1802-1802: LGTM! Excellent fix for the array modification bug.

The spread operator [...columns] creates a shallow copy of the input array, preventing the iteration from being affected if the original array is modified during the dropColumn operations. This change correctly addresses the bug described in issue #11563 where elements could be skipped during batch drop operations.

src/driver/oracle/OracleQueryRunner.ts (1)

1733-1733: LGTM! Correct fix for the array modification bug.

The use of the spread operator [...columns] creates a shallow copy of the array, preventing the iteration bug where dropping columns during the loop would modify the original array and cause elements to be skipped. This fix aligns perfectly with the PR objectives and ensures all specified columns are properly dropped.

src/driver/sap/SapQueryRunner.ts (1)

1831-1831: LGTM! Excellent fix for the array modification during iteration bug.

The use of the spread operator to create a shallow copy of the columns array before iteration is the correct solution to prevent elements from being skipped when the underlying data structures are modified during the dropColumn calls. This defensive programming approach ensures that all specified columns are properly dropped without iterator invalidation issues.

src/driver/postgres/PostgresQueryRunner.ts (2)

2530-2532: Iteration now safe against in-loop mutations – good fix

Iterating over a shallow copy ([...columns]) prevents the skipped-element bug when dropColumn mutates the original array. ✔️
No further issues spotted here.


2731-2733: Consistent defensive copy for unique constraints

Same protective pattern applied to dropUniqueConstraints. Looks correct and keeps behaviour consistent with the single-drop version.

src/driver/spanner/SpannerQueryRunner.ts (3)

1055-1055: LGTM! Critical bug fix implemented correctly.

The spread operator creates a shallow copy of the columns array, preventing the iteration bug where elements get skipped due to array modifications during the async dropColumn calls.


1345-1345: LGTM! Critical bug fix implemented correctly.

The spread operator creates a shallow copy of the foreignKeys array, preventing the iteration bug where elements get skipped due to array modifications during the async dropForeignKey calls.


1419-1419: LGTM! Critical bug fix implemented correctly.

The spread operator creates a shallow copy of the indices array, preventing the iteration bug where elements get skipped due to array modifications during the async dropIndex calls.

test/github-issues/11563/issue-11563.ts (5)

16-40: Excellent test suite setup with comprehensive driver coverage.

The test configuration properly includes all affected drivers and handles schema setup/teardown correctly. The driver selection aligns with the drivers modified in this PR.


41-143: Well-structured test for column drop iteration bug.

The test properly validates the core issue by:

  • Adding multiple test columns to trigger the bug scenario
  • Verifying all columns exist before the drop operation
  • Using the batch dropColumns method that was affected by the bug
  • Confirming all test columns are removed while preserving entity columns

The error messages are descriptive and will help with debugging if the test fails.


145-213: Comprehensive test for index drop iteration bug.

The test follows the same excellent pattern as the column test, properly validating that all indices are dropped without skipping any due to array modification during iteration.


215-349: Thorough foreign key test with proper driver filtering.

The test correctly skips Spanner (which doesn't support foreign keys) and includes proper setup of a referenced table. The cleanup properly removes the auxiliary table, preventing test interference.


351-465: Complete unique constraint test with appropriate driver exclusions.

The test properly excludes drivers that don't support unique constraints or handle them differently (Spanner, SAP, MySQL variants). This ensures the test only runs on databases where the functionality is supported.

src/driver/cockroachdb/CockroachQueryRunner.ts (4)

2314-2314: LGTM! Correctly fixes the array iteration bug.

Using the spread operator [...columns] creates a shallow copy of the array, preventing iteration issues when the original array is modified during individual drop operations. This matches the fix pattern described in the PR objectives.


2518-2518: LGTM! Correctly fixes the array iteration bug.

Using the spread operator [...uniqueConstraints] creates a shallow copy of the array, preventing iteration issues when the original array is modified during individual drop operations. This matches the fix pattern described in the PR objectives.


2715-2715: LGTM! Correctly fixes the array iteration bug.

Using the spread operator [...foreignKeys] creates a shallow copy of the array, preventing iteration issues when the original array is modified during individual drop operations. This matches the fix pattern described in the PR objectives.


2800-2800: LGTM! Correctly fixes the array iteration bug.

Using the spread operator [...indices] creates a shallow copy of the array, preventing iteration issues when the original array is modified during individual drop operations. This matches the fix pattern described in the PR objectives.

Copy link
Collaborator

@sgarner sgarner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great catch and fix, @taina0407 👌

import { Table } from "../../../src/schema-builder/table/Table"
import { TestEntity } from "./entity/TestEntity"

describe("github issues > #11563 QueryRunner array modification during iteration bug", () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are moving away from creating tests for specific GitHub issues.

Can you move these tests to the appropriate places in test/functional/query-runner/drop-column.ts, test/functional/query-runner/drop-index.ts, etc?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for suggestion @sgarner, I've created 4 separate tests in test/functional/query-runner.

Copy link

pkg-pr-new bot commented Jul 9, 2025

typeorm-sql-js-example

npm i https://pkg.pr.new/typeorm/typeorm@11564

commit: 99e366c

@coveralls
Copy link

Coverage Status

coverage: 76.44% (+0.05%) from 76.391%
when pulling 99e366c on taina0407:master
into 1737e97 on typeorm:master.

Copy link
Collaborator

@sgarner sgarner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution @taina0407 💜

@sgarner sgarner requested review from alumni and gioboa July 9, 2025 04:43
Copy link
Collaborator

@gioboa gioboa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @taina0407 thanks for this great fix 🚀

@gioboa gioboa merged commit f351757 into typeorm:master Jul 9, 2025
58 checks passed
@alumni
Copy link
Collaborator

alumni commented Jul 9, 2025

Nice :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants