Commit Graph

22188 Commits

Author SHA1 Message Date
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
Jonathan Hefner 18f8c6ac9b Add CHANGELOG entry for #45670 [ci-skip] 2022-08-17 11:51:31 -05:00
eileencodes 2efb06d1dd
Define and raise error if `legacy_connection_handling=` is called from an app
I deprecated `legacy_connection_handling` setter in 7.0 but then removed
it without setting it to false by default upgraded apps might have this
set to false in their config and still be getting an error. This
confusing behavior came up with one of our apps at work.

To make it less confusing I've redefined `legacy_connection_handling`
setter and am raising an argument error if called. I didn't redefine the
getter because that shouldn't be in a config, it's not useful in that
context.
2022-08-16 16:28:00 -04:00
Alex Ghiculescu 10a83207e6 Nested attribute docs improvements
2 small improvemennts to the docs for `ActiveRecord::NestedAttributes`.

- Include a link to `ActionView::Helpers::FormHelper::fields_for`, since you would typically use these tools in tandem I think it makes sense to point you to it.
- Show an example of how to write an integration test when using `ActionView::Helpers::FormHelper::fields_for`. You could argue that this example should also go in Action View, but I think it makes more sense here as the "parent" doc for the Nested Attributes feature.
2022-08-15 12:26:33 -05:00
fatkodima 885c024f70 Fix `ActiveRecord::QueryMethods#in_order_of` to work with nils
When passing `nil` to `in_order_of`, the invalid SQL query is generated (because `NULL != NULL`):

```ruby
Book.in_order_of(:format, ["hardcover", "ebook", nil]).to_sql
```

```sql
SELECT "books".* FROM "books" WHERE "books"."format" IN ('hardcover', 'ebook', NULL)
ORDER BY CASE "books"."format" WHEN 'hardcover' THEN 1
WHEN 'ebook' THEN 2 WHEN NULL THEN 3 ELSE 4 END ASC
```

This PR also removes the special order field generation (`ORDER BY FIELD`) for MYSQL, because it is
unable to work with `NULL`s and the default `ORDER BY CASE` works for it (tested on MariaDB and MySQL 5.7+).
2022-08-15 19:07:55 +03:00
John Hawthorn 08af60e01d
Merge pull request #45807 from ahoglund/binary-collation-fix-2
Run DROP DEFAULT for NULL value; Add text type checks
2022-08-12 13:55:55 -07:00
Eileen M. Uchitelle 18da7b6ba7
Merge pull request #45806 from adrianna-chang-shopify/ac-expose-schema-creation-objects
Expose `#schema_creation` methods
2022-08-12 12:06:23 -04:00
Jean Boussier bd19d1baf1 Optimize AS::LogSubscriber
The various LogSubscriber subclasses tend to subscribe to events
but then end up doing nothing if the log level is high enough.

But even if we end up not logging, we have to go through the
entire notification path, record timing etc.

By allowing subscribers to dynamically bail out early, we can
save a lot of work if all subscribers are silenced.
2022-08-12 09:58:17 +02:00
Andrew Hoglund 008d9d165a
Run DROP DEFAULT for NULL value; Add text type checks
This commit does two things.

1. Reverts a recent change that was calling SET DEFAULT NULL
when altering a table. This PR revert that back to instead call DROP
DEFAULT in the case when the column is not nullable.
2. Adds a check to ensure that only nonbinary data types are eligible
for having the `collation` carry through when performing a change_column
migration.
2022-08-11 20:16:17 +00:00
Iliana Hadzhiatanasova 79096618f0 Optimize add_timestamps to use a single SQL statement 2022-08-11 19:12:03 +03:00
fatkodima ead8558129 Make `check_constraint_exists?` public 2022-08-11 16:52:09 +03:00
fatkodima 86519996f9 Enable `Style/RedundantCondition` cop 2022-08-11 11:38:14 +03:00
Adrianna Chang be83bf4dee Expose #schema_creation methods
Accessing SchemaCreation instances allows consumers to visit schema definitions
and produce DDL / populate the definitions with DDL type information.
2022-08-10 14:15:14 -04:00
Eduardo Hernandez c619cec014 Docs: Update belongs_to [:default] option section
Just added some more details about how `belongs_to` behaves when using
this option based on what I found here:

- https://github.com/rails/rails/blob/main/activerecord/lib/active_record/associations/builder/belongs_to.rb#L102-L106
- https://github.com/rails/rails/blob/main/activerecord/lib/active_record/associations/belongs_to_association.rb#L35-L37
2022-08-09 21:27:46 -05:00
Andrew Hoglund be0a58b6c4 Ensure collation is set to database default when changing column
A recent change made it so that when you perform a change column
it will preserve collation on the column rather than nil it out·
which would select the database default. This works fine except in
the case of a binary type. In this case the collation should be
set to nil in the options so that the database default collation
type can be used on the column.
2022-08-09 17:42:18 +00:00
Mike Letscher 3c8dab7b12 [AR] Fix uniqueness validation on association not using overridden PK 2022-08-08 19:22:50 +00:00
Eileen M. Uchitelle c54b26d90c
Merge pull request #45772 from adrianna-chang-shopify/ac-remove-ddl-from-schema-definitions
Don't store `ddl` on schema definitions
2022-08-08 08:45:20 -04:00
Adrianna Chang 47141261c3 Don't store ddl on schema definitions
We'll ensure that any consumers that require ddl obtain it by visiting
the schema definition. Consequently, we should also stop visiting the
schema definition in the "schema definition builder" methods -- callers
will need to both build a schema definition, and then visit it using a
SchemaCreation object. This means schema definitions will not be populated
with SQL type information, or any other information that is set when
the definition is visited.
2022-08-08 08:12:24 -04:00
Yasuo Honda aa00ef35eb
Merge pull request #45740 from ghiculescu/enum-types-schema
Support schemas in Postgresql `enum_types`
2022-08-07 10:46:17 +09:00
Alex Ghiculescu 3ac1d8fe27 Support schemas in Postgresql `enum_types`
Follow up to https://github.com/rails/rails/pull/44896 and https://github.com/rails/rails/pull/45707
2022-08-06 09:07:56 -05:00
Jonathan Hefner 32c169d014 Linkify code references [ci-skip] 2022-08-05 21:36:18 -05:00
Jonathan Hefner b13107fe5e Fix typos [ci-skip] 2022-08-05 21:36:18 -05:00
Jonathan Hefner afe98a0b5d
Merge pull request #45774 from jonathanhefner/drop_enum-always-reversible
Ensure `drop_enum` is always reversible
2022-08-05 21:35:17 -05:00
Jonathan Hefner a24879d0cd
Merge pull request #45765 from sampatbadhe/patch-1
[ci skip] Add basic create_enum and drop_enum in CommandRecorder documentations
2022-08-05 12:10:15 -05:00
Jonathan Hefner 2aa16fa561 Ensure drop_enum is always reversible
Follow-up to #45735.

Prior to this commit, `drop_enum` could not be reversed when called with
the `if_exists: true` option, because `create_enum` could not accept
options.

This commit fixes that and adds test coverage.

This commit also ensures that reversing `drop_enum` will raise an
`ActiveRecord::IrreversibleMigration` error if no enum values are
specified.
2022-08-05 12:07:47 -05:00
eileencodes 70f2ae001e
Only setup shared pools if we have a connection
Previously `setup_shared_connection_pool` was called anytime
`setup_fixtures` saw a future connection established. Sometimes those
connections are `nil` but we were setting up the shared pools before
determining that.

This change moves the `setup_shared_connection_pool` into the check for
if we have a connection. The tests that were dealing with these settings
were originally set to not use transacitons. But without this we can't
test the change here so I've updated that as well.

This is an alternate fix to #45769. I realized after opening the PR it
wasn't quite right to skip if we don't have a writing pool config,
because the question is how were we ending up with that anyway? Well the
answer was that we were calling the shared setup in the wrong place.

Fixes #45767
2022-08-05 12:40:52 -04:00
Andrew Novoselac 7519457678 Fixes a defect in ActiveModel::Dirty#attribute_changed? where the :from
and :to options are not being type cast. For example, for an enum
attribute, attribute_changed? should handle a String, Symbol or Integer
for the :from and :to options.
2022-08-05 11:52:20 -04:00
Ryan MacGillivray 0a7a52405e
Update docs for `.unscoped` to be explicit about associations
We recently ran into an issue where we used `.unscoped` as part of a
method chain without realising that it would also unset the scoping of
the association. It feels like this would be useful information to
surface in the documentation so that there's a clear source of truth on
how the method behaves.
2022-08-05 16:29:35 +01:00
sampatbadhe 76d2185c60 [ci skip] adding basic create_enum and drop_enum in CommandRecorder documentations 2022-08-05 12:41:35 +05:30
eileencodes 1623892582
Fix rubocop warning re unused argument 2022-08-04 14:50:11 -04:00
eileencodes 8d0da3d568
Refactor changes in #44931 and add a test
The PR #44931 fell off my radar a few months ago so I decided to add a
test myself to ensure this behavior change is tested. While doing that I
decided to refactor the changes from that PR to make the code a little
cleaner and easier to read.
2022-08-04 12:58:39 -04:00
Matt Larraz 8af4165a18
Improve #configured_migrate_path logic (#44931)
* Improve #configured_migrate_path logic

This solves two issues:

* `migrations_paths` is an Array, which here gets coerced to a string.
  This is fine if it's a single path, but otherwise the generated path
  is a concatenation of all the paths. Instead, pick the first one.

* If the `--database` flag is not passed in, fall back to the primary
  database's `migrations_paths` entry if available before falling back
  to the global default migrate path.

* migrations_paths can be a String, Array, or nil
2022-08-04 12:58:20 -04:00
Aram Greenman affb8cf2fe Deprecate quoting ActiveSupport::Duration as an integer 2022-08-04 09:15:58 -07:00
Eileen M. Uchitelle 0c9e4069f7
Merge pull request #45751 from adrianna-chang-shopify/ac-build-change-column-default-definition
Define #build_change_column_definition for MySQL and PG
2022-08-04 08:37:11 -04:00
Alex Ghiculescu bd0fdc8094 Add `drop_enum` command for Postgres 2022-08-03 18:50:43 -05:00
Adrianna Chang 7455336638 Define #build_change_column_definition for MySQL and PG
Define APIs on the MySQL and PostgreSQL connection adapters for building
ChangeColumnDefaultDefinition objects. These provide information on what a
column default change would look like, when called with the same arguments as
would be passed to #change_column_default.
2022-08-03 16:27:25 -04:00
eileencodes 58e6f11878
Fix indentation
I messed up copying and pasting this. Rubocop failures caught
this in https://github.com/rails/rails/runs/7658743379?check_suite_focus=true
2022-08-03 14:46:11 -04:00
eileencodes 25f97a66bd
Add support for `if_exists` option when removing a check constraint
The `remove_check_constraint` method now accepts an `if_exists` option. If set
to true an error won't be raised if the check constraint doesn't exist.

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

Usage:

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

Fixes #45634

Co-authored-by: Margaret Parsa <mparsa@actbluetech.com>
Co-authored-by: Aditya Bhutani <adi_bhutani16@yahoo.in>
2022-08-03 14:39:54 -04:00
Jean Boussier 1288289ed0
Merge pull request #45697 from adrianna-chang-shopify/ac-change-column-default-mysql
Use SET DEFAULT when changing a column default in MySQL
2022-08-03 20:23:08 +02:00
Jean Boussier e1f566a6f4
Merge pull request #45734 from fatkodima/fix-columnless-enums
Raise when defining an enum not backed by a database column
2022-08-03 20:20:39 +02:00
Adrianna Chang b9b8852adc Use SET DEFAULT when changing a column default in MySQL
Aligns the SQL produced for changing a column default between MySQL and PostgreSQL.
MySQL is currently producing an <ALTER TABLE x CHANGE COLUMN y> query just to
change the default for a column, which is less efficient than using the SET DEFAULT syntax.

Additionally, extracts ChangeColumnDefaultDefinition and moves the SQL generation to the visitor class.
2022-08-03 11:16:06 -04:00
fatkodima 3a0d0f4bd8 Preserve collaction when changing column in MySQL 2022-08-03 13:09:25 +03:00
fatkodima 6c5fab0668 Raise when defining an enum not backed by a database column 2022-08-03 11:14:45 +03:00
Jean Boussier 408d061a80
Merge pull request #45720 from Shopify/find-or-create-or-find-by
find_or_create_by: handle race condition by finding again
2022-08-02 15:11:20 +02:00
fatkodima cd3508607d Accept empty list of attributes for `insert_all`, `insert_all!` and `upsert_all` 2022-08-02 13:21:31 +03:00
Jean Boussier 023a3eb3c0 find_or_create_by: handle race condition by finding again
Using `create_or_find_by` in codepaths where most of the time
the record already exist is wasteful on several accounts.

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

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

Co-Authored-By: Alex Kitchens <alexcameron98@gmail.com>
2022-08-02 09:58:26 +02:00
Jean Boussier 765caa44a7
Merge pull request #45714 from simi/proper-verbose-cleanup
Store previous verbose setting before it can fail in tests.
2022-08-01 19:29:17 +02:00
Jean Boussier 7de5053923 Refactor ExtendedDeterministicQueries to not mutate the arguments
Ref: https://github.com/rails/rails/pull/41659

Mutating the attributes hash requires to workaround the mutation
in `find_or_create_by` etc.

One extra Hash dup is not big deal.
2022-08-01 16:21:33 +02:00
Josef Šimánek 4f4dd2ef02 Set collation for postgresql test DBs. 2022-07-31 16:23:00 +02:00
Josef Šimánek bfdf158a15 Store previous verbose setting before it can fail in tests.
- thanks to hoisting it can be wrongly set to nil instead of previous value
2022-07-31 14:54:43 +02:00
fatkodima 438862b5d3 Support explicit schemas in PostgreSQL's `create_enum` 2022-07-31 00:12:07 +03:00
Matthew Draper 1adb6170d5 Add changelog entries for #44576 and #44591 2022-07-30 06:09:12 +09:30
Matthew Draper 72cdf1584a
Merge pull request #44591 from rails/defer-db-connect
Simplify adapter construction; defer connect until first use
2022-07-30 06:08:55 +09:30
Matthew Draper 7fe221d898
Merge pull request #44576 from rails/defer-db-verify
Defer verification of database connections
2022-07-30 06:08:34 +09:30
Eileen M. Uchitelle b623494a29
Merge pull request #45685 from adrianna-chang-shopify/ac-build-create-join-table-definition
Extract `#build_create_join_table_definition`
2022-07-29 13:38:22 -04:00
Matthew Draper 91a3a2d0cc Don't expect rollback to cause a reconnect 2022-07-30 00:55:54 +09:30
Matthew Draper e4193dee1d Be a bit clearer in the new-adapter-API argument error 2022-07-30 00:55:54 +09:30
Matthew Draper 0223967ab6 Drop default retry count to 1
This is enough to enable basic recovery for everyone by default, while
leaving more time-consuming repeated attempts for those who configure
them.
2022-07-30 00:55:54 +09:30
Matthew Draper d56188dc7a We're still inside the transaction until the ROLLBACK has run
This previously made no difference, but it now saves us reconnecting to
run a ROLLBACK: because we're still inside a transaction at that moment,
we won't be #restorable?.
2022-07-30 00:55:54 +09:30
Matthew Draper fe2846a154 Non-retryable exceptions also break connection verification 2022-07-30 00:55:54 +09:30
Matthew Draper deec3004d8 Configure legacy-API-supplied connection before first use 2022-07-30 00:55:54 +09:30
Matthew Draper 0e9267767f Ensure a lost connection upon COMMIT fails "cleanly"
There's no point attempting to rollback in this case; we can instead
just invalidate the now-lost transaction.

Similarly, even though we can't immediately reconnect for a connection
failure mid-transaction, we can and should drop and prior verification
of the connection: the next out-of-transaction query attempt needs to
fix it, not assume it's working.
2022-07-30 00:55:54 +09:30
Matthew Draper 8551e64e24 Simplify adapter construction; defer connect until first use 2022-07-30 00:55:48 +09:30
Adrianna Chang c59cbb55da Define #build_change_column_definition on connection adapters
Define APIs on the MySQL and PostgreSQL connection adapters for building
ChangeColumnDefinition objects. These provide information on what a
column change would look like, when called with the same arguments as
would be passed to #change_column.
2022-07-28 16:15:47 -04:00
Adrianna Chang 108c30e114 Extract build_create_join_table_definition
Exposes an API for building a TableDefinition for a join table operation.
2022-07-28 16:11:47 -04:00
Adrianna Chang e91b6421ec Extract #build_add_column_definition
Exposes AlterTable schema definition for adding new columns through new API,
and stores ddl on the schema definition. Refactors existing #add_column method to use
this as an intermediate method to build the create add_column definition.
2022-07-28 15:19:29 -04:00
Eileen M. Uchitelle f3339dfb95
Merge pull request #45630 from adrianna-chang-shopify/ac-extract-build-create-index-definition
Extract `#build_create_index_definition`
2022-07-28 14:51:32 -04:00
Adrianna Chang 54b45ff8e2 Extract #build_create_index_definition
Exposes CreateIndex schema definition through new API, and stores ddl
on the schema definition. Refactors existing #add_index methods to use
this as an intermediate method to build the create index definition.
2022-07-28 13:42:38 -04:00
Eileen M. Uchitelle 2e8f4cb062
Merge pull request #45669 from adrianna-chang-shopify/ac-allow-change-table-to-specify-base
Allow base for `Table` schema definition to be injected into `#change_table`
2022-07-27 13:58:21 -04:00
fatkodima fcd90558b7 Add missing test ensuring no subqueries for whole table batching 2022-07-27 17:37:46 +03:00
Adrianna Chang 1f777620a8 Allow base for Table schema definition to be injected into #change_table 2022-07-27 09:56:47 -04:00
Gannon McGibbon 8b94e87c11
Merge pull request #45659 from gmcgibbon/parent_option_default
Parent option defaults
2022-07-26 18:35:29 -05:00
Gannon McGibbon e1635ff4a7 Show default parent classes for generators in help texts
Print default parent class for controller, job, and model generators.

Before:

[--parent=PARENT]               # The parent class for the generated job

After:

[--parent=PARENT]               # The parent class for the generated job
                                # Default: ApplicationJob
2022-07-26 18:13:02 -05:00
Matthew Draper 57bc28f728 Add new tests for deferred connection verification and auto-reconnect 2022-07-27 02:15:15 +09:30
Jonathan Hefner e2d9747123 Fix update_all API doc [ci-skip]
Multi-line list items must be indented, and inline code must be quoted
using `+` or `<tt>`.  (RDoc does not support backticks.)
2022-07-26 10:38:37 -05:00
Matthew Draper 6693e5fc9a Deadlocks inside transactions are functionally not retryable
Even if we think the deadlocking query could be retried, the original
deadlock will have either broken or already rolled back our transaction.
2022-07-26 22:07:36 +09:30
Matthew Draper 3cc2f8ac77 Check #reconnect_can_restore_state? once, before running block
The block can't affect reconnectability, because we'll be re-running it
anyway.

Also, only #reconnect! once per query, to avoid squaring the total
number of permitted retries while attempting to query a pathologically
flakey server.
2022-07-26 22:07:35 +09:30
Matthew Draper 02f5de1bc9 Add retry handling to connection establishment too 2022-07-26 22:07:35 +09:30
Matthew Draper da52b0d954 Separate two groups of retryable DB exceptions
Connectivity issues need a reconnect to be retried, but there are also
in-server errors that can also be retried (lock timeouts and deadlocks).

To handle this better, we move translation into #with_raw_connection:
it's a much better representation of the boundary between the underlying
adapter library and Active Record's abstractions anyway. (It doesn't
know about the query being run, though, so we let #log enrich that into
the exception later.)
2022-07-26 22:07:35 +09:30
Matthew Draper b9f44e66ba Restore transactions in materialized state where appropriate
We may be restoring a connection after #with_raw_connection has already
passed #materialize_transactions, so it's important we eagerly return
things to exactly the state they had before.
2022-07-26 22:07:35 +09:30
Matthew Draper 129bfa193c Default most internal/schema queries to retryable 2022-07-26 22:07:35 +09:30
Matthew Draper 35d3ae3298 Switch Postgres statement pool to use the AR connection
We're still ultimately accessing the raw connection directly, but this
is necessary to keep the statement pool working across full reconnects.
2022-07-26 22:07:35 +09:30
Matthew Draper 542f0951dd Automatically reconnect to database mid-request
So far this is just infrastructure: no errors are considered retryable,
and no real queries are marked retryable.

Even in this state, we can drop connection verification from checkout,
and defer it until the first query is ready. But the more interesting
within-reach win will be when 'BEGIN' is retryable, and we can recognise
connectivity-related exceptions.
2022-07-26 22:07:35 +09:30
Alex Ghiculescu 82b8061ca2
Mention `Arel.sql` in `update_all` docs [docs]
This is needed when a string is input, to avoid type casting.

Fixes https://github.com/rails/rails/issues/45658
2022-07-25 15:14:21 -05:00
fatkodima 515a9215d1 Avoid removing a PostgreSQL extension when there are dependent objects 2022-07-22 15:17:18 +03:00
Eileen M. Uchitelle 69078b0820
Merge pull request #45625 from adrianna-chang-shopify/ac-extract-build-create-table-definition
Extract `#build_create_table_definition` method
2022-07-20 09:25:05 -04:00
Alan Savage 6ba065c198 Document ActiveRecord::Locking::Pessimistic#with_lock reloads the record 2022-07-19 18:34:08 -07:00
Jean Boussier 71c59c69f4
Merge pull request #44010 from siegfault/dangerous_query_method_allow_nested_functions
Accept nested functions in Dangerous Query Methods
2022-07-19 21:04:15 +02:00
Adrianna Chang d747b0f132 Define #build_create_table_definition on connection adapter
Define API on connection adapter for building a TableDefinition.
The provided TableDefinition object offers insight into what a table
created using the migration method #create_table would look like, including
information on primary keys, column types, etc.

As a part of this PR, we extract TableDefinition#set_primary_key to handle
some of the primary key setting logic currently being performed in #create_table.
2022-07-19 14:22:54 -04:00
Eileen M. Uchitelle fa7f977e2d
Merge pull request #45626 from eileencodes/remove-duplicate-code-in-rake-tasks
Remove duplicate code in dump task
2022-07-19 13:13:49 -04:00
eileencodes 31ab9cfe15
Remove duplicate code in dump task
The `_dump` task is private so it shouldn't be called by apps. Because
of this were establishing a connection to the db_config twice, once in
`_dump` and once in `dump`. There's no reason to do this since `_dump`
always calls `dump` and apps should not be calling `_dump`.
2022-07-19 12:42:30 -04:00
mario e3bb10d0c3 prefix table_schema to `ALTER TABLE ... VALIDATE CONSTRAINT` for postgres
In Postgres there was an issue when calling `ActiveRecord::ConnectionAdapters::PostgreSQL::ReferentialIntegrity#all_foreign_keys_valid?` if there were multiple schemas with foreign key constraints, because the ALTER TABLE statement did not include the schema.

This commit adds the necessary schema prefix

```
ALTER TABLE [schema].[table_name] VALIDATE CONSTRAINT [constraint]
             ^^^^^^
```
2022-07-19 17:56:22 +02:00
Eileen M. Uchitelle bb68040de4
Merge pull request #45621 from eileencodes/fix-granular-swapping-for-primary_abstract_class
Fix granular swapping for primary_abstract_class
2022-07-19 09:54:43 -04:00
eileencodes 741d029606
Fix granular swapping for primary_abstract_class
In #45450 we stopped removing connections when establish_connection was
called and an identical connection existed in the pool. Unfortunately
this broke granular connection swapping for the primary_abstract_class
because the class stored in the pool was incorrect.

What was happening was connection for the writing role were getting
stored with the class ActiveRecord::Base and connections for the reading
role were getting ApplicationRecord (or whatever the app set as their
`primary_abstract_class`. If we made sure that the reading connections
also got ActiveRecord::Base that would break granular swapping for both
writing and reading because Active Record doesn't know whether you
wanted global swapping or granular swapping for prevent writes. This bug
only manifests on prevent writes because it's the only value we actually
lookup on the connection pool (because at the point we need it we don't
have access to self).

To fix this I've decided to update the class on the existing pool if it
doesn't match the owner_name passed. This is kind of gross I admit, but
it's safe because the way we find connections is on the
`connection_name`. The class is only stored so that the connection can
find it's `current_preventing_writes` value. This also required me to
move the ivar to an instance method for connection_class on the pool
because we don't want to cache the value and want to make sure we're
getting it from the pool config directly.
2022-07-19 08:22:35 -04:00
fatkodima c5ef2f4fb9 Fix `QueryMethods#in_order_of` for non-integer columns in MySQL 2022-07-19 02:24:58 +03:00
Eileen M. Uchitelle 812bae8167
Merge pull request #45592 from eileencodes/remove-unnecessary-connection_handler-calls
Remove unnecessary or incorrect calls to connection_handler
2022-07-13 16:05:47 -04:00
Michael Siegfried 8fe1bd555f Accept nested functions in Dangerous Query Methods
Mailing list thread: https://discuss.rubyonrails.org/t/feature-proposal-accept-nested-functions-w-r-t-dangerous-query-methods/78650

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

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

*Background*
* PR accepting non-nested functions: https://github.com/rails/rails/pull/36448
* Deep background on deprecation and false positives: https://github.com/rails/rails/issues/32995
* Constants: `COLUMN_NAME` for the first and `COLUMN_NAME_WITH_ORDER` for both
2022-07-13 11:52:01 -07:00
eileencodes baa90408a2
Remove unnecessary or incorrect calls to connection_handler
In an effort to find all the internal callers of `connection_handler` I
decided to remove any uses that are either:

1) Unnecessary - we can call what we need on Base or elsewhere without
going through the handler
2) Incorrect - there's a better way call the same thing. For exmaple
avoiding calling the ivar, and instead calling `send` on an existing
method.

Doing this will inform us what cases the connection handler is required
and how we can expand Base to provide everything an application or gem
could need without going through the handler. Reducing calls makes it
easier to replace or refactor in the future.
2022-07-13 14:26:04 -04:00
Ben Sheldon 8fceabd1e8 Defer constant loading of `ActiveRecord::DestroyAssociationAsyncJob` via a String instead of a class constant
Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>
2022-07-13 11:58:20 -05:00
Jean Boussier 05fdb3edfd ActiveRecord::Store encode store as a regular Hash
Fix: https://github.com/rails/rails/issues/45585

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

We can cast it back to a regular hash before serialization.
2022-07-13 18:30:37 +02:00
Jean Boussier 84555afa2c Appease rubocop 2022-07-13 18:22:13 +02:00