activerecord 7.0.0 → 7.1.0

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 (249) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1607 -1040
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +17 -18
  5. data/lib/active_record/aggregations.rb +16 -13
  6. data/lib/active_record/association_relation.rb +1 -1
  7. data/lib/active_record/associations/association.rb +18 -3
  8. data/lib/active_record/associations/association_scope.rb +16 -9
  9. data/lib/active_record/associations/belongs_to_association.rb +14 -6
  10. data/lib/active_record/associations/builder/association.rb +3 -3
  11. data/lib/active_record/associations/builder/belongs_to.rb +21 -8
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
  13. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  14. data/lib/active_record/associations/collection_association.rb +17 -12
  15. data/lib/active_record/associations/collection_proxy.rb +22 -12
  16. data/lib/active_record/associations/foreign_association.rb +10 -3
  17. data/lib/active_record/associations/has_many_association.rb +27 -17
  18. data/lib/active_record/associations/has_many_through_association.rb +10 -6
  19. data/lib/active_record/associations/has_one_association.rb +10 -3
  20. data/lib/active_record/associations/join_dependency.rb +20 -14
  21. data/lib/active_record/associations/preloader/association.rb +27 -6
  22. data/lib/active_record/associations/preloader/through_association.rb +1 -1
  23. data/lib/active_record/associations/preloader.rb +13 -10
  24. data/lib/active_record/associations/singular_association.rb +1 -1
  25. data/lib/active_record/associations/through_association.rb +22 -11
  26. data/lib/active_record/associations.rb +345 -219
  27. data/lib/active_record/attribute_assignment.rb +0 -2
  28. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  29. data/lib/active_record/attribute_methods/dirty.rb +40 -26
  30. data/lib/active_record/attribute_methods/primary_key.rb +76 -24
  31. data/lib/active_record/attribute_methods/query.rb +28 -16
  32. data/lib/active_record/attribute_methods/read.rb +18 -5
  33. data/lib/active_record/attribute_methods/serialization.rb +172 -69
  34. data/lib/active_record/attribute_methods/write.rb +3 -3
  35. data/lib/active_record/attribute_methods.rb +110 -28
  36. data/lib/active_record/attributes.rb +3 -3
  37. data/lib/active_record/autosave_association.rb +56 -10
  38. data/lib/active_record/base.rb +10 -5
  39. data/lib/active_record/callbacks.rb +16 -32
  40. data/lib/active_record/coders/column_serializer.rb +61 -0
  41. data/lib/active_record/coders/json.rb +1 -1
  42. data/lib/active_record/coders/yaml_column.rb +70 -34
  43. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +164 -89
  44. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  45. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
  46. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +63 -43
  47. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  48. data/lib/active_record/connection_adapters/abstract/database_statements.rb +128 -32
  49. data/lib/active_record/connection_adapters/abstract/query_cache.rb +60 -22
  50. data/lib/active_record/connection_adapters/abstract/quoting.rb +52 -8
  51. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  52. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  53. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +163 -29
  54. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  55. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +302 -129
  56. data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
  57. data/lib/active_record/connection_adapters/abstract_adapter.rb +504 -106
  58. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +217 -104
  59. data/lib/active_record/connection_adapters/column.rb +9 -0
  60. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  61. data/lib/active_record/connection_adapters/mysql/database_statements.rb +23 -144
  62. data/lib/active_record/connection_adapters/mysql/quoting.rb +29 -12
  63. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  64. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  65. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  66. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +38 -14
  67. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
  68. data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
  69. data/lib/active_record/connection_adapters/pool_config.rb +14 -5
  70. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  71. data/lib/active_record/connection_adapters/postgresql/column.rb +3 -2
  72. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +72 -45
  73. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  74. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -2
  75. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  76. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +3 -1
  77. data/lib/active_record/connection_adapters/postgresql/quoting.rb +41 -8
  78. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +6 -10
  79. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  80. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
  81. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
  82. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +358 -57
  83. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  84. data/lib/active_record/connection_adapters/postgresql_adapter.rb +343 -181
  85. data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
  86. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  87. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +45 -39
  88. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +22 -5
  89. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
  90. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +41 -22
  91. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +242 -81
  92. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  93. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
  94. data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
  95. data/lib/active_record/connection_adapters.rb +3 -1
  96. data/lib/active_record/connection_handling.rb +73 -96
  97. data/lib/active_record/core.rb +136 -148
  98. data/lib/active_record/counter_cache.rb +46 -25
  99. data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -0
  100. data/lib/active_record/database_configurations/database_config.rb +9 -3
  101. data/lib/active_record/database_configurations/hash_config.rb +22 -12
  102. data/lib/active_record/database_configurations/url_config.rb +17 -11
  103. data/lib/active_record/database_configurations.rb +87 -34
  104. data/lib/active_record/delegated_type.rb +9 -4
  105. data/lib/active_record/deprecator.rb +7 -0
  106. data/lib/active_record/destroy_association_async_job.rb +2 -0
  107. data/lib/active_record/disable_joins_association_relation.rb +1 -1
  108. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  109. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  110. data/lib/active_record/encryption/config.rb +25 -1
  111. data/lib/active_record/encryption/configurable.rb +13 -14
  112. data/lib/active_record/encryption/context.rb +10 -3
  113. data/lib/active_record/encryption/contexts.rb +8 -4
  114. data/lib/active_record/encryption/derived_secret_key_provider.rb +9 -3
  115. data/lib/active_record/encryption/deterministic_key_provider.rb +1 -1
  116. data/lib/active_record/encryption/encryptable_record.rb +38 -22
  117. data/lib/active_record/encryption/encrypted_attribute_type.rb +19 -8
  118. data/lib/active_record/encryption/encryptor.rb +7 -7
  119. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +3 -3
  120. data/lib/active_record/encryption/extended_deterministic_queries.rb +83 -71
  121. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  122. data/lib/active_record/encryption/key_generator.rb +12 -1
  123. data/lib/active_record/encryption/message.rb +1 -1
  124. data/lib/active_record/encryption/message_serializer.rb +2 -0
  125. data/lib/active_record/encryption/properties.rb +4 -4
  126. data/lib/active_record/encryption/scheme.rb +20 -23
  127. data/lib/active_record/encryption.rb +1 -0
  128. data/lib/active_record/enum.rb +114 -27
  129. data/lib/active_record/errors.rb +108 -15
  130. data/lib/active_record/explain.rb +23 -3
  131. data/lib/active_record/explain_subscriber.rb +1 -1
  132. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  133. data/lib/active_record/fixture_set/render_context.rb +2 -0
  134. data/lib/active_record/fixture_set/table_row.rb +29 -8
  135. data/lib/active_record/fixtures.rb +121 -73
  136. data/lib/active_record/future_result.rb +30 -5
  137. data/lib/active_record/gem_version.rb +2 -2
  138. data/lib/active_record/inheritance.rb +30 -16
  139. data/lib/active_record/insert_all.rb +55 -8
  140. data/lib/active_record/integration.rb +10 -10
  141. data/lib/active_record/internal_metadata.rb +118 -30
  142. data/lib/active_record/locking/optimistic.rb +32 -18
  143. data/lib/active_record/locking/pessimistic.rb +8 -5
  144. data/lib/active_record/log_subscriber.rb +39 -17
  145. data/lib/active_record/marshalling.rb +56 -0
  146. data/lib/active_record/message_pack.rb +124 -0
  147. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  148. data/lib/active_record/middleware/database_selector.rb +18 -13
  149. data/lib/active_record/middleware/shard_selector.rb +7 -5
  150. data/lib/active_record/migration/command_recorder.rb +104 -9
  151. data/lib/active_record/migration/compatibility.rb +158 -64
  152. data/lib/active_record/migration/default_strategy.rb +23 -0
  153. data/lib/active_record/migration/execution_strategy.rb +19 -0
  154. data/lib/active_record/migration.rb +271 -117
  155. data/lib/active_record/model_schema.rb +82 -50
  156. data/lib/active_record/nested_attributes.rb +23 -3
  157. data/lib/active_record/normalization.rb +159 -0
  158. data/lib/active_record/persistence.rb +200 -47
  159. data/lib/active_record/promise.rb +84 -0
  160. data/lib/active_record/query_cache.rb +3 -21
  161. data/lib/active_record/query_logs.rb +87 -51
  162. data/lib/active_record/query_logs_formatter.rb +41 -0
  163. data/lib/active_record/querying.rb +16 -3
  164. data/lib/active_record/railtie.rb +127 -61
  165. data/lib/active_record/railties/controller_runtime.rb +12 -8
  166. data/lib/active_record/railties/databases.rake +142 -143
  167. data/lib/active_record/railties/job_runtime.rb +23 -0
  168. data/lib/active_record/readonly_attributes.rb +32 -5
  169. data/lib/active_record/reflection.rb +177 -45
  170. data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
  171. data/lib/active_record/relation/batches.rb +190 -61
  172. data/lib/active_record/relation/calculations.rb +200 -83
  173. data/lib/active_record/relation/delegation.rb +23 -9
  174. data/lib/active_record/relation/finder_methods.rb +77 -16
  175. data/lib/active_record/relation/merger.rb +2 -0
  176. data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
  177. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  179. data/lib/active_record/relation/predicate_builder.rb +26 -14
  180. data/lib/active_record/relation/query_attribute.rb +25 -1
  181. data/lib/active_record/relation/query_methods.rb +429 -76
  182. data/lib/active_record/relation/spawn_methods.rb +18 -1
  183. data/lib/active_record/relation.rb +98 -41
  184. data/lib/active_record/result.rb +25 -9
  185. data/lib/active_record/runtime_registry.rb +10 -1
  186. data/lib/active_record/sanitization.rb +57 -16
  187. data/lib/active_record/schema.rb +36 -22
  188. data/lib/active_record/schema_dumper.rb +65 -23
  189. data/lib/active_record/schema_migration.rb +68 -33
  190. data/lib/active_record/scoping/default.rb +20 -12
  191. data/lib/active_record/scoping/named.rb +2 -2
  192. data/lib/active_record/scoping.rb +2 -1
  193. data/lib/active_record/secure_password.rb +60 -0
  194. data/lib/active_record/secure_token.rb +21 -3
  195. data/lib/active_record/serialization.rb +5 -0
  196. data/lib/active_record/signed_id.rb +9 -7
  197. data/lib/active_record/store.rb +16 -11
  198. data/lib/active_record/suppressor.rb +3 -1
  199. data/lib/active_record/table_metadata.rb +16 -3
  200. data/lib/active_record/tasks/database_tasks.rb +138 -107
  201. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  202. data/lib/active_record/tasks/postgresql_database_tasks.rb +17 -15
  203. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  204. data/lib/active_record/test_fixtures.rb +123 -99
  205. data/lib/active_record/timestamp.rb +26 -14
  206. data/lib/active_record/token_for.rb +113 -0
  207. data/lib/active_record/touch_later.rb +11 -6
  208. data/lib/active_record/transactions.rb +39 -13
  209. data/lib/active_record/translation.rb +1 -1
  210. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  211. data/lib/active_record/type/internal/timezone.rb +7 -2
  212. data/lib/active_record/type/serialized.rb +8 -4
  213. data/lib/active_record/type/time.rb +4 -0
  214. data/lib/active_record/validations/absence.rb +1 -1
  215. data/lib/active_record/validations/associated.rb +3 -3
  216. data/lib/active_record/validations/numericality.rb +5 -4
  217. data/lib/active_record/validations/presence.rb +5 -28
  218. data/lib/active_record/validations/uniqueness.rb +50 -5
  219. data/lib/active_record/validations.rb +8 -4
  220. data/lib/active_record/version.rb +1 -1
  221. data/lib/active_record.rb +143 -16
  222. data/lib/arel/errors.rb +10 -0
  223. data/lib/arel/factory_methods.rb +4 -0
  224. data/lib/arel/filter_predications.rb +1 -1
  225. data/lib/arel/nodes/and.rb +4 -0
  226. data/lib/arel/nodes/binary.rb +6 -1
  227. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  228. data/lib/arel/nodes/cte.rb +36 -0
  229. data/lib/arel/nodes/filter.rb +1 -1
  230. data/lib/arel/nodes/fragments.rb +35 -0
  231. data/lib/arel/nodes/homogeneous_in.rb +0 -8
  232. data/lib/arel/nodes/leading_join.rb +8 -0
  233. data/lib/arel/nodes/node.rb +111 -2
  234. data/lib/arel/nodes/sql_literal.rb +6 -0
  235. data/lib/arel/nodes/table_alias.rb +4 -0
  236. data/lib/arel/nodes.rb +4 -0
  237. data/lib/arel/predications.rb +2 -0
  238. data/lib/arel/table.rb +9 -5
  239. data/lib/arel/visitors/mysql.rb +8 -1
  240. data/lib/arel/visitors/to_sql.rb +81 -17
  241. data/lib/arel/visitors/visitor.rb +2 -2
  242. data/lib/arel.rb +16 -2
  243. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  244. data/lib/rails/generators/active_record/migration.rb +3 -1
  245. data/lib/rails/generators/active_record/model/USAGE +113 -0
  246. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  247. metadata +50 -15
  248. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  249. data/lib/active_record/null_relation.rb +0 -63
@@ -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,7 +101,8 @@ module ActiveRecord
93
101
  sslcert: "--ssl-cert",
94
102
  sslcapath: "--ssl-capath",
95
103
  sslcipher: "--ssl-cipher",
96
- sslkey: "--ssl-key"
104
+ sslkey: "--ssl-key",
105
+ ssl_mode: "--ssl-mode"
97
106
  }.filter_map { |opt, arg| "#{arg}=#{configuration_hash[opt]}" if configuration_hash[opt] }
98
107
 
99
108
  args
@@ -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,7 +38,7 @@ 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
@@ -58,7 +55,6 @@ module ActiveRecord
58
55
  end
59
56
 
60
57
  args = ["--schema-only", "--no-privileges", "--no-owner"]
61
- args << "--no-comment" if connection.database_version >= 110_000
62
58
  args.concat(["--file", filename])
63
59
 
64
60
  args.concat(Array(extra_flags)) if extra_flags
@@ -71,6 +67,7 @@ module ActiveRecord
71
67
 
72
68
  ignore_tables = ActiveRecord::SchemaDumper.ignore_tables
73
69
  if ignore_tables.any?
70
+ ignore_tables = connection.data_sources.select { |table| ignore_tables.any? { |pattern| pattern === table } }
74
71
  args += ignore_tables.flat_map { |table| ["-T", table] }
75
72
  end
76
73
 
@@ -81,7 +78,7 @@ module ActiveRecord
81
78
  end
82
79
 
83
80
  def structure_load(filename, extra_flags)
84
- 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]
85
82
  args.concat(Array(extra_flags)) if extra_flags
86
83
  args << db_config.database
87
84
  run_cmd("psql", args, "loading")
@@ -90,15 +87,20 @@ module ActiveRecord
90
87
  private
91
88
  attr_reader :db_config, :configuration_hash
92
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
+
93
98
  def encoding
94
99
  configuration_hash[:encoding] || DEFAULT_ENCODING
95
100
  end
96
101
 
97
- def establish_master_connection
98
- establish_connection configuration_hash.merge(
99
- database: "postgres",
100
- schema_search_path: "public"
101
- )
102
+ def public_schema_config
103
+ configuration_hash.merge(database: "postgres", schema_search_path: "public")
102
104
  end
103
105
 
104
106
  def psql_env
@@ -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
@@ -17,13 +17,26 @@ module ActiveRecord
17
17
  end
18
18
 
19
19
  included do
20
- class_attribute :fixture_path, instance_writer: false
20
+ ##
21
+ # :singleton-method: fixture_paths
22
+ #
23
+ # Returns the ActiveRecord::FixtureSet collection
24
+
25
+ ##
26
+ # :singleton-method: fixture_paths=
27
+ #
28
+ # :call-seq:
29
+ # fixture_paths=(fixture_paths)
30
+ class_attribute :fixture_paths, instance_writer: false, default: []
21
31
  class_attribute :fixture_table_names, default: []
22
32
  class_attribute :fixture_class_names, default: {}
23
33
  class_attribute :use_transactional_tests, default: true
24
34
  class_attribute :use_instantiated_fixtures, default: false # true, false, or :no_instances
25
35
  class_attribute :pre_loaded_fixtures, default: false
26
36
  class_attribute :lock_threads, default: true
37
+ class_attribute :fixture_sets, default: {}
38
+
39
+ ActiveSupport.run_load_hooks(:active_record_fixtures, self)
27
40
  end
28
41
 
29
42
  module ClassMethods
@@ -39,12 +52,28 @@ module ActiveRecord
39
52
  self.fixture_class_names = fixture_class_names.merge(class_names.stringify_keys)
40
53
  end
41
54
 
55
+ def fixture_path # :nodoc:
56
+ ActiveRecord.deprecator.warn(<<~WARNING)
57
+ TestFixtures.fixture_path is deprecated and will be removed in Rails 7.2. Use .fixture_paths instead.
58
+ If multiple fixture paths have been configured with .fixture_paths, then .fixture_path will just return
59
+ the first path.
60
+ WARNING
61
+ fixture_paths.first
62
+ end
63
+
64
+ def fixture_path=(path) # :nodoc:
65
+ ActiveRecord.deprecator.warn("TestFixtures.fixture_path= is deprecated and will be removed in Rails 7.2. Use .fixture_paths= instead.")
66
+ self.fixture_paths = Array(path)
67
+ end
68
+
42
69
  def fixtures(*fixture_set_names)
43
70
  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("/") }
71
+ raise StandardError, "No fixture path found. Please set `#{self}.fixture_paths`." if fixture_paths.blank?
72
+ fixture_set_names = fixture_paths.flat_map do |path|
73
+ names = Dir[::File.join(path, "{**,*}/*.{yml}")].uniq
74
+ names.reject! { |f| f.start_with?(file_fixture_path.to_s) } if defined?(file_fixture_path) && file_fixture_path
75
+ names.map! { |f| f[path.to_s.size..-5].delete_prefix("/") }
76
+ end.uniq
48
77
  else
49
78
  fixture_set_names = fixture_set_names.flatten.map(&:to_s)
50
79
  end
@@ -55,37 +84,20 @@ module ActiveRecord
55
84
 
56
85
  def setup_fixture_accessors(fixture_set_names = nil)
57
86
  fixture_set_names = Array(fixture_set_names || fixture_table_names)
58
- methods = Module.new do
87
+ unless fixture_set_names.empty?
88
+ self.fixture_sets = fixture_sets.dup
59
89
  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
90
+ key = fs_name.to_s.include?("/") ? -fs_name.to_s.tr("/", "_") : fs_name
91
+ key = -key.to_s if key.is_a?(Symbol)
92
+ fs_name = -fs_name.to_s if fs_name.is_a?(Symbol)
93
+ fixture_sets[key] = fs_name
84
94
  end
85
95
  end
86
- include methods
87
96
  end
88
97
 
98
+ # Prevents automatically wrapping each specified test in a transaction,
99
+ # to allow application logic transactions to be tested in a top-level
100
+ # (non-nested) context.
89
101
  def uses_transaction(*methods)
90
102
  @uses_transaction = [] unless defined?(@uses_transaction)
91
103
  @uses_transaction.concat methods.map(&:to_s)
@@ -97,6 +109,15 @@ module ActiveRecord
97
109
  end
98
110
  end
99
111
 
112
+ def fixture_path # :nodoc:
113
+ ActiveRecord.deprecator.warn(<<~WARNING)
114
+ TestFixtures#fixture_path is deprecated and will be removed in Rails 7.2. Use #fixture_paths instead.
115
+ If multiple fixture paths have been configured with #fixture_paths, then #fixture_path will just return
116
+ the first path.
117
+ WARNING
118
+ fixture_paths.first
119
+ end
120
+
100
121
  def run_in_transaction?
101
122
  use_transactional_tests &&
102
123
  !self.class.uses_transaction?(name)
@@ -111,7 +132,6 @@ module ActiveRecord
111
132
  @fixture_connections = []
112
133
  @@already_loaded_fixtures ||= {}
113
134
  @connection_subscriber = nil
114
- @legacy_saved_pool_configs = Hash.new { |hash, key| hash[key] = {} }
115
135
  @saved_pool_configs = Hash.new { |hash, key| hash[key] = {} }
116
136
 
117
137
  # Load fixtures once and begin transaction.
@@ -132,21 +152,24 @@ module ActiveRecord
132
152
 
133
153
  # When connections are established in the future, begin a transaction too
134
154
  @connection_subscriber = ActiveSupport::Notifications.subscribe("!connection.active_record") do |_, _, _, _, payload|
135
- spec_name = payload[:spec_name] if payload.key?(:spec_name)
155
+ connection_name = payload[:connection_name] if payload.key?(:connection_name)
136
156
  shard = payload[:shard] if payload.key?(:shard)
137
- setup_shared_connection_pool
138
157
 
139
- if spec_name
158
+ if connection_name
140
159
  begin
141
- connection = ActiveRecord::Base.connection_handler.retrieve_connection(spec_name, shard: shard)
160
+ connection = ActiveRecord::Base.connection_handler.retrieve_connection(connection_name, shard: shard)
142
161
  rescue ConnectionNotEstablished
143
162
  connection = nil
144
163
  end
145
164
 
146
- if connection && !@fixture_connections.include?(connection)
147
- connection.begin_transaction joinable: false, _lazy: false
148
- connection.pool.lock_thread = true if lock_threads
149
- @fixture_connections << connection
165
+ if connection
166
+ setup_shared_connection_pool
167
+
168
+ if !@fixture_connections.include?(connection)
169
+ connection.begin_transaction joinable: false, _lazy: false
170
+ connection.pool.lock_thread = true if lock_threads
171
+ @fixture_connections << connection
172
+ end
150
173
  end
151
174
  end
152
175
  end
@@ -176,13 +199,13 @@ module ActiveRecord
176
199
  ActiveRecord::FixtureSet.reset_cache
177
200
  end
178
201
 
179
- ActiveRecord::Base.clear_active_connections!
202
+ ActiveRecord::Base.connection_handler.clear_active_connections!(:all)
180
203
  end
181
204
 
182
205
  def enlist_fixture_connections
183
206
  setup_shared_connection_pool
184
207
 
185
- ActiveRecord::Base.connection_handler.connection_pool_list.map(&:connection)
208
+ ActiveRecord::Base.connection_handler.connection_pool_list(:writing).map(&:connection)
186
209
  end
187
210
 
188
211
  private
@@ -193,79 +216,43 @@ module ActiveRecord
193
216
  # need to share a connection pool so that the reading connection
194
217
  # can see data in the open transaction on the writing connection.
195
218
  def setup_shared_connection_pool
196
- if ActiveRecord.legacy_connection_handling
197
- writing_handler = ActiveRecord::Base.connection_handlers[ActiveRecord.writing_role]
198
-
199
- ActiveRecord::Base.connection_handlers.values.each do |handler|
200
- if handler != writing_handler
201
- handler.connection_pool_names.each do |name|
202
- writing_pool_manager = writing_handler.send(:owner_to_pool_manager)[name]
203
- return unless writing_pool_manager
204
-
205
- pool_manager = handler.send(:owner_to_pool_manager)[name]
206
- @legacy_saved_pool_configs[handler][name] ||= {}
207
- pool_manager.shard_names.each do |shard_name|
208
- writing_pool_config = writing_pool_manager.get_pool_config(nil, shard_name)
209
- pool_config = pool_manager.get_pool_config(nil, shard_name)
210
- next if pool_config == writing_pool_config
211
-
212
- @legacy_saved_pool_configs[handler][name][shard_name] = pool_config
213
- pool_manager.set_pool_config(nil, shard_name, writing_pool_config)
214
- end
215
- end
216
- end
217
- end
218
- else
219
- handler = ActiveRecord::Base.connection_handler
220
-
221
- handler.connection_pool_names.each do |name|
222
- pool_manager = handler.send(:owner_to_pool_manager)[name]
223
- pool_manager.shard_names.each do |shard_name|
224
- writing_pool_config = pool_manager.get_pool_config(ActiveRecord.writing_role, shard_name)
225
- @saved_pool_configs[name][shard_name] ||= {}
226
- pool_manager.role_names.each do |role|
227
- next unless pool_config = pool_manager.get_pool_config(role, shard_name)
228
- next if pool_config == writing_pool_config
229
-
230
- @saved_pool_configs[name][shard_name][role] = pool_config
231
- pool_manager.set_pool_config(role, shard_name, writing_pool_config)
232
- end
219
+ handler = ActiveRecord::Base.connection_handler
220
+
221
+ handler.connection_pool_names.each do |name|
222
+ pool_manager = handler.send(:connection_name_to_pool_manager)[name]
223
+ pool_manager.shard_names.each do |shard_name|
224
+ writing_pool_config = pool_manager.get_pool_config(ActiveRecord.writing_role, shard_name)
225
+ @saved_pool_configs[name][shard_name] ||= {}
226
+ pool_manager.role_names.each do |role|
227
+ next unless pool_config = pool_manager.get_pool_config(role, shard_name)
228
+ next if pool_config == writing_pool_config
229
+
230
+ @saved_pool_configs[name][shard_name][role] = pool_config
231
+ pool_manager.set_pool_config(role, shard_name, writing_pool_config)
233
232
  end
234
233
  end
235
234
  end
236
235
  end
237
236
 
238
237
  def teardown_shared_connection_pool
239
- if ActiveRecord.legacy_connection_handling
240
- @legacy_saved_pool_configs.each_pair do |handler, names|
241
- names.each_pair do |name, shards|
242
- shards.each_pair do |shard_name, pool_config|
243
- pool_manager = handler.send(:owner_to_pool_manager)[name]
244
- pool_manager.set_pool_config(nil, shard_name, pool_config)
245
- end
246
- end
247
- end
248
- else
249
- handler = ActiveRecord::Base.connection_handler
238
+ handler = ActiveRecord::Base.connection_handler
250
239
 
251
- @saved_pool_configs.each_pair do |name, shards|
252
- pool_manager = handler.send(:owner_to_pool_manager)[name]
253
- shards.each_pair do |shard_name, roles|
254
- roles.each_pair do |role, pool_config|
255
- next unless pool_manager.get_pool_config(role, shard_name)
240
+ @saved_pool_configs.each_pair do |name, shards|
241
+ pool_manager = handler.send(:connection_name_to_pool_manager)[name]
242
+ shards.each_pair do |shard_name, roles|
243
+ roles.each_pair do |role, pool_config|
244
+ next unless pool_manager.get_pool_config(role, shard_name)
256
245
 
257
- pool_manager.set_pool_config(role, shard_name, pool_config)
258
- end
246
+ pool_manager.set_pool_config(role, shard_name, pool_config)
259
247
  end
260
248
  end
261
249
  end
262
250
 
263
- @legacy_saved_pool_configs.clear
264
251
  @saved_pool_configs.clear
265
252
  end
266
253
 
267
254
  def load_fixtures(config)
268
- ActiveRecord::FixtureSet.create_fixtures(fixture_path, fixture_table_names, fixture_class_names, config).index_by(&:name)
255
+ ActiveRecord::FixtureSet.create_fixtures(fixture_paths, fixture_table_names, fixture_class_names, config).index_by(&:name)
269
256
  end
270
257
 
271
258
  def instantiate_fixtures
@@ -283,5 +270,42 @@ module ActiveRecord
283
270
  def load_instances?
284
271
  use_instantiated_fixtures != :no_instances
285
272
  end
273
+
274
+ def method_missing(name, *args, **kwargs, &block)
275
+ if fs_name = fixture_sets[name.to_s]
276
+ access_fixture(fs_name, *args, **kwargs, &block)
277
+ else
278
+ super
279
+ end
280
+ end
281
+
282
+ def respond_to_missing?(name, include_private = false)
283
+ if include_private && fixture_sets.key?(name.to_s)
284
+ true
285
+ else
286
+ super
287
+ end
288
+ end
289
+
290
+ def access_fixture(fs_name, *fixture_names)
291
+ force_reload = fixture_names.pop if fixture_names.last == true || fixture_names.last == :reload
292
+ return_single_record = fixture_names.size == 1
293
+
294
+ fixture_names = @loaded_fixtures[fs_name].fixtures.keys if fixture_names.empty?
295
+ @fixture_cache[fs_name] ||= {}
296
+
297
+ instances = fixture_names.map do |f_name|
298
+ f_name = f_name.to_s if f_name.is_a?(Symbol)
299
+ @fixture_cache[fs_name].delete(f_name) if force_reload
300
+
301
+ if @loaded_fixtures[fs_name][f_name]
302
+ @fixture_cache[fs_name][f_name] ||= @loaded_fixtures[fs_name][f_name].find
303
+ else
304
+ raise StandardError, "No fixture named '#{f_name}' found for fixture set '#{fs_name}'"
305
+ end
306
+ end
307
+
308
+ return_single_record ? instances.first : instances
309
+ end
286
310
  end
287
311
  end
@@ -75,9 +75,17 @@ module ActiveRecord
75
75
  end
76
76
 
77
77
  def current_time_from_proper_timezone
78
- ActiveRecord.default_timezone == :utc ? Time.now.utc : Time.now
78
+ connection.default_timezone == :utc ? Time.now.utc : Time.now
79
79
  end
80
80
 
81
+ protected
82
+ def reload_schema_from_cache(recursive = true)
83
+ @timestamp_attributes_for_create_in_model = nil
84
+ @timestamp_attributes_for_update_in_model = nil
85
+ @all_timestamp_attributes_in_model = nil
86
+ super
87
+ end
88
+
81
89
  private
82
90
  def timestamp_attributes_for_create
83
91
  ["created_at", "created_on"].map! { |name| attribute_aliases[name] || name }
@@ -86,16 +94,14 @@ module ActiveRecord
86
94
  def timestamp_attributes_for_update
87
95
  ["updated_at", "updated_on"].map! { |name| attribute_aliases[name] || name }
88
96
  end
89
-
90
- def reload_schema_from_cache
91
- @timestamp_attributes_for_create_in_model = nil
92
- @timestamp_attributes_for_update_in_model = nil
93
- @all_timestamp_attributes_in_model = nil
94
- super
95
- end
96
97
  end
97
98
 
98
99
  private
100
+ def init_internals
101
+ super
102
+ @_touch_record = nil
103
+ end
104
+
99
105
  def _create_record
100
106
  if record_timestamps
101
107
  current_time = current_time_from_proper_timezone
@@ -109,6 +115,17 @@ module ActiveRecord
109
115
  end
110
116
 
111
117
  def _update_record
118
+ record_update_timestamps
119
+
120
+ super
121
+ end
122
+
123
+ def create_or_update(touch: true, **)
124
+ @_touch_record = touch
125
+ super
126
+ end
127
+
128
+ def record_update_timestamps
112
129
  if @_touch_record && should_record_timestamps?
113
130
  current_time = current_time_from_proper_timezone
114
131
 
@@ -118,12 +135,7 @@ module ActiveRecord
118
135
  end
119
136
  end
120
137
 
121
- super
122
- end
123
-
124
- def create_or_update(touch: true, **)
125
- @_touch_record = touch
126
- super
138
+ yield if block_given?
127
139
  end
128
140
 
129
141
  def should_record_timestamps?