activerecord 6.1.7 → 7.1.5

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 (311) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +2030 -1020
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +18 -18
  5. data/lib/active_record/aggregations.rb +17 -14
  6. data/lib/active_record/association_relation.rb +1 -11
  7. data/lib/active_record/associations/association.rb +51 -19
  8. data/lib/active_record/associations/association_scope.rb +17 -12
  9. data/lib/active_record/associations/belongs_to_association.rb +28 -9
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  11. data/lib/active_record/associations/builder/association.rb +11 -5
  12. data/lib/active_record/associations/builder/belongs_to.rb +40 -14
  13. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  14. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
  15. data/lib/active_record/associations/builder/has_many.rb +3 -2
  16. data/lib/active_record/associations/builder/has_one.rb +2 -1
  17. data/lib/active_record/associations/builder/singular_association.rb +6 -2
  18. data/lib/active_record/associations/collection_association.rb +39 -35
  19. data/lib/active_record/associations/collection_proxy.rb +30 -15
  20. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  21. data/lib/active_record/associations/foreign_association.rb +10 -3
  22. data/lib/active_record/associations/has_many_association.rb +28 -18
  23. data/lib/active_record/associations/has_many_through_association.rb +12 -7
  24. data/lib/active_record/associations/has_one_association.rb +20 -10
  25. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  26. data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
  27. data/lib/active_record/associations/join_dependency.rb +28 -20
  28. data/lib/active_record/associations/preloader/association.rb +210 -52
  29. data/lib/active_record/associations/preloader/batch.rb +48 -0
  30. data/lib/active_record/associations/preloader/branch.rb +147 -0
  31. data/lib/active_record/associations/preloader/through_association.rb +50 -14
  32. data/lib/active_record/associations/preloader.rb +50 -121
  33. data/lib/active_record/associations/singular_association.rb +9 -3
  34. data/lib/active_record/associations/through_association.rb +25 -14
  35. data/lib/active_record/associations.rb +446 -306
  36. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  37. data/lib/active_record/attribute_assignment.rb +1 -3
  38. data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
  39. data/lib/active_record/attribute_methods/dirty.rb +73 -22
  40. data/lib/active_record/attribute_methods/primary_key.rb +78 -26
  41. data/lib/active_record/attribute_methods/query.rb +31 -19
  42. data/lib/active_record/attribute_methods/read.rb +27 -12
  43. data/lib/active_record/attribute_methods/serialization.rb +194 -37
  44. data/lib/active_record/attribute_methods/time_zone_conversion.rb +8 -3
  45. data/lib/active_record/attribute_methods/write.rb +12 -15
  46. data/lib/active_record/attribute_methods.rb +161 -40
  47. data/lib/active_record/attributes.rb +27 -38
  48. data/lib/active_record/autosave_association.rb +65 -31
  49. data/lib/active_record/base.rb +25 -2
  50. data/lib/active_record/callbacks.rb +18 -34
  51. data/lib/active_record/coders/column_serializer.rb +61 -0
  52. data/lib/active_record/coders/json.rb +1 -1
  53. data/lib/active_record/coders/yaml_column.rb +70 -46
  54. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +367 -0
  55. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +113 -597
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +172 -50
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +78 -27
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +87 -73
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +367 -141
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +281 -59
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +631 -150
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +317 -164
  70. data/lib/active_record/connection_adapters/column.rb +13 -0
  71. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  72. data/lib/active_record/connection_adapters/mysql/database_statements.rb +25 -134
  73. data/lib/active_record/connection_adapters/mysql/quoting.rb +56 -25
  74. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  76. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  77. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +39 -14
  78. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
  79. data/lib/active_record/connection_adapters/mysql2_adapter.rb +112 -55
  80. data/lib/active_record/connection_adapters/pool_config.rb +20 -11
  81. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +89 -52
  84. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  89. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
  91. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  94. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -56
  96. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
  97. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
  98. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -3
  99. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
  100. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +397 -75
  101. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  102. data/lib/active_record/connection_adapters/postgresql_adapter.rb +508 -246
  103. data/lib/active_record/connection_adapters/schema_cache.rb +319 -90
  104. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  105. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +72 -53
  106. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +37 -21
  107. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
  108. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -22
  109. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +296 -104
  110. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  111. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  112. data/lib/active_record/connection_adapters/trilogy_adapter.rb +258 -0
  113. data/lib/active_record/connection_adapters.rb +9 -6
  114. data/lib/active_record/connection_handling.rb +108 -137
  115. data/lib/active_record/core.rb +242 -233
  116. data/lib/active_record/counter_cache.rb +52 -27
  117. data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -2
  118. data/lib/active_record/database_configurations/database_config.rb +21 -12
  119. data/lib/active_record/database_configurations/hash_config.rb +88 -16
  120. data/lib/active_record/database_configurations/url_config.rb +18 -12
  121. data/lib/active_record/database_configurations.rb +95 -59
  122. data/lib/active_record/delegated_type.rb +66 -20
  123. data/lib/active_record/deprecator.rb +7 -0
  124. data/lib/active_record/destroy_association_async_job.rb +4 -2
  125. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  126. data/lib/active_record/dynamic_matchers.rb +1 -1
  127. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  128. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  129. data/lib/active_record/encryption/cipher.rb +53 -0
  130. data/lib/active_record/encryption/config.rb +68 -0
  131. data/lib/active_record/encryption/configurable.rb +60 -0
  132. data/lib/active_record/encryption/context.rb +42 -0
  133. data/lib/active_record/encryption/contexts.rb +76 -0
  134. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  135. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  136. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  137. data/lib/active_record/encryption/encrypted_attribute_type.rb +155 -0
  138. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  139. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  140. data/lib/active_record/encryption/encryptor.rb +155 -0
  141. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  142. data/lib/active_record/encryption/errors.rb +15 -0
  143. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  144. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  145. data/lib/active_record/encryption/key.rb +28 -0
  146. data/lib/active_record/encryption/key_generator.rb +53 -0
  147. data/lib/active_record/encryption/key_provider.rb +46 -0
  148. data/lib/active_record/encryption/message.rb +33 -0
  149. data/lib/active_record/encryption/message_serializer.rb +92 -0
  150. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  151. data/lib/active_record/encryption/properties.rb +76 -0
  152. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  153. data/lib/active_record/encryption/scheme.rb +100 -0
  154. data/lib/active_record/encryption.rb +58 -0
  155. data/lib/active_record/enum.rb +154 -63
  156. data/lib/active_record/errors.rb +172 -15
  157. data/lib/active_record/explain.rb +23 -3
  158. data/lib/active_record/explain_registry.rb +11 -6
  159. data/lib/active_record/explain_subscriber.rb +1 -1
  160. data/lib/active_record/fixture_set/file.rb +15 -1
  161. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  162. data/lib/active_record/fixture_set/render_context.rb +2 -0
  163. data/lib/active_record/fixture_set/table_row.rb +70 -14
  164. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  165. data/lib/active_record/fixtures.rb +147 -86
  166. data/lib/active_record/future_result.rb +174 -0
  167. data/lib/active_record/gem_version.rb +3 -3
  168. data/lib/active_record/inheritance.rb +81 -29
  169. data/lib/active_record/insert_all.rb +135 -22
  170. data/lib/active_record/integration.rb +11 -10
  171. data/lib/active_record/internal_metadata.rb +119 -33
  172. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  173. data/lib/active_record/locking/optimistic.rb +37 -22
  174. data/lib/active_record/locking/pessimistic.rb +15 -6
  175. data/lib/active_record/log_subscriber.rb +52 -19
  176. data/lib/active_record/marshalling.rb +59 -0
  177. data/lib/active_record/message_pack.rb +124 -0
  178. data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
  179. data/lib/active_record/middleware/database_selector.rb +23 -13
  180. data/lib/active_record/middleware/shard_selector.rb +62 -0
  181. data/lib/active_record/migration/command_recorder.rb +112 -14
  182. data/lib/active_record/migration/compatibility.rb +233 -46
  183. data/lib/active_record/migration/default_strategy.rb +23 -0
  184. data/lib/active_record/migration/execution_strategy.rb +19 -0
  185. data/lib/active_record/migration/join_table.rb +1 -1
  186. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  187. data/lib/active_record/migration.rb +361 -173
  188. data/lib/active_record/model_schema.rb +125 -101
  189. data/lib/active_record/nested_attributes.rb +50 -20
  190. data/lib/active_record/no_touching.rb +3 -3
  191. data/lib/active_record/normalization.rb +167 -0
  192. data/lib/active_record/persistence.rb +409 -88
  193. data/lib/active_record/promise.rb +84 -0
  194. data/lib/active_record/query_cache.rb +4 -22
  195. data/lib/active_record/query_logs.rb +174 -0
  196. data/lib/active_record/query_logs_formatter.rb +41 -0
  197. data/lib/active_record/querying.rb +29 -6
  198. data/lib/active_record/railtie.rb +220 -44
  199. data/lib/active_record/railties/controller_runtime.rb +15 -10
  200. data/lib/active_record/railties/databases.rake +188 -252
  201. data/lib/active_record/railties/job_runtime.rb +23 -0
  202. data/lib/active_record/readonly_attributes.rb +41 -3
  203. data/lib/active_record/reflection.rb +248 -81
  204. data/lib/active_record/relation/batches/batch_enumerator.rb +23 -7
  205. data/lib/active_record/relation/batches.rb +192 -63
  206. data/lib/active_record/relation/calculations.rb +246 -90
  207. data/lib/active_record/relation/delegation.rb +28 -14
  208. data/lib/active_record/relation/finder_methods.rb +108 -51
  209. data/lib/active_record/relation/merger.rb +22 -13
  210. data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
  211. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  212. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  213. data/lib/active_record/relation/predicate_builder.rb +27 -20
  214. data/lib/active_record/relation/query_attribute.rb +30 -12
  215. data/lib/active_record/relation/query_methods.rb +670 -129
  216. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  217. data/lib/active_record/relation/spawn_methods.rb +20 -3
  218. data/lib/active_record/relation/where_clause.rb +10 -19
  219. data/lib/active_record/relation.rb +287 -120
  220. data/lib/active_record/result.rb +37 -11
  221. data/lib/active_record/runtime_registry.rb +32 -13
  222. data/lib/active_record/sanitization.rb +65 -20
  223. data/lib/active_record/schema.rb +36 -22
  224. data/lib/active_record/schema_dumper.rb +73 -24
  225. data/lib/active_record/schema_migration.rb +68 -33
  226. data/lib/active_record/scoping/default.rb +72 -15
  227. data/lib/active_record/scoping/named.rb +5 -13
  228. data/lib/active_record/scoping.rb +65 -34
  229. data/lib/active_record/secure_password.rb +60 -0
  230. data/lib/active_record/secure_token.rb +21 -3
  231. data/lib/active_record/serialization.rb +6 -1
  232. data/lib/active_record/signed_id.rb +10 -8
  233. data/lib/active_record/store.rb +10 -10
  234. data/lib/active_record/suppressor.rb +13 -15
  235. data/lib/active_record/table_metadata.rb +16 -3
  236. data/lib/active_record/tasks/database_tasks.rb +251 -140
  237. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
  238. data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
  239. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  240. data/lib/active_record/test_databases.rb +1 -1
  241. data/lib/active_record/test_fixtures.rb +117 -96
  242. data/lib/active_record/timestamp.rb +32 -19
  243. data/lib/active_record/token_for.rb +113 -0
  244. data/lib/active_record/touch_later.rb +11 -6
  245. data/lib/active_record/transactions.rb +48 -27
  246. data/lib/active_record/translation.rb +3 -3
  247. data/lib/active_record/type/adapter_specific_registry.rb +32 -14
  248. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  249. data/lib/active_record/type/internal/timezone.rb +7 -2
  250. data/lib/active_record/type/serialized.rb +9 -5
  251. data/lib/active_record/type/time.rb +4 -0
  252. data/lib/active_record/type/type_map.rb +17 -20
  253. data/lib/active_record/type.rb +1 -2
  254. data/lib/active_record/validations/absence.rb +1 -1
  255. data/lib/active_record/validations/associated.rb +4 -4
  256. data/lib/active_record/validations/numericality.rb +5 -4
  257. data/lib/active_record/validations/presence.rb +5 -28
  258. data/lib/active_record/validations/uniqueness.rb +51 -6
  259. data/lib/active_record/validations.rb +8 -4
  260. data/lib/active_record/version.rb +1 -1
  261. data/lib/active_record.rb +335 -32
  262. data/lib/arel/attributes/attribute.rb +0 -8
  263. data/lib/arel/crud.rb +28 -22
  264. data/lib/arel/delete_manager.rb +18 -4
  265. data/lib/arel/errors.rb +10 -0
  266. data/lib/arel/factory_methods.rb +4 -0
  267. data/lib/arel/filter_predications.rb +9 -0
  268. data/lib/arel/insert_manager.rb +2 -3
  269. data/lib/arel/nodes/and.rb +4 -0
  270. data/lib/arel/nodes/binary.rb +6 -1
  271. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  272. data/lib/arel/nodes/casted.rb +1 -1
  273. data/lib/arel/nodes/cte.rb +36 -0
  274. data/lib/arel/nodes/delete_statement.rb +12 -13
  275. data/lib/arel/nodes/filter.rb +10 -0
  276. data/lib/arel/nodes/fragments.rb +35 -0
  277. data/lib/arel/nodes/function.rb +1 -0
  278. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  279. data/lib/arel/nodes/insert_statement.rb +2 -2
  280. data/lib/arel/nodes/leading_join.rb +8 -0
  281. data/lib/arel/nodes/node.rb +111 -2
  282. data/lib/arel/nodes/select_core.rb +2 -2
  283. data/lib/arel/nodes/select_statement.rb +2 -2
  284. data/lib/arel/nodes/sql_literal.rb +6 -0
  285. data/lib/arel/nodes/table_alias.rb +4 -0
  286. data/lib/arel/nodes/update_statement.rb +8 -3
  287. data/lib/arel/nodes.rb +5 -0
  288. data/lib/arel/predications.rb +13 -3
  289. data/lib/arel/select_manager.rb +10 -4
  290. data/lib/arel/table.rb +9 -6
  291. data/lib/arel/tree_manager.rb +5 -13
  292. data/lib/arel/update_manager.rb +18 -4
  293. data/lib/arel/visitors/dot.rb +80 -90
  294. data/lib/arel/visitors/mysql.rb +16 -3
  295. data/lib/arel/visitors/postgresql.rb +0 -10
  296. data/lib/arel/visitors/to_sql.rb +141 -20
  297. data/lib/arel/visitors/visitor.rb +2 -2
  298. data/lib/arel.rb +18 -3
  299. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  300. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  301. data/lib/rails/generators/active_record/migration.rb +3 -1
  302. data/lib/rails/generators/active_record/model/USAGE +113 -0
  303. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  304. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  305. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  306. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  307. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  308. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  309. metadata +96 -16
  310. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  311. data/lib/active_record/null_relation.rb +0 -67
@@ -5,8 +5,6 @@ module ActiveRecord
5
5
  class MySQLDatabaseTasks # :nodoc:
6
6
  ER_DB_CREATE_EXISTS = 1007
7
7
 
8
- delegate :connection, :establish_connection, to: ActiveRecord::Base
9
-
10
8
  def self.using_database_configurations?
11
9
  true
12
10
  end
@@ -19,17 +17,18 @@ module ActiveRecord
19
17
  def create
20
18
  establish_connection(configuration_hash_without_database)
21
19
  connection.create_database(db_config.database, creation_options)
22
- establish_connection(db_config)
20
+ establish_connection
23
21
  end
24
22
 
25
23
  def drop
26
- establish_connection(db_config)
24
+ establish_connection
27
25
  connection.drop_database(db_config.database)
28
26
  end
29
27
 
30
28
  def purge
31
- establish_connection(db_config)
29
+ establish_connection(configuration_hash_without_database)
32
30
  connection.recreate_database(db_config.database, creation_options)
31
+ establish_connection
33
32
  end
34
33
 
35
34
  def charset
@@ -49,6 +48,7 @@ module ActiveRecord
49
48
 
50
49
  ignore_tables = ActiveRecord::SchemaDumper.ignore_tables
51
50
  if ignore_tables.any?
51
+ ignore_tables = connection.data_sources.select { |table| ignore_tables.any? { |pattern| pattern === table } }
52
52
  args += ignore_tables.map { |table| "--ignore-table=#{db_config.database}.#{table}" }
53
53
  end
54
54
 
@@ -70,6 +70,14 @@ module ActiveRecord
70
70
  private
71
71
  attr_reader :db_config, :configuration_hash
72
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
+
73
81
  def configuration_hash_without_database
74
82
  configuration_hash.merge(database: nil)
75
83
  end
@@ -93,8 +101,9 @@ module ActiveRecord
93
101
  sslcert: "--ssl-cert",
94
102
  sslcapath: "--ssl-capath",
95
103
  sslcipher: "--ssl-cipher",
96
- sslkey: "--ssl-key"
97
- }.map { |opt, arg| "#{arg}=#{configuration_hash[opt]}" if configuration_hash[opt] }.compact
104
+ sslkey: "--ssl-key",
105
+ ssl_mode: "--ssl-mode"
106
+ }.filter_map { |opt, arg| "#{arg}=#{configuration_hash[opt]}" if configuration_hash[opt] }
98
107
 
99
108
  args
100
109
  end
@@ -9,9 +9,6 @@ module ActiveRecord
9
9
  ON_ERROR_STOP_1 = "ON_ERROR_STOP=1"
10
10
  SQL_COMMENT_BEGIN = "--"
11
11
 
12
- delegate :connection, :establish_connection, :clear_active_connections!,
13
- to: ActiveRecord::Base
14
-
15
12
  def self.using_database_configurations?
16
13
  true
17
14
  end
@@ -21,14 +18,14 @@ module ActiveRecord
21
18
  @configuration_hash = db_config.configuration_hash
22
19
  end
23
20
 
24
- def create(master_established = false)
25
- establish_master_connection unless master_established
21
+ def create(connection_already_established = false)
22
+ establish_connection(public_schema_config) unless connection_already_established
26
23
  connection.create_database(db_config.database, configuration_hash.merge(encoding: encoding))
27
- establish_connection(db_config)
24
+ establish_connection
28
25
  end
29
26
 
30
27
  def drop
31
- establish_master_connection
28
+ establish_connection(public_schema_config)
32
29
  connection.drop_database(db_config.database)
33
30
  end
34
31
 
@@ -41,26 +38,27 @@ module ActiveRecord
41
38
  end
42
39
 
43
40
  def purge
44
- clear_active_connections!
41
+ ActiveRecord::Base.connection_handler.clear_active_connections!(:all)
45
42
  drop
46
43
  create true
47
44
  end
48
45
 
49
46
  def structure_dump(filename, extra_flags)
50
- set_psql_env
51
-
52
47
  search_path = \
53
- case ActiveRecord::Base.dump_schemas
48
+ case ActiveRecord.dump_schemas
54
49
  when :schema_search_path
55
50
  configuration_hash[:schema_search_path]
56
51
  when :all
57
52
  nil
58
53
  when String
59
- ActiveRecord::Base.dump_schemas
54
+ ActiveRecord.dump_schemas
60
55
  end
61
56
 
62
- args = ["--schema-only", "--no-privileges", "--no-owner", "--file", filename]
57
+ args = ["--schema-only", "--no-privileges", "--no-owner"]
58
+ args.concat(["--file", filename])
59
+
63
60
  args.concat(Array(extra_flags)) if extra_flags
61
+
64
62
  unless search_path.blank?
65
63
  args += search_path.split(",").map do |part|
66
64
  "--schema=#{part.strip}"
@@ -69,6 +67,7 @@ module ActiveRecord
69
67
 
70
68
  ignore_tables = ActiveRecord::SchemaDumper.ignore_tables
71
69
  if ignore_tables.any?
70
+ ignore_tables = connection.data_sources.select { |table| ignore_tables.any? { |pattern| pattern === table } }
72
71
  args += ignore_tables.flat_map { |table| ["-T", table] }
73
72
  end
74
73
 
@@ -79,8 +78,7 @@ module ActiveRecord
79
78
  end
80
79
 
81
80
  def structure_load(filename, extra_flags)
82
- set_psql_env
83
- args = ["--set", ON_ERROR_STOP_1, "--quiet", "--no-psqlrc", "--file", filename]
81
+ args = ["--set", ON_ERROR_STOP_1, "--quiet", "--no-psqlrc", "--output", File::NULL, "--file", filename]
84
82
  args.concat(Array(extra_flags)) if extra_flags
85
83
  args << db_config.database
86
84
  run_cmd("psql", args, "loading")
@@ -89,26 +87,37 @@ module ActiveRecord
89
87
  private
90
88
  attr_reader :db_config, :configuration_hash
91
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
+
92
98
  def encoding
93
99
  configuration_hash[:encoding] || DEFAULT_ENCODING
94
100
  end
95
101
 
96
- def establish_master_connection
97
- establish_connection configuration_hash.merge(
98
- database: "postgres",
99
- schema_search_path: "public"
100
- )
102
+ def public_schema_config
103
+ configuration_hash.merge(database: "postgres", schema_search_path: "public")
101
104
  end
102
105
 
103
- def set_psql_env
104
- ENV["PGHOST"] = db_config.host if db_config.host
105
- ENV["PGPORT"] = configuration_hash[:port].to_s if configuration_hash[:port]
106
- ENV["PGPASSWORD"] = configuration_hash[:password].to_s if configuration_hash[:password]
107
- ENV["PGUSER"] = configuration_hash[:username].to_s if configuration_hash[:username]
106
+ def psql_env
107
+ {}.tap do |env|
108
+ env["PGHOST"] = db_config.host if db_config.host
109
+ env["PGPORT"] = configuration_hash[:port].to_s if configuration_hash[:port]
110
+ env["PGPASSWORD"] = configuration_hash[:password].to_s if configuration_hash[:password]
111
+ env["PGUSER"] = configuration_hash[:username].to_s if configuration_hash[:username]
112
+ env["PGSSLMODE"] = configuration_hash[:sslmode].to_s if configuration_hash[:sslmode]
113
+ env["PGSSLCERT"] = configuration_hash[:sslcert].to_s if configuration_hash[:sslcert]
114
+ env["PGSSLKEY"] = configuration_hash[:sslkey].to_s if configuration_hash[:sslkey]
115
+ env["PGSSLROOTCERT"] = configuration_hash[:sslrootcert].to_s if configuration_hash[:sslrootcert]
116
+ end
108
117
  end
109
118
 
110
119
  def run_cmd(cmd, args, action)
111
- fail run_cmd_error(cmd, args, action) unless Kernel.system(cmd, *args)
120
+ fail run_cmd_error(cmd, args, action) unless Kernel.system(psql_env, cmd, *args)
112
121
  end
113
122
 
114
123
  def run_cmd_error(cmd, args, action)
@@ -3,8 +3,6 @@
3
3
  module ActiveRecord
4
4
  module Tasks # :nodoc:
5
5
  class SQLiteDatabaseTasks # :nodoc:
6
- delegate :connection, :establish_connection, to: ActiveRecord::Base
7
-
8
6
  def self.using_database_configurations?
9
7
  true
10
8
  end
@@ -17,25 +15,26 @@ module ActiveRecord
17
15
  def create
18
16
  raise DatabaseAlreadyExists if File.exist?(db_config.database)
19
17
 
20
- establish_connection(db_config)
18
+ establish_connection
21
19
  connection
22
20
  end
23
21
 
24
22
  def drop
25
- require "pathname"
26
- path = Pathname.new(db_config.database)
27
- file = path.absolute? ? path.to_s : File.join(root, path)
28
-
23
+ db_path = db_config.database
24
+ file = File.absolute_path?(db_path) ? db_path : File.join(root, db_path)
29
25
  FileUtils.rm(file)
26
+ FileUtils.rm_f(["#{file}-shm", "#{file}-wal"])
30
27
  rescue Errno::ENOENT => error
31
28
  raise NoDatabaseError.new(error.message)
32
29
  end
33
30
 
34
31
  def purge
32
+ connection.disconnect!
35
33
  drop
36
34
  rescue NoDatabaseError
37
35
  ensure
38
36
  create
37
+ connection.reconnect!
39
38
  end
40
39
 
41
40
  def charset
@@ -49,6 +48,7 @@ module ActiveRecord
49
48
 
50
49
  ignore_tables = ActiveRecord::SchemaDumper.ignore_tables
51
50
  if ignore_tables.any?
51
+ ignore_tables = connection.data_sources.select { |table| ignore_tables.any? { |pattern| pattern === table } }
52
52
  condition = ignore_tables.map { |table| connection.quote(table) }.join(", ")
53
53
  args << "SELECT sql FROM sqlite_master WHERE tbl_name NOT IN (#{condition}) ORDER BY tbl_name, type DESC, name"
54
54
  else
@@ -65,6 +65,14 @@ module ActiveRecord
65
65
  private
66
66
  attr_reader :db_config, :root
67
67
 
68
+ def connection
69
+ ActiveRecord::Base.connection
70
+ end
71
+
72
+ def establish_connection(config = db_config)
73
+ ActiveRecord::Base.establish_connection(config)
74
+ end
75
+
68
76
  def run_cmd(cmd, args, out)
69
77
  fail run_cmd_error(cmd, args) unless Kernel.system(cmd, *args, out: out)
70
78
  end
@@ -14,7 +14,7 @@ module ActiveRecord
14
14
  ActiveRecord::Base.configurations.configs_for(env_name: env_name).each do |db_config|
15
15
  db_config._database = "#{db_config.database}-#{i}"
16
16
 
17
- ActiveRecord::Tasks::DatabaseTasks.reconstruct_from_schema(db_config, ActiveRecord::Base.schema_format, nil)
17
+ ActiveRecord::Tasks::DatabaseTasks.reconstruct_from_schema(db_config, ActiveRecord.schema_format, nil)
18
18
  end
19
19
  ensure
20
20
  ActiveRecord::Base.establish_connection
@@ -13,17 +13,31 @@ module ActiveRecord
13
13
 
14
14
  def after_teardown # :nodoc:
15
15
  super
16
+ ensure
16
17
  teardown_fixtures
17
18
  end
18
19
 
19
20
  included do
20
- class_attribute :fixture_path, instance_writer: false
21
+ ##
22
+ # :singleton-method: fixture_paths
23
+ #
24
+ # Returns the ActiveRecord::FixtureSet collection
25
+
26
+ ##
27
+ # :singleton-method: fixture_paths=
28
+ #
29
+ # :call-seq:
30
+ # fixture_paths=(fixture_paths)
31
+ class_attribute :fixture_paths, instance_writer: false, default: []
21
32
  class_attribute :fixture_table_names, default: []
22
33
  class_attribute :fixture_class_names, default: {}
23
34
  class_attribute :use_transactional_tests, default: true
24
35
  class_attribute :use_instantiated_fixtures, default: false # true, false, or :no_instances
25
36
  class_attribute :pre_loaded_fixtures, default: false
26
37
  class_attribute :lock_threads, default: true
38
+ class_attribute :fixture_sets, default: {}
39
+
40
+ ActiveSupport.run_load_hooks(:active_record_fixtures, self)
27
41
  end
28
42
 
29
43
  module ClassMethods
@@ -39,12 +53,28 @@ module ActiveRecord
39
53
  self.fixture_class_names = fixture_class_names.merge(class_names.stringify_keys)
40
54
  end
41
55
 
56
+ def fixture_path # :nodoc:
57
+ ActiveRecord.deprecator.warn(<<~WARNING)
58
+ TestFixtures.fixture_path is deprecated and will be removed in Rails 7.2. Use .fixture_paths instead.
59
+ If multiple fixture paths have been configured with .fixture_paths, then .fixture_path will just return
60
+ the first path.
61
+ WARNING
62
+ fixture_paths.first
63
+ end
64
+
65
+ def fixture_path=(path) # :nodoc:
66
+ ActiveRecord.deprecator.warn("TestFixtures.fixture_path= is deprecated and will be removed in Rails 7.2. Use .fixture_paths= instead.")
67
+ self.fixture_paths = Array(path)
68
+ end
69
+
42
70
  def fixtures(*fixture_set_names)
43
71
  if fixture_set_names.first == :all
44
- raise StandardError, "No fixture path found. Please set `#{self}.fixture_path`." if fixture_path.blank?
45
- fixture_set_names = Dir[::File.join(fixture_path, "{**,*}/*.{yml}")].uniq
46
- fixture_set_names.reject! { |f| f.start_with?(file_fixture_path.to_s) } if defined?(file_fixture_path) && file_fixture_path
47
- fixture_set_names.map! { |f| f[fixture_path.to_s.size..-5].delete_prefix("/") }
72
+ raise StandardError, "No fixture path found. Please set `#{self}.fixture_paths`." if fixture_paths.blank?
73
+ fixture_set_names = fixture_paths.flat_map do |path|
74
+ names = Dir[::File.join(path, "{**,*}/*.{yml}")].uniq
75
+ names.reject! { |f| f.start_with?(file_fixture_path.to_s) } if defined?(file_fixture_path) && file_fixture_path
76
+ names.map! { |f| f[path.to_s.size..-5].delete_prefix("/") }
77
+ end.uniq
48
78
  else
49
79
  fixture_set_names = fixture_set_names.flatten.map(&:to_s)
50
80
  end
@@ -55,37 +85,20 @@ module ActiveRecord
55
85
 
56
86
  def setup_fixture_accessors(fixture_set_names = nil)
57
87
  fixture_set_names = Array(fixture_set_names || fixture_table_names)
58
- methods = Module.new do
88
+ unless fixture_set_names.empty?
89
+ self.fixture_sets = fixture_sets.dup
59
90
  fixture_set_names.each do |fs_name|
60
- fs_name = fs_name.to_s
61
- accessor_name = fs_name.tr("/", "_").to_sym
62
-
63
- define_method(accessor_name) do |*fixture_names|
64
- force_reload = fixture_names.pop if fixture_names.last == true || fixture_names.last == :reload
65
- return_single_record = fixture_names.size == 1
66
- fixture_names = @loaded_fixtures[fs_name].fixtures.keys if fixture_names.empty?
67
-
68
- @fixture_cache[fs_name] ||= {}
69
-
70
- instances = fixture_names.map do |f_name|
71
- f_name = f_name.to_s if f_name.is_a?(Symbol)
72
- @fixture_cache[fs_name].delete(f_name) if force_reload
73
-
74
- if @loaded_fixtures[fs_name][f_name]
75
- @fixture_cache[fs_name][f_name] ||= @loaded_fixtures[fs_name][f_name].find
76
- else
77
- raise StandardError, "No fixture named '#{f_name}' found for fixture set '#{fs_name}'"
78
- end
79
- end
80
-
81
- return_single_record ? instances.first : instances
82
- end
83
- private accessor_name
91
+ key = fs_name.to_s.include?("/") ? -fs_name.to_s.tr("/", "_") : fs_name
92
+ key = -key.to_s if key.is_a?(Symbol)
93
+ fs_name = -fs_name.to_s if fs_name.is_a?(Symbol)
94
+ fixture_sets[key] = fs_name
84
95
  end
85
96
  end
86
- include methods
87
97
  end
88
98
 
99
+ # Prevents automatically wrapping each specified test in a transaction,
100
+ # to allow application logic transactions to be tested in a top-level
101
+ # (non-nested) context.
89
102
  def uses_transaction(*methods)
90
103
  @uses_transaction = [] unless defined?(@uses_transaction)
91
104
  @uses_transaction.concat methods.map(&:to_s)
@@ -97,6 +110,15 @@ module ActiveRecord
97
110
  end
98
111
  end
99
112
 
113
+ def fixture_path # :nodoc:
114
+ ActiveRecord.deprecator.warn(<<~WARNING)
115
+ TestFixtures#fixture_path is deprecated and will be removed in Rails 7.2. Use #fixture_paths instead.
116
+ If multiple fixture paths have been configured with #fixture_paths, then #fixture_path will just return
117
+ the first path.
118
+ WARNING
119
+ fixture_paths.first
120
+ end
121
+
100
122
  def run_in_transaction?
101
123
  use_transactional_tests &&
102
124
  !self.class.uses_transaction?(name)
@@ -111,7 +133,6 @@ module ActiveRecord
111
133
  @fixture_connections = []
112
134
  @@already_loaded_fixtures ||= {}
113
135
  @connection_subscriber = nil
114
- @legacy_saved_pool_configs = Hash.new { |hash, key| hash[key] = {} }
115
136
  @saved_pool_configs = Hash.new { |hash, key| hash[key] = {} }
116
137
 
117
138
  # Load fixtures once and begin transaction.
@@ -132,19 +153,18 @@ module ActiveRecord
132
153
 
133
154
  # When connections are established in the future, begin a transaction too
134
155
  @connection_subscriber = ActiveSupport::Notifications.subscribe("!connection.active_record") do |_, _, _, _, payload|
135
- spec_name = payload[:spec_name] if payload.key?(:spec_name)
156
+ connection_name = payload[:connection_name] if payload.key?(:connection_name)
136
157
  shard = payload[:shard] if payload.key?(:shard)
137
- setup_shared_connection_pool if ActiveRecord::Base.legacy_connection_handling
138
158
 
139
- if spec_name
159
+ if connection_name
140
160
  begin
141
- connection = ActiveRecord::Base.connection_handler.retrieve_connection(spec_name, shard: shard)
161
+ connection = ActiveRecord::Base.connection_handler.retrieve_connection(connection_name, shard: shard)
142
162
  rescue ConnectionNotEstablished
143
163
  connection = nil
144
164
  end
145
165
 
146
166
  if connection
147
- setup_shared_connection_pool unless ActiveRecord::Base.legacy_connection_handling
167
+ setup_shared_connection_pool
148
168
 
149
169
  if !@fixture_connections.include?(connection)
150
170
  connection.begin_transaction joinable: false, _lazy: false
@@ -180,13 +200,13 @@ module ActiveRecord
180
200
  ActiveRecord::FixtureSet.reset_cache
181
201
  end
182
202
 
183
- ActiveRecord::Base.clear_active_connections!
203
+ ActiveRecord::Base.connection_handler.clear_active_connections!(:all)
184
204
  end
185
205
 
186
206
  def enlist_fixture_connections
187
207
  setup_shared_connection_pool
188
208
 
189
- ActiveRecord::Base.connection_handler.connection_pool_list.map(&:connection)
209
+ ActiveRecord::Base.connection_handler.connection_pool_list(:writing).map(&:connection)
190
210
  end
191
211
 
192
212
  private
@@ -197,79 +217,43 @@ module ActiveRecord
197
217
  # need to share a connection pool so that the reading connection
198
218
  # can see data in the open transaction on the writing connection.
199
219
  def setup_shared_connection_pool
200
- if ActiveRecord::Base.legacy_connection_handling
201
- writing_handler = ActiveRecord::Base.connection_handlers[ActiveRecord::Base.writing_role]
202
-
203
- ActiveRecord::Base.connection_handlers.values.each do |handler|
204
- if handler != writing_handler
205
- handler.connection_pool_names.each do |name|
206
- writing_pool_manager = writing_handler.send(:owner_to_pool_manager)[name]
207
- return unless writing_pool_manager
208
-
209
- pool_manager = handler.send(:owner_to_pool_manager)[name]
210
- @legacy_saved_pool_configs[handler][name] ||= {}
211
- pool_manager.shard_names.each do |shard_name|
212
- writing_pool_config = writing_pool_manager.get_pool_config(nil, shard_name)
213
- pool_config = pool_manager.get_pool_config(nil, shard_name)
214
- next if pool_config == writing_pool_config
215
-
216
- @legacy_saved_pool_configs[handler][name][shard_name] = pool_config
217
- pool_manager.set_pool_config(nil, shard_name, writing_pool_config)
218
- end
219
- end
220
- end
221
- end
222
- else
223
- handler = ActiveRecord::Base.connection_handler
224
-
225
- handler.connection_pool_names.each do |name|
226
- pool_manager = handler.send(:owner_to_pool_manager)[name]
227
- pool_manager.shard_names.each do |shard_name|
228
- writing_pool_config = pool_manager.get_pool_config(ActiveRecord::Base.writing_role, shard_name)
229
- @saved_pool_configs[name][shard_name] ||= {}
230
- pool_manager.role_names.each do |role|
231
- next unless pool_config = pool_manager.get_pool_config(role, shard_name)
232
- next if pool_config == writing_pool_config
233
-
234
- @saved_pool_configs[name][shard_name][role] = pool_config
235
- pool_manager.set_pool_config(role, shard_name, writing_pool_config)
236
- end
220
+ handler = ActiveRecord::Base.connection_handler
221
+
222
+ handler.connection_pool_names.each do |name|
223
+ pool_manager = handler.send(:connection_name_to_pool_manager)[name]
224
+ pool_manager.shard_names.each do |shard_name|
225
+ writing_pool_config = pool_manager.get_pool_config(ActiveRecord.writing_role, shard_name)
226
+ @saved_pool_configs[name][shard_name] ||= {}
227
+ pool_manager.role_names.each do |role|
228
+ next unless pool_config = pool_manager.get_pool_config(role, shard_name)
229
+ next if pool_config == writing_pool_config
230
+
231
+ @saved_pool_configs[name][shard_name][role] = pool_config
232
+ pool_manager.set_pool_config(role, shard_name, writing_pool_config)
237
233
  end
238
234
  end
239
235
  end
240
236
  end
241
237
 
242
238
  def teardown_shared_connection_pool
243
- if ActiveRecord::Base.legacy_connection_handling
244
- @legacy_saved_pool_configs.each_pair do |handler, names|
245
- names.each_pair do |name, shards|
246
- shards.each_pair do |shard_name, pool_config|
247
- pool_manager = handler.send(:owner_to_pool_manager)[name]
248
- pool_manager.set_pool_config(nil, shard_name, pool_config)
249
- end
250
- end
251
- end
252
- else
253
- handler = ActiveRecord::Base.connection_handler
239
+ handler = ActiveRecord::Base.connection_handler
254
240
 
255
- @saved_pool_configs.each_pair do |name, shards|
256
- pool_manager = handler.send(:owner_to_pool_manager)[name]
257
- shards.each_pair do |shard_name, roles|
258
- roles.each_pair do |role, pool_config|
259
- next unless pool_manager.get_pool_config(role, shard_name)
241
+ @saved_pool_configs.each_pair do |name, shards|
242
+ pool_manager = handler.send(:connection_name_to_pool_manager)[name]
243
+ shards.each_pair do |shard_name, roles|
244
+ roles.each_pair do |role, pool_config|
245
+ next unless pool_manager.get_pool_config(role, shard_name)
260
246
 
261
- pool_manager.set_pool_config(role, shard_name, pool_config)
262
- end
247
+ pool_manager.set_pool_config(role, shard_name, pool_config)
263
248
  end
264
249
  end
265
250
  end
266
251
 
267
- @legacy_saved_pool_configs.clear
268
252
  @saved_pool_configs.clear
269
253
  end
270
254
 
271
255
  def load_fixtures(config)
272
- ActiveRecord::FixtureSet.create_fixtures(fixture_path, fixture_table_names, fixture_class_names, config).index_by(&:name)
256
+ ActiveRecord::FixtureSet.create_fixtures(fixture_paths, fixture_table_names, fixture_class_names, config).index_by(&:name)
273
257
  end
274
258
 
275
259
  def instantiate_fixtures
@@ -287,5 +271,42 @@ module ActiveRecord
287
271
  def load_instances?
288
272
  use_instantiated_fixtures != :no_instances
289
273
  end
274
+
275
+ def method_missing(name, *args, **kwargs, &block)
276
+ if fs_name = fixture_sets[name.to_s]
277
+ access_fixture(fs_name, *args, **kwargs, &block)
278
+ else
279
+ super
280
+ end
281
+ end
282
+
283
+ def respond_to_missing?(name, include_private = false)
284
+ if include_private && fixture_sets.key?(name.to_s)
285
+ true
286
+ else
287
+ super
288
+ end
289
+ end
290
+
291
+ def access_fixture(fs_name, *fixture_names)
292
+ force_reload = fixture_names.pop if fixture_names.last == true || fixture_names.last == :reload
293
+ return_single_record = fixture_names.size == 1
294
+
295
+ fixture_names = @loaded_fixtures[fs_name].fixtures.keys if fixture_names.empty?
296
+ @fixture_cache[fs_name] ||= {}
297
+
298
+ instances = fixture_names.map do |f_name|
299
+ f_name = f_name.to_s if f_name.is_a?(Symbol)
300
+ @fixture_cache[fs_name].delete(f_name) if force_reload
301
+
302
+ if @loaded_fixtures[fs_name][f_name]
303
+ @fixture_cache[fs_name][f_name] ||= @loaded_fixtures[fs_name][f_name].find
304
+ else
305
+ raise StandardError, "No fixture named '#{f_name}' found for fixture set '#{fs_name}'"
306
+ end
307
+ end
308
+
309
+ return_single_record ? instances.first : instances
310
+ end
290
311
  end
291
312
  end