Skip to content

Conversation

@tusharrana786
Copy link

@tusharrana786 tusharrana786 commented Nov 2, 2025

Pull Request Checklist

  • Have you added new tests to prevent regressions?
  • If a documentation update is necessary, have you opened a PR to the documentation repository?
  • Did you update the typescript typings accordingly (if applicable)?
  • Does the description below contain a link to an existing issue (Closes #[issue]) or a description of the issue you are solving?
  • Does the name of your PR follow our conventions?

Description of Changes

Summary

Fixes a long-standing issue where Sequelize creates duplicate UNIQUE constraints on repeated sequelize.sync({ alter: true }) calls.

Closes #17978

Root Cause

attributeToSQL() emitted the inline UNIQUE keyword during changeColumn, without checking for existing uniqueKeys.

Fix

  • Propagated model.uniqueKeys into queryInterface.changeColumn and queryGenerator.attributeToSQL.
  • Skipped emitting inline UNIQUE if the field already has a unique constraint.

Test

Added test/integration/query-interface/unique-constraint.test.js (Postgres) verifying that repeated syncs do not create duplicate unique constraints.

List of Breaking Changes

None

Summary by CodeRabbit

  • Bug Fixes

    • Preserve existing UNIQUE indexes when altering columns during schema sync so UNIQUE constraints are not recreated or duplicated; improved detection prevents adding redundant UNIQUE declarations.
  • Tests

    • Added integration tests confirming that after force and alter sync operations only a single UNIQUE constraint exists for the affected field across supported dialects.

@tusharrana786 tusharrana786 requested a review from a team as a code owner November 2, 2025 21:50
@coderabbitai
Copy link

coderabbitai bot commented Nov 2, 2025

Walkthrough

Propagates existing unique-index metadata through sync and SQL generation so inline UNIQUE clauses are suppressed when an equivalent unique index already exists, preventing duplicate UNIQUE constraints during alter sync operations.

Changes

Cohort / File(s) Summary
Core query interface
packages/core/src/abstract-dialect/query-interface.js
changeColumn now forwards options?.uniqueKeys in the context passed to attributesToSQL, carrying existing unique-index metadata.
Postgres query generator
packages/postgres/src/query-generator.js
attributeToSQL computes an inline field key, normalizes field descriptors, inspects options.uniqueKeys for matching unique indexes, and only appends UNIQUE inline when no matching unique key exists.
Model sync logic
packages/core/src/model.js
During alter sync, collects modelDefinition.getIndexes(), filters unique indexes into a uniqueKeys mapping, and merges it into the options passed to queryInterface.changeColumn.
Tests
packages/core/test/integration/model/sync.test.js
Adds integration test(s) that run sync({ force: true }) then repeated sync({ alter: true }) and assert that only a single UNIQUE constraint is present (with allowances for sqlite variants).

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Sync as model.sync({ alter: true })
  participant Model as Model (core/src/model.js)
  participant QI as QueryInterface.changeColumn
  participant PG as Postgres QueryGenerator
  participant DB as Database

  Sync->>Model: run alter flow for changed attributes
  Model->>Model: modelDefinition.getIndexes()\nfilter unique indexes → build uniqueKeys
  Model->>QI: changeColumn(..., options + uniqueKeys)
  QI->>PG: attributesToSQL(context including uniqueKeys)
  PG->>PG: normalize field descriptor\ncheck options.uniqueKeys for matching unique index
  alt matching unique index exists
    PG-->>QI: emit column SQL WITHOUT inline UNIQUE
  else no matching unique index
    PG-->>QI: emit column SQL WITH inline UNIQUE
  end
  QI->>DB: execute ALTER TABLE / CREATE INDEX as needed
  DB-->>Sync: complete
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Inspect uniqueKeys construction and normalization in packages/core/src/model.js.
  • Verify matching logic for single-field vs composite unique indexes in packages/postgres/src/query-generator.js.
  • Review propagation in packages/core/src/abstract-dialect/query-interface.js.
  • Validate integration test behavior and DB-specific assertions in packages/core/test/integration/model/sync.test.js.

Suggested reviewers

  • sdepold
  • ephys

Poem

🐇 I hopped through schemas, counted every key,
I nudged the uniques, let duplicates flee.
One gentle constraint on each tidy field,
Alter now listens — no more keys to yield. ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the fix: preventing duplicate unique constraints during sync with alter mode on PostgreSQL, which matches the PR's main objective.
Linked Issues check ✅ Passed The PR addresses all coding requirements from issue #17978: propagating uniqueKeys to changeColumn, detecting existing unique constraints, and skipping inline UNIQUE when constraints already exist.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing duplicate unique constraint creation: propagating uniqueKeys through the column change pipeline and preventing redundant UNIQUE keyword emission.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 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 6a7f90a and c3895f3.

📒 Files selected for processing (1)
  • packages/core/test/integration/model/sync.test.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/core/test/integration/model/sync.test.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)

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

Comment @coderabbitai help to get the list of available commands and usage tips.

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: 2

🧹 Nitpick comments (3)
packages/core/test/integration/query-interface/changeColumn.test.js (3)

273-302: Follow coding guidelines for test structure.

The test correctly validates the fix for duplicate unique constraints. However, per coding guidelines, tests should use beforeEach/afterEach to sync with { force: true } and close Sequelize instances.

Consider restructuring to follow the established pattern:

 if (dialect === 'postgres' || dialect === 'postgres-native') {
   describe('unique constraint duplication fix', () => {
+    beforeEach(async function () {
+      this.User = this.sequelize.define('UserUniqueTest', {
+        email: {
+          type: DataTypes.STRING,
+          unique: true,
+        },
+      });
+      await this.sequelize.sync({ force: true });
+    });
+
     it('should not create duplicate unique constraints on repeated alter syncs', async function () {
-      const User = this.sequelize.define('UserUniqueTest', {
-        email: {
-          type: DataTypes.STRING,
-          unique: true,
-        },
-      });
-
-      await this.sequelize.sync({ force: true });
-
       await this.sequelize.sync({ alter: true });
       await this.sequelize.sync({ alter: true });

Based on coding guidelines.


296-296: Consider more robust constraint verification.

Filtering constraints by constraint_name.includes('email') may be fragile if Postgres changes its naming convention or if other constraints include "email" in their names.

Consider querying for constraints on the specific column:

-      const [results] = await this.sequelize.query(`
-        SELECT constraint_name
-        FROM information_schema.table_constraints
-        WHERE table_name = 'UserUniqueTests'
-          AND constraint_type = 'UNIQUE';
-      `);
-
-      // Filter only email-related constraints
-      const uniqueConstraints = results.filter(r => r.constraint_name.includes('email'));
+      const [results] = await this.sequelize.query(`
+        SELECT tc.constraint_name
+        FROM information_schema.table_constraints tc
+        JOIN information_schema.constraint_column_usage ccu
+          ON tc.constraint_name = ccu.constraint_name
+        WHERE tc.table_name = 'UserUniqueTests'
+          AND tc.constraint_type = 'UNIQUE'
+          AND ccu.column_name = 'email';
+      `);

This joins with constraint_column_usage to precisely identify constraints on the email column.


587-587: Remove unrelated formatting change.

This blank line addition appears unrelated to the unique constraint fix and should be removed to keep the PR focused.

-      
     }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 882b061 and 02a37ae.

📒 Files selected for processing (3)
  • packages/core/src/abstract-dialect/query-interface.js (2 hunks)
  • packages/core/test/integration/query-interface/changeColumn.test.js (2 hunks)
  • packages/postgres/src/query-generator.js (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
packages/**/test/**/*.test.js

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Name integration tests with the .test.js suffix

Files:

  • packages/core/test/integration/query-interface/changeColumn.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/integration/query-interface/changeColumn.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/core/src/abstract-dialect/query-interface.js
  • packages/postgres/src/query-generator.js
packages/*/src/query-generator.{js,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Implement dialect feature SQL in query-generator.ts (preferred) or legacy query-generator.js

Files:

  • packages/postgres/src/query-generator.js
packages/**/src/**/query-generator.{js,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

When modifying query generation, update both the base query generator and all dialect-specific implementations

Files:

  • packages/postgres/src/query-generator.js
🧠 Learnings (6)
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/*/src/query-interface.{js,ts} : Add schema operations for new features in query-interface.ts (preferred) or legacy query-interface.js

Applied to files:

  • packages/core/test/integration/query-interface/changeColumn.test.js
  • packages/core/src/abstract-dialect/query-interface.js
  • packages/postgres/src/query-generator.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 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/changeColumn.test.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/**/src/**/query-generator.{js,ts} : When modifying query generation, update both the base query generator and all dialect-specific implementations

Applied to files:

  • packages/core/test/integration/query-interface/changeColumn.test.js
  • packages/core/src/abstract-dialect/query-interface.js
  • packages/postgres/src/query-generator.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/**/test/**/*.test.{js,ts} : In tests, skip unsupported behavior with dialect.supports.featureName checks

Applied to files:

  • packages/core/test/integration/query-interface/changeColumn.test.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/**/test/**/*.test.{js,ts} : Use Support.getTestDialectTeaser() for dialect-specific test descriptions

Applied to files:

  • packages/core/test/integration/query-interface/changeColumn.test.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/*/src/query-generator.{js,ts} : Implement dialect feature SQL in query-generator.ts (preferred) or legacy query-generator.js

Applied to files:

  • packages/core/test/integration/query-interface/changeColumn.test.js
  • packages/core/src/abstract-dialect/query-interface.js
  • packages/postgres/src/query-generator.js
🧬 Code graph analysis (2)
packages/core/test/integration/query-interface/changeColumn.test.js (1)
packages/core/test/integration/query-interface/createTable.test.js (2)
  • dialect (9-9)
  • expect (5-5)
packages/core/src/abstract-dialect/query-interface.js (1)
packages/core/src/model-typescript.ts (1)
  • tableName (164-168)
🪛 ESLint
packages/core/test/integration/query-interface/changeColumn.test.js

[error] 276-276: 'User' is assigned a value but never used. Allowed unused vars must match /^ignore/u.

(no-unused-vars)

⏰ 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 (2)
packages/core/src/abstract-dialect/query-interface.js (1)

220-220: LGTM: Propagates uniqueKeys to query generator.

The change correctly passes model?.uniqueKeys to the query generator options, enabling the generator to avoid duplicate UNIQUE constraints. The optional chaining ensures graceful handling when no model is found.

packages/core/test/integration/query-interface/changeColumn.test.js (1)

276-276: ESLint unused variable warning is a false positive.

The User variable is used implicitly by the sequelize.sync() operations, which process all registered models including this one. The test verifies the table 'UserUniqueTests' created by this model definition.

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: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 02a37ae and 4b76cee.

📒 Files selected for processing (3)
  • packages/core/src/abstract-dialect/query-interface.js (2 hunks)
  • packages/core/test/integration/query-interface/changeColumn.test.js (1 hunks)
  • packages/postgres/src/query-generator.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/core/src/abstract-dialect/query-interface.js
🧰 Additional context used
📓 Path-based instructions (5)
packages/*/src/query-generator.{js,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Implement dialect feature SQL in query-generator.ts (preferred) or legacy query-generator.js

Files:

  • packages/postgres/src/query-generator.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/postgres/src/query-generator.js
packages/**/src/**/query-generator.{js,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

When modifying query generation, update both the base query generator and all dialect-specific implementations

Files:

  • packages/postgres/src/query-generator.js
packages/**/test/**/*.test.js

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Name integration tests with the .test.js suffix

Files:

  • packages/core/test/integration/query-interface/changeColumn.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/integration/query-interface/changeColumn.test.js
🧠 Learnings (6)
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/**/src/**/query-generator.{js,ts} : When modifying query generation, update both the base query generator and all dialect-specific implementations

Applied to files:

  • packages/postgres/src/query-generator.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/*/src/query-generator.{js,ts} : Implement dialect feature SQL in query-generator.ts (preferred) or legacy query-generator.js

Applied to files:

  • packages/postgres/src/query-generator.js
  • packages/core/test/integration/query-interface/changeColumn.test.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/*/src/query-interface.{js,ts} : Add schema operations for new features in query-interface.ts (preferred) or legacy query-interface.js

Applied to files:

  • packages/postgres/src/query-generator.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 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/changeColumn.test.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/**/test/**/*.test.{js,ts} : In tests, skip unsupported behavior with dialect.supports.featureName checks

Applied to files:

  • packages/core/test/integration/query-interface/changeColumn.test.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/**/test/**/*.test.{js,ts} : Use Support.getTestDialectTeaser() for dialect-specific test descriptions

Applied to files:

  • packages/core/test/integration/query-interface/changeColumn.test.js
🧬 Code graph analysis (1)
packages/core/test/integration/query-interface/changeColumn.test.js (2)
packages/core/test/integration/query-interface/createTable.test.js (2)
  • dialect (9-9)
  • expect (5-5)
packages/core/src/index.mjs (2)
  • DataTypes (91-91)
  • DataTypes (91-91)
⏰ 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/changeColumn.test.js (1)

273-300: Great regression coverage on duplicate unique constraints

Verifies the repeated sync({ alter: true }) flow end-to-end and asserts only one email constraint remains, which squarely guards the original bug.

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: 1

♻️ Duplicate comments (1)
packages/postgres/src/query-generator.js (1)

264-288: Incomplete implementation: Apply to all dialect implementations.

This deduplication logic correctly prevents duplicate UNIQUE constraints in PostgreSQL. However, per the coding guidelines and the previous review comment, query generation changes must be applied to all dialect-specific implementations, not just PostgreSQL.

The following dialect query generators still need this same logic:

  • packages/mysql/src/query-generator.js (around line 208)
  • packages/mssql/src/query-generator.js (around line 512)
  • packages/mariadb/src/query-generator.js (around line 193)
  • packages/ibmi/src/query-generator.js (around line 403)
  • packages/db2/src/query-generator.js (around line 592)
  • packages/snowflake/src/query-generator.js (around line 255)

Without this fix in all dialects, users on MySQL, SQL Server, MariaDB, etc. will still encounter duplicate UNIQUE constraints on repeated sync({ alter: true }) calls.

Based on coding guidelines and past review comments.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4b76cee and e581bc2.

📒 Files selected for processing (1)
  • packages/postgres/src/query-generator.js (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
packages/*/src/query-generator.{js,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Implement dialect feature SQL in query-generator.ts (preferred) or legacy query-generator.js

Files:

  • packages/postgres/src/query-generator.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/postgres/src/query-generator.js
packages/**/src/**/query-generator.{js,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

When modifying query generation, update both the base query generator and all dialect-specific implementations

Files:

  • packages/postgres/src/query-generator.js
🧠 Learnings (3)
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/**/src/**/query-generator.{js,ts} : When modifying query generation, update both the base query generator and all dialect-specific implementations

Applied to files:

  • packages/postgres/src/query-generator.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/*/src/query-generator.{js,ts} : Implement dialect feature SQL in query-generator.ts (preferred) or legacy query-generator.js

Applied to files:

  • packages/postgres/src/query-generator.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/*/src/query-interface.{js,ts} : Add schema operations for new features in query-interface.ts (preferred) or legacy query-interface.js

Applied to files:

  • packages/postgres/src/query-generator.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). (1)
  • GitHub Check: Lint code

@tusharrana786
Copy link
Author

Hi, it looks like one of the CI runs (MySQL dialect) failed with a connection refused error:

SequelizeConnectionRefusedError: connect ECONNREFUSED 127.0.0.1:20057

I’ve reviewed my changes and confirmed they don’t touch any connection or dialect setup code.
This seems to be a transient CI environment issue rather than something caused by the PR itself.

Could you please re-run the failed job when convenient?
Thanks!

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: 1

🧹 Nitpick comments (3)
packages/postgres/src/query-generator.js (1)

264-278: Fix style violations flagged by static analysis.

The logic correctly prevents duplicate UNIQUE constraints, but static analysis identifies two style issues:

  1. Line 267: Arrow function missing curly braces (single-line returns should use concise syntax or explicit braces)
  2. Line 271: Missing blank line before return statement

Regarding WikiRik's question about checking multiple properties (lines 269): Field descriptors in uniqueKey.fields can be objects with different property names depending on how the index was defined (field, column, attribute, or name). The multiple checks ensure all variants are handled.

Apply this diff to address the style issues:

 const normalizeFieldName = field => {
-  if (typeof field === 'string') return field;
+  if (typeof field === 'string') {
+    return field;
+  }
+
   if (field && typeof field === 'object') {
     return field.field ?? field.column ?? field.attribute ?? field.name;
   }
+
   return null;
 };

Based on static analysis hints.

packages/core/test/integration/model/sync.test.js (1)

705-719: Add blank line before test for consistency.

The test correctly verifies that repeated sync({ alter: true }) doesn't create duplicate UNIQUE constraints and properly uses queryInterface.showConstraints as requested in past reviews. However, static analysis flags a missing blank line before the test declaration.

Apply this diff to address the style issue:

+
   it('should not recreate a UNIQUE constraint if it already exists when { alter: true } is used', async () => {

Based on static analysis hints.

packages/core/test/integration/query-interface/changeColumn.test.js (1)

273-278: Remove extra blank line.

The SQLite-specific tests for constraint preservation during column changes are appropriately placed in this changeColumn test file. Static analysis flags multiple consecutive blank lines (line 273).

Apply this diff:

-

-    // sqlite has limited ALTER TABLE capapibilites which requires a workaround involving recreating tables.
+    // sqlite has limited ALTER TABLE capapibilites which requires a workaround involving recreating tables.

Based on static analysis hints.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 727d110 and c5b8289.

⛔ Files ignored due to path filters (2)
  • .yarn/releases/yarn-4.11.0.cjs is excluded by !**/.yarn/**
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (16)
  • .github/workflows/ci.yml (10 hunks)
  • .yarnrc.yml (1 hunks)
  • dev/db2/latest/docker-compose.yml (1 hunks)
  • package.json (2 hunks)
  • packages/cli/package.json (2 hunks)
  • packages/core/package.json (1 hunks)
  • packages/core/src/abstract-dialect/query-interface.js (4 hunks)
  • packages/core/test/integration/model/sync.test.js (1 hunks)
  • packages/core/test/integration/query-interface/changeColumn.test.js (1 hunks)
  • packages/mariadb/package.json (1 hunks)
  • packages/mssql/package.json (1 hunks)
  • packages/mysql/package.json (1 hunks)
  • packages/postgres/package.json (1 hunks)
  • packages/postgres/src/query-generator.js (1 hunks)
  • packages/utils/package.json (1 hunks)
  • packages/validator-js/package.json (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/mysql/package.json
🧰 Additional context used
📓 Path-based instructions (5)
packages/**/test/**/*.test.js

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Name integration tests with the .test.js suffix

Files:

  • packages/core/test/integration/model/sync.test.js
  • packages/core/test/integration/query-interface/changeColumn.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/integration/model/sync.test.js
  • packages/core/test/integration/query-interface/changeColumn.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/core/src/abstract-dialect/query-interface.js
  • packages/postgres/src/query-generator.js
packages/*/src/query-generator.{js,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Implement dialect feature SQL in query-generator.ts (preferred) or legacy query-generator.js

Files:

  • packages/postgres/src/query-generator.js
packages/**/src/**/query-generator.{js,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

When modifying query generation, update both the base query generator and all dialect-specific implementations

Files:

  • packages/postgres/src/query-generator.js
🧠 Learnings (8)
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 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/model/sync.test.js
  • packages/core/test/integration/query-interface/changeColumn.test.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/core/test/chai-extensions.d.ts : Use Chai assertions with the project’s custom extensions available in packages/core/test/chai-extensions.d.ts

Applied to files:

  • packages/utils/package.json
  • packages/mssql/package.json
  • packages/mariadb/package.json
  • packages/postgres/package.json
  • packages/validator-js/package.json
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/*/src/query-interface.{js,ts} : Add schema operations for new features in query-interface.ts (preferred) or legacy query-interface.js

Applied to files:

  • packages/core/src/abstract-dialect/query-interface.js
  • packages/core/test/integration/query-interface/changeColumn.test.js
  • packages/postgres/src/query-generator.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/*/src/query-generator.{js,ts} : Implement dialect feature SQL in query-generator.ts (preferred) or legacy query-generator.js

Applied to files:

  • packages/core/src/abstract-dialect/query-interface.js
  • packages/core/test/integration/query-interface/changeColumn.test.js
  • packages/postgres/src/query-generator.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/**/src/**/query-generator.{js,ts} : When modifying query generation, update both the base query generator and all dialect-specific implementations

Applied to files:

  • packages/core/src/abstract-dialect/query-interface.js
  • packages/core/test/integration/query-interface/changeColumn.test.js
  • packages/postgres/src/query-generator.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/**/test/**/*.test.{js,ts} : In tests, skip unsupported behavior with dialect.supports.featureName checks

Applied to files:

  • packages/core/test/integration/query-interface/changeColumn.test.js
📚 Learning: 2025-11-08T03:05:56.939Z
Learnt from: SippieCup
Repo: sequelize/sequelize PR: 18051
File: packages/core/src/utils/undot.ts:177-177
Timestamp: 2025-11-08T03:05:56.939Z
Learning: In packages/core/src/utils/undot.ts, the defensive check "if (!Array.isArray(obj))" before numeric leaf assignment in setByPathArray is intentional to handle edge cases where table aliases or oddly-named columns produce single-segment numeric keys (e.g., "0").

Applied to files:

  • packages/postgres/src/query-generator.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/*/src/dialect.ts : When adding a dialect feature, update packages/{dialect}/src/dialect.ts to declare feature support flags

Applied to files:

  • packages/postgres/src/query-generator.js
🧬 Code graph analysis (2)
packages/core/src/abstract-dialect/query-interface.js (3)
packages/core/src/model-typescript.ts (1)
  • uniqueKeys (352-384)
packages/core/src/utils/model-utils.ts (1)
  • extractModelDefinition (35-45)
packages/core/src/model-definition.ts (2)
  • attributeName (414-447)
  • index (706-757)
packages/postgres/src/query-generator.js (1)
packages/core/src/expression-builders/attribute.ts (1)
  • attribute (58-62)
🪛 Biome (2.1.2)
packages/core/src/abstract-dialect/query-interface.js

[error] 15-16: Illegal use of an import declaration outside of a module

not allowed inside scripts

(parse)

🪛 ESLint
packages/core/test/integration/model/sync.test.js

[error] 705-719: Expected blank line before this statement.

(padding-line-between-statements)

packages/core/src/abstract-dialect/query-interface.js

[error] 77-77: Unexpected console statement.

(no-console)


[error] 211-211: 'model' is not defined.

(no-undef)


[error] 213-213: Unexpected console statement.

(no-console)

packages/core/test/integration/query-interface/changeColumn.test.js

[error] 273-274: More than 1 blank line not allowed.

(no-multiple-empty-lines)

packages/postgres/src/query-generator.js

[error] 267-267: Expected { after 'if' condition.

(curly)


[error] 271-271: Expected blank line before this statement.

(padding-line-between-statements)

⏰ 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 (13)
.yarnrc.yml (1)

3-3: Yarn version alignment is correct.

The yarnPath update to 4.11.0 aligns with the root package.json packageManager specification. This ensures the local toolchain uses the declared version consistently.

package.json (1)

90-90: Dependency updates are consistent and safe.

Both the mocha patch version (11.7.4 → 11.7.5) and yarn minor version (4.10.3 → 4.11.0) updates are reflected consistently across the repository. These patch/minor version bumps are typically backward-compatible.

Also applies to: 103-103

packages/mariadb/package.json (1)

52-52: Mocha version update is consistent with repository-wide changes.

packages/utils/package.json (1)

65-65: Mocha version update is consistent with repository-wide changes.

packages/postgres/package.json (1)

55-55: Mocha version update is consistent with repository-wide changes.

Note: The PR objectives indicate this package includes query-generator.js changes to gate UNIQUE constraints. While the package.json update here is safe, the functional changes in query-generator.js would require separate review to verify the UNIQUE constraint deduplication logic.

packages/core/package.json (1)

86-86: Mocha version update is consistent with repository-wide changes.

However, the AI summary references runtime console.log statements added to query-interface.js (e.g., console.log('=== CREATE TABLE TRIGGERED ===', tableName)). These appear to be debug logging left in the code. Please verify whether these should be removed, made conditional, or replaced with proper observability instrumentation before merging.

packages/mssql/package.json (1)

51-51: Mocha version update is consistent with repository-wide changes.

dev/db2/latest/docker-compose.yml (1)

4-4: DB2 image version update is a safe patch-level bump.

The update from DB2 12.1.2.0 to 12.1.3.0 is a patch version bump with no configuration changes. Ensure the image is available in the registry before deployment.

.github/workflows/ci.yml (1)

41-41: LGTM! Routine GitHub Actions version updates.

The artifact action version bumps (upload-artifact v4→v5.0.0, download-artifact v5.0.0→v6.0.0) are standard maintenance updates that improve CI infrastructure reliability.

Also applies to: 56-56, 76-76, 149-149, 169-169, 194-194, 221-221, 250-250, 274-274, 336-336

packages/validator-js/package.json (1)

57-57: LGTM! Routine mocha version bump.

This devDependency update is part of a PR-wide upgrade to mocha 11.7.5 across multiple packages, consistent with general maintenance.

packages/core/src/abstract-dialect/query-interface.js (1)

211-224: How to obtain the model reference in changeColumn?

Since model is undefined, consider one of these approaches:

Option 1: Add model parameter

-  async changeColumn(tableName, attributeName, dataTypeOrOptions, options) {
+  async changeColumn(tableName, attributeName, dataTypeOrOptions, options, model) {

Option 2: Look up model from sequelize.models

const model = Object.values(this.sequelize.models).find(
  m => {
    const table = m.table;
    const targetTable = typeof tableName === 'string' ? tableName : tableName.tableName;
    const targetSchema = typeof tableName === 'object' ? tableName.schema : null;
    return table.tableName === targetTable && 
           (targetSchema ? table.schema === targetSchema : true);
  }
);

Option 3: Pass model via options

const model = options?.model;

Which approach aligns with the existing Sequelize architecture?

packages/core/test/integration/query-interface/changeColumn.test.js (1)

279-556: LGTM! Comprehensive SQLite constraint preservation tests.

These tests thoroughly verify that SQLite's table recreation workaround (required for ALTER TABLE operations) correctly preserves indexes, unique constraints, and foreign keys. The test coverage includes edge cases like null handling and ON DELETE/ON UPDATE actions.

packages/cli/package.json (1)

7-16: All new dependencies verified as actively used in CLI codebase.

Verification confirms:

  • ansis: Used in packages/cli/src/commands/seed/generate.ts and packages/cli/src/commands/migration/generate.ts for terminal styling
  • cosmiconfig: Used in packages/cli/src/_internal/config.ts for configuration file loading
  • zod: Used in packages/cli/src/_internal/config.ts (imported as z from 'zod/v3') for schema validation

The dependency updates are routine maintenance with properly integrated new tooling for CLI enhancements.

@tusharrana786 tusharrana786 marked this pull request as draft November 12, 2025 10:21
…olumn to model.sync for consistent availability during alters
@tusharrana786 tusharrana786 marked this pull request as ready for review November 12, 2025 19:32
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: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c5b8289 and 7ba91ea.

📒 Files selected for processing (3)
  • packages/core/src/abstract-dialect/query-interface.js (2 hunks)
  • packages/core/src/model.js (1 hunks)
  • packages/core/test/integration/model/sync.test.js (1 hunks)
🧰 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/core/src/abstract-dialect/query-interface.js
  • packages/core/src/model.js
packages/**/test/**/*.test.js

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Name integration tests with the .test.js suffix

Files:

  • packages/core/test/integration/model/sync.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/integration/model/sync.test.js
🧠 Learnings (4)
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/*/src/query-interface.{js,ts} : Add schema operations for new features in query-interface.ts (preferred) or legacy query-interface.js

Applied to files:

  • packages/core/src/abstract-dialect/query-interface.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/*/src/query-generator.{js,ts} : Implement dialect feature SQL in query-generator.ts (preferred) or legacy query-generator.js

Applied to files:

  • packages/core/src/abstract-dialect/query-interface.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/**/src/**/query-generator.{js,ts} : When modifying query generation, update both the base query generator and all dialect-specific implementations

Applied to files:

  • packages/core/src/abstract-dialect/query-interface.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 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/model/sync.test.js
🧬 Code graph analysis (2)
packages/core/src/model.js (1)
packages/core/src/model-typescript.ts (1)
  • uniqueKeys (352-384)
packages/core/test/integration/model/sync.test.js (1)
packages/core/src/index.mjs (2)
  • DataTypes (91-91)
  • DataTypes (91-91)
🪛 ESLint
packages/core/src/abstract-dialect/query-interface.js

[error] 76-76: Unexpected console statement.

(no-console)

packages/core/test/integration/model/sync.test.js

[error] 705-723: Expected blank line before this statement.

(padding-line-between-statements)

⏰ 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)

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 (1)
packages/postgres/src/query-generator.js (1)

278-282: Add defensive check for uniqueKey.fields.

While the framework likely guarantees that fields is always an array, adding optional chaining would make this code more robust against unexpected data structures or future refactoring.

Apply this diff to add defensive handling:

     const hasExistingUniqueKey =
       options?.uniqueKeys &&
       Object.values(options.uniqueKeys).some(uniqueKey =>
-        uniqueKey.fields.some(field => normalizeFieldName(field) === inlineUniqueField),
+        uniqueKey.fields?.some(field => normalizeFieldName(field) === inlineUniqueField),
       );
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bdb529e and 8f3e190.

📒 Files selected for processing (1)
  • packages/postgres/src/query-generator.js (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
packages/*/src/query-generator.{js,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Implement dialect feature SQL in query-generator.ts (preferred) or legacy query-generator.js

Files:

  • packages/postgres/src/query-generator.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/postgres/src/query-generator.js
packages/**/src/**/query-generator.{js,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

When modifying query generation, update both the base query generator and all dialect-specific implementations

Files:

  • packages/postgres/src/query-generator.js
🧠 Learnings (6)
📓 Common learnings
Learnt from: CR
Repo: sequelize/sequelize PR: 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
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/**/src/**/query-generator.{js,ts} : When modifying query generation, update both the base query generator and all dialect-specific implementations

Applied to files:

  • packages/postgres/src/query-generator.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/*/src/query-generator.{js,ts} : Implement dialect feature SQL in query-generator.ts (preferred) or legacy query-generator.js

Applied to files:

  • packages/postgres/src/query-generator.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/*/src/query-interface.{js,ts} : Add schema operations for new features in query-interface.ts (preferred) or legacy query-interface.js

Applied to files:

  • packages/postgres/src/query-generator.js
📚 Learning: 2025-11-08T03:05:56.939Z
Learnt from: SippieCup
Repo: sequelize/sequelize PR: 18051
File: packages/core/src/utils/undot.ts:177-177
Timestamp: 2025-11-08T03:05:56.939Z
Learning: In packages/core/src/utils/undot.ts, the defensive check "if (!Array.isArray(obj))" before numeric leaf assignment in setByPathArray is intentional to handle edge cases where table aliases or oddly-named columns produce single-segment numeric keys (e.g., "0").

Applied to files:

  • packages/postgres/src/query-generator.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/*/src/dialect.ts : When adding a dialect feature, update packages/{dialect}/src/dialect.ts to declare feature support flags

Applied to files:

  • packages/postgres/src/query-generator.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 (2)
packages/postgres/src/query-generator.js (2)

266-276: Clean abstraction for field name normalization.

The normalizeFieldName helper elegantly handles both string and object field descriptors, addressing the earlier concern about multiple comparisons. The property precedence order (field ?? column ?? attribute ?? name) is sensible and returning null for invalid types provides safe fallback behavior.


264-286: Well-implemented fix for duplicate unique constraints.

The logic correctly prevents duplicate UNIQUE constraints by:

  1. Computing the inline field name with proper fallback (attribute.field ?? options.key)
  2. Normalizing field descriptors to handle both string and object shapes
  3. Checking if an existing unique key already covers the field
  4. Only emitting the inline UNIQUE keyword when necessary

This addresses the root cause described in issue #17978 where repeated sync({ alter: true }) calls created duplicate named constraints in Postgres.

Minor note: While coding guidelines prefer new implementations in TypeScript, modifying existing .js query generator code is acceptable until the file is migrated.

Based on coding guidelines.

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 (3)
packages/core/test/integration/model/sync.test.js (3)

707-713: Rename variable for clarity.

The variable name ignoreUser is misleading since the model is actively used for constraint verification rather than being ignored. Consider renaming to User (consistent with other tests in this file) or user for clarity.

Apply this diff:

-    const ignoreUser = sequelize.define(
+    const User = sequelize.define(
       'UserUniqueSync',
       {
         email: { type: DataTypes.STRING, unique: true },
       },
       { timestamps: false },
     );

And update the usage on line 719:

-    const results = await sequelize.queryInterface.showConstraints(ignoreUser, {
+    const results = await sequelize.queryInterface.showConstraints(User, {

719-727: Consider verifying constraint name and fields for completeness.

While the test correctly verifies that duplicate constraints are not created, similar tests in this file (e.g., lines 167-189) also verify the constraint name and fields for more thorough validation. Additionally, the SQLite handling allows 0 constraints (lte(1)), which seems overly lenient—a column with unique: true should have at least one constraint even on SQLite.

Consider this more thorough approach:

     const results = await sequelize.queryInterface.showConstraints(ignoreUser, {
       constraintType: 'UNIQUE',
     });
 
-    if (sequelize.dialect.name === 'sqlite' || sequelize.dialect.name === 'sqlite3') {
-      expect(results.length).to.be.lte(1);
-    } else {
-      expect(results).to.have.length(1);
-    }
+    expect(results).to.have.length(1);
+    expect(results[0].columnNames).to.deep.eq(['email']);

If SQLite truly behaves differently and requires the lenient check, please add a comment explaining why it may have 0 constraints.


729-729: Remove trailing empty line.

There's an unnecessary empty line before the closing brace of the test.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8f3e190 and 6a7f90a.

📒 Files selected for processing (1)
  • packages/core/test/integration/model/sync.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/integration/model/sync.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/integration/model/sync.test.js
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: sequelize/sequelize PR: 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
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 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/model/sync.test.js
🧬 Code graph analysis (1)
packages/core/test/integration/model/sync.test.js (1)
packages/core/src/index.mjs (2)
  • DataTypes (91-91)
  • DataTypes (91-91)
⏰ 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)

@tusharrana786
Copy link
Author

@WikiRik Can you please review this again it has been open for long, it's my first time contributing and i just wanted to know when can it be merged

@WikiRik
Copy link
Member

WikiRik commented Dec 17, 2025

I've taken another look at this and it seems that the test passes already on the main branch. Could it be possible the issue is solved? Or do we need to add a different test?

@tusharrana786
Copy link
Author

I've taken another look at this and it seems that the test passes already on the main branch. Could it be possible the issue is solved? Or do we need to add a different test?

I feel like i will have to write a different test because this because showConstraints in the main branch is also probably unable to detect the duplicate constraint that is probably why it passes, can you please help me in understanding how i can actually verify this one.

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.

Sequelize keeps creating duplicate unique constraints on the same column

2 participants