1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00
rails--rails/activerecord/CHANGELOG.md

808 lines
26 KiB
Markdown
Raw Normal View History

2021-06-21 16:34:09 -04:00
* Allow preloading of associations with instance dependent scopes
*John Hawthorn*, *John Crepezzi*, *Adam Hess*, *Eileen M. Uchitelle*, *Dinah Shi*
* Do not try to rollback transactions that failed due to a `ActiveRecord::TransactionRollbackError`.
*Jamie McCarthy*
* Active Record Encryption will now encode values as UTF-8 when using deterministic
encryption. The encoding is part of the encrypted payload, so different encodings for
different values result in different ciphertexts. This can break unique constraints and
queries.
The new behavior is configurable via `active_record.encryption.forced_encoding_for_deterministic_encryption`
that is `Encoding::UTF_8` by default. It can be disabled by setting it to `nil`.
*Jorge Manrubia*
* Disable automatic write protection on replicas.
Write protection is no longer automatically enabled for replicas.
You can manually prevent writes in your app with
`while_preventing_writes`. To automatically disable all writes on
your replica, configure the database user you are using to connect
to your replica to prevent writes. How you configure this is
specific to which database adapter you are using, but it usually
involves only granting the database user permission to do `SELECT`
queries.
*Adam Hess*
* The MySQL adapter now cast numbers and booleans bind parameters to to string for safety reasons.
When comparing a string and a number in a query, MySQL convert the string to a number. So for
instance `"foo" = 0`, will implicitly cast `"foo"` to `0` and will evaluate to `TRUE` which can
lead to security vulnerabilities.
Active Record already protect against that vulnerability when it knows the type of the column
being compared, however until now it was still vulnerable when using bind parameters:
```ruby
User.where("login_token = ?", 0).first
```
Would perform:
```sql
SELECT * FROM `users` WHERE `login_token` = 0 LIMIT 1;
```
Now it will perform:
```sql
SELECT * FROM `users` WHERE `login_token` = '0' LIMIT 1;
```
*Jean Boussier*
* Fixture configurations (`_fixture`) are now strictly validated.
If an error will be raised if that entry contains unknown keys while previously it
would silently have no effects.
*Jean Boussier*
* Add `ActiveRecord::Base.update!` that works like `ActiveRecord::Base.update` but raises exceptions.
This allows for the same behavior as the instance method `#update!` at a class level.
```ruby
Person.update!(:all, state: "confirmed")
```
2021-06-10 06:39:45 -04:00
*Dorian Marié*
* Add `ActiveRecord::Base#attributes_for_database`
Returns attributes with values for assignment to the database.
*Chris Salzberg*
* Use an empty query to check if the PostgreSQL connection is still active
An empty query is faster than `SELECT 1`.
*Heinrich Lee Yu*
* Add `ActiveRecord::Base#previously_persisted?`
Returns `true` if the object has been previously persisted but now it has been deleted.
* Deprecate `partial_writes` in favor of `partial_inserts` and `partial_updates`.
This allows to have a different behavior on update and create.
*Jean Boussier*
* Fix compatibility with `psych >= 4`.
Starting in Psych 4.0.0 `YAML.load` behaves like `YAML.safe_load`. To preserve compatibility
Active Record's schema cache loader and `YAMLColumn` now uses `YAML.unsafe_load` if available.
*Jean Boussier*
* `ActiveRecord::Base.logger` is now a `class_attribute`.
This means it can no longer be accessed directly through `@@logger`, and that setting `logger =`
on a subclass won't change the parent's logger.
*Jean Boussier*
* Add `.asc.nulls_first` for all databases. Unfortunately MySQL still doesn't like `nulls_last`.
*Keenan Brock*
* Improve performance of `one?` and `many?` by limiting the generated count query to 2 results.
*Gonzalo Riestra*
2021-05-13 08:38:46 -04:00
* Don't check type when using `if_not_exists` on `add_column`.
Previously, if a migration called `add_column` with the `if_not_exists` option set to true
the `column_exists?` check would look for a column with the same name and type as the migration.
Recently it was discovered that the type passed to the migration is not always the same type
as the column after migration. For example a column set to `:mediumblob` in the migration will
be casted to `binary` when calling `column.type`. Since there is no straightforward way to cast
the type to the database type without running the migration, we opted to drop the type check from
`add_column`. This means that migrations adding a duplicate column with a different type will no
longer raise an error.
*Eileen M. Uchitelle*
* Log a warning message when running SQLite in production
Using SQLite in production ENV is generally discouraged. SQLite is also the default adapter
in a new Rails application.
For the above reasons log a warning message when running SQLite in production.
The warning can be disabled by setting `config.active_record.sqlite3_production_warning=false`.
*Jacopo Beschi*
* Add option to disable joins for `has_one` associations.
In a multiple database application, associations can't join across
databases. When set, this option instructs Rails to generate 2 or
more queries rather than generating joins for `has_one` associations.
Set the option on a has one through association:
```ruby
class Person
belongs_to :dog
has_one :veterinarian, through: :dog, disable_joins: true
end
```
Then instead of generating join SQL, two queries are used for `@person.veterinarian`:
```
SELECT "dogs"."id" FROM "dogs" WHERE "dogs"."person_id" = ? [["person_id", 1]]
SELECT "veterinarians".* FROM "veterinarians" WHERE "veterinarians"."dog_id" = ? [["dog_id", 1]]
```
*Sarah Vessels*, *Eileen M. Uchitelle*
* `Arel::Visitors::Dot` now renders a complete set of properties when visiting
`Arel::Nodes::SelectCore`, `SelectStatement`, `InsertStatement`, `UpdateStatement`, and
`DeleteStatement`, which fixes #42026. Previously, some properties were omitted.
*Mike Dalessio*
* `Arel::Visitors::Dot` now supports `Arel::Nodes::Bin`, `Case`, `CurrentRow`, `Distinct`,
`DistinctOn`, `Else`, `Except`, `InfixOperation`, `Intersect`, `Lock`, `NotRegexp`, `Quoted`,
`Regexp`, `UnaryOperation`, `Union`, `UnionAll`, `When`, and `With`. Previously, these node
types caused an exception to be raised by `Arel::Visitors::Dot#accept`.
*Mike Dalessio*
* Optimize `remove_columns` to use a single SQL statement.
```ruby
remove_columns :my_table, :col_one, :col_two
```
Now results in the following SQL:
```sql
ALTER TABLE "my_table" DROP COLUMN "col_one", DROP COLUMN "col_two"
```
*Jon Dufresne*
* Ensure `has_one` autosave association callbacks get called once.
Change the `has_one` autosave callback to be non cyclic as well.
By doing this the autosave callback are made more consistent for
all 3 cases: `has_many`, `has_one`, and `belongs_to`.
*Petrik de Heus*
Add option to skip joins for associations. In a multiple database application, associations can't join across databases. When set, this option tells Rails to make 2 or more queries rather than using joins for associations. Set the option on a has many through association: ```ruby class Dog has_many :treats, through: :humans, disable_joins: true has_many :humans end ``` Then instead of generating join SQL, two queries are used for `@dog.treats`: ``` SELECT "humans"."id" FROM "humans" WHERE "humans"."dog_id" = ? [["dog_id", 1]] SELECT "treats".* FROM "treats" WHERE "treats"."human_id" IN (?, ?, ?) [["human_id", 1], ["human_id", 2], ["human_id", 3]] ``` This code is extracted from a gem we use internally at GitHub which means the implementation here is used in production daily and isn't experimental. I often get the question "why can't Rails do this automatically" so I figured I'd include the answer in the commit. Rails can't do this automatically because associations are lazily loaded. `dog.treats` needs to load `Dog`, then `Human` and then `Treats`. When `dog.treats` is called Rails pre-generates the SQL that will be run and puts that information into a reflection object. Because the SQL parts are pre-generated, as soon as `dog.treats` is loaded it's too late to skip a join. The join is already available on the object and that join is what's run to load `treats` from `dog` through `humans`. I think the only way to avoid setting an option on the association is to rewrite how and when the SQL is generated for associations which is a large undertaking. Basically the way that Active Record associations are designed, it is currently impossible to have Rails figure out to not join (loading the association will cause the join to occur, and that join will raise an error if the models don't live in the same db). The original implementation was written by me and Aaron. Lee helped port over tests, and I refactored the extraction to better match Rails style. Co-authored-by: Lee Quarella <leequarella@gmail.com> Co-authored-by: Aaron Patterson <aaron@rubyonrails.org>
2020-11-03 13:01:41 -05:00
* Add option to disable joins for associations.
In a multiple database application, associations can't join across
databases. When set, this option instructs Rails to generate 2 or
more queries rather than generating joins for associations.
Set the option on a has many through association:
```ruby
class Dog
has_many :treats, through: :humans, disable_joins: true
has_many :humans
end
```
Then instead of generating join SQL, two queries are used for `@dog.treats`:
```
SELECT "humans"."id" FROM "humans" WHERE "humans"."dog_id" = ? [["dog_id", 1]]
SELECT "treats".* FROM "treats" WHERE "treats"."human_id" IN (?, ?, ?) [["human_id", 1], ["human_id", 2], ["human_id", 3]]
```
*Eileen M. Uchitelle*, *Aaron Patterson*, *Lee Quarella*
* Add setting for enumerating column names in SELECT statements.
Adding a column to a PostgreSQL database, for example, while the application is running can
change the result of wildcard `SELECT *` queries, which invalidates the result
2021-04-15 19:34:30 -04:00
of cached prepared statements and raises a `PreparedStatementCacheExpired` error.
2021-04-15 19:34:30 -04:00
When enabled, Active Record will avoid wildcards and always include column names
in `SELECT` queries, which will return consistent results and avoid prepared
statement errors.
Before:
```ruby
Book.limit(5)
# SELECT * FROM books LIMIT 5
```
After:
```ruby
# config/application.rb
module MyApp
class Application < Rails::Application
config.active_record.enumerate_columns_in_select_statements = true
end
end
# or, configure per-model
class Book < ApplicationRecord
self.enumerate_columns_in_select_statements = true
end
```
```ruby
Book.limit(5)
# SELECT id, author_id, name, format, status, language, etc FROM books LIMIT 5
```
*Matt Duszynski*
* Allow passing SQL as `on_duplicate` value to `#upsert_all` to make it possible to use raw SQL to update columns on conflict:
2021-04-12 11:01:42 -04:00
```ruby
Book.upsert_all(
[{ id: 1, status: 1 }, { id: 2, status: 1 }],
on_duplicate: Arel.sql("status = GREATEST(books.status, EXCLUDED.status)")
2021-04-12 11:01:42 -04:00
)
```
*Vladimir Dementyev*
* Allow passing SQL as `returning` statement to `#upsert_all`:
```ruby
Article.insert_all(
[
{ title: "Article 1", slug: "article-1", published: false },
{ title: "Article 2", slug: "article-2", published: false }
],
returning: Arel.sql("id, (xmax = '0') as inserted, name as new_name")
)
```
*Vladimir Dementyev*
* Deprecate `legacy_connection_handling`.
*Eileen M. Uchitelle*
* Add attribute encryption support.
Encrypted attributes are declared at the model level. These
are regular Active Record attributes backed by a column with
the same name. The system will transparently encrypt these
attributes before saving them into the database and will
decrypt them when retrieving their values.
```ruby
class Person < ApplicationRecord
encrypts :name
encrypts :email_address, deterministic: true
end
```
You can learn more in the [Active Record Encryption
guide](https://edgeguides.rubyonrails.org/active_record_encryption.html).
*Jorge Manrubia*
2021-03-28 03:15:50 -04:00
* Changed Arel predications `contains` and `overlaps` to use
`quoted_node` so that PostgreSQL arrays are quoted properly.
*Bradley Priest*
2021-03-03 16:33:32 -05:00
* Add mode argument to record level `strict_loading!`
This argument can be used when enabling strict loading for a single record
to specify that we only want to raise on n plus one queries.
```ruby
developer.strict_loading!(mode: :n_plus_one_only)
developer.projects.to_a # Does not raise
developer.projects.first.client # Raises StrictLoadingViolationError
```
Previously, enabling strict loading would cause any lazily loaded
association to raise an error. Using `n_plus_one_only` mode allows us to
lazily load belongs_to, has_many, and other associations that are fetched
through a single query.
*Dinah Shi*
* Fix Float::INFINITY assignment to datetime column with postgresql adapter
Before:
```ruby
# With this config
ActiveRecord::Base.time_zone_aware_attributes = true
# and the following schema:
create_table "postgresql_infinities" do |t|
t.datetime "datetime"
end
# This test fails
record = PostgresqlInfinity.create!(datetime: Float::INFINITY)
assert_equal Float::INFINITY, record.datetime # record.datetime gets nil
```
After this commit, `record.datetime` gets `Float::INFINITY` as expected.
*Shunichi Ikegami*
* Type cast enum values by the original attribute type.
The notable thing about this change is that unknown labels will no longer match 0 on MySQL.
```ruby
class Book < ActiveRecord::Base
enum :status, { proposed: 0, written: 1, published: 2 }
end
```
Before:
```ruby
# SELECT `books`.* FROM `books` WHERE `books`.`status` = 'prohibited' LIMIT 1
Book.find_by(status: :prohibited)
# => #<Book id: 1, status: "proposed", ...> (for mysql2 adapter)
# => ActiveRecord::StatementInvalid: PG::InvalidTextRepresentation: ERROR: invalid input syntax for type integer: "prohibited" (for postgresql adapter)
# => nil (for sqlite3 adapter)
```
After:
```ruby
# SELECT `books`.* FROM `books` WHERE `books`.`status` IS NULL LIMIT 1
Book.find_by(status: :prohibited)
# => nil (for all adapters)
```
*Ryuta Kamizono*
* Fixtures for `has_many :through` associations now load timestamps on join tables
Given this fixture:
```yml
### monkeys.yml
george:
name: George the Monkey
fruits: apple
### fruits.yml
apple:
name: apple
```
If the join table (`fruit_monkeys`) contains `created_at` or `updated_at` columns,
these will now be populated when loading the fixture. Previously, fixture loading
would crash if these columns were required, and leave them as null otherwise.
*Alex Ghiculescu*
Allow async executor to be configurable This is a followup/alternative to #41406. This change wouldn't work for GitHub because we intend to implement an executor for each database and use the database configuration to set the `min_threads` and `max_threads` for each one. The changes here borrow from #41406 by implementing an `Concurrent::ImmediateExecutor` by default. Otherwise applications have the option of having one global thread pool that is used by all connections or a thread pool for each connection. A global thread pool can set with `config.active_record.async_query_executor = :global_thread_pool`. This will create a single `Concurrent::ThreadPoolExecutor` for applications to utilize. By default the concurrency is 4, but it can be changed for the `global_thread_pool` by setting `global_executor_concurrency` to another number. If applications want to use a thread pool per database connection they can set `config.active_record.async_query_executor = :multi_thread_pool`. This will create a `Concurrent::ThreadPoolExecutor` for each database connection and set the `min_threads` and `max_threads` by their configuration values or the defaults. I've also moved the async tests out of the adapter test and into their own tests and added tests for all the new functionality. This change would allow us at GitHub to control threads per database and per writer/reader or other apps to use one global executor. The immediate executor allows apps to no-op by default. Took the immediate executor idea from Jean's PR. Co-authored-by: Jean Boussier <jean.boussier@gmail.com>
2021-02-18 13:10:57 -05:00
* Allow applications to configure the thread pool for async queries
Some applications may want one thread pool per database whereas others want to use
2021-04-11 12:57:29 -04:00
a single global thread pool for all queries. By default, Rails will set `async_query_executor`
to `nil` which will not initialize any executor. If `load_async` is called and no executor
has been configured, the query will be executed in the foreground.
Allow async executor to be configurable This is a followup/alternative to #41406. This change wouldn't work for GitHub because we intend to implement an executor for each database and use the database configuration to set the `min_threads` and `max_threads` for each one. The changes here borrow from #41406 by implementing an `Concurrent::ImmediateExecutor` by default. Otherwise applications have the option of having one global thread pool that is used by all connections or a thread pool for each connection. A global thread pool can set with `config.active_record.async_query_executor = :global_thread_pool`. This will create a single `Concurrent::ThreadPoolExecutor` for applications to utilize. By default the concurrency is 4, but it can be changed for the `global_thread_pool` by setting `global_executor_concurrency` to another number. If applications want to use a thread pool per database connection they can set `config.active_record.async_query_executor = :multi_thread_pool`. This will create a `Concurrent::ThreadPoolExecutor` for each database connection and set the `min_threads` and `max_threads` by their configuration values or the defaults. I've also moved the async tests out of the adapter test and into their own tests and added tests for all the new functionality. This change would allow us at GitHub to control threads per database and per writer/reader or other apps to use one global executor. The immediate executor allows apps to no-op by default. Took the immediate executor idea from Jean's PR. Co-authored-by: Jean Boussier <jean.boussier@gmail.com>
2021-02-18 13:10:57 -05:00
To create one thread pool for all database connections to use applications can set
`config.active_record.async_query_executor` to `:global_thread_pool` and optionally define
`config.active_record.global_executor_concurrency`. This defaults to 4. For applications that want
to have a thread pool for each database connection, `config.active_record.async_query_executor` can
be set to `:multi_thread_pool`. The configuration for each thread pool is set in the database
configuration.
*Eileen M. Uchitelle*
* Allow new syntax for `enum` to avoid leading `_` from reserved options.
Before:
```ruby
class Book < ActiveRecord::Base
enum status: [ :proposed, :written ], _prefix: true, _scopes: false
enum cover: [ :hard, :soft ], _suffix: true, _default: :hard
end
```
After:
```ruby
class Book < ActiveRecord::Base
enum :status, [ :proposed, :written ], prefix: true, scopes: false
enum :cover, [ :hard, :soft ], suffix: true, default: :hard
end
```
*Ryuta Kamizono*
* Add `ActiveRecord::Relation#load_async`.
This method schedules the query to be performed asynchronously from a thread pool.
If the result is accessed before a background thread had the opportunity to perform
the query, it will be performed in the foreground.
This is useful for queries that can be performed long enough before their result will be
needed, or for controllers which need to perform several independent queries.
```ruby
def index
@categories = Category.some_complex_scope.load_async
@posts = Post.some_complex_scope.load_async
end
```
*Jean Boussier*
* Implemented `ActiveRecord::Relation#excluding` method.
This method excludes the specified record (or collection of records) from
the resulting relation:
```ruby
Post.excluding(post)
Post.excluding(post_one, post_two)
```
Also works on associations:
```ruby
post.comments.excluding(comment)
post.comments.excluding(comment_one, comment_two)
```
This is short-hand for `Post.where.not(id: post.id)` (for a single record)
and `Post.where.not(id: [post_one.id, post_two.id])` (for a collection).
*Glen Crawford*
* Skip optimised #exist? query when #include? is called on a relation
with a having clause
Relations that have aliased select values AND a having clause that
references an aliased select value would generate an error when
#include? was called, due to an optimisation that would generate
call #exists? on the relation instead, which effectively alters
the select values of the query (and thus removes the aliased select
values), but leaves the having clause intact. Because the having
clause is then referencing an aliased column that is no longer
present in the simplified query, an ActiveRecord::InvalidStatement
error was raised.
2021-04-11 12:57:29 -04:00
A sample query affected by this problem:
```ruby
Author.select('COUNT(*) as total_posts', 'authors.*')
.joins(:posts)
.group(:id)
.having('total_posts > 2')
.include?(Author.first)
```
This change adds an addition check to the condition that skips the
simplified #exists? query, which simply checks for the presence of
a having clause.
Fixes #41417
*Michael Smart*
* Increment postgres prepared statement counter before making a prepared statement, so if the statement is aborted
without Rails knowledge (e.g., if app gets killed during long-running query or due to Rack::Timeout), app won't end
up in perpetual crash state for being inconsistent with Postgres.
*wbharding*, *Martin Tepper*
Enable scoping to apply to all queries Similar to https://github.com/rails/rails/pull/40720 and https://github.com/rails/rails/pull/40805 this change allows for the `scoping` method to apply to all queries in the block. Previously this would only apply to queries on the class and not the instance. Ie `Post.create`, Post.all`, but not `post.update`, or `post.delete`. The change here will create a global scope that is applied to all queries for a relation for the duration of the block. Benefits: This change allows applications to add a scope to any query for the duration of a block. This is useful for applications using sharding to be able to control the query without requiring a `default_scope`. This is useful if you want to have more control over when a `scoping` is used on a relation. This also brings `scoping` in parity with the behavior of `default_scope` so there are less surprises between the behavior of these two methods. There are a caveats to this behavior: 1) The `scoping` only applies to objects of the same type. IE you cannot scope `Post.where(blog_id: 1).scoping` and then expect `post.comments` will apply `blog_id = 1` to the `Comment` query. This is not possible because the scope is `posts.blog_id = 1` and we can't apply the `posts` scope to a `comments` query. To solve this, scopes must be nested. 2) If a block is scoped to `all_queries` it cannot be unscoped without exiting the block. I couldn't find a way around this but ActiveRecord scoping is a bit complex and turning off `all_queries` when it's already on in nested scoping blocks had interesting behavior that I decided was best left out.
2021-02-10 14:26:40 -05:00
* Add ability to apply `scoping` to `all_queries`.
Some applications may want to use the `scoping` method but previously it only
worked on certain types of queries. This change allows the `scoping` method to apply
to all queries for a model in a block.
```ruby
Post.where(blog_id: post.blog_id).scoping(all_queries: true) do
post.update(title: "a post title") # adds `posts.blog_id = 1` to the query
end
```
*Eileen M. Uchitelle*
* `ActiveRecord::Calculations.calculate` called with `:average`
(aliased as `ActiveRecord::Calculations.average`) will now use column-based
2021-04-11 12:57:29 -04:00
type casting. This means that floating-point number columns will now be
aggregated as `Float` and decimal columns will be aggregated as `BigDecimal`.
2021-02-10 16:37:18 -05:00
Integers are handled as a special case returning `BigDecimal` always
(this was the case before already).
2021-02-10 16:37:18 -05:00
```ruby
# With the following schema:
create_table "measurements" do |t|
t.float "temperature"
end
# Before:
Measurement.average(:temperature).class
# => BigDecimal
# After:
Measurement.average(:temperature).class
# => Float
```
Before this change, Rails just called `to_d` on average aggregates from the
database adapter. This is not the case anymore. If you relied on that kind
of magic, you now need to register your own `ActiveRecord::Type`
(see `ActiveRecord::Attributes::ClassMethods` for documentation).
2021-02-10 16:37:18 -05:00
*Josua Schmid*
Active Record + PostgreSQL: native support for `timestamp with time zone` In https://github.com/rails/rails/issues/21126 it was suggested to make "timestamp with time zone" the default type for datetime columns in PostgreSQL. This is in line with PostgreSQL [best practices](https://wiki.postgresql.org/wiki/Don't_Do_This#Don.27t_use_timestamp_.28without_time_zone.29). This PR lays some groundwork for that. This PR adds a configuration option, `ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type`. The default is `:timestamp` which preserves current Rails behavior of using "timestamp without time zone" when you do `t.datetime` in a migration. If you change it to `:timestamptz`, you'll get "timestamp with time zone" columns instead. If you change this setting in an existing app, you should immediately call `bin/rails db:migrate` to ensure your `schema.rb` file remains correct. If you do so, then existing columns will not be impacted, so for example if you have an app with a mixture of both types of columns, and you change the config, schema dumps will continue to output the correct types. This PR also adds two new types that can be used in migrations: `t.timestamp` and `t.timestamptz`. ```ruby ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type = :timestamp # default value is :timestamp create_table("foo1") do |t| t.datetime :default_format # "timestamp without time zone" t.timestamp :without_time_zone # "timestamp without time zone" t.timestamptz :with_time_zone # "timestamp with time zone" end ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type = :timestamptz create_table("foo2") do |t| t.datetime :default_format # "timestamp with time zone" <-- note how this has changed! t.timestamp :without_time_zone # "timestamp without time zone" t.timestamptz :with_time_zone # "timestamp with time zone" end ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::NATIVE_DATABASE_TYPES[:my_custom_type] = { name: "custom_datetime_format_i_invented" } ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type = :my_custom_type create_table("foo3") do |t| t.datetime :default_format # "custom_datetime_format_i_invented" t.timestamp :without_time_zone # "timestamp without time zone" t.timestamptz :with_time_zone # "timestamp with time zone" end ``` **Notes** - This PR doesn't change the default `datetime` format. The default is still "timestamp without time zone". A future PR could do that, but there was enough code here just getting the config option right. - See also https://github.com/rails/rails/pull/41395 which set some groundwork (and added some tests) for this. - This reverts some of https://github.com/rails/rails/pull/15184. https://github.com/rails/rails/pull/15184 alluded to issues in XML serialization, but I couldn't find any related tests that this broke.
2021-03-09 14:29:07 -05:00
* PostgreSQL: introduce `ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type`
This setting controls what native type Active Record should use when you call `datetime` in
a migration or schema. It takes a symbol which must correspond to one of the configured
`NATIVE_DATABASE_TYPES`. The default is `:timestamp`, meaning `t.datetime` in a migration
will create a "timestamp without time zone" column. To use "timestamp with time zone",
change this to `:timestamptz` in an initializer.
You should run `bin/rails db:migrate` to rebuild your schema.rb if you change this.
*Alex Ghiculescu*
* PostgreSQL: handle `timestamp with time zone` columns correctly in `schema.rb`.
Previously they dumped as `t.datetime :column_name`, now they dump as `t.timestamptz :column_name`,
and are created as `timestamptz` columns when the schema is loaded.
*Alex Ghiculescu*
* Removing trailing whitespace when matching columns in
`ActiveRecord::Sanitization.disallow_raw_sql!`.
*Gannon McGibbon*, *Adrian Hirt*
* Expose a way for applications to set a `primary_abstract_class`
Multiple database applications that use a primary abstract class that is not
named `ApplicationRecord` can now set a specific class to be the `primary_abstract_class`.
```ruby
class PrimaryApplicationRecord
self.primary_abstract_class
end
```
When an application boots it automatically connects to the primary or first database in the
database configuration file. In a multiple database application that then call `connects_to`
needs to know that the default connection is the same as the `ApplicationRecord` connection.
2021-04-11 12:57:29 -04:00
However, some applications have a differently named `ApplicationRecord`. This prevents Active
Record from opening duplicate connections to the same database.
*Eileen M. Uchitelle*, *John Crepezzi*
* Support hash config for `structure_dump_flags` and `structure_load_flags` flags
Now that Active Record supports multiple databases configuration
we need a way to pass specific flags for dump/load databases since
the options are not the same for different adapters.
We can use in the original way:
```ruby
ActiveRecord::Tasks::DatabaseTasks.structure_dump_flags = ['--no-defaults', '--skip-add-drop-table']
#or
ActiveRecord::Tasks::DatabaseTasks.structure_dump_flags = '--no-defaults --skip-add-drop-table'
```
And also use it passing a hash, with one or more keys, where the key
is the adapter
```ruby
ActiveRecord::Tasks::DatabaseTasks.structure_dump_flags = {
mysql2: ['--no-defaults', '--skip-add-drop-table'],
postgres: '--no-tablespaces'
}
```
*Gustavo Gonzalez*
* Connection specification now passes the "url" key as a configuration for the
adapter if the "url" protocol is "jdbc", "http", or "https". Previously only
urls with the "jdbc" prefix were passed to the Active Record Adapter, others
are assumed to be adapter specification urls.
Fixes #41137.
*Jonathan Bracy*
* Allow to opt-out of `strict_loading` mode on a per-record base.
This is useful when strict loading is enabled application wide or on a
model level.
```ruby
class User < ApplicationRecord
2021-02-04 07:36:09 -05:00
has_many :bookmarks
has_many :articles, strict_loading: true
end
user = User.first
user.articles # => ActiveRecord::StrictLoadingViolationError
user.bookmarks # => #<ActiveRecord::Associations::CollectionProxy>
user.strict_loading!(true) # => true
user.bookmarks # => ActiveRecord::StrictLoadingViolationError
user.strict_loading!(false) # => false
user.bookmarks # => #<ActiveRecord::Associations::CollectionProxy>
user.articles.strict_loading!(false) # => #<ActiveRecord::Associations::CollectionProxy>
```
*Ayrton De Craene*
* Add `FinderMethods#sole` and `#find_sole_by` to find and assert the
presence of exactly one record.
Used when you need a single row, but also want to assert that there aren't
multiple rows matching the condition; especially for when database
constraints aren't enough or are impractical.
```ruby
Product.where(["price = %?", price]).sole
# => ActiveRecord::RecordNotFound (if no Product with given price)
# => #<Product ...> (if one Product with given price)
# => ActiveRecord::SoleRecordExceeded (if more than one Product with given price)
2020-12-30 19:02:34 -05:00
user.api_keys.find_sole_by(key: key)
# as above
```
*Asherah Connor*
* Makes `ActiveRecord::AttributeMethods::Query` respect the getter overrides defined in the model.
Before:
```ruby
class User
def admin
false # Overriding the getter to always return false
end
end
user = User.first
user.update(admin: true)
user.admin # false (as expected, due to the getter overwrite)
user.admin? # true (not expected, returned the DB column value)
```
After this commit, `user.admin?` above returns false, as expected.
Fixes #40771.
*Felipe*
* Allow delegated_type to be specified primary_key and foreign_key.
Since delegated_type assumes that the foreign_key ends with `_id`,
`singular_id` defined by it does not work when the foreign_key does
not end with `id`. This change fixes it by taking into account
`primary_key` and `foreign_key` in the options.
*Ryota Egusa*
* Expose an `invert_where` method that will invert all scope conditions.
```ruby
class User
scope :active, -> { where(accepted: true, locked: false) }
end
User.active
# ... WHERE `accepted` = 1 AND `locked` = 0
User.active.invert_where
# ... WHERE NOT (`accepted` = 1 AND `locked` = 0)
```
*Kevin Deisz*
* Restore possibility of passing `false` to :polymorphic option of `belongs_to`.
Previously, passing `false` would trigger the option validation logic
to throw an error saying :polymorphic would not be a valid option.
*glaszig*
* Remove deprecated `database` kwarg from `connected_to`.
*Eileen M. Uchitelle*, *John Crepezzi*
* Allow adding nonnamed expression indexes to be revertible.
Fixes #40732.
Previously, the following code would raise an error, when executed while rolling back,
and the index name should be specified explicitly. Now, the index name is inferred
automatically.
```ruby
add_index(:items, "to_tsvector('english', description)")
```
*fatkodima*
* Only warn about negative enums if a positive form that would cause conflicts exists.
Fixes #39065.
*Alex Ghiculescu*
Add option for `default_scope` to run on all queries This change allows for applications to optionally run a `default_scope` on `update` and `delete` queries. Default scopes already ran on select and insert queries. Applications can now run a set default scope on all queries for a model by setting a `all_queries` option: ```ruby class Article < ApplicationRecord default_scope -> { where(blog_id: 1) }, all_queries: true end ``` Using the default scope in this way is useful for applications that need to query by more than the primary key by default. An example of this would be in an application using a sharding strategy like Vitess like. For Rails sharding, we route connections first and then query the database. However, Vitess and other solutions use a parameter in the query to figure out how to route the queries. By extending `default_scope` to apply to all queries we can allow applications to optionally apply additional constraints to all queries. Note that this only works with `where` queries as it does not make sense to select a record by primary key with an order. With this change we're allowing apps to select with a primary key and an additional key. To make this change dynamic for routing queries in a tenant sharding strategy applications can use the `Current` API or parameters in a request to route queries: ```ruby class Article < ApplicationRecord default_scope -> { where(blog_id: Current.blog.id) }, all_queries: true end ``` In order to achieve this I created a new object when default scopes are created. This allows us to store both the scope itself and whether we should run this on all queries. I chose not to implement an `on:` option that takes an array of actions because there is no simple or clear way to turn off the default scope for create/select. It also doesn't really make sense to only have a default scope for delete queries. The decision to use `all_queries` here allows for the implementation to be more flexible than it was without creating a mess in an application.
2020-11-19 17:20:45 -05:00
* Add option to run `default_scope` on all queries.
Previously, a `default_scope` would only run on select or insert queries. In some cases, like non-Rails tenant sharding solutions, it may be desirable to run `default_scope` on all queries in order to ensure queries are including a foreign key for the shard (i.e. `blog_id`).
Add option for `default_scope` to run on all queries This change allows for applications to optionally run a `default_scope` on `update` and `delete` queries. Default scopes already ran on select and insert queries. Applications can now run a set default scope on all queries for a model by setting a `all_queries` option: ```ruby class Article < ApplicationRecord default_scope -> { where(blog_id: 1) }, all_queries: true end ``` Using the default scope in this way is useful for applications that need to query by more than the primary key by default. An example of this would be in an application using a sharding strategy like Vitess like. For Rails sharding, we route connections first and then query the database. However, Vitess and other solutions use a parameter in the query to figure out how to route the queries. By extending `default_scope` to apply to all queries we can allow applications to optionally apply additional constraints to all queries. Note that this only works with `where` queries as it does not make sense to select a record by primary key with an order. With this change we're allowing apps to select with a primary key and an additional key. To make this change dynamic for routing queries in a tenant sharding strategy applications can use the `Current` API or parameters in a request to route queries: ```ruby class Article < ApplicationRecord default_scope -> { where(blog_id: Current.blog.id) }, all_queries: true end ``` In order to achieve this I created a new object when default scopes are created. This allows us to store both the scope itself and whether we should run this on all queries. I chose not to implement an `on:` option that takes an array of actions because there is no simple or clear way to turn off the default scope for create/select. It also doesn't really make sense to only have a default scope for delete queries. The decision to use `all_queries` here allows for the implementation to be more flexible than it was without creating a mess in an application.
2020-11-19 17:20:45 -05:00
Now applications can add an option to run on all queries including select, insert, delete, and update by adding an `all_queries` option to the default scope definition.
```ruby
class Article < ApplicationRecord
default_scope -> { where(blog_id: Current.blog.id) }, all_queries: true
end
```
*Eileen M. Uchitelle*
* Add `where.associated` to check for the presence of an association.
```ruby
# Before:
account.users.joins(:contact).where.not(contact_id: nil)
# After:
account.users.where.associated(:contact)
```
Also mirrors `where.missing`.
*Kasper Timm Hansen*
* Allow constructors (`build_association` and `create_association`) on
`has_one :through` associations.
*Santiago Perez Perret*
2020-12-02 18:37:26 -05:00
Please check [6-1-stable](https://github.com/rails/rails/blob/6-1-stable/activerecord/CHANGELOG.md) for previous changes.