-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
fix(core): ensure bulkInsert returns array of inserted IDs #17983
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
fix(core): ensure bulkInsert returns array of inserted IDs #17983
Conversation
WalkthroughModify MySQL and MariaDB insert result formatting so bulk inserts return consistent arrays of Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant App
participant QI as QueryInterface
participant Conn as Dialect Connection
participant Q as DialectQuery.formatResults
App->>QI: bulkInsert(table, rows, { returning: true })
QI->>Conn: Execute INSERT (bulk)
Conn-->>QI: ResultSetHeader { insertId, affectedRows, ... }
QI->>Q: formatResults(ResultSetHeader, options)
alt model context present
Q-->>QI: Array of inserted row objects (model-mapped/full rows)
else no model context
alt affectedRows > 1
Q-->>QI: Array of `{ id: startId + n }` for n = 0..affectedRows-1
else affectedRows == 1
Q-->>QI: Array with single `{ id: startId }`
else affectedRows == 0
Q-->>QI: Empty array
end
end
QI-->>App: Return dialect-specific result
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
|
Please adjust the tests and probably add new ones. It could be that we have tests that only check for certain dialects, you'll need to update those expectations as well to include mysql |
There was a problem hiding this 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 (1)
packages/core/test/unit/dialects/mysql/query.test.js (1)
39-45: Consider usingSupport.getTestDialectTeaser()for the test description.According to the coding guidelines, dialect-specific tests should use
Support.getTestDialectTeaser()in their descriptions. Since this test suite is specific to MySQL/MariaDB behavior, consider updating the describe block:- describe('formatResults', () => { + describe(Support.getTestDialectTeaser('MYSQL'), 'formatResults', () => {However, since the parent describe block at line 12 already indicates
[MYSQL/MARIADB Specific], this may be acceptable as-is.As per coding guidelines
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/core/test/unit/dialects/mysql/query.test.js(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
packages/**/test/**/*.test.js
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Name integration tests with the .test.js suffix
Files:
packages/core/test/unit/dialects/mysql/query.test.js
packages/**/test/**/*.test.{js,ts}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
packages/**/test/**/*.test.{js,ts}: Use Support.getTestDialectTeaser() for dialect-specific test descriptions
Use beforeEach/afterEach to sync with { force: true } and close Sequelize instances in tests
In tests, skip unsupported behavior with dialect.supports.featureName checks
Files:
packages/core/test/unit/dialects/mysql/query.test.js
🧬 Code graph analysis (1)
packages/core/test/unit/dialects/mysql/query.test.js (1)
packages/mysql/src/query.js (1)
MySqlQuery(25-324)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Upload install and build artifact (Node 18)
- GitHub Check: Upload install and build artifact (Node 20)
🔇 Additional comments (4)
packages/core/test/unit/dialects/mysql/query.test.js (4)
28-36: LGTM!The explicit assignment of
query.sqlensures the query object is properly initialized for the test, and the updated assertions correctly verify both the return value and the console.debug invocation.
46-64: LGTM!This test correctly validates the model context path for multi-row inserts, ensuring that when a model is present with an auto-increment primary key,
formatResultsreturns an array of ID objects ([{ id: 10 }, { id: 11 }, { id: 12 }]). The test setup and assertions align with the implementation inpackages/mysql/src/query.js.
66-78: LGTM!This test validates the core PR change: when there's no model context and multiple rows are inserted,
formatResultsreturns an array of ID objects ([{ id: 20 }, { id: 21 }]). This directly addresses issue #14325 and aligns with the implementation inpackages/mysql/src/query.js.
80-92: LGTM!This test ensures backward compatibility by validating that single-row inserts (affectedRows: 1) continue to return a scalar ID value (30) rather than an array. This confirms the PR change doesn't break existing behavior for single-row inserts.
|
Hi , it looks like the postgres oldest (Node 18) CI job failed due to a network issue while downloading dependencies (connection reset by peer). |
|
Hi @WikiRik |
WikiRik
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately we do not have a lot of tests for bulkInsert (or unified unit tests for bulkInsertQuery) so we should focus on that first. That's why I only gave comments to that file, especially with the returning option
Main thing to pay attention to is this comment; #14325 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New test files should be written in TypeScript.
|
|
||
| const dialect = Support.getTestDialect(); | ||
|
|
||
| describe(Support.getTestDialectTeaser('QueryInterface'), () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| describe(Support.getTestDialectTeaser('QueryInterface'), () => { | |
| describe('QueryInterface#bulkInsert', () => { | |
| beforeEach(function () { | |
| this.queryInterface = this.sequelize.queryInterface; | |
| }); |
This way we avoid another level of nesting
| if (!['mysql'].includes(dialect)) { | ||
| this.skip(); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should run on all dialects, and also give the same result
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (4)
packages/core/test/integration/query-interface/bulk-insert.test.ts (1)
33-33: Use Support.getTestDialectTeaser() for dialect-specific test descriptions.The coding guidelines specify using
Support.getTestDialectTeaser()for dialect-specific test descriptions. This helper provides consistent formatting and makes it clear when tests have dialect-specific behavior.Based on coding guidelines.
Apply this diff:
- it('should insert multiple rows and return inserted data according to dialect', async function testBulkInsert() { + it(`${Support.getTestDialectTeaser('should insert multiple rows and return inserted data')}`, async function testBulkInsert() {packages/mariadb/src/query.js (3)
125-137: Add warning comment about sequential ID assumption.The code assumes that IDs will be sequential (startId, startId+1, startId+2, etc.), similar to the model-context path (lines 107-123). However, the existing code includes an important warning (lines 111-112) that this only works when
@auto_increment_incrementis set to 1 and doesn't work with GALERA clusters. This same warning should be present for the non-model-context path.Apply this diff to add the warning comment:
// No model context, in queryinterface,bulkinsert for example + // ONLY TRUE IF @auto_increment_increment is set to 1 !! + // Doesn't work with GALERA => each node will reserve increment (x for first server, x+1 for next node...) const startId = Number(data[this.getInsertIdField()]); if (data.affectedRows > 1) {
126-126: Verify type consistency: Number() vs BigInt arithmetic.Line 126 uses
Number()to convertstartId, while the model-context path (line 119) uses BigInt arithmetic (startId + iwhereiis BigInt). If the database uses large ID values exceedingNumber.MAX_SAFE_INTEGER(2^53 - 1), this could cause precision loss.For consistency and to handle large IDs safely, consider using BigInt here as well, especially since the model-context path already does so.
Apply this diff if BigInt consistency is desired:
- const startId = Number(data[this.getInsertIdField()]); + const startId = BigInt(data[this.getInsertIdField()]); if (data.affectedRows > 1) { result = []; - for (let i = startId; i < startId + data.affectedRows; i++) { + for (let i = 0n; i < data.affectedRows; i++) { - result.push({ id: i }); + result.push({ id: Number(startId + i) }); } return [result, data.affectedRows]; } // Fallback: single insert ID - return [startId, data.affectedRows]; + return [Number(startId), data.affectedRows];
127-137: Consider return type consistency for single-row inserts.When multiple rows are affected (lines 128-134), the function returns an array of objects like
[{id: 1}, {id: 2}]. However, for single-row inserts (line 137), it returns a bare number directly:[startId, data.affectedRows].This inconsistency means callers must handle two different shapes:
- Multi-row:
[[{id: 1}, {id: 2}], 2]- Single-row:
[1, 1]For better consistency, consider wrapping the single-row ID in an object as well:
[{id: startId}, data.affectedRows]. This would make the return shape predictable regardless of the number of rows inserted.Apply this diff if consistent return shape is desired:
// Fallback: single insert ID - return [startId, data.affectedRows]; + return [{ id: startId }, data.affectedRows];Note: This change would need corresponding updates in callers and tests that expect the bare number for single-row inserts.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/core/test/integration/query-interface/bulk-insert.test.ts(1 hunks)packages/mariadb/src/query.js(1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
packages/**/test/**/*.test.ts
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Name unit tests with the .test.ts suffix
Files:
packages/core/test/integration/query-interface/bulk-insert.test.ts
packages/**/test/**/*.test.{js,ts}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
packages/**/test/**/*.test.{js,ts}: Use Support.getTestDialectTeaser() for dialect-specific test descriptions
Use beforeEach/afterEach to sync with { force: true } and close Sequelize instances in tests
In tests, skip unsupported behavior with dialect.supports.featureName checks
Files:
packages/core/test/integration/query-interface/bulk-insert.test.ts
packages/**/src/**/*.js
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Write all new implementations in TypeScript; avoid creating new .js files in src
Files:
packages/mariadb/src/query.js
🪛 Biome (2.1.2)
packages/core/test/integration/query-interface/bulk-insert.test.ts
[error] 18-27: Disallow duplicate setup and teardown hooks.
Disallow beforeEach duplicacy inside the describe function.
(lint/suspicious/noDuplicateTestHooks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Upload install and build artifact (Node 20)
- GitHub Check: Upload install and build artifact (Node 18)
🔇 Additional comments (1)
packages/core/test/integration/query-interface/bulk-insert.test.ts (1)
40-50: bulkInsert returning behavior is correct across all dialects Verified supports.returnValues and query generators: mysql/mariadb return only IDs; all other dialects return full rows whenreturning: true.
packages/core/test/integration/query-interface/bulk-insert.test.ts
Outdated
Show resolved
Hide resolved
There was a problem hiding this 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 (1)
packages/core/test/integration/query-interface/bulk-insert.test.ts (1)
31-49: Add a test case for single-row insert to verify array return.The multi-row test correctly validates the new behavior, but the PR context indicates that previously, inserting 2 rows returned a scalar
1instead of[1, 2]. To prevent regression, add a test case that inserts a single row and asserts the result is still an array (e.g.,[{ id: 1 }]for MySQL/MariaDB), not a scalar.Add this test case after the existing test:
it('should return an array even when inserting a single row', async function testSingleRowInsert() { const result = await this.queryInterface.bulkInsert('UsersBulkInsert', [{ name: 'SingleUser' }], { returning: true, }); if (['mysql', 'mariadb'].includes(dialect)) { expect(result).to.deep.equal([{ id: 1 }]); } else { expect(result).to.deep.equal([{ id: 1, name: 'SingleUser' }]); } });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/core/test/integration/query-interface/bulk-insert.test.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
packages/**/test/**/*.test.ts
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Name unit tests with the .test.ts suffix
Files:
packages/core/test/integration/query-interface/bulk-insert.test.ts
packages/**/test/**/*.test.{js,ts}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
packages/**/test/**/*.test.{js,ts}: Use Support.getTestDialectTeaser() for dialect-specific test descriptions
Use beforeEach/afterEach to sync with { force: true } and close Sequelize instances in tests
In tests, skip unsupported behavior with dialect.supports.featureName checks
Files:
packages/core/test/integration/query-interface/bulk-insert.test.ts
🧠 Learnings (1)
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
PR: sequelize/sequelize#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/**/test/**/*.test.{js,ts} : Use beforeEach/afterEach to sync with { force: true } and close Sequelize instances in tests
Applied to files:
packages/core/test/integration/query-interface/bulk-insert.test.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Upload install and build artifact (Node 18)
- GitHub Check: Upload install and build artifact (Node 20)
🔇 Additional comments (3)
packages/core/test/integration/query-interface/bulk-insert.test.ts (3)
1-12: LGTM!The imports and setup are correct. Using
Support.getTestDialect()to capture the current dialect for conditional assertions is appropriate.
14-25: LGTM! Duplicate beforeEach issue resolved.The setup hook correctly consolidates both queryInterface initialization and table creation in a single
beforeEach, addressing the duplicate-hook concern from the previous review.
27-29: LGTM!The teardown properly drops the test table after each test, following the coding guidelines for test cleanup.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (1)
packages/core/test/integration/query-interface/bulk-insert.test.ts (1)
5-9: Consider using ES6 imports for consistency.The coding guidelines state "Write all new implementations in TypeScript" for files in
packages/**/src/**/*.js, but this is a test file (.test.ts). Whilerequire()works, ES6 imports would be more consistent with TypeScript best practices.Consider applying this diff:
-const chai = require('chai'); - -const expect = chai.expect; -const Support = require('../support'); -const { DataTypes } = require('@sequelize/core'); +import chai from 'chai'; +import Support from '../support'; +import { DataTypes } from '@sequelize/core'; + +const expect = chai.expect;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
packages/core/test/integration/query-interface/bulk-insert.test.ts(1 hunks)packages/mariadb/src/query.js(1 hunks)packages/mysql/src/query.js(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/mysql/src/query.js
🧰 Additional context used
📓 Path-based instructions (3)
packages/**/src/**/*.js
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Write all new implementations in TypeScript; avoid creating new .js files in src
Files:
packages/mariadb/src/query.js
packages/**/test/**/*.test.ts
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Name unit tests with the .test.ts suffix
Files:
packages/core/test/integration/query-interface/bulk-insert.test.ts
packages/**/test/**/*.test.{js,ts}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
packages/**/test/**/*.test.{js,ts}: Use Support.getTestDialectTeaser() for dialect-specific test descriptions
Use beforeEach/afterEach to sync with { force: true } and close Sequelize instances in tests
In tests, skip unsupported behavior with dialect.supports.featureName checks
Files:
packages/core/test/integration/query-interface/bulk-insert.test.ts
🧠 Learnings (1)
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
PR: sequelize/sequelize#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/**/test/**/*.test.{js,ts} : Use beforeEach/afterEach to sync with { force: true } and close Sequelize instances in tests
Applied to files:
packages/core/test/integration/query-interface/bulk-insert.test.ts
🔇 Additional comments (3)
packages/mariadb/src/query.js (1)
126-132: Logic correctly generates sequential ID array.The implementation correctly generates an array of sequential ID objects for bulk inserts without model context, aligning with the PR objective and MySQL dialect behavior.
packages/core/test/integration/query-interface/bulk-insert.test.ts (2)
31-65: Test coverage looks comprehensive.The tests adequately cover both multi-row and single-row scenarios, with correct dialect-specific expectations that align with the PR objectives (MySQL/MariaDB return ID arrays, other dialects return full rows).
27-29: No manual Sequelize cleanup needed in this test
Integration support’s afterEach resets the database and closes any test-created instances; this test only uses the sharedsequelizeinstance, which should remain open.Likely an incorrect or invalid review comment.
packages/core/test/integration/query-interface/bulk-insert.test.ts
Outdated
Show resolved
Hide resolved
packages/core/test/integration/query-interface/bulk-insert.test.ts
Outdated
Show resolved
Hide resolved
| } | ||
|
|
||
| return [data[this.getInsertIdField()], data.affectedRows]; | ||
| // No model context, in queryinterface,bulkinsert for example |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix the comment formatting.
The comment has a minor formatting issue: "queryinterface,bulkinsert" should be "queryInterface.bulkInsert" (add space after comma and proper camelCase).
Apply this diff:
- // No model context, in queryinterface,bulkinsert for example
+ // No model context, in queryInterface.bulkInsert for example📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // No model context, in queryinterface,bulkinsert for example | |
| // No model context, in queryInterface.bulkInsert for example |
🤖 Prompt for AI Agents
In packages/mariadb/src/query.js around line 125, the inline comment "No model
context, in queryinterface,bulkinsert for example" has formatting issues; update
it to read "No model context, in queryInterface.bulkInsert for example" (fix
camelCase and add period/space after comma) so it uses proper camelCase and
punctuation.
There was a problem hiding this 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 (1)
packages/core/test/unit/dialects/mysql/query.test.js (1)
46-64: LGTM!The test correctly validates the formatResults behavior for bulk inserts with a model context. It properly verifies that an array of ID objects is generated from the insertId through affectedRows.
Consider adding tests for edge cases:
affectedRows: 0(no rows inserted)- String
insertIdvalues (the implementation has special handling:typeof startId === 'string' ? i.toString() : Number(i))- Very large
insertIdvalues where BigInt handling is relevant
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/core/test/integration/query-interface/bulk-insert.test.ts(1 hunks)packages/core/test/unit/dialects/mysql/query.test.js(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/core/test/integration/query-interface/bulk-insert.test.ts
🧰 Additional context used
📓 Path-based instructions (2)
packages/**/test/**/*.test.js
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Name integration tests with the .test.js suffix
Files:
packages/core/test/unit/dialects/mysql/query.test.js
packages/**/test/**/*.test.{js,ts}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
packages/**/test/**/*.test.{js,ts}: Use Support.getTestDialectTeaser() for dialect-specific test descriptions
Use beforeEach/afterEach to sync with { force: true } and close Sequelize instances in tests
In tests, skip unsupported behavior with dialect.supports.featureName checks
Files:
packages/core/test/unit/dialects/mysql/query.test.js
🧬 Code graph analysis (1)
packages/core/test/unit/dialects/mysql/query.test.js (1)
packages/mysql/src/query.js (1)
MySqlQuery(25-322)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Upload install and build artifact (Node 18)
- GitHub Check: Upload install and build artifact (Node 20)
🔇 Additional comments (4)
packages/core/test/unit/dialects/mysql/query.test.js (4)
28-36: LGTM!The improvements to the logWarnings test are correct. Setting
query.sqlensures the query instance is properly initialized, and the updated assertions now verify the actual behavior (that results are returned unchanged and console.debug is called) rather than just checking stub calls.
39-45: LGTM!The test suite setup correctly initializes a query instance with an INSERT SQL statement for each test. This ensures
isInsertQuery()returns true and provides a clean state for testing the formatResults behavior.
66-78: LGTM!This test validates the core fix from the PR: when there's no model context, formatResults now returns an array of ID objects (e.g.,
[{ id: 20 }, { id: 21 }]) instead of a single insertId. This aligns with the expected behavior described in issue #14325.
80-92: LGTM!This test correctly validates that even single-row inserts return an array format (
[{ id: 30 }]) for consistency. This ensures the return type is always an array, avoiding the need for callers to handle special cases.
There was a problem hiding this 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 (1)
packages/mysql/src/query.js (1)
146-169: Implementation looks correct; consider reducing code duplication.The no-model bulk insert logic correctly generates an array of
{ id }objects fromstartIdtostartId + affectedRows - 1, which addresses the core issue in #14325.However, there are opportunities for improvement:
Redundant branches (lines 161-165): Same issue as the model case—both the
isStringandelsebranches perform identical conversions.Code duplication: The type detection and conversion logic is duplicated between the model case (lines 119-144) and this no-model case. Consider extracting a helper function to reduce duplication.
Inconsistent increment style (line 156): Uses
i++instead ofi = i + 1nas in line 128. While both are valid for BigInt, consistency improves readability.Apply this diff to simplify and improve consistency:
- const start = isBigInt ? startId : BigInt(startId); - const end = start + BigInt(data.affectedRows); + const start = isBigInt ? startId : BigInt(startId); + const end = start + BigInt(data.affectedRows); for (let i = start; i < end; i++) { - let value; - if (isBigInt) { - value = i; - } // keep BigInt intact - else if (isString) { - value = Number(i); - } else { - value = Number(i); - } + const value = isBigInt ? i : Number(i); result.push({ id: value }); }Optional: Extract a shared helper function to eliminate duplication between lines 119-144 and 146-169:
/** * Converts a BigInt loop variable to the appropriate type based on the original startId type. * @param {bigint} value - The BigInt value to convert * @param {boolean} isBigInt - Whether the original startId was a BigInt * @returns {bigint|number} The converted value */ function convertInsertId(value, isBigInt) { return isBigInt ? value : Number(value); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/core/test/unit/dialects/mysql/query.test.js(1 hunks)packages/mysql/src/query.js(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/core/test/unit/dialects/mysql/query.test.js
🧰 Additional context used
📓 Path-based instructions (1)
packages/**/src/**/*.js
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Write all new implementations in TypeScript; avoid creating new .js files in src
Files:
packages/mysql/src/query.js
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Upload install and build artifact (Node 18)
- GitHub Check: Upload install and build artifact (Node 20)
🔇 Additional comments (1)
packages/mysql/src/query.js (1)
119-144: Simplify redundant type conversion and verify string insertId handling.Merge the
isStringandelsebranches since both useNumber(i):- let value; - if (isBigInt) { - value = i; - } else if (isString) { - value = Number(i); - } else { - value = Number(i); - } + const value = isBigInt ? i : Number(i);Confirm whether your MySQL client can return
insertIdas a string (and if IDs can exceedNumber.MAX_SAFE_INTEGER); if so, you may need to preserve the string or useBigIntto avoid precision loss.
…that it return the correct inserted ids
…an array even for a single inserted row. Also suggests extending coverage for: - affectedRows = 0 (no insertions) - string insertId handling - BigInt insertId handling for large values
e0a7c70 to
650a1d2
Compare
There was a problem hiding this 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 (1)
packages/mysql/src/query.js (1)
121-140: Consider adding safeguards for edge cases.The
generateInsertResultsfunction is well-structured, but there are a few edge cases to consider:
Large numbers and precision loss: When converting BigInt to Number (line 118), values larger than
Number.MAX_SAFE_INTEGER(9007199254740991) will lose precision. While this is an expected JavaScript limitation, it could lead to incorrect IDs for very large sequences.Negative or invalid
affectedRows: IfaffectedRowsis negative orNaN, the loop will not execute and return an empty array, which may not reflect the actual error state.Performance: For very large
affectedRowsvalues (e.g., 100,000+), the loop could impact performance. Consider whether a warning or optimization is needed for bulk operations.Consider adding input validation at the beginning of the function:
const generateInsertResults = (startId, affectedRows, keyName) => { + if (affectedRows < 0 || !Number.isFinite(affectedRows)) { + throw new Error(`Invalid affectedRows value: ${affectedRows}`); + } + const isBigInt = typeof startId === 'bigint'; const start = isBigInt ? startId : BigInt(startId); const end = start + BigInt(affectedRows);Alternatively, verify if upstream code (e.g., the database driver) guarantees
affectedRowsis always a valid non-negative integer.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
packages/core/test/integration/query-interface/bulk-insert.test.ts(1 hunks)packages/core/test/unit/dialects/mysql/query.test.js(1 hunks)packages/mariadb/src/query.js(1 hunks)packages/mysql/src/query.js(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/mariadb/src/query.js
- packages/core/test/integration/query-interface/bulk-insert.test.ts
🧰 Additional context used
📓 Path-based instructions (3)
packages/**/test/**/*.test.js
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Name integration tests with the .test.js suffix
Files:
packages/core/test/unit/dialects/mysql/query.test.js
packages/**/test/**/*.test.{js,ts}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
packages/**/test/**/*.test.{js,ts}: Use Support.getTestDialectTeaser() for dialect-specific test descriptions
Use beforeEach/afterEach to sync with { force: true } and close Sequelize instances in tests
In tests, skip unsupported behavior with dialect.supports.featureName checks
Files:
packages/core/test/unit/dialects/mysql/query.test.js
packages/**/src/**/*.js
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Write all new implementations in TypeScript; avoid creating new .js files in src
Files:
packages/mysql/src/query.js
🧬 Code graph analysis (1)
packages/core/test/unit/dialects/mysql/query.test.js (1)
packages/mysql/src/query.js (1)
MySqlQuery(25-340)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Upload install and build artifact (Node 20)
- GitHub Check: Upload install and build artifact (Node 18)
🔇 Additional comments (9)
packages/mysql/src/query.js (2)
110-119: LGTM!The helper function correctly handles type conversion based on the original
startIdtype. The implementation is clean and straightforward.
142-156: No issues with Case 2 conditional
Case 1 covers allbulkCreatecases whereautoIncrementAttributeNamematches the model’s PK; Case 2 only runs for rawbulkInsert(no model context). No unintended scenarios detected.packages/core/test/unit/dialects/mysql/query.test.js (7)
28-36: LGTM!The test correctly initializes
query.sqlbefore execution and updates assertions to verify both the return value and the console.debug invocation.
46-64: LGTM!The test correctly validates bulkInsert with model context, ensuring the array contains consecutive IDs starting from
insertId(10, 11, 12).
66-78: LGTM!The test correctly validates bulkInsert without model context, ensuring the generic 'id' key is used in the result array.
80-92: LGTM!The test correctly validates single-row insertion behavior, ensuring the result is still an array with one element.
94-105: LGTM!The test correctly handles the edge case of zero affected rows, returning an empty array as expected.
107-118: LGTM!The test correctly validates that string
insertIdvalues are converted to numbers in the result array (50, 51, 52).
120-134: LGTM!The test correctly validates BigInt handling by ensuring the result preserves BigInt type for IDs larger than
Number.MAX_SAFE_INTEGER. The use of BigInt literals with underscores (e.g.,9_007_199_254_740_991n) is valid and improves readability.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (1)
packages/mysql/src/query.js (1)
110-160: Consider TypeScript migration for new implementations.The coding guidelines state: "Write all new implementations in TypeScript; avoid creating new .js files in src." While this PR modifies an existing
.jsfile rather than creating a new one, the new helper functions (convertInsertIdandgenerateInsertResults) represent new implementations. Consider migrating this file to TypeScript in a future PR to align with the guidelines and benefit from type safety.As per coding guidelines.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/mysql/src/query.js(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
packages/**/src/**/*.js
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Write all new implementations in TypeScript; avoid creating new .js files in src
Files:
packages/mysql/src/query.js
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Upload install and build artifact (Node 20)
- GitHub Check: Upload install and build artifact (Node 18)
🔇 Additional comments (4)
packages/mysql/src/query.js (4)
110-119: LGTM! Clean type conversion helper.The
convertInsertIdfunction correctly preserves the original type of the insert ID (BigInt vs Number) during loop iteration.
134-143: Correct BigInt arithmetic and type handling.The function properly normalizes IDs to BigInt for loop arithmetic, then restores the original type via
convertInsertId. For very largeaffectedRows(e.g., > 100k), consider whether sequential ID generation in JavaScript remains performant, though this is unlikely to be a practical concern in most scenarios.
146-154: Verify thatstartIdis always defined for auto-increment inserts.Case 1 retrieves
startIdfromdata[this.getInsertIdField()]and passes it togenerateInsertResultswithout validation. If the database doesn't return an insert ID (e.g., on certain error conditions or table configurations),startIdwill beundefined, causing a runtime error whengenerateInsertResultsattemptsBigInt(undefined).Confirm that:
- MySQL/MariaDB always return a valid
insertIdwhenautoIncrementAttributeNameis set.- Or add a guard clause before calling
generateInsertResults:const startId = data[this.getInsertIdField()]; if (startId == null) { // Handle missing startId case - perhaps return empty array or original result result = []; } else { const keyName = modelDefinition.getColumnName(this.model.primaryKeyAttribute); result = generateInsertResults(startId, data.affectedRows, keyName); }
156-160: Verify thatstartIdis always defined for bulkInsert without model.Case 2 retrieves
startIdwithout validation. If the insert operation doesn't return aninsertId(e.g., inserting into a table without auto-increment), this will cause the same runtime error mentioned in the previous comment.Consider the same validation approach as suggested for Case 1:
const startId = data[this.getInsertIdField()]; if (startId == null) { result = []; } else { result = generateInsertResults(startId, data.affectedRows, 'id'); }
| const generateInsertResults = (startId, affectedRows, keyName) => { | ||
| if (affectedRows < 0 || !Number.isFinite(affectedRows)) { | ||
| throw new Error(`Invalid affectedRows value: ${affectedRows}`); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add validation for startId to prevent runtime errors.
The function validates affectedRows but doesn't check if startId is valid before converting it to BigInt. If startId is undefined, null, or an invalid value (e.g., when no rows are inserted or the table lacks auto-increment), BigInt(startId) on line 135 will throw a TypeError.
Apply this diff to add startId validation:
const generateInsertResults = (startId, affectedRows, keyName) => {
+ if (startId == null) {
+ throw new Error(`Invalid startId value: ${startId}`);
+ }
if (affectedRows < 0 || !Number.isFinite(affectedRows)) {
throw new Error(`Invalid affectedRows value: ${affectedRows}`);
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const generateInsertResults = (startId, affectedRows, keyName) => { | |
| if (affectedRows < 0 || !Number.isFinite(affectedRows)) { | |
| throw new Error(`Invalid affectedRows value: ${affectedRows}`); | |
| } | |
| const generateInsertResults = (startId, affectedRows, keyName) => { | |
| if (startId == null) { | |
| throw new Error(`Invalid startId value: ${startId}`); | |
| } | |
| if (affectedRows < 0 || !Number.isFinite(affectedRows)) { | |
| throw new Error(`Invalid affectedRows value: ${affectedRows}`); | |
| } |
🤖 Prompt for AI Agents
In packages/mysql/src/query.js around lines 129 to 132, the
generateInsertResults helper validates affectedRows but does not validate
startId before calling BigInt(startId), which can throw if startId is undefined,
null, or not a numeric string; add a validation that startId is neither null nor
undefined and is a safe integer or a numeric string (e.g., typeof startId ===
'number' && Number.isFinite(startId) or a string matching /^\d+$/) before
converting to BigInt, and if invalid throw a clear Error like "Invalid startId
value: <value>"; then proceed to convert to BigInt and compute results as
before.
Pull Request Checklist
Description of Changes
This PR ensures
queryInterface.bulkInsertconsistently returns an array of inserted IDs across mysql dialect, instead of a single value or undefined.bulkInsertimplementation to return an array.Previously:
now:
Closes #14325
List of Breaking Changes
Summary by CodeRabbit
New Features
Tests