Skip to content

feat(core): add Firebird dialect support to Sequelize#18154

Open
Juank2001 wants to merge 3 commits intosequelize:mainfrom
Juank2001:main
Open

feat(core): add Firebird dialect support to Sequelize#18154
Juank2001 wants to merge 3 commits intosequelize:mainfrom
Juank2001:main

Conversation

@Juank2001
Copy link
Copy Markdown

@Juank2001 Juank2001 commented Mar 11, 2026

Description of Changes

  • Introduced Firebird dialect in Sequelize, allowing users to connect to Firebird databases.
  • Added Firebird-specific data type overrides for compatibility with Sequelize's data types.
  • Implemented Firebird connection manager to handle database connections and transactions.
  • Created Firebird query generator to support Firebird SQL syntax, including handling of LIMIT/OFFSET and sequences for auto-increment.
  • Developed Firebird query interface for executing queries and mapping results to Sequelize's expected shapes.
  • Added necessary configuration files, including TypeScript and ESLint settings for the Firebird package.
  • Updated core Sequelize files to support the new Firebird dialect, including dialect options and connection handling.

Summary by CodeRabbit

  • New Features

    • Full Firebird dialect: connection support, query execution, pagination, introspection, sequences/triggers for auto-increment, and Firebird-specific data types; Firebird added to supported dialects.
  • Bug Fixes / Improvements

    • Authentication SQL is now dialect-aware for better cross-engine compatibility.
    • Identifier quoting avoids unnecessary escaping in edge cases.
  • Documentation

    • Added comprehensive Firebird docs with installation, usage examples, and data type mapping.
  • Chores

    • New Firebird package and build/test/config scaffolding added.

@Juank2001 Juank2001 requested a review from a team as a code owner March 11, 2026 20:18
@Juank2001 Juank2001 requested review from WikiRik and sdepold March 11, 2026 20:18
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 11, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a new Firebird dialect package and registers it in core: adds firebird to supported dialects, adjusts auth SQL for dialects, tweaks identifier quoting, and introduces a full Firebird implementation (connection manager, query generator, query executor, data type overrides, docs, and packaging).

Changes

Cohort / File(s) Summary
Core dialect & utils
packages/core/src/sequelize-typescript.ts, packages/core/src/sequelize.js, packages/core/src/utils/dialect.ts
Register firebird in SUPPORTED_DIALECTS; make auth FROM fragment dialect-aware (uses RDB$DATABASE for Firebird); quoteIdentifier returns early when ticks are empty.
Firebird package metadata & tooling
packages/firebird/package.json, packages/firebird/tsconfig.json, packages/firebird/.eslintrc.js, packages/firebird/typedoc.json
Add package manifest, TypeScript, ESLint and TypeDoc configs, exports and scripts for the new Firebird connector package.
Documentation
packages/firebird/readme.md
Add README describing installation, usage, dialect quirks, and data type mappings for Firebird.
Data type overrides
packages/firebird/src/_internal/data-types-overrides.ts
Implement Firebird-specific BaseTypes overrides (STRING, TEXT, numeric types, DATE/DATEONLY/TIME, UUID, BLOB, JSON) with SQL mappings and serialization/escape logic.
Connection management
packages/firebird/src/connection-manager.ts
New FirebirdConnectionManager supporting injected or node-firebird module, connect/validate/disconnect, connection option mapping, and error translation to Sequelize errors.
Dialect wiring
packages/firebird/src/dialect.ts, packages/firebird/src/index.ts
Add FirebirdDialect class, supported options and connection parsing, bind collector, and public re-exports.
Query generation
packages/firebird/src/query-generator.ts, packages/firebird/src/query-generator.internal.ts
Add FirebirdQueryGenerator and internals: version, table/list/describe/truncate/index/sequence SQL, and pagination via OFFSET/FETCH.
Query execution & interface types
packages/firebird/src/query.ts, packages/firebird/src/query-interface.ts, packages/firebird/src/query-interface.types.ts
Add FirebirdQuery adapting callback API to promises, normalizing rows and mapping errors; add QueryInterface subclass placeholder and describe-column type.
CI scope
.github/workflows/pr-title.yml
Add firebird to PR title semantic scopes.
Misc package script update
packages/mysql/package.json
Change build script to run node ../../build-packages.mjs mysql.

Sequence Diagram(s)

sequenceDiagram
    participant App as Application
    participant Seq as Sequelize Core
    participant Dial as FirebirdDialect
    participant ConnMgr as FirebirdConnectionManager
    participant FB as node-firebird
    participant Query as FirebirdQuery

    App->>Seq: new Sequelize({ dialect: "firebird", ... })
    Seq->>Dial: instantiate dialect
    Dial->>ConnMgr: initialize connection manager

    App->>Seq: query("SELECT ...")
    Seq->>Dial: create Query instance
    Query->>ConnMgr: acquire connection
    ConnMgr->>FB: attach(options, callback)
    FB-->>ConnMgr: connection
    ConnMgr-->>Query: connection ready

    Query->>FB: execute(sql, params, callback)
    FB-->>Query: rows / error
    Query->>Query: normalize rows, map errors
    Query->>Seq: resolve Promise(result)
    Seq-->>App: return results
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Suggested reviewers

  • sdepold
  • WikiRik

Poem

🐰 I hopped through code with nimble feet,

Wove Firebird trails where queries meet,
Connections chirp and types align,
Triggers hum and results shine,
A carrot nod — this patch is neat!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ 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%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately describes the main change: adding Firebird dialect support to Sequelize, which is the primary objective reflected throughout all file changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan for PR comments
  • Generate coding plan

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
Copy Markdown
Contributor

@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: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/core/src/sequelize-typescript.ts (1)

199-212: ⚠️ Potential issue | 🟠 Major

Add 'firebird' case to importDialect function.

The importDialect function in packages/core/src/sequelize.internals.ts lacks a case for 'firebird'. Users passing dialect: 'firebird' as a string will trigger the default error, which also does not mention 'firebird' in the list of supported dialects. Add the corresponding case handler and update the error message.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/sequelize-typescript.ts` around lines 199 - 212, The
SUPPORTED_DIALECTS list includes 'firebird' but the importDialect function in
packages/core/src/sequelize.internals.ts lacks a case for 'firebird', causing
the default error to be thrown (and the error string also omits 'firebird');
update importDialect to add a case for 'firebird' that imports/returns the
appropriate Firebird dialect implementation (mirror the pattern used for other
dialects in importDialect) and update the default error message to include
'firebird' so the supported-dialects list and import behavior are consistent
with SUPPORTED_DIALECTS.
🧹 Nitpick comments (9)
packages/firebird/src/query.ts (2)

14-14: Unused declaration: connectionManager property.

This property is declared but never used. Line 21 uses this.connection instead. Either this declaration is unnecessary or there's a property name mismatch.

♻️ Proposed fix - remove unused declaration
 export class FirebirdQuery extends AbstractQuery {
-  declare connectionManager: FirebirdConnection;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/firebird/src/query.ts` at line 14, The class has an unused property
declaration connectionManager while the code uses this.connection; remove the
unused connectionManager declaration or rename it to connection to match usage
(update any references to the property accordingly), ensuring the class property
name (connection) aligns with places like methods referencing this.connection in
the Query class.

157-182: Consider hoisting error class imports to module level.

Using require() inside the method body incurs repeated module resolution overhead on each error. These imports could be moved to the top of the file for better performance and clearer dependencies.

♻️ Proposed refactor
 import { AbstractQuery } from '@sequelize/core';
+import {
+  DatabaseError,
+  ForeignKeyConstraintError,
+  UniqueConstraintError,
+  ValidationError,
+  ValidationErrorItem,
+} from '@sequelize/core';
 import { logger } from '@sequelize/core/_non-semver-use-at-your-own-risk_/utils/logger.js';

Then replace the require() calls with direct usage of the imported classes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/firebird/src/query.ts` around lines 157 - 182, Hoist the runtime
imports for Sequelize error classes out of the method and into the module scope
to avoid repeated require() calls: add a single top-level destructuring
assignment like const { UniqueConstraintError, ForeignKeyConstraintError,
ValidationError, ValidationErrorItem, DatabaseError } =
require('@sequelize/core'); (or equivalent ES import) at the top of the file,
then replace the inline require(...) usages inside the function that reference
UniqueConstraintError, ForeignKeyConstraintError, ValidationError,
ValidationErrorItem, and DatabaseError with the now-top-level identifiers so the
function returns new UniqueConstraintError(...), new
ForeignKeyConstraintError(...), new ValidationError(...), and new
DatabaseError(...) without performing require() on each execution.
packages/firebird/src/query-interface.ts (1)

3-9: Placeholder class noted.

The class extends AbstractQueryInterface but doesn't implement any Firebird-specific methods. The doc comment mentions "auto-increment via SEQUENCE + BEFORE INSERT TRIGGER" support, but the actual DDL operations aren't implemented yet.

As per coding guidelines, schema operations for new features should be added in query-interface.ts. Consider tracking this as a follow-up task to implement the promised auto-increment DDL functionality.

Would you like me to open an issue to track implementing the Firebird-specific DDL operations (SEQUENCE/TRIGGER creation for auto-increment)?

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/firebird/src/query-interface.ts` around lines 3 - 9,
FirebirdQueryInterface currently only extends AbstractQueryInterface but lacks
the Firebird-specific DDL operations promised in the docstring; add
implementations to query-interface.ts that provide auto-increment support via
SEQUENCE + BEFORE INSERT TRIGGER: implement methods on the
FirebirdQueryInterface class such as createAutoIncrement (or createSequence and
createTrigger), dropAutoIncrement (or dropSequence/dropTrigger), and any helpers
to generate the proper SQL and execute it via the existing query runner; ensure
method names are exported on FirebirdQueryInterface and integrate with existing
AbstractQueryInterface patterns so callers can create/drop sequences and
triggers for auto-increment columns.
packages/firebird/readme.md (1)

63-79: Add language specifier to fenced code block.

The package structure code block should have a language specifier for proper rendering and linting compliance.

📝 Proposed fix
-```
+```text
 packages/firebird/
 ├── package.json
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/firebird/readme.md` around lines 63 - 79, The fenced
package-structure block in README.md is missing a language specifier; update the
opening fence for the snippet that lists the packages/firebird tree from ``` to
```text (or another appropriate language like ```bash) so the block becomes a
labeled code fence, leaving the closing ``` intact; this change targets the
README.md file's package structure code block.
packages/core/src/sequelize-typescript.ts (1)

203-206: Pre-existing duplicate entries in SUPPORTED_DIALECTS.

Note: 'mariadb' and 'mssql' appear twice in the array. This is a pre-existing issue unrelated to this PR, but may be worth addressing in a follow-up.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/sequelize-typescript.ts` around lines 203 - 206, The
SUPPORTED_DIALECTS array contains duplicate entries ('mariadb' and
'mssql')—remove the duplicates so each dialect appears only once; locate the
SUPPORTED_DIALECTS constant in sequelize-typescript.ts and update the array to
hold unique strings (e.g., use a deduplicated list or construct it via
Array.from(new Set(...))) to ensure no repeated dialect names.
packages/firebird/src/connection-manager.ts (1)

199-233: Consider static imports for error classes.

The error classes (ConnectionRefusedError, AccessDeniedError, etc.) are dynamically required within #translateError. Using static imports at the top of the file would improve bundler compatibility and code clarity.

Use static imports for error classes
-import type { AbstractConnection, ConnectionOptions } from '@sequelize/core';
-import { AbstractConnectionManager, ConnectionError } from '@sequelize/core';
+import type { AbstractConnection, ConnectionOptions } from '@sequelize/core';
+import {
+  AbstractConnectionManager,
+  AccessDeniedError,
+  ConnectionError,
+  ConnectionRefusedError,
+  HostNotFoundError,
+  HostNotReachableError,
+  InvalidConnectionError,
+} from '@sequelize/core';

 // ... later in `#translateError` ...

   `#translateError`(err: Error & { gdscode?: number }): ConnectionError {
     const msg = err.message ?? '';

     if (/connection refused/i.test(msg)) {
-      const { ConnectionRefusedError } = require('@sequelize/core');
       return new ConnectionRefusedError(err);
     }

     if (/access denied/i.test(msg) || err.gdscode === 335_544_472) {
-      const { AccessDeniedError } = require('@sequelize/core');
       return new AccessDeniedError(err);
     }

     // ... similar for other error types
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/firebird/src/connection-manager.ts` around lines 199 - 233, Move the
dynamic require calls inside `#translateError` to static imports at the top of the
module: import { ConnectionRefusedError, AccessDeniedError, HostNotFoundError,
HostNotReachableError, InvalidConnectionError, ConnectionError } from
'@sequelize/core'; then update `#translateError` to reference these imported
classes directly (remove require(...) calls) so the function uses the statically
imported ConnectionRefusedError, AccessDeniedError, HostNotFoundError,
HostNotReachableError, InvalidConnectionError and ConnectionError symbols.
packages/firebird/src/query-generator.internal.ts (1)

6-8: Consider documenting why technical schema names is empty.

Firebird doesn't support schemas in the same way other databases do. A brief comment explaining this would help maintainers understand the empty array isn't an oversight.

Add explanatory comment
   getTechnicalSchemaNames(): readonly string[] {
+    // Firebird does not support schemas; there are no technical schema names to exclude
     return [];
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/firebird/src/query-generator.internal.ts` around lines 6 - 8, The
getTechnicalSchemaNames() method currently returns an empty array but lacks
explanation; add a short comment above the getTechnicalSchemaNames() function
stating that Firebird does not implement named schemas like other DBs and
therefore there are no technical schema names to return, so returning [] is
intentional (reference the getTechnicalSchemaNames function to locate the spot).
packages/firebird/src/_internal/data-types-overrides.ts (2)

170-174: Shadowing the global JSON identifier.

The class name JSON shadows the global JSON object, which can cause confusion and potential issues if the global JSON is needed within this module. This is flagged by the static analysis tool.

Consider renaming to FirebirdJSON or JSON_TYPE to avoid shadowing, though since this is a data-type override module following the Sequelize pattern, the shadowing may be intentional for consistency with other dialects.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/firebird/src/_internal/data-types-overrides.ts` around lines 170 -
174, The class named JSON shadows the global JSON identifier; rename the export
class JSON (and any references) to a non-conflicting identifier like
FirebirdJSON or JSON_TYPE, update its extends clause (currently BaseTypes.JSON)
and any places that import or instantiate this type so they reference the new
name, and keep the toSql() implementation unchanged to return 'BLOB SUB_TYPE
TEXT'.

113-116: Method signature diverges from base class.

The escape method adds an optional _options parameter that doesn't exist in the base BOOLEAN.escape(value: boolean | unknown): string signature (see packages/core/src/abstract-dialect/data-types.ts:1349-1351). While this works due to TypeScript's structural typing, it's inconsistent with the base class contract.

Align with base class signature
-  escape(value: boolean | unknown, _options?: any): string {
+  escape(value: boolean | unknown): string {
     return value ? 'TRUE' : 'FALSE';
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/firebird/src/_internal/data-types-overrides.ts` around lines 113 -
116, The BOOLEAN.escape method signature diverges from the base class by
including an extra optional parameter; update the method signature in the
BOOLEAN class from escape(value: boolean | unknown, _options?: any): string to
match the base class signature escape(value: boolean | unknown): string, and
remove the unused _options parameter from the implementation so the override
exactly matches the base contract.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/firebird/package.json`:
- Around line 42-44: The package.json lists an invalid dependency
"node-firebird@^2.0.2"; update it to a valid published version (e.g.,
"node-firebird@^1.1.9") or replace it with the correct package name if a
different library was intended; edit the dependency entry in packages/firebird's
package.json (the "node-firebird" dependency line) and run npm install / yarn
install to verify resolution and update package-lock.json or yarn.lock
accordingly.

In `@packages/firebird/src/_internal/data-types-overrides.ts`:
- Around line 128-136: The stringify implementation assumes sanitize(value)
returns a native Date; instead detect and normalize dayjs/Moment returns
(sanitize from the DATE base can yield Date | dayjs.Dayjs | Moment) before
calling Date methods: in stringify(AcceptedDate) call this.sanitize(value), if
the result is a Date use it, otherwise if it has a toDate() method
(dayjs/Moment) call that to obtain a Date, then use getFullYear()/getMonth()
etc. to build the Firebird 'YYYY-MM-DD HH:MM:SS.mmm' string; ensure you
reference the existing sanitize() and stringify() methods and AcceptedDate type
when making the change.

In `@packages/firebird/src/connection-manager.ts`:
- Around line 74-79: The JSDoc for the password property says "@default
'masterkey'" but the code default in the implementation is an empty string; pick
one and make them consistent: either update the JSDoc comment on the password?:
string declaration to "@default ''" to reflect the current behavior, or change
the implementation where the default is assigned (the code that initializes the
password default) to use 'masterkey' instead of '' so it matches the JSDoc;
ensure you update the JSDoc text and the default-assignment site together
(referencing the password property and the function/constructor that sets its
default).

In `@packages/firebird/src/dialect.ts`:
- Around line 112-127: The constructor in the Firebird dialect sets
identifierDelimiter to an empty string which disables identifier quoting and
contradicts Firebird behavior; update the constructor (in the Firebird dialect
class where constructor(sequelize: Sequelize, options: FirebirdDialectOptions)
is defined) to set identifierDelimiter to a double-quote character (") so the
dialect and the FirebirdQueryGenerator/FirebirdQueryInterface behavior align and
support delimited identifiers and reserved-word quoting.

In `@packages/firebird/src/query-generator.ts`:
- Around line 145-152: The PK join is matching RDB$INDEX_SEGMENTS by field name
only (alias iseg) which pulls segments from any table with the same column name;
change the join order to first LEFT JOIN RDB$RELATION_CONSTRAINTS rc ON
rc.RDB$RELATION_NAME = rf.RDB$RELATION_NAME AND rc.RDB$CONSTRAINT_TYPE =
'PRIMARY KEY' and then LEFT JOIN RDB$INDEX_SEGMENTS iseg ON iseg.RDB$INDEX_NAME
= rc.RDB$INDEX_NAME AND iseg.RDB$FIELD_NAME = rf.RDB$FIELD_NAME so the segment
match is scoped to the relation's primary-key index (use the aliases rf, rc,
iseg shown in the diff).
- Line 58: The metadata queries currently upper-case quoted identifiers causing
failures for mixed/lowercase names; update the predicates in tableExistsQuery,
showIndexesQuery, and describeTableQuery to stop calling .toUpperCase() on
table.tableName (and any similar uses for index or column names), i.e., use
this.escape(table.tableName) (or the original identifier variable without
.toUpperCase()) so the TRIM(RDB$RELATION_NAME) and related comparisons match
preserved-case catalog entries.

In `@packages/firebird/src/query.ts`:
- Around line 136-147: The `#normaliseRow` function is incorrectly adding Model
internals by setting out.dataValues and out._previousDataValues; remove those
two assignments so the function only lowercases keys and converts Buffers to
UTF-8 strings, returning a plain object, and ensure any code calling
`#normaliseRow` relies on Sequelize's hydration (or uses the existing
handleSelectQuery) to create Model instances rather than expecting these
injected properties.
- Around line 54-70: The current DML handling in the block using
isInsertQuery(), isUpdateQuery(), isDeleteQuery() returns hardcoded/incorrect
counts; wrap INSERT/UPDATE/DELETE executions in an EXECUTE BLOCK that captures
ROW_COUNT and returns it (and keep the INSERT RETURNING row when available) so
counts reflect actual affected rows. Update the logic in the function containing
handleInsertQuery() to: for inserts, execute a wrapped EXECUTE BLOCK that
performs the INSERT (producing RETURNING row if needed) and returns ROW_COUNT
alongside the returned row so you can return [insertedRow, rowCount]; for
updates and deletes replace the current rows.length return by executing the DML
inside an EXECUTE BLOCK that returns ROW_COUNT and return that count (or [rows,
rowCount] if your API expects rows plus count). Ensure you reuse/extend
handleInsertQuery(), and adjust callers to accept the new rowCount value instead
of the hardcoded 1 or rows.length.

---

Outside diff comments:
In `@packages/core/src/sequelize-typescript.ts`:
- Around line 199-212: The SUPPORTED_DIALECTS list includes 'firebird' but the
importDialect function in packages/core/src/sequelize.internals.ts lacks a case
for 'firebird', causing the default error to be thrown (and the error string
also omits 'firebird'); update importDialect to add a case for 'firebird' that
imports/returns the appropriate Firebird dialect implementation (mirror the
pattern used for other dialects in importDialect) and update the default error
message to include 'firebird' so the supported-dialects list and import behavior
are consistent with SUPPORTED_DIALECTS.

---

Nitpick comments:
In `@packages/core/src/sequelize-typescript.ts`:
- Around line 203-206: The SUPPORTED_DIALECTS array contains duplicate entries
('mariadb' and 'mssql')—remove the duplicates so each dialect appears only once;
locate the SUPPORTED_DIALECTS constant in sequelize-typescript.ts and update the
array to hold unique strings (e.g., use a deduplicated list or construct it via
Array.from(new Set(...))) to ensure no repeated dialect names.

In `@packages/firebird/readme.md`:
- Around line 63-79: The fenced package-structure block in README.md is missing
a language specifier; update the opening fence for the snippet that lists the
packages/firebird tree from ``` to ```text (or another appropriate language like
```bash) so the block becomes a labeled code fence, leaving the closing ```
intact; this change targets the README.md file's package structure code block.

In `@packages/firebird/src/_internal/data-types-overrides.ts`:
- Around line 170-174: The class named JSON shadows the global JSON identifier;
rename the export class JSON (and any references) to a non-conflicting
identifier like FirebirdJSON or JSON_TYPE, update its extends clause (currently
BaseTypes.JSON) and any places that import or instantiate this type so they
reference the new name, and keep the toSql() implementation unchanged to return
'BLOB SUB_TYPE TEXT'.
- Around line 113-116: The BOOLEAN.escape method signature diverges from the
base class by including an extra optional parameter; update the method signature
in the BOOLEAN class from escape(value: boolean | unknown, _options?: any):
string to match the base class signature escape(value: boolean | unknown):
string, and remove the unused _options parameter from the implementation so the
override exactly matches the base contract.

In `@packages/firebird/src/connection-manager.ts`:
- Around line 199-233: Move the dynamic require calls inside `#translateError` to
static imports at the top of the module: import { ConnectionRefusedError,
AccessDeniedError, HostNotFoundError, HostNotReachableError,
InvalidConnectionError, ConnectionError } from '@sequelize/core'; then update
`#translateError` to reference these imported classes directly (remove
require(...) calls) so the function uses the statically imported
ConnectionRefusedError, AccessDeniedError, HostNotFoundError,
HostNotReachableError, InvalidConnectionError and ConnectionError symbols.

In `@packages/firebird/src/query-generator.internal.ts`:
- Around line 6-8: The getTechnicalSchemaNames() method currently returns an
empty array but lacks explanation; add a short comment above the
getTechnicalSchemaNames() function stating that Firebird does not implement
named schemas like other DBs and therefore there are no technical schema names
to return, so returning [] is intentional (reference the getTechnicalSchemaNames
function to locate the spot).

In `@packages/firebird/src/query-interface.ts`:
- Around line 3-9: FirebirdQueryInterface currently only extends
AbstractQueryInterface but lacks the Firebird-specific DDL operations promised
in the docstring; add implementations to query-interface.ts that provide
auto-increment support via SEQUENCE + BEFORE INSERT TRIGGER: implement methods
on the FirebirdQueryInterface class such as createAutoIncrement (or
createSequence and createTrigger), dropAutoIncrement (or
dropSequence/dropTrigger), and any helpers to generate the proper SQL and
execute it via the existing query runner; ensure method names are exported on
FirebirdQueryInterface and integrate with existing AbstractQueryInterface
patterns so callers can create/drop sequences and triggers for auto-increment
columns.

In `@packages/firebird/src/query.ts`:
- Line 14: The class has an unused property declaration connectionManager while
the code uses this.connection; remove the unused connectionManager declaration
or rename it to connection to match usage (update any references to the property
accordingly), ensuring the class property name (connection) aligns with places
like methods referencing this.connection in the Query class.
- Around line 157-182: Hoist the runtime imports for Sequelize error classes out
of the method and into the module scope to avoid repeated require() calls: add a
single top-level destructuring assignment like const { UniqueConstraintError,
ForeignKeyConstraintError, ValidationError, ValidationErrorItem, DatabaseError }
= require('@sequelize/core'); (or equivalent ES import) at the top of the file,
then replace the inline require(...) usages inside the function that reference
UniqueConstraintError, ForeignKeyConstraintError, ValidationError,
ValidationErrorItem, and DatabaseError with the now-top-level identifiers so the
function returns new UniqueConstraintError(...), new
ForeignKeyConstraintError(...), new ValidationError(...), and new
DatabaseError(...) without performing require() on each execution.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b402ffcb-fcd3-4e4d-9c70-6c7a35257db0

📥 Commits

Reviewing files that changed from the base of the PR and between 81a2032 and 1cf694f.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (17)
  • packages/core/src/sequelize-typescript.ts
  • packages/core/src/sequelize.js
  • packages/core/src/utils/dialect.ts
  • packages/firebird/.eslintrc.js
  • packages/firebird/package.json
  • packages/firebird/readme.md
  • packages/firebird/src/_internal/data-types-overrides.ts
  • packages/firebird/src/connection-manager.ts
  • packages/firebird/src/dialect.ts
  • packages/firebird/src/index.ts
  • packages/firebird/src/query-generator.internal.ts
  • packages/firebird/src/query-generator.ts
  • packages/firebird/src/query-interface.ts
  • packages/firebird/src/query-interface.types.ts
  • packages/firebird/src/query.ts
  • packages/firebird/tsconfig.json
  • packages/firebird/typedoc.json

Comment thread packages/firebird/package.json
Comment thread packages/firebird/src/_internal/data-types-overrides.ts
Comment thread packages/firebird/src/connection-manager.ts
Comment on lines +112 to +127
constructor(sequelize: Sequelize, options: FirebirdDialectOptions) {
super({
identifierDelimiter: '',
options,
dataTypeOverrides: DataTypes,
sequelize,
minimumDatabaseVersion: '3.0.0',
dataTypesDocumentationUrl:
'https://firebirdsql.org/refdocs/langrefupd25-datatypes.html',
name: 'firebird' as any,
});

this.connectionManager = new FirebirdConnectionManager(this);
this.queryGenerator = new FirebirdQueryGenerator(this);
this.queryInterface = new FirebirdQueryInterface(this);
}
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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Does Firebird SQL support double-quoted identifiers for reserved words?

💡 Result:

Yes. Firebird supports delimited identifiers enclosed in double quotes, and a delimited identifier can be a reserved word (e.g., "USER", "DATE"). This applies in SQL Dialect 3 (the normal/default dialect); delimited identifiers are case-sensitive and stored “as-is”. [1]

Example

create table "USER" ("DATE" date);
select "DATE" from "USER";

[1]


🏁 Script executed:

cat -n packages/firebird/src/dialect.ts | head -150

Repository: sequelize/sequelize

Length of output: 5571


🏁 Script executed:

cat -n packages/firebird/src/query-generator.ts | head -100

Repository: sequelize/sequelize

Length of output: 4357


🏁 Script executed:

cat -n packages/firebird/src/query-generator.internal.ts | head -50

Repository: sequelize/sequelize

Length of output: 1319


🏁 Script executed:

rg -A 5 "quoteIdentifier" packages/firebird/src/

Repository: sequelize/sequelize

Length of output: 1766


🏁 Script executed:

find packages/firebird -name "*.test.*" -o -name "*.spec.*" | head -10

Repository: sequelize/sequelize

Length of output: 45


🏁 Script executed:

fd -t f "*.test\." packages/firebird/

Repository: sequelize/sequelize

Length of output: 350


🏁 Script executed:

fd --glob "*.test.*" packages/firebird/

Repository: sequelize/sequelize

Length of output: 45


🏁 Script executed:

rg "identifierDelimiter" packages/firebird/

Repository: sequelize/sequelize

Length of output: 126


🏁 Script executed:

rg "identifierDelimiter" -B 2 -A 2 packages/ | grep -A 5 -B 5 "firebird\|Firebird"

Repository: sequelize/sequelize

Length of output: 1056


🏁 Script executed:

rg "Double-quoted identifiers" -B 3 -A 3 packages/firebird/

Repository: sequelize/sequelize

Length of output: 599


identifierDelimiter: '' contradicts documented Firebird behavior and disables identifier quoting.

The query generator explicitly documents "Double-quoted identifiers" as a key Firebird SQL difference, yet the dialect is configured with an empty identifierDelimiter, which disables all identifier quoting. This creates a contradiction since Firebird supports delimited identifiers in double quotes, particularly for:

  • Reserved words used as identifiers (e.g., "USER", "DATE")
  • Case-sensitive identifier storage
  • Special characters in identifiers

The configuration should use identifierDelimiter: '"' to align with Firebird's documented capabilities and the query generator's stated behavior. Other dialects in the codebase (e.g., DB2) use double quotes correctly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/firebird/src/dialect.ts` around lines 112 - 127, The constructor in
the Firebird dialect sets identifierDelimiter to an empty string which disables
identifier quoting and contradicts Firebird behavior; update the constructor (in
the Firebird dialect class where constructor(sequelize: Sequelize, options:
FirebirdDialectOptions) is defined) to set identifierDelimiter to a double-quote
character (") so the dialect and the
FirebirdQueryGenerator/FirebirdQueryInterface behavior align and support
delimited identifiers and reserved-word quoting.

Comment thread packages/firebird/src/query-generator.ts Outdated
Comment on lines +128 to +144
' CASE f.RDB$FIELD_TYPE',
" WHEN 7 THEN 'SMALLINT'",
" WHEN 8 THEN 'INTEGER'",
" WHEN 10 THEN 'FLOAT'",
" WHEN 12 THEN 'DATE'",
" WHEN 13 THEN 'TIME'",
" WHEN 14 THEN 'CHAR'",
" WHEN 16 THEN 'BIGINT'",
" WHEN 23 THEN 'BOOLEAN'",
" WHEN 27 THEN 'DOUBLE PRECISION'",
" WHEN 35 THEN 'TIMESTAMP'",
" WHEN 37 THEN 'VARCHAR'",
" WHEN 261 THEN 'BLOB'",
" ELSE 'UNKNOWN'",
' END AS "Type",',
" IIF(rf.RDB$NULL_FLAG = 1, 'NO', 'YES') AS \"Null\",",
' rf.RDB$DEFAULT_SOURCE AS "Default",',
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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In Firebird metadata, which columns from RDB$FIELDS / RDB$RELATION_FIELDS are needed to distinguish VARCHAR length and NUMERIC/DECIMAL precision+scale/subtype beyond the base RDB$FIELD_TYPE code?

💡 Result:

To fully distinguish VARCHAR length and NUMERIC/DECIMAL precision/scale/subtype beyond the base RDB$FIELD_TYPE, you need these metadata columns (typically by joining RDB$RELATION_FIELDS.RDB$FIELD_SOURCE to RDB$FIELDS.RDB$FIELD_NAME) [2]:

VARCHAR / CHAR

  • RDB$FIELDS.RDB$CHARACTER_LENGTH — declared length in characters (what you usually want for CHAR/VARCHAR) [1]
  • RDB$FIELDS.RDB$FIELD_LENGTH — max length in bytes (depends on character set; not reliable as “VARCHAR(n)” by itself) [1]
  • RDB$FIELDS.RDB$CHARACTER_SET_ID (and optionally collation) — needed to interpret byte-length vs character-length correctly [1]

NUMERIC / DECIMAL (fixed-point)

(Stored using an integer RDB$FIELD_TYPE, so you must look at these too.)

  • RDB$FIELDS.RDB$FIELD_SUB_TYPE — distinguishes NUMERIC vs DECIMAL for integer storage types (1 = NUMERIC, 2 = DECIMAL) [2]
  • RDB$FIELDS.RDB$FIELD_PRECISION — total digits (precision) [1]
  • RDB$FIELDS.RDB$FIELD_SCALE — scale (negative value = digits after decimal point) [1]

Linking column (table/view column → domain/field definition):

  • RDB$RELATION_FIELDS.RDB$FIELD_SOURCERDB$FIELDS.RDB$FIELD_NAME [2]

Sources: Firebird system tables reference for RDB$FIELDS and RDB$RELATION_FIELDS [1][2], plus note that RDB$FIELD_LENGTH is byte length and you need RDB$CHARACTER_LENGTH for CHAR/VARCHAR [3].

[1] https://fb5doc.tetrasys.fi/The_Firebird_5.0_Language_Reference/fblangref50-appx04-systables
[2] https://firebirdsql.org/file/documentation/chunk/en/refdocs/fblangref40/fblangref-appx04-fields.html
[3] https://stackoverflow.com/questions/51190573/firebird-list-domains-and-data-types


🏁 Script executed:

# Check the structure of packages to understand the dialect organization
git ls-files "packages/*/src/query-generator.ts" "packages/*/src/query-generator.js" | head -20

Repository: sequelize/sequelize

Length of output: 454


🏁 Script executed:

# Read the Firebird query-generator.ts around lines 128-144
head -150 packages/firebird/src/query-generator.ts | tail -30

Repository: sequelize/sequelize

Length of output: 1289


🏁 Script executed:

# Check if there's a base query-generator that should also be updated
find packages -type f -name "query-generator.ts" -o -name "query-generator.js" | sort

Repository: sequelize/sequelize

Length of output: 508


🏁 Script executed:

# Read the full describeTableQuery method to see all selected columns
sed -n '123,150p' packages/firebird/src/query-generator.ts

Repository: sequelize/sequelize

Length of output: 1223


🏁 Script executed:

# Check the base query-generator describeTableQuery method
grep -A 50 "describeTableQuery" packages/core/src/abstract-dialect/query-generator.js | head -70

Repository: sequelize/sequelize

Length of output: 45


🏁 Script executed:

# Look for any existing use of RDB$CHARACTER_LENGTH, RDB$FIELD_PRECISION, RDB$FIELD_SCALE in Firebird
rg "RDB\$CHARACTER_LENGTH|RDB\$FIELD_PRECISION|RDB\$FIELD_SCALE|RDB\$FIELD_SUB_TYPE" packages/firebird/

Repository: sequelize/sequelize

Length of output: 45


🏁 Script executed:

# Check what the full describeTableQuery looks like in Firebird - need to see if it continues
sed -n '120,160p' packages/firebird/src/query-generator.ts

Repository: sequelize/sequelize

Length of output: 1766


🏁 Script executed:

# Check if there are other Firebird-specific implementations that handle type projection better
rg "describeTableQuery" packages/firebird/

Repository: sequelize/sequelize

Length of output: 168


🏁 Script executed:

# Verify the base implementation for reference
rg -A 40 "describeTableQuery" packages/core/src/abstract-dialect/query-generator.js | head -50

Repository: sequelize/sequelize

Length of output: 45


🏁 Script executed:

# Check how other dialects (MySQL, PostgreSQL) handle type projection in describeTableQuery
rg -A 30 "describeTableQuery" packages/mysql/src/query-generator.js | head -40

Repository: sequelize/sequelize

Length of output: 45


🏁 Script executed:

# Also check PostgreSQL for comparison
rg -A 30 "describeTableQuery" packages/postgres/src/query-generator.js | head -40

Repository: sequelize/sequelize

Length of output: 45


🏁 Script executed:

# Search more broadly for describeTableQuery across all dialects
grep -r "describeTableQuery" packages/*/src/query-generator.* | head -20

Repository: sequelize/sequelize

Length of output: 168


🏁 Script executed:

# Check if there's any code that processes the Type result from describeTableQuery
rg "describeTableQuery" packages/core/src/ -A 5 | head -30

Repository: sequelize/sequelize

Length of output: 1251


🏁 Script executed:

# Look for any type resolution logic in Firebird package
rg -i "varchar|numeric|decimal|precision|scale" packages/firebird/src/ --type ts

Repository: sequelize/sequelize

Length of output: 1372


🏁 Script executed:

# Check how the result of describeTableQuery is used/parsed
rg "describeTableQuery" packages/core/src/abstract-dialect/ -B 2 -A 10 | head -40

Repository: sequelize/sequelize

Length of output: 2624


🏁 Script executed:

# Look at the DECIMAL and VARCHAR implementations in Firebird to understand what precision/scale should be captured
cat packages/firebird/src/_internal/data-types-overrides.ts | head -100

Repository: sequelize/sequelize

Length of output: 2425


🏁 Script executed:

# Check if there's any logic that expects full type strings from describeTableQuery
rg "Type.*VARCHAR|Type.*DECIMAL|Type.*NUMERIC" packages/ -i

Repository: sequelize/sequelize

Length of output: 13398


🏁 Script executed:

# Check if describeTableQuery results are used to reconstruct attributes
rg "describeTableQuery|DESCRIBE" packages/core/src/abstract-dialect/query-interface-typescript.ts -B 5 -A 15

Repository: sequelize/sequelize

Length of output: 1224


🏁 Script executed:

# Look for any logic that might parse the Type column back into DataType objects
rg "parseTypeString|parseType|Type.*varchar|Type.*decimal" packages/ -i --type ts --type js | grep -i firebird

Repository: sequelize/sequelize

Length of output: 245


🏁 Script executed:

# Verify if there are tests for describeTable in Firebird
find packages/firebird -name "*.test.*" -o -name "*.spec.*" | xargs grep -l "describeTable" 2>/dev/null

Repository: sequelize/sequelize

Length of output: 47


🏁 Script executed:

# Check if there are any integration tests for describeTable
find packages -name "*.test.ts" -o -name "*.test.js" | xargs grep -l "describeTable" | head -5

Repository: sequelize/sequelize

Length of output: 357


🏁 Script executed:

# Look at one of these tests to understand what describeTable output is expected to contain
rg -A 30 "describeTable" packages/core/test/integration/query-interface/describeTable.test.js | head -50

Repository: sequelize/sequelize

Length of output: 1913


🏁 Script executed:

# Confirm the data-types file shows DECIMAL and STRING support precision/scale/length
cat packages/firebird/src/_internal/data-types-overrides.ts | grep -A 10 "class DECIMAL\|class STRING\|class CHAR"

Repository: sequelize/sequelize

Length of output: 980


describeTable loses type fidelity by omitting length and precision metadata.

The CASE statement uses only RDB$FIELD_TYPE, so VARCHAR(32) and VARCHAR(255) both return "VARCHAR", and NUMERIC/DECIMAL columns collapse to their base storage type (INTEGER, BIGINT). This makes schema introspection and diffs unreliable.

Include RDB$CHARACTER_LENGTH for string types, RDB$FIELD_SUB_TYPE, RDB$FIELD_PRECISION, and RDB$FIELD_SCALE for numeric types to reconstruct the full type signature.

Comment on lines +145 to +152
" IIF(rc.RDB$CONSTRAINT_TYPE = 'PRIMARY KEY', 'PRI', '') AS \"Key\"",
'FROM RDB$RELATION_FIELDS rf',
'JOIN RDB$FIELDS f ON f.RDB$FIELD_NAME = rf.RDB$FIELD_SOURCE',
'LEFT JOIN RDB$INDEX_SEGMENTS iseg ON iseg.RDB$FIELD_NAME = rf.RDB$FIELD_NAME',
'LEFT JOIN RDB$RELATION_CONSTRAINTS rc',
' ON rc.RDB$INDEX_NAME = iseg.RDB$INDEX_NAME',
' AND rc.RDB$RELATION_NAME = rf.RDB$RELATION_NAME',
" AND rc.RDB$CONSTRAINT_TYPE = 'PRIMARY KEY'",
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.

⚠️ Potential issue | 🟠 Major

The PK lookup joins every same-named column in the database.

RDB$INDEX_SEGMENTS is joined on RDB$FIELD_NAME alone, so a common column like id will match index segments from unrelated tables. That can duplicate rows and make "Key" unstable. Join the table’s primary-key constraint first, then match its segments back to the current field.

🔧 Suggested fix
-      "  IIF(rc.RDB$CONSTRAINT_TYPE = 'PRIMARY KEY', 'PRI', '') AS \"Key\"",
+      "  IIF(iseg.RDB$FIELD_NAME IS NOT NULL, 'PRI', '') AS \"Key\"",
...
-      'LEFT JOIN RDB$INDEX_SEGMENTS iseg ON iseg.RDB$FIELD_NAME = rf.RDB$FIELD_NAME',
-      'LEFT JOIN RDB$RELATION_CONSTRAINTS rc',
-      '  ON  rc.RDB$INDEX_NAME    = iseg.RDB$INDEX_NAME',
-      '  AND rc.RDB$RELATION_NAME = rf.RDB$RELATION_NAME',
-      "  AND rc.RDB$CONSTRAINT_TYPE = 'PRIMARY KEY'",
+      'LEFT JOIN RDB$RELATION_CONSTRAINTS rc',
+      '  ON  rc.RDB$RELATION_NAME = rf.RDB$RELATION_NAME',
+      "  AND rc.RDB$CONSTRAINT_TYPE = 'PRIMARY KEY'",
+      'LEFT JOIN RDB$INDEX_SEGMENTS iseg',
+      '  ON  iseg.RDB$INDEX_NAME = rc.RDB$INDEX_NAME',
+      '  AND iseg.RDB$FIELD_NAME = rf.RDB$FIELD_NAME',
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/firebird/src/query-generator.ts` around lines 145 - 152, The PK join
is matching RDB$INDEX_SEGMENTS by field name only (alias iseg) which pulls
segments from any table with the same column name; change the join order to
first LEFT JOIN RDB$RELATION_CONSTRAINTS rc ON rc.RDB$RELATION_NAME =
rf.RDB$RELATION_NAME AND rc.RDB$CONSTRAINT_TYPE = 'PRIMARY KEY' and then LEFT
JOIN RDB$INDEX_SEGMENTS iseg ON iseg.RDB$INDEX_NAME = rc.RDB$INDEX_NAME AND
iseg.RDB$FIELD_NAME = rf.RDB$FIELD_NAME so the segment match is scoped to the
relation's primary-key index (use the aliases rf, rc, iseg shown in the diff).

Comment on lines +54 to +70
if (this.isInsertQuery()) {
if (rows.length > 0) {
this.handleInsertQuery(rows[0]);

return [rows[0], 1];
}

return [undefined, 1];
}

if (this.isUpdateQuery()) {
return [rows, rows.length];
}

if (this.isDeleteQuery()) {
return rows.length;
}
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.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

node-firebird npm how to get affected rows count

💡 Result:

node-firebird doesn’t reliably expose an “affected rows” number in the JS callback result (it mainly returns row data arrays), so the common approach is to ask Firebird itself for the count. (npmjs.com)

Recommended: wrap the DML in EXECUTE BLOCK and return ROW_COUNT

Firebird provides ROW_COUNT, which is the number of rows affected by the most recent DML statement in that block. (firebirdsql.org)

const sql = `
  EXECUTE BLOCK (P_ID INTEGER = ?) RETURNS (AFFECTED INTEGER) AS
  BEGIN
    UPDATE MY_TABLE SET COL = 1 WHERE ID = :P_ID;
    AFFECTED = ROW_COUNT;
    SUSPEND;
  END
`;

db.query(sql, [id], (err, rows) => {
  if (err) throw err;
  const affected = rows[0].AFFECTED;   // number of updated rows
});

Alternative: UPDATE ... RETURNING ... and count returned rows

Firebird supports UPDATE ... RETURNING to return values from the affected rows; you can count the rows returned (best only if you expect small row counts). (firebirdsql.org)

Citations:


🏁 Script executed:

wc -l packages/firebird/src/query.ts

Repository: sequelize/sequelize

Length of output: 97


🏁 Script executed:

cat -n packages/firebird/src/query.ts | sed -n '40,90p'

Repository: sequelize/sequelize

Length of output: 1685


🏁 Script executed:

# Look for the handleInsertQuery implementation
rg -n "handleInsertQuery" packages/firebird/src/query.ts -A 20

Repository: sequelize/sequelize

Length of output: 495


🏁 Script executed:

# Check the broader context of this file
head -n 100 packages/firebird/src/query.ts

Repository: sequelize/sequelize

Length of output: 2748


🏁 Script executed:

# Find AbstractQuery class
fd -t f "abstract" packages/core/src | grep -i query

Repository: sequelize/sequelize

Length of output: 45


🏁 Script executed:

# Look for INSERT/UPDATE/DELETE SQL generation
rg -n "isInsertQuery|isUpdateQuery|isDeleteQuery" packages/firebird/src/ -A 2 -B 2

Repository: sequelize/sequelize

Length of output: 855


🏁 Script executed:

# Check what SQL is being generated for inserts
rg -n "INSERT INTO" packages/firebird/src/

Repository: sequelize/sequelize

Length of output: 45


🏁 Script executed:

# Look at how bulk inserts might be handled
rg -n "bulk" packages/firebird/src/ -i

Repository: sequelize/sequelize

Length of output: 45


🏁 Script executed:

# Find the AbstractQuery class location
find packages -name "*.ts" -type f | xargs grep -l "class AbstractQuery"

Repository: sequelize/sequelize

Length of output: 474


🏁 Script executed:

# Check what's in the core packages
ls -la packages/core/src/

Repository: sequelize/sequelize

Length of output: 2665


🏁 Script executed:

# Look for how other dialects handle this
fd -t f "query.ts" packages/*/src/

Repository: sequelize/sequelize

Length of output: 137


🏁 Script executed:

# Check the git history or comments in the firebird query to understand design
head -n 50 packages/firebird/src/query.ts

Repository: sequelize/sequelize

Length of output: 1551


🏁 Script executed:

# Look at AbstractQuery implementation
head -n 200 packages/core/src/abstract-dialect/query.ts

Repository: sequelize/sequelize

Length of output: 7317


🏁 Script executed:

# Search for how other dialects handle INSERT/UPDATE/DELETE responses
for file in packages/*/src/query.ts; do
  if [ -f "$file" ]; then
    echo "=== $file ==="
    grep -A 10 "isInsertQuery()" "$file" 2>/dev/null || true
  fi
done

Repository: sequelize/sequelize

Length of output: 310


🏁 Script executed:

# Look for test files that might show expected behavior
find packages/firebird -name "*.test.*" -o -name "*.spec.*"

Repository: sequelize/sequelize

Length of output: 45


🏁 Script executed:

# Check if RETURNING is used anywhere in firebird dialect
rg -n "RETURNING" packages/firebird/

Repository: sequelize/sequelize

Length of output: 142


🏁 Script executed:

# Check the INSERT handling - look for RETURNING usage
rg -n "INSERT" packages/firebird/src/ -A 3 -B 3

Repository: sequelize/sequelize

Length of output: 2414


🏁 Script executed:

# Look at dialect.ts to understand the RETURNING limitation better
cat -n packages/firebird/src/dialect.ts | sed -n '40,80p'

Repository: sequelize/sequelize

Length of output: 1401


🏁 Script executed:

# Check what the query generator produces for UPDATE/DELETE
rg -n "UPDATE|DELETE" packages/firebird/src/query-generator.ts -A 2 -B 2

Repository: sequelize/sequelize

Length of output: 443


🏁 Script executed:

# Look for test expectations or integration tests
find packages/firebird -type f \( -name "*.test.*" -o -name "*.spec.*" \) -exec ls -la {} \;

Repository: sequelize/sequelize

Length of output: 45


🏁 Script executed:

# Check if RETURNING is used in INSERT queries
rg -n "RETURNING" packages/firebird/src/query-generator.ts -B 5 -A 5

Repository: sequelize/sequelize

Length of output: 45


🏁 Script executed:

# Look for INSERT query generation
rg -n "insertQuery" packages/firebird/src/ -B 3 -A 10

Repository: sequelize/sequelize

Length of output: 45


🏁 Script executed:

# Check how parent class handles INSERT RETURNING
rg -n "RETURNING" packages/core/src/abstract-dialect/query-generator.ts -B 3 -A 3 | head -60

Repository: sequelize/sequelize

Length of output: 156


🏁 Script executed:

# Understand the actual impact: check if bulk insert is even possible
rg -n "bulkInsert" packages/firebird/src/ -B 2 -A 5

Repository: sequelize/sequelize

Length of output: 45


🏁 Script executed:

# List files in abstract-dialect
ls -la packages/core/src/abstract-dialect/

Repository: sequelize/sequelize

Length of output: 1572


🏁 Script executed:

# Search for RETURNING in all TypeScript files
rg -t ts "RETURNING" packages/ | head -30

Repository: sequelize/sequelize

Length of output: 4532


🏁 Script executed:

# Check how INSERT queries are generated in Firebird
fd -t f "query-generator" packages/firebird/

Repository: sequelize/sequelize

Length of output: 153


🏁 Script executed:

# Look for return value handling in INSERT
rg -n "handleInsertQuery\|insertQuery" packages/firebird/src/query.ts -B 5 -A 10

Repository: sequelize/sequelize

Length of output: 45


🏁 Script executed:

# Look at the Firebird query-generator files
cat packages/firebird/src/query-generator.ts | head -100

Repository: sequelize/sequelize

Length of output: 3657


🏁 Script executed:

# Check if Firebird query generator has custom INSERT handling
rg -n "insertQuery\|INSERT" packages/firebird/src/query-generator.ts -B 2 -A 5

Repository: sequelize/sequelize

Length of output: 45


🏁 Script executed:

# Look at Firebird query generator internal
cat packages/firebird/src/query-generator.internal.ts

Repository: sequelize/sequelize

Length of output: 1137


🏁 Script executed:

# Check what returnValues means in the dialect
rg -n "returnValues" packages/core/src/abstract-dialect/ -B 2 -A 2

Repository: sequelize/sequelize

Length of output: 7442


🏁 Script executed:

# Check if bulk insert is actually a feature being used
rg -n "bulk" packages/firebird/src/ -i -B 2 -A 2

Repository: sequelize/sequelize

Length of output: 45


🏁 Script executed:

# Look at how INSERT queries are actually constructed
rg -n "INSERT INTO" packages/core/src/abstract-dialect/ -B 2 -A 5 | head -50

Repository: sequelize/sequelize

Length of output: 45


🏁 Script executed:

# Check if the query.ts response handler receives RETURNING results for UPDATE/DELETE
rg -n "UPDATE.*RETURNING\|DELETE.*RETURNING" packages/firebird/src/ -i

Repository: sequelize/sequelize

Length of output: 45


🏁 Script executed:

# Understand: does Firebird return affected rows somehow without RETURNING?
rg -n "node-firebird" packages/firebird/ -B 2 -A 2

Repository: sequelize/sequelize

Length of output: 4259


🏁 Script executed:

# Check if there are any tests or integration tests that show expected behavior
find packages/firebird -type f -name "*.test.*" -o -name "*.spec.*" 2>/dev/null

Repository: sequelize/sequelize

Length of output: 45


🏁 Script executed:

# Check if maybe affected rows are passed in some other way
rg -n "affected|count" packages/firebird/src/query.ts -i

Repository: sequelize/sequelize

Length of output: 45


🏁 Script executed:

# Look at the actual result structure from node-firebird
rg -n "conn.query\|result" packages/firebird/src/query.ts -B 3 -A 3

Repository: sequelize/sequelize

Length of output: 45


🏁 Script executed:

# Check if there's documentation or comments about the known limitation
cat packages/firebird/readme.md

Repository: sequelize/sequelize

Length of output: 3225


Affected row counts are inaccurate due to Firebird driver limitations.

The current implementation has two issues:

  1. Insert queries: Returns hardcoded 1 regardless of actual rows inserted, preventing bulk inserts from reporting correct counts.
  2. Update/delete queries: Returns rows.length which is always 0 because Firebird doesn't support the RETURNING clause for UPDATE and DELETE statements—only for INSERT.

Firebird provides ROW_COUNT within EXECUTE BLOCK to retrieve actual affected row counts. Consider refactoring DML queries to wrap them in EXECUTE BLOCK and use ROW_COUNT (as documented in Firebird's row count context variable) to return accurate counts for all DML operations.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/firebird/src/query.ts` around lines 54 - 70, The current DML
handling in the block using isInsertQuery(), isUpdateQuery(), isDeleteQuery()
returns hardcoded/incorrect counts; wrap INSERT/UPDATE/DELETE executions in an
EXECUTE BLOCK that captures ROW_COUNT and returns it (and keep the INSERT
RETURNING row when available) so counts reflect actual affected rows. Update the
logic in the function containing handleInsertQuery() to: for inserts, execute a
wrapped EXECUTE BLOCK that performs the INSERT (producing RETURNING row if
needed) and returns ROW_COUNT alongside the returned row so you can return
[insertedRow, rowCount]; for updates and deletes replace the current rows.length
return by executing the DML inside an EXECUTE BLOCK that returns ROW_COUNT and
return that count (or [rows, rowCount] if your API expects rows plus count).
Ensure you reuse/extend handleInsertQuery(), and adjust callers to accept the
new rowCount value instead of the hardcoded 1 or rows.length.

Comment on lines +136 to +147
#normaliseRow(row: Record<string, unknown>): Record<string, unknown> {
const out: Record<string, unknown> = {};

for (const [k, v] of Object.entries(row)) {
out[k.toLowerCase()] = v instanceof Buffer ? v.toString('utf8') : v;
}

out.dataValues = out;
out._previousDataValues = {};

return out;
}
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.

⚠️ Potential issue | 🟠 Major

Avoid injecting dataValues and _previousDataValues in row normalization.

These properties (dataValues, _previousDataValues) are Model instance internals that should be managed by Sequelize's model hydration layer, not raw query results. Adding them here may cause issues with:

  • Raw queries expecting plain objects
  • handleSelectQuery which builds Model instances separately
  • Double-wrapping when model instances are created
🐛 Proposed fix - remove artificial dataValues injection
 `#normaliseRow`(row: Record<string, unknown>): Record<string, unknown> {
   const out: Record<string, unknown> = {};

   for (const [k, v] of Object.entries(row)) {
     out[k.toLowerCase()] = v instanceof Buffer ? v.toString('utf8') : v;
   }

-  out.dataValues = out;
-  out._previousDataValues = {};
-
   return out;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/firebird/src/query.ts` around lines 136 - 147, The `#normaliseRow`
function is incorrectly adding Model internals by setting out.dataValues and
out._previousDataValues; remove those two assignments so the function only
lowercases keys and converts Buffers to UTF-8 strings, returning a plain object,
and ensure any code calling `#normaliseRow` relies on Sequelize's hydration (or
uses the existing handleSelectQuery) to create Model instances rather than
expecting these injected properties.

@Juank2001 Juank2001 changed the title feat(firebird): add Firebird dialect support to Sequelize feat(core): add Firebird dialect support to Sequelize Mar 11, 2026
Copy link
Copy Markdown
Contributor

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/pr-title.yml:
- Line 44: Update the misspelled scope string "firebase" to "firebird" in the PR
title validation workflow; locate the literal "firebase" token in the pr-title
workflow (the scope list or regex entry) and replace it with "firebird" so
Firebird dialect PRs validate correctly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a56a25ce-c841-4671-bb88-db84c7d23f27

📥 Commits

Reviewing files that changed from the base of the PR and between 1cf694f and 7b3f953.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (1)
  • .github/workflows/pr-title.yml

Comment thread .github/workflows/pr-title.yml Outdated
sqlite3
utils
validator.js
firebase
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.

⚠️ Potential issue | 🔴 Critical

Typo: firebase should be firebird.

The PR adds Firebird dialect support, but the scope is misspelled as "firebase" (Google's mobile platform) instead of "firebird" (the SQL database). This will cause PR title validation to fail for future Firebird-related PRs.

🐛 Proposed fix
-            firebase
+            firebird
📝 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.

Suggested change
firebase
firebird
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/pr-title.yml at line 44, Update the misspelled scope
string "firebase" to "firebird" in the PR title validation workflow; locate the
literal "firebase" token in the pr-title workflow (the scope list or regex
entry) and replace it with "firebird" so Firebird dialect PRs validate
correctly.

@sequelize-bot sequelize-bot bot added the conflicted This PR has merge conflicts and will not be present in the list of PRs to review label Mar 11, 2026
@sequelize-bot sequelize-bot bot removed the conflicted This PR has merge conflicts and will not be present in the list of PRs to review label Mar 11, 2026
- Introduced Firebird dialect with connection manager, query generator, and query interface.
- Implemented Firebird-specific data types and overrides for Sequelize.
- Added support for Firebird's SQL syntax differences, including handling of LIMIT/OFFSET, auto-increment via sequences, and BLOB handling.
- Created necessary configuration files and TypeScript definitions for the Firebird package.
- Updated GitHub workflow to include Firebase in the PR title check.
Copy link
Copy Markdown
Contributor

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/core/src/sequelize-typescript.ts (1)

199-212: ⚠️ Potential issue | 🔴 Critical

Add the Firebird import mapping too.

SUPPORTED_DIALECTS now accepts 'firebird', but packages/core/src/sequelize.internals.ts still has no case 'firebird' in importDialect(). new Sequelize({ dialect: 'firebird', ... }) will throw at runtime instead of loading @sequelize/firebird.

Suggested fix in `packages/core/src/sequelize.internals.ts`
     case 'oracle':
       // eslint-disable-next-line import/no-extraneous-dependencies -- legacy function, will be removed. User needs to install the dependency themselves
       return require('@sequelize/oracle').OracleDialect;
+    case 'firebird':
+      // eslint-disable-next-line import/no-extraneous-dependencies -- legacy function, will be removed. User needs to install the dependency themselves
+      return require('@sequelize/firebird').FirebirdDialect;
     default:
       throw new Error(
-        `The dialect ${dialect} is not natively supported. Native dialects: mariadb, mssql, mysql, postgres, sqlite3, ibmi, db2, oracle and snowflake.`,
+        `The dialect ${dialect} is not natively supported. Native dialects: mariadb, mssql, mysql, postgres, sqlite3, ibmi, db2, oracle, snowflake and firebird.`,
       );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/sequelize-typescript.ts` around lines 199 - 212,
SUPPORTED_DIALECTS now includes 'firebird' but importDialect() in
sequelize.internals.ts lacks a case for it; add a 'firebird' branch in the
importDialect function to return the module name '@sequelize/firebird' (same
pattern used for other dialects like 'postgres'/'mysql'), and ensure the import
mapping follows the existing switch/case or mapping structure so new Sequelize({
dialect: 'firebird', ... }) loads the `@sequelize/firebird` package at runtime
without throwing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/firebird/readme.md`:
- Line 67: Update the documented filename casing to match the actual file:
change the listed entry `README.md` to `readme.md` in the package tree shown in
packages/firebird/readme.md so the displayed structure matches the real filename
on case-sensitive filesystems; locate the package tree string (the line
containing "├── README.md") and replace it with "├── readme.md".
- Around line 63-79: The README's fenced package-tree block is unlabeled and
triggers MD040; edit the README's package-tree code fence (the block containing
the packages/firebird/ tree) to add a language identifier (e.g. ```text)
immediately after the opening backticks so the block becomes a labeled code
fence like ```text.

In `@packages/firebird/src/dialect.ts`:
- Around line 53-55: The dialect currently sets supports.returnValues =
'returning' which wrongly signals RETURNING support for INSERT/UPDATE/DELETE;
change the Firebird dialect to only advertise INSERT RETURNING (e.g., set
supports.returnValues to 'insert' or false) and/or add explicit overrides in the
Firebird query generator: implement updateQuery() and deleteQuery() in the
Firebird query generator to prevent appending RETURNING clauses (or strip them)
so only INSERT uses RETURNING; update references in dialect.ts
(supports.returnValues) and the Firebird query generator methods
updateQuery()/deleteQuery() accordingly.
- Around line 95-98: The truncate dialect metadata currently only sets
restartIdentity; add an explicit declaration that cascade is unsupported by
setting truncate.cascade to false (or marking it unsupported) so calls that pass
{ cascade: true } are rejected instead of silently falling back in
packages/firebird/src/query-generator.ts; update the truncate object in
dialect.ts (the truncate property) to include cascade: false and ensure any
validation or type hinting reflects this change.

In `@packages/firebird/src/query-interface.ts`:
- Around line 1-9: The FirebirdQueryInterface subclass is empty so
Firebird-specific DDL helpers (createSequenceQuery,
createAutoIncrementTriggerQuery) are never invoked; override createTable and
addColumn on the FirebirdQueryInterface class to detect autoIncrement columns
and call the helpers (createSequenceQuery and createAutoIncrementTriggerQuery)
after delegating to the parent via super.createTable / super.addColumn, ensuring
you preserve returned results and only run the sequence/trigger creation for
columns with autoIncrement metadata; add any necessary ordering (create sequence
before trigger) and error handling consistent with the project patterns.

In `@packages/firebird/src/query.ts`:
- Around line 16-18: getInsertIdField() currently returns the hardcoded string
'id' which fails for tables whose generated key uses a different column name;
change getInsertIdField() (in packages/firebird/src/query.ts) to derive the
actual generated-key column name instead of hardcoding it — e.g. read and return
the generated key name captured from the insert response/driver metadata (the
value stored when executing the INSERT with RETURNING or from the insert result
object like generatedKeys/insertIdName) or fall back to the model's primaryKey
property if available; update any code that calls getInsertIdField() to ensure
the real generated-column name produced by the insert is stored and returned.
- Around line 139-141: The loop that lower-cases keys and converts Buffer values
currently turns all Buffers into UTF-8 strings (for const [k, v] of
Object.entries(row) { out[k.toLowerCase()] = v instanceof Buffer ?
v.toString('utf8') : v; }), which corrupts binary BLOB/OCTETS; change this to
only call v.toString('utf8') when the read option for text BLOBs (e.g.
blobAsText) is set, otherwise leave Buffer values intact—update the
row-processing block that writes into out (and any surrounding function that
receives the blobAsText flag) to check the blobAsText flag before converting
Buffers to strings.

---

Outside diff comments:
In `@packages/core/src/sequelize-typescript.ts`:
- Around line 199-212: SUPPORTED_DIALECTS now includes 'firebird' but
importDialect() in sequelize.internals.ts lacks a case for it; add a 'firebird'
branch in the importDialect function to return the module name
'@sequelize/firebird' (same pattern used for other dialects like
'postgres'/'mysql'), and ensure the import mapping follows the existing
switch/case or mapping structure so new Sequelize({ dialect: 'firebird', ... })
loads the `@sequelize/firebird` package at runtime without throwing.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f65e48ad-811f-49e0-af9e-9e6be7c5506d

📥 Commits

Reviewing files that changed from the base of the PR and between 7b3f953 and e356c75.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (18)
  • .github/workflows/pr-title.yml
  • packages/core/src/sequelize-typescript.ts
  • packages/core/src/sequelize.js
  • packages/core/src/utils/dialect.ts
  • packages/firebird/.eslintrc.js
  • packages/firebird/package.json
  • packages/firebird/readme.md
  • packages/firebird/src/_internal/data-types-overrides.ts
  • packages/firebird/src/connection-manager.ts
  • packages/firebird/src/dialect.ts
  • packages/firebird/src/index.ts
  • packages/firebird/src/query-generator.internal.ts
  • packages/firebird/src/query-generator.ts
  • packages/firebird/src/query-interface.ts
  • packages/firebird/src/query-interface.types.ts
  • packages/firebird/src/query.ts
  • packages/firebird/tsconfig.json
  • packages/firebird/typedoc.json
✅ Files skipped from review due to trivial changes (1)
  • packages/firebird/src/index.ts
🚧 Files skipped from review as they are similar to previous changes (8)
  • .github/workflows/pr-title.yml
  • packages/firebird/.eslintrc.js
  • packages/firebird/tsconfig.json
  • packages/firebird/package.json
  • packages/firebird/typedoc.json
  • packages/firebird/src/query-interface.types.ts
  • packages/core/src/utils/dialect.ts
  • packages/core/src/sequelize.js

Comment thread packages/firebird/readme.md Outdated
Comment on lines +63 to +79
```
packages/firebird/
├── package.json
├── tsconfig.json
├── README.md
└── src/
├── index.ts ← barrel export
├── index.mjs ← ESM re-export
├── dialect.ts ← FirebirdDialect class
├── connection-manager.ts ← connect / disconnect via node-firebird
├── query-generator.ts ← SQL builder (FIRST/SKIP, sequences…)
├── query-interface.ts ← DDL: createTable, describeTable, etc.
├── query-interface.types.ts ← Public types
├── query.ts ← Query executor + result mapping
└── _internal/
└── data-types-overrides.ts ← Firebird type mappings
```
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.

⚠️ Potential issue | 🟡 Minor

Add a language to the fenced package-tree block.

This block is currently unlabeled, so it trips MD040 and will keep Markdown lint noisy.

Suggested fix
-```
+```text
 packages/firebird/
 ├── package.json
 ├── tsconfig.json
 ├── README.md
 └── src/
     ├── index.ts                        ← barrel export
     ├── index.mjs                       ← ESM re-export
     ├── dialect.ts                      ← FirebirdDialect class
     ├── connection-manager.ts           ← connect / disconnect via node-firebird
     ├── query-generator.ts              ← SQL builder (FIRST/SKIP, sequences…)
     ├── query-interface.ts              ← DDL: createTable, describeTable, etc.
     ├── query-interface.types.ts        ← Public types
     ├── query.ts                        ← Query executor + result mapping
     └── _internal/
         └── data-types-overrides.ts     ← Firebird type mappings
</details>

<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **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.

```suggestion

🧰 Tools
🪛 markdownlint-cli2 (0.21.0)

[warning] 63-63: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/firebird/readme.md` around lines 63 - 79, The README's fenced
package-tree block is unlabeled and triggers MD040; edit the README's
package-tree code fence (the block containing the packages/firebird/ tree) to
add a language identifier (e.g. ```text) immediately after the opening backticks
so the block becomes a labeled code fence like ```text.

Comment thread packages/firebird/readme.md Outdated
packages/firebird/
├── package.json
├── tsconfig.json
├── README.md
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.

⚠️ Potential issue | 🟡 Minor

Match the documented filename casing to the actual file.

Line 67 lists README.md, but this file is packages/firebird/readme.md. That mismatch makes the package structure inaccurate on case-sensitive filesystems.

Suggested fix
-├── README.md
+├── readme.md
📝 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.

Suggested change
├── README.md
├── readme.md
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/firebird/readme.md` at line 67, Update the documented filename
casing to match the actual file: change the listed entry `README.md` to
`readme.md` in the package tree shown in packages/firebird/readme.md so the
displayed structure matches the real filename on case-sensitive filesystems;
locate the package tree string (the line containing "├── README.md") and replace
it with "├── readme.md".

Comment on lines +53 to +55
// No native RETURNING except on INSERT
returnValues: 'returning',
inserts: {
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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, check the exact context in the Firebird dialect file
head -70 packages/firebird/src/dialect.ts | tail -30

Repository: sequelize/sequelize

Length of output: 885


🏁 Script executed:

# Search for how returnValues is used in query generators
rg -A 3 -B 3 'returnValues' --type ts packages/*/src/query-generator.ts | head -100

Repository: sequelize/sequelize

Length of output: 45


🏁 Script executed:

# Check the Firebird query generator for RETURNING handling
rg -A 5 'RETURNING' packages/firebird/src/query-generator.ts

Repository: sequelize/sequelize

Length of output: 45


🏁 Script executed:

# Look for the supports object definition pattern across dialects to understand the returnValues contract
rg -A 2 'supports.*returnValues' packages/*/src/dialect.ts

Repository: sequelize/sequelize

Length of output: 45


🏁 Script executed:

# Search for AbstractDialect to find where returnValues is consumed
rg -A 10 'returnValues' packages/core/src/dialects/abstract/index.ts | head -50

Repository: sequelize/sequelize

Length of output: 147


🏁 Script executed:

# Look for where RETURNING clauses are generated in update/delete
rg -B 5 -A 5 'updateQuery|deleteQuery' packages/core/src/dialects/abstract/ | grep -A 10 -B 10 -i 'returning' | head -80

Repository: sequelize/sequelize

Length of output: 139


🏁 Script executed:

# Check if Firebird query-generator has special handling for update/destroy
cat packages/firebird/src/query-generator.ts | head -100

Repository: sequelize/sequelize

Length of output: 3657


🏁 Script executed:

# Search for how other dialects set returnValues
rg 'returnValues:' packages/*/src/dialect.ts

Repository: sequelize/sequelize

Length of output: 367


🏁 Script executed:

# Find the core package structure
find packages -name "abstract-dialect*" -o -name "query-generator*" | grep -v node_modules | head -20

Repository: sequelize/sequelize

Length of output: 1192


🏁 Script executed:

# Search for returning method/function usage
rg 'returning' packages/firebird/src/query-generator.internal.ts -A 5 -B 5

Repository: sequelize/sequelize

Length of output: 45


🏁 Script executed:

# Look at the AbstractQueryGenerator in core
rg -l 'class AbstractQueryGenerator' packages/ --type ts

Repository: sequelize/sequelize

Length of output: 246


🏁 Script executed:

# Search for where returning clause is actually added to update/delete queries
rg 'UPDATE|DELETE' packages/firebird/src/query-generator.internal.ts -A 2 | head -50

Repository: sequelize/sequelize

Length of output: 45


🏁 Script executed:

# Check if there's any specific returning handling in firebird
rg 'returning' packages/firebird/src/ -i

Repository: sequelize/sequelize

Length of output: 203


🏁 Script executed:

# Look at the core query-generator implementation for how returnValues is used
grep -n 'returnValues\|returning' packages/core/src/abstract-dialect/query-generator.js | head -30

Repository: sequelize/sequelize

Length of output: 1993


🏁 Script executed:

# Check if there's conditional logic around returning based on the supports flag
rg -B 5 -A 10 'supports.*returning|returnValues' packages/core/src/abstract-dialect/query-generator.js | head -100

Repository: sequelize/sequelize

Length of output: 3324


🏁 Script executed:

# Search for UPDATE/DELETE query building in core
rg 'updateQuery|deleteQuery|UPDATE|DELETE' packages/core/src/abstract-dialect/query-generator.js -A 3 | grep -B 3 -A 3 'returning\|RETURNING' | head -60

Repository: sequelize/sequelize

Length of output: 238


🏁 Script executed:

# Check query-generator-internal.ts for where returning is handled
cat packages/core/src/abstract-dialect/query-generator-internal.ts | head -150

Repository: sequelize/sequelize

Length of output: 5850


🏁 Script executed:

# Verify that Firebird does NOT override updateQuery or deleteQuery
rg 'updateQuery|deleteQuery' packages/firebird/src/query-generator.ts

Repository: sequelize/sequelize

Length of output: 45


🏁 Script executed:

# Verify that Firebird does NOT override updateQuery or deleteQuery in internal
rg 'updateQuery|deleteQuery' packages/firebird/src/query-generator.internal.ts

Repository: sequelize/sequelize

Length of output: 45


🏁 Script executed:

# Check what the actual return value is for returnValues - is it a string or boolean?
cat packages/firebird/src/dialect.ts | sed -n '40,80p'

Repository: sequelize/sequelize

Length of output: 1114


supports.returnValues is too broad for Firebird.

This flag controls RETURNING clause generation for INSERT, UPDATE, and DELETE operations. Setting it to 'returning' tells core generators that RETURNING is universally supported, but the adjacent comment explicitly states Firebird only supports it on INSERT. Without overrides for updateQuery() and deleteQuery() in the Firebird query generator, this will generate invalid SQL on returning UPDATE/DELETE operations.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/firebird/src/dialect.ts` around lines 53 - 55, The dialect currently
sets supports.returnValues = 'returning' which wrongly signals RETURNING support
for INSERT/UPDATE/DELETE; change the Firebird dialect to only advertise INSERT
RETURNING (e.g., set supports.returnValues to 'insert' or false) and/or add
explicit overrides in the Firebird query generator: implement updateQuery() and
deleteQuery() in the Firebird query generator to prevent appending RETURNING
clauses (or strip them) so only INSERT uses RETURNING; update references in
dialect.ts (supports.returnValues) and the Firebird query generator methods
updateQuery()/deleteQuery() accordingly.

Comment on lines +95 to +98
truncate: {
// Firebird has no TRUNCATE TABLE
restartIdentity: false,
},
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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n packages/firebird/src/dialect.ts | head -120

Repository: sequelize/sequelize

Length of output: 4498


🏁 Script executed:

fd query-generator packages/firebird/src --type f

Repository: sequelize/sequelize

Length of output: 153


🏁 Script executed:

grep -n "cascade" packages/firebird/src/query-generator.ts

Repository: sequelize/sequelize

Length of output: 45


🏁 Script executed:

# Check how other dialects handle truncate.cascade
grep -r "truncate.*cascade" packages/*/src/dialect.ts

Repository: sequelize/sequelize

Length of output: 45


🏁 Script executed:

# Read query-generator implementation for truncate
grep -A 20 "truncate" packages/firebird/src/query-generator.ts | head -50

Repository: sequelize/sequelize

Length of output: 909


🏁 Script executed:

# Check core dialect to understand truncate options
fd dialect.ts packages/core/src --type f

Repository: sequelize/sequelize

Length of output: 143


🏁 Script executed:

# Check if cascade is defined as a feature in other dialects
grep -l "truncate" packages/*/src/dialect.ts

Repository: sequelize/sequelize

Length of output: 160


🏁 Script executed:

# Check postgres dialect truncate support
sed -n '/truncate:/,/}/p' packages/postgres/src/dialect.ts | head -20

Repository: sequelize/sequelize

Length of output: 135


🏁 Script executed:

# Check sqlite3 dialect truncate support
sed -n '/truncate:/,/}/p' packages/sqlite3/src/dialect.ts | head -20

Repository: sequelize/sequelize

Length of output: 115


🏁 Script executed:

# Find TRUNCATE_TABLE_QUERY_SUPPORTABLE_OPTIONS definition
rg "TRUNCATE_TABLE_QUERY_SUPPORTABLE_OPTIONS" --type ts

Repository: sequelize/sequelize

Length of output: 2231


🏁 Script executed:

# Get the exact definition of TRUNCATE_TABLE_QUERY_SUPPORTABLE_OPTIONS
grep -A 10 "export const TRUNCATE_TABLE_QUERY_SUPPORTABLE_OPTIONS" packages/core/src/abstract-dialect/query-generator-typescript.ts

Repository: sequelize/sequelize

Length of output: 381


🏁 Script executed:

# Also check the TruncateTableQueryOptions type definition
rg "interface TruncateTableQueryOptions|type TruncateTableQueryOptions" -A 5 packages/core/src

Repository: sequelize/sequelize

Length of output: 596


🏁 Script executed:

# Check rejectInvalidOptions to understand how it validates
rg "function rejectInvalidOptions|export.*rejectInvalidOptions" -A 15 packages/core/src

Repository: sequelize/sequelize

Length of output: 1365


🏁 Script executed:

# Also verify behavior - check if other dialects declare cascade: false
grep -B 2 -A 4 "truncate:" packages/*/src/dialect.ts | grep -A 4 "cascade"

Repository: sequelize/sequelize

Length of output: 417


Declare truncate.cascade as unsupported.

packages/firebird/src/query-generator.ts falls back to plain DELETE FROM for truncate operations. Explicitly declaring cascade: false ensures that passing { cascade: true } is rejected upfront, rather than being silently accepted and ignored.

Suggested fix
     truncate: {
       // Firebird has no TRUNCATE TABLE
+      cascade: false,
       restartIdentity: false,
     },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/firebird/src/dialect.ts` around lines 95 - 98, The truncate dialect
metadata currently only sets restartIdentity; add an explicit declaration that
cascade is unsupported by setting truncate.cascade to false (or marking it
unsupported) so calls that pass { cascade: true } are rejected instead of
silently falling back in packages/firebird/src/query-generator.ts; update the
truncate object in dialect.ts (the truncate property) to include cascade: false
and ensure any validation or type hinting reflects this change.

Comment on lines +1 to +9
import { AbstractQueryInterface } from '@sequelize/core';

/**
* FirebirdQueryInterface
*
* Extends Sequelize's abstract QueryInterface with Firebird-specific DDL
* operations, most notably auto-increment via SEQUENCE + BEFORE INSERT TRIGGER.
*/
export class FirebirdQueryInterface extends AbstractQueryInterface {}
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.

⚠️ Potential issue | 🟠 Major

Wire the Firebird schema hooks into the query interface.

This subclass is empty, so the new createSequenceQuery / createAutoIncrementTriggerQuery helpers have no Firebird-specific DDL hook here. If autoIncrement is meant to be backed by a sequence + trigger, createTable/addColumn still need overrides that call them.

As per coding guidelines, packages/*/src/query-interface.{js,ts}: Add schema operations for new features in query-interface.ts (preferred) or legacy query-interface.js.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/firebird/src/query-interface.ts` around lines 1 - 9, The
FirebirdQueryInterface subclass is empty so Firebird-specific DDL helpers
(createSequenceQuery, createAutoIncrementTriggerQuery) are never invoked;
override createTable and addColumn on the FirebirdQueryInterface class to detect
autoIncrement columns and call the helpers (createSequenceQuery and
createAutoIncrementTriggerQuery) after delegating to the parent via
super.createTable / super.addColumn, ensuring you preserve returned results and
only run the sequence/trigger creation for columns with autoIncrement metadata;
add any necessary ordering (create sequence before trigger) and error handling
consistent with the project patterns.

Comment on lines +16 to +18
getInsertIdField(): string {
return 'id';
}
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.

⚠️ Potential issue | 🟠 Major

Don't hardcode 'id' as the generated key name.

This only works for models whose auto-increment column is literally id. If Firebird returns user_id, invoice_no, or any other generated column name, the created instance won't get its primary key populated after insert.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/firebird/src/query.ts` around lines 16 - 18, getInsertIdField()
currently returns the hardcoded string 'id' which fails for tables whose
generated key uses a different column name; change getInsertIdField() (in
packages/firebird/src/query.ts) to derive the actual generated-key column name
instead of hardcoding it — e.g. read and return the generated key name captured
from the insert response/driver metadata (the value stored when executing the
INSERT with RETURNING or from the insert result object like
generatedKeys/insertIdName) or fall back to the model's primaryKey property if
available; update any code that calls getInsertIdField() to ensure the real
generated-column name produced by the insert is stored and returned.

Comment on lines +139 to +141
for (const [k, v] of Object.entries(row)) {
out[k.toLowerCase()] = v instanceof Buffer ? v.toString('utf8') : v;
}
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.

⚠️ Potential issue | 🟠 Major

Keep binary columns as Buffers.

blobAsText already covers text BLOBs. The remaining Buffer values are binary payloads (DataTypes.BLOB, OCTETS columns, etc.), and converting them to UTF-8 here will corrupt the result on read.

Suggested fix
-      out[k.toLowerCase()] = v instanceof Buffer ? v.toString('utf8') : v;
+      out[k.toLowerCase()] = v;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/firebird/src/query.ts` around lines 139 - 141, The loop that
lower-cases keys and converts Buffer values currently turns all Buffers into
UTF-8 strings (for const [k, v] of Object.entries(row) { out[k.toLowerCase()] =
v instanceof Buffer ? v.toString('utf8') : v; }), which corrupts binary
BLOB/OCTETS; change this to only call v.toString('utf8') when the read option
for text BLOBs (e.g. blobAsText) is set, otherwise leave Buffer values
intact—update the row-processing block that writes into out (and any surrounding
function that receives the blobAsText flag) to check the blobAsText flag before
converting Buffers to strings.

Copy link
Copy Markdown
Contributor

@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

♻️ Duplicate comments (8)
packages/firebird/src/connection-manager.ts (1)

74-79: ⚠️ Potential issue | 🟡 Minor

Align the documented password default with the actual one.

The JSDoc says the default password is 'masterkey', but connect() falls back to ''. Pick one so configuration docs and runtime behavior match.

Also applies to: 149-150

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/firebird/src/connection-manager.ts` around lines 74 - 79, The JSDoc
for the password property currently documents a default of 'masterkey' but
connect() actually falls back to an empty string; update the documentation to
match runtime behavior by changing the JSDoc default to '' (empty string) for
the password property and any other JSDoc occurrences (see the other docblock
around the password/join lines referenced), or alternatively change the
connect() fallback to 'masterkey' if you prefer that as the real default—ensure
consistency between the password?: string JSDoc and the connect()
implementation.
packages/firebird/src/_internal/data-types-overrides.ts (1)

128-136: ⚠️ Potential issue | 🟠 Major

Override toBindableValue(), not stringify().

AbstractDataType.escape() serializes DATE values through toBindableValue(), so this Firebird-specific formatter is bypassed and the base timestamp format still leaks through. The as Date cast is also unsafe because sanitize() can return DayJS/Moment objects.

Possible fix
 export class DATE extends BaseTypes.DATE {
   toSql(): string {
     // Firebird TIMESTAMP = date + time
     return 'TIMESTAMP';
   }

-  stringify(value: AcceptedDate): string {
-    const d = this.sanitize(value) as Date;
-    // Firebird accepts: 'YYYY-MM-DD HH:MM:SS.mmm'
-
-    return (
-      `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ` +
-      `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}.${pad(d.getMilliseconds(), 3)}`
-    );
+  override toBindableValue(value: AcceptedDate): string {
+    return this._applyTimezone(value).format('YYYY-MM-DD HH:mm:ss.SSS');
   }
 }
#!/bin/bash
rg -n "escape\\(|toBindableValue\\(|stringify\\(" \
  packages/core/src/abstract-dialect/data-types.ts \
  packages/firebird/src/_internal/data-types-overrides.ts -C2
packages/firebird/src/dialect.ts (3)

53-55: ⚠️ Potential issue | 🟠 Major

supports.returnValues overstates Firebird support.

The adjacent comment says Firebird only supports RETURNING on INSERT, but 'returning' advertises generic support and lets core generate invalid UPDATE ... RETURNING / DELETE ... RETURNING SQL unless those paths are overridden.

As per coding guidelines, "When adding a dialect feature, update packages/{dialect}/src/dialect.ts to declare feature support flags".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/firebird/src/dialect.ts` around lines 53 - 55, The
supports.returnValues flag currently set to the generic 'returning' overstates
Firebird's capability and allows core to emit UPDATE/DELETE ... RETURNING;
change supports.returnValues so it only advertises RETURNING for INSERT (e.g., a
structure indicating insert: 'returning' or otherwise disable update/delete
returning) and ensure any related places (the inserts configuration and any
update/delete code paths) are adjusted so core will not generate UPDATE ...
RETURNING or DELETE ... RETURNING for the Firebird dialect.

112-115: ⚠️ Potential issue | 🟠 Major

Don't disable identifier quoting for Firebird.

Firebird supports double-quoted identifiers. Using an empty delimiter turns quoting off completely, so reserved words and case-sensitive identifiers remain unsafe even though the core helper now avoids the regex crash.

As per coding guidelines, "When adding a dialect feature, update packages/{dialect}/src/dialect.ts to declare feature support flags".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/firebird/src/dialect.ts` around lines 112 - 115, The constructor in
this Firebird dialect currently disables identifier quoting by setting
identifierDelimiter: '', which is incorrect—change identifierDelimiter to '"'
(double-quote) so Firebird double-quoted identifiers remain supported, and
update the dialect feature flags in the same file (where Firebird dialect
features are declared) to explicitly mark identifier quoting/case-sensitive
identifier support per the coding guidelines; locate the constructor and the
feature flags block (references: constructor(sequelize: Sequelize, options:
FirebirdDialectOptions), identifierDelimiter) and adjust them accordingly.

95-98: ⚠️ Potential issue | 🟠 Major

Declare truncate.cascade as unsupported.

The dialect metadata should reject { cascade: true } up front instead of silently accepting it while the query generator falls back to DELETE FROM.

As per coding guidelines, "When adding a dialect feature, update packages/{dialect}/src/dialect.ts to declare feature support flags".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/firebird/src/dialect.ts` around lines 95 - 98, The truncate metadata
for the Firebird dialect currently only sets restartIdentity and must explicitly
mark cascade as unsupported; update the truncate object in
packages/firebird/src/dialect.ts (the truncate metadata) to include cascade:
false (or the dialect's unsupported flag) so the dialect rejects { cascade: true
} up front instead of silently accepting it and letting the query generator
fallback to DELETE FROM.
packages/firebird/src/query.ts (3)

16-18: ⚠️ Potential issue | 🟠 Major

Don't hardcode 'id' as the generated key name.

This only works for models whose auto-increment column is literally id. Inserts returning user_id, invoice_no, or any other generated column will not hydrate the instance primary key correctly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/firebird/src/query.ts` around lines 16 - 18, Replace the hardcoded
return in getInsertIdField() so it reads the model/table metadata for the
auto-generated primary key name instead of always returning 'id' — for example,
return the configured primary key from this.model (e.g. this.model.primaryKey or
this.model.getPrimaryKeyName() or this.model.meta.primaryKey) and only fall back
to 'id' if no metadata exists; update getInsertIdField() to use that metadata
path so inserts returning user_id, invoice_no, etc. hydrate correctly.

136-145: ⚠️ Potential issue | 🟠 Major

Keep normalized rows plain and lossless.

This block currently forces every key to lowercase, ignores the lowercase_keys connection option, converts every Buffer to UTF-8, and injects dataValues / _previousDataValues into raw results. That breaks case-sensitive aliases, corrupts binary BLOB/OCTETS data, and leaks model internals into rows that should stay plain.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/firebird/src/query.ts` around lines 136 - 145, The `#normaliseRow`
function currently mutates raw rows by forcing all keys to lowercase,
unconditionally converting Buffer values to UTF-8 strings, and injecting
dataValues / _previousDataValues; update it to keep rows plain and lossless:
respect the connection option lowercase_keys (only lower-case keys if that
option is enabled), preserve Buffer/Uint8Array/BLOB types instead of converting
them to strings, and remove the insertion of dataValues and _previousDataValues
so the method returns an untouched record with only the normalized key
transformation applied when enabled; locate the method named `#normaliseRow` to
implement these changes.

53-69: ⚠️ Potential issue | 🟠 Major

Affected-row counts are incorrect here.

Line 58/61 always reports insert count 1, and Lines 65/69 derive update/delete counts from returned rows even though those statements do not yield row sets in this dialect. That will break Sequelize's update, destroy, and bulk insert result contracts.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/firebird/src/query.ts` around lines 53 - 69, The `#handleResponse`
currently returns a hardcoded insert count of 1 and derives update/delete counts
from returned row arrays, which is incorrect for this dialect; change
`#handleResponse` (and related call to handleInsertQuery) so that for inserts you
return the inserted row(s) and the actual insert count (use rows.length, not 1),
and for update/delete do NOT use rows.length but instead extract the
affected-row count from the protocol/metadata (e.g. this.affectedRows or a new
helper like getAffectedCountFromResponse()) and return [rowsOrUndefined,
affectedCount]; update isInsertQuery/isUpdateQuery/isDeleteQuery branches to use
those real counts and add a small helper on the class to normalize metadata ->
affected count if needed (refer to `#handleResponse`, handleInsertQuery,
isInsertQuery, isUpdateQuery, isDeleteQuery).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/firebird/src/_internal/data-types-overrides.ts`:
- Line 170: The class currently declared as JSON shadows the global JSON and
triggers lint errors; rename the export to a non-conflicting identifier (e.g.,
JSONTYPE or JSONType) by changing the class declaration/export from JSON to
JSONTYPE and update any local references to this class within the overrides
collection so Object.values(overrides) and getDataTypeId() still find the
override via its base type (leave BaseTypes.JSON unchanged). Ensure the exported
symbol name is updated wherever this module's overrides are registered/consumed
so the dialect matching logic continues to work.

In `@packages/firebird/src/query.ts`:
- Line 14: The class currently narrows the wrong property: replace the incorrect
declaration of connectionManager with the parent property name by declaring the
connection field as the specific type; change the declaration to use
"connection: FirebirdConnection" (i.e., update the class to use declare
connection: FirebirdConnection) so AbstractQuery's connection is properly
narrowed to FirebirdConnection.

---

Duplicate comments:
In `@packages/firebird/src/connection-manager.ts`:
- Around line 74-79: The JSDoc for the password property currently documents a
default of 'masterkey' but connect() actually falls back to an empty string;
update the documentation to match runtime behavior by changing the JSDoc default
to '' (empty string) for the password property and any other JSDoc occurrences
(see the other docblock around the password/join lines referenced), or
alternatively change the connect() fallback to 'masterkey' if you prefer that as
the real default—ensure consistency between the password?: string JSDoc and the
connect() implementation.

In `@packages/firebird/src/dialect.ts`:
- Around line 53-55: The supports.returnValues flag currently set to the generic
'returning' overstates Firebird's capability and allows core to emit
UPDATE/DELETE ... RETURNING; change supports.returnValues so it only advertises
RETURNING for INSERT (e.g., a structure indicating insert: 'returning' or
otherwise disable update/delete returning) and ensure any related places (the
inserts configuration and any update/delete code paths) are adjusted so core
will not generate UPDATE ... RETURNING or DELETE ... RETURNING for the Firebird
dialect.
- Around line 112-115: The constructor in this Firebird dialect currently
disables identifier quoting by setting identifierDelimiter: '', which is
incorrect—change identifierDelimiter to '"' (double-quote) so Firebird
double-quoted identifiers remain supported, and update the dialect feature flags
in the same file (where Firebird dialect features are declared) to explicitly
mark identifier quoting/case-sensitive identifier support per the coding
guidelines; locate the constructor and the feature flags block (references:
constructor(sequelize: Sequelize, options: FirebirdDialectOptions),
identifierDelimiter) and adjust them accordingly.
- Around line 95-98: The truncate metadata for the Firebird dialect currently
only sets restartIdentity and must explicitly mark cascade as unsupported;
update the truncate object in packages/firebird/src/dialect.ts (the truncate
metadata) to include cascade: false (or the dialect's unsupported flag) so the
dialect rejects { cascade: true } up front instead of silently accepting it and
letting the query generator fallback to DELETE FROM.

In `@packages/firebird/src/query.ts`:
- Around line 16-18: Replace the hardcoded return in getInsertIdField() so it
reads the model/table metadata for the auto-generated primary key name instead
of always returning 'id' — for example, return the configured primary key from
this.model (e.g. this.model.primaryKey or this.model.getPrimaryKeyName() or
this.model.meta.primaryKey) and only fall back to 'id' if no metadata exists;
update getInsertIdField() to use that metadata path so inserts returning
user_id, invoice_no, etc. hydrate correctly.
- Around line 136-145: The `#normaliseRow` function currently mutates raw rows by
forcing all keys to lowercase, unconditionally converting Buffer values to UTF-8
strings, and injecting dataValues / _previousDataValues; update it to keep rows
plain and lossless: respect the connection option lowercase_keys (only
lower-case keys if that option is enabled), preserve Buffer/Uint8Array/BLOB
types instead of converting them to strings, and remove the insertion of
dataValues and _previousDataValues so the method returns an untouched record
with only the normalized key transformation applied when enabled; locate the
method named `#normaliseRow` to implement these changes.
- Around line 53-69: The `#handleResponse` currently returns a hardcoded insert
count of 1 and derives update/delete counts from returned row arrays, which is
incorrect for this dialect; change `#handleResponse` (and related call to
handleInsertQuery) so that for inserts you return the inserted row(s) and the
actual insert count (use rows.length, not 1), and for update/delete do NOT use
rows.length but instead extract the affected-row count from the
protocol/metadata (e.g. this.affectedRows or a new helper like
getAffectedCountFromResponse()) and return [rowsOrUndefined, affectedCount];
update isInsertQuery/isUpdateQuery/isDeleteQuery branches to use those real
counts and add a small helper on the class to normalize metadata -> affected
count if needed (refer to `#handleResponse`, handleInsertQuery, isInsertQuery,
isUpdateQuery, isDeleteQuery).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d162f3bf-cf30-45d0-94c5-038361dcb11c

📥 Commits

Reviewing files that changed from the base of the PR and between e356c75 and b599c01.

📒 Files selected for processing (18)
  • .github/workflows/pr-title.yml
  • packages/core/src/sequelize-typescript.ts
  • packages/core/src/sequelize.js
  • packages/core/src/utils/dialect.ts
  • packages/firebird/.eslintrc.js
  • packages/firebird/package.json
  • packages/firebird/readme.md
  • packages/firebird/src/_internal/data-types-overrides.ts
  • packages/firebird/src/connection-manager.ts
  • packages/firebird/src/dialect.ts
  • packages/firebird/src/index.ts
  • packages/firebird/src/query-generator.internal.ts
  • packages/firebird/src/query-generator.ts
  • packages/firebird/src/query-interface.ts
  • packages/firebird/src/query-interface.types.ts
  • packages/firebird/src/query.ts
  • packages/firebird/tsconfig.json
  • packages/firebird/typedoc.json
✅ Files skipped from review due to trivial changes (1)
  • packages/firebird/readme.md
🚧 Files skipped from review as they are similar to previous changes (10)
  • packages/firebird/.eslintrc.js
  • packages/firebird/src/query-interface.types.ts
  • packages/firebird/src/query-generator.internal.ts
  • packages/firebird/package.json
  • packages/firebird/src/query-interface.ts
  • packages/firebird/typedoc.json
  • packages/core/src/sequelize-typescript.ts
  • packages/core/src/sequelize.js
  • packages/firebird/src/query-generator.ts
  • packages/firebird/tsconfig.json


// ── JSON ──────────────────────────────────────────────────────────────────────

export class JSON extends BaseTypes.JSON {
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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Check if the file exists and view the relevant section
find packages/firebird -name "data-types-overrides.ts" -type f

Repository: sequelize/sequelize

Length of output: 118


🏁 Script executed:

# If file exists, check its content around line 170
if [ -f "packages/firebird/src/_internal/data-types-overrides.ts" ]; then
  wc -l "packages/firebird/src/_internal/data-types-overrides.ts"
fi

Repository: sequelize/sequelize

Length of output: 122


🏁 Script executed:

# Also search for data-types-overrides in firebird package
find packages -path "*firebird*" -name "*data-types*" -type f

Repository: sequelize/sequelize

Length of output: 118


🏁 Script executed:

# Read the file to see the JSON class definition and surrounding context
cat -n "packages/firebird/src/_internal/data-types-overrides.ts"

Repository: sequelize/sequelize

Length of output: 5523


🏁 Script executed:

# Find the firebird dialect.ts file to understand how overrides are registered
find packages/firebird -name "dialect.ts" -type f

Repository: sequelize/sequelize

Length of output: 95


🏁 Script executed:

# Search for how dataTypes overrides are used in firebird
rg "data-types-overrides" packages/firebird/src/ -A 3 -B 3

Repository: sequelize/sequelize

Length of output: 796


🏁 Script executed:

# Search for any references to JSON class in the firebird dialect
rg "\.JSON" packages/firebird/src/ -A 2 -B 2

Repository: sequelize/sequelize

Length of output: 558


🏁 Script executed:

# Read the firebird dialect.ts to understand how overrides are registered
cat -n "packages/firebird/src/dialect.ts" | head -100

Repository: sequelize/sequelize

Length of output: 3795


🏁 Script executed:

# Search for Object.values in the dialect or how DataTypes is used
rg "Object.values|DataTypes" packages/firebird/src/dialect.ts -B 2 -A 2

Repository: sequelize/sequelize

Length of output: 592


🏁 Script executed:

# Search for how dataTypeOverrides is used in AbstractDialect
rg "dataTypeOverrides" packages/core/src/_non-semver-use-at-your-own-risk_/abstract-dialect/ -B 2 -A 5

Repository: sequelize/sequelize

Length of output: 172


🏁 Script executed:

# Also check the core dialect to see the pattern
cat -n packages/core/src/_non-semver-use-at-your-own-risk_/abstract-dialect/dialect.ts | head -150

Repository: sequelize/sequelize

Length of output: 174


🏁 Script executed:

# Find the core package structure
find packages -name "abstract-dialect" -type d

Repository: sequelize/sequelize

Length of output: 97


🏁 Script executed:

# Search for AbstractDialect class definition
rg "class AbstractDialect" packages/ -B 2 -A 10

Repository: sequelize/sequelize

Length of output: 994


🏁 Script executed:

# Search for dataTypeOverrides in the dialect.ts file
rg "dataTypeOverrides" packages/core/src/abstract-dialect/dialect.ts -B 5 -A 10

Repository: sequelize/sequelize

Length of output: 2840


🏁 Script executed:

# Also look for Object.values in the dialect implementation
rg "Object.values" packages/core/src/abstract-dialect/ -B 3 -A 3

Repository: sequelize/sequelize

Length of output: 3103


🏁 Script executed:

# Search for getDataTypeId implementation to understand how it works
rg "getDataTypeId" packages/core/src/abstract-dialect/ -B 2 -A 5

Repository: sequelize/sequelize

Length of output: 11343


🏁 Script executed:

# Check the BaseTypes.JSON to see what its dataTypeId would be
rg "class JSON extends" packages/core/src/abstract-dialect/data-types.ts -B 2 -A 10

Repository: sequelize/sequelize

Length of output: 418


Rename this override to avoid the lint error.

Biome is correctly flagging JSON for shadowing the global JSON object. The dialect uses Object.values() to iterate through overrides and then calls getDataTypeId() to match them with their base types—the export name has no impact on this matching mechanism. Renaming to JSONTYPE or similar is safe.

Suggested fix
-export class JSON extends BaseTypes.JSON {
+export class JSONTYPE extends BaseTypes.JSON {
   toSql(): string {
     // Firebird has no native JSON type; store as BLOB SUB_TYPE TEXT.
     return 'BLOB SUB_TYPE TEXT';
   }
 }
📝 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.

Suggested change
export class JSON extends BaseTypes.JSON {
export class JSONTYPE extends BaseTypes.JSON {
toSql(): string {
// Firebird has no native JSON type; store as BLOB SUB_TYPE TEXT.
return 'BLOB SUB_TYPE TEXT';
}
}
🧰 Tools
🪛 Biome (2.4.6)

[error] 170-170: Do not shadow the global "JSON" property.

(lint/suspicious/noShadowRestrictedNames)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/firebird/src/_internal/data-types-overrides.ts` at line 170, The
class currently declared as JSON shadows the global JSON and triggers lint
errors; rename the export to a non-conflicting identifier (e.g., JSONTYPE or
JSONType) by changing the class declaration/export from JSON to JSONTYPE and
update any local references to this class within the overrides collection so
Object.values(overrides) and getDataTypeId() still find the override via its
base type (leave BaseTypes.JSON unchanged). Ensure the exported symbol name is
updated wherever this module's overrides are registered/consumed so the dialect
matching logic continues to work.

* query interface and maps raw results to Sequelize's expected shapes.
*/
export class FirebirdQuery extends AbstractQuery {
declare connectionManager: FirebirdConnection;
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.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n "class AbstractQuery|connectionManager|connection:" \
  packages/core/src/abstract-dialect/query.ts \
  packages/firebird/src/query.ts -C2

Repository: sequelize/sequelize

Length of output: 1320


connectionManager is the wrong field to narrow here.

The parent class AbstractQuery declares the property as connection: AbstractConnection, not connectionManager. To properly narrow its type to FirebirdConnection, use declare connection: FirebirdConnection.

Fix
 export class FirebirdQuery extends AbstractQuery {
-  declare connectionManager: FirebirdConnection;
+  declare connection: FirebirdConnection;
📝 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.

Suggested change
declare connectionManager: FirebirdConnection;
declare connection: FirebirdConnection;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/firebird/src/query.ts` at line 14, The class currently narrows the
wrong property: replace the incorrect declaration of connectionManager with the
parent property name by declaring the connection field as the specific type;
change the declaration to use "connection: FirebirdConnection" (i.e., update the
class to use declare connection: FirebirdConnection) so AbstractQuery's
connection is properly narrowed to FirebirdConnection.

Copy link
Copy Markdown
Contributor

@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/firebird/src/connection-manager.ts (2)

199-233: Consider importing error classes at the top instead of dynamic requires.

The repeated require('@sequelize/core') calls inside #translateError are inefficient and deviate from the ES module import pattern used elsewhere in the file. Since ConnectionError is already imported, the other error classes can be imported alongside it.

Suggested refactor
-import { AbstractConnectionManager, ConnectionError } from '@sequelize/core';
+import {
+  AbstractConnectionManager,
+  ConnectionError,
+  ConnectionRefusedError,
+  AccessDeniedError,
+  HostNotFoundError,
+  HostNotReachableError,
+  InvalidConnectionError,
+} from '@sequelize/core';
...
   `#translateError`(err: Error & { gdscode?: number }): ConnectionError {
     const msg = err.message ?? '';

     if (/connection refused/i.test(msg)) {
-      const { ConnectionRefusedError } = require('@sequelize/core');
       return new ConnectionRefusedError(err);
     }

     if (/access denied/i.test(msg) || err.gdscode === 335_544_472) {
-      const { AccessDeniedError } = require('@sequelize/core');
       return new AccessDeniedError(err);
     }
     // ... similar for other error types
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/firebird/src/connection-manager.ts` around lines 199 - 233, Replace
the dynamic require calls inside the private method `#translateError` with static
imports at the top of the module: import ConnectionRefusedError,
AccessDeniedError, HostNotFoundError, HostNotReachableError, and
InvalidConnectionError from '@sequelize/core' alongside the existing
ConnectionError import, then update the `#translateError` function to instantiate
those imported classes instead of calling require(...) so the method uses the
statically imported symbols (ConnectionRefusedError, AccessDeniedError,
HostNotFoundError, HostNotReachableError, InvalidConnectionError,
ConnectionError).

175-177: Validation only checks the closed flag, not actual connection health.

The validate() method returns true if the connection hasn't been explicitly closed, but doesn't verify the connection is still alive (e.g., server-side timeout or network drop). Consider adding a lightweight ping or query if node-firebird supports it.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/firebird/src/connection-manager.ts` around lines 175 - 177, The
validate() method currently only checks the CLOSED_SYMBOL flag; update
validate(connection: FirebirdConnection) to perform a lightweight health check
(e.g., call a ping method or run a trivial query like SELECT 1) in addition to
checking CLOSED_SYMBOL, catch any errors/timeouts and return false on failure,
and return true only when both the closed flag is false and the health check
succeeds; ensure the implementation uses the existing CLOSED_SYMBOL and handles
errors quickly (short timeout) to avoid blocking the pool.
packages/firebird/src/_internal/data-types-overrides.ts (1)

114-116: escape() may produce incorrect output for non-boolean falsy values.

The method coerces any falsy value (including 0, '', null, undefined) to 'FALSE'. Consider explicit boolean type checking for stricter correctness.

Suggested improvement
   escape(value: boolean | unknown, _options?: any): string {
-    return value ? 'TRUE' : 'FALSE';
+    return value === true ? 'TRUE' : 'FALSE';
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/firebird/src/_internal/data-types-overrides.ts` around lines 114 -
116, The escape(value: boolean | unknown, _options?: any) method currently
coerces any falsy value to 'FALSE'; change it to explicitly check boolean
identity so only true yields 'TRUE' and only false yields 'FALSE' (e.g., if
(value === true) return 'TRUE'; if (value === false) return 'FALSE';), and for
non-boolean inputs either throw a TypeError or handle them explicitly (choose
consistent behavior for non-boolean values) to avoid silently producing
incorrect SQL.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/firebird/src/query-generator.ts`:
- Around line 110-115: The generated index name is being lowercased when built
from attributes, but Firebird stores unquoted identifiers as uppercase; update
the logic that sets indexName (in the block using indexNameOrAttributes and
extractTableDetails) to not lowercase the result—use the Firebird normalization
(uppercase) or omit the .toLowerCase() call so the derived indexName matches the
catalog name used by DROP INDEX. Ensure the change is applied where indexName is
constructed from indexNameOrAttributes so DROP/CREATE use the same normalized
identifier.

---

Nitpick comments:
In `@packages/firebird/src/_internal/data-types-overrides.ts`:
- Around line 114-116: The escape(value: boolean | unknown, _options?: any)
method currently coerces any falsy value to 'FALSE'; change it to explicitly
check boolean identity so only true yields 'TRUE' and only false yields 'FALSE'
(e.g., if (value === true) return 'TRUE'; if (value === false) return 'FALSE';),
and for non-boolean inputs either throw a TypeError or handle them explicitly
(choose consistent behavior for non-boolean values) to avoid silently producing
incorrect SQL.

In `@packages/firebird/src/connection-manager.ts`:
- Around line 199-233: Replace the dynamic require calls inside the private
method `#translateError` with static imports at the top of the module: import
ConnectionRefusedError, AccessDeniedError, HostNotFoundError,
HostNotReachableError, and InvalidConnectionError from '@sequelize/core'
alongside the existing ConnectionError import, then update the `#translateError`
function to instantiate those imported classes instead of calling require(...)
so the method uses the statically imported symbols (ConnectionRefusedError,
AccessDeniedError, HostNotFoundError, HostNotReachableError,
InvalidConnectionError, ConnectionError).
- Around line 175-177: The validate() method currently only checks the
CLOSED_SYMBOL flag; update validate(connection: FirebirdConnection) to perform a
lightweight health check (e.g., call a ping method or run a trivial query like
SELECT 1) in addition to checking CLOSED_SYMBOL, catch any errors/timeouts and
return false on failure, and return true only when both the closed flag is false
and the health check succeeds; ensure the implementation uses the existing
CLOSED_SYMBOL and handles errors quickly (short timeout) to avoid blocking the
pool.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 307fcf3d-693a-49d6-a5dc-7ab7c42dfbf1

📥 Commits

Reviewing files that changed from the base of the PR and between b599c01 and c76dd1e.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (5)
  • packages/firebird/src/_internal/data-types-overrides.ts
  • packages/firebird/src/connection-manager.ts
  • packages/firebird/src/dialect.ts
  • packages/firebird/src/query-generator.ts
  • packages/mysql/package.json

Comment thread packages/firebird/src/query-generator.ts
@sequelize-bot sequelize-bot bot added the conflicted This PR has merge conflicts and will not be present in the list of PRs to review label Mar 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

conflicted This PR has merge conflicts and will not be present in the list of PRs to review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant