Initial implementation falls back to `primary_key.is_a?(Array)` so
it can be immediately used in place of direct `is_a?` checks.
Though implementation may be changed to rely on a pre-initialized
`@composite_primary_key` ivar in the future.
`ActiveRecord::FinderMethods#find` now supports passing sets of
composite primary key values like:
```ruby
Cpk::Book.find([1, 1])
Cpk::Book.find([[1, 1]])
Cpk::Book.find([1, 1], [1, 2])
Cpk::Book.find([[1, 1], [1, 2]])
```
and treats values as values of the composite primary key columns but
only for models with the `primary_key` being an `Array`.
The codepaths related to destroying associations asynchronously now
consider when query constraints are present. In most cases, this means
interpreting the primary key as an array of columns, and identifying
associated records by a tuple of these columns, where previously this
would've been a single ID. In each of the callsites, we use branching.
This is done to maintain backwards compatibility and ensure the
signature of the destroy job remains stable: it has consumers outside of
Rails.
Returning `self` and `db_config` from `resolve_config_for_connection` is
hold over from legacy behavior when we returned `self.name` or
`Base.name` rather than just `self`. We can simplify this by passing
`self` directly` to the handlers `establish_connection`.
By default, exclude constraints in PostgreSQL are checked after each statement.
This works for most use cases, but becomes a major limitation when replacing
records with overlapping ranges by using multiple statements.
```ruby
exclusion_constraint :users, "daterange(valid_from, valid_to) WITH &&", deferrable: :immediate
```
Passing `deferrable: :immediate` checks constraint after each statement,
but allows manually deferring the check using `SET CONSTRAINTS ALL DEFERRED`
within a transaction. This will cause the excludes to be checked after the transaction.
It's also possible to change the default behavior from an immediate check
(after the statement), to a deferred check (after the transaction):
```ruby
exclusion_constraint :users, "daterange(valid_from, valid_to) WITH &&", deferrable: :deferred
```
*Hiroyuki Ishii*
Given a model with a composite primary key, the `query_constraints_list`
should equal the `primary_key` value to enable capabilities managed by
`query_constraints_list` such as `destroy`, `update`, `delete` and others.
Given a model with a composite primary key:
```ruby
class Order < ActiveRecord::Base
self.primary_key = [:shop_id, :id]
end
```
`ActiveRecord::Base#id` method will return an array of values for every
column of the primary key.
```ruby
order = Order.create!(shop_id: 1, id: 2)
order.id # => [1, 2]
```
The `id` column is accessible through the `read_attribute` method:
```ruby
order.read_attribute(:id) # => 2
```
```ruby
add_unique_key :sections, [:position], deferrable: :deferred, name: "unique_section_position"
remove_unique_key :sections, name: "unique_section_position"
```
See PostgreSQL's [Unique Constraints](https://www.postgresql.org/docs/current/ddl-constraints.html#DDL-CONSTRAINTS-UNIQUE-CONSTRAINTS) documentation for more on unique constraints.
By default, unique constraints in PostgreSQL are checked after each statement.
This works for most use cases, but becomes a major limitation when replacing
records with unique column by using multiple statements.
An example of swapping unique columns between records.
```ruby
old_item = Item.create!(position: 1)
new_item = Item.create!(position: 2)
Item.transaction do
old_item.update!(position: 2)
new_item.update!(position: 1)
end
```
Using the default behavior, the transaction would fail when executing the
first `UPDATE` statement.
By passing the `:deferrable` option to the `add_unique_key` statement in
migrations, it's possible to defer this check.
```ruby
add_unique_key :items, [:position], deferrable: :immediate
```
Passing `deferrable: :immediate` does not change the behaviour of the previous example,
but allows manually deferring the check using `SET CONSTRAINTS ALL DEFERRED` within a transaction.
This will cause the unique constraints to be checked after the transaction.
It's also possible to adjust the default behavior from an immediate
check (after the statement), to a deferred check (after the transaction):
```ruby
add_unique_key :items, [:position], deferrable: :deferred
```
PostgreSQL allows users to create a unique constraints on top of the unique
index that cannot be deferred. In this case, even if users creates deferrable
unique constraint, the existing unique index does not allow users to violate uniqueness
within the transaction. If you want to change existing unique index to deferrable,
you need execute `remove_index` before creating deferrable unique constraints.
*Hiroyuki Ishii*
Shopify is implementing a custom ActiveRecord adapter to integrate
with [Vitess](https://vitess.io/), and we would like to overload the
`valid_{column,table}_definition_options` methods and add
additional valid options for schema migrations.
For example:
```ruby
module ActiveRecord
module ConnectionAdapters
class VitessMysql2Adapter < Mysql2Adapter
...
def valid_table_definition_options
super + [:skip_vschema_migrations, :sharding_keys, :auto_increment]
end
def valid_column_definition_options
super + [:skip_vschema_migrations, :sharding_keys, :auto_increment]
end
end
end
end
```
This is the simplest possible change and factors out the various `valid_{table,column,primary_key}_definition_options` to be a public
method on an adapter instance.
We should properly hex-encode binary strings for SQLite3 and MySQL.
For e.g. MySQL, _not_ doing that _may_ work, but it will also
produce database warnings.
When an attribute is missing the current message is unclear about which
class is missing the attribute, especially when there are multiple
classes that could miss the attribute.
By adding the classs name to the error message it is easier to debug:
```ruby
user = User.first
user.pets.select(:id).first.user_id
=> ActiveModel::MissingAttributeError: missing attribute 'user_id' for Pet
```
This also makes the error message more inline with the
UnknownAttributeError message:
```ruby
=> ActiveModel::UnknownAttributeError: unknown attribute 'name' for Person
```
Co-authored-by: Yasuo Honda <yasuo.honda@gmail.com
Given a models setup like:
```ruby
class BlogPost < ApplicationRecord
query_constraints :blog_id, :id
has_many :comments, query_constraints: [:blog_id, :blog_post_id]
end
class Comment < ApplicationRecord
query_constraints :blog_id, :blog_post_id
belongs_to :blog_post, query_constraints: [:blog_id, :blog_post_id]
end
```
It should be possible to nullify both `blog_post.comments = []` and
`comment.blog_post = nil` association which should result in
nullification of all parts of the composite query constraints,
meaning `blog_id` and `blog_post_id` being nil on the affected comments.
Co-authored-by: Paarth Madan <paarth.madan@shopify.com>
`where_values_hash` method signature accepts `table_name` which is not
always the same as the association name.
So passing `through_association.reflection.name.to_s` as `table_name`
in `through_scope_attributes` wasn't accurate for every case.
This commit fixes the issue by passing the `table_name` taken from
`through_association.reflection.klass.table_name` instead.
Given a has_many through association with a composite foreign key, like:
```ruby
class BlogPost < ApplicationRecord
query_constraints :blog_id, :id
has_many :blog_post_tags, query_constraints: [:blog_id, :blog_post_id]
has_many :tags, through: :blog_post_tags
end
class BlogPostTag < ApplicationRecord
belongs_to :blog_post, query_constraints: [:blog_id, :blog_post_id]
belongs_to :tag, query_constraints: [:blog_id, :tag_id]
end
class Tag < ApplicationRecord
query_constraints :blog_id, :id
has_many :blog_post_tags, query_constraints: [:blog_id, :tag_id]
has_many :blog_posts, through: :blog_post_tags
end
```
It should be possible to assign tags to a blog post:
```ruby
blog_post = BlogPost.create!(title: "Hello world")
blog_post.tags << Tag.new(name: "ruby")
```
* Replace call to BINARY with CAST(_ AS BINARY)
* Remove space b/w function and quote
Co-authored-by: Rafael Mendonça França <rafael@franca.dev>
* Update unit test to assert for new CAST syntax
---------
Co-authored-by: Rafael Mendonça França <rafael@franca.dev>
Now that we support a way to register custom configurations we need to
allow applications to find those configurations. This change adds a
`config_key` option to `configs_for` to find db configs where the
configuration_hash contains a particular key.
I have also removed the deprecation for `include_replicas` while I was
in here to make the method signature cleaner. I've updated the upgrade
guide with the removal.
Previously, applications could only have two types of database
configuration objects, `HashConfig` and `UrlConfig`. This meant that if
you wanted your config to implement custom methods you had to monkey
patch `DatabaseConfigurations` to take a custom class into account. This
PR allows applications to register a custom db_config handler so that
custom configs can respond to needed methods. This is especially useful
for tools like Vitess where we may want to indicate it's sharded, but
not give Rails direct access to that knowledge.
Using the following database.yml as an example:
```yaml
development:
primary:
database: my_db
animals:
database: my_animals_db
vitess:
sharded: 1
```
We can register a custom handler that will generate `VitessConfig`
objects instead of a `HashConfig` object in an initializer:
```ruby
ActiveRecord::DatabaseConfigurations.register_db_config_handler do |env_name, name, url, config|
next unless config.key?(:vitess)
VitessConfig.new(env_name, name, config)
end
```
and create the `VitessConfig` class:
```ruby
class VitessConfig < ActiveRecord::DatabaseConfigurations::UrlConfig
def sharded?
vitess_config.fetch("sharded", false)
end
private
def vitess_config
configuration_hash.fetch(:vitess_config)
end
end
```
Now when the application is booted, the config with the `vitess` key
will generate a `VitessConfig` object where all others will generate a
`HashConfig`.
Things to keep in mind:
1) It is recommended but not required that these custom configs inherit
from Rails so you don't need to reimplement all the existing methods.
2) Applications must implement the configuration in which their config
should be used, otherwise first config wins (so all their configs
will be the custom one.)
3) The procs must support 4 arguments to accommodate `UrlConfig`. I am
thinking of deprecating this and forcing the URL parsing to happen in
the `UrlConfig` directly.
4) There is one tiny behavior change where when we have a nil url key in
the config hash we no longer merge it back into the configuration hash.
We also end up with a `HashConfig` instead of a `UrlConfig`. I think
this is fine because a `nil` URL is...useless.
Co-authored-by: John Crepezzi <john.crepezzi@gmail.com>
In `ToSql`, operators that generate the sql for an `Attribute` use `visit(o.left, collector)`
This can be seen in `Equality` (and friends), `Case`, `SelectStatement`, and `In`.
`HomogeneousIn` manually produces the sql for an `Attribute`, introduced in 72fd0bae
This change makes `HomogeneousIn` follow the pattern.
Before, it was using the configured by Rails. Having a mechanism to configure it
for Active Record encryption makes sense to prevent problems with encrypted content
when the default in Rails changes.
Additionally, there was a bug making AR encryption use the older SHA1 before
`ActiveSupport.hash_digest_class` got initialized to SHA256. This bug was exposed
by https://github.com/rails/rails/pull/44540. We will now set SHA256 as the standard
for 7.1+, and SHA1 for previous versions.
- Without this commit
```ruby
DEPRECATION WARNING: Passing the coder as positional argument is deprecated and will be remove in Rails 7.2.
Please pass the coder as a keyword argument:
serialize :foo, coder: JSON
(called from <class:User> at rep.rb:20)
DEPRECATION WARNING: Passing the class as positional argument is deprecated and will be remove in Rails 7.2.
Please pass the class as a keyword argument:
serialize :bar, type: Hash
(called from <class:User> at rep.rb:21)
```
- With this commit
```ruby
DEPRECATION WARNING: Passing the coder as positional argument is deprecated and will be remove in Rails 7.2.
Please pass the coder as a keyword argument:
serialize :foo, coder: JSON
(called from <class:User> at rep.rb:20)
DEPRECATION WARNING: Passing the class as positional argument is deprecated and will be remove in Rails 7.2.
Please pass the class as a keyword argument:
serialize :bar, type: Hash
(called from <class:User> at rep.rb:21)
```
With the universal adoption of UTF-8 in browsers, user facing text can
use more optimal Unicode typography. In digital and print design, using
RIGHT SINGLE QUOTATION MARK (U+2019) is normally preferred over
APOSTROPHE (U+0027) in contractions.
For details, see the Unicode Standard Section 6.2:
https://www.unicode.org/versions/Unicode13.0.0/ch06.pdf
> Punctuation Apostrophe. U+2019 right single quotation mark is
> preferred where the character is to represent a punctuation mark, as
> for contractions: “We’ve been here before.” In this latter case,
> U+2019 is also referred to as a punctuation apostrophe.
YAML is great for configuration and such, but for serializing arbitrary
data it has many problem, both in term of performance and efficiency
but also in term of security.
As such, we shouldn't let it be the default.
The open question is wether we should provide another default, or
just let users chose what they want based on their own tradeoffs.
Many people would probably suggest JSON as the new default, unfortunately
I don't think it's a good fit either because the parsers available in
Ruby have some wonky behaviors:
```ruby
>> ActiveSupport::JSON.decode(ActiveSupport::JSON.encode(Object.new))
=> {}
>> JSON.load(JSON.dump(Object.new))
=> "#<Object:0x000000012b61a068>"
```
If we were to select another default, I beleive it would need several
properties:
- It should only serialized a safe list of primitive types.
- It should explictly raise if attempting to serialize complex types.
* Remove Copyright years
* Basecamp is now 37signals... again
Co-authored-by: David Heinemeier Hansson <dhh@hey.com>
---------
Co-authored-by: David Heinemeier Hansson <dhh@hey.com>
YAML has quite a bit of footguns, as such it's desirable
to be able to substitute it for something else or even
simply to force users to define a serializer explictly for
every serialized columns.
Given an association like:
```ruby
BlogPost.has_many :blog_post_tags,
query_constraints: [:blog_id, :blog_post_id]
BlogPost.has_many :tags, through: :blog_post_tags
```
The `tags` association records can be queried
and the resulting JOIN query will include both `blog_id` and
`blog_post_id` in the ON clause.
Add support for including non-key columns in
btree indexes for PostgreSQL with the INCLUDE
parameter.
Example:
def change
add_index :users, :email, include: [:id, :created_at]
end
Will result in:
CREATE INDEX index_users_on_email USING btree (email) INCLUDE (id,
created_at)
The INCLUDE parameter is described in the PostgreSQL docs:
https://www.postgresql.org/docs/current/sql-createindex.html
Fixes#47400.
Previously, when copying a table as a result of an ALTER TABLE
statement, the AUTOINCREMENT declaration on the PK column was dictated
by the absence of a `:default` option on that column. However,
we were always passing `default` when copying the columns to the new table,
regardless of whether the column originally had a default. This led to
the AUTOINCREMENT declaration being lost.
This commit fixes the issue by tracking a column's auto_increment value,
and only passing the `default` option in `#copy_table` when the column
does not have the `auto_increment` option set.
Co-authored-by: fatkodima <fatkodima123@gmail.com>
In legacy systems the `owner_name` didn't have to be a class so it's
possible that connections are using a string as an identifier. I think
this should be allowed since we turn it into a class that defines the
necessary methods that are called on the connection class object (like
`primary_class?` and `preventing_writes?`.
Before, `owner_name` had to be a class so any application wanting to use
a special identifier would be forced to use `ActiveRecord::Base` or
ensure their `config` was a `Symbol`. With this change, you can pass a
`db_config` object and a string to use for the owner OR you can pass a
symbol and still define a custom owner. In some cases you might not want
the name to default to the symbol version of your config name.