Bump minimum SQLite version to 3.8#32923
Conversation
|
(@rails-bot has picked a reviewer for you, use r? to override) |
There was a problem hiding this comment.
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.)
There was a problem hiding this comment.
Make sense. Reverted this change.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Agreed. deprecated supports_multi_insert? method.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Changed the condition to use ActiveRecord::Base.connection.supports_partial_index? only.
There was a problem hiding this comment.
Let's add a to_s to AbstractAdapter::Version.
There was a problem hiding this comment.
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.
3e1c66d to
b2e1e67
Compare
There was a problem hiding this comment.
Is there any reason not to use raise unlike other adapters?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Understood. Thanks for the comment.
There was a problem hiding this comment.
Unneeded the redundant to_s in string interpolation.
56cd2de to
c13883f
Compare
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
c13883f to
d1a74c1
Compare
To prevent redundant `to_s` like #32923 (comment) automatically in the future.
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. ```
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.
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.
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.
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.
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
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
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
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>
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>
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>
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>
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>
Summary
These OS versions have SQLite 3.8 or higher by default.
Raising the minimum version of SQLite 3.8 introduces these changes:
supports_multi_insert?supports_foreign_keys_in_create?andsupports_partial_index?alter_tablemethod for foreign key referenced tables by Disable foreign keys duringalter_tablefor sqlite3 adapter #32865