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
data/lib/active_record.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #--
4
- # Copyright (c) 2004-2021 David Heinemeier Hansson
4
+ # Copyright (c) David Heinemeier Hansson
5
5
  #
6
6
  # Permission is hereby granted, free of charge, to any person obtaining
7
7
  # a copy of this software and associated documentation files (the
@@ -30,9 +30,11 @@ require "arel"
30
30
  require "yaml"
31
31
 
32
32
  require "active_record/version"
33
+ require "active_record/deprecator"
33
34
  require "active_model/attribute_set"
34
35
  require "active_record/errors"
35
36
 
37
+ # :include: activerecord/README.rdoc
36
38
  module ActiveRecord
37
39
  extend ActiveSupport::Autoload
38
40
 
@@ -47,18 +49,22 @@ module ActiveRecord
47
49
  autoload :Encryption
48
50
  autoload :Enum
49
51
  autoload :Explain
52
+ autoload :FixtureSet, "active_record/fixtures"
50
53
  autoload :Inheritance
51
54
  autoload :Integration
52
55
  autoload :InternalMetadata
56
+ autoload :LogSubscriber
57
+ autoload :Marshalling
53
58
  autoload :Migration
54
59
  autoload :Migrator, "active_record/migration"
55
60
  autoload :ModelSchema
56
61
  autoload :NestedAttributes
57
62
  autoload :NoTouching
63
+ autoload :Normalization
58
64
  autoload :Persistence
59
65
  autoload :QueryCache
60
- autoload :Querying
61
66
  autoload :QueryLogs
67
+ autoload :Querying
62
68
  autoload :ReadonlyAttributes
63
69
  autoload :RecordInvalid, "active_record/validations"
64
70
  autoload :Reflection
@@ -68,6 +74,7 @@ module ActiveRecord
68
74
  autoload :SchemaDumper
69
75
  autoload :SchemaMigration
70
76
  autoload :Scoping
77
+ autoload :SecurePassword
71
78
  autoload :SecureToken
72
79
  autoload :Serialization
73
80
  autoload :SignedId
@@ -76,6 +83,7 @@ module ActiveRecord
76
83
  autoload :TestDatabases
77
84
  autoload :TestFixtures, "active_record/fixtures"
78
85
  autoload :Timestamp
86
+ autoload :TokenFor
79
87
  autoload :TouchLater
80
88
  autoload :Transactions
81
89
  autoload :Translation
@@ -93,7 +101,7 @@ module ActiveRecord
93
101
  autoload :DisableJoinsAssociationRelation
94
102
  autoload :FutureResult
95
103
  autoload :LegacyYamlAdapter
96
- autoload :NullRelation
104
+ autoload :Promise
97
105
  autoload :Relation
98
106
  autoload :Result
99
107
  autoload :StatementCache
@@ -101,17 +109,18 @@ module ActiveRecord
101
109
  autoload :Type
102
110
 
103
111
  autoload_under "relation" do
104
- autoload :QueryMethods
105
- autoload :FinderMethods
112
+ autoload :Batches
106
113
  autoload :Calculations
114
+ autoload :Delegation
115
+ autoload :FinderMethods
107
116
  autoload :PredicateBuilder
117
+ autoload :QueryMethods
108
118
  autoload :SpawnMethods
109
- autoload :Batches
110
- autoload :Delegation
111
119
  end
112
120
  end
113
121
 
114
122
  module Coders
123
+ autoload :ColumnSerializer, "active_record/coders/column_serializer"
115
124
  autoload :JSON, "active_record/coders/json"
116
125
  autoload :YAMLColumn, "active_record/coders/yaml_column"
117
126
  end
@@ -165,6 +174,9 @@ module ActiveRecord
165
174
  autoload :SQLiteDatabaseTasks, "active_record/tasks/sqlite_database_tasks"
166
175
  end
167
176
 
177
+ singleton_class.attr_accessor :disable_prepared_statements
178
+ self.disable_prepared_statements = false
179
+
168
180
  # Lazily load the schema cache. This option will load the schema cache
169
181
  # when a connection is established rather than on boot. If set,
170
182
  # +config.active_record.use_schema_cache_dump+ will be set to false.
@@ -177,9 +189,6 @@ module ActiveRecord
177
189
  singleton_class.attr_accessor :schema_cache_ignored_tables
178
190
  self.schema_cache_ignored_tables = []
179
191
 
180
- singleton_class.attr_accessor :legacy_connection_handling
181
- self.legacy_connection_handling = true
182
-
183
192
  singleton_class.attr_reader :default_timezone
184
193
 
185
194
  # Determines whether to use Time.utc (using :utc) or Time.local (using :local) when pulling
@@ -194,12 +203,53 @@ module ActiveRecord
194
203
 
195
204
  self.default_timezone = :utc
196
205
 
206
+ # The action to take when database query produces warning.
207
+ # Must be one of :ignore, :log, :raise, :report, or a custom proc.
208
+ # The default is :ignore.
209
+ singleton_class.attr_reader :db_warnings_action
210
+
211
+ def self.db_warnings_action=(action)
212
+ @db_warnings_action =
213
+ case action
214
+ when :ignore
215
+ nil
216
+ when :log
217
+ ->(warning) do
218
+ warning_message = "[#{warning.class}] #{warning.message}"
219
+ warning_message += " (#{warning.code})" if warning.code
220
+ ActiveRecord::Base.logger.warn(warning_message)
221
+ end
222
+ when :raise
223
+ ->(warning) { raise warning }
224
+ when :report
225
+ ->(warning) { Rails.error.report(warning, handled: true) }
226
+ when Proc
227
+ action
228
+ else
229
+ raise ArgumentError, "db_warnings_action must be one of :ignore, :log, :raise, :report, or a custom proc."
230
+ end
231
+ end
232
+
233
+ self.db_warnings_action = :ignore
234
+
235
+ # Specify allowlist of database warnings.
236
+ singleton_class.attr_accessor :db_warnings_ignore
237
+ self.db_warnings_ignore = []
238
+
197
239
  singleton_class.attr_accessor :writing_role
198
240
  self.writing_role = :writing
199
241
 
200
242
  singleton_class.attr_accessor :reading_role
201
243
  self.reading_role = :reading
202
244
 
245
+ def self.legacy_connection_handling=(_)
246
+ raise ArgumentError, <<~MSG.squish
247
+ The `legacy_connection_handling` setter was deprecated in 7.0 and removed in 7.1,
248
+ but is still defined in your configuration. Please remove this call as it no longer
249
+ has any effect."
250
+ MSG
251
+ end
252
+
203
253
  # Sets the async_query_executor for an application. By default the thread pool executor
204
254
  # set to +nil+ which will not run queries in the background. Applications must configure
205
255
  # a thread pool executor to use this feature. Options are:
@@ -258,6 +308,21 @@ module ActiveRecord
258
308
  singleton_class.attr_accessor :maintain_test_schema
259
309
  self.maintain_test_schema = nil
260
310
 
311
+ singleton_class.attr_accessor :raise_on_assign_to_attr_readonly
312
+ self.raise_on_assign_to_attr_readonly = false
313
+
314
+ singleton_class.attr_accessor :belongs_to_required_validates_foreign_key
315
+ self.belongs_to_required_validates_foreign_key = true
316
+
317
+ singleton_class.attr_accessor :before_committed_on_all_records
318
+ self.before_committed_on_all_records = false
319
+
320
+ singleton_class.attr_accessor :run_after_transaction_callbacks_in_order_defined
321
+ self.run_after_transaction_callbacks_in_order_defined = false
322
+
323
+ singleton_class.attr_accessor :commit_transaction_on_non_local_return
324
+ self.commit_transaction_on_non_local_return = false
325
+
261
326
  ##
262
327
  # :singleton-method:
263
328
  # Specify a threshold for the size of query result sets. If the number of
@@ -302,6 +367,12 @@ module ActiveRecord
302
367
  singleton_class.attr_accessor :timestamped_migrations
303
368
  self.timestamped_migrations = true
304
369
 
370
+ ##
371
+ # :singleton-method:
372
+ # Specify strategy to use for executing migrations.
373
+ singleton_class.attr_accessor :migration_strategy
374
+ self.migration_strategy = Migration::DefaultStrategy
375
+
305
376
  ##
306
377
  # :singleton-method:
307
378
  # Specify whether schema dump should happen at the end of the
@@ -321,12 +392,19 @@ module ActiveRecord
321
392
  singleton_class.attr_accessor :dump_schemas
322
393
  self.dump_schemas = :schema_search_path
323
394
 
324
- ##
325
- # :singleton-method:
326
- # Show a warning when Rails couldn't parse your database.yml
327
- # for multiple databases.
328
- singleton_class.attr_accessor :suppress_multiple_database_warning
329
- self.suppress_multiple_database_warning = false
395
+ def self.suppress_multiple_database_warning
396
+ ActiveRecord.deprecator.warn(<<-MSG.squish)
397
+ config.active_record.suppress_multiple_database_warning is deprecated and will be removed in Rails 7.2.
398
+ It no longer has any effect and should be removed from the configuration file.
399
+ MSG
400
+ end
401
+
402
+ def self.suppress_multiple_database_warning=(value)
403
+ ActiveRecord.deprecator.warn(<<-MSG.squish)
404
+ config.active_record.suppress_multiple_database_warning= is deprecated and will be removed in Rails 7.2.
405
+ It no longer has any effect and should be removed from the configuration file.
406
+ MSG
407
+ end
330
408
 
331
409
  ##
332
410
  # :singleton-method:
@@ -337,9 +415,53 @@ module ActiveRecord
337
415
  singleton_class.attr_accessor :verify_foreign_keys_for_fixtures
338
416
  self.verify_foreign_keys_for_fixtures = false
339
417
 
418
+ ##
419
+ # :singleton-method:
420
+ # If true, Rails will continue allowing plural association names in where clauses on singular associations
421
+ # This behavior will be removed in Rails 7.2.
422
+ singleton_class.attr_accessor :allow_deprecated_singular_associations_name
423
+ self.allow_deprecated_singular_associations_name = true
424
+
340
425
  singleton_class.attr_accessor :query_transformers
341
426
  self.query_transformers = []
342
427
 
428
+ ##
429
+ # :singleton-method:
430
+ # Application configurable boolean that instructs the YAML Coder to use
431
+ # an unsafe load if set to true.
432
+ singleton_class.attr_accessor :use_yaml_unsafe_load
433
+ self.use_yaml_unsafe_load = false
434
+
435
+ ##
436
+ # :singleton-method:
437
+ # Application configurable boolean that denotes whether or not to raise
438
+ # an exception when the PostgreSQLAdapter is provided with an integer that
439
+ # is wider than signed 64bit representation
440
+ singleton_class.attr_accessor :raise_int_wider_than_64bit
441
+ self.raise_int_wider_than_64bit = true
442
+
443
+ ##
444
+ # :singleton-method:
445
+ # Application configurable array that provides additional permitted classes
446
+ # to Psych safe_load in the YAML Coder
447
+ singleton_class.attr_accessor :yaml_column_permitted_classes
448
+ self.yaml_column_permitted_classes = [Symbol]
449
+
450
+ ##
451
+ # :singleton-method:
452
+ # Controls when to generate a value for <tt>has_secure_token</tt>
453
+ # declarations. Defaults to <tt>:create</tt>.
454
+ singleton_class.attr_accessor :generate_secure_token_on
455
+ self.generate_secure_token_on = :create
456
+
457
+ def self.marshalling_format_version
458
+ Marshalling.format_version
459
+ end
460
+
461
+ def self.marshalling_format_version=(value)
462
+ Marshalling.format_version = value
463
+ end
464
+
343
465
  def self.eager_load!
344
466
  super
345
467
  ActiveRecord::Locking.eager_load!
@@ -349,6 +471,11 @@ module ActiveRecord
349
471
  ActiveRecord::ConnectionAdapters.eager_load!
350
472
  ActiveRecord::Encryption.eager_load!
351
473
  end
474
+
475
+ # Explicitly closes all database connections in all pools.
476
+ def self.disconnect_all!
477
+ ConnectionAdapters::PoolConfig.disconnect_all!
478
+ end
352
479
  end
353
480
 
354
481
  ActiveSupport.on_load(:active_record) do
data/lib/arel/errors.rb CHANGED
@@ -6,4 +6,14 @@ module Arel # :nodoc: all
6
6
 
7
7
  class EmptyJoinError < ArelError
8
8
  end
9
+
10
+ class BindError < ArelError
11
+ def initialize(message, sql = nil)
12
+ if sql
13
+ super("#{message} in: #{sql.inspect}")
14
+ else
15
+ super(message)
16
+ end
17
+ end
18
+ end
9
19
  end
@@ -45,5 +45,9 @@ module Arel # :nodoc: all
45
45
  def coalesce(*exprs)
46
46
  Nodes::NamedFunction.new "COALESCE", exprs
47
47
  end
48
+
49
+ def cast(name, type)
50
+ Nodes::NamedFunction.new "CAST", [name.as(type)]
51
+ end
48
52
  end
49
53
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Arel
3
+ module Arel # :nodoc: all
4
4
  module FilterPredications
5
5
  def filter(expr)
6
6
  Nodes::Filter.new(self, expr)
@@ -18,6 +18,10 @@ module Arel # :nodoc: all
18
18
  children[1]
19
19
  end
20
20
 
21
+ def fetch_attribute(&block)
22
+ children.any? && children.all? { |child| child.fetch_attribute(&block) }
23
+ end
24
+
21
25
  def hash
22
26
  children.hash
23
27
  end
@@ -39,6 +39,12 @@ module Arel # :nodoc: all
39
39
  end
40
40
  end
41
41
 
42
+ class As < Binary
43
+ def to_cte
44
+ Arel::Nodes::Cte.new(left.name, right)
45
+ end
46
+ end
47
+
42
48
  class Between < Binary; include FetchAttribute; end
43
49
 
44
50
  class GreaterThan < Binary
@@ -112,7 +118,6 @@ module Arel # :nodoc: all
112
118
  end
113
119
 
114
120
  %w{
115
- As
116
121
  Assignment
117
122
  Join
118
123
  Union
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arel # :nodoc: all
4
+ module Nodes
5
+ class BoundSqlLiteral < NodeExpression
6
+ attr_reader :sql_with_placeholders, :positional_binds, :named_binds
7
+
8
+ def initialize(sql_with_placeholders, positional_binds, named_binds)
9
+ if !positional_binds.empty? && !named_binds.empty?
10
+ raise BindError.new("cannot mix positional and named binds", sql_with_placeholders)
11
+ elsif !positional_binds.empty?
12
+ if positional_binds.size != (expected = sql_with_placeholders.count("?"))
13
+ raise BindError.new("wrong number of bind variables (#{positional_binds.size} for #{expected})", sql_with_placeholders)
14
+ end
15
+ elsif !named_binds.empty?
16
+ tokens_in_string = sql_with_placeholders.scan(/:(?<!::)([a-zA-Z]\w*)/).flatten.map(&:to_sym).uniq
17
+ tokens_in_hash = named_binds.keys.map(&:to_sym).uniq
18
+
19
+ if !(missing = (tokens_in_string - tokens_in_hash)).empty?
20
+ if missing.size == 1
21
+ raise BindError.new("missing value for #{missing.first.inspect}", sql_with_placeholders)
22
+ else
23
+ raise BindError.new("missing values for #{missing.inspect}", sql_with_placeholders)
24
+ end
25
+ end
26
+ end
27
+
28
+ @sql_with_placeholders = sql_with_placeholders
29
+ if !positional_binds.empty?
30
+ @positional_binds = positional_binds
31
+ @named_binds = nil
32
+ else
33
+ @positional_binds = nil
34
+ @named_binds = named_binds
35
+ end
36
+ end
37
+
38
+ def hash
39
+ [self.class, sql_with_placeholders, positional_binds, named_binds].hash
40
+ end
41
+
42
+ def eql?(other)
43
+ self.class == other.class &&
44
+ sql_with_placeholders == other.sql_with_placeholders &&
45
+ positional_binds == other.positional_binds &&
46
+ named_binds == other.named_binds
47
+ end
48
+ alias :== :eql?
49
+
50
+ def +(other)
51
+ raise ArgumentError, "Expected Arel node" unless Arel.arel_node?(other)
52
+
53
+ Fragments.new([self, other])
54
+ end
55
+
56
+ def inspect
57
+ "#<#{self.class.name} #{sql_with_placeholders.inspect} #{(named_binds || positional_binds).inspect}>"
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arel # :nodoc: all
4
+ module Nodes
5
+ class Cte < Arel::Nodes::Binary
6
+ alias :name :left
7
+ alias :relation :right
8
+ attr_reader :materialized
9
+
10
+ def initialize(name, relation, materialized: nil)
11
+ super(name, relation)
12
+ @materialized = materialized
13
+ end
14
+
15
+ def hash
16
+ [name, relation, materialized].hash
17
+ end
18
+
19
+ def eql?(other)
20
+ self.class == other.class &&
21
+ self.name == other.name &&
22
+ self.relation == other.relation &&
23
+ self.materialized == other.materialized
24
+ end
25
+ alias :== :eql?
26
+
27
+ def to_cte
28
+ self
29
+ end
30
+
31
+ def to_table
32
+ Arel::Table.new(name)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Arel
3
+ module Arel # :nodoc: all
4
4
  module Nodes
5
5
  class Filter < Binary
6
6
  include Arel::WindowPredications
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arel # :nodoc: all
4
+ module Nodes
5
+ class Fragments < Arel::Nodes::Node
6
+ attr_reader :values
7
+
8
+ def initialize(values = [])
9
+ super()
10
+ @values = values
11
+ end
12
+
13
+ def initialize_copy(other)
14
+ super
15
+ @values = @values.clone
16
+ end
17
+
18
+ def hash
19
+ [@values].hash
20
+ end
21
+
22
+ def +(other)
23
+ raise ArgumentError, "Expected Arel node" unless Arel.arel_node?(other)
24
+
25
+ self.class.new([*@values, other])
26
+ end
27
+
28
+ def eql?(other)
29
+ self.class == other.class &&
30
+ self.values == other.values
31
+ end
32
+ alias :== :eql?
33
+ end
34
+ end
35
+ end
@@ -36,14 +36,6 @@ module Arel # :nodoc: all
36
36
  attribute.quoted_array(values)
37
37
  end
38
38
 
39
- def table_name
40
- attribute.relation.table_alias || attribute.relation.name
41
- end
42
-
43
- def column_name
44
- attribute.name
45
- end
46
-
47
39
  def casted_values
48
40
  type = attribute.type_caster
49
41
 
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arel # :nodoc: all
4
+ module Nodes
5
+ class LeadingJoin < Arel::Nodes::InnerJoin
6
+ end
7
+ end
8
+ end
@@ -2,8 +2,117 @@
2
2
 
3
3
  module Arel # :nodoc: all
4
4
  module Nodes
5
- ###
6
- # Abstract base class for all AST nodes
5
+ # = Using +Arel::Nodes::Node+
6
+ #
7
+ # Active Record uses Arel to compose SQL statements. Instead of building SQL strings directly, it's building an
8
+ # abstract syntax tree (AST) of the statement using various types of Arel::Nodes::Node. Each node represents a
9
+ # fragment of a SQL statement.
10
+ #
11
+ # The intermediate representation allows Arel to compile the statement into the database's specific SQL dialect
12
+ # only before sending it without having to care about the nuances of each database when building the statement.
13
+ # It also allows easier composition of statements without having to resort to (brittle and unsafe) string manipulation.
14
+ #
15
+ # == Building constraints
16
+ #
17
+ # One of the most common use cases of Arel is generating constraints for +SELECT+ statements. To help with that,
18
+ # most nodes include a couple of useful factory methods to create subtree structures for common constraints. For
19
+ # a full list of those, please refer to Arel::Predications.
20
+ #
21
+ # The following example creates an equality constraint where the value of the name column on the users table
22
+ # matches the value DHH.
23
+ #
24
+ # users = Arel::Table.new(:users)
25
+ # constraint = users[:name].eq("DHH")
26
+ #
27
+ # # => Arel::Nodes::Equality.new(
28
+ # # Arel::Attributes::Attribute.new(users, "name"),
29
+ # # Arel::Nodes::Casted.new(
30
+ # # "DHH",
31
+ # # Arel::Attributes::Attribute.new(users, "name")
32
+ # # )
33
+ # # )
34
+ #
35
+ # The resulting SQL fragment will look like this:
36
+ #
37
+ # "users"."name" = 'DHH'
38
+ #
39
+ # The constraint fragments can be used with regular ActiveRecord::Relation objects instead of a Hash. The
40
+ # following two examples show two ways of creating the same query.
41
+ #
42
+ # User.where(name: 'DHH')
43
+ #
44
+ # # SELECT "users".* FROM "users" WHERE "users"."name" = 'DHH'
45
+ #
46
+ # users = User.arel_table
47
+ #
48
+ # User.where(users[:name].eq('DHH'))
49
+ #
50
+ # # SELECT "users".* FROM "users" WHERE "users"."name" = 'DHH'
51
+ #
52
+ # == Functions
53
+ #
54
+ # Arel comes with built-in support for SQL functions like +COUNT+, +SUM+, +MIN+, +MAX+, and +AVG+. The
55
+ # Arel::Expressions module includes factory methods for the default functions.
56
+ #
57
+ # employees = Employee.arel_table
58
+ #
59
+ # Employee.select(employees[:department_id], employees[:salary].average).group(employees[:department_id])
60
+ #
61
+ # # SELECT "employees"."department_id", AVG("employees"."salary")
62
+ # # FROM "employees" GROUP BY "employees"."department_id"
63
+ #
64
+ # It’s also possible to use custom functions by using the Arel::Nodes::NamedFunction node type. It accepts a
65
+ # function name and an array of parameters.
66
+ #
67
+ # Arel::Nodes::NamedFunction.new('date_trunc', [Arel::Nodes.build_quoted('day'), User.arel_table[:created_at]])
68
+ #
69
+ # # date_trunc('day', "users"."created_at")
70
+ #
71
+ # == Quoting & bind params
72
+ #
73
+ # Values that you pass to Arel nodes need to be quoted or wrapped in bind params. This ensures they are properly
74
+ # converted into the correct format without introducing a possible SQL injection vulnerability. Most factory
75
+ # methods (like +eq+, +gt+, +lteq+, …) quote passed values automatically. When not using a factory method, it’s
76
+ # possible to convert a value and wrap it in an Arel::Nodes::Quoted node (if necessary) by calling +Arel::Nodes.
77
+ # build_quoted+.
78
+ #
79
+ # Arel::Nodes.build_quoted("foo") # 'foo'
80
+ # Arel::Nodes.build_quoted(12.3) # 12.3
81
+ #
82
+ # Instead of quoting values and embedding them directly in the SQL statement, it’s also possible to create bind
83
+ # params. This keeps the actual values outside of the statement and allows using the prepared statement feature
84
+ # of some databases.
85
+ #
86
+ # attribute = ActiveRecord::Relation::QueryAttribute.new(:name, "DHH", ActiveRecord::Type::String.new)
87
+ # Arel::Nodes::BindParam.new(attribute)
88
+ #
89
+ # When ActiveRecord runs the query, bind params are replaced by placeholders (like +$1+) and the values are passed
90
+ # separately.
91
+ #
92
+ # == SQL Literals
93
+ #
94
+ # For cases where there is no way to represent a particular SQL fragment using Arel nodes, you can use an SQL
95
+ # literal. SQL literals are strings that Arel will treat “as is”.
96
+ #
97
+ # Arel.sql('LOWER("users"."name")').eq('dhh')
98
+ #
99
+ # # LOWER("users"."name") = 'dhh'
100
+ #
101
+ # Please keep in mind that passing data as raw SQL literals might introduce a possible SQL injection. However,
102
+ # `Arel.sql` supports binding parameters which will ensure proper quoting. This can be useful when you need to
103
+ # control the exact SQL you run, but you still have potentially user-supplied values.
104
+ #
105
+ # Arel.sql('LOWER("users"."name") = ?', 'dhh')
106
+ #
107
+ # # LOWER("users"."name") = 'dhh'
108
+ #
109
+ # You can also combine SQL literals.
110
+ #
111
+ # sql = Arel.sql('SELECT * FROM "users" WHERE ')
112
+ # sql += Arel.sql('LOWER("users"."name") = :name', name: 'dhh')
113
+ # sql += Arel.sql('AND "users"."age" > :age', age: 35)
114
+ #
115
+ # # SELECT * FROM "users" WHERE LOWER("users"."name") = 'dhh' AND "users"."age" > '35'
7
116
  class Node
8
117
  include Arel::FactoryMethods
9
118
 
@@ -14,6 +14,12 @@ module Arel # :nodoc: all
14
14
 
15
15
  def fetch_attribute
16
16
  end
17
+
18
+ def +(other)
19
+ raise ArgumentError, "Expected Arel node" unless Arel.arel_node?(other)
20
+
21
+ Fragments.new([self, other])
22
+ end
17
23
  end
18
24
  end
19
25
  end
@@ -26,6 +26,10 @@ module Arel # :nodoc: all
26
26
  def able_to_type_cast?
27
27
  relation.respond_to?(:able_to_type_cast?) && relation.able_to_type_cast?
28
28
  end
29
+
30
+ def to_cte
31
+ Arel::Nodes::Cte.new(name, relation)
32
+ end
29
33
  end
30
34
  end
31
35
  end