feat: add parameter style by lohart13 · Pull Request #17560 · sequelize/sequelize · GitHub
Skip to content

feat: add parameter style - #17560

Merged
WikiRik merged 4 commits into
sequelize:mainfrom
lohart13:parameter-style
Oct 4, 2025
Merged

feat: add parameter style#17560
WikiRik merged 4 commits into
sequelize:mainfrom
lohart13:parameter-style

Conversation

@lohart13

@lohart13 lohart13 commented Oct 17, 2024

Copy link
Copy Markdown
Contributor

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

This PR implements the ParameterStyle changes in #17131 which are required for #16988 and #17063

List of Breaking Changes

The bindParam option have been replaced with parameterStyle which defaults to ParameterStyle.BIND.

Summary by CodeRabbit

  • New Features

    • Added configurable parameter binding via ParameterStyle (BIND or REPLACEMENT), exposed on Sequelize and core exports.
    • insertQuery/updateQuery now return a BoundQuery (query with optional bind object).
  • Refactor

    • Removed the bindParam option; providing it now throws. Use parameterStyle instead.
    • Consolidated QueryTypes, IndexHints, and TableHints under a unified enums export; public imports updated accordingly.
  • Tests

    • Updated tests to use ParameterStyle and BoundQuery, and to assert errors for deprecated bindParam usage.

@lohart13
lohart13 requested a review from a team as a code owner October 17, 2024 22:41
@lohart13
lohart13 requested review from ephys and sdepold October 17, 2024 22:41
@WikiRik
WikiRik self-requested a review October 17, 2024 22:50
@WikiRik
WikiRik marked this pull request as draft October 25, 2024 10:25
@lohart13
lohart13 marked this pull request as ready for review March 10, 2025 01:27
@coderabbitai

coderabbitai Bot commented Oct 4, 2025

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 (2)
packages/core/src/utils/sql.ts (1)

551-563: Seed the generator counter when reusing a populated bind map.

If the caller hands in a bind object that already contains sequelize_… entries (e.g. when augmenting an existing map), restarting the counter from zero will overwrite previously collected values. Please initialize the counter from the existing maximum (or assert the map is empty) before returning the closure.

 export function createBindParamGenerator(
   bind: Record<string, unknown>,
 ): (value: unknown) => string {
-  let i = 0;
+  let i = Object.keys(bind).reduce((max, key) => {
+    if (!key.startsWith('sequelize_')) {
+      return max;
+    }
+
+    const parsed = Number.parseInt(key.slice('sequelize_'.length), 10);
+
+    return Number.isNaN(parsed) ? max : Math.max(max, parsed);
+  }, 0);
 
   return (value: unknown): string => {
     const bindName = `sequelize_${++i}`;
packages/db2/src/query-generator.js (1)

368-407: Respect super.updateQuery’s parameter-style decision before rebuilding the LIMIT variant

Here we recompute the style from options (defaulting to BIND). If super.updateQuery already had to fall back to replacements (it returns { query } without a bind object, e.g. when searchPath / prependSearchPath forces multi-statement execution), this branch reintroduces bind parameters and ends up returning a bind bag again. That regresses the very scenario super just handled.

Derive the effective parameterStyle from the response of super.updateQuery before deciding whether to allocate bind / bindParam:

-    let bind;
-    let bindParam;
-    const parameterStyle = options?.parameterStyle ?? ParameterStyle.BIND;
+    let bind;
+    let bindParam;
+    let parameterStyle =
+      options?.parameterStyle ??
+      (sql.bind == null ? ParameterStyle.REPLACEMENT : ParameterStyle.BIND);
+
+    if (sql.bind == null && parameterStyle === ParameterStyle.BIND) {
+      parameterStyle = ParameterStyle.REPLACEMENT;
+    }

This keeps the LIMIT rewrite in lock-step with the base implementation and avoids resurrecting binds in contexts that already required inlined replacements.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9b9a1d7 and 78375d2.

📒 Files selected for processing (28)
  • packages/core/src/abstract-dialect/query-generator-typescript.ts (3 hunks)
  • packages/core/src/abstract-dialect/query-generator.d.ts (4 hunks)
  • packages/core/src/abstract-dialect/query-generator.js (7 hunks)
  • packages/core/src/abstract-dialect/query-generator.types.ts (2 hunks)
  • packages/core/src/abstract-dialect/query-interface-internal.ts (1 hunks)
  • packages/core/src/abstract-dialect/query-interface-typescript.ts (1 hunks)
  • packages/core/src/abstract-dialect/query-interface.d.ts (1 hunks)
  • packages/core/src/abstract-dialect/query-interface.js (1 hunks)
  • packages/core/src/abstract-dialect/query.d.ts (1 hunks)
  • packages/core/src/abstract-dialect/query.js (1 hunks)
  • packages/core/src/abstract-dialect/where-sql-builder.ts (1 hunks)
  • packages/core/src/enums.ts (1 hunks)
  • packages/core/src/index-hints.ts (0 hunks)
  • packages/core/src/index.d.ts (1 hunks)
  • packages/core/src/index.mjs (2 hunks)
  • packages/core/src/model.d.ts (1 hunks)
  • packages/core/src/model.js (1 hunks)
  • packages/core/src/query-types.ts (0 hunks)
  • packages/core/src/sequelize.d.ts (1 hunks)
  • packages/core/src/sequelize.js (2 hunks)
  • packages/core/src/table-hints.ts (0 hunks)
  • packages/core/src/utils/sql.ts (1 hunks)
  • packages/core/test/support.ts (3 hunks)
  • packages/core/test/unit/query-generator/insert-query.test.ts (3 hunks)
  • packages/core/test/unit/query-generator/update-query.test.ts (3 hunks)
  • packages/db2/src/query-generator.js (4 hunks)
  • packages/ibmi/src/query-generator.js (0 hunks)
  • packages/sqlite3/src/query-generator.js (4 hunks)
💤 Files with no reviewable changes (4)
  • packages/core/src/index-hints.ts
  • packages/core/src/query-types.ts
  • packages/ibmi/src/query-generator.js
  • packages/core/src/table-hints.ts
🧰 Additional context used
📓 Path-based instructions (5)
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.js
  • packages/core/src/abstract-dialect/query-generator.js
  • packages/core/src/sequelize.js
  • packages/core/src/abstract-dialect/query-interface.js
  • packages/sqlite3/src/query-generator.js
  • packages/db2/src/query-generator.js
  • packages/core/src/model.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/core/src/abstract-dialect/query-generator.js
  • packages/sqlite3/src/query-generator.js
  • packages/db2/src/query-generator.js
packages/**/test/**/*.test.ts

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

Name unit tests with the .test.ts suffix

Files:

  • packages/core/test/unit/query-generator/insert-query.test.ts
  • packages/core/test/unit/query-generator/update-query.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/unit/query-generator/insert-query.test.ts
  • packages/core/test/unit/query-generator/update-query.test.ts
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/sqlite3/src/query-generator.js
  • packages/db2/src/query-generator.js
🧠 Learnings (3)
📚 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/*/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-internal.ts
  • packages/core/src/abstract-dialect/query.js
  • packages/core/src/abstract-dialect/query-interface.js
📚 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/**/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-internal.ts
  • packages/core/src/abstract-dialect/query.js
  • packages/core/src/abstract-dialect/query-generator.js
  • packages/core/src/abstract-dialect/query-interface.js
  • packages/core/src/abstract-dialect/query.d.ts
  • packages/core/src/abstract-dialect/query-generator.d.ts
📚 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/*/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.js
  • packages/core/src/abstract-dialect/query-generator.js
  • packages/core/test/support.ts
  • packages/core/src/abstract-dialect/query-generator.d.ts
🧬 Code graph analysis (12)
packages/core/src/utils/sql.ts (1)
packages/core/src/abstract-dialect/where-sql-builder.ts (2)
  • value (751-765)
  • value (767-781)
packages/core/src/abstract-dialect/where-sql-builder.ts (1)
packages/core/src/abstract-dialect/query-generator-typescript.ts (1)
  • options (192-194)
packages/core/src/abstract-dialect/query-generator.js (2)
packages/core/src/index.mjs (2)
  • ParameterStyle (95-95)
  • ParameterStyle (95-95)
packages/core/src/utils/sql.ts (1)
  • createBindParamGenerator (551-563)
packages/core/test/support.ts (1)
packages/core/src/abstract-dialect/query-generator.types.ts (1)
  • BoundQuery (15-18)
packages/core/src/sequelize.js (1)
packages/core/src/index.mjs (4)
  • Sequelize (4-4)
  • Sequelize (4-4)
  • ParameterStyle (95-95)
  • ParameterStyle (95-95)
packages/core/src/abstract-dialect/query-generator-typescript.ts (3)
packages/core/src/abstract-dialect/data-types.ts (1)
  • BindParamOptions (65-67)
packages/core/src/index.mjs (2)
  • ParameterStyle (95-95)
  • ParameterStyle (95-95)
packages/core/src/sequelize.d.ts (1)
  • BindOrReplacements (113-113)
packages/core/test/unit/query-generator/insert-query.test.ts (1)
packages/core/src/index.mjs (4)
  • literal (11-11)
  • literal (11-11)
  • ParameterStyle (95-95)
  • ParameterStyle (95-95)
packages/sqlite3/src/query-generator.js (3)
packages/core/src/abstract-dialect/query-generator-typescript.ts (1)
  • options (192-194)
packages/core/src/index.mjs (2)
  • ParameterStyle (95-95)
  • ParameterStyle (95-95)
packages/core/src/utils/sql.ts (1)
  • createBindParamGenerator (551-563)
packages/core/src/abstract-dialect/query-generator.d.ts (3)
packages/core/src/model.d.ts (1)
  • UpdateOptions (1285-1342)
packages/core/src/abstract-dialect/query-generator-typescript.ts (1)
  • ParameterOptions (156-165)
packages/core/src/abstract-dialect/query-generator.types.ts (1)
  • BoundQuery (15-18)
packages/core/src/enums.ts (1)
packages/core/src/index.mjs (8)
  • IndexHints (90-90)
  • IndexHints (90-90)
  • ParameterStyle (95-95)
  • ParameterStyle (95-95)
  • QueryTypes (87-87)
  • QueryTypes (87-87)
  • TableHints (89-89)
  • TableHints (89-89)
packages/core/test/unit/query-generator/update-query.test.ts (1)
packages/core/src/index.mjs (4)
  • literal (11-11)
  • literal (11-11)
  • ParameterStyle (95-95)
  • ParameterStyle (95-95)
packages/db2/src/query-generator.js (3)
packages/core/src/abstract-dialect/query-generator-typescript.ts (1)
  • options (192-194)
packages/core/src/index.mjs (2)
  • ParameterStyle (95-95)
  • ParameterStyle (95-95)
packages/core/src/utils/sql.ts (1)
  • createBindParamGenerator (551-563)
🪛 Biome (2.1.2)
packages/core/src/abstract-dialect/query.js

[error] 3-4: Illegal use of an import declaration outside of a module

not allowed inside scripts

(parse)


[error] 4-5: Illegal use of an import declaration outside of a module

not allowed inside scripts

(parse)

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

[error] 18-19: Illegal use of an import declaration outside of a module

not allowed inside scripts

(parse)


[error] 19-20: Illegal use of an import declaration outside of a module

not allowed inside scripts

(parse)


[error] 27-28: Illegal use of an import declaration outside of a module

not allowed inside scripts

(parse)


[error] 28-29: Illegal use of an import declaration outside of a module

not allowed inside scripts

(parse)

packages/core/src/sequelize.js

[error] 19-20: Illegal use of an import declaration outside of a module

not allowed inside scripts

(parse)


[error] 20-21: Illegal use of an import declaration outside of a module

not allowed inside scripts

(parse)

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

[error] 10-11: Illegal use of an import declaration outside of a module

not allowed inside scripts

(parse)


[error] 11-12: Illegal use of an import declaration outside of a module

not allowed inside scripts

(parse)

packages/sqlite3/src/query-generator.js

[error] 2-3: Illegal use of an import declaration outside of a module

not allowed inside scripts

(parse)


[error] 3-7: Illegal use of an import declaration outside of a module

not allowed inside scripts

(parse)


[error] 11-12: Illegal use of an import declaration outside of a module

not allowed inside scripts

(parse)


[error] 12-13: Illegal use of an import declaration outside of a module

not allowed inside scripts

(parse)

packages/db2/src/query-generator.js

[error] 2-3: Illegal use of an import declaration outside of a module

not allowed inside scripts

(parse)


[error] 3-7: Illegal use of an import declaration outside of a module

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)

packages/core/src/model.js

[error] 40-41: Illegal use of an import declaration outside of a module

not allowed inside scripts

(parse)


[error] 41-42: Illegal use of an import declaration outside of a module

not allowed inside scripts

(parse)

⏰ 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 (14)
packages/core/src/abstract-dialect/query.d.ts (1)

1-1: LGTM! Import path updated correctly.

The import source for QueryTypes has been correctly updated to use the centralized enums module.

packages/core/src/abstract-dialect/query-generator.types.ts (2)

2-2: LGTM! Import path updated correctly.

The import source for TableHints has been correctly updated to use the centralized enums module.


15-18: LGTM! New BoundQuery interface is well-defined.

The BoundQuery interface provides a clean contract for representing SQL queries with optional bind parameters, supporting the new parameter-style binding mechanism.

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

4-4: LGTM! Import correctly updated to ES module.

The import has been correctly updated from CommonJS require to ES module import and now sources QueryTypes from the centralized enums module. The static analysis errors about "imports outside modules" are false positives since this file uses ES modules.

packages/core/src/enums.ts (1)

1-82: LGTM! Well-structured enum consolidation.

The centralized enums module successfully consolidates IndexHints, ParameterStyle, QueryTypes, and TableHints into a single location. All enums are:

  • Properly documented with JSDoc comments
  • Consistently structured with string values
  • Clearly named and organized

This improves maintainability by providing a single source of truth for these core enums.

packages/core/src/abstract-dialect/query-generator-typescript.ts (2)

8-9: LGTM! Imports updated correctly.

The imports have been correctly updated to source ParameterStyle, IndexHints, and TableHints from the centralized enums module.


133-165: LGTM! Improved parameter options design.

The refactor successfully replaces the Bindable interface with a cleaner separation of concerns:

  • ParameterOptions provides parameterStyle and replacements for parameter binding configuration
  • FormatWhereOptions now extends both Partial<BindParamOptions> and ParameterOptions
  • Added model property to aid type resolution

This aligns with the PR's goal of replacing bindParam with the more flexible parameterStyle API.

packages/core/src/abstract-dialect/where-sql-builder.ts (1)

410-412: Verify the safety of mutating the options object.

The code now mutates the options object in place by deleting the bindParam property, rather than creating a new object without it. While this achieves the same functional result for the IS operation, it could have side effects if the caller expects the original options object to remain unchanged.

Please confirm that this mutation is intentional and that callers of this method do not rely on the options object remaining immutable.

If immutability is preferred, consider reverting to the previous pattern:

-    // "IS" operator does not accept bind parameters, only literals
-    if (options.bindParam) {
-      delete options.bindParam;
-    }
+    // "IS" operator does not accept bind parameters, only literals
+    const optionsWithoutBind = options.bindParam
+      ? { ...options, bindParam: undefined }
+      : options;

-    return this.formatBinaryOperation(left, undefined, operator, right, undefined, options);
+    return this.formatBinaryOperation(left, undefined, operator, right, undefined, optionsWithoutBind);
packages/core/src/abstract-dialect/query-interface-internal.ts (1)

2-2: LGTM! Import path updated correctly.

The import source for QueryTypes has been correctly updated to use the centralized enums module.

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

11-11: LGTM! Import path updated correctly.

The import source for QueryTypes has been correctly updated to use the centralized enums module. The static analysis errors about "imports outside modules" are false positives since this file uses ES modules.

packages/core/src/index.d.ts (1)

49-49: Re-export looks good.
Consolidating the enum exports keeps the public surface consistent with the implementation move.

packages/core/src/model.js (1)

41-41: Import swap is fine.
The module already centralizes enums in ./enums.js, so pointing Model there keeps everything consistent.

packages/core/src/model.d.ts (1)

24-24: Type import matches runtime change.
Pulling IndexHints from ./enums.js aligns the typings with the new enum module.

packages/core/src/index.mjs (1)

86-96: New export wired correctly.
Re-exporting ParameterStyle keeps the top-level API in sync with the enum move.

Comment thread packages/core/src/abstract-dialect/query-interface-typescript.ts

@WikiRik WikiRik left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Apologies for not reviewing this sooner, this looks great! I'll check in a bit if there's already a PR for the website repo to document the removal of bindParam (and replacement with parameterStyle)

@WikiRik
WikiRik enabled auto-merge (squash) October 4, 2025 08:53
@WikiRik
WikiRik merged commit 1f4bdee into sequelize:main Oct 4, 2025
139 of 141 checks passed
papandreou added a commit to papandreou/sequelize that referenced this pull request Oct 26, 2025
@coderabbitai coderabbitai Bot mentioned this pull request Nov 7, 2025
5 tasks
papandreou added a commit to papandreou/sequelize that referenced this pull request Aug 31, 2026
…itions

Op.is/Op.isNot (used for IS NULL/IS TRUE/IS FALSE) cannot use a bind parameter for its right-hand side, so the WhereSqlBuilder strips bindParam before formatting it. Since 1f4bdee (feat: add parameter style, sequelize#17560) it did this by deleting the property from the *shared* options object instead of a local copy, which permanently disabled bind parameters for every WHERE condition evaluated after an Op.is comparison in the same clause -- not just that one. Those conditions fall back to literal escaping, which breaks for e.g. a JSON ->> (text) comparison against a plain number: Postgres has no text = integer operator. Surfaced via sequelize-core-papandreou release testing against peakon/api's notification service; bisected to 1f4bdee by comparing against alpha.44.

Releases sequelize-{core,postgres}-papandreou@7.0.0-alpha.48-patch2
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.

2 participants