activerecord 7.1.5.2 → 8.1.2.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.
Files changed (244) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +567 -2443
  3. data/README.rdoc +16 -16
  4. data/examples/performance.rb +2 -2
  5. data/lib/active_record/association_relation.rb +3 -2
  6. data/lib/active_record/associations/alias_tracker.rb +31 -23
  7. data/lib/active_record/associations/association.rb +43 -12
  8. data/lib/active_record/associations/belongs_to_association.rb +31 -8
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  10. data/lib/active_record/associations/builder/association.rb +23 -11
  11. data/lib/active_record/associations/builder/belongs_to.rb +18 -4
  12. data/lib/active_record/associations/builder/collection_association.rb +7 -3
  13. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
  14. data/lib/active_record/associations/builder/has_many.rb +3 -4
  15. data/lib/active_record/associations/builder/has_one.rb +3 -4
  16. data/lib/active_record/associations/builder/singular_association.rb +33 -5
  17. data/lib/active_record/associations/collection_association.rb +17 -9
  18. data/lib/active_record/associations/collection_proxy.rb +36 -5
  19. data/lib/active_record/associations/deprecation.rb +88 -0
  20. data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
  21. data/lib/active_record/associations/errors.rb +268 -0
  22. data/lib/active_record/associations/has_many_association.rb +1 -1
  23. data/lib/active_record/associations/has_many_through_association.rb +10 -3
  24. data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
  25. data/lib/active_record/associations/join_dependency.rb +4 -2
  26. data/lib/active_record/associations/nested_error.rb +47 -0
  27. data/lib/active_record/associations/preloader/association.rb +4 -3
  28. data/lib/active_record/associations/preloader/batch.rb +7 -1
  29. data/lib/active_record/associations/preloader/branch.rb +8 -1
  30. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  31. data/lib/active_record/associations/singular_association.rb +14 -3
  32. data/lib/active_record/associations/through_association.rb +1 -1
  33. data/lib/active_record/associations.rb +250 -315
  34. data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
  35. data/lib/active_record/attribute_assignment.rb +0 -2
  36. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  37. data/lib/active_record/attribute_methods/primary_key.rb +25 -61
  38. data/lib/active_record/attribute_methods/query.rb +34 -0
  39. data/lib/active_record/attribute_methods/read.rb +1 -13
  40. data/lib/active_record/attribute_methods/serialization.rb +20 -27
  41. data/lib/active_record/attribute_methods/time_zone_conversion.rb +19 -20
  42. data/lib/active_record/attribute_methods.rb +71 -75
  43. data/lib/active_record/attributes.rb +96 -68
  44. data/lib/active_record/autosave_association.rb +93 -58
  45. data/lib/active_record/base.rb +5 -7
  46. data/lib/active_record/callbacks.rb +1 -1
  47. data/lib/active_record/coders/json.rb +14 -5
  48. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +50 -126
  49. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -4
  50. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +52 -13
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +681 -135
  52. data/lib/active_record/connection_adapters/abstract/database_statements.rb +165 -86
  53. data/lib/active_record/connection_adapters/abstract/query_cache.rb +226 -78
  54. data/lib/active_record/connection_adapters/abstract/quoting.rb +79 -114
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +11 -7
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +37 -36
  57. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
  58. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +136 -34
  59. data/lib/active_record/connection_adapters/abstract/transaction.rb +163 -68
  60. data/lib/active_record/connection_adapters/abstract_adapter.rb +197 -129
  61. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +112 -63
  62. data/lib/active_record/connection_adapters/column.rb +17 -4
  63. data/lib/active_record/connection_adapters/mysql/database_statements.rb +11 -3
  64. data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -57
  65. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
  66. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +41 -10
  67. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +79 -46
  68. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +97 -101
  69. data/lib/active_record/connection_adapters/mysql2_adapter.rb +14 -33
  70. data/lib/active_record/connection_adapters/pool_config.rb +14 -13
  71. data/lib/active_record/connection_adapters/postgresql/column.rb +4 -0
  72. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +98 -44
  73. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -3
  74. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  75. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
  77. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  78. data/lib/active_record/connection_adapters/postgresql/quoting.rb +79 -68
  79. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
  80. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +9 -17
  81. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +44 -41
  82. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +69 -32
  83. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +155 -75
  84. data/lib/active_record/connection_adapters/postgresql_adapter.rb +104 -121
  85. data/lib/active_record/connection_adapters/schema_cache.rb +126 -133
  86. data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
  87. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +91 -95
  88. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -54
  89. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +16 -0
  90. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
  91. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +43 -2
  92. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -15
  93. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +231 -111
  94. data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
  95. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +40 -69
  96. data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -65
  97. data/lib/active_record/connection_adapters.rb +66 -0
  98. data/lib/active_record/connection_handling.rb +88 -46
  99. data/lib/active_record/core.rb +142 -58
  100. data/lib/active_record/counter_cache.rb +52 -18
  101. data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
  102. data/lib/active_record/database_configurations/database_config.rb +28 -5
  103. data/lib/active_record/database_configurations/hash_config.rb +103 -41
  104. data/lib/active_record/database_configurations/url_config.rb +30 -1
  105. data/lib/active_record/database_configurations.rb +8 -4
  106. data/lib/active_record/delegated_type.rb +43 -19
  107. data/lib/active_record/dynamic_matchers.rb +56 -71
  108. data/lib/active_record/encryption/config.rb +3 -1
  109. data/lib/active_record/encryption/encryptable_record.rb +12 -12
  110. data/lib/active_record/encryption/encrypted_attribute_type.rb +35 -6
  111. data/lib/active_record/encryption/encryptor.rb +58 -22
  112. data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
  113. data/lib/active_record/encryption/key_provider.rb +1 -1
  114. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  115. data/lib/active_record/encryption/message_serializer.rb +4 -0
  116. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  117. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  118. data/lib/active_record/encryption/scheme.rb +9 -2
  119. data/lib/active_record/enum.rb +57 -36
  120. data/lib/active_record/errors.rb +76 -26
  121. data/lib/active_record/explain.rb +14 -25
  122. data/lib/active_record/explain_registry.rb +51 -2
  123. data/lib/active_record/filter_attribute_handler.rb +73 -0
  124. data/lib/active_record/fixture_set/table_row.rb +19 -2
  125. data/lib/active_record/fixtures.rb +39 -35
  126. data/lib/active_record/future_result.rb +21 -13
  127. data/lib/active_record/gem_version.rb +3 -3
  128. data/lib/active_record/inheritance.rb +5 -3
  129. data/lib/active_record/insert_all.rb +28 -20
  130. data/lib/active_record/integration.rb +4 -1
  131. data/lib/active_record/internal_metadata.rb +48 -34
  132. data/lib/active_record/locking/optimistic.rb +15 -7
  133. data/lib/active_record/locking/pessimistic.rb +5 -0
  134. data/lib/active_record/log_subscriber.rb +3 -34
  135. data/lib/active_record/message_pack.rb +1 -1
  136. data/lib/active_record/middleware/shard_selector.rb +34 -17
  137. data/lib/active_record/migration/command_recorder.rb +46 -14
  138. data/lib/active_record/migration/compatibility.rb +40 -25
  139. data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
  140. data/lib/active_record/migration/default_strategy.rb +4 -5
  141. data/lib/active_record/migration/pending_migration_connection.rb +2 -2
  142. data/lib/active_record/migration.rb +119 -103
  143. data/lib/active_record/model_schema.rb +66 -78
  144. data/lib/active_record/nested_attributes.rb +17 -9
  145. data/lib/active_record/persistence.rb +161 -454
  146. data/lib/active_record/query_cache.rb +33 -15
  147. data/lib/active_record/query_logs.rb +106 -39
  148. data/lib/active_record/query_logs_formatter.rb +17 -28
  149. data/lib/active_record/querying.rb +28 -16
  150. data/lib/active_record/railtie.rb +61 -74
  151. data/lib/active_record/railties/controller_runtime.rb +19 -5
  152. data/lib/active_record/railties/databases.rake +64 -78
  153. data/lib/active_record/railties/job_checkpoints.rb +15 -0
  154. data/lib/active_record/railties/job_runtime.rb +10 -11
  155. data/lib/active_record/reflection.rb +147 -53
  156. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  157. data/lib/active_record/relation/batches.rb +153 -73
  158. data/lib/active_record/relation/calculations.rb +146 -96
  159. data/lib/active_record/relation/delegation.rb +30 -23
  160. data/lib/active_record/relation/finder_methods.rb +64 -34
  161. data/lib/active_record/relation/merger.rb +12 -14
  162. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  163. data/lib/active_record/relation/predicate_builder/association_query_value.rb +19 -11
  164. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +8 -8
  165. data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
  166. data/lib/active_record/relation/predicate_builder.rb +25 -10
  167. data/lib/active_record/relation/query_attribute.rb +4 -2
  168. data/lib/active_record/relation/query_methods.rb +351 -124
  169. data/lib/active_record/relation/spawn_methods.rb +9 -25
  170. data/lib/active_record/relation/where_clause.rb +17 -30
  171. data/lib/active_record/relation.rb +590 -124
  172. data/lib/active_record/result.rb +131 -59
  173. data/lib/active_record/runtime_registry.rb +44 -21
  174. data/lib/active_record/sanitization.rb +33 -25
  175. data/lib/active_record/schema.rb +8 -6
  176. data/lib/active_record/schema_dumper.rb +66 -31
  177. data/lib/active_record/schema_migration.rb +31 -14
  178. data/lib/active_record/scoping/named.rb +6 -2
  179. data/lib/active_record/scoping.rb +0 -1
  180. data/lib/active_record/secure_token.rb +3 -3
  181. data/lib/active_record/signed_id.rb +68 -20
  182. data/lib/active_record/statement_cache.rb +29 -25
  183. data/lib/active_record/store.rb +51 -22
  184. data/lib/active_record/structured_event_subscriber.rb +85 -0
  185. data/lib/active_record/table_metadata.rb +7 -33
  186. data/lib/active_record/tasks/abstract_tasks.rb +76 -0
  187. data/lib/active_record/tasks/database_tasks.rb +118 -90
  188. data/lib/active_record/tasks/mysql_database_tasks.rb +3 -42
  189. data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
  190. data/lib/active_record/tasks/sqlite_database_tasks.rb +17 -28
  191. data/lib/active_record/test_databases.rb +14 -4
  192. data/lib/active_record/test_fixtures.rb +123 -89
  193. data/lib/active_record/testing/query_assertions.rb +127 -0
  194. data/lib/active_record/timestamp.rb +6 -4
  195. data/lib/active_record/token_for.rb +22 -12
  196. data/lib/active_record/touch_later.rb +1 -1
  197. data/lib/active_record/transaction.rb +129 -0
  198. data/lib/active_record/transactions.rb +97 -18
  199. data/lib/active_record/translation.rb +0 -2
  200. data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
  201. data/lib/active_record/type/internal/timezone.rb +7 -0
  202. data/lib/active_record/type/json.rb +15 -2
  203. data/lib/active_record/type/serialized.rb +12 -7
  204. data/lib/active_record/type/type_map.rb +1 -1
  205. data/lib/active_record/type_caster/connection.rb +5 -4
  206. data/lib/active_record/validations/associated.rb +10 -4
  207. data/lib/active_record/validations/uniqueness.rb +23 -18
  208. data/lib/active_record/validations.rb +4 -1
  209. data/lib/active_record.rb +203 -59
  210. data/lib/arel/alias_predication.rb +3 -1
  211. data/lib/arel/collectors/bind.rb +4 -2
  212. data/lib/arel/collectors/composite.rb +7 -0
  213. data/lib/arel/collectors/sql_string.rb +2 -2
  214. data/lib/arel/collectors/substitute_binds.rb +3 -3
  215. data/lib/arel/crud.rb +8 -11
  216. data/lib/arel/delete_manager.rb +5 -0
  217. data/lib/arel/nodes/binary.rb +1 -7
  218. data/lib/arel/nodes/bound_sql_literal.rb +9 -5
  219. data/lib/arel/nodes/count.rb +2 -2
  220. data/lib/arel/nodes/delete_statement.rb +4 -2
  221. data/lib/arel/nodes/function.rb +4 -10
  222. data/lib/arel/nodes/named_function.rb +2 -2
  223. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  224. data/lib/arel/nodes/node.rb +6 -5
  225. data/lib/arel/nodes/sql_literal.rb +8 -1
  226. data/lib/arel/nodes/update_statement.rb +4 -2
  227. data/lib/arel/nodes.rb +2 -4
  228. data/lib/arel/predications.rb +1 -1
  229. data/lib/arel/select_manager.rb +14 -5
  230. data/lib/arel/table.rb +3 -7
  231. data/lib/arel/tree_manager.rb +3 -2
  232. data/lib/arel/update_manager.rb +7 -1
  233. data/lib/arel/visitors/dot.rb +3 -3
  234. data/lib/arel/visitors/mysql.rb +9 -4
  235. data/lib/arel/visitors/postgresql.rb +56 -12
  236. data/lib/arel/visitors/sqlite.rb +80 -8
  237. data/lib/arel/visitors/to_sql.rb +34 -37
  238. data/lib/arel.rb +10 -4
  239. data/lib/rails/generators/active_record/application_record/USAGE +1 -1
  240. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  241. metadata +23 -14
  242. data/lib/active_record/explain_subscriber.rb +0 -34
  243. data/lib/active_record/normalization.rb +0 -167
  244. data/lib/active_record/relation/record_fetch_warning.rb +0 -49
@@ -45,7 +45,7 @@ module ActiveRecord
45
45
  # Example:
46
46
  # ActiveRecord::Tasks::DatabaseTasks.structure_dump_flags = {
47
47
  # mysql2: ['--no-defaults', '--skip-add-drop-table'],
48
- # postgres: '--no-tablespaces'
48
+ # postgresql: '--no-tablespaces'
49
49
  # }
50
50
  mattr_accessor :structure_dump_flags, instance_accessor: false
51
51
 
@@ -66,7 +66,7 @@ module ActiveRecord
66
66
  return if ENV["DISABLE_DATABASE_ENVIRONMENT_CHECK"]
67
67
 
68
68
  configs_for(env_name: environment).each do |db_config|
69
- check_current_protected_environment!(db_config)
69
+ database_adapter_for(db_config).check_current_protected_environment!(db_config, migration_class)
70
70
  end
71
71
  end
72
72
 
@@ -125,11 +125,11 @@ module ActiveRecord
125
125
  end
126
126
 
127
127
  def create_all
128
- each_local_configuration do |db_config|
129
- with_temporary_connection(db_config) do
130
- create(db_config)
131
- end
132
- end
128
+ db_config = migration_connection.pool.db_config
129
+
130
+ each_local_configuration { |db_config| create(db_config) }
131
+
132
+ migration_class.establish_connection(db_config)
133
133
  end
134
134
 
135
135
  def setup_initial_database_yaml # :nodoc:
@@ -147,8 +147,6 @@ module ActiveRecord
147
147
  return if database_configs.count == 1
148
148
 
149
149
  database_configs.each do |db_config|
150
- next unless db_config.database_tasks?
151
-
152
150
  yield db_config.name
153
151
  end
154
152
  end
@@ -178,22 +176,9 @@ module ActiveRecord
178
176
  dump_db_configs = []
179
177
 
180
178
  each_current_configuration(env) do |db_config|
181
- with_temporary_pool(db_config) do
182
- begin
183
- database_initialized = migration_connection.schema_migration.table_exists?
184
- rescue ActiveRecord::NoDatabaseError
185
- create(db_config)
186
- retry
187
- end
188
-
189
- unless database_initialized
190
- if File.exist?(schema_dump_path(db_config))
191
- load_schema(db_config, ActiveRecord.schema_format, nil)
192
- end
179
+ database_initialized = initialize_database(db_config)
193
180
 
194
- seed = true
195
- end
196
- end
181
+ seed = true if database_initialized && db_config.seeds?
197
182
  end
198
183
 
199
184
  each_current_environment(env) do |environment|
@@ -253,13 +238,34 @@ module ActiveRecord
253
238
  end
254
239
  end
255
240
 
256
- def migrate(version = nil)
241
+ def migrate_all
242
+ db_configs = ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env)
243
+ db_configs.each { |db_config| initialize_database(db_config) }
244
+
245
+ if db_configs.size == 1 && db_configs.first.primary?
246
+ ActiveRecord::Tasks::DatabaseTasks.migrate(skip_initialize: true)
247
+ else
248
+ mapped_versions = ActiveRecord::Tasks::DatabaseTasks.db_configs_with_versions
249
+
250
+ mapped_versions.sort.each do |version, db_configs|
251
+ db_configs.each do |db_config|
252
+ ActiveRecord::Tasks::DatabaseTasks.with_temporary_connection(db_config) do
253
+ ActiveRecord::Tasks::DatabaseTasks.migrate(version, skip_initialize: true)
254
+ end
255
+ end
256
+ end
257
+ end
258
+ end
259
+
260
+ def migrate(version = nil, skip_initialize: false)
257
261
  scope = ENV["SCOPE"]
258
262
  verbose_was, Migration.verbose = Migration.verbose, verbose?
259
263
 
260
264
  check_target_version
261
265
 
262
- migration_connection.migration_context.migrate(target_version) do |migration|
266
+ initialize_database(migration_connection_pool.db_config) unless skip_initialize
267
+
268
+ migration_connection_pool.migration_context.migrate(target_version) do |migration|
263
269
  if version.blank?
264
270
  scope.blank? || scope == migration.scope
265
271
  else
@@ -269,7 +275,7 @@ module ActiveRecord
269
275
  Migration.write("No migrations ran. (using #{scope} scope)") if scope.present? && migrations_ran.empty?
270
276
  end
271
277
 
272
- migration_connection.schema_cache.clear!
278
+ migration_connection_pool.schema_cache.clear!
273
279
  ensure
274
280
  Migration.verbose = verbose_was
275
281
  end
@@ -277,9 +283,9 @@ module ActiveRecord
277
283
  def db_configs_with_versions(environment = env) # :nodoc:
278
284
  db_configs_with_versions = Hash.new { |h, k| h[k] = [] }
279
285
 
280
- with_temporary_connection_for_each(env: environment) do |conn|
281
- db_config = conn.pool.db_config
282
- versions_to_run = conn.migration_context.pending_migration_versions
286
+ with_temporary_pool_for_each(env: environment) do |pool|
287
+ db_config = pool.db_config
288
+ versions_to_run = pool.migration_context.pending_migration_versions
283
289
  target_version = ActiveRecord::Tasks::DatabaseTasks.target_version
284
290
 
285
291
  versions_to_run.each do |version|
@@ -292,15 +298,15 @@ module ActiveRecord
292
298
  end
293
299
 
294
300
  def migrate_status
295
- unless migration_connection.schema_migration.table_exists?
301
+ unless migration_connection_pool.schema_migration.table_exists?
296
302
  Kernel.abort "Schema migrations table does not exist yet."
297
303
  end
298
304
 
299
305
  # output
300
- puts "\ndatabase: #{migration_connection.pool.db_config.database}\n\n"
306
+ puts "\ndatabase: #{migration_connection_pool.db_config.database}\n\n"
301
307
  puts "#{'Status'.center(8)} #{'Migration ID'.ljust(14)} Migration Name"
302
308
  puts "-" * 50
303
- migration_connection.migration_context.migrations_status.each do |status, version, name|
309
+ migration_connection_pool.migration_context.migrations_status.each do |status, version, name|
304
310
  puts "#{status.center(8)} #{version.ljust(14)} #{name}"
305
311
  end
306
312
  puts
@@ -365,7 +371,8 @@ module ActiveRecord
365
371
  database_adapter_for(db_config, *arguments).structure_load(filename, flags)
366
372
  end
367
373
 
368
- def load_schema(db_config, format = ActiveRecord.schema_format, file = nil) # :nodoc:
374
+ def load_schema(db_config, format = db_config.schema_format, file = nil) # :nodoc:
375
+ format = format.to_sym
369
376
  file ||= schema_dump_path(db_config, format)
370
377
  return unless file
371
378
 
@@ -381,69 +388,87 @@ module ActiveRecord
381
388
  raise ArgumentError, "unknown format #{format.inspect}"
382
389
  end
383
390
 
384
- migration_connection.internal_metadata.create_table_and_set_flags(db_config.env_name, schema_sha1(file))
391
+ migration_connection_pool.internal_metadata.create_table_and_set_flags(db_config.env_name, schema_sha1(file))
385
392
  ensure
386
393
  Migration.verbose = verbose_was
387
394
  end
388
395
 
389
- def schema_up_to_date?(configuration, format = ActiveRecord.schema_format, file = nil)
396
+ def schema_up_to_date?(configuration, _ = nil, file = nil)
390
397
  db_config = resolve_configuration(configuration)
391
398
 
392
399
  file ||= schema_dump_path(db_config)
393
400
 
394
401
  return true unless file && File.exist?(file)
395
402
 
396
- with_temporary_connection(db_config) do |connection|
397
- return false unless connection.internal_metadata.enabled?
398
- return false unless connection.internal_metadata.table_exists?
403
+ with_temporary_pool(db_config) do |pool|
404
+ internal_metadata = pool.internal_metadata
405
+ return false unless internal_metadata.enabled?
406
+ return false unless internal_metadata.table_exists?
399
407
 
400
- connection.internal_metadata[:schema_sha1] == schema_sha1(file)
408
+ internal_metadata[:schema_sha1] == schema_sha1(file)
401
409
  end
402
410
  end
403
411
 
404
- def reconstruct_from_schema(db_config, format = ActiveRecord.schema_format, file = nil) # :nodoc:
405
- file ||= schema_dump_path(db_config, format)
412
+ def reconstruct_from_schema(db_config, file = nil) # :nodoc:
413
+ file ||= schema_dump_path(db_config, db_config.schema_format)
406
414
 
407
415
  check_schema_file(file) if file
408
416
 
409
417
  with_temporary_pool(db_config, clobber: true) do
410
- if schema_up_to_date?(db_config, format, file)
411
- truncate_tables(db_config)
418
+ if schema_up_to_date?(db_config, nil, file)
419
+ truncate_tables(db_config) unless ENV["SKIP_TEST_DATABASE_TRUNCATE"]
412
420
  else
413
421
  purge(db_config)
414
- load_schema(db_config, format, file)
422
+ load_schema(db_config, db_config.schema_format, file)
415
423
  end
416
424
  rescue ActiveRecord::NoDatabaseError
417
425
  create(db_config)
418
- load_schema(db_config, format, file)
426
+ load_schema(db_config, db_config.schema_format, file)
427
+ end
428
+ end
429
+
430
+ def dump_all
431
+ seen_schemas = []
432
+
433
+ ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env).each do |db_config|
434
+ schema_path = schema_dump_path(db_config, ENV["SCHEMA_FORMAT"] || db_config.schema_format)
435
+
436
+ next if seen_schemas.include?(schema_path)
437
+
438
+ ActiveRecord::Tasks::DatabaseTasks.dump_schema(db_config, ENV["SCHEMA_FORMAT"] || db_config.schema_format)
439
+ seen_schemas << schema_path
419
440
  end
420
441
  end
421
442
 
422
- def dump_schema(db_config, format = ActiveRecord.schema_format) # :nodoc:
443
+ def dump_schema(db_config, format = db_config.schema_format) # :nodoc:
423
444
  return unless db_config.schema_dump
424
445
 
425
446
  require "active_record/schema_dumper"
426
447
  filename = schema_dump_path(db_config, format)
427
448
  return unless filename
428
449
 
429
- FileUtils.mkdir_p(db_dir)
430
- case format
431
- when :ruby
432
- File.open(filename, "w:utf-8") do |file|
433
- ActiveRecord::SchemaDumper.dump(migration_connection, file)
434
- end
435
- when :sql
436
- structure_dump(db_config, filename)
437
- if migration_connection.schema_migration.table_exists?
438
- File.open(filename, "a") do |f|
439
- f.puts migration_connection.dump_schema_information
440
- f.print "\n"
450
+ with_temporary_pool(db_config) do |pool|
451
+ FileUtils.mkdir_p(db_dir)
452
+ case format.to_sym
453
+ when :ruby
454
+ File.open(filename, "w:utf-8") do |file|
455
+ ActiveRecord::SchemaDumper.dump(pool, file)
456
+ end
457
+ when :sql
458
+ structure_dump(db_config, filename)
459
+ if pool.schema_migration.table_exists?
460
+ File.open(filename, "a") do |f|
461
+ pool.with_connection do |connection|
462
+ f.puts connection.dump_schema_versions
463
+ end
464
+ f.print "\n"
465
+ end
441
466
  end
442
467
  end
443
468
  end
444
469
  end
445
470
 
446
- def schema_dump_path(db_config, format = ActiveRecord.schema_format)
471
+ def schema_dump_path(db_config, format = db_config.schema_format)
447
472
  return ENV["SCHEMA"] if ENV["SCHEMA"]
448
473
 
449
474
  filename = db_config.schema_dump(format)
@@ -456,20 +481,16 @@ module ActiveRecord
456
481
  end
457
482
  end
458
483
 
459
- def cache_dump_filename(db_config_name, schema_cache_path: nil)
460
- filename = if ActiveRecord::Base.configurations.primary?(db_config_name)
461
- "schema_cache.yml"
462
- else
463
- "#{db_config_name}_schema_cache.yml"
464
- end
465
-
466
- schema_cache_path || ENV["SCHEMA_CACHE"] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, filename)
484
+ def cache_dump_filename(db_config, schema_cache_path: nil)
485
+ schema_cache_path ||
486
+ db_config.schema_cache_path ||
487
+ db_config.default_schema_cache_path(ActiveRecord::Tasks::DatabaseTasks.db_dir)
467
488
  end
468
489
 
469
- def load_schema_current(format = ActiveRecord.schema_format, file = nil, environment = env)
490
+ def load_schema_current(format = nil, file = nil, environment = env)
470
491
  each_current_configuration(environment) do |db_config|
471
492
  with_temporary_connection(db_config) do
472
- load_schema(db_config, format, file)
493
+ load_schema(db_config, format || db_config.schema_format, file)
473
494
  end
474
495
  end
475
496
  end
@@ -495,29 +516,29 @@ module ActiveRecord
495
516
  # Dumps the schema cache in YAML format for the connection into the file
496
517
  #
497
518
  # ==== Examples
498
- # ActiveRecord::Tasks::DatabaseTasks.dump_schema_cache(ActiveRecord::Base.connection, "tmp/schema_dump.yaml")
499
- def dump_schema_cache(conn, filename)
500
- conn.schema_cache.dump_to(filename)
519
+ # ActiveRecord::Tasks::DatabaseTasks.dump_schema_cache(ActiveRecord::Base.lease_connection, "tmp/schema_dump.yaml")
520
+ def dump_schema_cache(conn_or_pool, filename)
521
+ conn_or_pool.schema_cache.dump_to(filename)
501
522
  end
502
523
 
503
524
  def clear_schema_cache(filename)
504
525
  FileUtils.rm_f filename, verbose: false
505
526
  end
506
527
 
507
- def with_temporary_connection_for_each(env: ActiveRecord::Tasks::DatabaseTasks.env, name: nil, clobber: false, &block) # :nodoc:
528
+ def with_temporary_pool_for_each(env: ActiveRecord::Tasks::DatabaseTasks.env, name: nil, clobber: false, &block) # :nodoc:
508
529
  if name
509
530
  db_config = ActiveRecord::Base.configurations.configs_for(env_name: env, name: name)
510
- with_temporary_connection(db_config, clobber: clobber, &block)
531
+ with_temporary_pool(db_config, clobber: clobber, &block)
511
532
  else
512
533
  ActiveRecord::Base.configurations.configs_for(env_name: env, name: name).each do |db_config|
513
- with_temporary_connection(db_config, clobber: clobber, &block)
534
+ with_temporary_pool(db_config, clobber: clobber, &block)
514
535
  end
515
536
  end
516
537
  end
517
538
 
518
- def with_temporary_connection(db_config, clobber: false) # :nodoc:
539
+ def with_temporary_connection(db_config, clobber: false, &block) # :nodoc:
519
540
  with_temporary_pool(db_config, clobber: clobber) do |pool|
520
- yield pool.connection
541
+ pool.with_connection(&block)
521
542
  end
522
543
  end
523
544
 
@@ -526,7 +547,11 @@ module ActiveRecord
526
547
  end
527
548
 
528
549
  def migration_connection # :nodoc:
529
- migration_class.connection
550
+ migration_class.lease_connection
551
+ end
552
+
553
+ def migration_connection_pool # :nodoc:
554
+ migration_class.connection_pool
530
555
  end
531
556
 
532
557
  private
@@ -623,20 +648,23 @@ module ActiveRecord
623
648
  end
624
649
  end
625
650
 
626
- def check_current_protected_environment!(db_config)
627
- with_temporary_pool(db_config) do |pool|
628
- connection = pool.connection
629
- current = connection.migration_context.current_environment
630
- stored = connection.migration_context.last_stored_environment
631
-
632
- if connection.migration_context.protected_environment?
633
- raise ActiveRecord::ProtectedEnvironmentError.new(stored)
651
+ def initialize_database(db_config)
652
+ with_temporary_pool(db_config) do
653
+ begin
654
+ database_already_initialized = migration_connection_pool.schema_migration.table_exists?
655
+ rescue ActiveRecord::NoDatabaseError
656
+ create(db_config)
657
+ retry
634
658
  end
635
659
 
636
- if stored && stored != current
637
- raise ActiveRecord::EnvironmentMismatchError.new(current: current, stored: stored)
660
+ unless database_already_initialized
661
+ schema_dump_path = schema_dump_path(db_config)
662
+ if schema_dump_path && File.exist?(schema_dump_path)
663
+ load_schema(db_config)
664
+ end
638
665
  end
639
- rescue ActiveRecord::NoDatabaseError
666
+
667
+ !database_already_initialized
640
668
  end
641
669
  end
642
670
  end
@@ -2,18 +2,7 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module Tasks # :nodoc:
5
- class MySQLDatabaseTasks # :nodoc:
6
- ER_DB_CREATE_EXISTS = 1007
7
-
8
- def self.using_database_configurations?
9
- true
10
- end
11
-
12
- def initialize(db_config)
13
- @db_config = db_config
14
- @configuration_hash = db_config.configuration_hash
15
- end
16
-
5
+ class MySQLDatabaseTasks < AbstractTasks # :nodoc:
17
6
  def create
18
7
  establish_connection(configuration_hash_without_database)
19
8
  connection.create_database(db_config.database, creation_options)
@@ -35,10 +24,6 @@ module ActiveRecord
35
24
  connection.charset
36
25
  end
37
26
 
38
- def collation
39
- connection.collation
40
- end
41
-
42
27
  def structure_dump(filename, extra_flags)
43
28
  args = prepare_command_options
44
29
  args.concat(["--result-file", "#{filename}"])
@@ -55,7 +40,7 @@ module ActiveRecord
55
40
  args.concat([db_config.database.to_s])
56
41
  args.unshift(*extra_flags) if extra_flags
57
42
 
58
- run_cmd("mysqldump", args, "dumping")
43
+ run_cmd("mysqldump", *args)
59
44
  end
60
45
 
61
46
  def structure_load(filename, extra_flags)
@@ -64,24 +49,10 @@ module ActiveRecord
64
49
  args.concat(["--database", db_config.database.to_s])
65
50
  args.unshift(*extra_flags) if extra_flags
66
51
 
67
- run_cmd("mysql", args, "loading")
52
+ run_cmd("mysql", *args)
68
53
  end
69
54
 
70
55
  private
71
- attr_reader :db_config, :configuration_hash
72
-
73
- def connection
74
- ActiveRecord::Base.connection
75
- end
76
-
77
- def establish_connection(config = db_config)
78
- ActiveRecord::Base.establish_connection(config)
79
- end
80
-
81
- def configuration_hash_without_database
82
- configuration_hash.merge(database: nil)
83
- end
84
-
85
56
  def creation_options
86
57
  Hash.new.tap do |options|
87
58
  options[:charset] = configuration_hash[:encoding] if configuration_hash.include?(:encoding)
@@ -107,16 +78,6 @@ module ActiveRecord
107
78
 
108
79
  args
109
80
  end
110
-
111
- def run_cmd(cmd, args, action)
112
- fail run_cmd_error(cmd, args, action) unless Kernel.system(cmd, *args)
113
- end
114
-
115
- def run_cmd_error(cmd, args, action)
116
- msg = +"failed to execute: `#{cmd}`\n"
117
- msg << "Please check the output above for any errors and make sure that `#{cmd}` is installed in your PATH and has proper permissions.\n\n"
118
- msg
119
- end
120
81
  end
121
82
  end
122
83
  end
@@ -4,20 +4,11 @@ require "tempfile"
4
4
 
5
5
  module ActiveRecord
6
6
  module Tasks # :nodoc:
7
- class PostgreSQLDatabaseTasks # :nodoc:
7
+ class PostgreSQLDatabaseTasks < AbstractTasks # :nodoc:
8
8
  DEFAULT_ENCODING = ENV["CHARSET"] || "utf8"
9
9
  ON_ERROR_STOP_1 = "ON_ERROR_STOP=1"
10
10
  SQL_COMMENT_BEGIN = "--"
11
11
 
12
- def self.using_database_configurations?
13
- true
14
- end
15
-
16
- def initialize(db_config)
17
- @db_config = db_config
18
- @configuration_hash = db_config.configuration_hash
19
- end
20
-
21
12
  def create(connection_already_established = false)
22
13
  establish_connection(public_schema_config) unless connection_already_established
23
14
  connection.create_database(db_config.database, configuration_hash.merge(encoding: encoding))
@@ -29,14 +20,6 @@ module ActiveRecord
29
20
  connection.drop_database(db_config.database)
30
21
  end
31
22
 
32
- def charset
33
- connection.encoding
34
- end
35
-
36
- def collation
37
- connection.collation
38
- end
39
-
40
23
  def purge
41
24
  ActiveRecord::Base.connection_handler.clear_active_connections!(:all)
42
25
  drop
@@ -72,29 +55,20 @@ module ActiveRecord
72
55
  end
73
56
 
74
57
  args << db_config.database
75
- run_cmd("pg_dump", args, "dumping")
58
+ run_cmd("pg_dump", *args)
76
59
  remove_sql_header_comments(filename)
77
60
  File.open(filename, "a") { |f| f << "SET search_path TO #{connection.schema_search_path};\n\n" }
78
61
  end
79
62
 
80
63
  def structure_load(filename, extra_flags)
81
- args = ["--set", ON_ERROR_STOP_1, "--quiet", "--no-psqlrc", "--output", File::NULL, "--file", filename]
64
+ args = ["--set", ON_ERROR_STOP_1, "--quiet", "--no-psqlrc", "--output", File::NULL]
82
65
  args.concat(Array(extra_flags)) if extra_flags
66
+ args.concat(["--file", filename])
83
67
  args << db_config.database
84
- run_cmd("psql", args, "loading")
68
+ run_cmd("psql", *args)
85
69
  end
86
70
 
87
71
  private
88
- attr_reader :db_config, :configuration_hash
89
-
90
- def connection
91
- ActiveRecord::Base.connection
92
- end
93
-
94
- def establish_connection(config = db_config)
95
- ActiveRecord::Base.establish_connection(config)
96
- end
97
-
98
72
  def encoding
99
73
  configuration_hash[:encoding] || DEFAULT_ENCODING
100
74
  end
@@ -116,15 +90,8 @@ module ActiveRecord
116
90
  end
117
91
  end
118
92
 
119
- def run_cmd(cmd, args, action)
120
- fail run_cmd_error(cmd, args, action) unless Kernel.system(psql_env, cmd, *args)
121
- end
122
-
123
- def run_cmd_error(cmd, args, action)
124
- msg = +"failed to execute:\n"
125
- msg << "#{cmd} #{args.join(' ')}\n\n"
126
- msg << "Please check the output above for any errors and make sure that `#{cmd}` is installed in your PATH and has proper permissions.\n\n"
127
- msg
93
+ def run_cmd(cmd, *args, **opts)
94
+ fail run_cmd_error(cmd, args) unless Kernel.system(psql_env, cmd, *args, **opts)
128
95
  end
129
96
 
130
97
  def remove_sql_header_comments(filename)
@@ -132,6 +99,13 @@ module ActiveRecord
132
99
  tempfile = Tempfile.open("uncommented_structure.sql")
133
100
  begin
134
101
  File.foreach(filename) do |line|
102
+ next if line.start_with?("\\restrict ")
103
+
104
+ if line.start_with?("\\unrestrict ")
105
+ removing_comments = true
106
+ next
107
+ end
108
+
135
109
  unless removing_comments && (line.start_with?(SQL_COMMENT_BEGIN) || line.blank?)
136
110
  tempfile << line
137
111
  removing_comments = false
@@ -2,11 +2,7 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module Tasks # :nodoc:
5
- class SQLiteDatabaseTasks # :nodoc:
6
- def self.using_database_configurations?
7
- true
8
- end
9
-
5
+ class SQLiteDatabaseTasks < AbstractTasks # :nodoc:
10
6
  def initialize(db_config, root = ActiveRecord::Tasks::DatabaseTasks.root)
11
7
  @db_config = db_config
12
8
  @root = root
@@ -37,10 +33,6 @@ module ActiveRecord
37
33
  connection.reconnect!
38
34
  end
39
35
 
40
- def charset
41
- connection.encoding
42
- end
43
-
44
36
  def structure_dump(filename, extra_flags)
45
37
  args = []
46
38
  args.concat(Array(extra_flags)) if extra_flags
@@ -50,11 +42,12 @@ module ActiveRecord
50
42
  if ignore_tables.any?
51
43
  ignore_tables = connection.data_sources.select { |table| ignore_tables.any? { |pattern| pattern === table } }
52
44
  condition = ignore_tables.map { |table| connection.quote(table) }.join(", ")
53
- args << "SELECT sql FROM sqlite_master WHERE tbl_name NOT IN (#{condition}) ORDER BY tbl_name, type DESC, name"
45
+ args << "SELECT sql || ';' FROM sqlite_master WHERE tbl_name NOT IN (#{condition}) ORDER BY tbl_name, type DESC, name"
54
46
  else
55
- args << ".schema"
47
+ args << ".schema --nosys"
56
48
  end
57
- run_cmd("sqlite3", args, filename)
49
+
50
+ run_cmd("sqlite3", *args, out: filename)
58
51
  end
59
52
 
60
53
  def structure_load(filename, extra_flags)
@@ -62,26 +55,22 @@ module ActiveRecord
62
55
  `sqlite3 #{flags} #{db_config.database} < "#{filename}"`
63
56
  end
64
57
 
65
- private
66
- attr_reader :db_config, :root
67
-
68
- def connection
69
- ActiveRecord::Base.connection
58
+ def check_current_protected_environment!(db_config, migration_class)
59
+ super
60
+ rescue ActiveRecord::StatementInvalid => e
61
+ case e.cause
62
+ when SQLite3::ReadOnlyException
63
+ else
64
+ raise e
70
65
  end
66
+ end
67
+
68
+ private
69
+ attr_reader :root
71
70
 
72
71
  def establish_connection(config = db_config)
73
72
  ActiveRecord::Base.establish_connection(config)
74
- end
75
-
76
- def run_cmd(cmd, args, out)
77
- fail run_cmd_error(cmd, args) unless Kernel.system(cmd, *args, out: out)
78
- end
79
-
80
- def run_cmd_error(cmd, args)
81
- msg = +"failed to execute:\n"
82
- msg << "#{cmd} #{args.join(' ')}\n\n"
83
- msg << "Please check the output above for any errors and make sure that `#{cmd}` is installed in your PATH and has proper permissions.\n\n"
84
- msg
73
+ connection.connect!
85
74
  end
86
75
  end
87
76
  end
@@ -4,17 +4,27 @@ require "active_support/testing/parallelization"
4
4
 
5
5
  module ActiveRecord
6
6
  module TestDatabases # :nodoc:
7
+ ActiveSupport::Testing::Parallelization.before_fork_hook do
8
+ if ActiveSupport.parallelize_test_databases
9
+ ActiveRecord::Base.connection_handler.clear_all_connections!
10
+ end
11
+ end
12
+
7
13
  ActiveSupport::Testing::Parallelization.after_fork_hook do |i|
8
- create_and_load_schema(i, env_name: ActiveRecord::ConnectionHandling::DEFAULT_ENV.call)
14
+ if ActiveSupport.parallelize_test_databases
15
+ create_and_load_schema(i, env_name: ActiveRecord::ConnectionHandling::DEFAULT_ENV.call)
16
+ end
9
17
  end
10
18
 
11
19
  def self.create_and_load_schema(i, env_name:)
12
20
  old, ENV["VERBOSE"] = ENV["VERBOSE"], "false"
13
21
 
14
- ActiveRecord::Base.configurations.configs_for(env_name: env_name).each do |db_config|
15
- db_config._database = "#{db_config.database}-#{i}"
22
+ ActiveRecord::Base.configurations.configs_for(env_name: env_name, include_hidden: true).each do |db_config|
23
+ db_config._database = "#{db_config.database}_#{i}"
16
24
 
17
- ActiveRecord::Tasks::DatabaseTasks.reconstruct_from_schema(db_config, ActiveRecord.schema_format, nil)
25
+ if db_config.database_tasks?
26
+ ActiveRecord::Tasks::DatabaseTasks.reconstruct_from_schema(db_config, nil)
27
+ end
18
28
  end
19
29
  ensure
20
30
  ActiveRecord::Base.establish_connection