Commit Graph

2473 Commits

Author SHA1 Message Date
Paarth Madan
e39e013cf7 Specify where clauses by mapping columns to tuples
With the introduction of composite primary keys, a common usecase is querying for records with tuples representing the composite key. This change introduces new syntax to the where clause that allows specifying an array of columns mapped to a list of corresponding tuples. It converts this to an OR-joined list of separate queries, similar to previous implementations that rely on grouping queries.
2023-03-22 13:56:47 -04:00
Nick Borromeo
6bd100711c Allow SQL Warnings to be ignored using error codes
In https://github.com/rails/rails/pull/46690 the `db_warnings_action` and `db_warnings_ignore` configs where added. The
`db_warnings_ignore` can take a list of warning messages to match against.

At GitHub we have a subscriber that that does something like this but also filters out error codes. There might also be
other applications that filter via error codes and this could be something they can use instead of just the explicit
messages.

This also refactors the adapter tests in order for mysql2 and postgresql adapters to share the same helper when setting
the db_warnings_action and db_warnings_ignore configs
2023-03-20 13:06:22 -07:00
sampatbadhe
d9d8d8a4d2 Correct typos in activerecord changelog and querylogs docs [ci-skip] 2023-03-18 19:07:41 +05:30
Andrew Novoselac
49283adc16 Run a load hook when TestFixtures is included 2023-03-17 13:36:48 -04:00
Rafael Mendonça França
74554cde8b
Merge PR #47675 2023-03-15 13:05:44 +00:00
Andrew Novoselac
6902cbce1b Introducs TestFixtures#fixture_paths.
Multiple fixture paths can now be specified using the `#fixture_paths` accessor.
2023-03-14 19:02:56 -04:00
alpaca-tc
cbc7b59749 Adds support for deferrable exclude constraints in PostgreSQL.
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*
2023-03-14 23:29:35 +09:00
Jason Karns
1f61fd65ae
Delegated Type supports customizeable foreign_type column 2023-03-14 09:19:30 -04:00
alpaca-tc
d849ee04c6 Add support for unique constraints (PostgreSQL-only).
```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*
2023-03-10 16:09:32 +09:00
Rafael Mendonça França
049dfd4ccf
Remove deprecated Tasks::DatabaseTasks.schema_file_type 2023-03-03 00:38:27 +00:00
Rafael Mendonça França
96b9fd6f14
Remove deprecated config.active_record.partial_writes 2023-03-03 00:38:25 +00:00
Rafael Mendonça França
96c9db1b48
Remove deprecated ActiveRecord::Base config accessors 2023-03-03 00:38:24 +00:00
eileencodes
232aed8504
Allow configs_for to accept a custom hash key
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.
2023-02-28 12:12:08 -05:00
eileencodes
3d004435b4
Allow applications to register custom database configurations
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>
2023-02-28 10:12:28 -05:00
Carlos Antonio da Silva
0b32852878 Small tweaks on AR changelog [ci skip] 2023-02-24 09:50:45 -03:00
Jean Boussier
50edf85acd Fix a typo in activerecord/CHANGELOG 2023-02-24 11:58:44 +01:00
Jean Boussier
144518de1f Add missing CHANGELOG entries for #47463 and #47422 2023-02-24 10:03:07 +01:00
zzak
c7e444e3b0
💅 changelog from #44803 2023-02-22 10:48:15 +09:00
sabrams86
e42d9f7066 Add support for include index option
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
2023-02-21 15:52:50 -07:00
Jean Boussier
3635d2d9cf YAMLColumn: use YAML.safe_dump if available
One particularly annoying thing with YAMLColumn type restriction
is that it is only checked on load.

Which means if your code insert data with unsupported types, the
insert will work, but now you'll be unable to read the record, which
makes it hard to fix etc.

That's the reason why I implemented `YAML.safe_dump` (https://github.com/ruby/psych/pull/495).

It applies exactly the same restrictions than `safe_load`, which means
if you attempt to store non-permitted fields, it will fail on insertion
and not on further reads, so you won't create an invalid record in your
database.
2023-02-13 14:39:43 +01:00
Rafael Mendonça França
25d52ab782
Fix typo 2023-02-02 17:10:37 +00:00
Jean Boussier
0ff9beba87 ActiveRecord::QueryLogs: handle invalid encoding
It can sometimes happen that `sql` is encoded in UTF-8 but contains
some invalid binary data of some sort.

When this happens `strip` end up raising an EncodingError.

Overall I think this strip is quite wasteful, so we might as well
just skip it.
2023-02-01 13:48:54 +01:00
Reid Lynch
f61a679902 ActiveRecord::Relation#explain accepts options
For databases and adapters which support them (currently PostgreSQL
and MySQL), options can be passed to `explain` to provide more
detailed query plan analysis.
2023-01-24 09:24:43 -05:00
Ole Friis
06ef7a95ac Add ability to concatenate Arel.sql fragments 2023-01-19 09:27:05 +00:00
Alex Ghiculescu
d092166347 ActiveRecord::Base#signed_id: raise if called on a new record 2023-01-18 13:05:47 -07:00
Adrianna Chang
56c5685324 Allow SQL warnings to be reported for MySQL and Postgres.
`ActiveRecord.db_warnings_action` can be used to configure the
action to take when a query produces a warning. The warning can be
logged, raised, or trigger custom behaviour provided via a proc.

`ActiveRecord.db_warnings_ignore` allows applications to set an
allowlist of SQL warnings that should always be ignored, regardless
of the configured action.

Co-authored-by: Paarth Madan <paarth.madan@shopify.com>
2023-01-17 11:58:56 -05:00
Danielius
cfc0144832
Add regroup method 2023-01-14 12:08:46 +02:00
Leonardo Luarte González
7e95d3e653 Adds schema parameter into enable_extension
This patch tries to solve Heroku's new [PostgreSQL extension policy](https://devcenter.heroku.com/changelog-items/2446)
while keeping the migration and schema code idiomatic.

PostgreSQL adapter method `enable_extension` now allows to add an schema in its name.
The extension must be installed on another schema.

Usage:

`enable_extension('other_schema.hstore')`

The `enable_extension` can work with `schema` only if the given schema
already exists in the database.
2023-01-05 06:58:48 +00:00
George Claghorn
827ae3c449
ActiveRecord::Relation#none?/#any?/#one?: support pattern arg (#46728) 2022-12-22 08:21:54 +01:00
Jonathan Hefner
d4c31bd849 Add ActiveRecord::Base::normalizes
`ActiveRecord::Base::normalizes` declares a normalization for one or
more attributes.  The normalization is applied when the attribute is
assigned or updated, and the normalized value will be persisted to the
database.  The normalization is also applied to the corresponding
keyword argument of finder methods.  This allows a record to be created
and later queried using unnormalized values.  For example:

  ```ruby
  class User < ActiveRecord::Base
    normalizes :email, with: -> email { email.strip.downcase }
  end

  user = User.create(email: " CRUISE-CONTROL@EXAMPLE.COM\n")
  user.email                  # => "cruise-control@example.com"

  user = User.find_by(email: "\tCRUISE-CONTROL@EXAMPLE.COM ")
  user.email                  # => "cruise-control@example.com"
  user.email_before_type_cast # => "cruise-control@example.com"

  User.exists?(email: "\tCRUISE-CONTROL@EXAMPLE.COM ")         # => true
  User.exists?(["email = ?", "\tCRUISE-CONTROL@EXAMPLE.COM "]) # => false
  ```
2022-12-21 12:08:36 -06:00
Adrianna Chang
0b5a705e1b Hide changes to before_committed! behaviour behind config
As of https://github.com/rails/rails/pull/46525, the behaviour around
before_committed! callbacks has changed: callbacks are run on every
enrolled record in a transaction, even multiple copies of the same record.
This is a significant change that apps should be able to opt into in order
to avoid unexpected issues.
2022-12-15 11:43:51 -05:00
Eileen M. Uchitelle
b94b9bd984
Merge pull request #46641 from ghiculescu/query-log-namespace
Query Logs: `namespaced_controller` tag should match `controller` format
2022-12-13 13:13:34 -05:00
Alex Ghiculescu
090d1acfe4 Query Logs: namespaced_controller tag should match controller format
Currently if you do this:

```ruby
config.active_record.query_log_tags = [:namespaced_controller]
```

A request that's processed by the `NameSpaced::UsersController` will log as `namespaced_controller='NameSpaced%3A%3AUsersController'`.

By contrast if you set the tag to `:controller` it would log as `controller='user'`, much nicer.

This PR makes the `:namespaced_controller` formatting more similar to `:controller` - it will now log as `namespaced_controller='name_spaced/users'`.
2022-12-12 10:31:00 -06:00
David Heinemeier Hansson
d18fc32999
Use storage/ instead of db/ for sqlite3 db files (#46699)
* Use storage/ instead of db/ for sqlite3 db files

db/ should be for configuration only, not data. This will make it easier to mount a single volume into a container for testing, development, and even sqlite3 in production.
2022-12-12 08:32:12 +01:00
Joshua Young
8fd336927a [Fix: #46455] ActiveRecord::Calculations#ids plucks included associations IDs 2022-12-09 08:23:48 +05:30
Phil Pirozhkov
61464b0728 Don't use lower() for citext columns
In case when an index is present, using `lower()` prevents from using
the index.

The index is typically present for columns with uniqueness, and
`lower()` is added for `validates_uniqueness_of ..., case_sensitive: false`.

However, if the index is defined with `lower()`, the query without
`lower()` wouldn't use the index either.

Setup:
```
CREATE EXTENSION citext;
CREATE TABLE citexts (cival citext);
INSERT INTO citexts (SELECT MD5(random()::text) FROM generate_series(1,1000000));
```

Without index:
```
EXPLAIN ANALYZE SELECT * from citexts WHERE cival = 'f00';
 Gather  (cost=1000.00..14542.43 rows=1 width=33) (actual time=165.923..169.065 rows=0 loops=1)
   Workers Planned: 2
   Workers Launched: 2
   ->  Parallel Seq Scan on citexts  (cost=0.00..13542.33 rows=1 width=33) (actual time=158.218..158.218 rows=0 loops=3)
         Filter: (cival = 'f00'::citext)
         Rows Removed by Filter: 333333
 Planning Time: 0.070 ms
 Execution Time: 169.089 ms
Time: 169.466 ms

EXPLAIN ANALYZE SELECT * from citexts WHERE lower(cival) = lower('f00');
 Gather  (cost=1000.00..16084.00 rows=5000 width=33) (actual time=166.896..169.881 rows=0 loops=1)
   Workers Planned: 2
   Workers Launched: 2
   ->  Parallel Seq Scan on citexts  (cost=0.00..14584.00 rows=2083 width=33) (actual time=157.348..157.349 rows=0 loops=3)
         Filter: (lower((cival)::text) = 'f00'::text)
         Rows Removed by Filter: 333333
 Planning Time: 0.084 ms
 Execution Time: 169.905 ms
Time: 170.338 ms
```

With index:
```
CREATE INDEX val_citexts ON citexts (cival);

EXPLAIN ANALYZE SELECT * from citexts WHERE cival = 'f00';
 Index Only Scan using val_citexts on citexts  (cost=0.42..4.44 rows=1 width=33) (actual time=0.051..0.052 rows=0 loops=1)
   Index Cond: (cival = 'f00'::citext)
   Heap Fetches: 0
 Planning Time: 0.118 ms
 Execution Time: 0.082 ms
Time: 0.616 ms

EXPLAIN ANALYZE SELECT * from citexts WHERE lower(cival) = lower('f00');
 Gather  (cost=1000.00..16084.00 rows=5000 width=33) (actual time=167.029..170.401 rows=0 loops=1)
   Workers Planned: 2
   Workers Launched: 2
   ->  Parallel Seq Scan on citexts  (cost=0.00..14584.00 rows=2083 width=33) (actual time=157.180..157.181 rows=0 loops=3)
         Filter: (lower((cival)::text) = 'f00'::text)
         Rows Removed by Filter: 333333
 Planning Time: 0.132 ms
 Execution Time: 170.427 ms
Time: 170.946 ms

DROP INDEX val_citexts;
```

With an index with `lower()` has a reverse effect, a query with
`lower()` performs better:
```
CREATE INDEX val_citexts ON citexts (lower(cival));

EXPLAIN ANALYZE SELECT * from citexts WHERE cival = 'f00';
 Gather  (cost=1000.00..14542.43 rows=1 width=33) (actual time=174.138..177.311 rows=0 loops=1)
   Workers Planned: 2
   Workers Launched: 2
   ->  Parallel Seq Scan on citexts  (cost=0.00..13542.33 rows=1 width=33) (actual time=165.983..165.984 rows=0 loops=3)
         Filter: (cival = 'f00'::citext)
         Rows Removed by Filter: 333333
 Planning Time: 0.080 ms
 Execution Time: 177.333 ms
Time: 177.701 ms

EXPLAIN ANALYZE SELECT * from citexts WHERE lower(cival) = lower('f00');
                                                            QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on citexts  (cost=187.18..7809.06 rows=5000 width=33) (actual time=0.021..0.022 rows=0 loops=1)
   Recheck Cond: (lower((cival)::text) = 'f00'::text)
   ->  Bitmap Index Scan on lower_val_on_citexts  (cost=0.00..185.93 rows=5000 width=0) (actual time=0.018..0.018 rows=0 loops=1)
         Index Cond: (lower((cival)::text) = 'f00'::text)
 Planning Time: 0.102 ms
 Execution Time: 0.048 ms
(6 rows)
Time: 0.491 ms
```
2022-11-30 10:54:32 +03:00
Adrianna Chang
dc8c086572 Extract #sync_timezone_changes method in AbstractMysqlAdapter
This enables subclasses to sync database timezone changes without overriding `#raw_execute`,
which removes the need to redefine `#raw_execute` in the `Mysql2Adapter` and other
adapters subclassing `AbstractMysqlAdapter`.

Co-authored-by: Paarth Madan <paarth.madan@shopify.com>
2022-11-29 11:21:39 -05:00
mishaschwartz
691dbf0997 do not write additional new lines when dumping sql migration versions 2022-11-28 08:49:01 -05:00
sampatbadhe
b161c7ca50 update load_defaults 7.1 to config.load_defaults 7.1 2022-11-26 13:55:08 +05:30
Greg Navis
461d88a59b Fix composed_of freezing and duplication
composed_of values should be automatically frozen by Active Record.
This worked correctly when assigning a new value object via the writer,
but objects instantiated based on database columns were NOT frozen. The
fix consists of calling #dup and then #freeze on the cached value
object when it's added to the aggregation cache in #reader_method.

Additionally, values assigned via the accessor are duplicated and then
frozen to avoid caller confusion.
2022-11-25 17:36:12 +01:00
Phil Pirozhkov
b39050e8d5 Fix incorrect caching of case-insensitivity
`false` values would not cache with `||=`, and `begin` would run each time.
2022-11-25 12:00:07 +01:00
Alfred Dominic
61b8253387 option to disable all methods that ActiveRecord.enum generates 2022-11-24 15:26:05 +04:00
fatkodima
e5d15140d2 Avoid validating belongs_to association if it has not changed 2022-11-24 13:04:10 +02:00
George Claghorn
725ee79971 Allow resetting singular associations 2022-11-23 14:53:36 -05:00
Carlos Palhares
e313fc5a4d
Permit YAML classes and unsafe load per attribute
Allow unsafe_load to override application setting

Process options on load

Add CHANGELOG entry
2022-11-23 18:25:06 +00:00
Rafael Mendonça França
2434e201cf
Merge PR #45696 2022-11-23 16:54:00 +00:00
Alex Ghiculescu
7b094755c8 Fix docs for Readonly Attributes changes
Co-authored-by: Petrik de Heus <petrik@deheus.net>
2022-11-21 10:22:32 -06:00
Rafael Mendonça França
f2bbe6eb28
Merge PR #46105 2022-11-17 19:28:35 +00:00
Hartley McGuire
7b6720dfc8
Raise on assignment to readonly attributes
Previously, assignment would succeed but silently not write to the
database.

The changes to counter_cache are necessary because incrementing the
counter cache for a column calls []=. I investigated an approach to use
_write_attribute instead, however counter caches are expected to resolve
attribute aliases so write_attribute/[]= seems more correct.

Similarly, []= was replaced with _write_attribute in merge_target_lists
to skip the overriden []= and the primary key check. attribute_names
will already return custom primary keys so the primary_key check in
write_attribute is not needed.

Co-authored-by: Alex Ghiculescu <alex@tanda.co>
2022-11-16 17:14:54 -05:00
Rafael Mendonça França
f7af67e8f5
Merge PR #45147 2022-11-14 23:32:37 +00:00