activerecord 7.0.8.6 → 7.2.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 (279) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +631 -1939
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +29 -29
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +16 -13
  7. data/lib/active_record/association_relation.rb +2 -2
  8. data/lib/active_record/associations/alias_tracker.rb +25 -19
  9. data/lib/active_record/associations/association.rb +35 -12
  10. data/lib/active_record/associations/association_scope.rb +16 -9
  11. data/lib/active_record/associations/belongs_to_association.rb +23 -8
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  13. data/lib/active_record/associations/builder/association.rb +3 -3
  14. data/lib/active_record/associations/builder/belongs_to.rb +22 -8
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
  16. data/lib/active_record/associations/builder/has_many.rb +3 -4
  17. data/lib/active_record/associations/builder/has_one.rb +3 -4
  18. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  19. data/lib/active_record/associations/collection_association.rb +26 -14
  20. data/lib/active_record/associations/collection_proxy.rb +29 -11
  21. data/lib/active_record/associations/errors.rb +265 -0
  22. data/lib/active_record/associations/foreign_association.rb +10 -3
  23. data/lib/active_record/associations/has_many_association.rb +21 -14
  24. data/lib/active_record/associations/has_many_through_association.rb +17 -7
  25. data/lib/active_record/associations/has_one_association.rb +10 -3
  26. data/lib/active_record/associations/join_dependency/join_association.rb +30 -27
  27. data/lib/active_record/associations/join_dependency.rb +10 -10
  28. data/lib/active_record/associations/nested_error.rb +47 -0
  29. data/lib/active_record/associations/preloader/association.rb +33 -8
  30. data/lib/active_record/associations/preloader/branch.rb +7 -1
  31. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  32. data/lib/active_record/associations/preloader.rb +13 -10
  33. data/lib/active_record/associations/singular_association.rb +7 -1
  34. data/lib/active_record/associations/through_association.rb +22 -11
  35. data/lib/active_record/associations.rb +354 -485
  36. data/lib/active_record/attribute_assignment.rb +0 -4
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  38. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  39. data/lib/active_record/attribute_methods/dirty.rb +53 -35
  40. data/lib/active_record/attribute_methods/primary_key.rb +45 -25
  41. data/lib/active_record/attribute_methods/query.rb +28 -16
  42. data/lib/active_record/attribute_methods/read.rb +8 -7
  43. data/lib/active_record/attribute_methods/serialization.rb +131 -32
  44. data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -6
  45. data/lib/active_record/attribute_methods/write.rb +6 -6
  46. data/lib/active_record/attribute_methods.rb +148 -33
  47. data/lib/active_record/attributes.rb +64 -50
  48. data/lib/active_record/autosave_association.rb +69 -37
  49. data/lib/active_record/base.rb +9 -5
  50. data/lib/active_record/callbacks.rb +11 -25
  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 -42
  54. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +123 -131
  55. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +4 -1
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +323 -88
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +160 -45
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +217 -63
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -63
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +307 -129
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +510 -111
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +278 -125
  69. data/lib/active_record/connection_adapters/column.rb +9 -0
  70. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  71. data/lib/active_record/connection_adapters/mysql/database_statements.rb +26 -139
  72. data/lib/active_record/connection_adapters/mysql/quoting.rb +53 -54
  73. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  76. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +25 -13
  77. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  78. data/lib/active_record/connection_adapters/mysql2_adapter.rb +101 -68
  79. data/lib/active_record/connection_adapters/pool_config.rb +20 -10
  80. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +14 -3
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +100 -43
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  87. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
  88. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  89. data/lib/active_record/connection_adapters/postgresql/quoting.rb +65 -61
  90. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
  91. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  92. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +151 -2
  93. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
  94. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +370 -63
  95. data/lib/active_record/connection_adapters/postgresql_adapter.rb +367 -201
  96. data/lib/active_record/connection_adapters/schema_cache.rb +302 -79
  97. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  98. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +60 -43
  99. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +45 -46
  100. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  101. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +14 -0
  102. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  103. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +50 -8
  104. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +290 -110
  105. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  106. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  107. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  108. data/lib/active_record/connection_adapters.rb +124 -1
  109. data/lib/active_record/connection_handling.rb +96 -104
  110. data/lib/active_record/core.rb +251 -176
  111. data/lib/active_record/counter_cache.rb +68 -34
  112. data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
  113. data/lib/active_record/database_configurations/database_config.rb +26 -5
  114. data/lib/active_record/database_configurations/hash_config.rb +52 -34
  115. data/lib/active_record/database_configurations/url_config.rb +37 -12
  116. data/lib/active_record/database_configurations.rb +87 -34
  117. data/lib/active_record/delegated_type.rb +39 -10
  118. data/lib/active_record/deprecator.rb +7 -0
  119. data/lib/active_record/destroy_association_async_job.rb +3 -1
  120. data/lib/active_record/dynamic_matchers.rb +2 -2
  121. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  122. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  123. data/lib/active_record/encryption/config.rb +25 -1
  124. data/lib/active_record/encryption/configurable.rb +12 -19
  125. data/lib/active_record/encryption/context.rb +10 -3
  126. data/lib/active_record/encryption/contexts.rb +5 -1
  127. data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
  128. data/lib/active_record/encryption/encryptable_record.rb +45 -21
  129. data/lib/active_record/encryption/encrypted_attribute_type.rb +47 -12
  130. data/lib/active_record/encryption/encryptor.rb +18 -3
  131. data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
  132. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  133. data/lib/active_record/encryption/key_generator.rb +12 -1
  134. data/lib/active_record/encryption/key_provider.rb +1 -1
  135. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  136. data/lib/active_record/encryption/message_serializer.rb +6 -0
  137. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  138. data/lib/active_record/encryption/properties.rb +3 -3
  139. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  140. data/lib/active_record/encryption/scheme.rb +22 -21
  141. data/lib/active_record/encryption.rb +3 -0
  142. data/lib/active_record/enum.rb +129 -28
  143. data/lib/active_record/errors.rb +151 -31
  144. data/lib/active_record/explain.rb +21 -12
  145. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  146. data/lib/active_record/fixture_set/render_context.rb +2 -0
  147. data/lib/active_record/fixture_set/table_row.rb +29 -8
  148. data/lib/active_record/fixtures.rb +167 -97
  149. data/lib/active_record/future_result.rb +47 -8
  150. data/lib/active_record/gem_version.rb +4 -4
  151. data/lib/active_record/inheritance.rb +34 -18
  152. data/lib/active_record/insert_all.rb +72 -22
  153. data/lib/active_record/integration.rb +11 -8
  154. data/lib/active_record/internal_metadata.rb +124 -20
  155. data/lib/active_record/locking/optimistic.rb +8 -7
  156. data/lib/active_record/locking/pessimistic.rb +5 -2
  157. data/lib/active_record/log_subscriber.rb +18 -22
  158. data/lib/active_record/marshalling.rb +59 -0
  159. data/lib/active_record/message_pack.rb +124 -0
  160. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  161. data/lib/active_record/middleware/database_selector.rb +6 -8
  162. data/lib/active_record/middleware/shard_selector.rb +3 -1
  163. data/lib/active_record/migration/command_recorder.rb +106 -8
  164. data/lib/active_record/migration/compatibility.rb +147 -5
  165. data/lib/active_record/migration/default_strategy.rb +22 -0
  166. data/lib/active_record/migration/execution_strategy.rb +19 -0
  167. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  168. data/lib/active_record/migration.rb +234 -117
  169. data/lib/active_record/model_schema.rb +90 -102
  170. data/lib/active_record/nested_attributes.rb +48 -11
  171. data/lib/active_record/normalization.rb +163 -0
  172. data/lib/active_record/persistence.rb +168 -339
  173. data/lib/active_record/promise.rb +84 -0
  174. data/lib/active_record/query_cache.rb +18 -25
  175. data/lib/active_record/query_logs.rb +92 -52
  176. data/lib/active_record/query_logs_formatter.rb +41 -0
  177. data/lib/active_record/querying.rb +33 -8
  178. data/lib/active_record/railtie.rb +129 -85
  179. data/lib/active_record/railties/controller_runtime.rb +22 -7
  180. data/lib/active_record/railties/databases.rake +145 -154
  181. data/lib/active_record/railties/job_runtime.rb +23 -0
  182. data/lib/active_record/readonly_attributes.rb +32 -5
  183. data/lib/active_record/reflection.rb +267 -69
  184. data/lib/active_record/relation/batches/batch_enumerator.rb +20 -5
  185. data/lib/active_record/relation/batches.rb +198 -63
  186. data/lib/active_record/relation/calculations.rb +250 -93
  187. data/lib/active_record/relation/delegation.rb +30 -19
  188. data/lib/active_record/relation/finder_methods.rb +93 -18
  189. data/lib/active_record/relation/merger.rb +6 -6
  190. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  191. data/lib/active_record/relation/predicate_builder/association_query_value.rb +18 -3
  192. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  193. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  194. data/lib/active_record/relation/predicate_builder.rb +28 -16
  195. data/lib/active_record/relation/query_attribute.rb +2 -1
  196. data/lib/active_record/relation/query_methods.rb +576 -107
  197. data/lib/active_record/relation/record_fetch_warning.rb +3 -0
  198. data/lib/active_record/relation/spawn_methods.rb +5 -4
  199. data/lib/active_record/relation/where_clause.rb +7 -19
  200. data/lib/active_record/relation.rb +580 -90
  201. data/lib/active_record/result.rb +49 -48
  202. data/lib/active_record/runtime_registry.rb +63 -1
  203. data/lib/active_record/sanitization.rb +70 -25
  204. data/lib/active_record/schema.rb +8 -7
  205. data/lib/active_record/schema_dumper.rb +63 -14
  206. data/lib/active_record/schema_migration.rb +75 -24
  207. data/lib/active_record/scoping/default.rb +15 -5
  208. data/lib/active_record/scoping/named.rb +3 -2
  209. data/lib/active_record/scoping.rb +2 -1
  210. data/lib/active_record/secure_password.rb +60 -0
  211. data/lib/active_record/secure_token.rb +21 -3
  212. data/lib/active_record/signed_id.rb +27 -6
  213. data/lib/active_record/statement_cache.rb +7 -7
  214. data/lib/active_record/store.rb +8 -8
  215. data/lib/active_record/suppressor.rb +3 -1
  216. data/lib/active_record/table_metadata.rb +1 -1
  217. data/lib/active_record/tasks/database_tasks.rb +190 -118
  218. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  219. data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
  220. data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
  221. data/lib/active_record/test_fixtures.rb +170 -155
  222. data/lib/active_record/testing/query_assertions.rb +121 -0
  223. data/lib/active_record/timestamp.rb +31 -17
  224. data/lib/active_record/token_for.rb +123 -0
  225. data/lib/active_record/touch_later.rb +12 -7
  226. data/lib/active_record/transaction.rb +132 -0
  227. data/lib/active_record/transactions.rb +106 -24
  228. data/lib/active_record/translation.rb +0 -2
  229. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  230. data/lib/active_record/type/internal/timezone.rb +7 -2
  231. data/lib/active_record/type/serialized.rb +1 -3
  232. data/lib/active_record/type/time.rb +4 -0
  233. data/lib/active_record/type_caster/connection.rb +4 -4
  234. data/lib/active_record/validations/absence.rb +1 -1
  235. data/lib/active_record/validations/associated.rb +9 -3
  236. data/lib/active_record/validations/numericality.rb +5 -4
  237. data/lib/active_record/validations/presence.rb +5 -28
  238. data/lib/active_record/validations/uniqueness.rb +61 -11
  239. data/lib/active_record/validations.rb +12 -5
  240. data/lib/active_record/version.rb +1 -1
  241. data/lib/active_record.rb +247 -33
  242. data/lib/arel/alias_predication.rb +1 -1
  243. data/lib/arel/collectors/bind.rb +2 -0
  244. data/lib/arel/collectors/composite.rb +7 -0
  245. data/lib/arel/collectors/sql_string.rb +1 -1
  246. data/lib/arel/collectors/substitute_binds.rb +1 -1
  247. data/lib/arel/errors.rb +10 -0
  248. data/lib/arel/factory_methods.rb +4 -0
  249. data/lib/arel/nodes/binary.rb +6 -7
  250. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  251. data/lib/arel/nodes/cte.rb +36 -0
  252. data/lib/arel/nodes/fragments.rb +35 -0
  253. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  254. data/lib/arel/nodes/leading_join.rb +8 -0
  255. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  256. data/lib/arel/nodes/node.rb +115 -5
  257. data/lib/arel/nodes/sql_literal.rb +13 -0
  258. data/lib/arel/nodes/table_alias.rb +4 -0
  259. data/lib/arel/nodes.rb +6 -2
  260. data/lib/arel/predications.rb +3 -1
  261. data/lib/arel/select_manager.rb +1 -1
  262. data/lib/arel/table.rb +9 -5
  263. data/lib/arel/tree_manager.rb +8 -3
  264. data/lib/arel/update_manager.rb +2 -1
  265. data/lib/arel/visitors/dot.rb +1 -0
  266. data/lib/arel/visitors/mysql.rb +17 -5
  267. data/lib/arel/visitors/postgresql.rb +1 -12
  268. data/lib/arel/visitors/sqlite.rb +25 -0
  269. data/lib/arel/visitors/to_sql.rb +112 -34
  270. data/lib/arel/visitors/visitor.rb +2 -2
  271. data/lib/arel.rb +21 -3
  272. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  273. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  274. data/lib/rails/generators/active_record/migration.rb +3 -1
  275. data/lib/rails/generators/active_record/model/USAGE +113 -0
  276. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  277. metadata +56 -14
  278. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  279. data/lib/active_record/null_relation.rb +0 -63
@@ -6,14 +6,16 @@ module ActiveRecord
6
6
  module Tasks # :nodoc:
7
7
  class DatabaseNotSupported < StandardError; end # :nodoc:
8
8
 
9
+ # = Active Record \DatabaseTasks
10
+ #
9
11
  # ActiveRecord::Tasks::DatabaseTasks is a utility class, which encapsulates
10
12
  # logic behind common tasks used to manage database and migrations.
11
13
  #
12
- # The tasks defined here are used with Rails commands provided by Active Record.
14
+ # The tasks defined here are used with \Rails commands provided by Active Record.
13
15
  #
14
16
  # In order to use DatabaseTasks, a few config values need to be set. All the needed
15
- # config values are set by Rails already, so it's necessary to do it only if you
16
- # want to change the defaults or when you want to use Active Record outside of Rails
17
+ # config values are set by \Rails already, so it's necessary to do it only if you
18
+ # want to change the defaults or when you want to use Active Record outside of \Rails
17
19
  # (in such case after configuring the database tasks, you can also use the rake tasks
18
20
  # defined in Active Record).
19
21
  #
@@ -27,7 +29,7 @@ module ActiveRecord
27
29
  # * +seed_loader+: an object which will load seeds, it needs to respond to the +load_seed+ method.
28
30
  # * +root+: a path to the root of the application.
29
31
  #
30
- # Example usage of DatabaseTasks outside Rails could look as such:
32
+ # Example usage of DatabaseTasks outside \Rails could look as such:
31
33
  #
32
34
  # include ActiveRecord::Tasks
33
35
  # DatabaseTasks.database_configuration = YAML.load_file('my_database_config.yml')
@@ -60,20 +62,12 @@ module ActiveRecord
60
62
 
61
63
  LOCAL_HOSTS = ["127.0.0.1", "localhost"]
62
64
 
63
- def check_protected_environments!
64
- unless ENV["DISABLE_DATABASE_ENVIRONMENT_CHECK"]
65
- current = ActiveRecord::Base.connection.migration_context.current_environment
66
- stored = ActiveRecord::Base.connection.migration_context.last_stored_environment
67
-
68
- if ActiveRecord::Base.connection.migration_context.protected_environment?
69
- raise ActiveRecord::ProtectedEnvironmentError.new(stored)
70
- end
65
+ def check_protected_environments!(environment = env)
66
+ return if ENV["DISABLE_DATABASE_ENVIRONMENT_CHECK"]
71
67
 
72
- if stored && stored != current
73
- raise ActiveRecord::EnvironmentMismatchError.new(current: current, stored: stored)
74
- end
68
+ configs_for(env_name: environment).each do |db_config|
69
+ check_current_protected_environment!(db_config)
75
70
  end
76
- rescue ActiveRecord::NoDatabaseError
77
71
  end
78
72
 
79
73
  def register_task(pattern, task)
@@ -82,6 +76,7 @@ module ActiveRecord
82
76
  end
83
77
 
84
78
  register_task(/mysql/, "ActiveRecord::Tasks::MySQLDatabaseTasks")
79
+ register_task(/trilogy/, "ActiveRecord::Tasks::MySQLDatabaseTasks")
85
80
  register_task(/postgresql/, "ActiveRecord::Tasks::PostgreSQLDatabaseTasks")
86
81
  register_task(/sqlite/, "ActiveRecord::Tasks::SQLiteDatabaseTasks")
87
82
 
@@ -130,28 +125,20 @@ module ActiveRecord
130
125
  end
131
126
 
132
127
  def create_all
133
- old_pool = ActiveRecord::Base.connection_handler.retrieve_connection_pool(ActiveRecord::Base.connection_specification_name)
128
+ db_config = migration_connection.pool.db_config
129
+
134
130
  each_local_configuration { |db_config| create(db_config) }
135
- if old_pool
136
- ActiveRecord::Base.connection_handler.establish_connection(old_pool.db_config)
137
- end
131
+
132
+ migration_class.establish_connection(db_config)
138
133
  end
139
134
 
140
- def setup_initial_database_yaml
135
+ def setup_initial_database_yaml # :nodoc:
141
136
  return {} unless defined?(Rails)
142
137
 
143
- begin
144
- Rails.application.config.load_database_yaml
145
- rescue
146
- unless ActiveRecord.suppress_multiple_database_warning
147
- $stderr.puts "Rails couldn't infer whether you are using multiple databases from your database.yml and can't generate the tasks for the non-primary databases. If you'd like to use this feature, please simplify your ERB."
148
- end
149
-
150
- {}
151
- end
138
+ Rails.application.config.load_database_yaml
152
139
  end
153
140
 
154
- def for_each(databases)
141
+ def for_each(databases) # :nodoc:
155
142
  return {} unless defined?(Rails)
156
143
 
157
144
  database_configs = ActiveRecord::DatabaseConfigurations.new(databases).configs_for(env_name: Rails.env)
@@ -166,7 +153,7 @@ module ActiveRecord
166
153
  end
167
154
  end
168
155
 
169
- def raise_for_multi_db(environment = env, command:)
156
+ def raise_for_multi_db(environment = env, command:) # :nodoc:
170
157
  db_configs = configs_for(env_name: environment)
171
158
 
172
159
  if db_configs.count > 1
@@ -182,40 +169,54 @@ module ActiveRecord
182
169
 
183
170
  def create_current(environment = env, name = nil)
184
171
  each_current_configuration(environment, name) { |db_config| create(db_config) }
185
- ActiveRecord::Base.establish_connection(environment.to_sym)
172
+
173
+ migration_class.establish_connection(environment.to_sym)
186
174
  end
187
175
 
188
176
  def prepare_all
189
177
  seed = false
178
+ dump_db_configs = []
190
179
 
191
180
  each_current_configuration(env) do |db_config|
192
- ActiveRecord::Base.establish_connection(db_config)
181
+ with_temporary_pool(db_config) do
182
+ begin
183
+ database_initialized = migration_connection_pool.schema_migration.table_exists?
184
+ rescue ActiveRecord::NoDatabaseError
185
+ create(db_config)
186
+ retry
187
+ end
193
188
 
194
- begin
195
- # Skipped when no database
196
- migrate
189
+ unless database_initialized
190
+ if File.exist?(schema_dump_path(db_config))
191
+ load_schema(db_config, ActiveRecord.schema_format, nil)
192
+ end
197
193
 
198
- if ActiveRecord.dump_schema_after_migration
199
- dump_schema(db_config, ActiveRecord.schema_format)
194
+ seed = true
200
195
  end
201
- rescue ActiveRecord::NoDatabaseError
202
- create(db_config)
203
-
204
- if File.exist?(schema_dump_path(db_config))
205
- load_schema(
206
- db_config,
207
- ActiveRecord.schema_format,
208
- nil
209
- )
210
- else
211
- migrate
196
+ end
197
+ end
198
+
199
+ each_current_environment(env) do |environment|
200
+ db_configs_with_versions(environment).sort.each do |version, db_configs|
201
+ dump_db_configs |= db_configs
202
+
203
+ db_configs.each do |db_config|
204
+ with_temporary_pool(db_config) do
205
+ migrate(version)
206
+ end
212
207
  end
208
+ end
209
+ end
213
210
 
214
- seed = true
211
+ # Dump schema for databases that were migrated.
212
+ if ActiveRecord.dump_schema_after_migration
213
+ dump_db_configs.each do |db_config|
214
+ with_temporary_pool(db_config) do
215
+ dump_schema(db_config)
216
+ end
215
217
  end
216
218
  end
217
219
 
218
- ActiveRecord::Base.establish_connection
219
220
  load_seed if seed
220
221
  end
221
222
 
@@ -240,10 +241,9 @@ module ActiveRecord
240
241
  end
241
242
 
242
243
  def truncate_tables(db_config)
243
- ActiveRecord::Base.establish_connection(db_config)
244
-
245
- connection = ActiveRecord::Base.connection
246
- connection.truncate_tables(*connection.tables)
244
+ with_temporary_connection(db_config) do |conn|
245
+ conn.truncate_tables(*conn.tables)
246
+ end
247
247
  end
248
248
  private :truncate_tables
249
249
 
@@ -254,12 +254,12 @@ module ActiveRecord
254
254
  end
255
255
 
256
256
  def migrate(version = nil)
257
- check_target_version
258
-
259
257
  scope = ENV["SCOPE"]
260
258
  verbose_was, Migration.verbose = Migration.verbose, verbose?
261
259
 
262
- Base.connection.migration_context.migrate(target_version) do |migration|
260
+ check_target_version
261
+
262
+ migration_connection_pool.migration_context.migrate(target_version) do |migration|
263
263
  if version.blank?
264
264
  scope.blank? || scope == migration.scope
265
265
  else
@@ -269,17 +269,17 @@ module ActiveRecord
269
269
  Migration.write("No migrations ran. (using #{scope} scope)") if scope.present? && migrations_ran.empty?
270
270
  end
271
271
 
272
- ActiveRecord::Base.clear_cache!
272
+ migration_connection_pool.schema_cache.clear!
273
273
  ensure
274
274
  Migration.verbose = verbose_was
275
275
  end
276
276
 
277
- def db_configs_with_versions(db_configs) # :nodoc:
277
+ def db_configs_with_versions(environment = env) # :nodoc:
278
278
  db_configs_with_versions = Hash.new { |h, k| h[k] = [] }
279
279
 
280
- db_configs.each do |db_config|
281
- ActiveRecord::Base.establish_connection(db_config)
282
- versions_to_run = ActiveRecord::Base.connection.migration_context.pending_migration_versions
280
+ with_temporary_pool_for_each(env: environment) do |pool|
281
+ db_config = pool.db_config
282
+ versions_to_run = pool.migration_context.pending_migration_versions
283
283
  target_version = ActiveRecord::Tasks::DatabaseTasks.target_version
284
284
 
285
285
  versions_to_run.each do |version|
@@ -292,22 +292,22 @@ module ActiveRecord
292
292
  end
293
293
 
294
294
  def migrate_status
295
- unless ActiveRecord::Base.connection.schema_migration.table_exists?
295
+ unless migration_connection_pool.schema_migration.table_exists?
296
296
  Kernel.abort "Schema migrations table does not exist yet."
297
297
  end
298
298
 
299
299
  # output
300
- puts "\ndatabase: #{ActiveRecord::Base.connection_db_config.database}\n\n"
300
+ puts "\ndatabase: #{migration_connection_pool.db_config.database}\n\n"
301
301
  puts "#{'Status'.center(8)} #{'Migration ID'.ljust(14)} Migration Name"
302
302
  puts "-" * 50
303
- ActiveRecord::Base.connection.migration_context.migrations_status.each do |status, version, name|
303
+ migration_connection_pool.migration_context.migrations_status.each do |status, version, name|
304
304
  puts "#{status.center(8)} #{version.ljust(14)} #{name}"
305
305
  end
306
306
  puts
307
307
  end
308
308
 
309
309
  def check_target_version
310
- if target_version && !(Migration::MigrationFilenameRegexp.match?(ENV["VERSION"]) || /\A\d+\z/.match?(ENV["VERSION"]))
310
+ if target_version && !Migration.valid_version_format?(ENV["VERSION"])
311
311
  raise "Invalid format of target version: `VERSION=#{ENV['VERSION']}`"
312
312
  end
313
313
  end
@@ -347,7 +347,8 @@ module ActiveRecord
347
347
 
348
348
  def purge_current(environment = env)
349
349
  each_current_configuration(environment) { |db_config| purge(db_config) }
350
- ActiveRecord::Base.establish_connection(environment.to_sym)
350
+
351
+ migration_class.establish_connection(environment.to_sym)
351
352
  end
352
353
 
353
354
  def structure_dump(configuration, *arguments)
@@ -370,7 +371,6 @@ module ActiveRecord
370
371
 
371
372
  verbose_was, Migration.verbose = Migration.verbose, verbose? && ENV["VERBOSE"]
372
373
  check_schema_file(file)
373
- ActiveRecord::Base.establish_connection(db_config)
374
374
 
375
375
  case format
376
376
  when :ruby
@@ -380,9 +380,8 @@ module ActiveRecord
380
380
  else
381
381
  raise ArgumentError, "unknown format #{format.inspect}"
382
382
  end
383
- ActiveRecord::InternalMetadata.create_table
384
- ActiveRecord::InternalMetadata[:environment] = db_config.env_name
385
- ActiveRecord::InternalMetadata[:schema_sha1] = schema_sha1(file)
383
+
384
+ migration_connection_pool.internal_metadata.create_table_and_set_flags(db_config.env_name, schema_sha1(file))
386
385
  ensure
387
386
  Migration.verbose = verbose_was
388
387
  end
@@ -394,12 +393,13 @@ module ActiveRecord
394
393
 
395
394
  return true unless file && File.exist?(file)
396
395
 
397
- ActiveRecord::Base.establish_connection(db_config)
398
-
399
- return false unless ActiveRecord::InternalMetadata.enabled?
400
- return false unless ActiveRecord::InternalMetadata.table_exists?
396
+ with_temporary_pool(db_config) do |pool|
397
+ internal_metadata = pool.internal_metadata
398
+ return false unless internal_metadata.enabled?
399
+ return false unless internal_metadata.table_exists?
401
400
 
402
- ActiveRecord::InternalMetadata[:schema_sha1] == schema_sha1(file)
401
+ internal_metadata[:schema_sha1] == schema_sha1(file)
402
+ end
403
403
  end
404
404
 
405
405
  def reconstruct_from_schema(db_config, format = ActiveRecord.schema_format, file = nil) # :nodoc:
@@ -407,53 +407,43 @@ module ActiveRecord
407
407
 
408
408
  check_schema_file(file) if file
409
409
 
410
- ActiveRecord::Base.establish_connection(db_config)
411
-
412
- if schema_up_to_date?(db_config, format, file)
413
- truncate_tables(db_config)
414
- else
415
- purge(db_config)
410
+ with_temporary_pool(db_config, clobber: true) do
411
+ if schema_up_to_date?(db_config, format, file)
412
+ truncate_tables(db_config) unless ENV["SKIP_TEST_DATABASE_TRUNCATE"]
413
+ else
414
+ purge(db_config)
415
+ load_schema(db_config, format, file)
416
+ end
417
+ rescue ActiveRecord::NoDatabaseError
418
+ create(db_config)
416
419
  load_schema(db_config, format, file)
417
420
  end
418
- rescue ActiveRecord::NoDatabaseError
419
- create(db_config)
420
- load_schema(db_config, format, file)
421
421
  end
422
422
 
423
423
  def dump_schema(db_config, format = ActiveRecord.schema_format) # :nodoc:
424
+ return unless db_config.schema_dump
425
+
424
426
  require "active_record/schema_dumper"
425
427
  filename = schema_dump_path(db_config, format)
426
428
  return unless filename
427
429
 
428
- connection = ActiveRecord::Base.connection
429
-
430
430
  FileUtils.mkdir_p(db_dir)
431
431
  case format
432
432
  when :ruby
433
433
  File.open(filename, "w:utf-8") do |file|
434
- ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
434
+ ActiveRecord::SchemaDumper.dump(migration_connection_pool, file)
435
435
  end
436
436
  when :sql
437
437
  structure_dump(db_config, filename)
438
- if connection.schema_migration.table_exists?
438
+ if migration_connection_pool.schema_migration.table_exists?
439
439
  File.open(filename, "a") do |f|
440
- f.puts connection.dump_schema_information
440
+ f.puts migration_connection.dump_schema_information
441
441
  f.print "\n"
442
442
  end
443
443
  end
444
444
  end
445
445
  end
446
446
 
447
- def schema_file_type(format = ActiveRecord.schema_format)
448
- case format
449
- when :ruby
450
- "schema.rb"
451
- when :sql
452
- "structure.sql"
453
- end
454
- end
455
- deprecate :schema_file_type
456
-
457
447
  def schema_dump_path(db_config, format = ActiveRecord.schema_format)
458
448
  return ENV["SCHEMA"] if ENV["SCHEMA"]
459
449
 
@@ -467,21 +457,34 @@ module ActiveRecord
467
457
  end
468
458
  end
469
459
 
470
- def cache_dump_filename(db_config_name, schema_cache_path: nil)
471
- filename = if ActiveRecord::Base.configurations.primary?(db_config_name)
472
- "schema_cache.yml"
460
+ def cache_dump_filename(db_config_or_name, schema_cache_path: nil)
461
+ if db_config_or_name.is_a?(DatabaseConfigurations::DatabaseConfig)
462
+ schema_cache_path ||
463
+ db_config_or_name.schema_cache_path ||
464
+ schema_cache_env ||
465
+ db_config_or_name.default_schema_cache_path(ActiveRecord::Tasks::DatabaseTasks.db_dir)
473
466
  else
474
- "#{db_config_name}_schema_cache.yml"
475
- end
467
+ ActiveRecord.deprecator.warn(<<~MSG.squish)
468
+ Passing a database name to `cache_dump_filename` is deprecated and will be removed in Rails 8.0. Pass a
469
+ `ActiveRecord::DatabaseConfigurations::DatabaseConfig` object instead.
470
+ MSG
471
+
472
+ filename = if ActiveRecord::Base.configurations.primary?(db_config_or_name)
473
+ "schema_cache.yml"
474
+ else
475
+ "#{db_config_or_name}_schema_cache.yml"
476
+ end
476
477
 
477
- schema_cache_path || ENV["SCHEMA_CACHE"] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, filename)
478
+ schema_cache_path || schema_cache_env || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, filename)
479
+ end
478
480
  end
479
481
 
480
482
  def load_schema_current(format = ActiveRecord.schema_format, file = nil, environment = env)
481
483
  each_current_configuration(environment) do |db_config|
482
- load_schema(db_config, format, file)
484
+ with_temporary_connection(db_config) do
485
+ load_schema(db_config, format, file)
486
+ end
483
487
  end
484
- ActiveRecord::Base.establish_connection(environment.to_sym)
485
488
  end
486
489
 
487
490
  def check_schema_file(filename)
@@ -504,17 +507,66 @@ module ActiveRecord
504
507
 
505
508
  # Dumps the schema cache in YAML format for the connection into the file
506
509
  #
507
- # ==== Examples:
508
- # ActiveRecord::Tasks::DatabaseTasks.dump_schema_cache(ActiveRecord::Base.connection, "tmp/schema_dump.yaml")
509
- def dump_schema_cache(conn, filename)
510
- conn.schema_cache.dump_to(filename)
510
+ # ==== Examples
511
+ # ActiveRecord::Tasks::DatabaseTasks.dump_schema_cache(ActiveRecord::Base.lease_connection, "tmp/schema_dump.yaml")
512
+ def dump_schema_cache(conn_or_pool, filename)
513
+ conn_or_pool.schema_cache.dump_to(filename)
511
514
  end
512
515
 
513
516
  def clear_schema_cache(filename)
514
517
  FileUtils.rm_f filename, verbose: false
515
518
  end
516
519
 
520
+ def with_temporary_pool_for_each(env: ActiveRecord::Tasks::DatabaseTasks.env, name: nil, clobber: false, &block) # :nodoc:
521
+ if name
522
+ db_config = ActiveRecord::Base.configurations.configs_for(env_name: env, name: name)
523
+ with_temporary_pool(db_config, clobber: clobber, &block)
524
+ else
525
+ ActiveRecord::Base.configurations.configs_for(env_name: env, name: name).each do |db_config|
526
+ with_temporary_pool(db_config, clobber: clobber, &block)
527
+ end
528
+ end
529
+ end
530
+
531
+ def with_temporary_connection(db_config, clobber: false, &block) # :nodoc:
532
+ with_temporary_pool(db_config, clobber: clobber) do |pool|
533
+ pool.with_connection(&block)
534
+ end
535
+ end
536
+
537
+ def migration_class # :nodoc:
538
+ ActiveRecord::Base
539
+ end
540
+
541
+ def migration_connection # :nodoc:
542
+ migration_class.lease_connection
543
+ end
544
+
545
+ def migration_connection_pool # :nodoc:
546
+ migration_class.connection_pool
547
+ end
548
+
517
549
  private
550
+ def schema_cache_env
551
+ if ENV["SCHEMA_CACHE"]
552
+ ActiveRecord.deprecator.warn(<<~MSG.squish)
553
+ Setting `ENV["SCHEMA_CACHE"]` is deprecated and will be removed in Rails 8.0.
554
+ Configure the `:schema_cache_path` in the database configuration instead.
555
+ MSG
556
+
557
+ nil
558
+ end
559
+ end
560
+
561
+ def with_temporary_pool(db_config, clobber: false)
562
+ original_db_config = migration_class.connection_db_config
563
+ pool = migration_class.connection_handler.establish_connection(db_config, clobber: clobber)
564
+
565
+ yield pool
566
+ ensure
567
+ migration_class.connection_handler.establish_connection(original_db_config, clobber: clobber)
568
+ end
569
+
518
570
  def configs_for(**options)
519
571
  Base.configurations.configs_for(**options)
520
572
  end
@@ -547,10 +599,7 @@ module ActiveRecord
547
599
  end
548
600
 
549
601
  def each_current_configuration(environment, name = nil)
550
- environments = [environment]
551
- environments << "test" if environment == "development" && !ENV["SKIP_TEST_DATABASE"] && !ENV["DATABASE_URL"]
552
-
553
- environments.each do |env|
602
+ each_current_environment(environment) do |env|
554
603
  configs_for(env_name: env).each do |db_config|
555
604
  next if name && name != db_config.name
556
605
 
@@ -559,6 +608,12 @@ module ActiveRecord
559
608
  end
560
609
  end
561
610
 
611
+ def each_current_environment(environment, &block)
612
+ environments = [environment]
613
+ environments << "test" if environment == "development" && !ENV["SKIP_TEST_DATABASE"] && !ENV["DATABASE_URL"]
614
+ environments.each(&block)
615
+ end
616
+
562
617
  def each_local_configuration
563
618
  configs_for.each do |db_config|
564
619
  next unless db_config.database
@@ -595,6 +650,23 @@ module ActiveRecord
595
650
  structure_load_flags
596
651
  end
597
652
  end
653
+
654
+ def check_current_protected_environment!(db_config)
655
+ with_temporary_pool(db_config) do |pool|
656
+ migration_context = pool.migration_context
657
+ current = migration_context.current_environment
658
+ stored = migration_context.last_stored_environment
659
+
660
+ if migration_context.protected_environment?
661
+ raise ActiveRecord::ProtectedEnvironmentError.new(stored)
662
+ end
663
+
664
+ if stored && stored != current
665
+ raise ActiveRecord::EnvironmentMismatchError.new(current: current, stored: stored)
666
+ end
667
+ rescue ActiveRecord::NoDatabaseError
668
+ end
669
+ end
598
670
  end
599
671
  end
600
672
  end
@@ -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.lease_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
@@ -70,6 +67,7 @@ module ActiveRecord
70
67
 
71
68
  ignore_tables = ActiveRecord::SchemaDumper.ignore_tables
72
69
  if ignore_tables.any?
70
+ ignore_tables = connection.data_sources.select { |table| ignore_tables.any? { |pattern| pattern === table } }
73
71
  args += ignore_tables.flat_map { |table| ["-T", table] }
74
72
  end
75
73
 
@@ -89,15 +87,20 @@ module ActiveRecord
89
87
  private
90
88
  attr_reader :db_config, :configuration_hash
91
89
 
90
+ def connection
91
+ ActiveRecord::Base.lease_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
106
  def psql_env