Skip to content

Bump minimum SQLite version to 3.8#32923

Merged
kamipo merged 1 commit into
rails:masterfrom
yahonda:bump_sqlite3_version_to_38
May 21, 2018
Merged

Bump minimum SQLite version to 3.8#32923
kamipo merged 1 commit into
rails:masterfrom
yahonda:bump_sqlite3_version_to_38

Conversation

@yahonda

@yahonda yahonda commented May 17, 2018

Copy link
Copy Markdown
Member

Summary

These OS versions have SQLite 3.8 or higher by default.

  • macOS 10.10 (Yosemite) or higher
  • Ubuntu 14.04 LTS or higher

Raising the minimum version of SQLite 3.8 introduces these changes:

@rails-bot

Copy link
Copy Markdown

r? @rafaelfranca

(@rails-bot has picked a reviewer for you, use r? to override)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This isn't true for the abstract adapter, so I think we still need the conditional for now. (Or, if we're okay to declare that a baseline requirement, it'd need to change there too.)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Make sense. Reverted this change.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

👍 happy to kill this one

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we can just delete this method and leave the inherited one. And as we're removing the caller, I think we should deprecate the method in the abstract adapter.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Agreed. deprecated supports_multi_insert? method.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm not sure whether this should be using an adapter type check, or the supports_partial_index? condition (as we do for index sort order below). Agree it definitely shouldn't be both.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Changed the condition to use ActiveRecord::Base.connection.supports_partial_index? only.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let's add a to_s to AbstractAdapter::Version.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I'm adding to_s method to AbstractAdapter::Version. Still, I need to call sqlite_version.to_s
and users will not see unnecessary exceptions here.

@yahonda yahonda force-pushed the bump_sqlite3_version_to_38 branch from 3e1c66d to b2e1e67 Compare May 20, 2018 13:24

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@version.join(".")

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Updated.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is there any reason not to use raise unlike other adapters?

if version < "5.1.10"
raise "Your version of MySQL (#{version_string}) is too old. Active Record supports MySQL >= 5.1.10."
end

if postgresql_version < 90100
raise "Your version of PostgreSQL (#{postgresql_version}) is too old. Active Record supports PostgreSQL >= 9.1."
end

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I'm thinking about implementing version check like #28939 just showing a necessary message for users by omitting stack traces like this.

$ sqlite3 --version
3.7.9 2011-11-01 00:52:41 c7c6050ef060877ebe77b41d959e9df13f8c9b5e
$ bundle exec ruby -w -Itest test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
Using sqlite3
"Your version of SQLite (3.7.9) is too old. Active Record supports SQLite >= 3.8."
$

I'd like to get some feedback on this change. I agree to make all bundled adapters behave similarly for the database version check. then I'll revert this change or open another pull request to change mysql2 and posgresql adapter version checks.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Usually, a library does/should not terminate a process itself.
If a library may terminate a process itself, it hard to handle that case by users.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Understood. Thanks for the comment.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Unneeded the redundant to_s in string interpolation.

@yahonda yahonda force-pushed the bump_sqlite3_version_to_38 branch 2 times, most recently from 56cd2de to c13883f Compare May 20, 2018 23:58
These OS versions have SQLite 3.8 or higher by default.

- macOS 10.10 (Yosemite) or higher
- Ubuntu 14.04 LTS or higher

Raising the minimum version of SQLite 3.8 introduces these changes:

- All of bundled adapters support `supports_multi_insert?`
- SQLite 3.8 always satisifies `supports_foreign_keys_in_create?` and `supports_partial_index?`
- sqlite adapter can support `alter_table` method for foreign key referenced tables by rails#32865
- Deprecated `supports_multi_insert?` method
@yahonda yahonda force-pushed the bump_sqlite3_version_to_38 branch from c13883f to d1a74c1 Compare May 21, 2018 04:57
kamipo added a commit that referenced this pull request May 21, 2018
To prevent redundant `to_s` like #32923 (comment)
automatically in the future.
@kamipo kamipo merged commit 054893d into rails:master May 21, 2018
yahonda added a commit to yahonda/rails that referenced this pull request May 22, 2018
Unlike other databases, changing SQLite3 table definitions need to create a temporary table.
While changing table operations, the original table needs dropped which caused
`SQLite3::ConstraintException: FOREIGN KEY constraint failed` if the table is referenced by foreign keys.
This pull request disables foreign keys by `disable_referential_integrity`.

Also `disable_referential_integrity` method needs to execute `defer_foreign_keys = ON`
to defer re-enabling foreign keys until the transaction is committed.

https://www.sqlite.org/pragma.html#pragma_defer_foreign_keys

Fixes rails#31988

- This `defer_foreign_keys = ON` has been supported since SQLite 3.8.0
https://www.sqlite.org/releaselog/3_8_0.html and Rails 6 requires SQLite 3.8 rails#32923 now

- <Models>.reset_column_information added to address `ActiveModel::UnknownAttributeError`

```
Error:
ActiveRecord::Migration::ForeignKeyChangeColumnTest#test_change_column_of_parent_table:
ActiveModel::UnknownAttributeError: unknown attribute 'name' for ActiveRecord::Migration::ForeignKeyChangeColumnTest::Post.
```
@yahonda yahonda deleted the bump_sqlite3_version_to_38 branch May 31, 2018 03:41
yahonda added a commit to yahonda/rails that referenced this pull request Jun 1, 2018
yahonda added a commit to yahonda/oracle-enhanced that referenced this pull request Apr 14, 2026
History:

- Deprecated upstream in rails/rails#32923 / rails/rails@d1a74c1e
  (2018-05-21, "Bump minimum SQLite version to 3.8") — the deprecation
  was added as part of raising SQLite's minimum so every bundled
  adapter satisfied supports_multi_insert?.
- Removed upstream in rails/rails@cbf43df2 (2020-05-07,
  "Remove deprecated
  ActiveRecord::ConnectionAdapters::AbstractAdapter#supports_multi_insert?"),
  shipping in Rails 6.1.

oracle-enhanced kept a local override that returned true for Oracle
11.2+, which is every Oracle version the gemspec allows (minimum is
11.2). That made the "else" branches — the per-statement INSERT
fallback and the spec contexts gated on it — unreachable on any
supported configuration.

- Drop supports_multi_insert? from OracleEnhancedAdapter.
- Collapse OracleEnhanced::SchemaVersionsFormatter#format to always
  emit INSERT ALL ... SELECT * FROM DUAL for Array input; the scalar
  (assume_migrated_upto_version) path is unchanged.
- Unwrap the "multi insert is supported" contexts in
  structure_dump_spec and schema_statements_spec and delete the
  matching "multi insert is NOT supported" contexts, which used to
  skip unconditionally on every Oracle we run in CI.

No behavior change on any supported Oracle.
yahonda added a commit to yahonda/oracle-enhanced that referenced this pull request Apr 14, 2026
History:

- Deprecated upstream in rails/rails#32923 / rails/rails@d1a74c1e
  (2018-05-21, "Bump minimum SQLite version to 3.8") — the deprecation
  was added as part of raising SQLite's minimum so every bundled
  adapter satisfied supports_multi_insert?.
- Removed upstream in rails/rails@cbf43df2 (2020-05-07,
  "Remove deprecated
  ActiveRecord::ConnectionAdapters::AbstractAdapter#supports_multi_insert?"),
  shipping in Rails 6.1.

oracle-enhanced kept a local override that returned true for Oracle
11.2+, which is every Oracle version the gemspec allows (minimum is
11.2). That made the "else" branches — the per-statement INSERT
fallback in insert_versions_sql and the spec contexts gated on it —
unreachable on any supported configuration.

- Drop supports_multi_insert? from OracleEnhancedAdapter.
- Collapse insert_versions_sql in oracle_enhanced/schema_statements.rb
  to always emit INSERT ALL ... SELECT * FROM DUAL for Array input;
  the scalar (assume_migrated_upto_version) path is unchanged.
- Unwrap the "multi insert is supported" contexts in
  structure_dump_spec and schema_statements_spec and delete the
  matching "multi insert is NOT supported" contexts, which used to
  skip unconditionally on every Oracle we run in CI.

No behavior change on any supported Oracle.
yahonda added a commit to yahonda/oracle-enhanced that referenced this pull request Apr 14, 2026
History:

- Deprecated upstream in rails/rails#32923 / rails/rails@d1a74c1e
  (2018-05-21, "Bump minimum SQLite version to 3.8") — the deprecation
  was added as part of raising SQLite's minimum so every bundled
  adapter satisfied supports_multi_insert?.
- Removed upstream in rails/rails@cbf43df2 (2020-05-07,
  "Remove deprecated
  ActiveRecord::ConnectionAdapters::AbstractAdapter#supports_multi_insert?"),
  shipping in Rails 6.1.

oracle-enhanced kept a local override:

    def supports_multi_insert?
      database_version.to_s >= [11, 2].to_s
    end

which gated the "INSERT ALL ... SELECT * FROM DUAL" multi-row form
against a per-statement fallback. Strictly speaking this predicate did
carry a behavior difference — on Oracle < 11.2 it returned false and
the fallback kicked in — but every Oracle version the adapter claims
to support (the gemspec minimum, README, and test_11g CI job all land
on 11.2.0.2) makes it return true, so the fallback branch and the spec
contexts gated on it are unreachable on any configuration we still
support.

Changes:

- Drop supports_multi_insert? from OracleEnhancedAdapter.
- Collapse insert_versions_sql in oracle_enhanced/schema_statements.rb
  to always emit INSERT ALL ... SELECT * FROM DUAL for Array input;
  the scalar (assume_migrated_upto_version) path is unchanged.
- Unwrap the "multi insert is supported" contexts in
  structure_dump_spec and schema_statements_spec and delete the
  matching "multi insert is NOT supported" contexts, which used to
  skip unconditionally on every Oracle we run in CI.

Technically a behavior change on Oracle < 11.2 (per-statement INSERT
with SQL*Plus "/" separators becomes a single INSERT ALL); those
versions are below the supported minimum and not exercised in CI, so
no supported configuration is affected.
yahonda added a commit to yahonda/oracle-enhanced that referenced this pull request Apr 14, 2026
History:

- Deprecated upstream in rails/rails#32923 / rails/rails@d1a74c1e
  (2018-05-21, "Bump minimum SQLite version to 3.8") — the deprecation
  was added as part of raising SQLite's minimum so every bundled
  adapter satisfied supports_multi_insert?.
- Removed upstream in rails/rails@cbf43df2 (2020-05-07,
  "Remove deprecated
  ActiveRecord::ConnectionAdapters::AbstractAdapter#supports_multi_insert?"),
  shipping in Rails 6.1.

oracle-enhanced kept a local override:

    def supports_multi_insert?
      database_version.to_s >= [11, 2].to_s
    end

which gated the "INSERT ALL ... SELECT * FROM DUAL" multi-row form
against a per-statement fallback. Strictly speaking this predicate did
carry a behavior difference — on Oracle < 11.2 it returned false and
the fallback kicked in — but every Oracle version the adapter claims
to support (the gemspec minimum, README, and test_11g CI job all land
on 11.2.0.2) makes it return true, so the fallback branch and the spec
contexts gated on it are unreachable on any configuration we still
support.

Changes:

- Drop supports_multi_insert? from OracleEnhancedAdapter.
- Collapse insert_versions_sql in oracle_enhanced/schema_statements.rb
  to always emit INSERT ALL ... SELECT * FROM DUAL for Array input;
  the scalar (assume_migrated_upto_version) path is unchanged.
- Unwrap the "multi insert is supported" contexts in
  structure_dump_spec and schema_statements_spec and delete the
  matching "multi insert is NOT supported" contexts, which used to
  skip unconditionally on every Oracle we run in CI.

Technically a behavior change on Oracle < 11.2 (per-statement INSERT
with SQL*Plus "/" separators becomes a single INSERT ALL); those
versions are below the supported minimum and not exercised in CI, so
no supported configuration is affected.
yahonda added a commit to yahonda/oracle-enhanced that referenced this pull request Apr 14, 2026
Rails removed supports_multi_insert? from AbstractAdapter years ago:

- Deprecated in rails/rails#32923 / rails/rails@d1a74c1e (2018-05-21,
  "Bump minimum SQLite version to 3.8") as part of raising SQLite's
  minimum so every bundled adapter satisfied the predicate, at which
  point it stopped carrying information.
- Removed in rails/rails@cbf43df2 (2020-05-07, "Remove deprecated
  ActiveRecord::ConnectionAdapters::AbstractAdapter#supports_multi_insert?"),
  shipping in Rails 6.1.

oracle-enhanced kept a local override:

    def supports_multi_insert?
      database_version.to_s >= [11, 2].to_s
    end

which has no analog upstream anymore. Delete the predicate.

Why not also collapse the two-branch insert_versions_sql, as would be
the usual follow-up:

The 11.2 cutoff is not cosmetic and not about when Oracle introduced
INSERT ALL (that was Oracle 9i Release 1, 2001). It encodes a specific
restriction documented in the Oracle 10.2 and 11.1 SQL References and
empirically reproduced in rsim#1084:

    "In a multitable insert, all of the insert_into_clauses cannot
     combine to specify more than 999 target columns."
     — Oracle Database 10.2 / 11.1 SQL Reference
       (This restriction does not apply to 11.2 and 12.1.)

Each schema_migrations row in the dump contributes one target column
to an INSERT ALL, so a single INSERT ALL can hold at most 999 rows on
pre-11.2 Oracle; beyond that the server raises:

    ORA-24335: cannot support more than 1000 columns

yahonda reproduced this in the PR rsim#1084 discussion (2016-12-15) by
running a 1000-row INSERT ALL against 10.2.0.5. Koichi then added the
supports_multi_insert? 11.2 gate so `rails db:schema:load` would stay
functional on 10.2 / 11.1 once schema_migrations exceeded ~999 rows,
by falling back to one INSERT per version joined with SQL*Plus "/"
separators.

oracle-enhanced's gemspec minimum is 11.2+ and the test_11g CI job
runs 11.2.0.2, so no supported configuration exercises the fallback
branch. Even so, deleting the fallback would strictly regress
structure.sql loads on 10.2 / 11.1 with long migration histories —
a niche case, but a real one that the fallback was deliberately added
to handle. The safer cleanup is to keep the branching and only drop
the predicate that Rails no longer exports.

Changes:

- Remove supports_multi_insert? from OracleEnhancedAdapter.
- Inline `database_version.to_s >= [11, 2].to_s` at its single call
  site in insert_versions_sql, with a block comment citing
  rsim#1084 and the Oracle 10.2 / 11.1 SQL references
  so the 11.2 threshold carries its reason in the code.
- Update the two spec skip guards in structure_dump_spec and
  schema_statements_spec to use the same inline predicate instead of
  the now-removed method.

Behaviour change: none. The per-version branching is preserved byte
for byte; only the indirection through supports_multi_insert? is
removed, and the gating predicate moves from the adapter to the
caller.

References:

- Oracle 10.2 SQL Reference, INSERT: multitable insert restrictions
  https://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_9014.htm#i2080134
- Oracle 11.1 SQL Reference, INSERT: multitable insert restrictions
  https://docs.oracle.com/cd/B28359_01/server.111/b28286/statements_9014.htm#i2080134
- Oracle 11.2 SQL Reference (restriction lifted)
  https://docs.oracle.com/cloud/latest/db112/SQLRF/statements_9014.htm#i2080134
- Oracle 12.1 SQL Reference (restriction lifted)
  https://docs.oracle.com/database/121/SQLRF/statements_9014.htm#i2080134
- rsim#1084 — "Fix ORA-00933 error when executing
  `rails db:schema:load`" (origin of the 11.2 gate and of the 10.2
  reproduction of ORA-24335)
- rails/rails#32923 — deprecation in Rails 5.2
- rails/rails@cbf43df2 — removal in Rails 6.1
yahonda added a commit to yahonda/oracle-enhanced that referenced this pull request Apr 14, 2026
Rails removed supports_multi_insert? from AbstractAdapter years ago:

- Deprecated in rails/rails#32923 / rails/rails@d1a74c1e (2018-05-21,
  "Bump minimum SQLite version to 3.8") as part of raising SQLite's
  minimum so every bundled adapter satisfied the predicate, at which
  point it stopped carrying information.
- Removed in rails/rails@cbf43df2 (2020-05-07, "Remove deprecated
  ActiveRecord::ConnectionAdapters::AbstractAdapter#supports_multi_insert?"),
  shipping in Rails 6.1.

oracle-enhanced kept a local override:

    def supports_multi_insert?
      database_version.to_s >= [11, 2].to_s
    end

which has no analog upstream anymore. Delete the predicate.

Why not also collapse the two-branch insert_versions_sql, as would be
the usual follow-up:

The 11.2 cutoff is not cosmetic and not about when Oracle introduced
INSERT ALL (that was Oracle 9i Release 1, 2001). It encodes a specific
restriction documented in the Oracle 10.2 and 11.1 SQL References:

    "In a multitable insert, all of the insert_into_clauses cannot
     combine to specify more than 999 target columns."
     — Oracle Database 10.2 / 11.1 SQL Reference
       (This restriction does not apply to 11.2 and 12.1.)

Each schema_migrations row in the dump contributes one target column
to an INSERT ALL, so a single INSERT ALL can hold at most 999 rows on
pre-11.2 Oracle per the docs; beyond that the server raises:

    ORA-24335: cannot support more than 1000 columns

yahonda reproduced this empirically on Oracle 10.2.0.5 in the PR rsim#1084
discussion (2016-12-15) with a 1000-row INSERT ALL. 11.1 was not
directly tested — the behaviour there is known only from the 11.1 SQL
Reference — but the docs for 10.2 and 11.1 are identically worded, so
the safer assumption is that 11.1 behaves the same way. Koichi then
added the supports_multi_insert? 11.2 gate so `rails db:schema:load`
would stay functional on 10.2 (and, by docs, 11.1) once
schema_migrations exceeded ~999 rows, by falling back to one INSERT
per version joined with SQL*Plus "/" separators.

oracle-enhanced's gemspec minimum is 11.2+ and the test_11g CI job
runs 11.2.0.2, so no supported configuration exercises the fallback
branch. Still, deleting the fallback would strictly regress
structure.sql loads on 10.2 (confirmed) and on 11.1 (per Oracle's own
documentation) once a project accumulates more than ~999 migrations —
niche cases, but real ones that the fallback was deliberately added
to handle. Keep the branching just in case, and only drop the
predicate that Rails no longer exports.

Changes:

- Remove supports_multi_insert? from OracleEnhancedAdapter.
- Inline `database_version.to_s >= [11, 2].to_s` at its single call
  site in insert_versions_sql, with a block comment citing
  rsim#1084 and the Oracle 10.2 / 11.1 SQL references
  so the 11.2 threshold carries its reason in the code.
- Update the two spec skip guards in structure_dump_spec and
  schema_statements_spec to use the same inline predicate instead of
  the now-removed method.

Behaviour change: none. The per-version branching is preserved byte
for byte; only the indirection through supports_multi_insert? is
removed, and the gating predicate moves from the adapter to the
caller.

References:

- Oracle 10.2 SQL Reference, INSERT: multitable insert restrictions
  https://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_9014.htm#i2080134
- Oracle 11.1 SQL Reference, INSERT: multitable insert restrictions
  https://docs.oracle.com/cd/B28359_01/server.111/b28286/statements_9014.htm#i2080134
- Oracle 11.2 SQL Reference (restriction lifted)
  https://docs.oracle.com/cloud/latest/db112/SQLRF/statements_9014.htm#i2080134
- Oracle 12.1 SQL Reference (restriction lifted)
  https://docs.oracle.com/database/121/SQLRF/statements_9014.htm#i2080134
- rsim#1084 — "Fix ORA-00933 error when executing
  `rails db:schema:load`" (origin of the 11.2 gate and of the 10.2
  reproduction of ORA-24335)
- rails/rails#32923 — deprecation in Rails 5.2
- rails/rails@cbf43df2 — removal in Rails 6.1
yahonda added a commit to yahonda/oracle-enhanced that referenced this pull request Apr 14, 2026
Rails removed supports_multi_insert? from AbstractAdapter years ago:

- Deprecated in rails/rails#32923 / rails/rails@d1a74c1e (2018-05-21,
  "Bump minimum SQLite version to 3.8") as part of raising SQLite's
  minimum so every bundled adapter satisfied the predicate, at which
  point it stopped carrying information.
- Removed in rails/rails@cbf43df2 (2020-05-07, "Remove deprecated
  ActiveRecord::ConnectionAdapters::AbstractAdapter#supports_multi_insert?"),
  shipping in Rails 6.1.

oracle-enhanced kept a local override:

    def supports_multi_insert?
      database_version.to_s >= [11, 2].to_s
    end

which has no analog upstream anymore. Delete the predicate.

Why not also collapse the two-branch insert_versions_sql, as would be
the usual follow-up:

The 11.2 cutoff is not cosmetic and not about when Oracle introduced
INSERT ALL (that was Oracle 9i Release 1, 2001). It encodes a specific
restriction documented in the Oracle 10.2 and 11.1 SQL References:

    "In a multitable insert, all of the insert_into_clauses cannot
     combine to specify more than 999 target columns."
     — Oracle Database 10.2 / 11.1 SQL Reference
       (This restriction does not apply to 11.2 and 12.1.)

Each schema_migrations row in the dump contributes one target column
to an INSERT ALL, so a single INSERT ALL can hold at most 999 rows on
pre-11.2 Oracle per the docs; beyond that the server raises:

    ORA-24335: cannot support more than 1000 columns

yahonda reproduced this empirically on Oracle 10.2.0.5 in the PR rsim#1084
discussion (2016-12-15) with a 1000-row INSERT ALL. 11.1 was not
directly tested — the behaviour there is known only from the 11.1 SQL
Reference — but the docs for 10.2 and 11.1 are identically worded, so
the safer assumption is that 11.1 behaves the same way. Koichi then
added the supports_multi_insert? 11.2 gate so `rails db:schema:load`
would stay functional on 10.2 (and, by docs, 11.1) once
schema_migrations exceeded ~999 rows, by falling back to one INSERT
per version joined with SQL*Plus "/" separators.

oracle-enhanced's gemspec minimum is 11.2+ and the test_11g CI job
runs 11.2.0.2, so no supported configuration exercises the fallback
branch. Still, deleting the fallback would strictly regress
structure.sql loads on 10.2 (confirmed) and on 11.1 (per Oracle's own
documentation) once a project accumulates more than ~999 migrations —
niche cases, but real ones that the fallback was deliberately added
to handle. Keep the branching just in case, and only drop the
predicate that Rails no longer exports.

Changes:

- Remove supports_multi_insert? from OracleEnhancedAdapter.
- Inline `database_version.to_s >= [11, 2].to_s` at its single call
  site in insert_versions_sql, with a block comment citing
  rsim#1084 and the Oracle 10.2 / 11.1 SQL references
  so the 11.2 threshold carries its reason in the code.
- Update the two spec skip guards in structure_dump_spec and
  schema_statements_spec to use the same inline predicate instead of
  the now-removed method.

Behaviour change: none. The per-version branching is preserved byte
for byte; only the indirection through supports_multi_insert? is
removed, and the gating predicate moves from the adapter to the
caller.

References:

- Oracle 10.2 SQL Reference, INSERT: multitable insert restrictions
  https://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_9014.htm#i2080134
- Oracle 11.1 SQL Reference, INSERT: multitable insert restrictions
  https://docs.oracle.com/cd/B28359_01/server.111/b28286/statements_9014.htm#i2080134
- Oracle 11.2 SQL Reference (restriction lifted)
  https://docs.oracle.com/cloud/latest/db112/SQLRF/statements_9014.htm#i2080134
- Oracle 12.1 SQL Reference (restriction lifted)
  https://docs.oracle.com/database/121/SQLRF/statements_9014.htm#i2080134
- rsim#1084 — "Fix ORA-00933 error when executing
  `rails db:schema:load`" (origin of the 11.2 gate and of the 10.2
  reproduction of ORA-24335)
- rails/rails#32923 — deprecation in Rails 5.2
- rails/rails@cbf43df2 — removal in Rails 6.1
yahonda added a commit to yahonda/oracle-enhanced that referenced this pull request May 5, 2026
rails/rails#53797 (Rails 8.1) made schema_migrations dump pluggable
via ActiveRecord.schema_versions_formatter. The default class
(ActiveRecord::Migration::DefaultSchemaVersionsFormatter) emits the
standard multi-row VALUES syntax, which Oracle < 23c does not
support, so the adapter has to ship its own default while still
letting apps swap in a custom formatter.

This change adds OracleEnhanced::SchemaVersionsFormatter mirroring
the Rails formatter API, and rewrites insert_versions_sql to delegate
through ActiveRecord.schema_versions_formatter. When the global
formatter is the Rails default, OracleEnhanced::SchemaVersionsFormatter
is substituted so Oracle structure dumps keep their Oracle dialect.
When the user has configured a custom formatter, theirs is used as-is
- same pluggability story as Postgres/MySQL/SQLite.

Why dump order is not flipped here
----------------------------------

rails/rails#44363 (Rails 7.1) reversed the schema_migrations dump
order on the other adapters to reduce merge conflicts. That benefit
is specific to the default formatter's multi-row VALUES list, where
appending a row forces the trailing ; on the previous last line to
shuffle to a , - the kind of edit that produces real conflicts.

Oracle's dump formats do not have that coupling. In the INSERT ALL
... SELECT * FROM DUAL block each INTO line is self-contained, and
in the per-statement fallback each INSERT statement is joined by a
/ separator on its own line. Adding a migration is a pure line
addition with no existing-line modification in either format, so
reversing the order delivers no merge-conflict reduction (and no
regression). The natural oldest-first order is preserved to match
current master output and avoid a behavior change with no payoff.

Why the new formatter does not call supports_multi_insert?
----------------------------------------------------------

ActiveRecord::ConnectionAdapters::AbstractAdapter#supports_multi_insert?
originally meant "supports multi-row VALUES syntax" upstream. Rails
deprecated it in 5.2 (rails/rails#32923) and removed it in 6.1
(rails/rails@cbf43df2). Oracle does not accept the multi-row VALUES
form prior to 23c, so the predicate's natural answer here would have
been false for every supported version.

The adapter's local override returns
database_version.to_s >= [11, 2].to_s, which answers a different
question entirely - whether a single INSERT ALL (multitable insert)
can hold more than 999 target columns. That threshold encodes the
restriction documented in the Oracle 10.2 / 11.1 SQL References
(lifted in 11.2); exceeding it raises ORA-24335. The 11.2 gate was
added in rsim#1084 to keep db:schema:load working on pre-11.2 once
schema_migrations exceeded ~999 rows.

The new formatter inlines that 999-column-limit predicate at the
branching point with a comment block citing the Oracle SQL
References and rsim#1084, instead of reaching for the misnamed
supports_multi_insert? hook. This expresses what the code is
actually deciding and avoids re-introducing the wrong name in a new
file just as rsim#2528 retires it.

References

- rails/rails#44363 (Rails 7.1) - reverse dump order
- rails/rails#53797 (Rails 8.1) - pluggable schema_versions_formatter
- rsim#1084 - origin of the 11.2 gate / ORA-24335
- rails/rails#32923 / cbf43df2 - upstream deprecation+removal of
  supports_multi_insert?

Closes rsim#2260.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
yahonda added a commit to yahonda/oracle-enhanced that referenced this pull request May 5, 2026
rails/rails#53797 (rails/rails@0cc1842d38, "Introduce versions
formatter for the schema dumper",
rails/rails#53797) made schema_migrations
dumping pluggable via ActiveRecord.schema_versions_formatter and
shipped in Rails 8.1. The default class
(ActiveRecord::Migration::DefaultSchemaVersionsFormatter) emits the
standard multi-row VALUES syntax, which Oracle < 23c does not
support, so the adapter has to ship its own default while still
letting apps swap in a custom formatter.

This change adds OracleEnhanced::SchemaVersionsFormatter mirroring
the Rails formatter API, and rewrites insert_versions_sql to delegate
through ActiveRecord.schema_versions_formatter. When the global
formatter is the Rails default, OracleEnhanced::SchemaVersionsFormatter
is substituted so Oracle structure dumps keep their Oracle dialect.
When the user has configured a custom formatter, theirs is used as-is
- same pluggability story as Postgres/MySQL/SQLite.

Why dump order is not flipped here
----------------------------------

rails/rails#44363 (Rails 7.1,
rails/rails#44363) reversed the
schema_migrations dump order on the other adapters to reduce merge
conflicts. That benefit is specific to the default formatter's
multi-row VALUES list, where appending a row forces the trailing ;
on the previous last line to shuffle to a , - the kind of edit that
produces real conflicts.

Oracle's dump formats do not have that coupling. In the INSERT ALL
... SELECT * FROM DUAL block each INTO line is self-contained, and
in the per-statement fallback each INSERT statement is joined by a
/ separator on its own line. Adding a migration is a pure line
addition with no existing-line modification in either format, so
reversing the order delivers no merge-conflict reduction (and no
regression). The natural oldest-first order is preserved to match
current master output and avoid a behavior change with no payoff.

Why the multi-insert branch routes through supports_multi_insert?
-----------------------------------------------------------------

The intent is to inline the 999-column-limit predicate
(database_version >= "11.2") at the formatter's branching point so
the code expresses what it is actually deciding -- the upstream
"supports multi-row VALUES" name has no relevance on Oracle (which
does not accept that syntax prior to 23c) and the threshold
encodes a different restriction:

The Oracle 10.2 / 11.1 SQL References constrain a multitable
insert (INSERT ALL) to at most 999 target columns combined across
all insert_into_clauses; exceeding it raises ORA-24335. The
restriction was lifted in 11.2 / 12.1. See rsim#1084.

An initial attempt at that inlining
(connection.database_version.to_s >= [11, 2].to_s) evaluated to
false in CI on configurations where the adapter's
supports_multi_insert? predicate (whose body is the same comparison)
returns true, sending INSERT ALL traffic down the per-statement
fallback path. Until the divergence is understood, the formatter
delegates to connection.supports_multi_insert? so it cannot disagree
with the existing call sites. The accompanying comment block keeps
the actual reason visible (Oracle 10.2 / 11.1 multitable-insert
999-column restriction; rsim#1084) and notes that rsim#2528, which retires
the misnamed predicate, should replace this call with whatever
inline check it adopts.

References

- rails/rails#44363 (Rails 7.1) - reverse dump order
  rails/rails#44363
- rails/rails#53797 / rails/rails@0cc1842d38 (Rails 8.1) - pluggable
  schema_versions_formatter
  rails/rails#53797
- rsim#1084 - origin of the 11.2 gate / ORA-24335
- rails/rails#32923 / rails/rails@cbf43df2 - upstream
  deprecation+removal of supports_multi_insert?

Closes rsim#2260.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
yahonda added a commit to yahonda/oracle-enhanced that referenced this pull request May 5, 2026
rails/rails#53797 (rails/rails@0cc1842d38, "Introduce versions
formatter for the schema dumper",
rails/rails#53797) made schema_migrations
dumping pluggable via ActiveRecord.schema_versions_formatter and
shipped in Rails 8.1. The default class
(ActiveRecord::Migration::DefaultSchemaVersionsFormatter) emits the
standard multi-row VALUES syntax, which Oracle < 23c does not
support, so the adapter has to ship its own default while still
letting apps swap in a custom formatter.

This change adds OracleEnhanced::SchemaVersionsFormatter mirroring
the Rails formatter API, and rewrites insert_versions_sql to delegate
through ActiveRecord.schema_versions_formatter. When the global
formatter is exactly the Rails default,
OracleEnhanced::SchemaVersionsFormatter is substituted so Oracle
structure dumps keep their Oracle dialect. When the user has
configured a custom formatter, theirs is used as-is - same
pluggability story as Postgres/MySQL/SQLite.

Identity check uses #equal? rather than #== so a user-supplied
subclass of DefaultSchemaVersionsFormatter is run as the user
configured it, instead of being silently rerouted into Oracle's
dialect.

Why dump order is not flipped here
----------------------------------

rails/rails#44363 (Rails 7.1,
rails/rails#44363) reversed the
schema_migrations dump order on the other adapters to reduce merge
conflicts. That benefit is specific to the default formatter's
multi-row VALUES list, where appending a row forces the trailing ;
on the previous last line to shuffle to a , - the kind of edit that
produces real conflicts.

Oracle's dump formats do not have that coupling. In the INSERT ALL
... SELECT * FROM DUAL block each INTO line is self-contained, and
in the per-statement fallback each INSERT statement is joined by a
/ separator on its own line. Adding a migration is a pure line
addition with no existing-line modification in either format, so
reversing the order delivers no merge-conflict reduction (and no
regression). The natural oldest-first order is preserved to match
current master output and avoid a behavior change with no payoff.

Why the multi-insert branch routes through supports_multi_insert?
-----------------------------------------------------------------

The intent is to inline the 999-column-limit predicate
(database_version >= "11.2") at the formatter's branching point so
the code expresses what it is actually deciding -- the upstream
"supports multi-row VALUES" name has no relevance on Oracle (which
does not accept that syntax prior to 23c) and the threshold
encodes a different restriction:

The Oracle 10.2 / 11.1 SQL References constrain a multitable
insert (INSERT ALL) to at most 999 target columns combined across
all insert_into_clauses; exceeding it raises ORA-24335. The
restriction was lifted in 11.2 / 12.1. See rsim#1084.

An initial attempt at that inlining
(connection.database_version.to_s >= [11, 2].to_s) evaluated to
false in CI on configurations where the adapter's
supports_multi_insert? predicate (whose body is the same comparison)
returns true, sending INSERT ALL traffic down the per-statement
fallback path. Until the divergence is understood, the formatter
delegates to connection.supports_multi_insert? so it cannot disagree
with the existing call sites. rsim#2528, which
retires the misnamed predicate, should replace this call with
whatever inline check it adopts.

Spec coverage
-------------

structure_dump_spec adds:

- An explicit "when the Rails default formatter is configured"
  context that asserts the substitution produces Oracle dialect
  output (INSERT ALL ... SELECT * FROM DUAL or per-statement
  fallback) and never the Rails default's multi-row VALUES leak.
- A "with a user-configured schema_versions_formatter" context
  for the Array path that installs a stub formatter and asserts
  the dump goes through it.
- A scalar (assume_migrated_upto_version) context that exercises
  insert_versions_sql directly with a single version string,
  covering both the default-formatter path (single-row Oracle
  INSERT) and the user-configured-formatter path. Setup is unified
  on RSpec around blocks for symmetry with the Array context.

connection.pool.schema_migration.table_name is used for the table
name (matching what Rails' DefaultSchemaVersionsFormatter does)
rather than ActiveRecord::Tasks::DatabaseTasks.migration_connection_pool.
In a single-DB configuration the two are equivalent; in multi-DB
the former is the better answer here, since it produces the table
for the connection actually being dumped.

References

- rails/rails#44363 (Rails 7.1) - reverse dump order
  rails/rails#44363
- rails/rails#53797 / rails/rails@0cc1842d38 (Rails 8.1) - pluggable
  schema_versions_formatter
  rails/rails#53797
- rsim#1084 - origin of the 11.2 gate / ORA-24335
- rails/rails#32923 / rails/rails@cbf43df2 - upstream
  deprecation+removal of supports_multi_insert?

Closes rsim#2260.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
yahonda added a commit to yahonda/oracle-enhanced that referenced this pull request May 5, 2026
rails/rails#53797 (rails/rails@0cc1842d38, "Introduce versions
formatter for the schema dumper",
rails/rails#53797) made schema_migrations
dumping pluggable via ActiveRecord.schema_versions_formatter and
shipped in Rails 8.1. The default class
(ActiveRecord::Migration::DefaultSchemaVersionsFormatter) emits the
standard multi-row VALUES syntax, which Oracle < 23c does not
support, so the adapter has to ship its own default while still
letting apps swap in a custom formatter.

This change adds OracleEnhanced::SchemaVersionsFormatter mirroring
the Rails formatter API, and rewrites insert_versions_sql to delegate
through ActiveRecord.schema_versions_formatter. When the global
formatter is exactly the Rails default,
OracleEnhanced::SchemaVersionsFormatter is substituted so Oracle
structure dumps keep their Oracle dialect. When the user has
configured a custom formatter, theirs is used as-is - same
pluggability story as Postgres/MySQL/SQLite.

Identity check uses #equal? rather than #== so a user-supplied
subclass of DefaultSchemaVersionsFormatter is run as the user
configured it, instead of being silently rerouted into Oracle's
dialect.

Why dump order is not flipped here
----------------------------------

rails/rails#44363 (Rails 7.1,
rails/rails#44363) reversed the
schema_migrations dump order on the other adapters to reduce merge
conflicts. That benefit is specific to the default formatter's
multi-row VALUES list, where appending a row forces the trailing ;
on the previous last line to shuffle to a , - the kind of edit that
produces real conflicts.

Oracle's dump formats do not have that coupling. In the INSERT ALL
... SELECT * FROM DUAL block each INTO line is self-contained, and
in the per-statement fallback each INSERT statement is joined by a
/ separator on its own line. Adding a migration is a pure line
addition with no existing-line modification in either format, so
reversing the order delivers no merge-conflict reduction (and no
regression). The natural oldest-first order is preserved to match
current master output and avoid a behavior change with no payoff.

Why the multi-insert branch routes through supports_multi_insert?
-----------------------------------------------------------------

The intent is to inline the 999-column-limit predicate
(database_version >= "11.2") at the formatter's branching point so
the code expresses what it is actually deciding -- the upstream
"supports multi-row VALUES" name has no relevance on Oracle (which
does not accept that syntax prior to 23c) and the threshold
encodes a different restriction:

The Oracle 10.2 / 11.1 SQL References constrain a multitable
insert (INSERT ALL) to at most 999 target columns combined across
all insert_into_clauses; exceeding it raises ORA-24335. The
restriction was lifted in 11.2 / 12.1. See rsim#1084.

An initial attempt at that inlining
(connection.database_version.to_s >= [11, 2].to_s) evaluated to
false in CI on configurations where the adapter's
supports_multi_insert? predicate (whose body is the same comparison)
returns true, sending INSERT ALL traffic down the per-statement
fallback path. Until the divergence is understood, the formatter
delegates to connection.supports_multi_insert? so it cannot disagree
with the existing call sites. rsim#2528, which
retires the misnamed predicate, should replace this call with
whatever inline check it adopts.

Spec coverage
-------------

structure_dump_spec adds:

- An explicit "when the Rails default formatter is configured"
  context that asserts the substitution produces Oracle dialect
  output (INSERT ALL ... SELECT * FROM DUAL or per-statement
  fallback) and never the Rails default's multi-row VALUES leak.
- A "with a user-configured schema_versions_formatter" context
  for the Array path that installs a stub formatter and asserts
  the dump goes through it.
- A "with a subclass of DefaultSchemaVersionsFormatter" context
  that proves the #equal? identity check: a subclass is run
  verbatim and is *not* silently rerouted into Oracle's dialect.
- A scalar-version context exercising insert_versions_sql directly
  with a single version string, covering both the default formatter
  path (single-row Oracle INSERT) and the user-configured path.
  Note: on current Rails, assume_migrated_upto_version no longer
  routes the scalar through insert_versions_sql -- it executes
  INSERT directly. The scalar branch is kept for parity with the
  Rails default formatter's API contract and for any external
  callers that pass a single version to insert_versions_sql.

connection.pool.schema_migration.table_name is used for the table
name (matching what Rails' DefaultSchemaVersionsFormatter does)
rather than ActiveRecord::Tasks::DatabaseTasks.migration_connection_pool.
In a single-DB configuration the two are equivalent; in multi-DB
the former is the better answer here, since it produces the table
for the connection actually being dumped.

References

- rails/rails#44363 (Rails 7.1) - reverse dump order
  rails/rails#44363
- rails/rails#53797 / rails/rails@0cc1842d38 (Rails 8.1) - pluggable
  schema_versions_formatter
  rails/rails#53797
- rsim#1084 - origin of the 11.2 gate / ORA-24335
- rails/rails#32923 / rails/rails@cbf43df2 - upstream
  deprecation+removal of supports_multi_insert?

Closes rsim#2260.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
yahonda added a commit to yahonda/oracle-enhanced that referenced this pull request May 5, 2026
Rails removed AbstractAdapter#supports_multi_insert? years ago:

- Deprecated in rails/rails#32923 / rails/rails@d1a74c1e (Rails 5.2,
  2018-05-21, "Bump minimum SQLite version to 3.8") as part of
  raising SQLite's minimum so every bundled adapter satisfied the
  predicate, at which point it stopped carrying information.
- Removed in rails/rails@cbf43df2 (Rails 6.1, 2020-05-07).

oracle-enhanced kept a local override:

    def supports_multi_insert?
      database_version >= "11.2"
    end

which has no analog upstream anymore. The name is also misleading
on this adapter: upstream "supports_multi_insert?" meant "supports
multi-row VALUES syntax", but Oracle does not accept that syntax
prior to 23c. The override answers a different question entirely --
whether a single INSERT ALL (multitable insert) can hold more than
999 target columns (the Oracle 10.2 / 11.1 SQL Reference restriction
that raises ORA-24335; lifted in 11.2 / 12.1, see
rsim#1084). The rsim#2526 formatter
re-routed traffic through this misnamed predicate as a temporary
delegate; this commit retires the predicate and inlines the actual
check at its sole call site.

Changes:

- Remove +supports_multi_insert?+ from +OracleEnhancedAdapter+.
- Inline +database_version >= "11.2"+ at the formatter's branching
  point in +schema_versions_formatter.rb+ -- the only real call site
  after rsim#2526 moved INSERT ALL emission out of
  +insert_versions_sql+ and into +OracleEnhanced::SchemaVersionsFormatter+.
- Replace the +supports_multi_insert?+ skip-guard in
  +structure_dump_spec+ and +schema_statements_spec+ with the same
  inline predicate. Rename the now-misleading +"multi insert is
  supported" / "multi insert is NOT supported"+ contexts to
  +"when INSERT ALL accepts 1000+ rows (Oracle 11.2 or later)" /
  "when INSERT ALL is capped at 999 rows (Oracle older than 11.2)"+
  so the context describes the actual Oracle behavior under test
  rather than a predicate name that no longer exists.

Why the per-version branching in the formatter is preserved
-----------------------------------------------------------

The 11.2 cutoff is not cosmetic and not about when Oracle introduced
INSERT ALL (Oracle 9i Release 1, 2001). It encodes a specific
restriction documented in the Oracle 10.2 and 11.1 SQL References:

    "In a multitable insert, all of the insert_into_clauses cannot
     combine to specify more than 999 target columns."
     -- Oracle Database 10.2 / 11.1 SQL Reference
        (Restriction lifted in 11.2 / 12.1.)

Each schema_migrations row in the dump contributes one target column
to an INSERT ALL, so a single INSERT ALL can hold at most 999 rows
on pre-11.2 Oracle per the docs; beyond that the server raises
+ORA-24335: cannot support more than 1000 columns+.

@yahonda reproduced this empirically on Oracle 10.2.0.5 in the PR
rsim#1084 discussion (2016-12-15) with a 1000-row
INSERT ALL. 11.1 was not retested -- the behavior there is known
only from the 11.1 SQL Reference -- but the docs for 10.2 and 11.1
are identically worded. The fallback path was added to keep
+rails db:schema:load+ working on 10.2 / 11.1 once schema_migrations
exceeds ~999 rows, by emitting one INSERT per version joined with
SQL*Plus +/+ separators.

oracle-enhanced's gemspec minimum is 11.2+ and the +test_11g+ CI job
runs 11.2.0.2, so no supported configuration exercises the fallback
branch. Still, deleting the fallback would strictly regress
+structure.sql+ loads on 10.2 (confirmed) and on 11.1 (per Oracle's
own documentation) once a project accumulates more than ~999
migrations -- niche cases, but real ones the fallback was added to
handle. Keep the branching; only drop the predicate Rails no longer
exports.

Behavior change: none. The per-version branching is preserved byte
for byte; only the indirection through +supports_multi_insert?+ is
removed, and the gating predicate is rephrased into the post-rsim#2677
+database_version >= "11.2"+ form (which uses
+OracleEnhancedAdapter::Version#<=>+ rather than the brittle
+Array#to_s+ string compare).

References

- Oracle 10.2 SQL Reference, INSERT: multitable insert restrictions
  https://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_9014.htm#i2080134
- Oracle 11.1 SQL Reference, INSERT: multitable insert restrictions
  https://docs.oracle.com/cd/B28359_01/server.111/b28286/statements_9014.htm#i2080134
- Oracle 11.2 SQL Reference (restriction lifted)
  https://docs.oracle.com/cloud/latest/db112/SQLRF/statements_9014.htm#i2080134
- Oracle 12.1 SQL Reference (restriction lifted)
  https://docs.oracle.com/database/121/SQLRF/statements_9014.htm#i2080134
- rsim#1084 -- "Fix ORA-00933 error when executing
  +rails db:schema:load+" (origin of the 11.2 gate / 10.2
  reproduction of ORA-24335)
- rsim#2526 -- moved INSERT ALL emission into
  +SchemaVersionsFormatter+, leaving +supports_multi_insert?+ with a
  single call site
- rsim#2677 -- replaced +database_version+'s
  Array shape with +OracleEnhancedAdapter::Version+ and migrated
  predicates from +.to_s >= [11, 2].to_s+ to +>= "11.2"+
- rails/rails#32923 -- Rails deprecation of +supports_multi_insert?+
- rails/rails@cbf43df2 -- Rails removal of +supports_multi_insert?+

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

5 participants