activerecord 7.2.2.2 → 7.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +316 -7
- data/README.rdoc +1 -1
- data/lib/active_record/associations/alias_tracker.rb +6 -4
- data/lib/active_record/associations/belongs_to_association.rb +18 -2
- data/lib/active_record/associations/collection_association.rb +9 -7
- data/lib/active_record/associations/join_dependency/join_association.rb +25 -27
- data/lib/active_record/attribute_methods/serialization.rb +1 -1
- data/lib/active_record/attribute_methods.rb +24 -19
- data/lib/active_record/attributes.rb +37 -26
- data/lib/active_record/autosave_association.rb +22 -12
- data/lib/active_record/base.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +49 -32
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +17 -6
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +5 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +3 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +32 -3
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +14 -6
- data/lib/active_record/connection_adapters/mysql/quoting.rb +7 -1
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +3 -3
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -1
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +14 -12
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +8 -3
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +1 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +10 -5
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +1 -1
- data/lib/active_record/connection_handling.rb +12 -8
- data/lib/active_record/core.rb +27 -7
- data/lib/active_record/counter_cache.rb +1 -1
- data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -1
- data/lib/active_record/delegated_type.rb +18 -18
- data/lib/active_record/encryption/encryptable_record.rb +1 -1
- data/lib/active_record/encryption/encrypted_attribute_type.rb +1 -1
- data/lib/active_record/encryption/encryptor.rb +21 -20
- data/lib/active_record/enum.rb +13 -12
- data/lib/active_record/errors.rb +3 -3
- data/lib/active_record/fixture_set/table_row.rb +19 -2
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/migration.rb +2 -1
- data/lib/active_record/query_logs.rb +4 -0
- data/lib/active_record/querying.rb +4 -4
- data/lib/active_record/railtie.rb +2 -2
- data/lib/active_record/railties/databases.rake +2 -1
- data/lib/active_record/relation/calculations.rb +35 -30
- data/lib/active_record/relation/finder_methods.rb +10 -10
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +2 -0
- data/lib/active_record/relation/query_attribute.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +16 -9
- data/lib/active_record/relation/where_clause.rb +8 -2
- data/lib/active_record/relation.rb +15 -5
- data/lib/active_record/schema_dumper.rb +29 -11
- data/lib/active_record/secure_token.rb +3 -3
- data/lib/active_record/signed_id.rb +7 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +7 -0
- data/lib/active_record/transactions.rb +3 -1
- data/lib/active_record.rb +1 -1
- data/lib/arel/collectors/bind.rb +1 -1
- data/lib/arel/crud.rb +2 -0
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/nodes/delete_statement.rb +4 -2
- data/lib/arel/nodes/update_statement.rb +4 -2
- data/lib/arel/select_manager.rb +6 -2
- data/lib/arel/update_manager.rb +5 -0
- data/lib/arel/visitors/dot.rb +2 -0
- data/lib/arel/visitors/to_sql.rb +3 -1
- metadata +8 -8
| @@ -21,28 +21,34 @@ module ActiveRecord | |
| 21 21 | 
             
                  # your domain objects across much of Active Record, without having to
         | 
| 22 22 | 
             
                  # rely on implementation details or monkey patching.
         | 
| 23 23 | 
             
                  #
         | 
| 24 | 
            -
                  #  | 
| 25 | 
            -
                  # column which this will persist to.
         | 
| 24 | 
            +
                  # ==== Parameters
         | 
| 26 25 | 
             
                  #
         | 
| 27 | 
            -
                  # + | 
| 28 | 
            -
                  #  | 
| 29 | 
            -
                  #  | 
| 30 | 
            -
                  # Otherwise, the type will be ActiveModel::Type::Value.
         | 
| 31 | 
            -
                  # See the examples below for more information about providing custom type objects.
         | 
| 26 | 
            +
                  # [+name+]
         | 
| 27 | 
            +
                  #   The name of the methods to define attribute methods for, and the
         | 
| 28 | 
            +
                  #   column which this will persist to.
         | 
| 32 29 | 
             
                  #
         | 
| 33 | 
            -
                  #  | 
| 30 | 
            +
                  # [+cast_type+]
         | 
| 31 | 
            +
                  #   A symbol such as +:string+ or +:integer+, or a type object to be used
         | 
| 32 | 
            +
                  #   for this attribute. If this parameter is not passed, the previously
         | 
| 33 | 
            +
                  #   defined type (if any) will be used. Otherwise, the type will be
         | 
| 34 | 
            +
                  #   ActiveModel::Type::Value. See the examples below for more information
         | 
| 35 | 
            +
                  #   about providing custom type objects.
         | 
| 34 36 | 
             
                  #
         | 
| 35 | 
            -
                  #  | 
| 37 | 
            +
                  # ==== Options
         | 
| 36 38 | 
             
                  #
         | 
| 37 | 
            -
                  #  | 
| 38 | 
            -
                  #  | 
| 39 | 
            -
                  #  | 
| 39 | 
            +
                  # [+:default+]
         | 
| 40 | 
            +
                  #   The default value to use when no value is provided. If this option is
         | 
| 41 | 
            +
                  #   not passed, the previously defined default value (if any) on the
         | 
| 42 | 
            +
                  #   superclass or in the schema will be used. Otherwise, the default will
         | 
| 43 | 
            +
                  #   be +nil+.
         | 
| 40 44 | 
             
                  #
         | 
| 41 | 
            -
                  #  | 
| 42 | 
            -
                  #  | 
| 45 | 
            +
                  # [+:array+]
         | 
| 46 | 
            +
                  #   (PostgreSQL only) Specifies that the type should be an array. See the
         | 
| 47 | 
            +
                  #   examples below.
         | 
| 43 48 | 
             
                  #
         | 
| 44 | 
            -
                  #  | 
| 45 | 
            -
                  #  | 
| 49 | 
            +
                  # [+:range+]
         | 
| 50 | 
            +
                  #   (PostgreSQL only) Specifies that the type should be a range. See the
         | 
| 51 | 
            +
                  #   examples below.
         | 
| 46 52 | 
             
                  #
         | 
| 47 53 | 
             
                  # When using a symbol for +cast_type+, extra options are forwarded to the
         | 
| 48 54 | 
             
                  # constructor of the type object.
         | 
| @@ -178,8 +184,8 @@ module ActiveRecord | |
| 178 184 | 
             
                  #       @currency_converter = currency_converter
         | 
| 179 185 | 
             
                  #     end
         | 
| 180 186 | 
             
                  #
         | 
| 181 | 
            -
                  #     # value will be the result of  | 
| 182 | 
            -
                  #     #  | 
| 187 | 
            +
                  #     # value will be the result of #deserialize or
         | 
| 188 | 
            +
                  #     # #cast. Assumed to be an instance of Money in
         | 
| 183 189 | 
             
                  #     # this case.
         | 
| 184 190 | 
             
                  #     def serialize(value)
         | 
| 185 191 | 
             
                  #       value_in_bitcoins = @currency_converter.convert_to_bitcoins(value)
         | 
| @@ -217,17 +223,22 @@ module ActiveRecord | |
| 217 223 | 
             
                  # is provided so it can be used by plugin authors, application code
         | 
| 218 224 | 
             
                  # should probably use ClassMethods#attribute.
         | 
| 219 225 | 
             
                  #
         | 
| 220 | 
            -
                  #  | 
| 226 | 
            +
                  # ==== Parameters
         | 
| 227 | 
            +
                  #
         | 
| 228 | 
            +
                  # [+name+]
         | 
| 229 | 
            +
                  #   The name of the attribute being defined. Expected to be a +String+.
         | 
| 221 230 | 
             
                  #
         | 
| 222 | 
            -
                  # +cast_type+ | 
| 231 | 
            +
                  # [+cast_type+]
         | 
| 232 | 
            +
                  #   The type object to use for this attribute.
         | 
| 223 233 | 
             
                  #
         | 
| 224 | 
            -
                  # +default+ | 
| 225 | 
            -
                  #  | 
| 226 | 
            -
                  #  | 
| 227 | 
            -
                  # will be  | 
| 234 | 
            +
                  # [+default+]
         | 
| 235 | 
            +
                  #   The default value to use when no value is provided. If this option
         | 
| 236 | 
            +
                  #   is not passed, the previous default value (if any) will be used.
         | 
| 237 | 
            +
                  #   Otherwise, the default will be +nil+. A proc can also be passed, and
         | 
| 238 | 
            +
                  #   will be called once each time a new value is needed.
         | 
| 228 239 | 
             
                  #
         | 
| 229 | 
            -
                  # +user_provided_default+ | 
| 230 | 
            -
                  # +cast+ or +deserialize+.
         | 
| 240 | 
            +
                  # [+user_provided_default+]
         | 
| 241 | 
            +
                  #   Whether the default value should be cast using +cast+ or +deserialize+.
         | 
| 231 242 | 
             
                  def define_attribute(
         | 
| 232 243 | 
             
                    name,
         | 
| 233 244 | 
             
                    cast_type,
         | 
| @@ -338,19 +338,29 @@ module ActiveRecord | |
| 338 338 | 
             
                    return true if record.destroyed? || (association.options[:autosave] && record.marked_for_destruction?)
         | 
| 339 339 |  | 
| 340 340 | 
             
                    context = validation_context if custom_validation_context?
         | 
| 341 | 
            +
                    return true if record.valid?(context)
         | 
| 341 342 |  | 
| 342 | 
            -
                     | 
| 343 | 
            -
                       | 
| 344 | 
            -
             | 
| 345 | 
            -
             | 
| 346 | 
            -
             | 
| 347 | 
            -
             | 
| 348 | 
            -
                        }
         | 
| 349 | 
            -
                      else
         | 
| 350 | 
            -
                        errors.add(association.reflection.name)
         | 
| 351 | 
            -
                      end
         | 
| 343 | 
            +
                    if context || record.changed_for_autosave?
         | 
| 344 | 
            +
                      associated_errors = record.errors.objects
         | 
| 345 | 
            +
                    else
         | 
| 346 | 
            +
                      # If there are existing invalid records in the DB, we should still be able to reference them.
         | 
| 347 | 
            +
                      # Unless a record (no matter where in the association chain) is invalid and is being changed.
         | 
| 348 | 
            +
                      associated_errors = record.errors.objects.select { |error| error.is_a?(Associations::NestedError) }
         | 
| 352 349 | 
             
                    end
         | 
| 353 | 
            -
             | 
| 350 | 
            +
             | 
| 351 | 
            +
                    if association.options[:autosave]
         | 
| 352 | 
            +
                      return if equal?(record)
         | 
| 353 | 
            +
             | 
| 354 | 
            +
                      associated_errors.each { |error|
         | 
| 355 | 
            +
                        errors.objects.append(
         | 
| 356 | 
            +
                          Associations::NestedError.new(association, error)
         | 
| 357 | 
            +
                        )
         | 
| 358 | 
            +
                      }
         | 
| 359 | 
            +
                    elsif associated_errors.any?
         | 
| 360 | 
            +
                      errors.add(association.reflection.name)
         | 
| 361 | 
            +
                    end
         | 
| 362 | 
            +
             | 
| 363 | 
            +
                    errors.any?
         | 
| 354 364 | 
             
                  end
         | 
| 355 365 |  | 
| 356 366 | 
             
                  # Is used as an around_save callback to check while saving a collection
         | 
| @@ -483,7 +493,7 @@ module ActiveRecord | |
| 483 493 |  | 
| 484 494 | 
             
                    class_name = record._read_attribute(reflection.inverse_of.foreign_type)
         | 
| 485 495 |  | 
| 486 | 
            -
                    reflection.active_record !=  | 
| 496 | 
            +
                    reflection.active_record.polymorphic_name != class_name
         | 
| 487 497 | 
             
                  end
         | 
| 488 498 |  | 
| 489 499 | 
             
                  # Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
         | 
    
        data/lib/active_record/base.rb
    CHANGED
    
    | @@ -256,13 +256,13 @@ module ActiveRecord # :nodoc: | |
| 256 256 | 
             
              # * AssociationTypeMismatch - The object assigned to the association wasn't of the type
         | 
| 257 257 | 
             
              #   specified in the association definition.
         | 
| 258 258 | 
             
              # * AttributeAssignmentError - An error occurred while doing a mass assignment through the
         | 
| 259 | 
            -
              #   {ActiveRecord::Base#attributes=}[rdoc-ref:AttributeAssignment#attributes=] method.
         | 
| 259 | 
            +
              #   {ActiveRecord::Base#attributes=}[rdoc-ref:ActiveModel::AttributeAssignment#attributes=] method.
         | 
| 260 260 | 
             
              #   You can inspect the +attribute+ property of the exception object to determine which attribute
         | 
| 261 261 | 
             
              #   triggered the error.
         | 
| 262 262 | 
             
              # * ConnectionNotEstablished - No connection has been established.
         | 
| 263 263 | 
             
              #   Use {ActiveRecord::Base.establish_connection}[rdoc-ref:ConnectionHandling#establish_connection] before querying.
         | 
| 264 264 | 
             
              # * MultiparameterAssignmentErrors - Collection of errors that occurred during a mass assignment using the
         | 
| 265 | 
            -
              #   {ActiveRecord::Base#attributes=}[rdoc-ref:AttributeAssignment#attributes=] method.
         | 
| 265 | 
            +
              #   {ActiveRecord::Base#attributes=}[rdoc-ref:ActiveModel::AttributeAssignment#attributes=] method.
         | 
| 266 266 | 
             
              #   The +errors+ property of this exception contains an array of
         | 
| 267 267 | 
             
              #   AttributeAssignmentError
         | 
| 268 268 | 
             
              #   objects that should be inspected to determine which attributes triggered the errors.
         | 
| @@ -38,6 +38,7 @@ module ActiveRecord | |
| 38 38 |  | 
| 39 39 | 
             
                  def schema_cache; end
         | 
| 40 40 | 
             
                  def connection_class; end
         | 
| 41 | 
            +
                  def query_cache; end
         | 
| 41 42 | 
             
                  def checkin(_); end
         | 
| 42 43 | 
             
                  def remove(_); end
         | 
| 43 44 | 
             
                  def async_executor; end
         | 
| @@ -118,24 +119,30 @@ module ActiveRecord | |
| 118 119 | 
             
                # * private methods that require being called in a +synchronize+ blocks
         | 
| 119 120 | 
             
                #   are now explicitly documented
         | 
| 120 121 | 
             
                class ConnectionPool
         | 
| 121 | 
            -
                   | 
| 122 | 
            -
             | 
| 123 | 
            -
             | 
| 124 | 
            -
                     | 
| 125 | 
            -
             | 
| 126 | 
            -
                     | 
| 127 | 
            -
             | 
| 128 | 
            -
             | 
| 129 | 
            -
                       | 
| 130 | 
            -
             | 
| 122 | 
            +
                  # Prior to 3.3.5, WeakKeyMap had a use after free bug
         | 
| 123 | 
            +
                  # https://bugs.ruby-lang.org/issues/20688
         | 
| 124 | 
            +
                  if ObjectSpace.const_defined?(:WeakKeyMap) && RUBY_VERSION >= "3.3.5"
         | 
| 125 | 
            +
                    WeakThreadKeyMap = ObjectSpace::WeakKeyMap
         | 
| 126 | 
            +
                  else
         | 
| 127 | 
            +
                    class WeakThreadKeyMap # :nodoc:
         | 
| 128 | 
            +
                      # FIXME: On 3.3 we could use ObjectSpace::WeakKeyMap
         | 
| 129 | 
            +
                      # but it currently cause GC crashes: https://github.com/byroot/rails/pull/3
         | 
| 130 | 
            +
                      def initialize
         | 
| 131 | 
            +
                        @map = {}
         | 
| 132 | 
            +
                      end
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                      def clear
         | 
| 135 | 
            +
                        @map.clear
         | 
| 136 | 
            +
                      end
         | 
| 131 137 |  | 
| 132 | 
            -
             | 
| 133 | 
            -
             | 
| 134 | 
            -
             | 
| 138 | 
            +
                      def [](key)
         | 
| 139 | 
            +
                        @map[key]
         | 
| 140 | 
            +
                      end
         | 
| 135 141 |  | 
| 136 | 
            -
             | 
| 137 | 
            -
             | 
| 138 | 
            -
             | 
| 142 | 
            +
                      def []=(key, value)
         | 
| 143 | 
            +
                        @map.select! { |c, _| c&.alive? }
         | 
| 144 | 
            +
                        @map[key] = value
         | 
| 145 | 
            +
                      end
         | 
| 139 146 | 
             
                    end
         | 
| 140 147 | 
             
                  end
         | 
| 141 148 |  | 
| @@ -309,8 +316,9 @@ module ActiveRecord | |
| 309 316 | 
             
                  # held in a cache keyed by a thread.
         | 
| 310 317 | 
             
                  def lease_connection
         | 
| 311 318 | 
             
                    lease = connection_lease
         | 
| 312 | 
            -
                    lease.sticky = true
         | 
| 313 319 | 
             
                    lease.connection ||= checkout
         | 
| 320 | 
            +
                    lease.sticky = true
         | 
| 321 | 
            +
                    lease.connection
         | 
| 314 322 | 
             
                  end
         | 
| 315 323 |  | 
| 316 324 | 
             
                  def permanent_lease? # :nodoc:
         | 
| @@ -336,6 +344,7 @@ module ActiveRecord | |
| 336 344 | 
             
                    end
         | 
| 337 345 |  | 
| 338 346 | 
             
                    @pinned_connection.lock_thread = ActiveSupport::IsolatedExecutionState.context if lock_thread
         | 
| 347 | 
            +
                    @pinned_connection.pinned = true
         | 
| 339 348 | 
             
                    @pinned_connection.verify! # eagerly validate the connection
         | 
| 340 349 | 
             
                    @pinned_connection.begin_transaction joinable: false, _lazy: false
         | 
| 341 350 | 
             
                  end
         | 
| @@ -358,6 +367,7 @@ module ActiveRecord | |
| 358 367 | 
             
                      end
         | 
| 359 368 |  | 
| 360 369 | 
             
                      if @pinned_connection.nil?
         | 
| 370 | 
            +
                        connection.pinned = false
         | 
| 361 371 | 
             
                        connection.steal!
         | 
| 362 372 | 
             
                        connection.lock_thread = nil
         | 
| 363 373 | 
             
                        checkin(connection)
         | 
| @@ -548,20 +558,25 @@ module ActiveRecord | |
| 548 558 | 
             
                  # Raises:
         | 
| 549 559 | 
             
                  # - ActiveRecord::ConnectionTimeoutError no connection can be obtained from the pool.
         | 
| 550 560 | 
             
                  def checkout(checkout_timeout = @checkout_timeout)
         | 
| 551 | 
            -
                     | 
| 552 | 
            -
             | 
| 553 | 
            -
             | 
| 561 | 
            +
                    return checkout_and_verify(acquire_connection(checkout_timeout)) unless @pinned_connection
         | 
| 562 | 
            +
             | 
| 563 | 
            +
                    @pinned_connection.lock.synchronize do
         | 
| 564 | 
            +
                      synchronize do
         | 
| 565 | 
            +
                        # The pinned connection may have been cleaned up before we synchronized, so check if it is still present
         | 
| 566 | 
            +
                        if @pinned_connection
         | 
| 554 567 | 
             
                          @pinned_connection.verify!
         | 
| 568 | 
            +
             | 
| 555 569 | 
             
                          # Any leased connection must be in @connections otherwise
         | 
| 556 570 | 
             
                          # some methods like #connected? won't behave correctly
         | 
| 557 571 | 
             
                          unless @connections.include?(@pinned_connection)
         | 
| 558 572 | 
             
                            @connections << @pinned_connection
         | 
| 559 573 | 
             
                          end
         | 
| 574 | 
            +
             | 
| 575 | 
            +
                          @pinned_connection
         | 
| 576 | 
            +
                        else
         | 
| 577 | 
            +
                          checkout_and_verify(acquire_connection(checkout_timeout))
         | 
| 560 578 | 
             
                        end
         | 
| 561 579 | 
             
                      end
         | 
| 562 | 
            -
                      @pinned_connection
         | 
| 563 | 
            -
                    else
         | 
| 564 | 
            -
                      checkout_and_verify(acquire_connection(checkout_timeout))
         | 
| 565 580 | 
             
                    end
         | 
| 566 581 | 
             
                  end
         | 
| 567 582 |  | 
| @@ -696,6 +711,14 @@ module ActiveRecord | |
| 696 711 | 
             
                    Thread.pass
         | 
| 697 712 | 
             
                  end
         | 
| 698 713 |  | 
| 714 | 
            +
                  def new_connection # :nodoc:
         | 
| 715 | 
            +
                    connection = db_config.new_connection
         | 
| 716 | 
            +
                    connection.pool = self
         | 
| 717 | 
            +
                    connection
         | 
| 718 | 
            +
                  rescue ConnectionNotEstablished => ex
         | 
| 719 | 
            +
                    raise ex.set_pool(self)
         | 
| 720 | 
            +
                  end
         | 
| 721 | 
            +
             | 
| 699 722 | 
             
                  private
         | 
| 700 723 | 
             
                    def connection_lease
         | 
| 701 724 | 
             
                      @leases[ActiveSupport::IsolatedExecutionState.context]
         | 
| @@ -875,18 +898,12 @@ module ActiveRecord | |
| 875 898 | 
             
                    #--
         | 
| 876 899 | 
             
                    # if owner_thread param is omitted, this must be called in synchronize block
         | 
| 877 900 | 
             
                    def remove_connection_from_thread_cache(conn, owner_thread = conn.owner)
         | 
| 878 | 
            -
                       | 
| 901 | 
            +
                      if owner_thread
         | 
| 902 | 
            +
                        @leases[owner_thread].clear(conn)
         | 
| 903 | 
            +
                      end
         | 
| 879 904 | 
             
                    end
         | 
| 880 905 | 
             
                    alias_method :release, :remove_connection_from_thread_cache
         | 
| 881 906 |  | 
| 882 | 
            -
                    def new_connection
         | 
| 883 | 
            -
                      connection = db_config.new_connection
         | 
| 884 | 
            -
                      connection.pool = self
         | 
| 885 | 
            -
                      connection
         | 
| 886 | 
            -
                    rescue ConnectionNotEstablished => ex
         | 
| 887 | 
            -
                      raise ex.set_pool(self)
         | 
| 888 | 
            -
                    end
         | 
| 889 | 
            -
             | 
| 890 907 | 
             
                    # If the pool is not at a <tt>@size</tt> limit, establish new connection. Connecting
         | 
| 891 908 | 
             
                    # to the DB is done outside main synchronized section.
         | 
| 892 909 | 
             
                    #--
         | 
| @@ -191,15 +191,26 @@ module ActiveRecord | |
| 191 191 | 
             
                    end
         | 
| 192 192 | 
             
                  end
         | 
| 193 193 |  | 
| 194 | 
            -
                  attr_accessor :query_cache
         | 
| 195 | 
            -
             | 
| 196 194 | 
             
                  def initialize(*)
         | 
| 197 195 | 
             
                    super
         | 
| 198 196 | 
             
                    @query_cache = nil
         | 
| 199 197 | 
             
                  end
         | 
| 200 198 |  | 
| 199 | 
            +
                  attr_writer :query_cache
         | 
| 200 | 
            +
             | 
| 201 | 
            +
                  def query_cache
         | 
| 202 | 
            +
                    if @pinned && @owner != ActiveSupport::IsolatedExecutionState.context
         | 
| 203 | 
            +
                      # With transactional tests, if the connection is pinned, any thread
         | 
| 204 | 
            +
                      # other than the one that pinned the connection need to go through the
         | 
| 205 | 
            +
                      # query cache pool, so each thread get a different cache.
         | 
| 206 | 
            +
                      pool.query_cache
         | 
| 207 | 
            +
                    else
         | 
| 208 | 
            +
                      @query_cache
         | 
| 209 | 
            +
                    end
         | 
| 210 | 
            +
                  end
         | 
| 211 | 
            +
             | 
| 201 212 | 
             
                  def query_cache_enabled
         | 
| 202 | 
            -
                     | 
| 213 | 
            +
                    query_cache&.enabled?
         | 
| 203 214 | 
             
                  end
         | 
| 204 215 |  | 
| 205 216 | 
             
                  # Enable the query cache within the block.
         | 
| @@ -238,7 +249,7 @@ module ActiveRecord | |
| 238 249 |  | 
| 239 250 | 
             
                    # If arel is locked this is a SELECT ... FOR UPDATE or somesuch.
         | 
| 240 251 | 
             
                    # Such queries should not be cached.
         | 
| 241 | 
            -
                    if  | 
| 252 | 
            +
                    if query_cache_enabled && !(arel.respond_to?(:locked) && arel.locked)
         | 
| 242 253 | 
             
                      sql, binds, preparable, allow_retry = to_sql_and_binds(arel, binds, preparable)
         | 
| 243 254 |  | 
| 244 255 | 
             
                      if async
         | 
| @@ -262,7 +273,7 @@ module ActiveRecord | |
| 262 273 |  | 
| 263 274 | 
             
                      result = nil
         | 
| 264 275 | 
             
                      @lock.synchronize do
         | 
| 265 | 
            -
                        result =  | 
| 276 | 
            +
                        result = query_cache[key]
         | 
| 266 277 | 
             
                      end
         | 
| 267 278 |  | 
| 268 279 | 
             
                      if result
         | 
| @@ -281,7 +292,7 @@ module ActiveRecord | |
| 281 292 | 
             
                      hit = true
         | 
| 282 293 |  | 
| 283 294 | 
             
                      @lock.synchronize do
         | 
| 284 | 
            -
                        result =  | 
| 295 | 
            +
                        result = query_cache.compute_if_absent(key) do
         | 
| 285 296 | 
             
                          hit = false
         | 
| 286 297 | 
             
                          yield
         | 
| 287 298 | 
             
                        end
         | 
| @@ -160,6 +160,8 @@ module ActiveRecord | |
| 160 160 | 
             
                  end
         | 
| 161 161 |  | 
| 162 162 | 
             
                  def defined_for?(to_table: nil, validate: nil, **options)
         | 
| 163 | 
            +
                    options = options.slice(*self.options.keys)
         | 
| 164 | 
            +
             | 
| 163 165 | 
             
                    (to_table.nil? || to_table.to_s == self.to_table) &&
         | 
| 164 166 | 
             
                      (validate.nil? || validate == self.options.fetch(:validate, validate)) &&
         | 
| 165 167 | 
             
                      options.all? { |k, v| Array(self.options[k]).map(&:to_s) == Array(v).map(&:to_s) }
         | 
| @@ -186,6 +188,8 @@ module ActiveRecord | |
| 186 188 | 
             
                  end
         | 
| 187 189 |  | 
| 188 190 | 
             
                  def defined_for?(name:, expression: nil, validate: nil, **options)
         | 
| 191 | 
            +
                    options = options.slice(*self.options.keys)
         | 
| 192 | 
            +
             | 
| 189 193 | 
             
                    self.name == name.to_s &&
         | 
| 190 194 | 
             
                      (validate.nil? || validate == self.options.fetch(:validate, validate)) &&
         | 
| 191 195 | 
             
                      options.all? { |k, v| self.options[k].to_s == v.to_s }
         | 
| @@ -431,7 +435,7 @@ module ActiveRecord | |
| 431 435 | 
             
                  #
         | 
| 432 436 | 
             
                  # == Examples
         | 
| 433 437 | 
             
                  #
         | 
| 434 | 
            -
                  #  # Assuming  | 
| 438 | 
            +
                  #  # Assuming `td` is an instance of TableDefinition
         | 
| 435 439 | 
             
                  #  td.column(:granted, :boolean, index: true)
         | 
| 436 440 | 
             
                  #
         | 
| 437 441 | 
             
                  # == Short-hand examples
         | 
| @@ -186,6 +186,9 @@ module ActiveRecord | |
| 186 186 | 
             
                  #   Join tables for {ActiveRecord::Base.has_and_belongs_to_many}[rdoc-ref:Associations::ClassMethods#has_and_belongs_to_many] should set it to false.
         | 
| 187 187 | 
             
                  #
         | 
| 188 188 | 
             
                  #   A Symbol can be used to specify the type of the generated primary key column.
         | 
| 189 | 
            +
                  #
         | 
| 190 | 
            +
                  #   A Hash can be used to specify the generated primary key column creation options.
         | 
| 191 | 
            +
                  #   See {add_column}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_column] for available options.
         | 
| 189 192 | 
             
                  # [<tt>:primary_key</tt>]
         | 
| 190 193 | 
             
                  #   The name of the primary key, if one is to be added automatically.
         | 
| 191 194 | 
             
                  #   Defaults to +id+. If <tt>:id</tt> is false, then this option is ignored.
         | 
| @@ -43,6 +43,7 @@ module ActiveRecord | |
| 43 43 |  | 
| 44 44 | 
             
                  attr_reader :pool
         | 
| 45 45 | 
             
                  attr_reader :visitor, :owner, :logger, :lock
         | 
| 46 | 
            +
                  attr_accessor :pinned # :nodoc:
         | 
| 46 47 | 
             
                  alias :in_use? :owner
         | 
| 47 48 |  | 
| 48 49 | 
             
                  def pool=(value)
         | 
| @@ -151,6 +152,7 @@ module ActiveRecord | |
| 151 152 | 
             
                    end
         | 
| 152 153 |  | 
| 153 154 | 
             
                    @owner = nil
         | 
| 155 | 
            +
                    @pinned = false
         | 
| 154 156 | 
             
                    @instrumenter = ActiveSupport::Notifications.instrumenter
         | 
| 155 157 | 
             
                    @pool = ActiveRecord::ConnectionAdapters::NullPool.new
         | 
| 156 158 | 
             
                    @idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
         | 
| @@ -169,6 +171,7 @@ module ActiveRecord | |
| 169 171 | 
             
                    @default_timezone = self.class.validate_default_timezone(@config[:default_timezone])
         | 
| 170 172 |  | 
| 171 173 | 
             
                    @raw_connection_dirty = false
         | 
| 174 | 
            +
                    @last_activity = nil
         | 
| 172 175 | 
             
                    @verified = false
         | 
| 173 176 | 
             
                  end
         | 
| 174 177 |  | 
| @@ -218,6 +221,10 @@ module ActiveRecord | |
| 218 221 | 
             
                    (@config[:connection_retries] || 1).to_i
         | 
| 219 222 | 
             
                  end
         | 
| 220 223 |  | 
| 224 | 
            +
                  def verify_timeout
         | 
| 225 | 
            +
                    (@config[:verify_timeout] || 2).to_i
         | 
| 226 | 
            +
                  end
         | 
| 227 | 
            +
             | 
| 221 228 | 
             
                  def retry_deadline
         | 
| 222 229 | 
             
                    if @config[:retry_deadline]
         | 
| 223 230 | 
             
                      @config[:retry_deadline].to_f
         | 
| @@ -344,6 +351,13 @@ module ActiveRecord | |
| 344 351 | 
             
                    Process.clock_gettime(Process::CLOCK_MONOTONIC) - @idle_since
         | 
| 345 352 | 
             
                  end
         | 
| 346 353 |  | 
| 354 | 
            +
                  # Seconds since this connection last communicated with the server
         | 
| 355 | 
            +
                  def seconds_since_last_activity # :nodoc:
         | 
| 356 | 
            +
                    if @raw_connection && @last_activity
         | 
| 357 | 
            +
                      Process.clock_gettime(Process::CLOCK_MONOTONIC) - @last_activity
         | 
| 358 | 
            +
                    end
         | 
| 359 | 
            +
                  end
         | 
| 360 | 
            +
             | 
| 347 361 | 
             
                  def unprepared_statement
         | 
| 348 362 | 
             
                    cache = prepared_statements_disabled_cache.add?(object_id) if @prepared_statements
         | 
| 349 363 | 
             
                    yield
         | 
| @@ -663,11 +677,12 @@ module ActiveRecord | |
| 663 677 |  | 
| 664 678 | 
             
                      enable_lazy_transactions!
         | 
| 665 679 | 
             
                      @raw_connection_dirty = false
         | 
| 680 | 
            +
                      @last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
         | 
| 666 681 | 
             
                      @verified = true
         | 
| 667 682 |  | 
| 668 683 | 
             
                      reset_transaction(restore: restore_transactions) do
         | 
| 669 684 | 
             
                        clear_cache!(new_connection: true)
         | 
| 670 | 
            -
                         | 
| 685 | 
            +
                        attempt_configure_connection
         | 
| 671 686 | 
             
                      end
         | 
| 672 687 | 
             
                    rescue => original_exception
         | 
| 673 688 | 
             
                      translated_exception = translate_exception_class(original_exception, nil, nil)
         | 
| @@ -682,6 +697,7 @@ module ActiveRecord | |
| 682 697 | 
             
                        end
         | 
| 683 698 | 
             
                      end
         | 
| 684 699 |  | 
| 700 | 
            +
                      @last_activity = nil
         | 
| 685 701 | 
             
                      @verified = false
         | 
| 686 702 |  | 
| 687 703 | 
             
                      raise translated_exception
         | 
| @@ -719,7 +735,7 @@ module ActiveRecord | |
| 719 735 | 
             
                  def reset!
         | 
| 720 736 | 
             
                    clear_cache!(new_connection: true)
         | 
| 721 737 | 
             
                    reset_transaction
         | 
| 722 | 
            -
                     | 
| 738 | 
            +
                    attempt_configure_connection
         | 
| 723 739 | 
             
                  end
         | 
| 724 740 |  | 
| 725 741 | 
             
                  # Removes the connection from the pool and disconnect it.
         | 
| @@ -755,7 +771,8 @@ module ActiveRecord | |
| 755 771 | 
             
                        if @unconfigured_connection
         | 
| 756 772 | 
             
                          @raw_connection = @unconfigured_connection
         | 
| 757 773 | 
             
                          @unconfigured_connection = nil
         | 
| 758 | 
            -
                           | 
| 774 | 
            +
                          attempt_configure_connection
         | 
| 775 | 
            +
                          @last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
         | 
| 759 776 | 
             
                          @verified = true
         | 
| 760 777 | 
             
                          return
         | 
| 761 778 | 
             
                        end
         | 
| @@ -985,6 +1002,9 @@ module ActiveRecord | |
| 985 1002 | 
             
                        if @verified
         | 
| 986 1003 | 
             
                          # Cool, we're confident the connection's ready to use. (Note this might have
         | 
| 987 1004 | 
             
                          # become true during the above #materialize_transactions.)
         | 
| 1005 | 
            +
                        elsif (last_activity = seconds_since_last_activity) && last_activity < verify_timeout
         | 
| 1006 | 
            +
                          # We haven't actually verified the connection since we acquired it, but it
         | 
| 1007 | 
            +
                          # has been used very recently. We're going to assume it's still okay.
         | 
| 988 1008 | 
             
                        elsif reconnectable
         | 
| 989 1009 | 
             
                          if allow_retry
         | 
| 990 1010 | 
             
                            # Not sure about the connection yet, but if anything goes wrong we can
         | 
| @@ -1026,6 +1046,7 @@ module ActiveRecord | |
| 1026 1046 | 
             
                            # Barring a known-retryable error inside the query (regardless of
         | 
| 1027 1047 | 
             
                            # whether we were in a _position_ to retry it), we should infer that
         | 
| 1028 1048 | 
             
                            # there's likely a real problem with the connection.
         | 
| 1049 | 
            +
                            @last_activity = nil
         | 
| 1029 1050 | 
             
                            @verified = false
         | 
| 1030 1051 | 
             
                          end
         | 
| 1031 1052 |  | 
| @@ -1040,6 +1061,7 @@ module ActiveRecord | |
| 1040 1061 | 
             
                    # `with_raw_connection` block only when the block is guaranteed to
         | 
| 1041 1062 | 
             
                    # exercise the raw connection.
         | 
| 1042 1063 | 
             
                    def verified!
         | 
| 1064 | 
            +
                      @last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
         | 
| 1043 1065 | 
             
                      @verified = true
         | 
| 1044 1066 | 
             
                    end
         | 
| 1045 1067 |  | 
| @@ -1205,6 +1227,13 @@ module ActiveRecord | |
| 1205 1227 | 
             
                      check_version
         | 
| 1206 1228 | 
             
                    end
         | 
| 1207 1229 |  | 
| 1230 | 
            +
                    def attempt_configure_connection
         | 
| 1231 | 
            +
                      configure_connection
         | 
| 1232 | 
            +
                    rescue Exception # Need to handle things such as Timeout::ExitException
         | 
| 1233 | 
            +
                      disconnect!
         | 
| 1234 | 
            +
                      raise
         | 
| 1235 | 
            +
                    end
         | 
| 1236 | 
            +
             | 
| 1208 1237 | 
             
                    def default_prepared_statements
         | 
| 1209 1238 | 
             
                      true
         | 
| 1210 1239 | 
             
                    end
         | 
| @@ -174,6 +174,10 @@ module ActiveRecord | |
| 174 174 | 
             
                    mariadb? && database_version >= "10.5.0"
         | 
| 175 175 | 
             
                  end
         | 
| 176 176 |  | 
| 177 | 
            +
                  def return_value_after_insert?(column) # :nodoc:
         | 
| 178 | 
            +
                    supports_insert_returning? ? column.auto_populated? : column.auto_increment?
         | 
| 179 | 
            +
                  end
         | 
| 180 | 
            +
             | 
| 177 181 | 
             
                  def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
         | 
| 178 182 | 
             
                    query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
         | 
| 179 183 | 
             
                  end
         | 
| @@ -414,7 +418,11 @@ module ActiveRecord | |
| 414 418 | 
             
                    type ||= column.sql_type
         | 
| 415 419 |  | 
| 416 420 | 
             
                    unless options.key?(:default)
         | 
| 417 | 
            -
                      options[:default] = column. | 
| 421 | 
            +
                      options[:default] = if column.default_function
         | 
| 422 | 
            +
                        -> { column.default_function }
         | 
| 423 | 
            +
                      else
         | 
| 424 | 
            +
                        column.default
         | 
| 425 | 
            +
                      end
         | 
| 418 426 | 
             
                    end
         | 
| 419 427 |  | 
| 420 428 | 
             
                    unless options.key?(:null)
         | 
| @@ -639,24 +647,26 @@ module ActiveRecord | |
| 639 647 | 
             
                  end
         | 
| 640 648 |  | 
| 641 649 | 
             
                  def build_insert_sql(insert) # :nodoc:
         | 
| 650 | 
            +
                    # Can use any column as it will be assigned to itself.
         | 
| 642 651 | 
             
                    no_op_column = quote_column_name(insert.keys.first) if insert.keys.first
         | 
| 643 652 |  | 
| 644 653 | 
             
                    # MySQL 8.0.19 replaces `VALUES(<expression>)` clauses with row and column alias names, see https://dev.mysql.com/worklog/task/?id=6312 .
         | 
| 645 654 | 
             
                    # then MySQL 8.0.20 deprecates the `VALUES(<expression>)` see https://dev.mysql.com/worklog/task/?id=13325 .
         | 
| 646 655 | 
             
                    if supports_insert_raw_alias_syntax?
         | 
| 656 | 
            +
                      quoted_table_name = insert.model.quoted_table_name
         | 
| 647 657 | 
             
                      values_alias = quote_table_name("#{insert.model.table_name.parameterize}_values")
         | 
| 648 658 | 
             
                      sql = +"INSERT #{insert.into} #{insert.values_list} AS #{values_alias}"
         | 
| 649 659 |  | 
| 650 660 | 
             
                      if insert.skip_duplicates?
         | 
| 651 661 | 
             
                        if no_op_column
         | 
| 652 | 
            -
                          sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{ | 
| 662 | 
            +
                          sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{quoted_table_name}.#{no_op_column}"
         | 
| 653 663 | 
             
                        end
         | 
| 654 664 | 
             
                      elsif insert.update_duplicates?
         | 
| 655 665 | 
             
                        if insert.raw_update_sql?
         | 
| 656 666 | 
             
                          sql = +"INSERT #{insert.into} #{insert.values_list} ON DUPLICATE KEY UPDATE #{insert.raw_update_sql}"
         | 
| 657 667 | 
             
                        else
         | 
| 658 668 | 
             
                          sql << " ON DUPLICATE KEY UPDATE "
         | 
| 659 | 
            -
                          sql << insert.touch_model_timestamps_unless { |column| "#{ | 
| 669 | 
            +
                          sql << insert.touch_model_timestamps_unless { |column| "#{quoted_table_name}.#{column}<=>#{values_alias}.#{column}" }
         | 
| 660 670 | 
             
                          sql << insert.updatable_columns.map { |column| "#{column}=#{values_alias}.#{column}" }.join(",")
         | 
| 661 671 | 
             
                        end
         | 
| 662 672 | 
             
                      end
         | 
| @@ -757,9 +767,7 @@ module ActiveRecord | |
| 757 767 |  | 
| 758 768 | 
             
                  private
         | 
| 759 769 | 
             
                    def strip_whitespace_characters(expression)
         | 
| 760 | 
            -
                      expression | 
| 761 | 
            -
                      expression = expression.gsub(/\s{2,}/, " ")
         | 
| 762 | 
            -
                      expression
         | 
| 770 | 
            +
                      expression.gsub('\\\n', "").gsub("x0A", "").squish
         | 
| 763 771 | 
             
                    end
         | 
| 764 772 |  | 
| 765 773 | 
             
                    def extended_type_map_key
         | 
| @@ -110,7 +110,13 @@ module ActiveRecord | |
| 110 110 | 
             
                        else
         | 
| 111 111 | 
             
                          value.getlocal
         | 
| 112 112 | 
             
                        end
         | 
| 113 | 
            -
                      when  | 
| 113 | 
            +
                      when Time
         | 
| 114 | 
            +
                        if default_timezone == :utc
         | 
| 115 | 
            +
                          value.utc? ? value : value.getutc
         | 
| 116 | 
            +
                        else
         | 
| 117 | 
            +
                          value.utc? ? value.getlocal : value
         | 
| 118 | 
            +
                        end
         | 
| 119 | 
            +
                      when Date
         | 
| 114 120 | 
             
                        value
         | 
| 115 121 | 
             
                      else
         | 
| 116 122 | 
             
                        super
         | 
| @@ -23,7 +23,7 @@ module ActiveRecord | |
| 23 23 | 
             
                          end
         | 
| 24 24 | 
             
                        end
         | 
| 25 25 | 
             
                      else
         | 
| 26 | 
            -
                        exec_stmt_and_free(sql, name, binds, cache_stmt: prepare, async: async) do |_, result|
         | 
| 26 | 
            +
                        exec_stmt_and_free(sql, name, binds, cache_stmt: prepare, async: async, allow_retry: allow_retry) do |_, result|
         | 
| 27 27 | 
             
                          if result
         | 
| 28 28 | 
             
                            build_result(columns: result.fields, rows: result.to_a)
         | 
| 29 29 | 
             
                          else
         | 
| @@ -105,7 +105,7 @@ module ActiveRecord | |
| 105 105 | 
             
                        end
         | 
| 106 106 | 
             
                      end
         | 
| 107 107 |  | 
| 108 | 
            -
                      def exec_stmt_and_free(sql, name, binds, cache_stmt: false, async: false)
         | 
| 108 | 
            +
                      def exec_stmt_and_free(sql, name, binds, cache_stmt: false, async: false, allow_retry: false)
         | 
| 109 109 | 
             
                        sql = transform_query(sql)
         | 
| 110 110 | 
             
                        check_if_write_query(sql)
         | 
| 111 111 |  | 
| @@ -114,7 +114,7 @@ module ActiveRecord | |
| 114 114 | 
             
                        type_casted_binds = type_casted_binds(binds)
         | 
| 115 115 |  | 
| 116 116 | 
             
                        log(sql, name, binds, type_casted_binds, async: async) do |notification_payload|
         | 
| 117 | 
            -
                          with_raw_connection do |conn|
         | 
| 117 | 
            +
                          with_raw_connection(allow_retry: allow_retry) do |conn|
         | 
| 118 118 | 
             
                            sync_timezone_changes(conn)
         | 
| 119 119 |  | 
| 120 120 | 
             
                            if cache_stmt
         | 
| @@ -113,7 +113,14 @@ module ActiveRecord | |
| 113 113 | 
             
                  end
         | 
| 114 114 |  | 
| 115 115 | 
             
                  def active?
         | 
| 116 | 
            -
                    connected? | 
| 116 | 
            +
                    if connected?
         | 
| 117 | 
            +
                      @lock.synchronize do
         | 
| 118 | 
            +
                        if @raw_connection&.ping
         | 
| 119 | 
            +
                          verified!
         | 
| 120 | 
            +
                          true
         | 
| 121 | 
            +
                        end
         | 
| 122 | 
            +
                      end
         | 
| 123 | 
            +
                    end || false
         | 
| 117 124 | 
             
                  end
         | 
| 118 125 |  | 
| 119 126 | 
             
                  alias :reset! :reconnect!
         |