Commit Graph

22188 Commits

Author SHA1 Message Date
sampatbadhe 9d3fd35082 Update ActiveRecord::QueryMethods#in_order_of example [ci-skip]
Replace ORDER BY FIELD example with ORDER BY CASE
ORDER BY FIELD was MYSQL specific, which was replaced with default ORDER BY CASE in https://github.com/rails/rails/pull/45670
2022-11-12 08:46:53 +05:30
Nikita Vasilevsky 53f4a4b511 Add `invalidate!` directly to `Transaction`
Every instance of `Transaction` including instances of `NullTransaction`
now respond to `invalidate!` which allows us to use transactions in
a "tell do not ask" way. This should simplify transaction invalidation
for cases were we needed check whether transaction is not a
`NullTransaction` to prevent calling `invalidate!` on `nil` state.
2022-11-11 16:52:10 +00:00
Rafael Mendonça França 9253d40347
Merge pull request #46468 from nvasilevski/adjust-current-transaction-check
Check for transaction  before invalidating the state
2022-11-10 18:35:15 -05:00
Jonathan Hefner e6ab2888c8
Merge pull request #46456 from jonathanhefner/follow-up-46330-and-46337
Add missing `assert_not_deprecated` deprecator args
2022-11-10 16:29:48 -06:00
Petrik de Heus fcf6edf7c6
Merge pull request #46449 from AlexB52/update-collection-proxy-documentation [ci-skip]
Update ActiveRecord::Associations::CollectionProxy#== documentation
2022-11-10 21:58:24 +01:00
Alex Barret a83451d9e2 Update ActiveRecord::Associations::CollectionProxy#== documentation
[ci skip]
The current documentation for #== shows an example that demonstrates an
incorrect behaviour. This change updates the example accordingly and adds a note
too.

Co-authored-by: Petrik de Heus <petrik@deheus.net>
2022-11-11 09:49:40 +13:00
Nikita Vasilevsky e4b4c50058 Check for transaction before invalidating the state 2022-11-10 20:02:23 +00:00
Sean Denny 313a379a01 Add a build persistence method
Provides a wrapper for `new`, to provide feature parity with `create`s
ability to create multiple records from an array of hashes, using the
same notation as the `build` method on associations.

- Associations can create multiple objects from an array of hashes
- Associations can build multiple objects from an array of hashes
- Classes can create multiple objects from an array of hashes
- Classes can build multiple objects from an array of hashes (<- You are here)
2022-11-10 11:28:21 -05:00
Adrianna Chang 6d55eb992b Use filename when printing pending migrations
Filename provides the path, whereas basename does not. It makes
sense to show the full filename, since migrations can be stored across
directories in multi-db apps.
2022-11-10 09:51:27 -05:00
fatkodima 25579fc727 Fix `index_exists?` to handle empty columns argument 2022-11-10 03:52:31 +02:00
Jonathan Hefner 8a7aba0c76 Add missing assert_not_deprecated deprecator args
Follow-up to #46330 and #46337.

This adds a deprecator arg to some `assert_not_deprecated` calls that
were missed in #46330 and #46337.
2022-11-09 12:07:29 -06:00
Nixon c68c360e2a Fix touching has_one grandparent associations 2022-11-05 22:09:40 -03:00
Jonathan Hefner 45eceb2744
Merge pull request #46418 from lazaronixon/missing-has-one-touch-doc
Add :touch to has_one options in documentation [ci-skip]
2022-11-05 10:32:51 -05:00
Nixon db0ed7e5b4 Add :touch to has_one options in documentation 2022-11-04 23:51:48 -03:00
eileencodes 845ba3013e
Fix schema_up_to_date connection
This method was failing to return the connection to the correct state
which would break tests when eager loading the application. What's
happening is that when we check `schema_up_to_date` when the application
is eager loaded all the models are eager loaded as well so it sees that
ApplicationRecord has a connection already but that connection is set to
the wrong one. It doesn't get replaced with the right one so we see
errors about looking for tables in the wrong database. This doesn't
happen when eager loading is off because ApplicationRecord isn't set and
gets initialized with the correct connection (from it's
`connects_to/establish_connection` call).

I also refactored this to pull `needs_update` into a method and make the
loop easier to read.
2022-11-04 15:54:06 -04:00
eileencodes f732c8f83d
Call pending_migrations once
This was accidentally calling pending_migrations twice, and while
probably not a big deal, there's no reason to.
2022-11-04 09:24:07 -04:00
Jean Boussier 029a603d66
Merge pull request #46406 from fatkodima/insert_all-sti
Fix `insert_all`/`upsert_all` to handle STI
2022-11-03 22:59:17 +01:00
Eileen M. Uchitelle cdd41707bf
Merge pull request #46399 from eileencodes/fix-pending-migrations-error-message
Fix pending migrations error message
2022-11-03 15:25:40 -04:00
Eileen M. Uchitelle 8069d7faa5
Merge pull request #46402 from Shopify/reload-method-should-respect-query-constraints
`ActiveRecord::Persistence#reload` respects `query_constraints` config
2022-11-03 14:42:53 -04:00
eileencodes e9d835f05a
Fix pending migrations error message
While this was checking each connection for pending migrations it was
also raising as soon as it hit any pending migration. Instead we want
this to output all current pending migrations in each directory.

This change loops through all the connections to collect pending
migrations and then passes them to the error message handling if
applicable. Instead of changing the public `check_pending!`, I stopped
calling that from `check_pending_migrations` and raise the error
directory from `check_pending_migrations`. I also updated
`check_pending` to pass pending migrations from the passed connection.

I think evenutally we'll want to deprecate `check_pending!`,
it's not very useful without the other connections but since
it's public I avoided changing it.
2022-11-03 13:21:59 -04:00
Petrik de Heus 07f48f781b
Merge pull request #46414 from dersam/link-rollback-doc [ci-skip]
Add link to Rollback in transactions doc.
2022-11-03 15:19:31 +01:00
Sam Schmidt 8a3dcf941d
Add link to Rollback in transactions doc.
A common misconception with transaction rollback in Rails is that
exceptions will not be re-raised from nested transactions. This is only
true for ActiveRecord::Rollback. The documentation for that class makes
that clear.

A link to that class doc has been added, as well as an explicit
restatement that all other exceptions will be re-raised.
2022-11-03 09:09:09 -04:00
Jean Boussier 336c720e2e
Merge pull request #46394 from dsusviela/add_frozen_to_dangerous_methods
Add frozen to dangerous methods
2022-11-03 00:02:48 +01:00
Jonathan Hefner 32957f3c56
Merge pull request #46407 from gregnavis/fix-attribute-methods-array-writer-docs
Update docs for ActiveRecord::AttributeMethods#[]= [ci-skip]
2022-11-02 10:17:55 -05:00
Greg Navis 7f58b74150 Update docs for ActiveRecord::AttributeMethods#[]=
The docs incorrectly claimed that write_attribute is protected despite
the method being public.
2022-11-02 14:44:04 +01:00
fatkodima 5b91169076 Fix `insert_all`/`upsert_all` to handle STI 2022-11-02 14:06:00 +02:00
Jean Boussier 7db044f385 Merge pull request #46243 from shouichi/clear-locking-column-on-dup
Clear locking column on #dup

Fix: #46243
2022-11-02 11:35:50 +01:00
Shouichi Kamiya 13d3fc9d32 Clear locking column on #dup
When duplicating records, we usually want to create a new record and
don't want to keep the original lock_version. Just like timestamp
columns are not copied.

Co-authored-by: Seonggi Yang <seonggi.yang@gmail.com>
Co-authored-by: Ryohei UEDA <ueda@anipos.co.jp>
2022-11-02 09:40:41 +09:00
Nikita Vasilevsky bf24af73ff Invalidate transaction as early as possible
This should allow framework to avoid issuing `ROLLBACK` statements
for cases when db has already rolled back the transaction
2022-11-02 00:05:44 +00:00
Nikita Vasilevsky 54cc42befd `ActiveRecord::Persistence#reload` respects `query_constraints` config 2022-11-01 23:10:56 +00:00
Daniel Susviela 7e5c5a2abe Add frozen? to the dangerous method list
Change the test to check for frozen?
2022-11-01 22:29:28 +01:00
Jean Boussier d2cb5b7469
Merge pull request #46386 from lazaronixon/fix-has-one-create-record
Improve singular association creation
2022-11-01 22:20:36 +01:00
Eileen M. Uchitelle 232a057dd1
Merge pull request #46331 from Shopify/introduce-AR-query-by-configuration
Allow specifying columns to use in ActiveRecord::Base object queries
2022-11-01 12:06:14 -04:00
Nikita Vasilevsky 415e6b6391 Allow specifying columns to use in `ActiveRecord::Base` object queries
Renames `_primary_key_constraints_hash` private method to avoid implying
that it strictly represents a constraint based on the model primary key

Allows columns list used in query constraints to be configurable using
`ActiveRecord::Base.query_constraints` macro
2022-11-01 15:52:34 +00:00
Jonathan Hefner 7eff9d6f68 Add assertion helpers to PendingMigrationsTest
This adds `assert_pending_migrations` and `assert_no_pending_migrations`
helpers to `PendingMigrationsTest`, to clarify what is being tested.
2022-10-31 16:44:54 -05:00
Jonathan Hefner 0344e3f270
Merge pull request #46380 from jonathanhefner/active_record-pending_migrations_test-isolate-db
Fix flakey tests due to leaking migration state
2022-10-30 16:00:05 -05:00
Nixon 9103b8580e Improve singular association creation
Now when you have a has_one association and create_association is 
called:

```
# begin transaction
DELETE old record
INSERT new record
# commit transaction
```
2022-10-30 04:43:25 -03:00
Jonathan Hefner 3e96dc7cdc Fix flakey tests due to leaking migration state
Follow-up to #46097.

Prior to #46097, `PendingMigrationsTest` created an in-memory database
to run migrations on.  This prevented those migrations from affecting
other tests.  #46097 switched to an on-disk database to test pending
migrations for databases other than the current
`ActiveRecord::Base.connection` database.  However, #46097 used the
fixture database that other tests also use, causing those other tests to
sometimes fail.  For example:

  ```
  $ cd activerecord
  $ ARCONN=sqlite3 bin/test --seed 56617 -n '/test_understands_migrations_created_out_of_order|test_migrate_using_scope_and_verbose_mode|test_migrate_using_empty_scope_and_verbose_mode/'

  ...

  Failure:
  ActiveRecord::DatabaseTasksMigrateScopeTest#test_migrate_using_empty_scope_and_verbose_mode [/rails/activerecord/test/cases/tasks/database_tasks_test.rb:1026]:
  Expected "" to include "migrating".

  ...

  Failure:
  ActiveRecord::DatabaseTasksMigrateScopeTest#test_migrate_using_scope_and_verbose_mode [/rails/activerecord/test/cases/tasks/database_tasks_test.rb:990]:
  Expected "No migrations ran. (using mysql scope)\n" to include "migrating".
  ```

This commit changes `PendingMigrationsTest` to use isolated on-disk
databases.

Closes #46374.

Co-authored-by: fatkodima <fatkodima123@gmail.com>
2022-10-29 15:26:16 -05:00
Jonathan Hefner 73bdebdcd5 Map through subtype in PostgreSQL::OID::Array
This changes `PostgreSQL::OID::Array#map` to invoke `subtype.map` for
each element of the array.  When `subtype` is a scalar, `subtype.map`
(as implemented by `ActiveModel::Type::Value#map`) will simply call the
block with the element.  When `subtype` is a non-scalar, such as a
`PostgreSQL::OID::Range`, `subtype.map` ensures that the nested scalar
values are properly mapped.

Partially reverts #45348, as an alternative fix.
Fixes #46371.
Closes #46372.

Co-authored-by: fatkodima <fatkodima123@gmail.com>
2022-10-28 21:32:08 -05:00
Rafael Mendonça França a1a91e2133
Only ask people to run migration in a environment in non-development environments
When running test, you might have pending migrations. Before Rails would
tell you to run migrations with RAILS_ENV=test.

This causes the problem that now the development environment still have
pending migrations.

The correct workflow is to run migrations in the development environment
and let Rails prepare the test database based on the schema or structure
file.

In the development environment we don't need to tell to use the
`RAILS_ENV` environment variable.
2022-10-27 20:39:39 +00:00
eileencodes 1bb2e55704
Fix missed connection handler call
In #45924 I updated the methods on connection handler to clear all
connections regardless of role. I missed this call because it doesn't
throw deprecation warnings in the Active Record tests.
2022-10-27 09:09:12 -04:00
Yasuo Honda 1562651e5f
Merge pull request #46339 from TAGraves/tg-validate-in-schema-rb
Adds validate to foreign keys and check constraints in schema.rb
2022-10-26 08:54:16 +09:00
Tommy Graves 26f316a855
Adds validate to foreign keys and check constraints in schema.rb 2022-10-25 19:34:39 -04:00
Eileen M. Uchitelle 577274d57d
Merge pull request #46336 from Shopify/multi-db-version-check-support
Add multi-database support for the `db:version` task
2022-10-25 16:08:27 -04:00
Jonathan Hefner 682353e3de Add ActiveRecord.deprecator
This commit adds `ActiveRecord.deprecator` and replaces all usages of
`ActiveSupport::Deprecation.warn` in `activerecord/lib` with
`ActiveRecord.deprecator`.

Additionally, this commit adds `ActiveRecord.deprecator` to
`Rails.application.deprecators` so that it can be configured using e.g.
`config.active_support.report_deprecations`.
2022-10-25 14:23:08 -05:00
Hormoz Kheradmand a48fdffccf
Add multi-db support for db:version tasks
The `rails db:version` task makes no effort today to display information for multi-database applications.

This brings up this simplistic tasks up to parity with the rest of the DB tasks that support multi-database applications.

This is accomplished by following the pattern of making the non-namespaced task (i.e. `db:version`) to output a multi-line string, iterating through all defined databases for the current environment in `database.yml`

It also adds a namespaced instance of the task for each database defined (e.g. `db:version:animals`).

**A hidden change, but critical (for the purposes that I'm working on), that is happening here** is that database configurations with `database_tasks: false` will be excluded from the logic in this task – due to the usage of the `ActiveRecord::Base.configurations.configs_for` method, which by default has `include_hidden: false`!
2022-10-25 12:23:02 -07:00
Eileen M. Uchitelle b4344c86ad
Merge pull request #46273 from adrianna-chang-shopify/ac-execute-takes-allow-retry
Allow adapter `#execute` methods  to take `allow_retry` option
2022-10-25 11:45:38 -04:00
Jean Boussier f4b422b658
Merge pull request #46326 from bdewater/sqlcommenter-value-serialization
Fix SQLCommenter value serialization
2022-10-25 12:44:10 +02:00
Ernesto Tagwerker 7f32ef70f5 Improve `test_destroy_linked_models` by adding two assertions
This change makes sure that a test scenario has reasonable assertions.
2022-10-24 20:52:18 -04:00
Adrianna Chang 8f780c58ea Allow adapter #execute methods to take allow_retry option
If a user is calling `#execute` directly with a SQL string, they can
also specify whether they'd like the query to be retried in the case
of a connection-related exception. By nature, this means that applications
looking to opt into retries across all queries can patch `#execute` to
call super with `allow_retry: true` instead of needing to reimplement the
method entirely.
2022-10-24 19:04:02 -04:00
Bart de Water 3d922a958e Fix Active Record SQLCommenter value encoding
The specification describes values must be URL encoded first, then any remaining single quote characters must be escaped: https://google.github.io/sqlcommenter/spec/#value-serialization
Fortunately ERB::Util.url_encode already encodes this character, unlike for example Javascript's encodeURIComponent, so this step is unneccessary.

Fix Active Record Query Logs SQLCommenter sorting

Serialized key-value pairs must be sorted by lexicographic order: https://google.github.io/sqlcommenter/spec/#sorting
2022-10-24 11:12:18 -04:00
Eugene Kenny c4194e560d
Merge pull request #45481 from gregolsen/migration_bulk_rollback4
Add support for bulk change_table revert
2022-10-24 09:41:39 +01:00
Jean Boussier 76c48fb9c3
Merge pull request #46310 from modulitos/fix-changelog
Clarify activerecord changelog for sqlcommenter
2022-10-23 12:10:02 +02:00
Akira Matsuda 5b0680437c
2022-10-23 17:54:09 +09:00
modulitos e333bda876 Clarify ActiveRecord changelog for SQLCommenter
Forgot to update the changelog when we changed the default values for
`config.active_record.query_log_tags_format` from `:legacy` to
`:sqlcommenter`.

This change is a followup to: https://github.com/rails/rails/issues/46179
2022-10-22 22:27:58 -07:00
Ben Sheldon e32c7043d7
Do not trigger `after_commit :destroy` callback again on destroy if record previously was destroyed 2022-10-21 11:20:58 -07:00
Jonathan Hefner 02e351e48c Fix ciphertext_for for yet-to-be-encrypted values
Prior to this commit, `ciphertext_for` returned the cleartext of values
that had not yet been encrypted, such as with an unpersisted record:

  ```ruby
  Post.encrypts :body

  post = Post.create!(body: "Hello")
  post.ciphertext_for(:body)
  # => "{\"p\":\"abc..."

  post.body = "World"
  post.ciphertext_for(:body)
  # => "World"
  ```

This commit fixes `ciphertext_for` to always return the ciphertext of
encrypted attributes:

  ```ruby
  Post.encrypts :body

  post = Post.create!(body: "Hello")
  post.ciphertext_for(:body)
  # => "{\"p\":\"abc..."

  post.body = "World"
  post.ciphertext_for(:body)
  # => "{\"p\":\"xyz..."
  ```
2022-10-20 12:31:45 -05:00
eileencodes 0a1f2ad4f1
Fix missed deprecations
In #46274 I deprecated delegating to the connection handler but missed
these two calls.
2022-10-20 11:19:18 -04:00
Eileen M. Uchitelle 146f909c3b
Merge pull request #46289 from codeminator/fix-change-table-compatibility
Skip validation options for `change_table` in old migrations
2022-10-20 08:19:52 -04:00
Ahmed Shahin 164d1477bf Skip validation options for `change_*` in old migrations
Co-authored-by: Matthew Draper <matthew@trebex.net>
2022-10-20 13:00:01 +02:00
Ryuta Kamizono 4136145819 Fixup CHANGELOGs [ci-skip] 2022-10-20 18:37:48 +09:00
Dooor 31c15b4791 Fix a bug where using groups and counts with long table names would return incorrect results.
Fixed a bug that caused the alias name of "group by" to be too long and the first half of the name would be the same in both cases if it was cut by max identifier length.

Fix #46285

Co-authored-by: Yusaku ONO <yono@users.noreply.github.com>
2022-10-20 16:15:45 +09:00
Jonathan Hefner fecd8becbe Add read_attribute_for_database
The `BeforeTypeCast` module defines `read_attribute_before_type_cast`,
`attributes_before_type_cast`, and `*_before_type_cast` attribute
methods.  It also defines `attributes_for_database` and `*_for_database`
attribute methods, but no corresponding `read_attribute_for_database`
method.

This commit adds the missing `read_attribute_for_database` method.
2022-10-19 23:22:38 -05:00
Jonathan Hefner 6676989957
Merge pull request #46281 from jonathanhefner/active_record-encrypt-column-defaults
Fix encryption of column default values
2022-10-19 23:17:03 -05:00
Jonathan Hefner c48a83020a Fix encryption of column default values
Prior to this commit, encrypted attributes that used column default
values appeared to be encrypted on create, but were not:

  ```ruby
  Book.encrypts :name

  book = Book.create!
  book.name
  # => "<untitled>"
  book.name_before_type_cast
  # => "{\"p\":\"abc..."
  book.reload.name_before_type_cast
  # => "<untitled>"
  ```

This commit ensures attributes with column default values are encrypted:

  ```ruby
  Book.encrypts :name

  book = Book.create!
  book.name
  # => "<untitled>"
  book.name_before_type_cast
  # => "{\"p\":\"abc..."
  book.reload.name_before_type_cast
  # => "{\"p\":\"abc..."
  ```

The existing "support encrypted attributes defined on columns with
default values" test in `encryptable_record_test.rb` shows the intended
behavior, but it was not failing without a `reload`.
2022-10-19 22:56:59 -05:00
Jonathan Hefner 9ba037471d Avoid encrypt + decrypt on attribute assignment
Prior to this commit, `EncryptedAttributeType` inherited its `cast`
method from `ActiveModel::Type::Helpers::Mutable`.  `Mutable#cast` is
implemented as a `serialize` + `deserialize`.  For
`EncryptedAttributeType`, this means `encrypt` + `decrypt`.  This
overhead can be avoided by simply delegating `cast` to the `cast_type`.

**Before**

  ```irb
  irb> Post.encrypts :body, encryptor: MyLoggingEncryptor.new

  irb> post = Post.create!(body: "Hello")
  ! encrypting "Hello"
  ! decrypted "Hello"
  ! encrypting "Hello"
    TRANSACTION (0.1ms)  begin transaction
    Post Create (1.0ms)  INSERT INTO "posts" ...
  ! encrypting "Hello"
    TRANSACTION (76.7ms)  commit transaction

  irb> post.update!(body: "World")
  ! decrypted "Hello"
  ! encrypting "World"
  ! decrypted "World"
  ! decrypted "Hello"
  ! encrypting "World"
    TRANSACTION (0.1ms)  begin transaction
    Post Update (0.6ms)  UPDATE "posts" ...
  ! encrypting "World"
    TRANSACTION (100.0ms)  commit transaction
  ```

**After**

  ```irb
  irb> Post.encrypts :body, encryptor: MyLoggingEncryptor.new

  irb> post = Post.create!(body: "Hello")
  ! encrypting "Hello"
    TRANSACTION (0.1ms)  begin transaction
    Post Create (0.8ms)  INSERT INTO "posts" ...
  ! encrypting "Hello"
    TRANSACTION (97.0ms)  commit transaction

  irb> post.update!(body: "World")
  ! decrypted "Hello"
  ! decrypted "Hello"
  ! encrypting "World"
    TRANSACTION (0.1ms)  begin transaction
    Post Update (0.9ms)  UPDATE "posts" ...
  ! encrypting "World"
    TRANSACTION (85.4ms)  commit transaction
  ```
2022-10-19 22:53:23 -05:00
eileencodes a93d8fe294
Deprecate delegation to connection handler from Base
In a multi-db world, delegating from `Base` to the handler doesn't make
much sense. Applications should know when they are dealing with a single
connection (Base.connection) or the handler which deals with multiple
connections. Delegating to the connection handler from Base breaks this
contract and makes behavior confusing. I think eventually a new object
will replace the handler altogether but for now I'd like to just
separate concerns to avoid confusion.
2022-10-19 10:20:37 -04:00
eileencodes ab483d58d7
Cleanup check pending
We don't need to pass the db configs to `check_pending_migrations`
because we always want to loop through all of them so the argument
doesn't gain us anything. It's also a private API so applications
shouldn't be consuming this method (and therefore makes it even less
necessary to allow configs to be passed in)
2022-10-18 14:00:22 -04:00
eileencodes e7db645821
Remove extra establish connection in pending migrations
`purge` always re-establishes the connection at the end so we _should_
always have a connection and shouldn't need to re-establish.
2022-10-18 12:55:23 -04:00
Jean Boussier d73d3cdda5
Merge pull request #46253 from sampatbadhe/extend_reselect_with_hashes
Allow ActiveRecord::QueryMethods#reselect to accept a hash
2022-10-17 23:10:04 +02:00
sampatbadhe 90e96e5122 Allow ActiveRecord::QueryMethods#reselect to accept a hash
Add ability to use hash with columns and aliases inside #reselect method similar to #select which introduced in https://github.com/rails/rails/pull/45612
2022-10-18 02:36:56 +05:30
Jonathan Hefner caf9413604
Merge pull request #46231 from jonathanhefner/active_model-memoize-value_for_database
Avoid unnecessary `serialize` calls after save
2022-10-16 17:13:21 -05:00
Jonathan Hefner 28ebf3c81c Avoid double cast in types that only override cast
Follow-up to #44625.

In #44625, the `SerializeCastValue` module was added to allow types to
avoid a redundant call to `cast` when serializing a value for the
database.  Because it introduced a new method (`serialize_cast_value`)
that was not part of the `ActiveModel::Type::Value` contract, it was
designed to be opt-in.  Furthermore, to guard against incompatible
`serialize` and `serialize_cast_value` implementations in types that
override `serialize` but (unintentionally) inherit `serialize_cast_value`,
types were required to explicitly include the `SerializeCastValue`
module to activate the optimization.  i.e. It was not sufficient just to
have `SerializeCastValue` in the ancestor chain.

The `SerializeCastValue` module is not part of the public API, and there
are no plans to change that, which meant user-created custom types could
not benefit from this optimization.

This commit changes the opt-in condition such that it is sufficient for
the owner of the `serialize_cast_value` method to be the same or below
the owner of the `serialize` method in the ancestor chain.  This means
a user-created type that only overrides `cast`, **not** `serialize`,
will now benefit from the optimization.  For example, a type like:

  ```ruby
  class DowncasedString < ActiveModel::Type::String
    def cast(value)
      super&.downcase
    end
  end
  ```

As demonstrated in the benchmark below, this commit does not change the
current performance of the built-in Active Model types.  However, for a
simple custom type like `DowncasedString`, the performance of
`value_for_database` is twice as fast.  For types with more expensive
`cast` operations, the improvement may be greater.

**Benchmark**

  ```ruby
  # frozen_string_literal: true

  require "benchmark/ips"
  require "active_model"

  class DowncasedString < ActiveModel::Type::String
    def cast(value)
      super&.downcase
    end
  end

  ActiveModel::Type.register(:downcased_string, DowncasedString)

  VALUES = {
    my_big_integer: "123456",
    my_boolean: "true",
    my_date: "1999-12-31",
    my_datetime: "1999-12-31 12:34:56 UTC",
    my_decimal: "123.456",
    my_float: "123.456",
    my_immutable_string: "abcdef",
    my_integer: "123456",
    my_string: "abcdef",
    my_time: "1999-12-31T12:34:56.789-10:00",
    my_downcased_string: "AbcDef",
  }

  TYPES = VALUES.to_h { |name, value| [name, name.to_s.delete_prefix("my_").to_sym] }

  class MyModel
    include ActiveModel::API
    include ActiveModel::Attributes

    TYPES.each do |name, type|
      attribute name, type
    end
  end

  attribute_set = MyModel.new(VALUES).instance_variable_get(:@attributes)

  TYPES.each do |name, type|
    attribute = attribute_set[name.to_s]

    Benchmark.ips do |x|
      x.report(type.to_s) { attribute.value_for_database }
    end
  end
  ```

**Before**

  ```
          big_integer      2.986M (± 1.2%) i/s -     15.161M in   5.078972s
              boolean      2.980M (± 1.1%) i/s -     15.074M in   5.059456s
                 date      2.960M (± 1.1%) i/s -     14.831M in   5.011355s
             datetime      1.368M (± 0.9%) i/s -      6.964M in   5.092074s
              decimal      2.930M (± 1.2%) i/s -     14.911M in   5.089048s
                float      2.932M (± 1.3%) i/s -     14.713M in   5.018512s
     immutable_string      3.013M (± 1.3%) i/s -     15.239M in   5.058085s
              integer      1.603M (± 0.8%) i/s -      8.096M in   5.052046s
               string      2.977M (± 1.1%) i/s -     15.168M in   5.094874s
                 time      1.338M (± 0.9%) i/s -      6.699M in   5.006046s
     downcased_string      1.394M (± 0.9%) i/s -      7.034M in   5.046972s
  ```

**After**

  ```
          big_integer      3.016M (± 1.0%) i/s -     15.238M in   5.053005s
              boolean      2.965M (± 1.3%) i/s -     15.037M in   5.071921s
                 date      2.924M (± 1.0%) i/s -     14.754M in   5.046294s
             datetime      1.435M (± 0.9%) i/s -      7.295M in   5.082498s
              decimal      2.950M (± 0.9%) i/s -     14.800M in   5.017225s
                float      2.964M (± 0.9%) i/s -     14.987M in   5.056405s
     immutable_string      2.907M (± 1.4%) i/s -     14.677M in   5.049194s
              integer      1.638M (± 0.9%) i/s -      8.227M in   5.022401s
               string      2.971M (± 1.0%) i/s -     14.891M in   5.011709s
                 time      1.454M (± 0.9%) i/s -      7.384M in   5.079993s
     downcased_string      2.939M (± 0.9%) i/s -     14.872M in   5.061100s
  ```
2022-10-16 16:06:16 -05:00
Jonathan Hefner 5e62c194e5 Avoid unnecessary serialize calls after save
Saving a record calls `ActiveModel::Attribute#value_for_database` on
each of its attributes.  `value_for_database`, in turn, calls
`serialize`.  After a record is successfully saved, its attributes are
reset via `ActiveModel::Attribute#forgetting_assignment`, which also
calls `value_for_database`.  This means attributes are unnecessarily
re-serialized right after they are saved.

This commit memoizes `value_for_database` so that `serialize` is not
called a 2nd time after save.  Because `value` is the single source of
truth and can change in place, the memoization carefully checks for
when `value` differs from the memoized `@value_for_database`.

This yields a small performance increase when saving, and a larger
performance increase when repeatedly reading `value_for_database` for
most types.

**Benchmark script**

  ```ruby
  # frozen_string_literal: true
  require "benchmark/ips"

  ActiveModel::Attribute.subclasses.each { |subclass| subclass.send(:public, :_value_for_database) }

  VALUES = {
    my_big_integer: "123456",
    my_boolean: "true",
    my_date: "1999-12-31",
    my_datetime: "1999-12-31 12:34:56 UTC",
    my_decimal: "123.456",
    my_float: "123.456",
    my_immutable_string: "abcdef",
    my_integer: "123456",
    my_string: "abcdef",
    my_time: "1999-12-31T12:34:56.789-10:00",
  }

  TYPES = VALUES.to_h { |name, value| [name, name.to_s.delete_prefix("my_").to_sym] }

  class MyModel
    include ActiveModel::API
    include ActiveModel::Attributes

    TYPES.each do |name, type|
      attribute name, type
    end
  end

  attribute_set = MyModel.new(VALUES).instance_variable_get(:@attributes)

  def class_name(object) = object.class.name.demodulize

  def mimic_save(attribute)
    puts "=== #{__method__} / #{class_name attribute.type} #{class_name attribute} ".ljust(70, "=")

    Benchmark.ips do |x|
      x.report("before") do
        fresh_copy = attribute.dup
        fresh_copy._value_for_database
        fresh_copy.forgetting_assignment
      end

      x.report("after") do
        fresh_copy = attribute.dup
        fresh_copy.value_for_database
        fresh_copy.forgetting_assignment
      end

      x.compare!
    end
  end

  VALUES.each_key do |name|
    mimic_save(attribute_set[name.to_s].forgetting_assignment)
    mimic_save(attribute_set[name.to_s])
  end

  def get_value_for_database(attribute)
    puts "=== #{__method__} / #{class_name attribute.type} #{class_name attribute} ".ljust(70, "=")

    Benchmark.ips do |x|
      x.report("before") { attribute._value_for_database }
      x.report("after") { attribute.value_for_database }
      x.compare!
    end
  end

  VALUES.each_key do |name|
    get_value_for_database(attribute_set[name.to_s].forgetting_assignment)
    get_value_for_database(attribute_set[name.to_s])
  end
  ```

**`mimic_save` Results**

  ```
  === mimic_save / BigInteger FromDatabase =============================
  Warming up --------------------------------------
                before    24.460k i/100ms
                 after    28.474k i/100ms
  Calculating -------------------------------------
                before    243.390k (± 1.0%) i/s -      1.223M in   5.025334s
                 after    284.497k (± 0.8%) i/s -      1.424M in   5.004566s

  Comparison:
                 after:   284497.1 i/s
                before:   243389.7 i/s - 1.17x  (± 0.00) slower

  === mimic_save / BigInteger FromUser =================================
  Warming up --------------------------------------
                before    58.151k i/100ms
                 after    64.633k i/100ms
  Calculating -------------------------------------
                before    581.268k (± 1.2%) i/s -      2.908M in   5.002814s
                 after    645.165k (± 1.2%) i/s -      3.232M in   5.009752s

  Comparison:
                 after:   645164.8 i/s
                before:   581267.9 i/s - 1.11x  (± 0.00) slower

  === mimic_save / Boolean FromDatabase ================================
  Warming up --------------------------------------
                before    36.771k i/100ms
                 after    38.218k i/100ms
  Calculating -------------------------------------
                before    371.521k (± 1.1%) i/s -      1.875M in   5.048310s
                 after    384.021k (± 0.9%) i/s -      1.949M in   5.075966s

  Comparison:
                 after:   384021.4 i/s
                before:   371520.8 i/s - 1.03x  (± 0.00) slower

  === mimic_save / Boolean FromUser ====================================
  Warming up --------------------------------------
                before    58.738k i/100ms
                 after    63.935k i/100ms
  Calculating -------------------------------------
                before    582.358k (± 0.9%) i/s -      2.937M in   5.043559s
                 after    633.391k (± 0.9%) i/s -      3.197M in   5.047443s

  Comparison:
                 after:   633390.5 i/s
                before:   582358.2 i/s - 1.09x  (± 0.00) slower

  === mimic_save / Date FromDatabase ===================================
  Warming up --------------------------------------
                before    28.242k i/100ms
                 after    31.247k i/100ms
  Calculating -------------------------------------
                before    282.438k (± 1.0%) i/s -      1.412M in   5.000177s
                 after    311.108k (± 1.0%) i/s -      1.562M in   5.022362s

  Comparison:
                 after:   311108.4 i/s
                before:   282437.9 i/s - 1.10x  (± 0.00) slower

  === mimic_save / Date FromUser =======================================
  Warming up --------------------------------------
                before    43.427k i/100ms
                 after    47.354k i/100ms
  Calculating -------------------------------------
                before    431.978k (± 1.3%) i/s -      2.171M in   5.027373s
                 after    470.658k (± 1.3%) i/s -      2.368M in   5.031540s

  Comparison:
                 after:   470658.0 i/s
                before:   431978.2 i/s - 1.09x  (± 0.00) slower

  === mimic_save / DateTime FromDatabase ===============================
  Warming up --------------------------------------
                before    20.997k i/100ms
                 after    24.962k i/100ms
  Calculating -------------------------------------
                before    210.672k (± 0.9%) i/s -      1.071M in   5.083391s
                 after    248.114k (± 0.8%) i/s -      1.248M in   5.030687s

  Comparison:
                 after:   248114.5 i/s
                before:   210671.9 i/s - 1.18x  (± 0.00) slower

  === mimic_save / DateTime FromUser ===================================
  Warming up --------------------------------------
                before    30.406k i/100ms
                 after    45.886k i/100ms
  Calculating -------------------------------------
                before    304.374k (± 0.9%) i/s -      1.551M in   5.095184s
                 after    456.754k (± 1.3%) i/s -      2.294M in   5.023891s

  Comparison:
                 after:   456753.8 i/s
                before:   304374.0 i/s - 1.50x  (± 0.00) slower

  === mimic_save / Decimal FromDatabase ================================
  Warming up --------------------------------------
                before    11.381k i/100ms
                 after    13.632k i/100ms
  Calculating -------------------------------------
                before    112.355k (± 1.4%) i/s -    569.050k in   5.065752s
                 after    135.940k (± 1.5%) i/s -    681.600k in   5.015094s

  Comparison:
                 after:   135939.8 i/s
                before:   112355.1 i/s - 1.21x  (± 0.00) slower

  === mimic_save / Decimal FromUser ====================================
  Warming up --------------------------------------
                before    59.270k i/100ms
                 after    64.668k i/100ms
  Calculating -------------------------------------
                before    595.050k (± 1.3%) i/s -      3.023M in   5.080703s
                 after    644.206k (± 0.9%) i/s -      3.233M in   5.019581s

  Comparison:
                 after:   644205.8 i/s
                before:   595049.7 i/s - 1.08x  (± 0.00) slower

  === mimic_save / Float FromDatabase ==================================
  Warming up --------------------------------------
                before    35.564k i/100ms
                 after    35.632k i/100ms
  Calculating -------------------------------------
                before    355.836k (± 1.5%) i/s -      1.814M in   5.098367s
                 after    361.603k (± 1.1%) i/s -      1.817M in   5.026122s

  Comparison:
                 after:   361603.1 i/s
                before:   355835.7 i/s - same-ish: difference falls within error

  === mimic_save / Float FromUser ======================================
  Warming up --------------------------------------
                before    57.544k i/100ms
                 after    63.450k i/100ms
  Calculating -------------------------------------
                before    572.265k (± 1.1%) i/s -      2.877M in   5.028412s
                 after    631.023k (± 1.1%) i/s -      3.172M in   5.028143s

  Comparison:
                 after:   631022.8 i/s
                before:   572264.9 i/s - 1.10x  (± 0.00) slower

  === mimic_save / ImmutableString FromDatabase ========================
  Warming up --------------------------------------
                before    27.239k i/100ms
                 after    29.235k i/100ms
  Calculating -------------------------------------
                before    272.882k (± 1.1%) i/s -      1.389M in   5.091389s
                 after    292.142k (± 1.1%) i/s -      1.462M in   5.004132s

  Comparison:
                 after:   292142.0 i/s
                before:   272882.0 i/s - 1.07x  (± 0.00) slower

  === mimic_save / ImmutableString FromUser ============================
  Warming up --------------------------------------
                before    44.308k i/100ms
                 after    48.680k i/100ms
  Calculating -------------------------------------
                before    438.869k (± 1.2%) i/s -      2.215M in   5.048665s
                 after    482.455k (± 1.1%) i/s -      2.434M in   5.045670s

  Comparison:
                 after:   482454.7 i/s
                before:   438868.9 i/s - 1.10x  (± 0.00) slower

  === mimic_save / Integer FromDatabase ================================
  Warming up --------------------------------------
                before    25.554k i/100ms
                 after    29.236k i/100ms
  Calculating -------------------------------------
                before    254.308k (± 1.1%) i/s -      1.278M in   5.024863s
                 after    292.265k (± 1.1%) i/s -      1.462M in   5.002250s

  Comparison:
                 after:   292265.3 i/s
                before:   254308.2 i/s - 1.15x  (± 0.00) slower

  === mimic_save / Integer FromUser ====================================
  Warming up --------------------------------------
                before    46.034k i/100ms
                 after    64.028k i/100ms
  Calculating -------------------------------------
                before    458.343k (± 1.2%) i/s -      2.302M in   5.022546s
                 after    636.237k (± 1.1%) i/s -      3.201M in   5.032346s

  Comparison:
                 after:   636237.2 i/s
                before:   458343.4 i/s - 1.39x  (± 0.00) slower

  === mimic_save / String FromDatabase =================================
  Warming up --------------------------------------
                before    25.804k i/100ms
                 after    26.682k i/100ms
  Calculating -------------------------------------
                before    259.941k (± 1.2%) i/s -      1.316M in   5.063398s
                 after    268.140k (± 1.0%) i/s -      1.361M in   5.075435s

  Comparison:
                 after:   268140.2 i/s
                before:   259941.3 i/s - 1.03x  (± 0.00) slower

  === mimic_save / String FromUser =====================================
  Warming up --------------------------------------
                before    40.607k i/100ms
                 after    42.735k i/100ms
  Calculating -------------------------------------
                before    407.731k (± 1.2%) i/s -      2.071M in   5.079973s
                 after    424.659k (± 1.1%) i/s -      2.137M in   5.032247s

  Comparison:
                 after:   424659.2 i/s
                before:   407731.2 i/s - 1.04x  (± 0.00) slower

  === mimic_save / Time FromDatabase ===================================
  Warming up --------------------------------------
                before    21.555k i/100ms
                 after    25.151k i/100ms
  Calculating -------------------------------------
                before    213.479k (± 1.0%) i/s -      1.078M in   5.049047s
                 after    249.833k (± 1.2%) i/s -      1.258M in   5.034246s

  Comparison:
                 after:   249833.1 i/s
                before:   213479.1 i/s - 1.17x  (± 0.00) slower

  === mimic_save / Time FromUser =======================================
  Warming up --------------------------------------
                before    30.226k i/100ms
                 after    45.704k i/100ms
  Calculating -------------------------------------
                before    303.729k (± 1.2%) i/s -      1.542M in   5.076124s
                 after    457.186k (± 0.9%) i/s -      2.331M in   5.098810s

  Comparison:
                 after:   457186.0 i/s
                before:   303729.0 i/s - 1.51x  (± 0.00) slower
  ```

**`get_value_for_database` Results**

  ```
  === get_value_for_database / BigInteger FromDatabase =================
  Warming up --------------------------------------
                before   101.504k i/100ms
                 after   328.924k i/100ms
  Calculating -------------------------------------
                before      1.007M (± 0.7%) i/s -      5.075M in   5.040604s
                 after      3.303M (± 0.6%) i/s -     16.775M in   5.079630s

  Comparison:
                 after:  3302566.7 i/s
                before:  1006908.5 i/s - 3.28x  (± 0.00) slower

  === get_value_for_database / BigInteger FromUser =====================
  Warming up --------------------------------------
                before   282.580k i/100ms
                 after   325.867k i/100ms
  Calculating -------------------------------------
                before      2.840M (± 0.6%) i/s -     14.412M in   5.074481s
                 after      3.329M (± 0.6%) i/s -     16.945M in   5.090498s

  Comparison:
                 after:  3328905.3 i/s
                before:  2840125.6 i/s - 1.17x  (± 0.00) slower

  === get_value_for_database / Boolean FromDatabase ====================
  Warming up --------------------------------------
                before   197.974k i/100ms
                 after   327.017k i/100ms
  Calculating -------------------------------------
                before      1.984M (± 0.8%) i/s -     10.097M in   5.088429s
                 after      3.269M (± 0.7%) i/s -     16.351M in   5.001320s

  Comparison:
                 after:  3269485.0 i/s
                before:  1984376.2 i/s - 1.65x  (± 0.00) slower

  === get_value_for_database / Boolean FromUser ========================
  Warming up --------------------------------------
                before   286.138k i/100ms
                 after   340.681k i/100ms
  Calculating -------------------------------------
                before      2.900M (± 0.7%) i/s -     14.593M in   5.031863s
                 after      3.387M (± 0.6%) i/s -     17.034M in   5.028800s

  Comparison:
                 after:  3387438.6 i/s
                before:  2900285.2 i/s - 1.17x  (± 0.00) slower

  === get_value_for_database / Date FromDatabase =======================
  Warming up --------------------------------------
                before   133.983k i/100ms
                 after   327.549k i/100ms
  Calculating -------------------------------------
                before      1.344M (± 0.7%) i/s -      6.833M in   5.085972s
                 after      3.272M (± 0.7%) i/s -     16.377M in   5.005522s

  Comparison:
                 after:  3272057.0 i/s
                before:  1343591.3 i/s - 2.44x  (± 0.00) slower

  === get_value_for_database / Date FromUser ===========================
  Warming up --------------------------------------
                before   291.156k i/100ms
                 after   336.507k i/100ms
  Calculating -------------------------------------
                before      2.917M (± 1.0%) i/s -     14.849M in   5.090985s
                 after      3.383M (± 0.9%) i/s -     17.162M in   5.073857s

  Comparison:
                 after:  3382717.0 i/s
                before:  2917023.0 i/s - 1.16x  (± 0.00) slower

  === get_value_for_database / DateTime FromDatabase ===================
  Warming up --------------------------------------
                before    75.632k i/100ms
                 after   334.488k i/100ms
  Calculating -------------------------------------
                before    759.512k (± 0.8%) i/s -      3.857M in   5.078867s
                 after      3.363M (± 0.9%) i/s -     17.059M in   5.072516s

  Comparison:
                 after:  3363268.0 i/s
                before:   759512.4 i/s - 4.43x  (± 0.00) slower

  === get_value_for_database / DateTime FromUser =======================
  Warming up --------------------------------------
                before   133.780k i/100ms
                 after   330.351k i/100ms
  Calculating -------------------------------------
                before      1.346M (± 0.8%) i/s -      6.823M in   5.068844s
                 after      3.303M (± 0.9%) i/s -     16.518M in   5.001328s

  Comparison:
                 after:  3302885.8 i/s
                before:  1346115.9 i/s - 2.45x  (± 0.00) slower

  === get_value_for_database / Decimal FromDatabase ====================
  Warming up --------------------------------------
                before    43.500k i/100ms
                 after   329.669k i/100ms
  Calculating -------------------------------------
                before    437.058k (± 1.7%) i/s -      2.218M in   5.077481s
                 after      3.290M (± 0.9%) i/s -     16.483M in   5.010687s

  Comparison:
                 after:  3289905.0 i/s
                before:   437058.2 i/s - 7.53x  (± 0.00) slower

  === get_value_for_database / Decimal FromUser ========================
  Warming up --------------------------------------
                before   288.315k i/100ms
                 after   330.565k i/100ms
  Calculating -------------------------------------
                before      2.886M (± 0.7%) i/s -     14.704M in   5.095872s
                 after      3.309M (± 0.8%) i/s -     16.859M in   5.094675s

  Comparison:
                 after:  3309344.5 i/s
                before:  2885624.4 i/s - 1.15x  (± 0.00) slower

  === get_value_for_database / Float FromDatabase ======================
  Warming up --------------------------------------
                before   187.267k i/100ms
                 after   337.589k i/100ms
  Calculating -------------------------------------
                before      1.888M (± 0.9%) i/s -      9.551M in   5.057695s
                 after      3.350M (± 0.9%) i/s -     16.879M in   5.039205s

  Comparison:
                 after:  3349910.7 i/s
                before:  1888499.4 i/s - 1.77x  (± 0.00) slower

  === get_value_for_database / Float FromUser ==========================
  Warming up --------------------------------------
                before   280.405k i/100ms
                 after   338.447k i/100ms
  Calculating -------------------------------------
                before      2.822M (± 1.0%) i/s -     14.301M in   5.068052s
                 after      3.392M (± 0.8%) i/s -     17.261M in   5.089235s

  Comparison:
                 after:  3391855.1 i/s
                before:  2822015.9 i/s - 1.20x  (± 0.00) slower

  === get_value_for_database / ImmutableString FromDatabase ============
  Warming up --------------------------------------
                before   142.061k i/100ms
                 after   340.814k i/100ms
  Calculating -------------------------------------
                before      1.429M (± 0.9%) i/s -      7.245M in   5.071044s
                 after      3.369M (± 0.8%) i/s -     17.041M in   5.058261s

  Comparison:
                 after:  3369088.0 i/s
                before:  1428830.1 i/s - 2.36x  (± 0.00) slower

  === get_value_for_database / ImmutableString FromUser ================
  Warming up --------------------------------------
                before   285.588k i/100ms
                 after   338.146k i/100ms
  Calculating -------------------------------------
                before      2.890M (± 0.9%) i/s -     14.565M in   5.041037s
                 after      3.369M (± 0.9%) i/s -     16.907M in   5.018268s

  Comparison:
                 after:  3369429.6 i/s
                before:  2889532.4 i/s - 1.17x  (± 0.00) slower

  === get_value_for_database / Integer FromDatabase ====================
  Warming up --------------------------------------
                before   106.915k i/100ms
                 after   334.301k i/100ms
  Calculating -------------------------------------
                before      1.070M (± 1.0%) i/s -      5.453M in   5.098578s
                 after      3.373M (± 0.8%) i/s -     17.049M in   5.054221s

  Comparison:
                 after:  3373498.9 i/s
                before:  1069550.2 i/s - 3.15x  (± 0.00) slower

  === get_value_for_database / Integer FromUser ========================
  Warming up --------------------------------------
                before   175.719k i/100ms
                 after   339.506k i/100ms
  Calculating -------------------------------------
                before      1.760M (± 1.0%) i/s -      8.962M in   5.093648s
                 after      3.368M (± 0.8%) i/s -     16.975M in   5.040973s

  Comparison:
                 after:  3367705.1 i/s
                before:  1759569.2 i/s - 1.91x  (± 0.00) slower

  === get_value_for_database / String FromDatabase =====================
  Warming up --------------------------------------
                before   143.817k i/100ms
                 after   287.976k i/100ms
  Calculating -------------------------------------
                before      1.433M (± 0.9%) i/s -      7.191M in   5.017545s
                 after      2.898M (± 0.9%) i/s -     14.687M in   5.067782s

  Comparison:
                 after:  2898309.6 i/s
                before:  1433247.4 i/s - 2.02x  (± 0.00) slower

  === get_value_for_database / String FromUser =========================
  Warming up --------------------------------------
                before   288.320k i/100ms
                 after   287.449k i/100ms
  Calculating -------------------------------------
                before      2.891M (± 0.7%) i/s -     14.704M in   5.085633s
                 after      2.899M (± 0.6%) i/s -     14.660M in   5.057520s

  Comparison:
                 after:  2898730.4 i/s
                before:  2891484.2 i/s - same-ish: difference falls within error

  === get_value_for_database / Time FromDatabase =======================
  Warming up --------------------------------------
                before    72.976k i/100ms
                 after   335.541k i/100ms
  Calculating -------------------------------------
                before    741.313k (± 0.7%) i/s -      3.722M in   5.020770s
                 after      3.368M (± 0.6%) i/s -     17.113M in   5.080733s

  Comparison:
                 after:  3368266.8 i/s
                before:   741313.5 i/s - 4.54x  (± 0.00) slower

  === get_value_for_database / Time FromUser ===========================
  Warming up --------------------------------------
                before   137.338k i/100ms
                 after   336.559k i/100ms
  Calculating -------------------------------------
                before      1.382M (± 0.6%) i/s -      7.004M in   5.069264s
                 after      3.393M (± 0.5%) i/s -     17.165M in   5.059474s

  Comparison:
                 after:  3392622.1 i/s
                before:  1381763.3 i/s - 2.46x  (± 0.00) slower
  ```

Co-authored-by: Jorge Manrubia <jorge@hey.com>
2022-10-16 16:05:47 -05:00
eileencodes d54f0c52fa
Minor cleanup from #46097
1) We don't need this comment
2) The begin/end was redudant and not necessary
2022-10-14 15:53:09 -04:00
Eileen M. Uchitelle a744d477cb
Merge pull request #46097 from Shopify/fix-multi-db-check-pending
Multi-database: better support pending migration checks
2022-10-14 15:51:28 -04:00
Hormoz Kheradmand 6793482df9
Fix pending migration checks for multi-db
Co-authored-by: Guo Xiang Tan <gxtan1990@gmail.com>

Fixes https://github.com/rails/rails/issues/37524
2022-10-14 12:15:29 -07:00
Ron Shinall 62fd2a94e2 Correct errors in docs
There are two errors on line 305: "field:" should be "field_one:" and "other_field:" should be "field_two:"
2022-10-14 11:45:30 -04:00
Inokentii Mykhailov 7cad3c7728 Add support for bulk change_table revert
Co-authored-by: Eugene Kenny <elkenny@gmail.com>
2022-10-14 11:40:14 +00:00
Yasuo Honda 69cd9c2e8f
Merge pull request #46241 from skipkayhil/fix-migration-compat-inheritance
Fix some Migration versions not inheriting options
2022-10-14 16:03:13 +09:00
Hartley McGuire 16f8bd7944
Fix some Migration versions not inheriting options
Previously, most version of the Migration class would call `super` in
compatible_table_definition to ensure that they append all of the later
Migration verion's TableDefinition modules. However, the latest
Migration class did not do this and prepended its own TableDefinition
because there was no super method for it to call.

This led to an issue where Action Text migrations started failing on
Rails main, after migration option validation was added in e6da3eb. The
new functionality was not supposed to affect V6_0 Migrations, but it was
because both V6_1 and V7_0 were not calling super.

This commit fixes the issue by correctly calling super in both classes.
It also adds a compatible_table_definition method to Current that
returns the value passed in so that all of the versioned Migrations can
always return super.
2022-10-13 17:40:10 -04:00
John Hume d278b1bb07 Remove Calculations#count doc example with 0
as a Model.group(columns).count won't return entries for combinations
that don't occur.
2022-10-13 10:06:42 -05:00
eileencodes 07d8b99ebe
Remove extra advisory lock connection
In #38235 I moved advisory locks to their own named connections, then
in #39758 the advisory lock was left on Base.connection but then moved
it it's own connection handler. I believe with #45450 that this change
was made obsolete and can be returned to the prior behavior without
having to open an additional connection. The tests added pass and I also
tested this in my local demo to ensure that this is working correctly.

When I originally changed the behavior here Matthew noted that this
could be surprising for some setups that expect only one connection for
a running migration. I thought there was an issue related to this but I
can't find it.
2022-10-12 15:46:28 -04:00
eileencodes 69296dd716
Fix spelling error
Codespell is failing on main due to this misspelled word so fixing it,
should be `suppress` not `supress`.
2022-10-11 11:42:27 -04:00
Jean Boussier be52c380aa Better fix for the redefinition warning in Active Record Rakefile 2022-10-11 14:49:54 +02:00
Jean Boussier 9712d138ff Fix a warning in Active Record's Rakefile 2022-10-11 14:40:56 +02:00
Jean Boussier 9d8712a8b9
Merge pull request #46189 from Shopify/strict-warnings-in-test
Enable verbose mode in test and report warnings as errors
2022-10-11 10:03:26 +02:00
Alan Guo Xiang Tan e6da3ebd6c
Validate options when managing columns and tables in migration
This commit adds a step to validate the options that are used when managing columns and tables in migrations.
The intention is to only validate options for new migrations that are added. Invalid options used in old migrations are silently ignored like they always have been.

Fixes #33284
Fixes #39230

Co-authored-by: George Wambold <georgewambold@gmail.com>
2022-10-11 15:31:22 +08:00
Jean Boussier d917896f45 Enable verbose mode in test and report warnings as errors
We recently let a few very easy to avoid warnings get merged.
The root cause is that locally the test suite doesn't run in
verbose mode unless you explictly pass `-w`.

On CI warnings are enabled, but there is no reason to look at the
build output unless something is failing. And even if one wanted
to do that, that would be particularly work intensive since warnings
may be specific to a Ruby version etc.

Because of this I believe we should:

  - Always run the test suite with warnings enabled.
  - Raise an error if a warning is unexpected.

We've been using this pattern for a long time at Shopify both in private
and public repositories.
2022-10-11 09:25:18 +02:00
Shouichi Kamiya cdbbb969ad Improve ActiveRecord::QueryMethods.readonly doc [skip ci]
> Sets readonly attributes for the returned relation.

The current documentation gives the impression that it can manipulate
`attr_readonly`.

Co-authored-by: Petrik de Heus <petrik@deheus.net>
2022-10-07 17:29:57 +09:00
eileencodes acf30afc91
Fix `index_exists?` when column is an array
When `column` is passed as an array and the column does not exist,
then `index_exists?` check would fail to return early which would cause
`index_name_for_remove` to raise an error instead of ignoring the
missing column. This change ensures that `defined_for` checks the
column options in addition to the column argument.

Fixes #46196
2022-10-06 11:34:46 -04:00
Jonathan Hefner 007b040997 Align `case` / `when` indentation for Rubocop
This fixes the following Rubocop offenses:

  ```
  activerecord/lib/active_record/query_logs.rb:96:9: C: [Correctable] Layout/CaseIndentation: Indent when as deep as case.
          when :legacy
          ^^^^
  activerecord/lib/active_record/query_logs.rb:98:9: C: [Correctable] Layout/CaseIndentation: Indent when as deep as case.
          when :sqlcommenter
          ^^^^
  ```
2022-10-05 14:33:30 -05:00
Rafael Mendonça França b48abc0019
Remove complexity from the custom query logs
We only have two specific implementations, so we don't need to
create generic formatter implementations for this feature.
2022-10-05 18:33:17 +00:00
Rafael Mendonça França cc8658ad1f
Remove uncessary Factory object 2022-10-05 18:23:15 +00:00
Rafael Mendonça França 33e1efd4be
No need to call `present?` for this config 2022-10-05 18:06:38 +00:00
Yasuo Honda b8ffc02f97 Address `warning: mismatched indentations` at test_factory_invalid_formatter
This commit addresses the `warning: mismatched indentations` below.

```ruby
$ bundle exec ruby -w -Itest test/cases/query_logs_formatter_test.rb -n test_factory_invalid_formatter
test/cases/query_logs_formatter_test.rb:10: warning: mismatched indentations at 'end' with 'def' at 6
Using sqlite3
Run options: -n test_factory_invalid_formatter --seed 48904

.

Finished in 0.008743s, 114.3776 runs/s, 114.3776 assertions/s.
1 runs, 1 assertions, 0 failures, 0 errors, 0 skips
$
```

Follow-up #45081
2022-10-04 17:03:41 +09:00
Jean Boussier 6d0bafaa66
Merge pull request #46186 from Shopify/remove-transaction-joinable-from-doc
Remove private `transaction(joinable:)` parameter from RDoc
2022-10-04 09:56:50 +02:00
Jean Boussier 2f777dc61d Remove private `transaction(joinable:)` parameter from RDoc
Fix: https://github.com/rails/rails/issues/46182

This argument is only for internal use, like transactional fixtures
or the `--sandbox` console flag.
2022-10-04 09:27:17 +02:00
Datt Dongare 6c6fb51848 Fix sqlite -> SQLite 2022-10-02 09:17:18 +05:30
Eileen M. Uchitelle f8e97a1464
Merge pull request #46166 from eileencodes/use-adapter-class-over-connection-class
Use `adapter_class` instead of `connection_class` for adapters
2022-09-30 16:51:00 -04:00
eileencodes 5bb357f14a
Use `adapter_class` instead of `connection_class` for adapters
In Active Record `connection_class` already means "the class in your
application that established the connection" so I would prefer it only
have that one meaning. This change swaps `connection_class` for
`adapter_class` where applicable.
2022-09-30 16:14:51 -04:00
Rafael Mendonça França 910af8f3c4
Merge pull request #46140 from ahoglund/ahoglund/nil-precision-option
Check for Existing but nil `:precision` Option
2022-09-30 16:12:45 -04:00
Andrew Hoglund a7703ce10b Add precision assignment back to timestamps method
This code block was removed in a previous PR, but it had
a side effect of causing certian timestamp fields to be
changed from `datetime(6)` to `datetime` when performing a schema
migration in the 6.1 version of the migration class. This is due
to `options[:precision]` being set to `nil` and therefore
not being set to the default of 6 later on in the code path.
2022-09-30 14:59:02 -05:00
Gannon McGibbon 4bcb8e4afb Move dbconsole logic to Active Record connection adapter.
Instead of hosting the connection logic in the command object, the
database adapter should be responsible for connecting to a console session.
This patch moves #find_cmd_and_exec to the adapter and exposes a new API to
lookup the adapter class without instantiating it.

Co-authored-by: Paarth Madan <paarth.madan@shopify.com>
2022-09-29 16:29:06 -05:00
Jonathan Hefner 8e383fdad6 Avoid double type cast when serializing attributes
Most model attribute types try to cast a given value before serializing
it.  This allows uncast values to be passed to finder methods and still
be serialized appropriately.  However, when persisting a model, this
cast is unnecessary because the value will already have been cast by
`ActiveModel::Attribute#value`.

To eliminate the overhead of a 2nd cast, this commit introduces a
`ActiveModel::Type::SerializeCastValue` module.  Types can include this
module, and their `serialize_cast_value` method will be called instead
of `serialize` when serializing an already-cast value.

To preserve existing behavior of any user types that subclass Rails'
types, `serialize_after_cast` will only be called if the type itself
(not a superclass) includes `ActiveModel::Type::SerializeCastValue`.
This also applies to type decorators implemented via `DelegateClass`.

Benchmark script:

  ```ruby
  require "active_model"
  require "benchmark/ips"

  class ActiveModel::Attribute
    alias baseline_value_for_database value_for_database
  end

  VALUES = {
    my_big_integer: "123456",
    my_boolean: "true",
    my_date: "1999-12-31",
    my_datetime: "1999-12-31 12:34:56 UTC",
    my_decimal: "123.456",
    my_float: "123.456",
    my_immutable_string: "abcdef",
    my_integer: "123456",
    my_string: "abcdef",
    my_time: "1999-12-31T12:34:56.789-10:00",
  }

  TYPES = VALUES.to_h { |name, value| [name, name.to_s.delete_prefix("my_").to_sym] }

  class MyModel
    include ActiveModel::API
    include ActiveModel::Attributes

    TYPES.each do |name, type|
      attribute name, type
    end
  end

  TYPES.each do |name, type|
    $attribute_set ||= MyModel.new(VALUES).instance_variable_get(:@attributes)
    attribute = $attribute_set[name.to_s]

    puts "=" * 72
    Benchmark.ips do |x|
      x.report("#{type} before") { attribute.baseline_value_for_database }
      x.report("#{type} after") { attribute.value_for_database }
      x.compare!
    end
  end
  ```

Benchmark results:

  ```
  ========================================================================
  Warming up --------------------------------------
    big_integer before   100.417k i/100ms
     big_integer after   260.375k i/100ms
  Calculating -------------------------------------
    big_integer before      1.005M (± 1.0%) i/s -      5.121M in   5.096498s
     big_integer after      2.630M (± 1.0%) i/s -     13.279M in   5.050387s

  Comparison:
     big_integer after:  2629583.6 i/s
    big_integer before:  1004961.2 i/s - 2.62x  (± 0.00) slower

  ========================================================================
  Warming up --------------------------------------
        boolean before   230.663k i/100ms
         boolean after   299.262k i/100ms
  Calculating -------------------------------------
        boolean before      2.313M (± 0.7%) i/s -     11.764M in   5.085925s
         boolean after      3.037M (± 0.6%) i/s -     15.262M in   5.026280s

  Comparison:
         boolean after:  3036640.8 i/s
        boolean before:  2313127.8 i/s - 1.31x  (± 0.00) slower

  ========================================================================
  Warming up --------------------------------------
           date before   148.821k i/100ms
            date after   298.939k i/100ms
  Calculating -------------------------------------
           date before      1.486M (± 0.6%) i/s -      7.441M in   5.006091s
            date after      2.963M (± 0.8%) i/s -     14.947M in   5.045651s

  Comparison:
            date after:  2962535.3 i/s
           date before:  1486459.4 i/s - 1.99x  (± 0.00) slower

  ========================================================================
  Warming up --------------------------------------
       datetime before    92.818k i/100ms
        datetime after   136.710k i/100ms
  Calculating -------------------------------------
       datetime before    920.236k (± 0.6%) i/s -      4.641M in   5.043355s
        datetime after      1.366M (± 0.8%) i/s -      6.836M in   5.003307s

  Comparison:
        datetime after:  1366294.1 i/s
       datetime before:   920236.1 i/s - 1.48x  (± 0.00) slower

  ========================================================================
  Warming up --------------------------------------
        decimal before    50.194k i/100ms
         decimal after   298.674k i/100ms
  Calculating -------------------------------------
        decimal before    494.141k (± 1.4%) i/s -      2.510M in   5.079995s
         decimal after      3.015M (± 1.0%) i/s -     15.232M in   5.052929s

  Comparison:
         decimal after:  3014901.3 i/s
        decimal before:   494141.2 i/s - 6.10x  (± 0.00) slower

  ========================================================================
  Warming up --------------------------------------
          float before   217.547k i/100ms
           float after   298.106k i/100ms
  Calculating -------------------------------------
          float before      2.157M (± 0.8%) i/s -     10.877M in   5.043292s
           float after      2.991M (± 0.6%) i/s -     15.203M in   5.082806s

  Comparison:
           float after:  2991262.8 i/s
          float before:  2156940.2 i/s - 1.39x  (± 0.00) slower

  ========================================================================
  Warming up --------------------------------------
  immutable_string before
                         163.287k i/100ms
  immutable_string after
                         298.245k i/100ms
  Calculating -------------------------------------
  immutable_string before
                            1.652M (± 0.7%) i/s -      8.328M in   5.040855s
  immutable_string after
                            3.022M (± 0.9%) i/s -     15.210M in   5.033151s

  Comparison:
  immutable_string after:  3022313.3 i/s
  immutable_string before:  1652121.7 i/s - 1.83x  (± 0.00) slower

  ========================================================================
  Warming up --------------------------------------
        integer before   115.383k i/100ms
         integer after   159.702k i/100ms
  Calculating -------------------------------------
        integer before      1.132M (± 0.8%) i/s -      5.769M in   5.095041s
         integer after      1.641M (± 0.5%) i/s -      8.305M in   5.061893s

  Comparison:
         integer after:  1640635.8 i/s
        integer before:  1132381.5 i/s - 1.45x  (± 0.00) slower

  ========================================================================
  Warming up --------------------------------------
         string before   163.061k i/100ms
          string after   299.885k i/100ms
  Calculating -------------------------------------
         string before      1.659M (± 0.7%) i/s -      8.316M in   5.012609s
          string after      2.999M (± 0.6%) i/s -     15.294M in   5.100008s

  Comparison:
          string after:  2998956.0 i/s
         string before:  1659115.6 i/s - 1.81x  (± 0.00) slower

  ========================================================================
  Warming up --------------------------------------
           time before    98.250k i/100ms
            time after   133.463k i/100ms
  Calculating -------------------------------------
           time before    987.771k (± 0.7%) i/s -      5.011M in   5.073023s
            time after      1.330M (± 0.5%) i/s -      6.673M in   5.016573s

  Comparison:
            time after:  1330253.9 i/s
           time before:   987771.0 i/s - 1.35x  (± 0.00) slower
  ```
2022-09-29 11:34:29 -05:00
Yasuo Honda 441f967a3f
Merge pull request #45790 from mikeletscher/bind-attribute-primary-key-relation
[AR] Fix uniqueness validation on association not using overridden PK
2022-09-28 17:44:12 +09:00
Aaron Patterson 2fb72f578f
Merge pull request #45081 from iheanyi/iheanyi/custom-query-log-tags-separators
Add ability to set the `tags_format` for `QueryLogs`
2022-09-27 17:27:16 -07:00
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
Petrik de Heus 9553814a3a
Merge pull request #45628 from asavageiv/alan/fix-lock-documentation [ci-skip]
Document ActiveRecord::Locking::Pessimistic#with_lock reloads the record
2022-09-27 21:45:57 +02:00
Eileen M. Uchitelle 5b8421f56e
Merge pull request #46134 from eikes/remove-dummy-erb-compiler
Facilitate use of any regular ERB in database.yml
2022-09-27 11:27:26 -04:00
Eike Send 5ba8aa5854
Facilitate use of any regular ERB in database.yml
Commit 37d1429ab1 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
fatkodima 5fecf23e4d Do not preserve original column collation in `change_column` for older migrations 2022-09-25 23:43:20 +03:00
Rafael Mendonça França fb4a6d49cb
Merge pull request #46112 from sambostock/remove-duplicate-datetime-precision-default
Remove duplicate datetime precision default
2022-09-23 15:34:06 -04: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 94e64a2452
Remove duplicate datetime precision default
We already set the default precision for all datetime columns, so there is no
need to check it in the timestamps method.
2022-09-22 19:11:28 -04: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
Sam Bostock 919dd4a331
Add test for existing virtual datetime precision
Active Record erroneously uses a precision of `nil` instead of the default
precision, when adding a virtual datetime column.

This tests that behavior ahead of fixing it.
2022-09-22 17:55:44 -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
Jean Boussier 6c70d02f02
Merge pull request #46046 from adrianna-chang-shopify/ac-db-retries-with-timeout
Take into account timeout limit when retrying queries
2022-09-22 20:49:49 +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
Rafael Mendonça França e910ec6646
Merge pull request #46090 from nvasilevski/do-not-write-to-store-unchanged-value
Do not write unchanged value to ActiveRecord::Store
2022-09-21 14:16:51 -04:00
Nikita Vasilevsky 53082b7076 Do not write unchanged value to ActiveRecord::Store 2022-09-21 16:17:55 +00:00
Yasuo Honda 859af30e3b
Merge pull request #45216 from yahonda/rubocop_minitest_020_enables_skip_ensure_cop
Do not run tests that have `ensure` instead of `skip`
2022-09-21 08:09:32 +09:00
Rafael Mendonça França 485ec2a5ab
Merge pull request #45770 from RyanMacG/update-docs-for-unscoped
Update docs for `.unscoped` to be explicit about associations
2022-09-20 14:56:14 -04:00
Yasuo Honda 1b736a8720 Do not run tests that have `ensure` instead of `skip`
Even if tests are "skipped" by `Minitest::Assertions#skip`
`ensure` is executed because tests methods are Ruby methods.

Adding conditions outside of the test methods, entire test methods are
executed only when they are necessary.
2022-09-20 22:12:20 +09:00
Eileen M. Uchitelle fd7c773387
Merge pull request #46067 from eileencodes/dont-delegate-tasks-to-base
Don't delegate tasks to ActiveRecord::Base
2022-09-19 16:00:25 -04:00
Rafael Mendonça França 6ffa34dcf7
Register autoload for `ActiveRecord::LogSubscriber`
Before f56b4187be this constant was being
loaded in the railtie, but it was removed since we didn't needed it
anymore.

It causes some application to break if they try to reference the
subscriber before `ActiveRecord::Base` is loaded.
2022-09-19 19:50:34 +00:00
eileencodes c74b290414
Don't delegate tasks to ActiveRecord::Base
This PR calls `ActiveRecord::Base` directly on `establish_connection`
and `connection` rather than delegating. While this doesn't have much
effect right now, I'm working on moving the database tasks away from
their reliance on Base and eventually we'll need to pass a class through
to these adapter tasks. This change prepares these adapter tasks for
that change.
2022-09-19 14:50:01 -04:00
fatkodima d49252008f Updating the `ActiveRecord::Store` and changing it back should not mark accessor as changed 2022-09-19 11:52:40 +03: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
Robert Nubel 66579a1d75 Adds test case for query cache false positive bug 2022-09-19 09:41:35 +02:00
Petrik c328ac5c86 Use `sslmode` option instead of `ssl_mode` for MySQL command connections
In f36c261a44 support for the ssl-mode option was added for MySQL when
using the dbconsole command and MySQLDatabaseTasks.
However, the mysql2 gem uses `ssl_mode` instead of `sslmode`:

ba4d46551d/lib/mysql2/client.rb (L51)

As `ssl_mode` is what should be used in the database.yml configuration
for MySQL we should use it as well for the dbconsole command and
MySQLDatabaseTasks.

Co-authored-by: Chris Gunther <chris@room118solutions.com>
2022-09-17 18:53:54 +02:00
Petrik d14662a5ae Tiny copy fix in CHANGELOGs [ci-skip] 2022-09-17 12:54:43 +02:00
Rafael Mendonça França 26ceb7fc33
Merge pull request #42933 from ghiculescu/postgres-column-types
Don't skip some columns in `column_types` on Postgres
2022-09-16 17:04:12 -04:00
Rafael Mendonça França fbc4dff578
Merge pull request #46051 from jbampton/fix-case-of-json
Fix word case. `json` -> `JSON`
2022-09-16 16:02:42 -04:00
Eileen M. Uchitelle b8d9a36320
Merge pull request #46054 from eileencodes/fix-loggers-for-sm-and-im
Fix logger tags for SchemaMigration and InternalMetadata
2022-09-16 16:00:03 -04:00
eileencodes f47ef2365e
Fix logger tags for SchemaMigration and InternalMetadata
When I moved `SchemaMigration` and `InternalMetadata` away from inheriting
from `ActiveRecord::Base` I accidentally set the logger tags to `self`
rather than `self.class` which meant they were accidentally logging
the instance rather than the class name. While fixing these I realized I
missed a few tags for other queries and have fixed those as well.
2022-09-16 15:37:37 -04:00
eileencodes 5e9d52e7fe
Improve warning when db is missing
I found the previous warning to confusing because it reads like one
sentence but is two. I think this change improves the grammar of the error.
2022-09-16 15:04:46 -04:00
John Bampton 3a32915bbc Fix word case. `json` -> `JSON` 2022-09-17 04:11:36 +10:00
Jean Boussier 26826eb354
Merge pull request #45771 from andrewn617/type-cast-attribute-changed-from-and-to-options
Type cast #attribute_changed? :from and :to options
2022-09-16 12:16:27 +02:00
Yasuo Honda 8c619c3096
Merge pull request #46008 from p8/activerecord/ssl-verify-connection
Add ssl-mode option to dbconsole command and MySQLDatabaseTasks
2022-09-16 16:51:51 +09: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
Rafael Mendonça França 8e34831f97
Merge pull request #45298 from joshuay03/fix-inconsistent-polymorphic-association-autosave
Fix: Inconsistent Polymorphic Association Autosave
2022-09-15 13:15:30 -04:00
Jean Boussier f56b4187be Fix Active Record :db_runtime metric
In https://github.com/rails/rails/pull/45796 I overlooked that
`ActiveRecord::LogSubscriber#sql` wasn't only logging the SQL query
but was also responsible for collecting the `:db_runtime` metric.

Ultimately I think it is cleaner to move this concern to `RuntimeRegistry`.
2022-09-15 10:43:33 +02:00
Joshua Young 589321d99a Fix: Inconsistent Polymorphic Association Autosave 2022-09-15 13:24:19 +10:00
Rafael Mendonça França 514ae381a3
Merge pull request #45877 from neilvilela/nc-composed-of-hash
Clarify `composed_of` allows a Hash for `mapping`
2022-09-14 17:39:53 -04:00
Rafael Mendonça França 83b24d3911
Merge pull request #46034 from fatkodima/flaky-enum-migration-inverting-test
Fix flaky PostgreSQL enum migration reversibility test
2022-09-14 15:58:32 -04:00
fatkodima 34dec308a4 Fix flaky PostgreSQL enum migration reversibility test 2022-09-14 22:15:14 +03:00
fatkodima 49c2342add Remove redundant `ELSE` branch from the `ActiveRecord::Base.in_order_of` SQL query 2022-09-14 18:37:37 +03:00
Rafael Mendonça França 46bfabcfd4
Merge pull request #44547 from skipkayhil/fix-incorrect-assertions
fix remaining asserts that should be assert_equal
2022-09-12 20:32:42 -04:00
eileencodes 3d50f34af0
Fix bug in internal metadata
Since changing internal metadata to no longer inherit from Base
in #45982 I accidentally changed the behavior when a key's value is the
same. Prior to this change the record would not be updated if the
environment key was found an the value had not changed. So instead of
just checking whether we have an entry here we also need to check if the
value should actually be updated, otherwise we should return the entry.
2022-09-12 16:02:59 -04:00
eileencodes e0d7034e37
Minor refactoring to schema migration and internal metadata
This clean up wraps the messages at 80 characters and clarifies the
changes needed. 99% of apps won't see this warning as they aren't
accessing MigrationContext directly. This is only for libraries or apps
that are passing a custom `SchemaMigration` or `InternalMetadata` class
so they get a more helpful error.

In addition I moved the `NullSchemaMigration` up to the top of the class
to match InternalMetadata.
2022-09-12 13:59:06 -04:00
eileencodes 32c5c8f0ff
Fix missed test changes for pool method deprecations
Following #45924 sometimes these calls will throw deprecation warnings
depending on the test order and whether there are connections left
behind from other tests. I attempted to prevent leaking connections but
when using fixtures it's pretty hard to avoid because the
`teardown_fixture_connections` will often put connections back in place.
It is eaiser to ensure these calls opt into the new behavior to avoid
deprecation warnings.
2022-09-12 13:05:55 -04: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
Gannon McGibbon ee81a0ed34
Merge pull request #45976 from gmcgibbon/log_subscriber_modes
Add italic and underline support to `ActiveSupport::LogSubscriber#color`
2022-09-09 23:43:50 -05:00
Gannon McGibbon 6016f9ef31 Add italic and underline support to `ActiveSupport::LogSubscriber#color`
Previously, only bold text was supported via a positional argument.
This allows for bold, italic, and underline options to be specified
for colored logs.

```ruby
info color("Hello world!", :red, bold: true, underline: true)
```
2022-09-09 22:55:33 -05:00
Hartley McGuire c62dcf54eb
fix remaining asserts that should be assert_equal
Found using Minitest/AssertWithExpectedArgument.

Also enabled the rule per feedback and fixed 29 additional violations
2022-09-09 19:22:21 -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 606fdfb0c6
Merge pull request #45829 from ghiculescu/nested-attribute-docs-improvements
Nested attribute docs improvements
2022-09-09 17:28:13 -04:00
Rafael Mendonça França 663e846381
Require used model to fix isolated tests 2022-09-09 21:17:59 +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
Gannon McGibbon 2b42bbc8a2
Merge pull request #45975 from gmcgibbon/application_record_generator_usage
Delegate application record generator description to orm hooked generator.
2022-09-09 12:32:31 -04:00
Iliana Hadzhiatanasova a0fd15ee7e Default prepared_statements to false for mysql2 adapter 2022-09-09 16:21:33 +01:00
Jean Boussier 63be3385bd Mysql2Adapter remove reference to closed connection
If `connect` fails, we'd stick with a closed `Mysql2` connection
which means on the next try we'll try to ping it without any
chance of success.
2022-09-09 16:37:55 +02:00
Gannon McGibbon f54cbe1f4a Delegate application record generator description to orm hooked generator. 2022-09-08 18:19:51 -05: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
Eileen M. Uchitelle e24fbf71fa
Merge pull request #45916 from eileencodes/reduce-calls-to-pool-configs
Reduce calls to pool_configs
2022-09-08 11:23:19 -04:00
eileencodes bc951b4719
Reduce allocations of pool_configs
This is a partial fix for #45906 to allocate less objects when iterating
through pool configs. My original attempt to fix this wasn't feasible
due to how the query cache works. The idea to use a dedicated `each_*`
came from jonathanhefner and I incorporated it here.

Using the following script:

```
Benchmark.memory do |x|
  x.report("all") do
    ActiveRecord::Base.connection_handler.all_connection_pools.each { }
  end

  x.report("each") do
    ActiveRecord::Base.connection_handler.each_connection_pool { }
  end

  x.compare!
end
```

We can see that less objects are allocated with the each method. This is
even more important now that we are calling `all_connection_pools` in
more placed since #45924 and #45961.

```
Calculating -------------------------------------
                 all   408.000  memsize (     0.000  retained)
                         7.000  objects (     0.000  retained)
                         0.000  strings (     0.000  retained)
                each   328.000  memsize (     0.000  retained)
                         3.000  objects (     0.000  retained)
                         0.000  strings (     0.000  retained)

Comparison:
                each:        328 allocated
                 all:        408 allocated - 1.24x more
```

Note that if this is run again on this branch `all_connection_pools`
will have even more allocations due to the deprecation warning that was
introduced. However removing that shows similar numbers to the benchmark
here.

Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>
2022-09-08 10:27:15 -04:00
Gabriel Oliveira fd67de56d0 Fix typo in :active_record_suppressor_registry symbol 2022-09-08 15:48:33 +02:00
Ryuta Kamizono e88005a875
Merge pull request #45965 from koic/enable_minitest_assert_raises_with_regexp_argument_cop
Enable `Minitest/AssertRaisesWithRegexpArgument` cop
2022-09-08 18:10:07 +09:00
Koichi ITO eeeb0b1b7a Enable `Minitest/AssertRaisesWithRegexpArgument` cop
This PR enables `Minitest/AssertRaisesWithRegexpArgument` cop
and it suppresses the new warning below.

```console
% bundle exec rubocop
(snip)

Offenses:

activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb:111:9: C: Minitest/AssertRaisesWithRegexpArgument:
Do not pass regular expression literals to assert_raises. Test the resulting exception.
assert_raises(ActiveRecord::StatementInvalid, /TypeError/) do
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb:628:9: C: Minitest/AssertRaisesWithRegexpArgument:
Do not pass regular expression literals to assert_raises. Test the resulting exception.
assert_raises(ActiveRecord::StatementInvalid, /SQLite3::ReadOnlyException/) do
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
railties/test/application/rake/multi_dbs_test.rb:307:13: C: Minitest/AssertRaisesWithRegexpArgument:
Do not pass regular expression literals to assert_raises. Test the resulting exception.
assert_raises RuntimeError, /You're using a multiple database application/ do
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
railties/test/application/rake/multi_dbs_test.rb:311:13: C: Minitest/AssertRaisesWithRegexpArgument:
Do not pass regular expression literals to assert_raises. Test the resulting exception.
assert_raises RuntimeError, /You're using a multiple database application/ do
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
railties/test/application/rake/multi_dbs_test.rb:336:13: C: Minitest/AssertRaisesWithRegexpArgument:
Do not pass regular expression literals to assert_raises. Test the resulting exception.
assert_raises RuntimeError, /You're using a multiple database application/ do
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
railties/test/application/rake/multi_dbs_test.rb:361:13: C: Minitest/AssertRaisesWithRegexpArgument:
Do not pass regular expression literals to assert_raises. Test the resulting exception.
assert_raises RuntimeError, /You're using a multiple database application/ do
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
```

The last argument of `assert_raises` is a custom message string to help explain failures.
So, it's not the argument that `exception.message` is compared to.
`assert_raises` returns a raised exception and can be used to match against a regular expression.

And it updates the dependency version of rubocop-minitest in the Gemfile.
Because `Minitest/AssertRaisesWithRegexpArgument` cop was introduced in minitest 0.22.
https://github.com/rubocop/rubocop-minitest/releases/tag/v0.22.0
2022-09-08 17:44:40 +09:00
fatkodima 8f12731b90 Fix loading records with encrypted attributes defined on columns with default values 2022-09-07 23:30:35 +03: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
John Hawthorn 80a0eca40b Speed up arel visitor dispatch
This commit makes two performance tweaks to the dispatch_cache in
Arel::Visitors::Visitor.

First, it interns the method name into a symbol rather than a string.
send expects a symbol, so this avoids Ruby needing to intern that string
itself.

Second, it changes the dispatch cache to an identity hash. Classes are
already compared by identity, but Ruby is able to perform the lookup
more quickly if it is explicit on the hash.
2022-09-02 12:33:20 -07:00
fatkodima 2714e21021 Do not mutate relation when implicitly selecting a primary key in `ActiveRecord.find` 2022-09-02 16:38:06 +03:00
fatkodima b55f0c54df Fix `ActiveRecord::FinderMethods.find` when passing multiple ids and primary key is not selected 2022-09-02 15:22:36 +03:00
Jean Boussier 2b01f332c6
Merge pull request #45612 from alextrueman/extend_select_with_hashes
Allow ActiveRecord::QueryMethods#select to accept a hash
2022-09-02 11:33:56 +02: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
HParker ce6a50c101 Avoid using Module#<= in value_for
During where clause generation we search for scope type for the model.
We can do this with Module#!= instead as long as we grab the final scope type after the loop.

This is a small, but significant performance improvement.

Co-authored-by: John Hawthorn <jhawthorn@github.com>
2022-09-01 12:18:50 -07:00
Tom Hallett decafbd9cf
Docs: Example should append to ignored_columns
Updating the documentation for setting `self.ignored_columns` on an ActiveRecord model to append to the existing value instead of overwriting the existing array.

Appending to the existing value, `+=`, is helpful for the scenario when 2 developers set `ignored_columns` in different parts of the file and the second one overwrites the first one.
2022-08-30 10:23:13 -04:00
fatkodima 76a8db07b0 Fix reverting the addition of invalid foreign keys and check constraints 2022-08-28 01:43:36 +03:00
fatkodima 3158bbb9f6 Update `rubocop-performance` and enable more performance-related cops 2022-08-26 15:07:11 +03:00
Jonathan Hefner e45947328e Fix typo in CHANGELOG [ci-skip]
Follow-up to #42650.
2022-08-25 11:54:13 -05:00
Jonathan Hefner 0fbac7f409
Merge pull request #42650 from intrip/becomes_with_virtual_attrs
Normalize virtual attributes on ActiveRecord::Persistence#becomes
2022-08-25 10:48:54 -05:00
fatkodima 4c62903dfb Extend dangerous attribute methods with dangerous methods from Object 2022-08-25 03:09:36 +03: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
Akshay Birajdar 6ac4ee967d Use #to_hash to serialize AS::HWIA & Hash for stored attributes 2022-08-25 00:28:48 +05:30
Neil Carvalho ccb2393979 Clarify `composed_of` allows a Hash for `mapping`
[`composed_of`](https://api.rubyonrails.org/classes/ActiveRecord/Aggregations/ClassMethods.html#method-i-composed_of)
is a feature that is not widely used and its API is somewhat confusing,
especially for beginners. It was even deprecated for a while, but 10
years after its deprecation, it's still here.

The Rails documentation includes these examples including mapping:
```ruby
composed_of :temperature, mapping: %w(reading celsius)
composed_of :balance, class_name: "Money", mapping: %w(balance amount)
composed_of :address, mapping: [ %w(address_street street),
%w(address_city city) ]
```

Hashes are accepted kind-of accidentally for the `mapping` option. Using
a hash, instead of an array or array of arrays makes the documentation
more beginner-friendly.

```ruby
composed_of :temperature, mapping: { reading: :celsius }
composed_of :balance, class_name: "Money", mapping: { balance: :amount }
composed_of :address, mapping: { address_street: :street, address_city:
:city }
```

Before Ruby 1.9, looping through a hash didn't have deterministic order,
and the mapping order is important, as that's the same order Rails uses
when initializing the value object. Since Ruby 1.9, this isn't an issue
anymore, so we can change the documentation to use hashes instead.

This commit changes the documentation for `composed_of`, clarifying that
any key-value format (both a `Hash` and an `Array`) are accepted for the
`mapping` option. It also adds tests to ensure hashes are also accepted.
2022-08-23 15:57:54 -03:00
eileencodes 92240559f2
Clean up schema load tasks
While working on another PR I noticed that we have 6 different tasks for
loading the schema; 3 for all dbs, 3 for namespaced dbs. They are:

* `db:schema:load`
* `db:schema:load:namespace`
* `db:test:load_schema`
* `db:test:load_schema:namespace`
* `db:test:load` (calls `db:test:load_schema` only)
* `db:test:load:namespace` (calls `db:test:load_schema:namespace` only)

I've removed the last two because they didn't call anything except for
another task and they were already private. I believe the original
`db:test:load` and `db:test:load:namescace` were added to handle whether
to load sql or ruby schema fules, but we no longer need that (it's
handled by the `load_schema` method directly now).

In addition I removed `:load_config, :check_protected_environments` from
the `db:schema:load:namespace` task because `purge` already calls these
so we don't need to do it twice.

I tried to remove `db:test:load_schema:namespace` and call
`db:schema:load:namespace` instead but we can't do that because
`db:test:load_schema:namespace` needs to set the environment to "test",
not use the current environment. `db:schema:load` also can't be used
instead of `db:test:load_schema` because it tries to load both dev and
test schemas, but it's callers want just the test schema.
2022-08-22 14:35:33 -04:00
Jean Boussier 2418939007
Merge pull request #45796 from Shopify/log-subscriber-silenced
Optimize AS::LogSubscriber
2022-08-22 10:43:49 +02:00
Jean Boussier d1a9a9928d
Merge pull request #45015 from fatkodima/missing-and-associated-parent-child
Fix `where.missing` and `where.associated` for parent/child associations
2022-08-20 08:43:16 +02:00
Jean Boussier 8cd4d65fe2
Merge pull request #45808 from fatkodima/check_constraint_exists-public
Make `check_constraint_exists?` public
2022-08-19 23:20:04 +02:00
Jean Boussier 3dcd28acdd
Merge pull request #45860 from HParker/add-sqlite=binary-column-default-support
Add binary column default value support to sqlite
2022-08-19 23:00:57 +02:00
eileencodes 46e4580d39
Move InternalMetadata to migration context
This PR is similar to #36439 which moved schema migrations to the
connection and migration context. This PR does the same thing for the
internal metadata class.

Previously the metadata class was only accessible on Base which makes it
a little weird when switching connections. This didn't really cause any
issues but I noticed it when working on a PR to change how connections
work in the database rake tasks.

In a multiple database environment it makes sense to access the
`ar_internal_metadata` on the existing connection rather than on
`Base.connection`. I previously didn't implement this because I felt we
wanted only one place to ask for the metadata information but recently
realized that we were still recording metadata on all the databases, so
we might as well record it correctly.

Applications should notice no change in behavior because this is only
accessed through the rake tasks. Additionally behavior between schema
migrations and internal metadata are now more similar. Eventually I'd
like neither of these to inherit from or rely on Base, but we have a
lot more work to do before that's possible.
2022-08-19 16:05:15 -04:00
HParker e1bf63fca9 Add binary column default value support to sqlite
This adds support for reading binary column default values before the column data is read from the database.
This makes binary columns behave more like other column types with default values
2022-08-19 11:27:21 -07:00
eileencodes 43a6b4fc0f
Use Time instead of Date for comparison
In Ruby trunk date changed to use the gregorian calendar in
https://github.com/ruby/date/pull/73.

This had the result that dates not in the gregorian calendar can't be
compared to times not in the gregorian calendar. Since this test isn't
testing datetime comparison but rather that bc timestamps can be stored
in the database, we can compare with `Time.new(0) - 1.week` rather than
`Date.new(0) - 1.week`. I think this might be a bug in Ruby but I'm not
sure there's a great fix. For now however, let's get this test passing.
2022-08-18 14:48:53 -04:00
Alex Ghiculescu 2220b55c3b
Update activerecord/lib/active_record/nested_attributes.rb
Co-authored-by: Petrik de Heus <petrik@deheus.net>
2022-08-17 18:12:33 -05:00