Checking whether a record has a foreign key for a composite primary key
association requires us to check whether all parts of the foreign key
are present. Otherwise, the inverse association will not be set.
I noticed in a build that this test was flaky and causing subsequent
tests to fail with a no connection pool error. I don't entirely get why
this test was failing, it's not the only one where we remove a
connection. When this test fails, `@automatic_reconnect` is `false`.
After spending far too much time trying to figure out the combination of
tests that break this I decided to fix it by making a brand new
connection that we can remove. It's also a more accurate test because
we're asserting the connection is actually not `connected?`. Note that I
had to use the connection handler to remove the connection because there
is an inconsitency in remove_connection on the model. Unfortunately,
`NoConnection.remove_connection` will cause `connected?` to fall back to
`ActiveRecord::Base` which is contrary to the behavior of all other
methods on the active record models. I'm going to send a PR to fix that
but for now this should fix the (rare) flaky test for a primary key with
no connection.
To reproduce I used this seed: `SEED=29535 bundle exec rake test:mysql2`
Ref: https://github.com/rails/rails/issues/48330
When replacing the has_one target, it seems more correct to first
delete the old record, so that if the associated model has a uniqueness
validator, it won't fail.
Both the delete and the insert are in a single transaction, so if the new
record fail to be saved, the transaction will be rolled back, which seems
correct.
If `dependent: :destroy` isn't set, Active Record will try to orphan the
record, which may or may not be valid depending on the schema and validators.
Co-Authored-By: zzak <zzakscott@gmail.com>
`assert_raises` has been extended with a `match:` keyword argument to
assert the error message so there is no reason for Rails itself to keep
maintaining custom implementations of the same feature.
This commit stops issuing the
"Active Record does not support composite primary key" warning
and allows `ActiveRecord::Base#primary_key` to be derived as an `Array`
`#scope_for_create` for singular associations removes the primary key
from the scope so that we don't assign PK columns when building an
association.
However, removing the primary key from the scope doesn't currently
handle composite primary keys. This commit fixes that.
When find_each/find_in_batches/in_batches are performed on a table with composite primary keys, ascending or descending order can be selected for each key.
```ruby
Person.find_each(order: [:desc, :asc]) do |person|
person.party_all_night!
end
```
This commit handles destroying CPK associations when autosave is set
and the parent association is marked for destruction. It does so
by ensuring that all parts of the foreign key (the parent's CPK)
are set to nil before destroying the parent record.
Anytime an exception is raised from an adapter we now provide a
`connection_pool` along for the application to further debug what went
wrong. This is an important feature when running a multi-database Rails
application.
We chose to provide the `connection_pool` as it has relevant context
like connection, role and shard. We wanted to avoid providing the
`connection` directly as it might accidentally be used after it's
returned to the pool and been handed to another thread.
The `ConnectionAdapters::PoolConfig` would also have been a reasonable
option except it's `:nodoc:`.
While we had hoped to turn prepared statements on for Rails 7.2, the bug
that's preventing us from doing that is still present. See #43005.
Until this bug is fixed we should not be encouraging applications
running mysql to change the `prepared_statements` in the config to
`true`. In addition to this bug being present, Trilogy does not yet
support `prepared_statements` (although work is in progress).
It will be better to implement this deprecation when mysql2 and trilogy
can both handle `prepared_statements` without major bugs.
This commit extends Active Record creation logic to allow for a database
auto-populated attributes to be assigned on object creation.
Given a `Post` model represented by the following schema:
```ruby
create_table :posts, id: false do |t|
t.integer :sequential_number, auto_increment: true
t.string :title, primary_key: true
t.string :ruby_on_rails, default: -> { "concat('R', 'o', 'R')" }
end
```
where `title` is being used as a primary key, the table has an
integer `sequential_number` column populated by a sequence and
`ruby_on_rails` column has a default function - creation of
`Post` records should populate the `sequential_number` and
`ruby_on_rails` attributes:
```ruby
new_post = Post.create(title: 'My first post')
new_post.sequential_number # => 1
new_post.ruby_on_rails # => 'RoR'
```
* At this moment MySQL and SQLite adapters are limited to only one
column being populated and the column must be the `auto_increment`
while PostgreSQL adapter supports any number of auto-populated
columns through `RETURNING` statement.
If an application is using sharding, they may not want to use `default`
as the `default_shard`. Unfortunately Rails expects there to be a shard
named `default` for certain actions internally. This leads to some
errors on boot and the application is left manually setting
`default_shard=` in their model or updating their shards in
`connects_to` to name `shard_one` to `default`. Neither are a great
solution, especially if Rails can do this for you. Changes to Active
Record are:
* Simplify `connects_to` by merging `database` into `shards` kwarg so we
can do a single loop through provided options.
* Set the `self.default_shard` to the first keys in the shards kwarg.
* Add a test for this behavior
* Update existing test that wasn't testing this to use `default`. I
could have left this test but it really messes with connections in the
other tests and since this isn't testing shard behavior specifically, I
updated it to use `default` as the default shard name.
This is a slight change in behavior from existing applications but
arguably this is fixing a bug because without this an application won't
boot. I originally thought that this would require a huge refactoring to
fix but realized that it makes a lot of sense to take the first shard as
they default. They should all have the same schema so we can assume it's
fine to take the first one.
Fixes: #45390