Commit Graph

2405 Commits

Author SHA1 Message Date
modulitos
6df894fd56 Add sqlcommenter formatting support on query logs
Co-authored-by: Iheanyi Ekechukwu <iekechukwu@gmail.com>
2022-09-27 14:38:35 -07:00
Eike Send
5ba8aa5854
Facilitate use of any regular ERB in database.yml
Commit 37d1429ab1d introduced the DummyERB to avoid loading the environment when
running `rake -T`.

The DummyCompiler simply replaced all output from `<%=` with a fixed string and
removed everything else. This worked okay when it was used for YAML values.
When using `<%=` within a YAML key, it caused an error in the YAML parser,
making it impossible to use ERB as you would expect. For example a
`database.yml` file containing the following should be possible:

  development:
    <% 5.times do |i| %>
    shard_<%= i %>:
      database: db/development_shard_<%= i %>.sqlite3
      adapter: sqlite3
    <% end %>

Instead of using a broken ERB compiler we can temporarily use a
`Rails.application.config` that does not raise an error when configurations are
accessed which have not been set as described in #35468.

This change removes the `DummyCompiler` and uses the standard `ERB::Compiler`.
It introduces the `DummyConfig` which delegates all known configurations to the
real `Rails::Application::Configuration` instance and returns a dummy string for
everything else. This restores the full ERB capabilities without compromising on
speed when generating the rake tasks for multiple databases.

Deprecates `config.active_record.suppress_multiple_database_warning`.
2022-09-27 17:07:40 +02:00
Petrik
39a20095bf Add table to error for duplicate column definitions
If a migration defines duplicate columns for a table the error message
should show which table it concerns.
2022-09-23 15:40:37 +02:00
Sam Bostock
b85ee91633
Fix virtual datetime default precision
Prior to this change

    t.virtual :column_name, type: :datetime

would erroneously produce the same result as

    t.virtual :column_name, type: :datetime, precision: nil

This is because the code path for `virtual` skipped the default lookup:

- `t.datetime` is delegated to the `column` method
  - `column` sees `type == :datetime` and sets a default `precision` is none was given
  - `column` calls `new_column_definition` to add the result to `@columns_hash`
- `t.virtual` is delegated to the `column` method
  - `column` sees `type == :virtual`, not `:datetime`, so skips the default lookup
  - `column` calls `new_column_definition` to add the result to `@columns_hash`
    - `new_column_definition` sees `type == :virtual`, so sets `type = options[:type]`

By moving the default lookup, we get consistent code paths:

- `t.datetime` is delegated to the `column` method
  - `column` calls `new_column_definition` to add the result to `@columns_hash`
    - `new_column_definition` sees `type == :datetime` and sets a default `precision` is none was given
- `t.virtual` is delegated to the `column` method
  - `column` calls `new_column_definition` to add the result to `@columns_hash`
    - `new_column_definition` sees `type == :virtual`, so sets `type = options[:type]`
    - `new_column_definition` sees `type == :datetime` and sets a default `precision` is none was given
2022-09-22 17:58:25 -04:00
Adrianna Chang
b00ca21999 Use connection from #with_raw_connection in #quote_string.
This ensures we hook into the retry logic for connection-related exceptions
that `#with_raw_connection` provides.
2022-09-22 17:07:55 -04:00
Shouichi Kamiya
364939c2b1 Add expires_at option to signed_id
Problem:

Though, the `MessageVerifier` that powers `signed_id` supports both
`expires_in` and `expires_at`, `signed_id` only supports `expires_in`.
Because of this, generating signed_id that expires at a certain time is
somewhat tedious. Imagine issuing a coupon that is valid only for a
day.

Solution:

Add `expires_at` option to `signed_id` to generate signed ids that
expire at the given time.
2022-09-22 22:39:55 +02:00
Adrianna Chang
4d13f05a2e Take into account timeout limit when retrying queries
Building on the work done in #44576 and #44591, we extend the logic that automatically
reconnects broken db connections to take into account a timeout limit. This ensures
that retries + reconnects are slow-query aware, and that we don't retry queries
if a given amount of time has already passed since the query was first tried.

This value will default to 5 seconds, but can be adjusted via the `connection_retry_timeout`
config.
2022-09-22 14:29:33 -04:00
Aaron Patterson
5abb45d53a Dup and freeze complex types when making query attributes
This avoids problems when complex data structures are mutated _after_
being handed to ActiveRecord for processing.  For example false hits in
the query cache.

Fixes #46044
2022-09-19 09:41:39 +02:00
Petrik
d14662a5ae Tiny copy fix in CHANGELOGs [ci-skip] 2022-09-17 12:54:43 +02:00
Petrik
ed3b92b38f Add ssl-mode option to dbconsole command and MySQLDatabaseTasks
According to the MySQL documentation, database connections default to
ssl-mode=PREFERRED. But PREFERRED doesn't verify the server's identity:

    The default setting, --ssl-mode=PREFERRED, produces an encrypted
    connection if the other default settings are unchanged. However, to
    help prevent sophisticated man-in-the-middle attacks, it is
    important for the client to verify the server’s identity. The
    settings --ssl-mode=VERIFY_CA and --ssl-mode=VERIFY_IDENTITY are a
    better choice than the default setting to help prevent this type of
    attack. VERIFY_CA makes the client check that the server’s
    certificate is valid. VERIFY_IDENTITY makes the client check that
    the server’s certificate is valid, and also makes the client check
    that the host name the client is using matches the identity in the
    server’s certificate.

https://dev.mysql.com/doc/refman/8.0/en/using-encrypted-connections.html

However both the Rails::DBConsole command and the MySQLDatabaseTasks
ignore the ssl-mode option, making the connection fallback to PREFERRED.

Adding ssl-mode to the forwarded options makes sure the expected mode is
passed to the connection.
2022-09-15 19:20:16 +02:00
eileencodes
93ddf338a0
Move InternalMetadata to an independent object
Followup to #45908 to match the same behavior as SchemaMigration

Previously, InternalMetadata inherited from ActiveRecord::Base. This is
problematic for multiple databases and resulted in building the code in
AbstractAdapter that was previously there. Rather than hacking around
the fact that InternalMetadata inherits from Base, this PR makes
InternalMetadata an independent object. Then each connection can get it's
own InternalMetadata object. This change required defining the methods
that InternalMetadata was depending on ActiveRecord::Base for (ex
create!). I reimplemented only the methods called by the framework as
this class is no-doc's so it doesn't need to implement anything beyond
that. Now each connection gets it's own InternalMetadata object which
stores the connection.

This change also required adding a NullInternalMetadata class for cases
when we don't have a connection yet but still need to copy migrations
from the MigrationContext. Ultimately I think this is a little weird -
we need to do so much work to pick up a set of files? Maybe something to
explore in the future.

Aside from removing the hack we added back in #36439 this change will
enable my work to stop clobbering and depending directly on
Base.connection in the rake tasks. While working on this I discovered
that we always have a ActiveRecord::InternalMetadata because the
connection is always on Base in the rake tasks. This will free us up
to do less hacky stuff in the migrations and tasks.

Both schema migration and internal metadata are blockers to removing
`Base.connection` and `Base.establish_connection` from rake tasks, work
that is required to drop the reliance on `Base.connection` which will
enable more robust (and correct) sharding behavior in Rails..
2022-09-12 09:17:02 -04:00
Rafael Mendonça França
91a2ae5e59
Remove CHANGELOG entry for change only existing in main
The issue fixed by the commit that introduced that entry only existed
in the main branch, so it isn't really a released change worthy of a
CHANGELOG entry.
2022-09-09 22:56:36 +00:00
Rafael Mendonça França
9f372c94ea
Merge PR #44438 2022-09-09 21:14:08 +00:00
admin
62c358595c
improve "in_order_of" to allow string column name 2022-09-09 21:46:04 +03:00
Iliana Hadzhiatanasova
a0fd15ee7e Default prepared_statements to false for mysql2 adapter 2022-09-09 16:21:33 +01:00
eileencodes
436277da88
Move SchemaMigration to an independent object
Previously, SchemaMigration inherited from ActiveRecord::Base. This is
problematic for multiple databases and resulted in building the code in
AbstractAdapter that was previously there. Rather than hacking around
the fact that SchemaMigration inherits from Base, this PR makes
SchemaMigration an independent object. Then each connection can get it's
own SchemaMigration object. This change required defining the methods
that SchemaMigration was depending on ActiveRecord::Base for (ex
create!). I reimplemented only the methods called by the framework as
this class is no-doc's so it doesn't need to implement anything beyond
that. Now each connection gets it's own SchemaMigration object which
stores the connection. I also decided to update the method names (create
-> create_version, delete_by -> delete_version, delete_all ->
delete_all_versions) to be more explicit.

This change also required adding a NullSchemaMigraiton class for cases
when we don't have a connection yet but still need to copy migrations
from the MigrationContext. Ultimately I think this is a little weird -
we need to do so much work to pick up a set of files? Maybe something to
explore in the future.

Aside from removing the hack we added back in #36439 this change will
enable my work to stop clobbering and depending directly on
Base.connection in the rake tasks. While working on this I discovered
that we always have a `ActiveRecord::SchemaMigration` because the
connection is always on `Base` in the rake tasks. This will free us up
to do less hacky stuff in the migrations and tasks.
2022-09-08 11:32:32 -04:00
eileencodes
69396b75c9
Make connection_pool_list take an explicit argument
Following on #45924 I realized that `all_connection_pools` and
`connection_pool_list` don't make much sense as separate methods and
should follow the same deprecation as the other methods on the handler
here. So this PR deprecates `all_connection_pools` in favor of
`connection_pool_list` with an explicit argument of the role or `:all`.
Passing `nil` will throw a deprecation warning to get applications to
be explicit about behavior they expect.
2022-09-07 14:44:18 -04:00
eileencodes
74cb960e66
Fix bug in connection handler methods using all pools
Previously when I implemented multiple database roles in Rails there
were two handlers so it made sense for the methods
`active_connections?`, `clear_active_connections!`,
`clear_reloadable_connections!`, `clear_all_connections!`, and
`flush_idle_connections!` to only operate on the current (or passed)
role and not all pools regardless of role. When I removed this and moved
all the pools to the handler maintained by a pool manager, I left these
methods as-is to preserve the original behavior.

This made sense because I thought these methods were only called by
applications and not called by Rails. I realized yesterday that some of
these methods (`flush_idle_connections!`, `clear_active_connections!`,
and `clear_reloadable_connections!` are all called on boot by the
Active Record railtie.

Unfortunately this means that applications using multiple databases
aren't getting connections flushed or cleared on boot for any connection
but the writing ones.

The change here continues existing behavior if a role like reading is
passed in directly. Otherwise if the role is `nil` (which is the new
default` we fall back to all connections and issue a deprecation
warning. This will be the new default behavior in the future. In order
to easily allow turning off the deprecation warning I've added an `:all`
argument that will use all pools but no warning. The deprecation warning
will only fire if there is more than one role in the pool manager,
otherwise we assume prior behavior.

This bug would have only affected applications with more than one role
and only when these methods are called outside the context of a
`connected_to` block. These methods no longer consider the set
`current_role` and applications need to be explicit if they don't want
these methods to operate on all pools.
2022-09-07 13:42:23 -04:00
alextrueman
f4cfc2acdd Allow AR::QueryMethods#select to accept a hash
Add ability to use hash with columns and aliases inside #select method.

    Post
      .joins(:comments)
      .select(
        posts: { id: :post_id, title: :post_title },
        comments: { id: :comment_id, body: :comment_body}
      )

    instead

    Post
      .joins(:comments)
      .select(
        "posts.id as post_id, posts.title as post_title,
        comments.id as comment_id, comments.body as comment_body"
      )

Co-authored-by: Josef Šimánek <193936+simi@users.noreply.github.com>
Co-authored-by: Jean byroot Boussier <19192189+casperisfine@users.noreply.github.com>
2022-09-02 11:00:58 +02:00
Jonathan Hefner
e45947328e Fix typo in CHANGELOG [ci-skip]
Follow-up to #42650.
2022-08-25 11:54:13 -05:00
Jacopo
2f5136a3be Normalize virtual attributes on ActiveRecord::Persistence#becomes
When source and target classes have a different set of attributes adapts
attributes such that the extra attributes from target are added.

Fixes #41195

Co-authored-by: SampsonCrowley <sampsonsprojects@gmail.com>
Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>
2022-08-24 22:51:57 +02:00
Jonathan Hefner
18f8c6ac9b Add CHANGELOG entry for #45670 [ci-skip] 2022-08-17 11:51:31 -05:00
Iliana Hadzhiatanasova
79096618f0 Optimize add_timestamps to use a single SQL statement 2022-08-11 19:12:03 +03:00
Aram Greenman
affb8cf2fe Deprecate quoting ActiveSupport::Duration as an integer 2022-08-04 09:15:58 -07:00
Alex Ghiculescu
bd0fdc8094 Add drop_enum command for Postgres 2022-08-03 18:50:43 -05:00
eileencodes
25f97a66bd
Add support for if_exists option when removing a check constraint
The `remove_check_constraint` method now accepts an `if_exists` option. If set
to true an error won't be raised if the check constraint doesn't exist.

This commit is a combination of PR #45726 and #45718 with some
additional changes to improve wording, testing, and implementation.

Usage:

```ruby
remove_check_constraint :products, name: "price_check", if_exists: true
```

Fixes #45634

Co-authored-by: Margaret Parsa <mparsa@actbluetech.com>
Co-authored-by: Aditya Bhutani <adi_bhutani16@yahoo.in>
2022-08-03 14:39:54 -04:00
Jean Boussier
023a3eb3c0 find_or_create_by: handle race condition by finding again
Using `create_or_find_by` in codepaths where most of the time
the record already exist is wasteful on several accounts.

`create_or_find_by` should be the method to use when most of the
time the record doesn't already exist, not a race condition safe
version of `find_or_create_by`.

To make `find_or_create_by` race-condition free, we can search
the record again if the creation failed because of an unicity
constraint.

Co-Authored-By: Alex Kitchens <alexcameron98@gmail.com>
2022-08-02 09:58:26 +02:00
Matthew Draper
1adb6170d5 Add changelog entries for #44576 and #44591 2022-07-30 06:09:12 +09:30
fatkodima
515a9215d1 Avoid removing a PostgreSQL extension when there are dependent objects 2022-07-22 15:17:18 +03:00
Michael Siegfried
8fe1bd555f Accept nested functions in Dangerous Query Methods
Mailing list thread: https://discuss.rubyonrails.org/t/feature-proposal-accept-nested-functions-w-r-t-dangerous-query-methods/78650

*Summary*
I think there’s an opportunity to reduce additional false positives for
Dangerous Query Method deprecations/errors.

*Nested Functions*
Similar to https://github.com/rails/rails/pull/36448, it seems
reasonable to allow functions that accept other functions (e.g.
`length(trim(title))`).

*Background*
* PR accepting non-nested functions: https://github.com/rails/rails/pull/36448
* Deep background on deprecation and false positives: https://github.com/rails/rails/issues/32995
* Constants: `COLUMN_NAME` for the first and `COLUMN_NAME_WITH_ORDER` for both
2022-07-13 11:52:01 -07:00
Ben Sheldon
8fceabd1e8 Defer constant loading of ActiveRecord::DestroyAssociationAsyncJob via a String instead of a class constant
Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>
2022-07-13 11:58:20 -05:00
Jean Boussier
05fdb3edfd ActiveRecord::Store encode store as a regular Hash
Fix: https://github.com/rails/rails/issues/45585

There's no benefit in serializing it as HWIA, it requires
to allow that type for YAML safe_load and takes more space.

We can cast it back to a regular hash before serialization.
2022-07-13 18:30:37 +02:00
Yasuo Honda
2cf8f37d50
Merge pull request #44601 from ghiculescu/time-zone-aware-type-postgres
Add `timestamptz` as a time zone aware type for PostgreSQL
2022-07-11 08:23:04 +09:00
Alex Ghiculescu
2f52610c49
Missing author on changelog entry for #44189
cc @jonathanhefner
2022-07-10 15:33:50 -05:00
Alex Ghiculescu
75c406d774 Make timestamptz a time zone aware type for Postgres
https://github.com/rails/rails/pull/41395 added support for the `timestamptz` type on the Postgres adapter.

As we found [here](https://github.com/rails/rails/pull/41084#issuecomment-1056430921) this causes issues because in some scenarios the new type is not considered a time zone aware attribute, meaning values of this type in the DB are presented as a `Time`, not an `ActiveSupport::TimeWithZone`.

This PR fixes that by ensuring that `timestamptz` is always a time zone aware type, for Postgres users.
2022-07-10 15:32:18 -05:00
Jonathan Hefner
d44e786d21 Add CHANGELOG entry for #44189 [ci-skip] 2022-07-08 14:54:42 -05:00
fatkodima
620f247829 Optimize Active Record batching for whole table iterations 2022-07-06 00:21:39 +03:00
Vlado Cingel
098b0eb5db .with query method added.
Construct common table expressions with ease and get `ActiveRecord::Relation` back.
2022-07-01 17:44:51 +02:00
Rafael Mendonça França
92fa470aaa
Copy edit the CHANGELOG
Active Record names has space between the words.
2022-06-29 22:14:48 +00:00
eileencodes
adb64db43d
Only remove connection for an existing pool if the config is different
Previously Rails would always remove the connection if it found a
matching class in the pool manager. Therefore if
`ActiveRecord::Base.establish_connection` was called with the same
config, each time it was called it would be clobbered, even though the
config hasn't changed and the existing connection is prefectly fine. As
far as I can tell from conversations and reading the history this
functionality was added for ActiveRecord tests to be able to clobber the
connection and use a new config, then re-establish the old connection.
Essentially outside Rake tasks and AR tests, this functionality doesn't
have a ton of value.

On top of not adding a ton of value, this has resulted in a few bugs. In
Rails 6.0 I made it so that if you established a connection on
`ApplicationRecord` Rails would treat that connection the same as
`ActiveRecord::Base.` The reason for this is that the Railtie
establishes a connection on boot to the first database, but then if
you're using multiple databases you're calling `connects_to` in your
`ApplicationRecord` or primary abstract class which essentially doubles
your connections to the same database. To avoid opening 2 connections to
the same database, Rails treats them the same.

However, because we have this code that removes existing connections,
when an application boots, `ApplicationRecord` will clobber the
connection that the Railtie established even though the connection
configs are the same.

This removal of the connection caused bugs in migrations that load up a
model connected to `ApplicationRecord` (ex `Post.first`) and then calls
`execute("SELECT 1")` (obviously a simplified example). When `execute`
runs the connection is different from the one opened to run the
migration and essentially it is lost when the `remove_connection` code
is called.

To fix this I've updated the code to only remove the connection if the
database config is different. Ultimately I'd like to remove this code
altogether but to do that we first need to stop using
`Base.establish_connection` in the rake tasks and tests. This will fix
the major bugs until I come up with a solution for the areas that
currently need to call `establish_connection` on Base.

The added benefit of this change is that if your app is calling
`establish_connection` multiple times with the same config, it is now
3x faster than the previous implementation because we can return the
found pool instead of setting it up again. To benchmark this I
duplicated the `establish_connection` method to use the new behavior
with a new name.

Benchmark script:

```ruby
require "active_record"
require "logger"
require "benchmark/ips"

config_hash = { "development" => { "primary" => { "adapter" => "mysql2", "username" => "rails", "database" => "activerecord_unittest"}}}
ActiveRecord::Base.configurations = config_hash

db_config = ActiveRecord::Base.configurations.configs_for(env_name: "development", name: "primary")

p "Same model same config"
ActiveRecord::Base.connected_to(role: :writing, prevent_writes: true) do
  Benchmark.ips do |x|
    x.report "establish_connection with remove" do
      ActiveRecord::Base.establish_connection(db_config)
    end

    x.report "establish_connection without remove" do
      ActiveRecord::Base.establish_connection_no_remove(db_config)
    end

    x.compare!
  end
end
```

Benchmark results:

```
Warming up --------------------------------------
establish_connection with remove
                         4.677k i/100ms
establish_connection without remove
                        19.501k i/100ms
Calculating -------------------------------------
establish_connection with remove
                         41.252k (±11.3%) i/s -    205.788k in   5.075525s
establish_connection without remove
                        179.205k (± 6.9%) i/s -    897.046k in   5.029742s

Comparison:
establish_connection without remove:   179205.1 i/s
establish_connection with remove:    41252.3 i/s - 4.34x  (± 0.00) slower
```

Other changes:

1) sqlite3 now disconnects and reconnects the connection when `purge` is
called. This is necessary now that a new connection isn't created
everyt time `establish_connection` is called. Without this change to
purge the new database is left in an inaccessible state causing a
readonly error from the sqlite3 client. This wasn't happening in mysql
or postgres because they were already reconnecting the db connection.
2) I added `remove_connection` to tests that use `ApplicationRecord`.
This is required because `ApplicationRecord` or any class that is a
`primary_abstract_class` will be treated the same as
`ActiveRecord::Base`. This is fine in applications because they are
shared connections, but in the AR test environment, we don't want those
connnections to stick around (we want AR::Base back).
3) In the async tests I removed 2 calls to `establish_connection`. These
were causing sqlite3 tests to leak the state of async_executor because
it's stored on the connection. I'm not sure why these were calling
`establish_connection` but it's not necessary and was leaking state when
now that we are no longer removing the connection.

Fixes: #41855
Fixes: #41876
Fixes: #42873
Fixes: #43004
2022-06-29 11:25:17 -04:00
Ben Sheldon [he/him]
660fee087d
Allow db:prepare to load schema if database already exists but is empty; also dumps schema after migrations 2022-06-28 06:36:24 -07:00
Wojciech Wnętrzak
73b1a153b5
Fix supporting timezone awareness for tsrange and tstzrange array columns.
Before the fix, the error was thrown "TypeError: can't iterate from ActiveSupport::TimeWithZone".
2022-06-26 12:29:34 +02:00
Yasuo Honda
681ada1879 Remove trailing whitespaces of Active Record CHANGELOG
Follow up #45280
There are couple of pull requests that remove these trailing spaces.
Changing the trailing whitespaces is not related to them.
2022-06-26 17:10:31 +09:00
Adrianna Chang
6673d8ef3b Delegate migration methods to strategy object
The default strategy will continue to forward messages to the connection adapter,
but applications can configure a custom strategy class to use instead.
2022-06-23 09:14:02 -04:00
Paulo Barros
db82002f2b
Add adapter option disallowing foreign keys 2022-06-20 11:11:46 +02:00
HParker
3a04c7b339 Add configurable deprecation warning for singular associations
This removes the singularize from `where` which runs on all `expand_from_hash` keys which might be reflections or column names. This saves a lot of time by avoiding singularizing column names.

Previously in https://github.com/rails/rails/pull/45163 the singularize was removed entirely. after some reflection, I think it is better to at least give a warning for one release since `where` is a very popular API and the problems you can run into with incorrect relation could be hard to debug.

Configurable with `ActiveRecord::Base.allow_deprecated_singular_assocaitions_name = false` / `config.active_record.allow_deprecated_singular_assocaitions_name = false`
2022-06-16 09:14:12 -07:00
fatkodima
21a6dbd313 Enable strict strings mode for SQLite3Adapter
Co-authored-by: Jean Boussier <jean.boussier@gmail.com>
2022-06-14 23:59:17 +03:00
Cameron Bothner
936a862f3c
Run transactional callbacks on instances most likely to match DB state 2022-06-14 13:34:25 -04:00
Austen Madden
a237867e8f Reset cache_versions on relation
Currently, when #reset is called on a relation object it does not reset
the cache_versions ivar. This can lead to a confusing and buggy
situation where despite having the correct data the relation is still
reporting a stale cache_version. Resetting this ivar along with the
other relation state corrects this situation.

Update test assertion

Assert the specific cache_version matches the latest .all relation

Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>
2022-06-14 10:21:01 -04:00
Alex Robbin
1ceffeb4d9
adds support for exclusion constraints (PostgreSQL-only)
```ruby
add_exclusion_constraint :invoices, "daterange(start_date, end_date) WITH &&", using: :gist, name: "invoices_date_overlap"
remove_exclusion_constraint :invoices, name: "invoices_date_overlap"
```

See PostgreSQL's [`CREATE TABLE ... EXCLUDE ...`](https://www.postgresql.org/docs/12/sql-createtable.html#SQL-CREATETABLE-EXCLUDE) documentation for more on exclusion constraints.
2022-06-14 08:09:47 -04:00