activerecord 6.1.4.1 → 7.0.1
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.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1132 -936
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/lib/active_record/aggregations.rb +1 -1
- data/lib/active_record/association_relation.rb +0 -10
- data/lib/active_record/associations/association.rb +33 -17
- data/lib/active_record/associations/association_scope.rb +1 -3
- data/lib/active_record/associations/belongs_to_association.rb +15 -4
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
- data/lib/active_record/associations/builder/association.rb +8 -2
- data/lib/active_record/associations/builder/belongs_to.rb +19 -6
- data/lib/active_record/associations/builder/collection_association.rb +10 -3
- data/lib/active_record/associations/builder/has_many.rb +3 -2
- data/lib/active_record/associations/builder/has_one.rb +2 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -2
- data/lib/active_record/associations/collection_association.rb +34 -27
- data/lib/active_record/associations/collection_proxy.rb +8 -3
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +2 -1
- data/lib/active_record/associations/has_one_association.rb +10 -7
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +6 -2
- data/lib/active_record/associations/preloader/association.rb +187 -55
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +147 -0
- data/lib/active_record/associations/preloader/through_association.rb +49 -13
- data/lib/active_record/associations/preloader.rb +39 -113
- data/lib/active_record/associations/singular_association.rb +8 -2
- data/lib/active_record/associations/through_association.rb +3 -3
- data/lib/active_record/associations.rb +118 -90
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +1 -1
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
- data/lib/active_record/attribute_methods/dirty.rb +49 -16
- data/lib/active_record/attribute_methods/primary_key.rb +2 -2
- data/lib/active_record/attribute_methods/query.rb +2 -2
- data/lib/active_record/attribute_methods/read.rb +7 -5
- data/lib/active_record/attribute_methods/serialization.rb +66 -12
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
- data/lib/active_record/attribute_methods/write.rb +7 -10
- data/lib/active_record/attribute_methods.rb +13 -14
- data/lib/active_record/attributes.rb +24 -35
- data/lib/active_record/autosave_association.rb +6 -21
- data/lib/active_record/base.rb +19 -1
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +47 -561
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +46 -22
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
- data/lib/active_record/connection_adapters/abstract/quoting.rb +42 -72
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +34 -9
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +78 -22
- data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
- data/lib/active_record/connection_adapters/abstract_adapter.rb +149 -74
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +97 -81
- data/lib/active_record/connection_adapters/column.rb +4 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +37 -23
- data/lib/active_record/connection_adapters/mysql/quoting.rb +35 -21
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +5 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
- data/lib/active_record/connection_adapters/pool_config.rb +7 -7
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +21 -12
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +50 -50
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +29 -18
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +205 -105
- data/lib/active_record/connection_adapters/schema_cache.rb +29 -4
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +27 -19
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +28 -16
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +16 -14
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +89 -30
- data/lib/active_record/connection_adapters.rb +6 -5
- data/lib/active_record/connection_handling.rb +47 -53
- data/lib/active_record/core.rb +122 -132
- data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
- data/lib/active_record/database_configurations/database_config.rb +12 -9
- data/lib/active_record/database_configurations/hash_config.rb +63 -5
- data/lib/active_record/database_configurations/url_config.rb +2 -2
- data/lib/active_record/database_configurations.rb +16 -32
- data/lib/active_record/delegated_type.rb +52 -11
- data/lib/active_record/destroy_association_async_job.rb +1 -1
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +44 -0
- data/lib/active_record/encryption/configurable.rb +61 -0
- data/lib/active_record/encryption/context.rb +35 -0
- data/lib/active_record/encryption/contexts.rb +72 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +208 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +155 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +42 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_serializer.rb +90 -0
- data/lib/active_record/encryption/null_encryptor.rb +21 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
- data/lib/active_record/encryption/scheme.rb +99 -0
- data/lib/active_record/encryption.rb +55 -0
- data/lib/active_record/enum.rb +49 -42
- data/lib/active_record/errors.rb +67 -4
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/fixture_set/file.rb +15 -1
- data/lib/active_record/fixture_set/table_row.rb +41 -6
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +17 -20
- data/lib/active_record/future_result.rb +139 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +55 -17
- data/lib/active_record/insert_all.rb +80 -14
- data/lib/active_record/integration.rb +4 -3
- data/lib/active_record/internal_metadata.rb +3 -5
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +10 -9
- data/lib/active_record/locking/pessimistic.rb +9 -3
- data/lib/active_record/log_subscriber.rb +14 -3
- data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
- data/lib/active_record/middleware/database_selector.rb +8 -3
- data/lib/active_record/middleware/shard_selector.rb +60 -0
- data/lib/active_record/migration/command_recorder.rb +4 -4
- data/lib/active_record/migration/compatibility.rb +107 -3
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +109 -79
- data/lib/active_record/model_schema.rb +45 -58
- data/lib/active_record/nested_attributes.rb +13 -12
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/null_relation.rb +2 -6
- data/lib/active_record/persistence.rb +219 -52
- data/lib/active_record/query_cache.rb +2 -2
- data/lib/active_record/query_logs.rb +138 -0
- data/lib/active_record/querying.rb +15 -5
- data/lib/active_record/railtie.rb +127 -17
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +66 -129
- data/lib/active_record/readonly_attributes.rb +11 -0
- data/lib/active_record/reflection.rb +67 -50
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +3 -3
- data/lib/active_record/relation/calculations.rb +43 -38
- data/lib/active_record/relation/delegation.rb +6 -6
- data/lib/active_record/relation/finder_methods.rb +31 -35
- data/lib/active_record/relation/merger.rb +20 -13
- data/lib/active_record/relation/predicate_builder.rb +1 -6
- data/lib/active_record/relation/query_attribute.rb +5 -11
- data/lib/active_record/relation/query_methods.rb +243 -61
- data/lib/active_record/relation/record_fetch_warning.rb +7 -9
- data/lib/active_record/relation/spawn_methods.rb +2 -2
- data/lib/active_record/relation/where_clause.rb +10 -19
- data/lib/active_record/relation.rb +184 -84
- data/lib/active_record/result.rb +17 -7
- data/lib/active_record/runtime_registry.rb +9 -13
- data/lib/active_record/sanitization.rb +11 -7
- data/lib/active_record/schema_dumper.rb +10 -3
- data/lib/active_record/schema_migration.rb +4 -4
- data/lib/active_record/scoping/default.rb +61 -12
- data/lib/active_record/scoping/named.rb +3 -11
- data/lib/active_record/scoping.rb +64 -34
- data/lib/active_record/serialization.rb +1 -1
- data/lib/active_record/signed_id.rb +1 -1
- data/lib/active_record/suppressor.rb +11 -15
- data/lib/active_record/tasks/database_tasks.rb +120 -58
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -12
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +4 -4
- data/lib/active_record/timestamp.rb +3 -4
- data/lib/active_record/transactions.rb +9 -14
- data/lib/active_record/translation.rb +2 -2
- data/lib/active_record/type/adapter_specific_registry.rb +32 -7
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
- data/lib/active_record/type/internal/timezone.rb +2 -2
- data/lib/active_record/type/serialized.rb +1 -1
- data/lib/active_record/type/type_map.rb +17 -20
- data/lib/active_record/type.rb +1 -2
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record/validations/uniqueness.rb +1 -1
- data/lib/active_record.rb +204 -28
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/crud.rb +28 -22
- data/lib/arel/delete_manager.rb +18 -4
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/delete_statement.rb +12 -13
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/function.rb +1 -0
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- data/lib/arel/nodes/update_statement.rb +8 -3
- data/lib/arel/nodes.rb +1 -0
- data/lib/arel/predications.rb +11 -3
- data/lib/arel/select_manager.rb +10 -4
- data/lib/arel/table.rb +0 -1
- data/lib/arel/tree_manager.rb +0 -12
- data/lib/arel/update_manager.rb +18 -4
- data/lib/arel/visitors/dot.rb +80 -90
- data/lib/arel/visitors/mysql.rb +8 -2
- data/lib/arel/visitors/postgresql.rb +0 -10
- data/lib/arel/visitors/to_sql.rb +58 -2
- data/lib/arel.rb +2 -1
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- metadata +59 -14
| @@ -0,0 +1,139 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module ActiveRecord
         | 
| 4 | 
            +
              class FutureResult # :nodoc:
         | 
| 5 | 
            +
                class EventBuffer
         | 
| 6 | 
            +
                  def initialize(future_result, instrumenter)
         | 
| 7 | 
            +
                    @future_result = future_result
         | 
| 8 | 
            +
                    @instrumenter = instrumenter
         | 
| 9 | 
            +
                    @events = []
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  def instrument(name, payload = {}, &block)
         | 
| 13 | 
            +
                    event = @instrumenter.new_event(name, payload)
         | 
| 14 | 
            +
                    @events << event
         | 
| 15 | 
            +
                    event.record(&block)
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  def flush
         | 
| 19 | 
            +
                    events, @events = @events, []
         | 
| 20 | 
            +
                    events.each do |event|
         | 
| 21 | 
            +
                      event.payload[:lock_wait] = @future_result.lock_wait
         | 
| 22 | 
            +
                      ActiveSupport::Notifications.publish_event(event)
         | 
| 23 | 
            +
                    end
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                Canceled = Class.new(ActiveRecordError)
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                delegate :empty?, :to_a, to: :result
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                attr_reader :lock_wait
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                def initialize(pool, *args, **kwargs)
         | 
| 34 | 
            +
                  @mutex = Mutex.new
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  @session = nil
         | 
| 37 | 
            +
                  @pool = pool
         | 
| 38 | 
            +
                  @args = args
         | 
| 39 | 
            +
                  @kwargs = kwargs
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                  @pending = true
         | 
| 42 | 
            +
                  @error = nil
         | 
| 43 | 
            +
                  @result = nil
         | 
| 44 | 
            +
                  @instrumenter = ActiveSupport::Notifications.instrumenter
         | 
| 45 | 
            +
                  @event_buffer = nil
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                def schedule!(session)
         | 
| 49 | 
            +
                  @session = session
         | 
| 50 | 
            +
                  @pool.schedule_query(self)
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                def execute!(connection)
         | 
| 54 | 
            +
                  execute_query(connection)
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                def cancel
         | 
| 58 | 
            +
                  @pending = false
         | 
| 59 | 
            +
                  @error = Canceled
         | 
| 60 | 
            +
                  self
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                def execute_or_skip
         | 
| 64 | 
            +
                  return unless pending?
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                  @pool.with_connection do |connection|
         | 
| 67 | 
            +
                    return unless @mutex.try_lock
         | 
| 68 | 
            +
                    begin
         | 
| 69 | 
            +
                      if pending?
         | 
| 70 | 
            +
                        @event_buffer = EventBuffer.new(self, @instrumenter)
         | 
| 71 | 
            +
                        connection.with_instrumenter(@event_buffer) do
         | 
| 72 | 
            +
                          execute_query(connection, async: true)
         | 
| 73 | 
            +
                        end
         | 
| 74 | 
            +
                      end
         | 
| 75 | 
            +
                    ensure
         | 
| 76 | 
            +
                      @mutex.unlock
         | 
| 77 | 
            +
                    end
         | 
| 78 | 
            +
                  end
         | 
| 79 | 
            +
                end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                def result
         | 
| 82 | 
            +
                  execute_or_wait
         | 
| 83 | 
            +
                  @event_buffer&.flush
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                  if canceled?
         | 
| 86 | 
            +
                    raise Canceled
         | 
| 87 | 
            +
                  elsif @error
         | 
| 88 | 
            +
                    raise @error
         | 
| 89 | 
            +
                  else
         | 
| 90 | 
            +
                    @result
         | 
| 91 | 
            +
                  end
         | 
| 92 | 
            +
                end
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                def pending?
         | 
| 95 | 
            +
                  @pending && (!@session || @session.active?)
         | 
| 96 | 
            +
                end
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                private
         | 
| 99 | 
            +
                  def canceled?
         | 
| 100 | 
            +
                    @session && !@session.active?
         | 
| 101 | 
            +
                  end
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                  def execute_or_wait
         | 
| 104 | 
            +
                    if pending?
         | 
| 105 | 
            +
                      start = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)
         | 
| 106 | 
            +
                      @mutex.synchronize do
         | 
| 107 | 
            +
                        if pending?
         | 
| 108 | 
            +
                          execute_query(@pool.connection)
         | 
| 109 | 
            +
                        else
         | 
| 110 | 
            +
                          @lock_wait = (Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond) - start)
         | 
| 111 | 
            +
                        end
         | 
| 112 | 
            +
                      end
         | 
| 113 | 
            +
                    else
         | 
| 114 | 
            +
                      @lock_wait = 0.0
         | 
| 115 | 
            +
                    end
         | 
| 116 | 
            +
                  end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                  def execute_query(connection, async: false)
         | 
| 119 | 
            +
                    @result = exec_query(connection, *@args, **@kwargs, async: async)
         | 
| 120 | 
            +
                  rescue => error
         | 
| 121 | 
            +
                    @error = error
         | 
| 122 | 
            +
                  ensure
         | 
| 123 | 
            +
                    @pending = false
         | 
| 124 | 
            +
                  end
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                  def exec_query(connection, *args, **kwargs)
         | 
| 127 | 
            +
                    connection.exec_query(*args, **kwargs)
         | 
| 128 | 
            +
                  end
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                  class SelectAll < FutureResult # :nodoc:
         | 
| 131 | 
            +
                    private
         | 
| 132 | 
            +
                      def exec_query(*, **)
         | 
| 133 | 
            +
                        super
         | 
| 134 | 
            +
                      rescue ::RangeError
         | 
| 135 | 
            +
                        ActiveRecord::Result.empty
         | 
| 136 | 
            +
                      end
         | 
| 137 | 
            +
                  end
         | 
| 138 | 
            +
              end
         | 
| 139 | 
            +
            end
         | 
| @@ -1,5 +1,6 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            +
            require "active_support/inflector"
         | 
| 3 4 | 
             
            require "active_support/core_ext/hash/indifferent_access"
         | 
| 4 5 |  | 
| 5 6 | 
             
            module ActiveRecord
         | 
| @@ -43,6 +44,8 @@ module ActiveRecord | |
| 43 44 | 
             
                  # Determines whether to store the full constant name including namespace when using STI.
         | 
| 44 45 | 
             
                  # This is true, by default.
         | 
| 45 46 | 
             
                  class_attribute :store_full_sti_class, instance_writer: false, default: true
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  set_base_class
         | 
| 46 49 | 
             
                end
         | 
| 47 50 |  | 
| 48 51 | 
             
                module ClassMethods
         | 
| @@ -85,7 +88,7 @@ module ActiveRecord | |
| 85 88 | 
             
                    end
         | 
| 86 89 | 
             
                  end
         | 
| 87 90 |  | 
| 88 | 
            -
                  def finder_needs_type_condition?  | 
| 91 | 
            +
                  def finder_needs_type_condition? # :nodoc:
         | 
| 89 92 | 
             
                    # This is like this because benchmarking justifies the strange :false stuff
         | 
| 90 93 | 
             
                    :true == (@finder_needs_type_condition ||= descends_from_active_record? ? :false : :true)
         | 
| 91 94 | 
             
                  end
         | 
| @@ -98,17 +101,7 @@ module ActiveRecord | |
| 98 101 | 
             
                  #
         | 
| 99 102 | 
             
                  # If B < A and C < B and if A is an abstract_class then both B.base_class
         | 
| 100 103 | 
             
                  # and C.base_class would return B as the answer since A is an abstract_class.
         | 
| 101 | 
            -
                   | 
| 102 | 
            -
                    unless self < Base
         | 
| 103 | 
            -
                      raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
         | 
| 104 | 
            -
                    end
         | 
| 105 | 
            -
             | 
| 106 | 
            -
                    if superclass == Base || superclass.abstract_class?
         | 
| 107 | 
            -
                      self
         | 
| 108 | 
            -
                    else
         | 
| 109 | 
            -
                      superclass.base_class
         | 
| 110 | 
            -
                    end
         | 
| 111 | 
            -
                  end
         | 
| 104 | 
            +
                  attr_reader :base_class
         | 
| 112 105 |  | 
| 113 106 | 
             
                  # Returns whether the class is a base class.
         | 
| 114 107 | 
             
                  # See #base_class for more information.
         | 
| @@ -164,6 +157,21 @@ module ActiveRecord | |
| 164 157 | 
             
                    defined?(@abstract_class) && @abstract_class == true
         | 
| 165 158 | 
             
                  end
         | 
| 166 159 |  | 
| 160 | 
            +
                  # Sets the application record class for Active Record
         | 
| 161 | 
            +
                  #
         | 
| 162 | 
            +
                  # This is useful if your application uses a different class than
         | 
| 163 | 
            +
                  # ApplicationRecord for your primary abstract class. This class
         | 
| 164 | 
            +
                  # will share a database connection with Active Record. It is the class
         | 
| 165 | 
            +
                  # that connects to your primary database.
         | 
| 166 | 
            +
                  def primary_abstract_class
         | 
| 167 | 
            +
                    if ActiveRecord.application_record_class && ActiveRecord.application_record_class.name != name
         | 
| 168 | 
            +
                      raise ArgumentError, "The `primary_abstract_class` is already set to #{ActiveRecord.application_record_class.inspect}. There can only be one `primary_abstract_class` in an application."
         | 
| 169 | 
            +
                    end
         | 
| 170 | 
            +
             | 
| 171 | 
            +
                    self.abstract_class = true
         | 
| 172 | 
            +
                    ActiveRecord.application_record_class = self
         | 
| 173 | 
            +
                  end
         | 
| 174 | 
            +
             | 
| 167 175 | 
             
                  # Returns the value to be stored in the inheritance column for STI.
         | 
| 168 176 | 
             
                  def sti_name
         | 
| 169 177 | 
             
                    store_full_sti_class && store_full_class_name ? name : name.demodulize
         | 
| @@ -174,7 +182,7 @@ module ActiveRecord | |
| 174 182 | 
             
                  # It is used to find the class correspondent to the value stored in the inheritance column.
         | 
| 175 183 | 
             
                  def sti_class_for(type_name)
         | 
| 176 184 | 
             
                    if store_full_sti_class && store_full_class_name
         | 
| 177 | 
            -
                       | 
| 185 | 
            +
                      type_name.constantize
         | 
| 178 186 | 
             
                    else
         | 
| 179 187 | 
             
                      compute_type(type_name)
         | 
| 180 188 | 
             
                    end
         | 
| @@ -196,17 +204,31 @@ module ActiveRecord | |
| 196 204 | 
             
                  # It is used to find the class correspondent to the value stored in the polymorphic type column.
         | 
| 197 205 | 
             
                  def polymorphic_class_for(name)
         | 
| 198 206 | 
             
                    if store_full_class_name
         | 
| 199 | 
            -
                       | 
| 207 | 
            +
                      name.constantize
         | 
| 200 208 | 
             
                    else
         | 
| 201 209 | 
             
                      compute_type(name)
         | 
| 202 210 | 
             
                    end
         | 
| 203 211 | 
             
                  end
         | 
| 204 212 |  | 
| 205 213 | 
             
                  def inherited(subclass)
         | 
| 214 | 
            +
                    subclass.set_base_class
         | 
| 206 215 | 
             
                    subclass.instance_variable_set(:@_type_candidates_cache, Concurrent::Map.new)
         | 
| 207 216 | 
             
                    super
         | 
| 208 217 | 
             
                  end
         | 
| 209 218 |  | 
| 219 | 
            +
                  def dup # :nodoc:
         | 
| 220 | 
            +
                    # `initialize_dup` / `initialize_copy` don't work when defined
         | 
| 221 | 
            +
                    # in the `singleton_class`.
         | 
| 222 | 
            +
                    other = super
         | 
| 223 | 
            +
                    other.set_base_class
         | 
| 224 | 
            +
                    other
         | 
| 225 | 
            +
                  end
         | 
| 226 | 
            +
             | 
| 227 | 
            +
                  def initialize_clone(other) # :nodoc:
         | 
| 228 | 
            +
                    super
         | 
| 229 | 
            +
                    set_base_class
         | 
| 230 | 
            +
                  end
         | 
| 231 | 
            +
             | 
| 210 232 | 
             
                  protected
         | 
| 211 233 | 
             
                    # Returns the class type of the record using the current module as a prefix. So descendants of
         | 
| 212 234 | 
             
                    # MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
         | 
| @@ -214,10 +236,10 @@ module ActiveRecord | |
| 214 236 | 
             
                      if type_name.start_with?("::")
         | 
| 215 237 | 
             
                        # If the type is prefixed with a scope operator then we assume that
         | 
| 216 238 | 
             
                        # the type_name is an absolute reference.
         | 
| 217 | 
            -
                         | 
| 239 | 
            +
                        type_name.constantize
         | 
| 218 240 | 
             
                      else
         | 
| 219 241 | 
             
                        type_candidate = @_type_candidates_cache[type_name]
         | 
| 220 | 
            -
                        if type_candidate && type_constant =  | 
| 242 | 
            +
                        if type_candidate && type_constant = type_candidate.safe_constantize
         | 
| 221 243 | 
             
                          return type_constant
         | 
| 222 244 | 
             
                        end
         | 
| 223 245 |  | 
| @@ -227,7 +249,7 @@ module ActiveRecord | |
| 227 249 | 
             
                        candidates << type_name
         | 
| 228 250 |  | 
| 229 251 | 
             
                        candidates.each do |candidate|
         | 
| 230 | 
            -
                          constant =  | 
| 252 | 
            +
                          constant = candidate.safe_constantize
         | 
| 231 253 | 
             
                          if candidate == constant.to_s
         | 
| 232 254 | 
             
                            @_type_candidates_cache[type_name] = candidate
         | 
| 233 255 | 
             
                            return constant
         | 
| @@ -238,6 +260,22 @@ module ActiveRecord | |
| 238 260 | 
             
                      end
         | 
| 239 261 | 
             
                    end
         | 
| 240 262 |  | 
| 263 | 
            +
                    def set_base_class # :nodoc:
         | 
| 264 | 
            +
                      @base_class = if self == Base
         | 
| 265 | 
            +
                        self
         | 
| 266 | 
            +
                      else
         | 
| 267 | 
            +
                        unless self < Base
         | 
| 268 | 
            +
                          raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
         | 
| 269 | 
            +
                        end
         | 
| 270 | 
            +
             | 
| 271 | 
            +
                        if superclass == Base || superclass.abstract_class?
         | 
| 272 | 
            +
                          self
         | 
| 273 | 
            +
                        else
         | 
| 274 | 
            +
                          superclass.base_class
         | 
| 275 | 
            +
                        end
         | 
| 276 | 
            +
                      end
         | 
| 277 | 
            +
                    end
         | 
| 278 | 
            +
             | 
| 241 279 | 
             
                  private
         | 
| 242 280 | 
             
                    # Called by +instantiate+ to decide which class to use for a new
         | 
| 243 281 | 
             
                    # record instance. For single-table inheritance, we check the record
         | 
| @@ -5,13 +5,19 @@ require "active_support/core_ext/enumerable" | |
| 5 5 | 
             
            module ActiveRecord
         | 
| 6 6 | 
             
              class InsertAll # :nodoc:
         | 
| 7 7 | 
             
                attr_reader :model, :connection, :inserts, :keys
         | 
| 8 | 
            -
                attr_reader :on_duplicate, :returning, :unique_by
         | 
| 8 | 
            +
                attr_reader :on_duplicate, :update_only, :returning, :unique_by, :update_sql
         | 
| 9 9 |  | 
| 10 | 
            -
                def initialize(model, inserts, on_duplicate:, returning: nil, unique_by: nil)
         | 
| 10 | 
            +
                def initialize(model, inserts, on_duplicate:, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
         | 
| 11 11 | 
             
                  raise ArgumentError, "Empty list of attributes passed" if inserts.blank?
         | 
| 12 12 |  | 
| 13 13 | 
             
                  @model, @connection, @inserts, @keys = model, model.connection, inserts, inserts.first.keys.map(&:to_s)
         | 
| 14 | 
            -
                  @on_duplicate, @returning, @unique_by = on_duplicate, returning, unique_by
         | 
| 14 | 
            +
                  @on_duplicate, @update_only, @returning, @unique_by = on_duplicate, update_only, returning, unique_by
         | 
| 15 | 
            +
                  @record_timestamps = record_timestamps.nil? ? model.record_timestamps : record_timestamps
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  disallow_raw_sql!(on_duplicate)
         | 
| 18 | 
            +
                  disallow_raw_sql!(returning)
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  configure_on_duplicate_update_logic
         | 
| 15 21 |  | 
| 16 22 | 
             
                  if model.scope_attributes?
         | 
| 17 23 | 
             
                    @scope_attributes = model.scope_attributes
         | 
| @@ -36,7 +42,7 @@ module ActiveRecord | |
| 36 42 | 
             
                end
         | 
| 37 43 |  | 
| 38 44 | 
             
                def updatable_columns
         | 
| 39 | 
            -
                  keys - readonly_columns - unique_by_columns
         | 
| 45 | 
            +
                  @updatable_columns ||= keys - readonly_columns - unique_by_columns
         | 
| 40 46 | 
             
                end
         | 
| 41 47 |  | 
| 42 48 | 
             
                def primary_keys
         | 
| @@ -56,18 +62,50 @@ module ActiveRecord | |
| 56 62 | 
             
                  inserts.map do |attributes|
         | 
| 57 63 | 
             
                    attributes = attributes.stringify_keys
         | 
| 58 64 | 
             
                    attributes.merge!(scope_attributes) if scope_attributes
         | 
| 65 | 
            +
                    attributes.reverse_merge!(timestamps_for_create) if record_timestamps?
         | 
| 59 66 |  | 
| 60 67 | 
             
                    verify_attributes(attributes)
         | 
| 61 68 |  | 
| 62 | 
            -
                     | 
| 69 | 
            +
                    keys_including_timestamps.map do |key|
         | 
| 63 70 | 
             
                      yield key, attributes[key]
         | 
| 64 71 | 
             
                    end
         | 
| 65 72 | 
             
                  end
         | 
| 66 73 | 
             
                end
         | 
| 67 74 |  | 
| 75 | 
            +
                def record_timestamps?
         | 
| 76 | 
            +
                  @record_timestamps
         | 
| 77 | 
            +
                end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                # TODO: Consider remaining this method, as it only conditionally extends keys, not always
         | 
| 80 | 
            +
                def keys_including_timestamps
         | 
| 81 | 
            +
                  @keys_including_timestamps ||= if record_timestamps?
         | 
| 82 | 
            +
                    keys + model.all_timestamp_attributes_in_model
         | 
| 83 | 
            +
                  else
         | 
| 84 | 
            +
                    keys
         | 
| 85 | 
            +
                  end
         | 
| 86 | 
            +
                end
         | 
| 87 | 
            +
             | 
| 68 88 | 
             
                private
         | 
| 69 89 | 
             
                  attr_reader :scope_attributes
         | 
| 70 90 |  | 
| 91 | 
            +
                  def configure_on_duplicate_update_logic
         | 
| 92 | 
            +
                    if custom_update_sql_provided? && update_only.present?
         | 
| 93 | 
            +
                      raise ArgumentError, "You can't set :update_only and provide custom update SQL via :on_duplicate at the same time"
         | 
| 94 | 
            +
                    end
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                    if update_only.present?
         | 
| 97 | 
            +
                      @updatable_columns = Array(update_only)
         | 
| 98 | 
            +
                      @on_duplicate = :update
         | 
| 99 | 
            +
                    elsif custom_update_sql_provided?
         | 
| 100 | 
            +
                      @update_sql = on_duplicate
         | 
| 101 | 
            +
                      @on_duplicate = :update
         | 
| 102 | 
            +
                    end
         | 
| 103 | 
            +
                  end
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                  def custom_update_sql_provided?
         | 
| 106 | 
            +
                    @custom_update_sql_provided ||= Arel.arel_node?(on_duplicate)
         | 
| 107 | 
            +
                  end
         | 
| 108 | 
            +
             | 
| 71 109 | 
             
                  def find_unique_index_for(unique_by)
         | 
| 72 110 | 
             
                    if !connection.supports_insert_conflict_target?
         | 
| 73 111 | 
             
                      return if unique_by.nil?
         | 
| @@ -126,15 +164,28 @@ module ActiveRecord | |
| 126 164 |  | 
| 127 165 |  | 
| 128 166 | 
             
                  def verify_attributes(attributes)
         | 
| 129 | 
            -
                    if  | 
| 167 | 
            +
                    if keys_including_timestamps != attributes.keys.to_set
         | 
| 130 168 | 
             
                      raise ArgumentError, "All objects being inserted must have the same keys"
         | 
| 131 169 | 
             
                    end
         | 
| 132 170 | 
             
                  end
         | 
| 133 171 |  | 
| 172 | 
            +
                  def disallow_raw_sql!(value)
         | 
| 173 | 
            +
                    return if !value.is_a?(String) || Arel.arel_node?(value)
         | 
| 174 | 
            +
             | 
| 175 | 
            +
                    raise ArgumentError, "Dangerous query method (method whose arguments are used as raw " \
         | 
| 176 | 
            +
                                         "SQL) called: #{value}. " \
         | 
| 177 | 
            +
                                         "Known-safe values can be passed " \
         | 
| 178 | 
            +
                                         "by wrapping them in Arel.sql()."
         | 
| 179 | 
            +
                  end
         | 
| 180 | 
            +
             | 
| 181 | 
            +
                  def timestamps_for_create
         | 
| 182 | 
            +
                    model.all_timestamp_attributes_in_model.index_with(connection.high_precision_current_timestamp)
         | 
| 183 | 
            +
                  end
         | 
| 184 | 
            +
             | 
| 134 185 | 
             
                  class Builder # :nodoc:
         | 
| 135 186 | 
             
                    attr_reader :model
         | 
| 136 187 |  | 
| 137 | 
            -
                    delegate :skip_duplicates?, :update_duplicates?, :keys, to: :insert_all
         | 
| 188 | 
            +
                    delegate :skip_duplicates?, :update_duplicates?, :keys, :keys_including_timestamps, :record_timestamps?, to: :insert_all
         | 
| 138 189 |  | 
| 139 190 | 
             
                    def initialize(insert_all)
         | 
| 140 191 | 
             
                      @insert_all, @model, @connection = insert_all, insert_all.model, insert_all.connection
         | 
| @@ -145,9 +196,10 @@ module ActiveRecord | |
| 145 196 | 
             
                    end
         | 
| 146 197 |  | 
| 147 198 | 
             
                    def values_list
         | 
| 148 | 
            -
                      types = extract_types_from_columns_on(model.table_name, keys:  | 
| 199 | 
            +
                      types = extract_types_from_columns_on(model.table_name, keys: keys_including_timestamps)
         | 
| 149 200 |  | 
| 150 201 | 
             
                      values_list = insert_all.map_key_with_value do |key, value|
         | 
| 202 | 
            +
                        next value if Arel::Nodes::SqlLiteral === value
         | 
| 151 203 | 
             
                        connection.with_yaml_fallback(types[key].serialize(value))
         | 
| 152 204 | 
             
                      end
         | 
| 153 205 |  | 
| @@ -155,7 +207,13 @@ module ActiveRecord | |
| 155 207 | 
             
                    end
         | 
| 156 208 |  | 
| 157 209 | 
             
                    def returning
         | 
| 158 | 
            -
                       | 
| 210 | 
            +
                      return unless insert_all.returning
         | 
| 211 | 
            +
             | 
| 212 | 
            +
                      if insert_all.returning.is_a?(String)
         | 
| 213 | 
            +
                        insert_all.returning
         | 
| 214 | 
            +
                      else
         | 
| 215 | 
            +
                        format_columns(insert_all.returning)
         | 
| 216 | 
            +
                      end
         | 
| 159 217 | 
             
                    end
         | 
| 160 218 |  | 
| 161 219 | 
             
                    def conflict_target
         | 
| @@ -173,22 +231,30 @@ module ActiveRecord | |
| 173 231 | 
             
                    end
         | 
| 174 232 |  | 
| 175 233 | 
             
                    def touch_model_timestamps_unless(&block)
         | 
| 176 | 
            -
                       | 
| 234 | 
            +
                      return "" unless update_duplicates? && record_timestamps?
         | 
| 235 | 
            +
             | 
| 236 | 
            +
                      model.timestamp_attributes_for_update_in_model.filter_map do |column_name|
         | 
| 177 237 | 
             
                        if touch_timestamp_attribute?(column_name)
         | 
| 178 | 
            -
                          "#{column_name}=(CASE WHEN (#{updatable_columns.map(&block).join(" AND ")}) THEN #{model.quoted_table_name}.#{column_name} ELSE  | 
| 238 | 
            +
                          "#{column_name}=(CASE WHEN (#{updatable_columns.map(&block).join(" AND ")}) THEN #{model.quoted_table_name}.#{column_name} ELSE #{connection.high_precision_current_timestamp} END),"
         | 
| 179 239 | 
             
                        end
         | 
| 180 | 
            -
                      end. | 
| 240 | 
            +
                      end.join
         | 
| 181 241 | 
             
                    end
         | 
| 182 242 |  | 
| 243 | 
            +
                    def raw_update_sql
         | 
| 244 | 
            +
                      insert_all.update_sql
         | 
| 245 | 
            +
                    end
         | 
| 246 | 
            +
             | 
| 247 | 
            +
                    alias raw_update_sql? raw_update_sql
         | 
| 248 | 
            +
             | 
| 183 249 | 
             
                    private
         | 
| 184 250 | 
             
                      attr_reader :connection, :insert_all
         | 
| 185 251 |  | 
| 186 252 | 
             
                      def touch_timestamp_attribute?(column_name)
         | 
| 187 | 
            -
                         | 
| 253 | 
            +
                        insert_all.updatable_columns.exclude?(column_name)
         | 
| 188 254 | 
             
                      end
         | 
| 189 255 |  | 
| 190 256 | 
             
                      def columns_list
         | 
| 191 | 
            -
                        format_columns(insert_all. | 
| 257 | 
            +
                        format_columns(insert_all.keys_including_timestamps)
         | 
| 192 258 | 
             
                      end
         | 
| 193 259 |  | 
| 194 260 | 
             
                      def extract_types_from_columns_on(table_name, keys:)
         | 
| @@ -79,7 +79,7 @@ module ActiveRecord | |
| 79 79 | 
             
                      timestamp = max_updated_column_timestamp
         | 
| 80 80 |  | 
| 81 81 | 
             
                      if timestamp
         | 
| 82 | 
            -
                        timestamp = timestamp.utc. | 
| 82 | 
            +
                        timestamp = timestamp.utc.to_formatted_s(cache_timestamp_format)
         | 
| 83 83 | 
             
                        "#{model_name.cache_key}/#{id}-#{timestamp}"
         | 
| 84 84 | 
             
                      else
         | 
| 85 85 | 
             
                        "#{model_name.cache_key}/#{id}"
         | 
| @@ -101,8 +101,9 @@ module ActiveRecord | |
| 101 101 | 
             
                    timestamp = updated_at_before_type_cast
         | 
| 102 102 | 
             
                    if can_use_fast_cache_version?(timestamp)
         | 
| 103 103 | 
             
                      raw_timestamp_to_cache_version(timestamp)
         | 
| 104 | 
            +
             | 
| 104 105 | 
             
                    elsif timestamp = updated_at
         | 
| 105 | 
            -
                      timestamp.utc. | 
| 106 | 
            +
                      timestamp.utc.to_formatted_s(cache_timestamp_format)
         | 
| 106 107 | 
             
                    end
         | 
| 107 108 | 
             
                  elsif self.class.has_attribute?("updated_at")
         | 
| 108 109 | 
             
                    raise ActiveModel::MissingAttributeError, "missing attribute: updated_at"
         | 
| @@ -177,7 +178,7 @@ module ActiveRecord | |
| 177 178 | 
             
                  def can_use_fast_cache_version?(timestamp)
         | 
| 178 179 | 
             
                    timestamp.is_a?(String) &&
         | 
| 179 180 | 
             
                      cache_timestamp_format == :usec &&
         | 
| 180 | 
            -
                      default_timezone == :utc &&
         | 
| 181 | 
            +
                      ActiveRecord.default_timezone == :utc &&
         | 
| 181 182 | 
             
                      !updated_at_came_from_user?
         | 
| 182 183 | 
             
                  end
         | 
| 183 184 |  | 
| @@ -10,15 +10,13 @@ module ActiveRecord | |
| 10 10 | 
             
              # This is enabled by default. To disable this functionality set
         | 
| 11 11 | 
             
              # `use_metadata_table` to false in your database configuration.
         | 
| 12 12 | 
             
              class InternalMetadata < ActiveRecord::Base # :nodoc:
         | 
| 13 | 
            +
                self.record_timestamps = true
         | 
| 14 | 
            +
             | 
| 13 15 | 
             
                class << self
         | 
| 14 16 | 
             
                  def enabled?
         | 
| 15 17 | 
             
                    ActiveRecord::Base.connection.use_metadata_table?
         | 
| 16 18 | 
             
                  end
         | 
| 17 19 |  | 
| 18 | 
            -
                  def _internal?
         | 
| 19 | 
            -
                    true
         | 
| 20 | 
            -
                  end
         | 
| 21 | 
            -
             | 
| 22 20 | 
             
                  def primary_key
         | 
| 23 21 | 
             
                    "key"
         | 
| 24 22 | 
             
                  end
         | 
| @@ -36,7 +34,7 @@ module ActiveRecord | |
| 36 34 | 
             
                  def [](key)
         | 
| 37 35 | 
             
                    return unless enabled?
         | 
| 38 36 |  | 
| 39 | 
            -
                    where(key: key). | 
| 37 | 
            +
                    where(key: key).pick(:value)
         | 
| 40 38 | 
             
                  end
         | 
| 41 39 |  | 
| 42 40 | 
             
                  # Creates an internal metadata table with columns +key+ and +value+
         | 
| @@ -2,50 +2,13 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module ActiveRecord
         | 
| 4 4 | 
             
              module LegacyYamlAdapter # :nodoc:
         | 
| 5 | 
            -
                def self.convert( | 
| 5 | 
            +
                def self.convert(coder)
         | 
| 6 6 | 
             
                  return coder unless coder.is_a?(Psych::Coder)
         | 
| 7 7 |  | 
| 8 8 | 
             
                  case coder["active_record_yaml_version"]
         | 
| 9 9 | 
             
                  when 1, 2 then coder
         | 
| 10 10 | 
             
                  else
         | 
| 11 | 
            -
                     | 
| 12 | 
            -
                      YAML loading from legacy format older than Rails 5.0 is deprecated
         | 
| 13 | 
            -
                      and will be removed in Rails 6.2.
         | 
| 14 | 
            -
                    MSG
         | 
| 15 | 
            -
                    if coder["attributes"].is_a?(ActiveModel::AttributeSet)
         | 
| 16 | 
            -
                      Rails420.convert(klass, coder)
         | 
| 17 | 
            -
                    else
         | 
| 18 | 
            -
                      Rails41.convert(klass, coder)
         | 
| 19 | 
            -
                    end
         | 
| 20 | 
            -
                  end
         | 
| 21 | 
            -
                end
         | 
| 22 | 
            -
             | 
| 23 | 
            -
                module Rails420 # :nodoc:
         | 
| 24 | 
            -
                  def self.convert(klass, coder)
         | 
| 25 | 
            -
                    attribute_set = coder["attributes"]
         | 
| 26 | 
            -
             | 
| 27 | 
            -
                    klass.attribute_names.each do |attr_name|
         | 
| 28 | 
            -
                      attribute = attribute_set[attr_name]
         | 
| 29 | 
            -
                      if attribute.type.is_a?(Delegator)
         | 
| 30 | 
            -
                        type_from_klass = klass.type_for_attribute(attr_name)
         | 
| 31 | 
            -
                        attribute_set[attr_name] = attribute.with_type(type_from_klass)
         | 
| 32 | 
            -
                      end
         | 
| 33 | 
            -
                    end
         | 
| 34 | 
            -
             | 
| 35 | 
            -
                    coder
         | 
| 36 | 
            -
                  end
         | 
| 37 | 
            -
                end
         | 
| 38 | 
            -
             | 
| 39 | 
            -
                module Rails41 # :nodoc:
         | 
| 40 | 
            -
                  def self.convert(klass, coder)
         | 
| 41 | 
            -
                    attributes = klass.attributes_builder
         | 
| 42 | 
            -
                      .build_from_database(coder["attributes"])
         | 
| 43 | 
            -
                    new_record = coder["attributes"][klass.primary_key].blank?
         | 
| 44 | 
            -
             | 
| 45 | 
            -
                    {
         | 
| 46 | 
            -
                      "attributes" => attributes,
         | 
| 47 | 
            -
                      "new_record" => new_record,
         | 
| 48 | 
            -
                    }
         | 
| 11 | 
            +
                    raise("Active Record doesn't know how to load YAML with this format.")
         | 
| 49 12 | 
             
                  end
         | 
| 50 13 | 
             
                end
         | 
| 51 14 | 
             
              end
         | 
| @@ -56,11 +56,11 @@ module ActiveRecord | |
| 56 56 | 
             
                    class_attribute :lock_optimistically, instance_writer: false, default: true
         | 
| 57 57 | 
             
                  end
         | 
| 58 58 |  | 
| 59 | 
            -
                  def locking_enabled?  | 
| 59 | 
            +
                  def locking_enabled? # :nodoc:
         | 
| 60 60 | 
             
                    self.class.locking_enabled?
         | 
| 61 61 | 
             
                  end
         | 
| 62 62 |  | 
| 63 | 
            -
                  def increment!(*, **)  | 
| 63 | 
            +
                  def increment!(*, **) # :nodoc:
         | 
| 64 64 | 
             
                    super.tap do
         | 
| 65 65 | 
             
                      if locking_enabled?
         | 
| 66 66 | 
             
                        self[self.class.locking_column] += 1
         | 
| @@ -90,7 +90,9 @@ module ActiveRecord | |
| 90 90 | 
             
                      begin
         | 
| 91 91 | 
             
                        locking_column = self.class.locking_column
         | 
| 92 92 | 
             
                        lock_attribute_was = @attributes[locking_column]
         | 
| 93 | 
            -
             | 
| 93 | 
            +
             | 
| 94 | 
            +
                        update_constraints = _primary_key_constraints_hash
         | 
| 95 | 
            +
                        update_constraints[locking_column] = _lock_value_for_database(locking_column)
         | 
| 94 96 |  | 
| 95 97 | 
             
                        attribute_names = attribute_names.dup if attribute_names.frozen?
         | 
| 96 98 | 
             
                        attribute_names << locking_column
         | 
| @@ -99,8 +101,7 @@ module ActiveRecord | |
| 99 101 |  | 
| 100 102 | 
             
                        affected_rows = self.class._update_record(
         | 
| 101 103 | 
             
                          attributes_with_values(attribute_names),
         | 
| 102 | 
            -
                           | 
| 103 | 
            -
                          locking_column => lock_value_for_database
         | 
| 104 | 
            +
                          update_constraints
         | 
| 104 105 | 
             
                        )
         | 
| 105 106 |  | 
| 106 107 | 
             
                        if affected_rows != 1
         | 
| @@ -121,10 +122,10 @@ module ActiveRecord | |
| 121 122 |  | 
| 122 123 | 
             
                      locking_column = self.class.locking_column
         | 
| 123 124 |  | 
| 124 | 
            -
                       | 
| 125 | 
            -
             | 
| 126 | 
            -
             | 
| 127 | 
            -
                      )
         | 
| 125 | 
            +
                      delete_constraints = _primary_key_constraints_hash
         | 
| 126 | 
            +
                      delete_constraints[locking_column] = _lock_value_for_database(locking_column)
         | 
| 127 | 
            +
             | 
| 128 | 
            +
                      affected_rows = self.class._delete_record(delete_constraints)
         | 
| 128 129 |  | 
| 129 130 | 
             
                      if affected_rows != 1
         | 
| 130 131 | 
             
                        raise ActiveRecord::StaleObjectError.new(self, "destroy")
         | 
| @@ -81,9 +81,15 @@ module ActiveRecord | |
| 81 81 |  | 
| 82 82 | 
             
                  # Wraps the passed block in a transaction, locking the object
         | 
| 83 83 | 
             
                  # before yielding. You can pass the SQL locking clause
         | 
| 84 | 
            -
                  # as argument (see <tt | 
| 85 | 
            -
                   | 
| 86 | 
            -
             | 
| 84 | 
            +
                  # as an optional argument (see <tt>#lock!</tt>).
         | 
| 85 | 
            +
                  #
         | 
| 86 | 
            +
                  # You can also pass options like <tt>requires_new:</tt>, <tt>isolation:</tt>,
         | 
| 87 | 
            +
                  # and <tt>joinable:</tt> to the wrapping transaction (see
         | 
| 88 | 
            +
                  # <tt>ActiveRecord::ConnectionAdapters::DatabaseStatements#transaction</tt>).
         | 
| 89 | 
            +
                  def with_lock(*args)
         | 
| 90 | 
            +
                    transaction_opts = args.extract_options!
         | 
| 91 | 
            +
                    lock = args.present? ? args.first : true
         | 
| 92 | 
            +
                    transaction(**transaction_opts) do
         | 
| 87 93 | 
             
                      lock!(lock)
         | 
| 88 94 | 
             
                      yield
         | 
| 89 95 | 
             
                    end
         |