Skip to content

Conversation

@sudarshan12s
Copy link

@sudarshan12s sudarshan12s commented Nov 7, 2025

Pull Request Checklist

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

Description of Changes

This change fixes CI issues on top of PR #17403.

Major updates:

  • Oracle upsert:
    The Oracle dialect generates both updateQuery and insertQuery, combining them into a single PL/SQL block with binds.
    To ensure bind numbering continues correctly between the two queries, createBindParamGenerator was updated to accept the dialect parameter and continue numbering based on Object.keys(bind).length.

  • Oracle bulkInsert:
    Skips mapBindParameters and bind generation since Oracle uses an array of arrays for multi-row bind batches.

  • ignoreDuplicates dialect supports option is enhanced to include false as valid value for dialects not supporting it.
    ignoreDuplicates:
    | false /* Not supported /
    | string /
    dialect specific words for INSERT IGNORE or DO NOTHING */;

    Ex:
    MariaDb: ' IGNORE' → string .
    Postgres: '' → string
    Oracle, db2, ibmi: false → Not supported.

  • updateOnDuplicate:
    updateOnDuplicate: '', is set for ibmi so that dialect.supports.inserts.updateOnDuplicate can be used

  • Unit tests:
    Updated to use $sequelize_1, $sequelize_2, ... placeholders to match the positional bind style used in SQL generation.

  • Miscellaneous:
    Updated copyright year.

List of Breaking Changes

Summary by CodeRabbit

  • New Features

    • Official Oracle dialect added with full runtime support and a new published package.
  • Tests

    • Extensive Oracle-focused unit and integration tests, helpers, and guards added; CI now runs Oracle tests.
  • Chores

    • Local Oracle dev tooling, npm scripts, and Docker compose/start/stop/reset scripts for running Oracle test instances.

✏️ Tip: You can customize this high-level summary in your review settings.

…d showConstraints and test cases related to that
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (7)
packages/oracle/src/query-generator.js (5)

174-176: Consider aliasing the version column.

The query returns VERSION_FULL without an alias. Aliasing it to "version" would standardize the output and potentially avoid the need to override queryInterface.fetchDatabaseVersion in other parts of the codebase.

   versionQuery() {
-    return `SELECT VERSION_FULL FROM PRODUCT_COMPONENT_VERSION WHERE PRODUCT LIKE 'Oracle%'`;
+    return `SELECT VERSION_FULL AS "version" FROM PRODUCT_COMPONENT_VERSION WHERE PRODUCT LIKE 'Oracle%'`;
   }

1-1291: Migrate this entire file to TypeScript.

This file is implemented in JavaScript but the coding guidelines require: "Write all new implementations in TypeScript; avoid creating new .js files in src". The base OracleQueryGeneratorTypeScript already exists in packages/oracle/src/query-generator-typescript.internal.ts, so these Oracle-specific methods should be migrated into that TypeScript file rather than maintained in a separate JavaScript file.

Based on coding guidelines and learnings.


324-326: Fix array splice bug when removing processed indexes.

Splicing in forward order causes index misalignment. After the first splice, all subsequent indices shift down by one, making the remaining indices in idxToDelete point to wrong elements.

Apply this diff to fix by iterating in reverse:

-    idxToDelete.forEach(idx => {
-      options.indexes.splice(idx, 1);
-    });
+    for (let i = idxToDelete.length - 1; i >= 0; i--) {
+      options.indexes.splice(idxToDelete[i], 1);
+    }

741-747: Fix auto-increment detection across multiple rows.

allColumns[key] is overwritten for each row, so only the last row's value determines whether the column is treated as auto-increment. If rows have mixed null and non-null values for an auto-increment column, the detection will be incorrect unless the last row happens to be null.

The check at lines 773-777 partially catches this, but only when processing rows after allColumns[key] was already set to true. To properly enforce the invariant ("either all rows are null or all are non-null"), track both states:

+    const sawNull = {};
+    const sawNonNull = {};
+
     for (const fieldValueHash of fieldValueHashes) {
       forOwn(fieldValueHash, (value, key) => {
+        if (!fieldMappedAttributes[key]?.autoIncrement) {
+          allColumns[key] = false;
+          return;
+        }
+
+        const isNull = value === null;
+        sawNull[key] ||= isNull;
+        sawNonNull[key] ||= !isNull;
+
+        if (sawNull[key] && sawNonNull[key]) {
+          throw new Error(
+            'For an auto-increment column either all rows must be null or non-null; a mix is not allowed.',
+          );
+        }
+
-        allColumns[key] =
-          fieldMappedAttributes[key] &&
-          fieldMappedAttributes[key].autoIncrement === true &&
-          value === null;
+        allColumns[key] = sawNull[key];
       });
     }

947-950: Fix unreachable DOUBLE condition.

Line 949 compares attribute.type === 'DOUBLE' against a string, but attribute.type is a DataType instance, not a string. This condition is unreachable and will never execute. The pattern at lines 921, 933, and 940 correctly uses instanceof.

Change to:

     if (attribute.autoIncrement) {
       template = ' NUMBER(*,0) GENERATED BY DEFAULT ON NULL AS IDENTITY';
-    } else if (attribute.type && attribute.type === 'DOUBLE') {
-      template = attribute.type.toSql();
+    } else if (attribute.type instanceof DataTypes.DOUBLE) {
+      template = attribute.type.toSql();
     } else if (attribute.type) {
packages/core/test/integration/sequelize/query.test.js (2)

122-125: Verify Date object vs string handling for Oracle timestamps.

Oracle uses Date objects for createdAt and updatedAt while other dialects use strings. This differs from the insertQuery construction (lines 79-83) which uses string literals formatted by dateLiteral(). Please verify this discrepancy is intentional and reflects Oracle driver behavior for bind parameters vs literals.

Run the following script to check how Oracle bind parameters handle Date objects:

#!/bin/bash
# Search for Oracle date/timestamp handling in driver and serialization code
rg -nP --type=ts --type=js -C5 'oracle.*date|timestamp.*oracle' -g '!**/test/**' -g '**/oracle/**'

555-560: Unique constraint violations return generic DatabaseError for Oracle.

Oracle returns DatabaseError instead of UniqueConstraintError for duplicate key violations, preventing applications from catching and handling unique constraint errors specifically. This is a functional limitation that should either be fixed in the Oracle error classification logic or properly documented.

Verify whether Oracle error codes can be mapped to UniqueConstraintError:

#!/bin/bash
# Search for Oracle error classification logic
ast-grep --pattern 'class $_ extends DatabaseError {
  $$$
}'

# Find Oracle unique constraint error codes (typically ORA-00001)
rg -nP 'ORA-00001|unique.*constraint.*oracle|oracle.*unique.*constraint' -g '!**/test/**'
🧹 Nitpick comments (2)
packages/oracle/src/query-generator.js (1)

464-471: Remove commented-out code.

Large blocks of commented code (lines 464-471 and 1114-1156) should be removed. If this code might be needed later, it's preserved in version control history. Keeping commented code in the source reduces readability and maintainability.

Also applies to: 1114-1156

packages/core/test/integration/sequelize/query.test.js (1)

892-918: Document why Oracle skips the duplicate named parameter test.

The test is excluded for Oracle (and db2) without explanation. If Oracle doesn't support reusing the same named bind parameter multiple times in a query, consider adding a comment or using a dialect.supports feature check instead of a direct dialect name check.

As per coding guidelines.

If this is a known Oracle limitation, add a clarifying comment:

-     if (!['db2', 'oracle'].includes(dialectName)) {
+     // Oracle and db2 don't support reusing named bind parameters in the same query
+     if (!['db2', 'oracle'].includes(dialectName)) {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c777f1f and 7bb3589.

📒 Files selected for processing (4)
  • packages/core/test/integration/query-interface/add-show-remove-constraint.test.ts (5 hunks)
  • packages/core/test/integration/sequelize/query.test.js (27 hunks)
  • packages/core/test/unit/query-generator/show-constraints-query.test.ts (9 hunks)
  • packages/oracle/src/query-generator.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/core/test/unit/query-generator/show-constraints-query.test.ts
  • packages/core/test/integration/query-interface/add-show-remove-constraint.test.ts
🧰 Additional context used
📓 Path-based instructions (5)
packages/**/test/**/*.test.js

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

Name integration tests with the .test.js suffix

Files:

  • packages/core/test/integration/sequelize/query.test.js
packages/**/test/**/*.test.{js,ts}

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

packages/**/test/**/*.test.{js,ts}: Use Support.getTestDialectTeaser() for dialect-specific test descriptions
Use beforeEach/afterEach to sync with { force: true } and close Sequelize instances in tests
In tests, skip unsupported behavior with dialect.supports.featureName checks

Files:

  • packages/core/test/integration/sequelize/query.test.js
packages/*/src/query-generator.{js,ts}

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

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

Files:

  • packages/oracle/src/query-generator.js
packages/**/src/**/*.js

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

Write all new implementations in TypeScript; avoid creating new .js files in src

Files:

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

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

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

Files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • packages/core/test/integration/sequelize/query.test.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/**/test/**/*.test.{js,ts} : Use beforeEach/afterEach to sync with { force: true } and close Sequelize instances in tests

Applied to files:

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

Applied to files:

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

Applied to files:

  • packages/oracle/src/query-generator.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Co-locate TypeScript type definitions with their implementation files

Applied to files:

  • packages/oracle/src/query-generator.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/**/src/**/*.js : Write all new implementations in TypeScript; avoid creating new .js files in src

Applied to files:

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

Applied to files:

  • packages/oracle/src/query-generator.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Prefer TypeScript for all new code; only modify existing .js files when necessary

Applied to files:

  • packages/oracle/src/query-generator.js
🧬 Code graph analysis (2)
packages/core/test/integration/sequelize/query.test.js (2)
packages/core/test/integration/transaction.test.js (3)
  • fromQuery (23-29)
  • sequelize (858-860)
  • sequelize (876-878)
packages/core/test/integration/sequelize.test.js (1)
  • qq (23-33)
packages/oracle/src/query-generator.js (5)
packages/oracle/src/query-generator-typescript.internal.ts (1)
  • OracleQueryGeneratorTypeScript (33-331)
packages/core/src/utils/check.ts (1)
  • rejectInvalidOptions (33-54)
packages/core/src/abstract-dialect/query-generator.js (5)
  • CREATE_TABLE_QUERY_SUPPORTABLE_OPTIONS (43-51)
  • CREATE_TABLE_QUERY_SUPPORTABLE_OPTIONS (43-51)
  • ADD_COLUMN_QUERY_SUPPORTABLE_OPTIONS (52-52)
  • ADD_COLUMN_QUERY_SUPPORTABLE_OPTIONS (52-52)
  • DataTypes (38-38)
packages/core/src/utils/object.ts (2)
  • EMPTY_SET (16-16)
  • getObjectFromMap (227-237)
packages/core/src/utils/query-builder-utils.ts (1)
  • defaultValueSchemable (15-35)
🪛 Biome (2.1.2)
packages/oracle/src/query-generator.js

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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


[error] 8-9: Illegal use of an import declaration outside of a module

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


[error] 25-26: Illegal use of an import declaration outside of a module

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


[error] 154-1286: Illegal use of an export declaration outside of a module

not allowed inside scripts

(parse)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Upload install and build artifact (Node 20)
  • GitHub Check: Upload install and build artifact (Node 18)
🔇 Additional comments (3)
packages/core/test/integration/sequelize/query.test.js (3)

28-57: LGTM! Helper functions correctly implement Oracle-specific SQL syntax.

The additions to qq() and the new fromQuery() and dateLiteral() helpers properly centralize Oracle-specific SQL generation patterns. Oracle's requirement for FROM DUAL, double-quoted identifiers, and to_date() format are all correctly handled.


574-579: Oracle timestamp literal correctly uses TO_TIMESTAMP.

The Oracle-specific query construction properly uses TO_TIMESTAMP('2012-01-01 10:10:10', 'YYYY-MM-DD HH24:MI:SS') format. Note that foreign key constraint errors are properly classified (line 593 expects ForeignKeyConstraintError), which contrasts with the unique constraint error issue flagged earlier.


159-1000: Comprehensive Oracle integration looks correct.

The extensive use of fromQuery(), proper handling of Oracle's uppercase column names in results, inclusion of Oracle in appropriate dialect lists, and use of SYSDATE for timestamps all follow correct Oracle conventions and are consistently applied throughout the test suite.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
packages/core/test/integration/json.test.ts (2)

68-70: Use this.skip() instead of early return to mark unsupported Oracle JSON as skipped

Right now this test just returns early on unsupported Oracle versions, which Mocha reports as a passing test instead of a skipped one, and the arrow function prevents access to the Mocha context (this.skip()).

You can make the intent explicit and align with the other Oracle JSON guards by switching to a regular function and calling this.skip():

-  it('should be able to store strings that require escaping', async () => {
-    if (dialect.name === 'oracle' && !(await isOracleJSONConstraintsSupported())) {
-      return;
-    }
+  it('should be able to store strings that require escaping', async function (this: Mocha.Context) {
+    if (dialect.name === 'oracle' && !(await isOracleJSONConstraintsSupported())) {
+      this.skip();
+    }

This makes unsupported Oracle configurations clearly show up as skipped rather than silently passing.


88-96: Simplify Oracle JSON version guard before hook

The logic here is correct and nicely addresses the earlier feedback about skipping only unsupported Oracle versions. The nested async IIFE is redundant, though; you can await the helper directly:

-  if (dialect.name === 'oracle') {
-    before(async function checkOracleVersionForJSONSupport(this: Mocha.Context) {
-      return (async () => {
-        if (!(await isOracleJSONConstraintsSupported())) {
-          this.skip();
-        }
-      })();
-    });
-  }
+  if (dialect.name === 'oracle') {
+    before(async function checkOracleVersionForJSONSupport(this: Mocha.Context) {
+      if (!(await isOracleJSONConstraintsSupported())) {
+        this.skip();
+      }
+    });
+  }

Behavior stays the same, but the hook is easier to read.

packages/core/test/integration/sequelize/query.test.js (1)

51-57: Inconsistent date literal format with inline usage.

The dateLiteral() helper uses to_date(...) for Oracle, but lines 533-534 and 572 use TO_TIMESTAMP(...) directly. Consider whether both formats are needed or if one should be standardized.

Apply this diff if you want to align the helper with the inline usage:

 const dateLiteral = str => {
   if (dialectName === 'oracle') {
-    return `to_date('${str}','YYYY-MM-DD HH24:MI:SS')`;
+    return `TO_TIMESTAMP('${str}','YYYY-MM-DD HH24:MI:SS')`;
   }
 
   return `'${str}'`;
 };
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7bb3589 and 45d8d40.

📒 Files selected for processing (2)
  • packages/core/test/integration/json.test.ts (3 hunks)
  • packages/core/test/integration/sequelize/query.test.js (27 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
packages/**/test/**/*.test.ts

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

Name unit tests with the .test.ts suffix

Files:

  • packages/core/test/integration/json.test.ts
packages/**/test/**/*.test.{js,ts}

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

packages/**/test/**/*.test.{js,ts}: Use Support.getTestDialectTeaser() for dialect-specific test descriptions
Use beforeEach/afterEach to sync with { force: true } and close Sequelize instances in tests
In tests, skip unsupported behavior with dialect.supports.featureName checks

Files:

  • packages/core/test/integration/json.test.ts
  • packages/core/test/integration/sequelize/query.test.js
packages/**/test/**/*.test.js

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

Name integration tests with the .test.js suffix

Files:

  • packages/core/test/integration/sequelize/query.test.js
🧠 Learnings (9)
📓 Common learnings
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/*/src/query-generator.{js,ts} : Implement dialect feature SQL in query-generator.ts (preferred) or legacy query-generator.js
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/*/src/query-interface.{js,ts} : Add schema operations for new features in query-interface.ts (preferred) or legacy query-interface.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/**/test/**/*.test.{js,ts} : In tests, skip unsupported behavior with dialect.supports.featureName checks

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • packages/core/test/integration/json.test.ts
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/core/test/chai-extensions.d.ts : Use Chai assertions with the project’s custom extensions available in packages/core/test/chai-extensions.d.ts

Applied to files:

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

Applied to files:

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

Applied to files:

  • packages/core/test/integration/json.test.ts
  • packages/core/test/integration/sequelize/query.test.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/**/test/**/*.test.{js,ts} : Use beforeEach/afterEach to sync with { force: true } and close Sequelize instances in tests

Applied to files:

  • packages/core/test/integration/json.test.ts
  • packages/core/test/integration/sequelize/query.test.js
🧬 Code graph analysis (2)
packages/core/test/integration/json.test.ts (1)
packages/core/test/integration/support.ts (1)
  • isOracleJSONConstraintsSupported (360-380)
packages/core/test/integration/sequelize/query.test.js (2)
packages/core/test/integration/transaction.test.js (3)
  • fromQuery (23-29)
  • sequelize (858-860)
  • sequelize (876-878)
packages/core/test/integration/sequelize.test.js (1)
  • qq (23-33)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Upload install and build artifact (Node 18)
  • GitHub Check: Upload install and build artifact (Node 20)
🔇 Additional comments (7)
packages/core/test/integration/json.test.ts (1)

11-18: Importing isOracleJSONConstraintsSupported here looks good

Centralizing the Oracle JSON constraint version check via the shared helper keeps these tests consistent with the rest of the suite; no issues with this import block.

packages/core/test/integration/sequelize/query.test.js (6)

28-57: LGTM! Helper functions follow established patterns.

The qq(), fromQuery(), and dateLiteral() helpers correctly implement Oracle-specific test utilities and follow the same pattern used in other test files (e.g., transaction.test.js).

Based on learnings


159-220: LGTM! Logging tests correctly use fromQuery().

All logging-related tests properly append fromQuery() to SELECT statements, ensuring Oracle gets the required FROM DUAL clause.


286-287: LGTM! Oracle correctly categorized with positional bind dialects.

Oracle is appropriately grouped with db2, postgres, mariadb, and mysql as dialects that use positional bind parameters.


531-538: LGTM! TO_TIMESTAMP syntax correct for Oracle error tests.

The Oracle-specific query construction using TO_TIMESTAMP('2012-01-01 10:10:10', 'YYYY-MM-DD HH24:MI:SS') is correct and ensures proper error handling tests for unique constraints and foreign key constraints.

Also applies to: 571-576


923-926: LGTM! Uppercase identifier handling correct for Oracle.

Oracle's default behavior of returning unquoted identifiers in uppercase is correctly handled in the expected results across all bind parameter tests.

Also applies to: 943-946, 959-962, 971-974


889-915: Verify whether Sequelize's Oracle dialect truly cannot reuse named bind parameters.

Node-oracledb supports reusing the same named bind variable multiple times in a SQL statement (e.g., SELECT :x, :x FROM dual with { x: 42 }). However, the test at line 889 excludes Oracle without explanation, suggesting Sequelize's Oracle dialect may have its own limitation.

Please confirm whether:

  1. This is a known Sequelize limitation, or
  2. Whether the exclusion can be safely removed and the test run against Oracle

If supported, this test should be enabled for Oracle to ensure consistency with actual driver capabilities.

@sudarshan12s sudarshan12s requested a review from WikiRik November 21, 2025 18:43
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (5)
packages/oracle/src/query.js (5)

24-38: Constructor still reads outFormat from raw options parameter.

This issue was flagged in a previous review but remains unresolved. Line 37 reads from the raw options parameter which can be undefined, causing a TypeError. The merged this.options (line 26) already handles options || {}, so you should use it consistently.

Apply this fix:

-    this.outFormat = options.outFormat || oracledb.OBJECT;
+    this.outFormat = this.options.outFormat || oracledb.OBJECT;

146-167: Transaction control flags don't take effect due to property name mismatch.

This critical issue was flagged in a previous review but remains unresolved. Lines 148, 155, and 162 set this.autocommit (lowercase), but all other code reads this.autoCommit (camelCase) at lines 45, 170, 180, 194, 202, 204, 253, 257. This prevents BEGIN TRANSACTION, SET AUTOCOMMIT ON, and SET AUTOCOMMIT OFF from affecting subsequent query execution.

Apply these fixes:

     if (this.sql.startsWith('BEGIN TRANSACTION')) {
-      this.autocommit = false;
+      this.autoCommit = false;
 
       // eslint-disable-next-line unicorn/no-useless-promise-resolve-reject
       return Promise.resolve();
     }
 
     if (this.sql.startsWith('SET AUTOCOMMIT ON')) {
-      this.autocommit = true;
+      this.autoCommit = true;
 
       // eslint-disable-next-line unicorn/no-useless-promise-resolve-reject
       return Promise.resolve();
     }
 
     if (this.sql.startsWith('SET AUTOCOMMIT OFF')) {
-      this.autocommit = false;
+      this.autoCommit = false;
 
       // eslint-disable-next-line unicorn/no-useless-promise-resolve-reject
       return Promise.resolve();
     }

339-435: Potential double-parse or type error for JSON attributes.

This issue was flagged in a previous review but remains unresolved. Lines 412-414 unconditionally call JSON.parse(value) for JSON types, then lines 426-429 still invoke the dialect parser for the same value. This risks:

  • Double-parsing if the dialect parser also decodes JSON
  • TypeError if value is a Buffer or already an object (Oracle stores JSON as BLOB per data-types-overrides.ts)

Prefer one of these solutions:

Option 1 (recommended): Let the dialect parser handle JSON entirely:

           if (modelDefinition.rawAttributes[key] && modelDefinition.rawAttributes[key].type) {
             let typeid = modelDefinition.rawAttributes[key].type.toLocaleString();
-            if (modelDefinition.rawAttributes[key].type.getDataTypeId() === 'JSON') {
-              value = JSON.parse(value);
-            }
 
             // For some types, the "name" of the type is returned with the length, we remove it

Option 2: Only parse strings and skip dialect parser for JSON:

+            let isJsonType = false;
             if (modelDefinition.rawAttributes[key].type.getDataTypeId() === 'JSON') {
+              isJsonType = true;
-              value = JSON.parse(value);
+              if (typeof value === 'string') {
+                value = JSON.parse(value);
+              }
             }
 
             // For some types, the "name" of the type is returned with the length, we remove it
             // For Boolean we skip this because BOOLEAN is mapped to CHAR(1) and we dont' want to
             // remove the (1) for BOOLEAN
             if (
               typeid.includes('(') &&
               modelDefinition.rawAttributes[key].type.getDataTypeId() !== 'BOOLEAN'
             ) {
               typeid = typeid.slice(0, typeid.indexOf('('));
             }
 
             const parser = this.sequelize.dialect.getParserForDatabaseDataType(typeid);
-            if (value !== null && parser) {
+            if (value !== null && parser && !isJsonType) {
               value = parser(value);
             }

Based on learnings.


545-559: isNewRecord flag remains inverted in upsert result.

This issue was flagged in a previous review but remains unresolved. Line 558 sets isNewRecord: data.isUpdate, which is 1 for updates and 0 for inserts—the opposite of the intended semantics. isNewRecord should be true for new records and false for existing ones.

Apply this fix:

-      result = [{ isNewRecord: data.isUpdate, value: data }, data.isUpdate == 0];
+      result = [{ isNewRecord: data.isUpdate == 0, value: data }, data.isUpdate == 0];

656-660: ORA-02443 error handling still has regex and constructor issues.

While the ORA-02291/02292 handling was fixed, the ORA-02443 case remains unresolved from the previous review. Line 657's regex has no capture group, so match[1] is undefined. Additionally, UnknownConstraintError expects an options object, not a string parameter.

Apply this fix:

     // ORA-02443: Cannot drop constraint  - nonexistent constraint
-    match = err.message.match(/ORA-02443/);
-    if (match && match.length > 0) {
-      return new UnknownConstraintError(match[1]);
+    match = err.message.match(/ORA-02443:[^-]*-\s*(.+?)(?:\s|$)/);
+    if (match && match.length > 1) {
+      return new UnknownConstraintError({
+        constraint: match[1].trim(),
+        cause: err,
+      });
     }
🧹 Nitpick comments (4)
packages/core/test/integration/error.test.ts (4)

553-563: Oracle ValidationError.errors parity with other dialects

Treating Oracle like db2 (i.e. ValidationError.errors being empty for bulk unique violations) is acceptable for initial support, but it does mean Oracle exposes less structured info than most other dialects here. Consider adding a TODO to parse the ORA-00001 details into error.errors later for parity, once the dialect stabilizes.

Also applies to: 633-647


606-608: Harden Oracle unique-constraint regex for index-name matching

The generic ORA‑00001 check is fine, but the index‑specific one:

/ORA-00001: unique constraint \(.*.users_username_unique\) violated/

relies on exact casing and an unescaped . before users_username_unique. To make this more resilient to schema prefixes & case differences, you could tighten it to something like:

- /ORA-00001: unique constraint \(.*.users_username_unique\) violated/
+ /ORA-00001: unique constraint \((?:.+\.)?users_username_unique\) violated/i

This still asserts the right constraint name while tolerating driver/DDL variations.

Also applies to: 682-686


763-769: Oracle FK errors expose less metadata than other dialects

For Oracle FK violations you currently assert error.table is undefined and error.fields is null, whereas other dialects generally surface at least table or fields. Oracle’s ORA‑02291/02292 messages contain the constraint name, so in the future it should be possible to extract table/column info for better parity; if you plan to improve the Oracle error mapping, these expectations will need to be relaxed or updated accordingly.

Also applies to: 846-852


896-899: Consider aligning Oracle duplicate-constraint mapping with UnknownConstraintError

For duplicate constraint names you assert a bare DatabaseError on Oracle, while MariaDB/MySQL/MSSQL/sqlite3 surface an AggregateError that includes an UnknownConstraintError with constraint and table. That’s a divergence in the cross‑dialect error contract: user code catching UnknownConstraintError won’t work on Oracle here. If feasible, consider mapping ORA‑02264 into the same UnknownConstraintError shape so behavior is consistent across dialects.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 45d8d40 and 5da24f8.

📒 Files selected for processing (2)
  • packages/core/test/integration/error.test.ts (8 hunks)
  • packages/oracle/src/query.js (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
packages/**/test/**/*.test.ts

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

Name unit tests with the .test.ts suffix

Files:

  • packages/core/test/integration/error.test.ts
packages/**/test/**/*.test.{js,ts}

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

packages/**/test/**/*.test.{js,ts}: Use Support.getTestDialectTeaser() for dialect-specific test descriptions
Use beforeEach/afterEach to sync with { force: true } and close Sequelize instances in tests
In tests, skip unsupported behavior with dialect.supports.featureName checks

Files:

  • packages/core/test/integration/error.test.ts
packages/**/src/**/*.js

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

Write all new implementations in TypeScript; avoid creating new .js files in src

Files:

  • packages/oracle/src/query.js
🧠 Learnings (6)
📓 Common learnings
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/*/src/query-generator.{js,ts} : Implement dialect feature SQL in query-generator.ts (preferred) or legacy query-generator.js
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/*/src/query-interface.{js,ts} : Add schema operations for new features in query-interface.ts (preferred) or legacy query-interface.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/**/test/**/*.test.{js,ts} : In tests, skip unsupported behavior with dialect.supports.featureName checks

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • packages/core/test/integration/error.test.ts
  • packages/oracle/src/query.js
🧬 Code graph analysis (2)
packages/core/test/integration/error.test.ts (1)
packages/core/src/errors/database-error.ts (1)
  • DatabaseError (22-38)
packages/oracle/src/query.js (6)
packages/oracle/src/query.d.ts (1)
  • OracleQuery (5-5)
packages/oracle/src/index.mjs (2)
  • OracleQuery (7-7)
  • OracleQuery (7-7)
packages/oracle/src/_internal/data-types-overrides.ts (1)
  • JSON (331-358)
packages/core/src/model-definition.ts (1)
  • index (706-757)
packages/core/src/model-typescript.ts (1)
  • uniqueKeys (352-384)
packages/core/src/utils/string.ts (1)
  • nameIndex (62-70)
🪛 Biome (2.1.2)
packages/oracle/src/query.js

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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


[error] 22-753: Illegal use of an export declaration outside of a module

not allowed inside scripts

(parse)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Upload install and build artifact (Node 18)
  • GitHub Check: Upload install and build artifact (Node 20)
🔇 Additional comments (1)
packages/core/test/integration/error.test.ts (1)

494-503: Oracle branch correctly reuses quoted-identifier INSERT

Including oracle in the ['db2', 'ibmi', 'oracle'] branch so it uses the double‑quoted INSERT INTO "users" ("name") ... SQL matches how those dialects quote identifiers and keeps the test intent clear. No issues here.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (9)
packages/oracle/src/query-generator.js (4)

3-1214: This new dialect implementation should ideally live in TypeScript, not a .js file.

Per the repo guidelines, new implementations under packages/**/src/** should be written in TypeScript, and dialect SQL should go into query-generator-typescript.internal.ts (with a thin JS wrapper only if absolutely needed). This file contains substantial new logic (DDL, DML, JSON helpers, bind handling) that would benefit from typings, and it also triggers lint/parser issues in tools expecting ESM TS.

Consider migrating the concrete methods from this file into OracleQueryGeneratorTypeScript and keeping this JS file as a minimal public wrapper (or removing it entirely if consumers can import the TS‑built artifact directly).


254-323: Fix unique‑index → unique‑constraint handling: splice logic and unique type assumptions.

Two issues in the createTableQuery index‑to‑constraint conversion block:

  1. Forward splice over idxToDelete can remove wrong indexes.
    Splicing options.indexes in ascending index order shifts subsequent indices; later deletions may target the wrong element or no element at all. This leaves some indexes in options.indexes even though they were meant to be converted to constraints.

  2. index.unique.length assumes unique is not false.
    For { unique: false }, the condition index.unique === true || (index.unique.length > 0 && index.unique !== false) still evaluates the length access on a boolean, which will throw at runtime.

A safe fix is to normalize unique to a boolean and remove indexes in reverse order:

-    if (options && options.indexes && options.indexes.length > 0) {
-      const idxToDelete = [];
+    if (options && options.indexes && options.indexes.length > 0) {
+      const idxToDelete: number[] = [];
       options.indexes.forEach((index, idx) => {
-        if (
-          'unique' in index &&
-          (index.unique === true || (index.unique.length > 0 && index.unique !== false))
-        ) {
+        const unique =
+          typeof index.unique === 'boolean'
+            ? index.unique
+            : Boolean(index.unique);
+
+        if ('unique' in index && unique) {
           // If unique index, transform to unique constraint on column
           const fields = index.fields.map(field => {
             if (typeof field === 'string') {
               return field;
             }
 
             return field.attribute;
           });
@@
-      idxToDelete.forEach(idx => {
-        options.indexes.splice(idx, 1);
-      });
+      // delete from the end to avoid index shifting
+      for (let i = idxToDelete.length - 1; i >= 0; i--) {
+        options.indexes.splice(idxToDelete[i], 1);
+      }

This preserves the intended behavior and avoids index‑shift bugs.


682-727: Auto‑increment detection in bulkInsertQuery is incorrect when last row is non‑null.

The allColumns map is recomputed for every row:

for (const fieldValueHash of fieldValueHashes) {
  forOwn(fieldValueHash, (value, key) => {
    allColumns[key] =
      fieldMappedAttributes[key] &&
      fieldMappedAttributes[key].autoIncrement === true &&
      value === null;
  });
}

This means allColumns[key] only reflects the last row’s value for that column. Later, when building tuples, mixed null / non‑null values are only rejected if the last row is null; if the last row is non‑null, a mix of null and non‑null silently passes and the column is treated as a regular in‑bind.

To enforce the intended invariant (“either all rows are null or all are non‑null for auto‑increment columns”), track both “saw null” and “saw non‑null” per column:

-    const allColumns = {};
+    const allColumns: Record<string, boolean> = {};
@@
-    for (const fieldValueHash of fieldValueHashes) {
-      forOwn(fieldValueHash, (value, key) => {
-        allColumns[key] =
-          fieldMappedAttributes[key] &&
-          fieldMappedAttributes[key].autoIncrement === true &&
-          value === null;
-      });
-    }
+    const sawNull: Record<string, boolean> = {};
+    const sawNonNull: Record<string, boolean> = {};
+
+    for (const fieldValueHash of fieldValueHashes) {
+      forOwn(fieldValueHash, (value, key) => {
+        if (!fieldMappedAttributes[key]?.autoIncrement) {
+          allColumns[key] = false;
+          return;
+        }
+
+        const isNull = value === null;
+        sawNull[key] ||= isNull;
+        sawNonNull[key] ||= !isNull;
+
+        if (sawNull[key] && sawNonNull[key]) {
+          throw new Error(
+            'For an auto-increment column either all rows must be null or non-null; a mix is not allowed.',
+          );
+        }
+
+        // true means "use DEFAULT / outbind"
+        allColumns[key] = sawNull[key];
+      });
+    }

This ensures you either generate DEFAULT for that column in every row or treat it as a regular bound column, and you consistently reject mixed sets.


848-955: Unreachable / incorrect DOUBLE handling and type checks in attributeToSQL.

The branch:

} else if (attribute.type && attribute.type === 'DOUBLE') {
  template = attribute.type.toSql();
}

is problematic:

  • In normal usage, attribute.type is a DataTypes instance, not the string 'DOUBLE', so this condition never matches.
  • If attribute.type were the string 'DOUBLE', calling toSql() would throw.

Oracle DOUBLE columns will currently go through the generic else if (attribute.type) path, so this is effectively dead or buggy code.

A safer approach is either to:

  • Remove this branch entirely and let the generic attribute.type.toString() path handle DOUBLE, or
  • Check for the actual type instance, e.g.:
-    } else if (attribute.type && attribute.type === 'DOUBLE') {
-      template = attribute.type.toSql();
-    } else if (attribute.type) {
+    } else if (attribute.type instanceof DataTypes.DOUBLE) {
+      template = attribute.type.toSql();
+    } else if (attribute.type) {

which aligns with the earlier ENUM / JSON / BOOLEAN checks.

packages/oracle/src/query-generator-typescript.internal.ts (2)

140-157: removeConstraintQuery generates invalid Oracle syntax for ifExists.

For constraints starting with sys, this override emits:

ALTER TABLE <table> DROP CONSTRAINT IF EXISTS <name> [CASCADE]

Oracle’s ALTER TABLE ... DROP CONSTRAINT does not support IF EXISTS, so any call with options.ifExists === true will fail at runtime.

To fix this:

  • Either reject ifExists for Oracle by validating options before building SQL, e.g.:
+    if (options) {
+      // Oracle does not support IF EXISTS for DROP CONSTRAINT
+      rejectInvalidOptions(
+        'removeConstraintQuery',
+        this.dialect,
+        /* REMOVE_CONSTRAINT_QUERY_SUPPORTABLE_OPTIONS */ new Set(['ifExists', 'cascade']),
+        /* supportedOptions */ new Set(['cascade']),
+        options,
+      );
+    }
+
     if (constraintName.startsWith('sys')) {
       return joinSQLFragments([
         'ALTER TABLE',
         this.quoteTable(tableName),
         'DROP CONSTRAINT',
-        options?.ifExists ? 'IF EXISTS' : '',
         constraintName,
         options?.cascade ? 'CASCADE' : '',
       ]);
     }
  • Or, if you truly need an “if exists” behavior, wrap the DROP in a PL/SQL block that checks the data dictionary before executing.

Right now, any use of ifExists here will be broken.

Does Oracle SQL support `ALTER TABLE ... DROP CONSTRAINT IF EXISTS` syntax, and what is the recommended pattern to conditionally drop a constraint if it exists?

379-408: Use Oracle’s COMMIT / ROLLBACK syntax instead of T‑SQL forms.

commitTransactionQuery and rollbackTransactionQuery currently return:

return 'COMMIT TRANSACTION';
...
return 'ROLLBACK TRANSACTION';

These are SQL Server / T‑SQL forms and are not valid Oracle SQL; Oracle expects COMMIT (optionally COMMIT WORK) and ROLLBACK. Because Oracle does not enable supports.connectionTransactionMethods by default, these methods will be called and produce invalid SQL.

You should either:

  • Return plain Oracle statements:
  commitTransactionQuery() {
-    return 'COMMIT TRANSACTION';
+    return 'COMMIT';
  }

  rollbackTransactionQuery(): string {
    if (this.dialect.supports.connectionTransactionMethods) {
      throw new Error(
        `rollbackTransactionQuery is not supported by the ${this.dialect.name} dialect.`,
      );
    }

-    return 'ROLLBACK TRANSACTION';
+    return 'ROLLBACK';
  }
  • Or, if Oracle is always meant to use connection‑level transaction control, set supports.connectionTransactionMethods = true for this dialect and make these methods throw unconditionally.

As written, they are very likely to generate invalid SQL.

What are the valid SQL statements for committing and rolling back a transaction in Oracle Database, and does Oracle accept `COMMIT TRANSACTION` / `ROLLBACK TRANSACTION`?
packages/oracle/src/dialect.ts (3)

88-90: Documentation URL doesn't match minimum database version.

Line 88 links to Oracle 23 documentation, but line 90 sets minimumDatabaseVersion to '18.0.0'. Oracle 23 introduced features unavailable in 18.0.0 (native JSON SQL type, SQL Domains, spatial enhancements).

Either update the URL to Oracle 18 documentation or increase the minimum version to 23.0.0 if Oracle 23-specific features are required.


109-111: Runtime error when replication config is absent.

this.sequelize.options.replication.write.username?.toUpperCase() will throw if replication or write is undefined (common in non-replication setups), because property access occurs before optional chaining on username.

Apply this diff to handle both replication and direct username safely:

 getDefaultSchema(): string {
-  return this.sequelize.options.replication.write.username?.toUpperCase() ?? '';
+  const replicationUser = this.sequelize.options.replication?.write?.username;
+  const directUser = this.sequelize.options.username;
+  const username = replicationUser ?? directUser;
+  return username?.toUpperCase() ?? '';
 }

135-182: Fix typos in validation comments.

The date validation logic is thorough and security-conscious, but there are two typos:

  • Line 141: "indicated y" should be "indicated by"
  • Line 146: "TIMESTAM_TZ" should be "TIMESTAMP_TZ"

Apply this diff:

     // Validate that the split result has exactly three parts (function name, parameters, and an empty string)
-    // and that there are no additional SQL commands after the function call (indicated y the last empty string).
+    // and that there are no additional SQL commands after the function call (indicated by the last empty string).
     if (splitVal.length !== 3 || splitVal[2] !== '') {
       throw new Error('Invalid SQL function call.'); // Error if function call has unexpected format
     }
 
-    // Extract the function name (either 'TO_TIMESTAM_TZ' or 'TO_DATE') and the contents inside the parentheses
+    // Extract the function name (either 'TO_TIMESTAMP_TZ' or 'TO_DATE') and the contents inside the parentheses
     const functionName = splitVal[0].trim(); // Function name should be 'TO_TIMESTAMP_TZ' or 'TO_DATE'
🧹 Nitpick comments (3)
packages/oracle/src/query-interface-typescript.internal.ts (1)

13-26: Avoid overriding fetchDatabaseVersion by aligning Oracle’s versionQuery output.

This override exists only because Oracle returns VERSION_FULL. If you instead alias it to version in OracleQueryGeneratorTypeScript.versionQuery (e.g. SELECT VERSION_FULL AS version ...), you can drop this method and rely on the base AbstractQueryInterfaceTypeScript.fetchDatabaseVersion (including its runtime assertion on the version field). That keeps Oracle in line with other dialects and removes an extra Oracle‑specific code path here.

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

261-279: Good use of a dialect flag to control concurrent FK drops.

The new concurrentDropConstraints guard makes FK removal concurrent only for dialects that explicitly support it, while keeping the previous sequential behavior as the default. This limits deadlock/timeout risk and keeps behavior configurable per dialect.

Ensure each dialect sets supports.dropTable.concurrentDropConstraints appropriately (e.g. conservative false for engines known to deadlock under concurrent DDL).

packages/oracle/src/dialect.ts (1)

16-16: Consider using a type alias for the empty interface.

An empty interface is equivalent to {}. If this is a placeholder for future options, consider using a type alias for better semantics.

Apply this diff:

-export interface OracleDialectOptions {}
+export type OracleDialectOptions = Record<string, never>;

Alternatively, if options will be added soon, the empty interface is acceptable as a placeholder.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5da24f8 and 8454ae2.

📒 Files selected for processing (7)
  • packages/core/src/abstract-dialect/dialect.ts (5 hunks)
  • packages/core/src/abstract-dialect/query-interface-typescript.ts (1 hunks)
  • packages/core/test/integration/support.ts (1 hunks)
  • packages/oracle/src/dialect.ts (1 hunks)
  • packages/oracle/src/query-generator-typescript.internal.ts (1 hunks)
  • packages/oracle/src/query-generator.js (1 hunks)
  • packages/oracle/src/query-interface-typescript.internal.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
packages/*/src/query-generator.{js,ts}

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

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

Files:

  • packages/oracle/src/query-generator.js
packages/**/src/**/*.js

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

Write all new implementations in TypeScript; avoid creating new .js files in src

Files:

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

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

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

Files:

  • packages/oracle/src/query-generator.js
packages/*/src/dialect.ts

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

When adding a dialect feature, update packages/{dialect}/src/dialect.ts to declare feature support flags

Files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • packages/oracle/src/query-interface-typescript.internal.ts
  • packages/oracle/src/query-generator.js
  • packages/core/src/abstract-dialect/query-interface-typescript.ts
  • packages/core/src/abstract-dialect/dialect.ts
  • packages/oracle/src/dialect.ts
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Co-locate TypeScript type definitions with their implementation files

Applied to files:

  • packages/oracle/src/query-generator.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/**/src/**/*.js : Write all new implementations in TypeScript; avoid creating new .js files in src

Applied to files:

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

Applied to files:

  • packages/oracle/src/query-generator.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Prefer TypeScript for all new code; only modify existing .js files when necessary

Applied to files:

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

Applied to files:

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

Applied to files:

  • packages/core/src/abstract-dialect/dialect.ts
  • packages/oracle/src/dialect.ts
🧬 Code graph analysis (5)
packages/oracle/src/query-interface-typescript.internal.ts (2)
packages/core/src/abstract-dialect/query-interface-internal.ts (1)
  • AbstractQueryInterfaceInternal (11-50)
packages/core/src/abstract-dialect/query-interface.types.ts (1)
  • FetchDatabaseVersionOptions (128-128)
packages/oracle/src/query-generator.js (5)
packages/oracle/src/query-generator-typescript.internal.ts (1)
  • OracleQueryGeneratorTypeScript (34-409)
packages/core/src/utils/check.ts (1)
  • rejectInvalidOptions (33-54)
packages/core/src/abstract-dialect/query-generator.js (5)
  • CREATE_TABLE_QUERY_SUPPORTABLE_OPTIONS (43-51)
  • CREATE_TABLE_QUERY_SUPPORTABLE_OPTIONS (43-51)
  • ADD_COLUMN_QUERY_SUPPORTABLE_OPTIONS (52-52)
  • ADD_COLUMN_QUERY_SUPPORTABLE_OPTIONS (52-52)
  • DataTypes (38-38)
packages/core/src/utils/object.ts (2)
  • EMPTY_SET (16-16)
  • getObjectFromMap (227-237)
packages/core/src/utils/query-builder-utils.ts (1)
  • defaultValueSchemable (15-35)
packages/core/test/integration/support.ts (1)
packages/core/test/support.ts (1)
  • sequelize (504-504)
packages/oracle/src/query-generator-typescript.internal.ts (8)
packages/oracle/src/query-generator.internal.ts (1)
  • OracleQueryGeneratorInternal (11-54)
packages/oracle/src/dialect.ts (1)
  • OracleDialect (23-191)
packages/core/src/abstract-dialect/query-generator.types.ts (8)
  • TableOrModel (13-13)
  • RemoveIndexQueryOptions (194-198)
  • RenameTableQueryOptions (66-68)
  • RemoveColumnQueryOptions (77-80)
  • CreateSchemaQueryOptions (35-42)
  • TruncateTableQueryOptions (71-74)
  • ShowConstraintsQueryOptions (171-175)
  • BulkDeleteQueryOptions (189-191)
packages/core/src/utils/check.ts (1)
  • rejectInvalidOptions (33-54)
packages/core/src/utils/object.ts (1)
  • EMPTY_SET (16-16)
packages/core/src/utils/string.ts (1)
  • generateIndexName (72-107)
packages/core/src/abstract-dialect/query-interface.d.ts (1)
  • TableNameWithSchema (71-75)
packages/core/src/utils/model-utils.ts (1)
  • extractModelDefinition (35-45)
packages/oracle/src/dialect.ts (4)
packages/core/src/abstract-dialect/dialect.ts (1)
  • SupportableNumericOptions (15-19)
packages/oracle/src/connection-manager.ts (1)
  • OracleConnectionOptions (26-33)
packages/core/src/utils/sql.ts (1)
  • createSpecifiedOrderedBindCollector (401-419)
packages/oracle/src/_internal/connection-options.ts (1)
  • CONNECTION_OPTION_NAMES (75-84)
🪛 Biome (2.1.2)
packages/oracle/src/query-generator.js

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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


[error] 8-9: Illegal use of an import declaration outside of a module

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


[error] 25-26: Illegal use of an import declaration outside of a module

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


[error] 154-1209: Illegal use of an export declaration outside of a module

not allowed inside scripts

(parse)

packages/oracle/src/dialect.ts

[error] 16-16: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Upload install and build artifact (Node 20)
  • GitHub Check: Upload install and build artifact (Node 18)
🔇 Additional comments (9)
packages/core/src/abstract-dialect/dialect.ts (3)

116-118: LGTM! Type change enables explicit opt-out.

The change from string to false | string properly allows dialects that don't support INSERT IGNORE/DO NOTHING to explicitly declare this limitation while maintaining backward compatibility.


246-246: LGTM! New property supports dialect-specific constraint drop behavior.

The concurrentDropConstraints property enables dialects like Oracle to indicate they cannot drop multiple constraints on the same table concurrently (to avoid resource contention errors).


336-337: LGTM! Safe defaults for new properties.

The default values are conservative:

  • returnIntoValues: false and topLevelOrderByRequired: false default to unsupported
  • concurrentDropConstraints: true assumes concurrent operations are safe by default

Dialects can override these as needed.

Also applies to: 473-473

packages/oracle/src/dialect.ts (6)

18-21: LGTM! Numeric options correctly configured for Oracle.

Oracle supports unsigned integers but not zerofill, which is correctly reflected in this configuration.


24-79: LGTM! Comprehensive dialect capabilities declaration.

The supports configuration correctly declares Oracle's capabilities, including proper usage of the new core dialect properties (returnIntoValues, topLevelOrderByRequired, concurrentDropConstraints).


113-115: LGTM! Bind collector correctly configured for Oracle.

Using createSpecifiedOrderedBindCollector(':') is appropriate for Oracle's named bind parameter syntax (:1, :2, etc.).


117-127: LGTM! String escaping with security validation for date functions.

The special handling for TO_TIMESTAMP_TZ and TO_DATE with validation via assertDate prevents SQL injection while allowing Oracle date literals.


184-190: LGTM! Supported options correctly declared.

The static methods properly declare that Oracle has no dialect-specific options beyond connection settings.


129-133: Based on my verification of Oracle's hex literal syntax, the current implementation is correct:

In Oracle, you can use a string literal containing a hex value to insert data into a RAW column, exactly as the code does with '${hex}'. Oracle performs an implicit cast from VARCHAR2 to RAW when needed, allowing plain quoted hex strings to work seamlessly without the X prefix.

The implementation matches the actual Oracle syntax, similar to how PostgreSQL handles it. While different SQL dialects have different conventions (MSSQL uses 0x${hex}, the abstract default uses X'${hex}'), Oracle's approach with plain quoted strings is both valid and commonly used in the ecosystem.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

♻️ Duplicate comments (4)
packages/oracle/src/query-generator.js (4)

1-1237: This file should be implemented in TypeScript.

The coding guidelines require all new implementations to be written in TypeScript. This was already flagged in previous reviews with a comprehensive migration plan, but the file remains as JavaScript.

Based on coding guidelines.


320-322: Fix array splice bug when removing processed indexes.

Splicing in forward order within the loop causes index misalignment. After the first splice, all subsequent indices shift down, making the remaining indices in idxToDelete point to the wrong elements.

Apply this diff to fix by iterating in reverse order:

-    idxToDelete.forEach(idx => {
-      options.indexes.splice(idx, 1);
-    });
+    // Splice in reverse order to avoid index shifting issues
+    for (let i = idxToDelete.length - 1; i >= 0; i--) {
+      options.indexes.splice(idxToDelete[i], 1);
+    }

713-720: Fix auto-increment detection to validate across all rows.

allColumns[key] is overwritten for each row, so it only reflects the last row's value. A mix of null and non-null values for an auto-increment column is only detected when the last row is null. If the last row is non-null, the mix passes silently.

Apply this diff to track both states and enforce the invariant:

+    const sawNull = {};
+    const sawNonNull = {};
+
     for (const fieldValueHash of fieldValueHashes) {
       forOwn(fieldValueHash, (value, key) => {
-        allColumns[key] =
-          fieldMappedAttributes[key] &&
-          fieldMappedAttributes[key].autoIncrement === true &&
-          value === null;
+        if (!fieldMappedAttributes[key]?.autoIncrement) {
+          allColumns[key] = false;
+          return;
+        }
+
+        const isNull = value === null;
+        sawNull[key] ||= isNull;
+        sawNonNull[key] ||= !isNull;
+
+        if (sawNull[key] && sawNonNull[key]) {
+          throw new Error(
+            'For an auto-increment column either all rows must be null or non-null; a mix is not allowed.',
+          );
+        }
+
+        allColumns[key] = sawNull[key];
       });
     }

921-922: Fix unreachable DOUBLE condition.

Line 921 compares attribute.type === 'DOUBLE' against a string, but attribute.type is a DataType class instance. This condition is unreachable dead code.

Apply this diff to use instanceof check (consistent with ENUM at line 893, JSON at line 905, and BOOLEAN at line 912):

-    } else if (attribute.autoIncrement) {
+    if (attribute.autoIncrement) {
       template = ' NUMBER(*,0) GENERATED BY DEFAULT ON NULL AS IDENTITY';
-    } else if (attribute.type && attribute.type === 'DOUBLE') {
-      template = attribute.type.toSql();
-    } else if (attribute.type) {
+    } else if (attribute.type instanceof DataTypes.DOUBLE) {
+      template = attribute.type.toSql();
+    } else if (attribute.type) {
🧹 Nitpick comments (5)
packages/oracle/src/query-generator.js (2)

436-443: Remove commented-out code.

Commented-out code should be removed and tracked in version control history instead. If this implementation is needed later, add a TODO comment with context.

Apply this diff:

-  // addConstraintQuery(tableName, options) {
-  //   options = options || {};
-
-  //   const constraintSnippet = this.getConstraintSnippet(tableName, options);
-
-  //   tableName = this.quoteTable(tableName);
-  //   return `ALTER TABLE ${tableName} ADD ${constraintSnippet};`;
-  // }
-
   addColumnQuery(table, key, dataType, options) {

1060-1102: Remove large commented-out code block.

This 42-line commented-out implementation should be removed. If the functionality is needed, create a TODO comment with an issue reference.

Apply this diff:

-  // handleSequelizeMethod(smth, tableName, factory, options, prepend) {
-  //   let str;
-  //   if (smth instanceof Utils.Json) {
-  //     // Parse nested object
-  //     if (smth.conditions) {
-  //       const conditions = this.parseConditionObject(smth.conditions).map(condition =>
-  //         `${this.jsonPathExtractionQuery(condition.path[0], _.tail(condition.path))} = '${condition.value}'`
-  //       );
-  //
-  //       return conditions.join(' AND ');
-  //     }
-  //     if (smth.path) {
-  //
-  //       // Allow specifying conditions using the sqlite json functions
-  //       if (this._checkValidJsonStatement(smth.path)) {
-  //         str = smth.path;
-  //       } else {
-  //         // Also support json property accessors
-  //         const paths = _.toPath(smth.path);
-  //         const column = paths.shift();
-  //         str = this.jsonPathExtractionQuery(column, paths);
-  //       }
-  //       if (smth.value) {
-  //         str += util.format(' = %s', this.escape(smth.value));
-  //       }
-  //
-  //       return str;
-  //     }
-  //   }
-  //   if (smth instanceof Utils.Cast) {
-  //     if (smth.val instanceof Utils.SequelizeMethod) {
-  //       str = this.handleSequelizeMethod(smth.val, tableName, factory, options, prepend);
-  //       if (smth.type === 'boolean') {
-  //         str = `(CASE WHEN ${str}='true' THEN 1 ELSE 0 END)`;
-  //         return `CAST(${str} AS NUMBER)`;
-  //       } if (smth.type === 'timestamptz' && /json_value\(/.test(str)) {
-  //         str = str.slice(0, -1);
-  //         return `${str} RETURNING TIMESTAMP WITH TIME ZONE)`;
-  //       }
-  //     }
-  //   }
-  //   return super.handleSequelizeMethod(smth, tableName, factory, options, prepend);
-  // }
-
   _checkValidJsonStatement(stmt) {
packages/oracle/src/query-interface.d.ts (1)

1-8: Consider adding explicit upsert method typing to the type declaration file.

The type file inherits the upsert method from AbstractQueryInterface through the empty class body, which is technically correct. However, since the implementation file contains custom Oracle-specific upsert logic (handling upsertKeys, constraints, and unique indexes), explicitly declaring the method signature in the .d.ts file would improve type clarity and IDE support for library consumers:

async upsert(
  tableName: string | { tableName: string },
  insertValues: object,
  updateValues: object,
  where: object,
  options: object
): Promise<boolean | number | undefined>;

This makes the contract explicit and improves discoverability of the Oracle dialect's upsert behavior.

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

475-667: Oracle index expectations look consistent, but test is position‑sensitive

The new Oracle branch that assigns idx1 = args[1], idx2 = args[2], idx3 = args[3] and asserts only on fields (not type) matches how Oracle is expected to surface these indexes (1 composite + 2 single‑column). This is consistent with the rest of the suite.

The only downside is the usual brittleness around relying on args[N] ordering. If Oracle ever changes index ordering (e.g., by name), this test could break even though behavior is correct. Long‑term, consider locating indexes by name (e.g., via args.find(idx => idx.name === 'a_b_uniq')) instead of array position, similar to what you already do in some other tests.

packages/core/test/unit/sql/update.test.js (1)

90-90: Remove redundant Oracle expectation.

The Oracle expectation is identical to the default expectation after quote character substitution. Per the comment at line 10, the expectsql helper automatically replaces [] with dialect-specific quote characters (double quotes for Oracle). Adding an explicit Oracle expectation here is redundant and increases maintenance overhead without providing value.

Apply this diff to remove the redundant line:

             db2: 'SELECT * FROM FINAL TABLE (UPDATE "users" SET "user_name"=$sequelize_1 WHERE "id" = $sequelize_2);',
             snowflake: 'UPDATE "users" SET "user_name"=$sequelize_1 WHERE "id" = $sequelize_2',
-            oracle: `UPDATE "users" SET "user_name"=$sequelize_1 WHERE "id" = $sequelize_2`,
             default: 'UPDATE `users` SET `user_name`=$sequelize_1 WHERE `id` = $sequelize_2',

Note: This addresses the past review comment asking "What's the difference with the default?"

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8454ae2 and a26e0f9.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (6)
  • packages/core/test/integration/model.test.js (9 hunks)
  • packages/core/test/unit/query-generator/version-query.test.ts (1 hunks)
  • packages/core/test/unit/sql/update.test.js (2 hunks)
  • packages/oracle/src/query-generator.js (1 hunks)
  • packages/oracle/src/query-interface.d.ts (1 hunks)
  • packages/oracle/src/query-interface.js (1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
packages/**/test/**/*.test.js

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

Name integration tests with the .test.js suffix

Files:

  • packages/core/test/unit/sql/update.test.js
  • packages/core/test/integration/model.test.js
packages/**/test/**/*.test.{js,ts}

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

packages/**/test/**/*.test.{js,ts}: Use Support.getTestDialectTeaser() for dialect-specific test descriptions
Use beforeEach/afterEach to sync with { force: true } and close Sequelize instances in tests
In tests, skip unsupported behavior with dialect.supports.featureName checks

Files:

  • packages/core/test/unit/sql/update.test.js
  • packages/core/test/integration/model.test.js
  • packages/core/test/unit/query-generator/version-query.test.ts
packages/*/src/query-generator.{js,ts}

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

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

Files:

  • packages/oracle/src/query-generator.js
packages/**/src/**/*.js

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

Write all new implementations in TypeScript; avoid creating new .js files in src

Files:

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

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

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

Files:

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

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

Add schema operations for new features in query-interface.ts (preferred) or legacy query-interface.js

Files:

  • packages/oracle/src/query-interface.js
packages/**/test/**/*.test.ts

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

Name unit tests with the .test.ts suffix

Files:

  • packages/core/test/unit/query-generator/version-query.test.ts
🧠 Learnings (11)
📓 Common learnings
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/*/src/query-generator.{js,ts} : Implement dialect feature SQL in query-generator.ts (preferred) or legacy query-generator.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/*/src/query-interface.{js,ts} : Add schema operations for new features in query-interface.ts (preferred) or legacy query-interface.js

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • packages/core/test/integration/model.test.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Co-locate TypeScript type definitions with their implementation files

Applied to files:

  • packages/oracle/src/query-generator.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/**/src/**/*.js : Write all new implementations in TypeScript; avoid creating new .js files in src

Applied to files:

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

Applied to files:

  • packages/oracle/src/query-generator.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Prefer TypeScript for all new code; only modify existing .js files when necessary

Applied to files:

  • packages/oracle/src/query-generator.js
🧬 Code graph analysis (3)
packages/oracle/src/query-interface.d.ts (2)
packages/oracle/src/query-interface.js (1)
  • OracleQueryInterface (9-92)
packages/oracle/src/dialect.ts (1)
  • OracleDialect (23-191)
packages/oracle/src/query-generator.js (6)
packages/oracle/src/query-generator-typescript.internal.ts (1)
  • OracleQueryGeneratorTypeScript (34-409)
packages/core/src/abstract-dialect/query-generator-typescript.ts (2)
  • options (192-194)
  • quoteIdentifier (703-705)
packages/core/src/utils/check.ts (1)
  • rejectInvalidOptions (33-54)
packages/core/src/abstract-dialect/query-generator.js (5)
  • CREATE_TABLE_QUERY_SUPPORTABLE_OPTIONS (43-51)
  • CREATE_TABLE_QUERY_SUPPORTABLE_OPTIONS (43-51)
  • ADD_COLUMN_QUERY_SUPPORTABLE_OPTIONS (52-52)
  • ADD_COLUMN_QUERY_SUPPORTABLE_OPTIONS (52-52)
  • DataTypes (38-38)
packages/core/src/utils/object.ts (2)
  • EMPTY_SET (16-16)
  • getObjectFromMap (227-237)
packages/core/src/utils/query-builder-utils.ts (1)
  • defaultValueSchemable (15-35)
packages/oracle/src/query-interface.js (3)
packages/oracle/src/query-interface.d.ts (1)
  • OracleQueryInterface (6-8)
packages/core/src/model-typescript.ts (2)
  • primaryKeys (140-150)
  • uniqueKeys (352-384)
packages/core/src/utils/sql.ts (1)
  • assertNoReservedBind (437-449)
🪛 Biome (2.1.2)
packages/oracle/src/query-generator.js

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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


[error] 8-9: Illegal use of an import declaration outside of a module

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


[error] 25-26: Illegal use of an import declaration outside of a module

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


[error] 154-1232: Illegal use of an export declaration outside of a module

not allowed inside scripts

(parse)

packages/oracle/src/query-interface.js

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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


[error] 8-92: Illegal use of an export declaration outside of a module

not allowed inside scripts

(parse)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Upload install and build artifact (Node 20)
  • GitHub Check: Upload install and build artifact (Node 18)
🔇 Additional comments (8)
packages/oracle/src/query-interface.js (2)

46-52: Verify the logic for falling back to primary keys.

The condition at lines 47-49 falls back to using primary keys as upsertKeys when:

  1. No unique constraint is available, OR
  2. The update data contains any primary key field

The second condition (intersection(options.updateOnDuplicate, primaryKeys).length) seems counterintuitive. Why should updating a primary key field force the use of primary keys as the upsert constraint?

Please verify this is the intended Oracle upsert behavior. If this logic is specific to Oracle's MERGE statement requirements, consider adding a comment explaining why.


76-91: LGTM: SQL generation and execution logic is sound.

The handling of bind parameters (lines 86-88) correctly prevents conflicts by clearing options.bind when sql.bind is present. The shallow copy of options at line 15 ensures this doesn't affect the caller's original options object.

packages/core/test/integration/model.test.js (5)

269-297: Oracle index field shape aligned with db2/mssql

Adding case 'oracle' to share the same showIndex field shape as db2/mssql (attribute + collate + length + order: 'ASC') is consistent with the expectations you set elsewhere for Oracle indexes. No issues from this test-side change.


1022-1070: Schema describeTable logging condition correctly keeps postgres special

The else if (dialectName !== 'postgres') guard keeps postgres out of the SQL string assertions while letting all other dialects (including oracle & snowflake) use the default behavior, with sqlite still handled specially via the TABLE_INFO check. This matches the maintainer guidance about only needing a postgres special case here.


1089-1134: Oracle quoting for FK references matches ANSI‑style dialects

Adding oracle alongside postgres, db2, and ibmi for the foreign‑key REFERENCES "prefix"."UserPubs" ("id") expectation is correct: Oracle uses double‑quoted schema and table identifiers in this pattern. The test change aligns with the intended quoting style.


1136-1251: Oracle schema-qualified INSERT/UPDATE SQL expectations look correct

Including oracle with postgres/db2/ibmi for:

  • INSERT INTO "UserPublics" (default schema),
  • INSERT INTO "special"."UserSpecials",
  • UPDATE "special"."UserSpecials"

matches Oracle’s double‑quoted, schema‑qualified SQL style. These assertions are consistent with how other ANSI‑style dialects are handled.


1418-1436: Good: Oracle foreign‑key error case explicitly matches ORA‑00942

The new case 'oracle' branch asserting /^ORA-00942:/ for an invalid referenced table cleanly documents the Oracle‑specific behavior and is in line with how other dialects are handled here.

packages/core/test/unit/sql/update.test.js (1)

137-137: Oracle's UPDATE with LIMIT rownum approach is correct and intentional.

The abstract query generator (packages/core/src/abstract-dialect/query-generator.js:507-516) explicitly handles Oracle UPDATE statements with LIMIT by appending rownum <= N directly to the WHERE clause. This matches the test expectation and is a valid, widely-supported Oracle pattern for limiting UPDATE operations.

The design difference from DELETE (which uses a subquery pattern) is intentional: UPDATE statements evaluate the WHERE clause and apply rownum filtering directly, while DELETE requires a subquery to first identify which specific rows match before deleting them. Both approaches are correct for their respective operations.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (5)
packages/oracle/src/query-generator-typescript.internal.ts (2)

145-154: Remove IF EXISTS from Oracle constraint drop—still not supported.

Line 150 still emits IF EXISTS when options?.ifExists is true, but Oracle does not support IF EXISTS in DROP CONSTRAINT clauses. This will cause a runtime SQL error (ORA-11557).

Either reject ifExists as unsupported (validate via rejectInvalidOptions before emitting SQL), or implement conditional dropping via PL/SQL (query catalog and conditionally execute DROP).


396-408: Fix transaction SQL to use Oracle syntax.

Lines 397 and 407 return 'COMMIT TRANSACTION' and 'ROLLBACK TRANSACTION', which are SQL Server/T-SQL forms. Oracle uses 'COMMIT' and 'ROLLBACK'.

Since the Oracle dialect does not set connectionTransactionMethods: true, these methods will be called and produce invalid SQL at runtime.

Apply this diff:

  commitTransactionQuery() {
-   return 'COMMIT TRANSACTION';
+   return 'COMMIT';
  }

  rollbackTransactionQuery(): string {
    if (this.dialect.supports.connectionTransactionMethods) {
      throw new Error(
        `rollbackTransactionQuery is not supported by the ${this.dialect.name} dialect.`,
      );
    }

-   return 'ROLLBACK TRANSACTION';
+   return 'ROLLBACK';
  }
packages/oracle/src/query-generator.js (3)

320-322: Fix array splice to avoid index shifting.

Splicing options.indexes by iterating idxToDelete in forward order causes index misalignment. After the first splice, all subsequent indices shift down, making remaining indices in idxToDelete point to wrong elements.

Apply this diff to splice in reverse order:

-    idxToDelete.forEach(idx => {
-      options.indexes.splice(idx, 1);
-    });
+    // Splice in reverse order to avoid index shifting
+    for (let i = idxToDelete.length - 1; i >= 0; i--) {
+      options.indexes.splice(idxToDelete[i], 1);
+    }

690-696: Fix auto-increment detection across multiple rows.

allColumns[key] is overwritten for each row (line 692-695), so only the last row's value is checked. A mix of null and non-null values for an auto-increment column is only detected when the last row is null. If the last row is non-null, the mix passes silently.

Track both "sawNull" and "sawNonNull" per column and throw if both are observed.

Apply this diff:

+    const sawNull = {};
+    const sawNonNull = {};
+
     for (const fieldValueHash of fieldValueHashes) {
       forOwn(fieldValueHash, (value, key) => {
-        allColumns[key] =
-          fieldMappedAttributes[key] &&
-          fieldMappedAttributes[key].autoIncrement === true &&
-          value === null;
+        if (!fieldMappedAttributes[key]?.autoIncrement) {
+          allColumns[key] = false;
+          return;
+        }
+
+        const isNull = value === null;
+        sawNull[key] ||= isNull;
+        sawNonNull[key] ||= !isNull;
+
+        if (sawNull[key] && sawNonNull[key]) {
+          throw new Error(
+            'For an auto-increment column either all rows must be null or non-null; a mix is not allowed.',
+          );
+        }
+
+        allColumns[key] = sawNull[key];
       });
     }

898-899: Fix unreachable DOUBLE condition.

Line 898 compares attribute.type === 'DOUBLE' against a string, but attribute.type is a DataType instance. This condition is unreachable and will never execute.

Change to use instanceof:

-    } else if (attribute.autoIncrement) {
+    if (attribute.autoIncrement) {
       template = ' NUMBER(*,0) GENERATED BY DEFAULT ON NULL AS IDENTITY';
-    } else if (attribute.type && attribute.type === 'DOUBLE') {
-      template = attribute.type.toSql();
-    } else if (attribute.type) {
+    } else if (attribute.type instanceof DataTypes.DOUBLE) {
+      template = attribute.type.toSql();
+    } else if (attribute.type) {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a26e0f9 and c0b2f3d.

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

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

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

Files:

  • packages/oracle/src/query-generator.js
packages/**/src/**/*.js

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

Write all new implementations in TypeScript; avoid creating new .js files in src

Files:

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

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

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

Files:

  • packages/oracle/src/query-generator.js
🧠 Learnings (9)
📓 Common learnings
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/*/src/query-generator.{js,ts} : Implement dialect feature SQL in query-generator.ts (preferred) or legacy query-generator.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/**/src/**/query-generator.{js,ts} : When modifying query generation, update both the base query generator and all dialect-specific implementations

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • packages/oracle/src/query-generator.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Co-locate TypeScript type definitions with their implementation files

Applied to files:

  • packages/oracle/src/query-generator.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/**/src/**/*.js : Write all new implementations in TypeScript; avoid creating new .js files in src

Applied to files:

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

Applied to files:

  • packages/oracle/src/query-generator.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Prefer TypeScript for all new code; only modify existing .js files when necessary

Applied to files:

  • packages/oracle/src/query-generator.js
🧬 Code graph analysis (2)
packages/oracle/src/query-generator-typescript.internal.ts (5)
packages/oracle/src/query-generator.internal.ts (1)
  • OracleQueryGeneratorInternal (11-54)
packages/core/src/utils/check.ts (1)
  • rejectInvalidOptions (33-54)
packages/core/src/utils/object.ts (1)
  • EMPTY_SET (16-16)
packages/core/src/utils/string.ts (1)
  • generateIndexName (72-107)
packages/core/src/utils/model-utils.ts (1)
  • extractModelDefinition (35-45)
packages/oracle/src/query-generator.js (6)
packages/oracle/src/query-generator.d.ts (1)
  • OracleQueryGenerator (5-5)
packages/oracle/src/query-generator-typescript.internal.ts (1)
  • OracleQueryGeneratorTypeScript (34-409)
packages/core/src/utils/check.ts (1)
  • rejectInvalidOptions (33-54)
packages/core/src/abstract-dialect/query-generator.js (5)
  • CREATE_TABLE_QUERY_SUPPORTABLE_OPTIONS (43-51)
  • CREATE_TABLE_QUERY_SUPPORTABLE_OPTIONS (43-51)
  • ADD_COLUMN_QUERY_SUPPORTABLE_OPTIONS (52-52)
  • ADD_COLUMN_QUERY_SUPPORTABLE_OPTIONS (52-52)
  • DataTypes (38-38)
packages/core/src/utils/object.ts (2)
  • EMPTY_SET (16-16)
  • getObjectFromMap (227-237)
packages/core/src/utils/query-builder-utils.ts (1)
  • defaultValueSchemable (15-35)
🪛 Biome (2.1.2)
packages/oracle/src/query-generator.js

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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


[error] 8-9: Illegal use of an import declaration outside of a module

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


[error] 25-26: Illegal use of an import declaration outside of a module

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


[error] 154-1209: Illegal use of an export declaration outside of a module

not allowed inside scripts

(parse)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Upload install and build artifact (Node 18)
  • GitHub Check: Upload install and build artifact (Node 20)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (3)
packages/oracle/src/dialect.ts (3)

86-101: Address the documentation URL and minimum version mismatch.

The dataTypesDocumentationUrl points to Oracle Database 23 documentation, while minimumDatabaseVersion is set to '18.0.0'. Oracle 23 introduced features not available in Oracle 18 (native JSON SQL type, SQL Domains, spatial enhancements). This mismatch may confuse users running Oracle 18 who reference the Oracle 23 documentation.

Consider either:

  • Updating dataTypesDocumentationUrl to reference Oracle 18 documentation to align with the minimum version, or
  • Increasing minimumDatabaseVersion to '23.0.0' if Oracle 23 features are required

109-111: CRITICAL: Fix runtime error in getDefaultSchema for non-replication setups.

getDefaultSchema will throw a runtime error when replication or write is undefined, which is the default for non-replication configurations. The optional chaining on username is evaluated only after the property access to replication.write succeeds, so it doesn't prevent the error.

Apply this diff to safely handle both replication and non-replication configs:

 getDefaultSchema(): string {
-  return this.sequelize.options.replication.write.username?.toUpperCase() ?? '';
+  const replicationUser = this.sequelize.options.replication?.write?.username;
+  const directUser = this.sequelize.options.username;
+
+  const username = replicationUser ?? directUser;
+
+  return username?.toUpperCase() ?? '';
 }

135-182: Fix typos in comments.

Two minor typos remain in the validation comments:

  • Line 141: "indicated y the last empty string" → "indicated by the last empty string"
  • Line 146: Comment references "TIMESTAM_TZ" → should be "TIMESTAMP_TZ"

Apply this diff:

     // Validate that the split result has exactly three parts (function name, parameters, and an empty string)
-    // and that there are no additional SQL commands after the function call (indicated y the last empty string).
+    // and that there are no additional SQL commands after the function call (indicated by the last empty string).
     if (splitVal.length !== 3 || splitVal[2] !== '') {
       throw new Error('Invalid SQL function call.'); // Error if function call has unexpected format
     }

-    // Extract the function name (either 'TO_TIMESTAMP_TZ' or 'TO_DATE') and the contents inside the parentheses
+    // Extract the function name (either 'TO_TIMESTAMP_TZ' or 'TO_DATE') and the contents inside the parentheses
     const functionName = splitVal[0].trim(); // Function name should be 'TO_TIMESTAMP_TZ' or 'TO_DATE'
🧹 Nitpick comments (1)
packages/oracle/src/dialect.ts (1)

16-16: Convert empty interface to type alias or add properties.

The empty interface is flagged by static analysis and is equivalent to {}. Consider using a type alias instead for clarity, or add Oracle-specific dialect options if needed.

Apply this diff to convert to a type alias:

-export interface OracleDialectOptions {}
+export type OracleDialectOptions = Record<string, never>;

Or, if Oracle-specific options are planned, add a comment:

-export interface OracleDialectOptions {}
+// TODO: Add Oracle-specific dialect options as needed
+export interface OracleDialectOptions {}

Based on static analysis hints.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c0b2f3d and 2ac298a.

📒 Files selected for processing (1)
  • packages/oracle/src/dialect.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
packages/*/src/dialect.ts

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

When adding a dialect feature, update packages/{dialect}/src/dialect.ts to declare feature support flags

Files:

  • packages/oracle/src/dialect.ts
🧠 Learnings (6)
📓 Common learnings
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/*/src/query-generator.{js,ts} : Implement dialect feature SQL in query-generator.ts (preferred) or legacy query-generator.js
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/**/src/**/query-generator.{js,ts} : When modifying query generation, update both the base query generator and all dialect-specific implementations
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/*/src/query-generator.{js,ts} : Implement dialect feature SQL in query-generator.ts (preferred) or legacy query-generator.js

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • packages/oracle/src/dialect.ts
🧬 Code graph analysis (1)
packages/oracle/src/dialect.ts (4)
packages/core/src/abstract-dialect/dialect.ts (1)
  • SupportableNumericOptions (15-19)
packages/oracle/src/connection-manager.ts (1)
  • OracleConnectionOptions (26-33)
packages/core/src/utils/sql.ts (1)
  • createSpecifiedOrderedBindCollector (401-419)
packages/oracle/src/_internal/connection-options.ts (1)
  • CONNECTION_OPTION_NAMES (75-84)
🪛 Biome (2.1.2)
packages/oracle/src/dialect.ts

[error] 16-16: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Upload install and build artifact (Node 18)
  • GitHub Check: Upload install and build artifact (Node 20)

@cloudeyes
Copy link

I'm eagerly awaiting the @sequelize/oracle release for my legacy-app migration. @sudarshan12s, could you please let me know the estimated release date? Is there a chance it might be released within a month?

@WikiRik
Copy link
Member

WikiRik commented Dec 15, 2025

I'm eagerly awaiting the @sequelize/oracle release for my legacy-app migration. @sudarshan12s, could you please let me know the estimated release date? Is there a chance it might be released within a month?

Doing another review is first on my list for this Wednesday, a release somewhere in January is possible

Copy link
Member

@WikiRik WikiRik left a comment

Choose a reason for hiding this comment

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

I think these should be the final comments

Comment on lines 85 to 100
constructor(sequelize: Sequelize, options: OracleDialectOptions) {
super({
dataTypesDocumentationUrl:
'https://docs.oracle.com/en/database/oracle/oracle-database/23/sqlrf/Data-Types.html',
minimumDatabaseVersion: '18.4.0',
identifierDelimiter: '"',
name: 'oracle',
options,
sequelize,
dataTypeOverrides: DataTypes,
});

this.connectionManager = new OracleConnectionManager(this);
this.queryGenerator = new OracleQueryGenerator(this);
this.queryInterface = new OracleQueryInterface(this);
}
Copy link
Member

Choose a reason for hiding this comment

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

This seems like a valid comment

Comment on lines 1037 to 1079
// handleSequelizeMethod(smth, tableName, factory, options, prepend) {
// let str;
// if (smth instanceof Utils.Json) {
// // Parse nested object
// if (smth.conditions) {
// const conditions = this.parseConditionObject(smth.conditions).map(condition =>
// `${this.jsonPathExtractionQuery(condition.path[0], _.tail(condition.path))} = '${condition.value}'`
// );

// return conditions.join(' AND ');
// }
// if (smth.path) {

// // Allow specifying conditions using the sqlite json functions
// if (this._checkValidJsonStatement(smth.path)) {
// str = smth.path;
// } else {
// // Also support json property accessors
// const paths = _.toPath(smth.path);
// const column = paths.shift();
// str = this.jsonPathExtractionQuery(column, paths);
// }
// if (smth.value) {
// str += util.format(' = %s', this.escape(smth.value));
// }

// return str;
// }
// }
// if (smth instanceof Utils.Cast) {
// if (smth.val instanceof Utils.SequelizeMethod) {
// str = this.handleSequelizeMethod(smth.val, tableName, factory, options, prepend);
// if (smth.type === 'boolean') {
// str = `(CASE WHEN ${str}='true' THEN 1 ELSE 0 END)`;
// return `CAST(${str} AS NUMBER)`;
// } if (smth.type === 'timestamptz' && /json_value\(/.test(str)) {
// str = str.slice(0, -1);
// return `${str} RETURNING TIMESTAMP WITH TIME ZONE)`;
// }
// }
// }
// return super.handleSequelizeMethod(smth, tableName, factory, options, prepend);
// }
Copy link
Member

Choose a reason for hiding this comment

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

This should be removed if it's not used

Copy link
Author

@sudarshan12s sudarshan12s Dec 18, 2025

Choose a reason for hiding this comment

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

Yes removed it.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (10)
packages/core/test/unit/query-generator/create-schema-query.test.ts (2)

16-16: Role quoting and idempotency issues remain in Oracle CREATE SCHEMA expectation.

The password security concern from the previous review has been resolved (now using IDENTIFIED EXTERNALLY). However, two issues remain:

  1. Incorrect role quoting: Oracle predefined role names like CONNECT should be granted without quotes. The expectation should use GRANT CONNECT TO "mySchema" instead of GRANT "CONNECT" TO "mySchema" (and similarly for other system privileges).

  2. Partial idempotency: The grants only execute when NOT USER_FOUND (i.e., user newly created). If the user already exists but lacks required permissions, no grants are re-applied, potentially leaving the user in an incomplete state.

These issues should be addressed in the implementation (packages/oracle/src/query-generator-typescript.internal.ts) and the test expectation updated accordingly.


128-132: Incomplete list of invalid options for Oracle in combined options test.

The Oracle expectation only lists 3 invalid options but should list 6. Based on the individual option tests:

  • Line 65: comment is only supported by Snowflake, so Oracle should reject it
  • Line 74: ifNotExists is explicitly rejected by Oracle
  • Line 84: replace is only supported by MariaDB and Snowflake, so Oracle should reject it
🔎 Apply this diff to add the missing invalid options:
 oracle: buildInvalidOptionReceivedError('createSchemaQuery', dialectName, [
   'authorization',
   'charset',
   'collate',
+  'comment',
+  'ifNotExists',
+  'replace',
 ]),
packages/oracle/src/query-generator-typescript.internal.ts (2)

396-408: Fix transaction SQL to use Oracle syntax.

Lines 397 and 407 return T-SQL syntax (COMMIT TRANSACTION / ROLLBACK TRANSACTION), which Oracle does not support. Oracle requires COMMIT and ROLLBACK without the TRANSACTION keyword. Since the Oracle dialect does not set connectionTransactionMethods: true, these methods will be invoked and produce runtime SQL errors.

🔎 Apply this diff to fix the SQL syntax:
  commitTransactionQuery() {
-   return 'COMMIT TRANSACTION';
+   return 'COMMIT';
  }

  rollbackTransactionQuery(): string {
    if (this.dialect.supports.connectionTransactionMethods) {
      throw new Error(
        `rollbackTransactionQuery is not supported by the ${this.dialect.name} dialect.`,
      );
    }

-   return 'ROLLBACK TRANSACTION';
+   return 'ROLLBACK';
  }

140-157: IF EXISTS in removeConstraintQuery is still present despite being marked resolved.

The past review comment flagged that Oracle does not support IF EXISTS in DROP CONSTRAINT clauses (line 150), marked as "✅ Addressed in commit 8454ae2." However, the code still emits IF EXISTS when options?.ifExists is true for sys-prefixed constraints, which will cause ORA-11557 runtime errors.

Either validate and reject the ifExists option before building SQL, or implement PL/SQL conditional logic querying the data dictionary to check constraint existence.

packages/oracle/src/query-generator.js (3)

320-322: Fix index shifting bug when splicing options.indexes.

Splicing array elements while iterating idxToDelete in forward order causes index misalignment. After the first splice, remaining indices point to wrong elements. This results in incorrect indexes being removed.

🔎 Apply this diff to fix (reverse iteration):
-    idxToDelete.forEach(idx => {
-      options.indexes.splice(idx, 1);
-    });
+    // Splice in reverse order to avoid index shifting
+    for (let i = idxToDelete.length - 1; i >= 0; i--) {
+      options.indexes.splice(idxToDelete[i], 1);
+    }

Or use filter to rebuild the array:

-    idxToDelete.forEach(idx => {
-      options.indexes.splice(idx, 1);
-    });
+    const idxSet = new Set(idxToDelete);
+    options.indexes = options.indexes.filter((_, idx) => !idxSet.has(idx));

898-899: Fix unreachable DOUBLE condition in attributeToSQL.

Line 898 compares attribute.type === 'DOUBLE' (strict equality with a string), but attribute.type is a DataType class instance. This condition is unreachable dead code and inconsistent with other branches that use instanceof (lines 870, 882, 889).

🔎 Apply this diff to fix:
-  } else if (attribute.type && attribute.type === 'DOUBLE') {
-    template = attribute.type.toSql();
+  } else if (attribute.type instanceof DataTypes.DOUBLE) {
+    template = attribute.type.toSql();

690-696: Fix auto-increment detection to check all rows, not just the last one.

The loop overwrites allColumns[key] for each row (lines 692-695), so only the last row's value determines the final state. A mix of null and non-null values for an auto-increment column is only detected when the last row is null. If the last row is non-null, the mix passes silently.

🔎 Apply this diff to enforce the invariant across all rows:
+    const sawNull = {};
+    const sawNonNull = {};
+
     for (const fieldValueHash of fieldValueHashes) {
       forOwn(fieldValueHash, (value, key) => {
-        allColumns[key] =
-          fieldMappedAttributes[key] &&
-          fieldMappedAttributes[key].autoIncrement === true &&
-          value === null;
+        if (!fieldMappedAttributes[key]?.autoIncrement) {
+          allColumns[key] = false;
+          return;
+        }
+
+        const isNull = value === null;
+        sawNull[key] ||= isNull;
+        sawNonNull[key] ||= !isNull;
+
+        if (sawNull[key] && sawNonNull[key]) {
+          throw new Error(
+            'For an auto-increment column either all rows must be null or non-null; a mix is not allowed.',
+          );
+        }
+
+        allColumns[key] = sawNull[key];
       });
     }
packages/oracle/src/dialect.ts (3)

109-111: getDefaultSchema will throw in non-replication setups.

Line 110 accesses this.sequelize.options.replication.write.username without safe optional chaining on replication or write. In default (non-replication) configurations, replication or write will be undefined, causing a runtime error before the optional chaining on username is evaluated.

🔎 Apply this diff to fix:
  getDefaultSchema(): string {
-   return this.sequelize.options.replication.write.username?.toUpperCase() ?? '';
+   const replicationUser = this.sequelize.options.replication?.write?.username;
+   const directUser = this.sequelize.options.username;
+   const username = replicationUser ?? directUser;
+   return username?.toUpperCase() ?? '';
  }

88-90: Update documentation URL to match minimum database version.

Line 88 links to Oracle Database 23 documentation, but line 89 declares minimumDatabaseVersion: '18.0.0'. Oracle 23 introduced features unavailable in 18 (native JSON SQL type, SQL Domains, spatial enhancements). The documentation should align with the declared minimum version.

🔎 Apply this diff:
-    dataTypesDocumentationUrl:
-      'https://docs.oracle.com/en/database/oracle/oracle-database/23/sqlrf/Data-Types.html',
+    dataTypesDocumentationUrl:
+      'https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/Data-Types.html',

141-146: Fix typos in comments.

Line 141: "indicated y the last empty string" should be "indicated by the last empty string"

Line 146: Comment mentions "TIMESTAM_TZ" but should be "TIMESTAMP_TZ"

🔎 Apply this diff:
     // Validate that the split result has exactly three parts (function name, parameters, and an empty string)
-    // and that there are no additional SQL commands after the function call (indicated y the last empty string).
+    // and that there are no additional SQL commands after the function call (indicated by the last empty string).
     if (splitVal.length !== 3 || splitVal[2] !== '') {
       throw new Error('Invalid SQL function call.'); // Error if function call has unexpected format
     }

-    // Extract the function name (either 'TO_TIMESTAM_TZ' or 'TO_DATE') and the contents inside the parentheses
+    // Extract the function name (either 'TO_TIMESTAMP_TZ' or 'TO_DATE') and the contents inside the parentheses
🧹 Nitpick comments (2)
packages/core/src/abstract-dialect/dialect.ts (1)

244-247: Consider using consistent comment style.

The comment uses // inline style, while other properties in DialectSupports use /* */ block comment style. This is a minor consistency nit.

🔎 Apply this diff for consistent comment style:
   dropTable: {
     cascade: boolean;
-    concurrentDropConstraints: boolean; // If Constraints on same table can be dropped concurrently.
+    /** Whether constraints on the same table can be dropped concurrently */
+    concurrentDropConstraints: boolean;
   };
packages/oracle/src/dialect.ts (1)

16-16: Consider using a type alias for the empty OracleDialectOptions.

The empty OracleDialectOptions interface is functionally equivalent to {}. While valid as a placeholder for future options, a type alias is slightly cleaner for empty definitions.

🔎 Apply this diff if preferred:
-export interface OracleDialectOptions {}
+export type OracleDialectOptions = Record<string, never>;

Or simply:

-export interface OracleDialectOptions {}
+// eslint-disable-next-line @typescript-eslint/no-empty-object-type
+export interface OracleDialectOptions {}

Based on static analysis hint from Biome.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f0517e9 and a2e1522.

📒 Files selected for processing (5)
  • packages/core/src/abstract-dialect/dialect.ts (5 hunks)
  • packages/core/test/unit/query-generator/create-schema-query.test.ts (4 hunks)
  • packages/oracle/src/dialect.ts (1 hunks)
  • packages/oracle/src/query-generator-typescript.internal.ts (1 hunks)
  • packages/oracle/src/query-generator.js (1 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
packages/**/test/**/*.test.ts

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

Name unit tests with the .test.ts suffix

Files:

  • packages/core/test/unit/query-generator/create-schema-query.test.ts
packages/**/test/**/*.test.{js,ts}

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

packages/**/test/**/*.test.{js,ts}: Use Support.getTestDialectTeaser() for dialect-specific test descriptions
Use beforeEach/afterEach to sync with { force: true } and close Sequelize instances in tests
In tests, skip unsupported behavior with dialect.supports.featureName checks

Files:

  • packages/core/test/unit/query-generator/create-schema-query.test.ts
packages/*/src/query-generator.{js,ts}

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

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

Files:

  • packages/oracle/src/query-generator.js
packages/**/src/**/*.js

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

Write all new implementations in TypeScript; avoid creating new .js files in src

Files:

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

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

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

Files:

  • packages/oracle/src/query-generator.js
packages/*/src/dialect.ts

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

When adding a dialect feature, update packages/{dialect}/src/dialect.ts to declare feature support flags

Files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • packages/oracle/src/query-generator.js
  • packages/core/src/abstract-dialect/dialect.ts
  • packages/oracle/src/dialect.ts
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Co-locate TypeScript type definitions with their implementation files

Applied to files:

  • packages/oracle/src/query-generator.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Applies to packages/**/src/**/*.js : Write all new implementations in TypeScript; avoid creating new .js files in src

Applied to files:

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

Applied to files:

  • packages/oracle/src/query-generator.js
📚 Learning: 2025-09-29T19:49:24.715Z
Learnt from: CR
Repo: sequelize/sequelize PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-29T19:49:24.715Z
Learning: Prefer TypeScript for all new code; only modify existing .js files when necessary

Applied to files:

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

Applied to files:

  • packages/core/src/abstract-dialect/dialect.ts
  • packages/oracle/src/dialect.ts
🧬 Code graph analysis (2)
packages/core/test/unit/query-generator/create-schema-query.test.ts (1)
packages/core/src/utils/check.ts (1)
  • buildInvalidOptionReceivedError (88-96)
packages/oracle/src/query-generator.js (3)
packages/oracle/src/query-generator-typescript.internal.ts (1)
  • OracleQueryGeneratorTypeScript (34-409)
packages/core/src/utils/check.ts (1)
  • rejectInvalidOptions (33-54)
packages/core/src/abstract-dialect/query-generator.js (4)
  • CREATE_TABLE_QUERY_SUPPORTABLE_OPTIONS (43-51)
  • CREATE_TABLE_QUERY_SUPPORTABLE_OPTIONS (43-51)
  • ADD_COLUMN_QUERY_SUPPORTABLE_OPTIONS (52-52)
  • ADD_COLUMN_QUERY_SUPPORTABLE_OPTIONS (52-52)
🪛 Biome (2.1.2)
packages/oracle/src/query-generator.js

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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


[error] 8-9: Illegal use of an import declaration outside of a module

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


[error] 25-26: Illegal use of an import declaration outside of a module

not allowed inside scripts

(parse)


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

not allowed inside scripts

(parse)


[error] 154-1165: Illegal use of an export declaration outside of a module

not allowed inside scripts

(parse)

packages/oracle/src/dialect.ts

[error] 16-16: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Upload install and build artifact (Node 18)
  • GitHub Check: Upload install and build artifact (Node 20)
🔇 Additional comments (4)
packages/core/src/abstract-dialect/dialect.ts (4)

71-75: LGTM!

The new returnIntoValues and topLevelOrderByRequired capability flags are well-documented and properly typed. These additions support Oracle-specific behaviors for RETURNING INTO binds and ORDER BY requirements.


116-118: LGTM!

The type expansion to false | string properly models dialects that don't support ignore-duplicates semantics (Oracle, DB2, IBMi) versus those that use dialect-specific keywords.


336-337: LGTM!

Default values of false for both properties are appropriate, requiring Oracle (and any other dialect needing these features) to explicitly opt-in.


471-474: LGTM!

Defaulting concurrentDropConstraints: true maintains backward compatibility for existing dialects while allowing Oracle to override this to false in its dialect implementation.

@sudarshan12s
Copy link
Author

I think these should be the final comments

Thanks. I have addressed all the comments.

@sudarshan12s sudarshan12s requested a review from WikiRik December 18, 2025 13:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants