rails/activerecord/CHANGELOG.md

341 lines
10 KiB
Markdown
Raw Normal View History

* When running `db:migrate` on a fresh database, load the database schema before running migrations.
*Andrew Novoselac*
* Fix an issue where `.left_outer_joins` used with multiple associations that have
the same child association but different parents does not join all parents.
Previously, using `.left_outer_joins` with the same child association would only join one of the parents.
Now it will correctly join both parents.
Fixes #41498.
*Garrett Blehm*
* Deprecate `unsigned_float` and `unsigned_decimal` short-hand column methods.
As of MySQL 8.0.17, the UNSIGNED attribute is deprecated for columns of type FLOAT, DOUBLE,
and DECIMAL. Consider using a simple CHECK constraint instead for such columns.
https://dev.mysql.com/doc/refman/8.0/en/numeric-type-syntax.html
*Ryuta Kamizono*
* Drop MySQL 5.5 support.
MySQL 5.5 is the only version that does not support datetime with precision,
which we have supported in the core. Now we support MySQL 5.6.4 or later, which
is the first version to support datetime with precision.
*Ryuta Kamizono*
* Make Active Record asynchronous queries compatible with transactional fixtures.
Previously transactional fixtures would disable asynchronous queries, because transactional
fixtures impose all queries use the same connection.
Now asynchronous queries will use the connection pinned by transactional fixtures, and behave
much closer to production.
*Jean Boussier*
Fix binary decryption on Postgres When encrypting attributes we need to do it just before inserting the data into the database, so after any other serialization steps, e.g. for serialized types or normalization. And we need things to happen in reverse order when decrypting. With attribute decoration we end up with types nested in other types. To ensure that the encryption happens in the right place, the EncryptedAttributeType first serializes the value with the type it is wrapping and then encrypts it. And in reverse it decrypts then deserializes with the wrapped type. There's an assumption here, which is that the wrapped type doesn't need to do anything in between the database and the encryption layer - so any database specific casting is skipped. This works fine for String columns as there's nothing for them to do. It also works for binary columns for MySQL and SQLite. But is doesn't for PostgreSQL which needs to receive the data as Binary::Data and has to call `PG::Connection.unescape_bytea` when deserializing the data. The serialization part was fixed in https://github.com/rails/rails/pull/50920, where the encryption output is wrapped in Binary::Data, which let's the PostgreSQL adapter know to convert the value [here](https://github.com/rails/rails/blob/5a0b2fa5a3be6ffd49fa7f1c3544c1da403c9cb5/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb#L83). That PR however didn't fix deserializing the data when it comes back out of the database (it wasn't round-tripping the data properly in the tests). We need to deserialize binary types before decrypting them - and we'll have to just assume that the wrapped type can do that for us. This won't work for serialized types as they'll also attempt to convert the data with the coder which needs to happen after decryption, so we need to special case them and extract the subtype instead. This isn't ideal but it should work ok for all built in types.
2024-08-27 17:02:52 +08:00
* Deserialize binary data before decrypting
This ensures that we call `PG::Connection.unescape_bytea` on PostgreSQL before decryption.
*Donal McBreen*
* Ensure `ActiveRecord::Encryption.config` is always ready before access.
Previously, `ActiveRecord::Encryption` configuration was deferred until `ActiveRecord::Base`
was loaded. Therefore, accessing `ActiveRecord::Encryption.config` properties before
`ActiveRecord::Base` was loaded would give incorrect results.
`ActiveRecord::Encryption` now has its own loading hook so that its configuration is set as
soon as needed.
When `ActiveRecord::Base` is loaded, even lazily, it in turn triggers the loading of
`ActiveRecord::Encryption`, thus preserving the original behavior of having its config ready
before any use of `ActiveRecord::Base`.
*Maxime Réty*
* Add `TimeZoneConverter#==` method, so objects will be properly compared by
their type, scale, limit & precision.
Address #52699.
*Ruy Rocha*
* Add support for SQLite3 full-text-search and other virtual tables.
Previously, adding sqlite3 virtual tables messed up `schema.rb`.
Now, virtual tables can safely be added using `create_virtual_table`.
*Zacharias Knudsen*
* Support use of alternative database interfaces via the `database_cli` ActiveRecord configuration option.
```ruby
Rails.application.configure do
config.active_record.database_cli = { postgresql: "pgcli" }
end
```
*T S Vallender*
* Add support for dumping table inheritance and native partitioning table definitions for PostgeSQL adapter
*Justin Talbott*
* Infer default `:inverse_of` option for `delegated_type` definitions.
```ruby
class Entry < ApplicationRecord
delegated_type :entryable, types: %w[ Message ]
# => defaults to inverse_of: :entry
end
```
*Sean Doyle*
* Add support for `ActiveRecord::Point` type casts using `Hash` values
This allows `ActiveRecord::Point` to be cast or serialized from a hash
with `:x` and `:y` keys of numeric values, mirroring the functionality of
existing casts for string and array values. Both string and symbol keys are
supported.
```ruby
class PostgresqlPoint < ActiveRecord::Base
attribute :x, :point
attribute :y, :point
attribute :z, :point
end
val = PostgresqlPoint.new({
x: '(12.34, -43.21)',
y: [12.34, '-43.21'],
z: {x: '12.34', y: -43.21}
})
ActiveRecord::Point.new(12.32, -43.21) == val.x == val.y == val.z
```
*Stephen Drew*
2024-08-17 15:21:46 +08:00
* Replace `SQLite3::Database#busy_timeout` with `#busy_handler_timeout=`.
2024-08-17 15:21:46 +08:00
Provides a non-GVL-blocking, fair retry interval busy handler implementation.
*Stephen Margheim*
* SQLite3Adapter: Translate `SQLite3::BusyException` into `ActiveRecord::StatementTimeout`.
*Matthew Nguyen*
* Include schema name in `enable_extension` statements in `db/schema.rb`.
The schema dumper will now include the schema name in generated
`enable_extension` statements if they differ from the current schema.
For example, if you have a migration:
```ruby
enable_extension "heroku_ext.pgcrypto"
enable_extension "pg_stat_statements"
```
then the generated schema dump will also contain:
```ruby
enable_extension "heroku_ext.pgcrypto"
enable_extension "pg_stat_statements"
```
*Tony Novak*
* Fix `ActiveRecord::Encryption::EncryptedAttributeType#type` to return
actual cast type.
*Vasiliy Ermolovich*
2024-08-11 15:05:44 +08:00
* SQLite3Adapter: Bulk insert fixtures.
Previously one insert command was executed for each fixture, now they are
aggregated in a single bulk insert command.
*Lázaro Nixon*
* PostgreSQLAdapter: Allow `disable_extension` to be called with schema-qualified name.
For parity with `enable_extension`, the `disable_extension` method can be called with a schema-qualified
name (e.g. `disable_extension "myschema.pgcrypto"`). Note that PostgreSQL's `DROP EXTENSION` does not
actually take a schema name (unlike `CREATE EXTENSION`), so the resulting SQL statement will only name
the extension, e.g. `DROP EXTENSION IF EXISTS "pgcrypto"`.
*Tony Novak*
* Make `create_schema` / `drop_schema` reversible in migrations.
Previously, `create_schema` and `drop_schema` were irreversible migration operations.
*Tony Novak*
2024-07-13 22:19:06 +08:00
* Support batching using custom columns.
```ruby
Product.in_batches(cursor: [:shop_id, :id]) do |relation|
# do something with relation
end
```
*fatkodima*
* Use SQLite `IMMEDIATE` transactions when possible.
Transactions run against the SQLite3 adapter default to IMMEDIATE mode to improve concurrency support and avoid busy exceptions.
*Stephen Margheim*
* Raise specific exception when a connection is not defined.
2024-08-30 14:59:44 +08:00
The new `ConnectionNotDefined` exception provides connection name, shard and role accessors indicating the details of the connection that was requested.
*Hana Harencarova*, *Matthew Draper*
* Delete the deprecated constant `ActiveRecord::ImmutableRelation`.
*Xavier Noria*
* Fix duplicate callback execution when child autosaves parent with `has_one` and `belongs_to`.
Before, persisting a new child record with a new associated parent record would run `before_validation`,
`after_validation`, `before_save` and `after_save` callbacks twice.
Now, these callbacks are only executed once as expected.
*Joshua Young*
* `ActiveRecord::Encryption::Encryptor` now supports a `:compressor` option to customize the compression algorithm used.
```ruby
module ZstdCompressor
def self.deflate(data)
Zstd.compress(data)
end
def self.inflate(data)
Zstd.decompress(data)
end
end
class User
encrypts :name, compressor: ZstdCompressor
end
```
You disable compression by passing `compress: false`.
```ruby
class User
encrypts :name, compress: false
end
```
*heka1024*
* Add condensed `#inspect` for `ConnectionPool`, `AbstractAdapter`, and
`DatabaseConfig`.
*Hartley McGuire*
Add `.shard_keys` & `.connected_to_all_shards` Currently, there is no (simple) way to ask a model if it connects to a single database or to multiple shards. Furthermore, without looping through a model's connections, I don't believe there's an easy way to return a list of shards a model can connect to. This commit adds a `@shard_keys` ivar that's set whenever `.connects_to` is called. It sets the ivar to the result of `shards.keys`. `shards` in `.connects_to` defaults to an empty hash and therefore when calling `connects_to database: {...}` `@shard_keys` will be set to an empty array. `@shard_keys` is set _before_ the following lines: ``` if shards.empty? shards[:default] = database end ``` This conditional sets the one and only shard (`:default`) to the value of `database` that we pass to `.connects_to`. This allows for calling `connected_to(shard: :default)` on models configured to only connect to a database e.g.: ```ruby class UnshardedBase < ActiveRecord::Base self.abstract_class = true connects_to database: { writing: :primary } end class UnshardedModel < UnshardedBase end UnshardedBase.connected_to(shard: :default) { UnshardedBase.connection_pool.db_config.name } => primary ``` This is ultimately still an _unsharded_ model which is why `@shard_keys` gets set before the conditional. With the new `@shard_keys` ivar we need a way for descendants of the abstract AR model to return that same value. For that we leverage the existing `.connection_class_for_self` method. That method returns the ancestor of the model where `.connects_to` was called, or returns self if it's the connection class: ```ruby class UnshardedBase < ActiveRecord::Base self.abstract_class = true connects_to database: { writing: :primary } end class UnshardedModel < UnshardedBase end ActiveRecord::Base.connection_class_for_self => ActiveRecord::Base UnshardedBase.connection_class_for_self => UnshardedBase(abstract) UnshardedModel.connection_class_for_self => UnshardedBase(abstract) ``` The new `.shard_keys` method is a getter which returns the value of `@shard_keys` from the connection class or it returns an empty array. The empty array is necessary in cases where `connects_to` was never called. Finally, I've added an `.connected_to_all_shards` method which takes all of the arguments for `.connected_to` except for `shard`. Instead, it loops through every shard key and then delegates everything else to `.connected_to`. I've used `.map` instead of `.each` so that we can collect the results of each block.
2024-02-08 16:05:22 +08:00
* Add `.shard_keys`, `.sharded?`, & `.connected_to_all_shards` methods.
```ruby
class ShardedBase < ActiveRecord::Base
self.abstract_class = true
connects_to shards: {
shard_one: { writing: :shard_one },
shard_two: { writing: :shard_two }
}
end
class ShardedModel < ShardedBase
end
ShardedModel.shard_keys => [:shard_one, :shard_two]
ShardedModel.sharded? => true
ShardedBase.connected_to_all_shards { ShardedModel.current_shard } => [:shard_one, :shard_two]
```
*Nony Dutton*
* Add a `filter` option to `in_order_of` to prioritize certain values in the sorting without filtering the results
by these values.
*Igor Depolli*
* Fix an issue where the IDs reader method did not return expected results
for preloaded associations in models using composite primary keys.
*Jay Ang*
* Allow to configure `strict_loading_mode` globally or within a model.
Defaults to `:all`, can be changed to `:n_plus_one_only`.
*Garen Torikian*
* Add `ActiveRecord::Relation#readonly?`.
Reflects if the relation has been marked as readonly.
*Theodor Tonum*
* Improve `ActiveRecord::Store` to raise a descriptive exception if the column is not either
structured (e.g., PostgreSQL +hstore+/+json+, or MySQL +json+) or declared serializable via
`ActiveRecord.store`.
Previously, a `NoMethodError` would be raised when the accessor was read or written:
NoMethodError: undefined method `accessor' for an instance of ActiveRecord::Type::Text
Now, a descriptive `ConfigurationError` is raised:
ActiveRecord::ConfigurationError: the column 'metadata' has not been configured as a store.
Please make sure the column is declared serializable via 'ActiveRecord.store' or, if your
database supports it, use a structured column type like hstore or json.
*Mike Dalessio*
* Fix inference of association model on nested models with the same demodularized name.
E.g. with the following setup:
```ruby
class Nested::Post < ApplicationRecord
has_one :post, through: :other
end
```
Before, `#post` would infer the model as `Nested::Post`, but now it correctly infers `Post`.
*Joshua Young*
* Add public method for checking if a table is ignored by the schema cache.
Previously, an application would need to reimplement `ignored_table?` from the schema cache class to check if a table was set to be ignored. This adds a public method to support this and updates the schema cache to use that directly.
```ruby
ActiveRecord.schema_cache_ignored_tables = ["developers"]
ActiveRecord.schema_cache_ignored_table?("developers")
=> true
```
*Eileen M. Uchitelle*
Please check [7-2-stable](https://github.com/rails/rails/blob/7-2-stable/activerecord/CHANGELOG.md) for previous changes.