activerecord 6.0.0.beta3 → 6.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +286 -6
  3. data/README.rdoc +3 -1
  4. data/lib/active_record.rb +0 -1
  5. data/lib/active_record/associations.rb +3 -2
  6. data/lib/active_record/associations/association.rb +1 -1
  7. data/lib/active_record/associations/builder/association.rb +14 -18
  8. data/lib/active_record/associations/builder/belongs_to.rb +5 -2
  9. data/lib/active_record/associations/builder/collection_association.rb +3 -13
  10. data/lib/active_record/associations/builder/has_many.rb +2 -0
  11. data/lib/active_record/associations/builder/has_one.rb +35 -1
  12. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  13. data/lib/active_record/associations/collection_proxy.rb +1 -1
  14. data/lib/active_record/associations/has_many_through_association.rb +4 -11
  15. data/lib/active_record/associations/preloader.rb +11 -6
  16. data/lib/active_record/associations/preloader/association.rb +32 -30
  17. data/lib/active_record/associations/preloader/through_association.rb +48 -28
  18. data/lib/active_record/attribute_methods.rb +4 -3
  19. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
  20. data/lib/active_record/attribute_methods/dirty.rb +42 -14
  21. data/lib/active_record/attribute_methods/primary_key.rb +7 -15
  22. data/lib/active_record/attribute_methods/query.rb +2 -3
  23. data/lib/active_record/attribute_methods/read.rb +3 -9
  24. data/lib/active_record/attribute_methods/write.rb +6 -12
  25. data/lib/active_record/attributes.rb +13 -0
  26. data/lib/active_record/autosave_association.rb +13 -3
  27. data/lib/active_record/base.rb +0 -1
  28. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +1 -0
  29. data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
  30. data/lib/active_record/connection_adapters/abstract/database_statements.rb +84 -61
  31. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -1
  32. data/lib/active_record/connection_adapters/abstract/quoting.rb +10 -6
  33. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +4 -7
  34. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +70 -14
  35. data/lib/active_record/connection_adapters/abstract_adapter.rb +56 -11
  36. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +65 -69
  37. data/lib/active_record/connection_adapters/column.rb +17 -13
  38. data/lib/active_record/connection_adapters/mysql/database_statements.rb +45 -7
  39. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +4 -4
  40. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +9 -6
  41. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  42. data/lib/active_record/connection_adapters/mysql2_adapter.rb +6 -2
  43. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -30
  44. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +5 -1
  45. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +34 -38
  46. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +23 -27
  47. data/lib/active_record/connection_adapters/postgresql_adapter.rb +57 -27
  48. data/lib/active_record/connection_adapters/schema_cache.rb +32 -14
  49. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  50. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
  51. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +2 -2
  52. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +50 -112
  53. data/lib/active_record/connection_handling.rb +17 -10
  54. data/lib/active_record/core.rb +15 -20
  55. data/lib/active_record/database_configurations.rb +14 -14
  56. data/lib/active_record/database_configurations/hash_config.rb +11 -11
  57. data/lib/active_record/database_configurations/url_config.rb +12 -12
  58. data/lib/active_record/dynamic_matchers.rb +1 -1
  59. data/lib/active_record/enum.rb +6 -0
  60. data/lib/active_record/errors.rb +1 -1
  61. data/lib/active_record/gem_version.rb +1 -1
  62. data/lib/active_record/insert_all.rb +180 -0
  63. data/lib/active_record/integration.rb +13 -1
  64. data/lib/active_record/internal_metadata.rb +5 -1
  65. data/lib/active_record/locking/optimistic.rb +3 -4
  66. data/lib/active_record/log_subscriber.rb +1 -1
  67. data/lib/active_record/migration.rb +25 -18
  68. data/lib/active_record/migration/command_recorder.rb +28 -14
  69. data/lib/active_record/migration/compatibility.rb +10 -0
  70. data/lib/active_record/persistence.rb +206 -13
  71. data/lib/active_record/querying.rb +17 -12
  72. data/lib/active_record/railties/databases.rake +68 -6
  73. data/lib/active_record/reflection.rb +2 -2
  74. data/lib/active_record/relation.rb +98 -20
  75. data/lib/active_record/relation/calculations.rb +39 -39
  76. data/lib/active_record/relation/delegation.rb +22 -30
  77. data/lib/active_record/relation/finder_methods.rb +3 -9
  78. data/lib/active_record/relation/merger.rb +7 -16
  79. data/lib/active_record/relation/query_methods.rb +153 -38
  80. data/lib/active_record/relation/where_clause.rb +9 -5
  81. data/lib/active_record/sanitization.rb +3 -2
  82. data/lib/active_record/schema_dumper.rb +5 -0
  83. data/lib/active_record/schema_migration.rb +1 -1
  84. data/lib/active_record/scoping/default.rb +6 -7
  85. data/lib/active_record/scoping/named.rb +1 -1
  86. data/lib/active_record/statement_cache.rb +2 -2
  87. data/lib/active_record/store.rb +48 -0
  88. data/lib/active_record/table_metadata.rb +3 -3
  89. data/lib/active_record/tasks/database_tasks.rb +36 -1
  90. data/lib/active_record/touch_later.rb +2 -2
  91. data/lib/active_record/transactions.rb +52 -41
  92. data/lib/active_record/validations/uniqueness.rb +3 -5
  93. data/lib/arel/insert_manager.rb +3 -3
  94. data/lib/arel/nodes.rb +2 -1
  95. data/lib/arel/nodes/comment.rb +29 -0
  96. data/lib/arel/nodes/select_core.rb +16 -12
  97. data/lib/arel/nodes/unary.rb +1 -0
  98. data/lib/arel/nodes/values_list.rb +2 -17
  99. data/lib/arel/select_manager.rb +10 -10
  100. data/lib/arel/visitors/depth_first.rb +6 -1
  101. data/lib/arel/visitors/dot.rb +7 -2
  102. data/lib/arel/visitors/ibm_db.rb +13 -0
  103. data/lib/arel/visitors/informix.rb +6 -0
  104. data/lib/arel/visitors/mssql.rb +15 -1
  105. data/lib/arel/visitors/oracle12.rb +4 -5
  106. data/lib/arel/visitors/postgresql.rb +4 -10
  107. data/lib/arel/visitors/to_sql.rb +87 -108
  108. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
  109. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  110. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  111. data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
  112. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  113. metadata +12 -11
  114. data/lib/active_record/collection_cache_key.rb +0 -53
  115. data/lib/arel/nodes/values.rb +0 -16
@@ -72,7 +72,7 @@ module ActiveRecord
72
72
  table = strip_table_name_prefix_and_suffix(table)
73
73
  fk_to_table = strip_table_name_prefix_and_suffix(fk.to_table)
74
74
  fk_to_table == table && options.all? { |k, v| fk.options[k].to_s == v.to_s }
75
- end || raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{to_table}")
75
+ end || raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{to_table || options}")
76
76
 
77
77
  foreign_keys.delete(fkey)
78
78
  alter_table(from_table, foreign_keys)
@@ -105,7 +105,7 @@ module ActiveRecord
105
105
  end
106
106
 
107
107
  type_metadata = fetch_type_metadata(field["type"])
108
- Column.new(field["name"], default, type_metadata, field["notnull"].to_i == 0, table_name, nil, field["collation"])
108
+ Column.new(field["name"], default, type_metadata, field["notnull"].to_i == 0, collation: field["collation"])
109
109
  end
110
110
 
111
111
  def data_source_sql(name = nil, type: nil)
@@ -4,12 +4,13 @@ require "active_record/connection_adapters/abstract_adapter"
4
4
  require "active_record/connection_adapters/statement_pool"
5
5
  require "active_record/connection_adapters/sqlite3/explain_pretty_printer"
6
6
  require "active_record/connection_adapters/sqlite3/quoting"
7
+ require "active_record/connection_adapters/sqlite3/database_statements"
7
8
  require "active_record/connection_adapters/sqlite3/schema_creation"
8
9
  require "active_record/connection_adapters/sqlite3/schema_definitions"
9
10
  require "active_record/connection_adapters/sqlite3/schema_dumper"
10
11
  require "active_record/connection_adapters/sqlite3/schema_statements"
11
12
 
12
- gem "sqlite3", "~> 1.3", ">= 1.3.6"
13
+ gem "sqlite3", "~> 1.4"
13
14
  require "sqlite3"
14
15
 
15
16
  module ActiveRecord
@@ -36,8 +37,6 @@ module ActiveRecord
36
37
  config.merge(results_as_hash: true)
37
38
  )
38
39
 
39
- db.busy_timeout(ConnectionAdapters::SQLite3Adapter.type_cast_config_to_integer(config[:timeout])) if config[:timeout]
40
-
41
40
  ConnectionAdapters::SQLite3Adapter.new(db, logger, nil, config)
42
41
  rescue Errno::ENOENT => error
43
42
  if error.message.include?("No such file or directory")
@@ -60,6 +59,7 @@ module ActiveRecord
60
59
 
61
60
  include SQLite3::Quoting
62
61
  include SQLite3::SchemaStatements
62
+ include SQLite3::DatabaseStatements
63
63
 
64
64
  NATIVE_DATABASE_TYPES = {
65
65
  primary_key: "integer PRIMARY KEY AUTOINCREMENT NOT NULL",
@@ -95,9 +95,6 @@ module ActiveRecord
95
95
 
96
96
  def initialize(connection, logger, connection_options, config)
97
97
  super(connection, logger, config)
98
-
99
- @active = true
100
- @statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
101
98
  configure_connection
102
99
  end
103
100
 
@@ -114,7 +111,7 @@ module ActiveRecord
114
111
  end
115
112
 
116
113
  def supports_expression_index?
117
- sqlite_version >= "3.9.0"
114
+ database_version >= "3.9.0"
118
115
  end
119
116
 
120
117
  def requires_reloading?
@@ -137,23 +134,29 @@ module ActiveRecord
137
134
  true
138
135
  end
139
136
 
137
+ def supports_insert_on_conflict?
138
+ database_version >= "3.24.0"
139
+ end
140
+ alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
141
+ alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
142
+ alias supports_insert_conflict_target? supports_insert_on_conflict?
143
+
140
144
  def active?
141
- @active
145
+ !@connection.closed?
146
+ end
147
+
148
+ def reconnect!
149
+ super
150
+ connect if @connection.closed?
142
151
  end
143
152
 
144
153
  # Disconnects from the database if already connected. Otherwise, this
145
154
  # method does nothing.
146
155
  def disconnect!
147
156
  super
148
- @active = false
149
157
  @connection.close rescue nil
150
158
  end
151
159
 
152
- # Clears the prepared statements cache.
153
- def clear_cache!
154
- @statements.clear
155
- end
156
-
157
160
  def supports_index_sort_order?
158
161
  true
159
162
  end
@@ -201,91 +204,11 @@ module ActiveRecord
201
204
  #--
202
205
  # DATABASE STATEMENTS ======================================
203
206
  #++
204
-
205
- READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(:begin, :commit, :explain, :select, :pragma, :release, :savepoint, :rollback) # :nodoc:
206
- private_constant :READ_QUERY
207
-
208
- def write_query?(sql) # :nodoc:
209
- !READ_QUERY.match?(sql)
210
- end
211
-
212
207
  def explain(arel, binds = [])
213
208
  sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
214
209
  SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
215
210
  end
216
211
 
217
- def exec_query(sql, name = nil, binds = [], prepare: false)
218
- if preventing_writes? && write_query?(sql)
219
- raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
220
- end
221
-
222
- materialize_transactions
223
-
224
- type_casted_binds = type_casted_binds(binds)
225
-
226
- log(sql, name, binds, type_casted_binds) do
227
- ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
228
- # Don't cache statements if they are not prepared
229
- unless prepare
230
- stmt = @connection.prepare(sql)
231
- begin
232
- cols = stmt.columns
233
- unless without_prepared_statement?(binds)
234
- stmt.bind_params(type_casted_binds)
235
- end
236
- records = stmt.to_a
237
- ensure
238
- stmt.close
239
- end
240
- else
241
- stmt = @statements[sql] ||= @connection.prepare(sql)
242
- cols = stmt.columns
243
- stmt.reset!
244
- stmt.bind_params(type_casted_binds)
245
- records = stmt.to_a
246
- end
247
-
248
- ActiveRecord::Result.new(cols, records)
249
- end
250
- end
251
- end
252
-
253
- def exec_delete(sql, name = "SQL", binds = [])
254
- exec_query(sql, name, binds)
255
- @connection.changes
256
- end
257
- alias :exec_update :exec_delete
258
-
259
- def last_inserted_id(result)
260
- @connection.last_insert_row_id
261
- end
262
-
263
- def execute(sql, name = nil) #:nodoc:
264
- if preventing_writes? && write_query?(sql)
265
- raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
266
- end
267
-
268
- materialize_transactions
269
-
270
- log(sql, name) do
271
- ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
272
- @connection.execute(sql)
273
- end
274
- end
275
- end
276
-
277
- def begin_db_transaction #:nodoc:
278
- log("begin transaction", nil) { @connection.transaction }
279
- end
280
-
281
- def commit_db_transaction #:nodoc:
282
- log("commit transaction", nil) { @connection.commit }
283
- end
284
-
285
- def exec_rollback_db_transaction #:nodoc:
286
- log("rollback transaction", nil) { @connection.rollback }
287
- end
288
-
289
212
  # SCHEMA STATEMENTS ========================================
290
213
 
291
214
  def primary_keys(table_name) # :nodoc:
@@ -381,15 +304,26 @@ module ActiveRecord
381
304
  end
382
305
  end
383
306
 
384
- def insert_fixtures_set(fixture_set, tables_to_delete = [])
385
- disable_referential_integrity do
386
- transaction(requires_new: true) do
387
- tables_to_delete.each { |table| delete "DELETE FROM #{quote_table_name(table)}", "Fixture Delete" }
307
+ def build_insert_sql(insert) # :nodoc:
308
+ sql = +"INSERT #{insert.into} #{insert.values_list}"
388
309
 
389
- fixture_set.each do |table_name, rows|
390
- rows.each { |row| insert_fixture(row, table_name) }
391
- end
392
- end
310
+ if insert.skip_duplicates?
311
+ sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
312
+ elsif insert.update_duplicates?
313
+ sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
314
+ sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
315
+ end
316
+
317
+ sql
318
+ end
319
+
320
+ def get_database_version # :nodoc:
321
+ SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
322
+ end
323
+
324
+ def check_version # :nodoc:
325
+ if database_version < "3.8.0"
326
+ raise "Your version of SQLite (#{database_version}) is too old. Active Record supports SQLite >= 3.8."
393
327
  end
394
328
  end
395
329
 
@@ -400,12 +334,6 @@ module ActiveRecord
400
334
  999
401
335
  end
402
336
 
403
- def check_version
404
- if sqlite_version < "3.8.0"
405
- raise "Your version of SQLite (#{sqlite_version}) is too old. Active Record supports SQLite >= 3.8."
406
- end
407
- end
408
-
409
337
  def initialize_type_map(m = type_map)
410
338
  super
411
339
  register_class_with_limit m, %r(int)i, SQLite3Integer
@@ -523,10 +451,6 @@ module ActiveRecord
523
451
  SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
524
452
  end
525
453
 
526
- def sqlite_version
527
- @sqlite_version ||= SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
528
- end
529
-
530
454
  def translate_exception(exception, message:, sql:, binds:)
531
455
  case exception.message
532
456
  # SQLite 3.8.2 returns a newly formatted error message:
@@ -589,7 +513,21 @@ module ActiveRecord
589
513
  Arel::Visitors::SQLite.new(self)
590
514
  end
591
515
 
516
+ def build_statement_pool
517
+ StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
518
+ end
519
+
520
+ def connect
521
+ @connection = ::SQLite3::Database.new(
522
+ @config[:database].to_s,
523
+ @config.merge(results_as_hash: true)
524
+ )
525
+ configure_connection
526
+ end
527
+
592
528
  def configure_connection
529
+ @connection.busy_timeout(self.class.type_cast_config_to_integer(@config[:timeout])) if @config[:timeout]
530
+
593
531
  execute("PRAGMA foreign_keys = ON", "SCHEMA")
594
532
  end
595
533
 
@@ -85,14 +85,14 @@ module ActiveRecord
85
85
  # based on the requested role:
86
86
  #
87
87
  # ActiveRecord::Base.connected_to(role: :writing) do
88
- # Dog.create! # creates dog using dog connection
88
+ # Dog.create! # creates dog using dog writing connection
89
89
  # end
90
90
  #
91
91
  # ActiveRecord::Base.connected_to(role: :reading) do
92
92
  # Dog.create! # throws exception because we're on a replica
93
93
  # end
94
94
  #
95
- # ActiveRecord::Base.connected_to(role: :unknown_ode) do
95
+ # ActiveRecord::Base.connected_to(role: :unknown_role) do
96
96
  # # raises exception due to non-existent role
97
97
  # end
98
98
  #
@@ -100,11 +100,20 @@ module ActiveRecord
100
100
  # you can use +connected_to+ with a +database+ argument. The +database+ argument
101
101
  # expects a symbol that corresponds to the database key in your config.
102
102
  #
103
- # This will connect to a new database for the queries inside the block.
104
- #
105
103
  # ActiveRecord::Base.connected_to(database: :animals_slow_replica) do
106
104
  # Dog.run_a_long_query # runs a long query while connected to the +animals_slow_replica+
107
105
  # end
106
+ #
107
+ # This will connect to a new database for the queries inside the block. By
108
+ # default the `:writing` role will be used since all connections must be assigned
109
+ # a role. If you would like to use a different role you can pass a hash to database:
110
+ #
111
+ # ActiveRecord::Base.connected_to(database: { readonly_slow: :animals_slow_replica }) do
112
+ # # runs a long query while connected to the +animals_slow_replica+ using the readonly_slow role.
113
+ # Dog.run_a_long_query
114
+ # end
115
+ #
116
+ # When using the database key a new connection will be established every time.
108
117
  def connected_to(database: nil, role: nil, &blk)
109
118
  if database && role
110
119
  raise ArgumentError, "connected_to can only accept a `database` or a `role` argument, but not both arguments."
@@ -112,17 +121,14 @@ module ActiveRecord
112
121
  if database.is_a?(Hash)
113
122
  role, database = database.first
114
123
  role = role.to_sym
115
- else
116
- role = database.to_sym
117
124
  end
118
125
 
119
126
  config_hash = resolve_config_for_connection(database)
120
127
  handler = lookup_connection_handler(role)
121
128
 
122
- with_handler(role) do
123
- handler.establish_connection(config_hash)
124
- yield
125
- end
129
+ handler.establish_connection(config_hash)
130
+
131
+ with_handler(role, &blk)
126
132
  elsif role
127
133
  with_handler(role.to_sym, &blk)
128
134
  else
@@ -154,6 +160,7 @@ module ActiveRecord
154
160
  end
155
161
 
156
162
  def lookup_connection_handler(handler_key) # :nodoc:
163
+ handler_key ||= ActiveRecord::Base.writing_role
157
164
  connection_handlers[handler_key] ||= ActiveRecord::ConnectionAdapters::ConnectionHandler.new
158
165
  end
159
166
 
@@ -101,7 +101,6 @@ module ActiveRecord
101
101
  # environment where dumping schema is rarely needed.
102
102
  mattr_accessor :dump_schema_after_migration, instance_writer: false, default: true
103
103
 
104
- mattr_accessor :database_selector, instance_writer: false
105
104
  ##
106
105
  # :singleton-method:
107
106
  # Specifies which database schemas to dump when calling db:structure:dump.
@@ -161,7 +160,7 @@ module ActiveRecord
161
160
  return super if block_given? ||
162
161
  primary_key.nil? ||
163
162
  scope_attributes? ||
164
- columns_hash.include?(inheritance_column)
163
+ columns_hash.key?(inheritance_column) && !base_class?
165
164
 
166
165
  id = ids.first
167
166
 
@@ -175,14 +174,14 @@ module ActiveRecord
175
174
 
176
175
  record = statement.execute([id], connection)&.first
177
176
  unless record
178
- raise RecordNotFound.new("Couldn't find #{name} with '#{primary_key}'=#{id}",
179
- name, primary_key, id)
177
+ raise RecordNotFound.new("Couldn't find #{name} with '#{key}'=#{id}", name, key, id)
180
178
  end
181
179
  record
182
180
  end
183
181
 
184
182
  def find_by(*args) # :nodoc:
185
- return super if scope_attributes? || reflect_on_all_aggregations.any?
183
+ return super if scope_attributes? || reflect_on_all_aggregations.any? ||
184
+ columns_hash.key?(inheritance_column) && !base_class?
186
185
 
187
186
  hash = args.first
188
187
 
@@ -269,7 +268,8 @@ module ActiveRecord
269
268
  end
270
269
 
271
270
  def arel_attribute(name, table = arel_table) # :nodoc:
272
- name = attribute_alias(name) if attribute_alias?(name)
271
+ name = name.to_s
272
+ name = attribute_aliases[name] || name
273
273
  table[name]
274
274
  end
275
275
 
@@ -317,7 +317,7 @@ module ActiveRecord
317
317
  # # Instantiates a single new object
318
318
  # User.new(first_name: 'Jamie')
319
319
  def initialize(attributes = nil)
320
- self.class.define_attribute_methods
320
+ @new_record = true
321
321
  @attributes = self.class._default_attributes.deep_dup
322
322
 
323
323
  init_internals
@@ -354,12 +354,10 @@ module ActiveRecord
354
354
  # +attributes+ should be an attributes object, and unlike the
355
355
  # `initialize` method, no assignment calls are made per attribute.
356
356
  def init_with_attributes(attributes, new_record = false) # :nodoc:
357
- init_internals
358
-
359
357
  @new_record = new_record
360
358
  @attributes = attributes
361
359
 
362
- self.class.define_attribute_methods
360
+ init_internals
363
361
 
364
362
  yield self if block_given?
365
363
 
@@ -398,13 +396,13 @@ module ActiveRecord
398
396
  ##
399
397
  def initialize_dup(other) # :nodoc:
400
398
  @attributes = @attributes.deep_dup
401
- @attributes.reset(self.class.primary_key)
399
+ @attributes.reset(@primary_key)
402
400
 
403
401
  _run_initialize_callbacks
404
402
 
405
403
  @new_record = true
406
404
  @destroyed = false
407
- @_start_transaction_state = {}
405
+ @_start_transaction_state = nil
408
406
  @transaction_state = nil
409
407
 
410
408
  super
@@ -465,6 +463,7 @@ module ActiveRecord
465
463
 
466
464
  # Returns +true+ if the attributes hash has been frozen.
467
465
  def frozen?
466
+ sync_with_transaction_state if @transaction_state&.finalized?
468
467
  @attributes.frozen?
469
468
  end
470
469
 
@@ -569,22 +568,18 @@ module ActiveRecord
569
568
  end
570
569
 
571
570
  def init_internals
571
+ @primary_key = self.class.primary_key
572
572
  @readonly = false
573
573
  @destroyed = false
574
574
  @marked_for_destruction = false
575
575
  @destroyed_by_association = nil
576
- @new_record = true
577
- @_start_transaction_state = {}
576
+ @_start_transaction_state = nil
578
577
  @transaction_state = nil
579
- end
580
578
 
581
- def initialize_internals_callback
579
+ self.class.define_attribute_methods
582
580
  end
583
581
 
584
- def thaw
585
- if frozen?
586
- @attributes = @attributes.dup
587
- end
582
+ def initialize_internals_callback
588
583
  end
589
584
 
590
585
  def custom_inspect_method_defined?