activerecord 6.1.3.2 → 7.0.0.alpha2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (229) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +734 -1058
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/active_record/aggregations.rb +1 -1
  6. data/lib/active_record/association_relation.rb +0 -10
  7. data/lib/active_record/associations/association.rb +35 -7
  8. data/lib/active_record/associations/association_scope.rb +1 -3
  9. data/lib/active_record/associations/belongs_to_association.rb +16 -6
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  11. data/lib/active_record/associations/builder/association.rb +8 -2
  12. data/lib/active_record/associations/builder/belongs_to.rb +19 -6
  13. data/lib/active_record/associations/builder/collection_association.rb +1 -1
  14. data/lib/active_record/associations/builder/has_many.rb +3 -2
  15. data/lib/active_record/associations/builder/has_one.rb +2 -1
  16. data/lib/active_record/associations/builder/singular_association.rb +2 -2
  17. data/lib/active_record/associations/collection_association.rb +24 -25
  18. data/lib/active_record/associations/collection_proxy.rb +8 -3
  19. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  20. data/lib/active_record/associations/has_many_association.rb +1 -1
  21. data/lib/active_record/associations/has_many_through_association.rb +2 -1
  22. data/lib/active_record/associations/has_one_association.rb +10 -7
  23. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  24. data/lib/active_record/associations/preloader/association.rb +161 -49
  25. data/lib/active_record/associations/preloader/batch.rb +51 -0
  26. data/lib/active_record/associations/preloader/branch.rb +147 -0
  27. data/lib/active_record/associations/preloader/through_association.rb +37 -11
  28. data/lib/active_record/associations/preloader.rb +46 -110
  29. data/lib/active_record/associations/singular_association.rb +8 -2
  30. data/lib/active_record/associations/through_association.rb +1 -1
  31. data/lib/active_record/associations.rb +76 -81
  32. data/lib/active_record/asynchronous_queries_tracker.rb +57 -0
  33. data/lib/active_record/attribute_assignment.rb +1 -1
  34. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  35. data/lib/active_record/attribute_methods/dirty.rb +41 -16
  36. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  37. data/lib/active_record/attribute_methods/query.rb +2 -2
  38. data/lib/active_record/attribute_methods/read.rb +7 -5
  39. data/lib/active_record/attribute_methods/serialization.rb +66 -12
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
  41. data/lib/active_record/attribute_methods/write.rb +7 -10
  42. data/lib/active_record/attribute_methods.rb +6 -9
  43. data/lib/active_record/attributes.rb +24 -35
  44. data/lib/active_record/autosave_association.rb +3 -18
  45. data/lib/active_record/base.rb +19 -1
  46. data/lib/active_record/callbacks.rb +2 -2
  47. data/lib/active_record/coders/yaml_column.rb +11 -1
  48. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +312 -0
  49. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
  50. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +31 -558
  52. data/lib/active_record/connection_adapters/abstract/database_statements.rb +45 -21
  53. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
  54. data/lib/active_record/connection_adapters/abstract/quoting.rb +14 -7
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +5 -18
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -9
  57. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +60 -16
  58. data/lib/active_record/connection_adapters/abstract/transaction.rb +17 -6
  59. data/lib/active_record/connection_adapters/abstract_adapter.rb +115 -69
  60. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +96 -81
  61. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +6 -2
  62. data/lib/active_record/connection_adapters/mysql/database_statements.rb +33 -21
  63. data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -1
  64. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +3 -0
  65. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
  66. data/lib/active_record/connection_adapters/pool_config.rb +1 -3
  67. data/lib/active_record/connection_adapters/pool_manager.rb +5 -1
  68. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -12
  69. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  70. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  72. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  73. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +28 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  76. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  77. data/lib/active_record/connection_adapters/postgresql/quoting.rb +6 -6
  78. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -0
  79. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +5 -1
  80. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +12 -12
  81. data/lib/active_record/connection_adapters/postgresql_adapter.rb +157 -100
  82. data/lib/active_record/connection_adapters/schema_cache.rb +35 -4
  83. data/lib/active_record/connection_adapters/sql_type_metadata.rb +0 -2
  84. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +23 -17
  85. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -2
  86. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -30
  87. data/lib/active_record/connection_adapters.rb +8 -5
  88. data/lib/active_record/connection_handling.rb +20 -38
  89. data/lib/active_record/core.rb +129 -117
  90. data/lib/active_record/database_configurations/database_config.rb +12 -0
  91. data/lib/active_record/database_configurations/hash_config.rb +27 -1
  92. data/lib/active_record/database_configurations/url_config.rb +2 -2
  93. data/lib/active_record/database_configurations.rb +18 -9
  94. data/lib/active_record/delegated_type.rb +33 -11
  95. data/lib/active_record/destroy_association_async_job.rb +1 -1
  96. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  97. data/lib/active_record/dynamic_matchers.rb +1 -1
  98. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  99. data/lib/active_record/encryption/cipher.rb +53 -0
  100. data/lib/active_record/encryption/config.rb +44 -0
  101. data/lib/active_record/encryption/configurable.rb +61 -0
  102. data/lib/active_record/encryption/context.rb +35 -0
  103. data/lib/active_record/encryption/contexts.rb +72 -0
  104. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  105. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  106. data/lib/active_record/encryption/encryptable_record.rb +208 -0
  107. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  108. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  109. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  110. data/lib/active_record/encryption/encryptor.rb +155 -0
  111. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  112. data/lib/active_record/encryption/errors.rb +15 -0
  113. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  114. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +29 -0
  115. data/lib/active_record/encryption/key.rb +28 -0
  116. data/lib/active_record/encryption/key_generator.rb +42 -0
  117. data/lib/active_record/encryption/key_provider.rb +46 -0
  118. data/lib/active_record/encryption/message.rb +33 -0
  119. data/lib/active_record/encryption/message_serializer.rb +80 -0
  120. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  121. data/lib/active_record/encryption/properties.rb +76 -0
  122. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  123. data/lib/active_record/encryption/scheme.rb +99 -0
  124. data/lib/active_record/encryption.rb +55 -0
  125. data/lib/active_record/enum.rb +44 -46
  126. data/lib/active_record/errors.rb +66 -3
  127. data/lib/active_record/fixture_set/file.rb +15 -1
  128. data/lib/active_record/fixture_set/table_row.rb +40 -5
  129. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  130. data/lib/active_record/fixtures.rb +16 -11
  131. data/lib/active_record/future_result.rb +139 -0
  132. data/lib/active_record/gem_version.rb +4 -4
  133. data/lib/active_record/inheritance.rb +55 -17
  134. data/lib/active_record/insert_all.rb +39 -6
  135. data/lib/active_record/integration.rb +1 -1
  136. data/lib/active_record/internal_metadata.rb +3 -5
  137. data/lib/active_record/legacy_yaml_adapter.rb +1 -1
  138. data/lib/active_record/locking/optimistic.rb +10 -9
  139. data/lib/active_record/log_subscriber.rb +6 -2
  140. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  141. data/lib/active_record/middleware/database_selector.rb +8 -3
  142. data/lib/active_record/migration/command_recorder.rb +4 -4
  143. data/lib/active_record/migration/compatibility.rb +83 -1
  144. data/lib/active_record/migration/join_table.rb +1 -1
  145. data/lib/active_record/migration.rb +109 -79
  146. data/lib/active_record/model_schema.rb +46 -32
  147. data/lib/active_record/nested_attributes.rb +3 -3
  148. data/lib/active_record/no_touching.rb +2 -2
  149. data/lib/active_record/null_relation.rb +2 -6
  150. data/lib/active_record/persistence.rb +134 -45
  151. data/lib/active_record/query_cache.rb +2 -2
  152. data/lib/active_record/query_logs.rb +203 -0
  153. data/lib/active_record/querying.rb +15 -5
  154. data/lib/active_record/railtie.rb +117 -17
  155. data/lib/active_record/railties/controller_runtime.rb +1 -1
  156. data/lib/active_record/railties/databases.rake +83 -58
  157. data/lib/active_record/readonly_attributes.rb +11 -0
  158. data/lib/active_record/reflection.rb +45 -44
  159. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  160. data/lib/active_record/relation/batches.rb +3 -3
  161. data/lib/active_record/relation/calculations.rb +42 -25
  162. data/lib/active_record/relation/delegation.rb +6 -6
  163. data/lib/active_record/relation/finder_methods.rb +32 -23
  164. data/lib/active_record/relation/merger.rb +20 -13
  165. data/lib/active_record/relation/predicate_builder.rb +1 -6
  166. data/lib/active_record/relation/query_attribute.rb +5 -11
  167. data/lib/active_record/relation/query_methods.rb +233 -50
  168. data/lib/active_record/relation/record_fetch_warning.rb +2 -2
  169. data/lib/active_record/relation/spawn_methods.rb +2 -2
  170. data/lib/active_record/relation/where_clause.rb +22 -15
  171. data/lib/active_record/relation.rb +170 -87
  172. data/lib/active_record/result.rb +17 -2
  173. data/lib/active_record/runtime_registry.rb +2 -4
  174. data/lib/active_record/sanitization.rb +11 -7
  175. data/lib/active_record/schema_dumper.rb +3 -3
  176. data/lib/active_record/schema_migration.rb +0 -4
  177. data/lib/active_record/scoping/default.rb +62 -15
  178. data/lib/active_record/scoping/named.rb +3 -11
  179. data/lib/active_record/scoping.rb +40 -22
  180. data/lib/active_record/serialization.rb +1 -1
  181. data/lib/active_record/signed_id.rb +1 -1
  182. data/lib/active_record/statement_cache.rb +2 -2
  183. data/lib/active_record/tasks/database_tasks.rb +107 -23
  184. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  185. data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -11
  186. data/lib/active_record/test_databases.rb +1 -1
  187. data/lib/active_record/test_fixtures.rb +45 -4
  188. data/lib/active_record/timestamp.rb +3 -4
  189. data/lib/active_record/transactions.rb +9 -14
  190. data/lib/active_record/translation.rb +2 -2
  191. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  192. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  193. data/lib/active_record/type/internal/timezone.rb +2 -2
  194. data/lib/active_record/type/serialized.rb +1 -1
  195. data/lib/active_record/type/type_map.rb +17 -20
  196. data/lib/active_record/type.rb +1 -2
  197. data/lib/active_record/validations/associated.rb +1 -1
  198. data/lib/active_record/validations/numericality.rb +1 -1
  199. data/lib/active_record.rb +170 -2
  200. data/lib/arel/attributes/attribute.rb +0 -8
  201. data/lib/arel/collectors/bind.rb +2 -2
  202. data/lib/arel/collectors/composite.rb +3 -3
  203. data/lib/arel/collectors/sql_string.rb +1 -1
  204. data/lib/arel/collectors/substitute_binds.rb +1 -1
  205. data/lib/arel/crud.rb +18 -22
  206. data/lib/arel/delete_manager.rb +2 -4
  207. data/lib/arel/insert_manager.rb +2 -3
  208. data/lib/arel/nodes/casted.rb +1 -1
  209. data/lib/arel/nodes/delete_statement.rb +8 -13
  210. data/lib/arel/nodes/homogeneous_in.rb +4 -0
  211. data/lib/arel/nodes/insert_statement.rb +2 -2
  212. data/lib/arel/nodes/select_core.rb +2 -2
  213. data/lib/arel/nodes/select_statement.rb +2 -2
  214. data/lib/arel/nodes/update_statement.rb +3 -2
  215. data/lib/arel/predications.rb +3 -3
  216. data/lib/arel/select_manager.rb +10 -4
  217. data/lib/arel/table.rb +0 -1
  218. data/lib/arel/tree_manager.rb +0 -12
  219. data/lib/arel/update_manager.rb +2 -4
  220. data/lib/arel/visitors/dot.rb +80 -90
  221. data/lib/arel/visitors/mysql.rb +6 -1
  222. data/lib/arel/visitors/postgresql.rb +0 -10
  223. data/lib/arel/visitors/to_sql.rb +44 -3
  224. data/lib/arel.rb +1 -1
  225. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  226. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  227. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  228. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  229. metadata +55 -16
@@ -8,8 +8,6 @@ require "active_support/core_ext/array/wrap"
8
8
 
9
9
  module ActiveRecord
10
10
  module QueryMethods
11
- extend ActiveSupport::Concern
12
-
13
11
  include ActiveModel::ForbiddenAttributesProtection
14
12
 
15
13
  # WhereChain objects act as placeholder for queries in which #where does not have any parameter.
@@ -50,6 +48,34 @@ module ActiveRecord
50
48
  @scope
51
49
  end
52
50
 
51
+ # Returns a new relation with joins and where clause to identify
52
+ # associated relations.
53
+ #
54
+ # For example, posts that are associated to a related author:
55
+ #
56
+ # Post.where.associated(:author)
57
+ # # SELECT "posts".* FROM "posts"
58
+ # # INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
59
+ # # WHERE "authors"."id" IS NOT NULL
60
+ #
61
+ # Additionally, multiple relations can be combined. This will return posts
62
+ # associated to both an author and any comments:
63
+ #
64
+ # Post.where.associated(:author, :comments)
65
+ # # SELECT "posts".* FROM "posts"
66
+ # # INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
67
+ # # INNER JOIN "comments" ON "comments"."post_id" = "posts"."id"
68
+ # # WHERE "authors"."id" IS NOT NULL AND "comments"."id" IS NOT NULL
69
+ def associated(*associations)
70
+ associations.each do |association|
71
+ reflection = @scope.klass._reflect_on_association(association)
72
+ @scope.joins!(association)
73
+ self.not(reflection.table_name => { reflection.association_primary_key => nil })
74
+ end
75
+
76
+ @scope
77
+ end
78
+
53
79
  # Returns a new relation with left outer joins and where clause to identify
54
80
  # missing relations.
55
81
  #
@@ -68,12 +94,11 @@ module ActiveRecord
68
94
  # # LEFT OUTER JOIN "authors" ON "authors"."id" = "posts"."author_id"
69
95
  # # LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id"
70
96
  # # WHERE "authors"."id" IS NULL AND "comments"."id" IS NULL
71
- def missing(*args)
72
- args.each do |arg|
73
- reflection = @scope.klass._reflect_on_association(arg)
74
- opts = { reflection.table_name => { reflection.association_primary_key => nil } }
75
- @scope.left_outer_joins!(arg)
76
- @scope.where!(opts)
97
+ def missing(*associations)
98
+ associations.each do |association|
99
+ reflection = @scope.klass._reflect_on_association(association)
100
+ @scope.left_outer_joins!(association)
101
+ @scope.where!(reflection.table_name => { reflection.association_primary_key => nil })
77
102
  end
78
103
 
79
104
  @scope
@@ -148,7 +173,7 @@ module ActiveRecord
148
173
  #
149
174
  # User.includes(:posts).where(posts: { name: 'example' })
150
175
  def includes(*args)
151
- check_if_method_has_arguments!(:includes, args)
176
+ check_if_method_has_arguments!(__callee__, args)
152
177
  spawn.includes!(*args)
153
178
  end
154
179
 
@@ -164,7 +189,7 @@ module ActiveRecord
164
189
  # # FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" =
165
190
  # # "users"."id"
166
191
  def eager_load(*args)
167
- check_if_method_has_arguments!(:eager_load, args)
192
+ check_if_method_has_arguments!(__callee__, args)
168
193
  spawn.eager_load!(*args)
169
194
  end
170
195
 
@@ -178,7 +203,7 @@ module ActiveRecord
178
203
  # User.preload(:posts)
179
204
  # # SELECT "posts".* FROM "posts" WHERE "posts"."user_id" IN (1, 2, 3)
180
205
  def preload(*args)
181
- check_if_method_has_arguments!(:preload, args)
206
+ check_if_method_has_arguments!(__callee__, args)
182
207
  spawn.preload!(*args)
183
208
  end
184
209
 
@@ -211,7 +236,7 @@ module ActiveRecord
211
236
  # User.includes(:posts).where("posts.name = 'foo'").references(:posts)
212
237
  # # Query now knows the string references posts, so adds a JOIN
213
238
  def references(*table_names)
214
- check_if_method_has_arguments!(:references, table_names)
239
+ check_if_method_has_arguments!(__callee__, table_names)
215
240
  spawn.references!(*table_names)
216
241
  end
217
242
 
@@ -269,7 +294,7 @@ module ActiveRecord
269
294
  return super()
270
295
  end
271
296
 
272
- check_if_method_has_arguments!(:select, fields, "Call `select' with at least one field.")
297
+ check_if_method_has_arguments!(__callee__, fields, "Call `select' with at least one field.")
273
298
  spawn._select!(*fields)
274
299
  end
275
300
 
@@ -289,7 +314,7 @@ module ActiveRecord
289
314
  # This is short-hand for <tt>unscope(:select).select(fields)</tt>.
290
315
  # Note that we're unscoping the entire select statement.
291
316
  def reselect(*args)
292
- check_if_method_has_arguments!(:reselect, args)
317
+ check_if_method_has_arguments!(__callee__, args)
293
318
  spawn.reselect!(*args)
294
319
  end
295
320
 
@@ -320,7 +345,7 @@ module ActiveRecord
320
345
  # User.select([:id, :first_name]).group(:id, :first_name).first(3)
321
346
  # # => [#<User id: 1, first_name: "Bill">, #<User id: 2, first_name: "Earl">, #<User id: 3, first_name: "Beto">]
322
347
  def group(*args)
323
- check_if_method_has_arguments!(:group, args)
348
+ check_if_method_has_arguments!(__callee__, args)
324
349
  spawn.group!(*args)
325
350
  end
326
351
 
@@ -329,17 +354,37 @@ module ActiveRecord
329
354
  self
330
355
  end
331
356
 
332
- # Allows to specify an order attribute:
357
+ # Applies an <code>ORDER BY</code> clause to a query.
358
+ #
359
+ # #order accepts arguments in one of several formats.
360
+ #
361
+ # === symbols
362
+ #
363
+ # The symbol represents the name of the column you want to order the results by.
333
364
  #
334
365
  # User.order(:name)
335
366
  # # SELECT "users".* FROM "users" ORDER BY "users"."name" ASC
336
367
  #
368
+ # By default, the order is ascending. If you want descending order, you can
369
+ # map the column name symbol to +:desc+.
370
+ #
337
371
  # User.order(email: :desc)
338
372
  # # SELECT "users".* FROM "users" ORDER BY "users"."email" DESC
339
373
  #
374
+ # Multiple columns can be passed this way, and they will be applied in the order specified.
375
+ #
340
376
  # User.order(:name, email: :desc)
341
377
  # # SELECT "users".* FROM "users" ORDER BY "users"."name" ASC, "users"."email" DESC
342
378
  #
379
+ # === strings
380
+ #
381
+ # Strings are passed directly to the database, allowing you to specify
382
+ # simple SQL expressions.
383
+ #
384
+ # This could be a source of SQL injection, so only strings composed of plain
385
+ # column names and simple <code>function(column_name)</code> expressions
386
+ # with optional +ASC+/+DESC+ modifiers are allowed.
387
+ #
343
388
  # User.order('name')
344
389
  # # SELECT "users".* FROM "users" ORDER BY name
345
390
  #
@@ -348,8 +393,21 @@ module ActiveRecord
348
393
  #
349
394
  # User.order('name DESC, email')
350
395
  # # SELECT "users".* FROM "users" ORDER BY name DESC, email
396
+ #
397
+ # === Arel
398
+ #
399
+ # If you need to pass in complicated expressions that you have verified
400
+ # are safe for the database, you can use Arel.
401
+ #
402
+ # User.order(Arel.sql('end_date - start_date'))
403
+ # # SELECT "users".* FROM "users" ORDER BY end_date - start_date
404
+ #
405
+ # Custom query syntax, like JSON columns for Postgres, is supported in this way.
406
+ #
407
+ # User.order(Arel.sql("payload->>'kind'"))
408
+ # # SELECT "users".* FROM "users" ORDER BY payload->>'kind'
351
409
  def order(*args)
352
- check_if_method_has_arguments!(:order, args) do
410
+ check_if_method_has_arguments!(__callee__, args) do
353
411
  sanitize_order_arguments(args)
354
412
  end
355
413
  spawn.order!(*args)
@@ -362,6 +420,23 @@ module ActiveRecord
362
420
  self
363
421
  end
364
422
 
423
+ # Allows to specify an order by a specific set of values. Depending on your
424
+ # adapter this will either use a CASE statement or a built-in function.
425
+ #
426
+ # User.in_order_of(:id, [1, 5, 3])
427
+ # # SELECT "users".* FROM "users" ORDER BY FIELD("users"."id", 1, 5, 3)
428
+ #
429
+ def in_order_of(column, values)
430
+ klass.disallow_raw_sql!([column], permit: connection.column_name_with_order_matcher)
431
+
432
+ references = column_references([column])
433
+ self.references_values |= references unless references.empty?
434
+
435
+ column = order_column(column.to_s) if column.is_a?(Symbol)
436
+
437
+ spawn.order!(connection.field_ordered_value(column, values))
438
+ end
439
+
365
440
  # Replaces any existing order defined on the relation with the specified order.
366
441
  #
367
442
  # User.order('email DESC').reorder('id ASC') # generated SQL has 'ORDER BY id ASC'
@@ -372,7 +447,7 @@ module ActiveRecord
372
447
  #
373
448
  # generates a query with 'ORDER BY id ASC, name ASC'.
374
449
  def reorder(*args)
375
- check_if_method_has_arguments!(:reorder, args) do
450
+ check_if_method_has_arguments!(__callee__, args) do
376
451
  sanitize_order_arguments(args) unless args.all?(&:blank?)
377
452
  end
378
453
  spawn.reorder!(*args)
@@ -425,7 +500,7 @@ module ActiveRecord
425
500
  # has_many :comments, -> { unscope(where: :trashed) }
426
501
  #
427
502
  def unscope(*args)
428
- check_if_method_has_arguments!(:unscope, args)
503
+ check_if_method_has_arguments!(__callee__, args)
429
504
  spawn.unscope!(*args)
430
505
  end
431
506
 
@@ -458,7 +533,7 @@ module ActiveRecord
458
533
  self
459
534
  end
460
535
 
461
- # Performs a joins on +args+. The given symbol(s) should match the name of
536
+ # Performs JOINs on +args+. The given symbol(s) should match the name of
462
537
  # the association(s).
463
538
  #
464
539
  # User.joins(:posts)
@@ -487,7 +562,7 @@ module ActiveRecord
487
562
  # User.joins("LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id")
488
563
  # # SELECT "users".* FROM "users" LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id
489
564
  def joins(*args)
490
- check_if_method_has_arguments!(:joins, args)
565
+ check_if_method_has_arguments!(__callee__, args)
491
566
  spawn.joins!(*args)
492
567
  end
493
568
 
@@ -496,7 +571,7 @@ module ActiveRecord
496
571
  self
497
572
  end
498
573
 
499
- # Performs a left outer joins on +args+:
574
+ # Performs LEFT OUTER JOINs on +args+:
500
575
  #
501
576
  # User.left_outer_joins(:posts)
502
577
  # => SELECT "users".* FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"
@@ -578,13 +653,13 @@ module ActiveRecord
578
653
  #
579
654
  # Fields can be symbols or strings. Values can be single values, arrays, or ranges.
580
655
  #
581
- # User.where({ name: "Joe", email: "joe@example.com" })
656
+ # User.where(name: "Joe", email: "joe@example.com")
582
657
  # # SELECT * FROM users WHERE name = 'Joe' AND email = 'joe@example.com'
583
658
  #
584
- # User.where({ name: ["Alice", "Bob"]})
659
+ # User.where(name: ["Alice", "Bob"])
585
660
  # # SELECT * FROM users WHERE name IN ('Alice', 'Bob')
586
661
  #
587
- # User.where({ created_at: (Time.now.midnight - 1.day)..Time.now.midnight })
662
+ # User.where(created_at: (Time.now.midnight - 1.day)..Time.now.midnight)
588
663
  # # SELECT * FROM users WHERE (created_at BETWEEN '2012-06-09 07:00:00.000000' AND '2012-06-10 07:00:00.000000')
589
664
  #
590
665
  # In the case of a belongs_to relationship, an association key can be used
@@ -614,8 +689,8 @@ module ActiveRecord
614
689
  #
615
690
  # For hash conditions, you can either use the table name in the key, or use a sub-hash.
616
691
  #
617
- # User.joins(:posts).where({ "posts.published" => true })
618
- # User.joins(:posts).where({ posts: { published: true } })
692
+ # User.joins(:posts).where("posts.published" => true)
693
+ # User.joins(:posts).where(posts: { published: true })
619
694
  #
620
695
  # === no argument
621
696
  #
@@ -668,6 +743,59 @@ module ActiveRecord
668
743
  scope
669
744
  end
670
745
 
746
+ # Allows you to invert an entire where clause instead of manually applying conditions.
747
+ #
748
+ # class User
749
+ # scope :active, -> { where(accepted: true, locked: false) }
750
+ # end
751
+ #
752
+ # User.where(accepted: true)
753
+ # # WHERE `accepted` = 1
754
+ #
755
+ # User.where(accepted: true).invert_where
756
+ # # WHERE `accepted` != 1
757
+ #
758
+ # User.active
759
+ # # WHERE `accepted` = 1 AND `locked` = 0
760
+ #
761
+ # User.active.invert_where
762
+ # # WHERE NOT (`accepted` = 1 AND `locked` = 0)
763
+ #
764
+ # Be careful because this inverts all conditions before +invert_where+ call.
765
+ #
766
+ # class User
767
+ # scope :active, -> { where(accepted: true, locked: false) }
768
+ # scope :inactive, -> { active.invert_where } # Do not attempt it
769
+ # end
770
+ #
771
+ # # It also inverts `where(role: 'admin')` unexpectedly.
772
+ # User.where(role: 'admin').inactive
773
+ # # WHERE NOT (`role` = 'admin' AND `accepted` = 1 AND `locked` = 0)
774
+ #
775
+ def invert_where
776
+ spawn.invert_where!
777
+ end
778
+
779
+ def invert_where! # :nodoc:
780
+ self.where_clause = where_clause.invert
781
+ self
782
+ end
783
+
784
+ # Checks whether the given relation is structurally compatible with this relation, to determine
785
+ # if it's possible to use the #and and #or methods without raising an error. Structurally
786
+ # compatible is defined as: they must be scoping the same model, and they must differ only by
787
+ # #where (if no #group has been defined) or #having (if a #group is present).
788
+ #
789
+ # Post.where("id = 1").structurally_compatible?(Post.where("author_id = 3"))
790
+ # # => true
791
+ #
792
+ # Post.joins(:comments).structurally_compatible?(Post.where("id = 1"))
793
+ # # => false
794
+ #
795
+ def structurally_compatible?(other)
796
+ structurally_incompatible_values_for(other).empty?
797
+ end
798
+
671
799
  # Returns a new relation, which is the logical intersection of this relation and the one passed
672
800
  # as an argument.
673
801
  #
@@ -886,7 +1014,7 @@ module ActiveRecord
886
1014
  self
887
1015
  end
888
1016
 
889
- # Specifies table from which the records will be fetched. For example:
1017
+ # Specifies the table from which the records will be fetched. For example:
890
1018
  #
891
1019
  # Topic.select('title').from('posts')
892
1020
  # # SELECT title FROM posts
@@ -896,9 +1024,26 @@ module ActiveRecord
896
1024
  # Topic.select('title').from(Topic.approved)
897
1025
  # # SELECT title FROM (SELECT * FROM topics WHERE approved = 't') subquery
898
1026
  #
1027
+ # Passing a second argument (string or symbol), creates the alias for the SQL from clause. Otherwise the alias "subquery" is used:
1028
+ #
899
1029
  # Topic.select('a.title').from(Topic.approved, :a)
900
1030
  # # SELECT a.title FROM (SELECT * FROM topics WHERE approved = 't') a
901
1031
  #
1032
+ # It does not add multiple arguments to the SQL from clause. The last +from+ chained is the one used:
1033
+ #
1034
+ # Topic.select('title').from(Topic.approved).from(Topic.inactive)
1035
+ # # SELECT title FROM (SELECT topics.* FROM topics WHERE topics.active = 'f') subquery
1036
+ #
1037
+ # For multiple arguments for the SQL from clause, you can pass a string with the exact elements in the SQL from list:
1038
+ #
1039
+ # color = "red"
1040
+ # Color
1041
+ # .from("colors c, JSONB_ARRAY_ELEMENTS(colored_things) AS colorvalues(colorvalue)")
1042
+ # .where("colorvalue->>'color' = ?", color)
1043
+ # .select("c.*").to_a
1044
+ # # SELECT c.*
1045
+ # # FROM colors c, JSONB_ARRAY_ELEMENTS(colored_things) AS colorvalues(colorvalue)
1046
+ # # WHERE (colorvalue->>'color' = 'red')
902
1047
  def from(value, subquery_name = nil)
903
1048
  spawn.from!(value, subquery_name)
904
1049
  end
@@ -994,7 +1139,7 @@ module ActiveRecord
994
1139
  # Topic.optimizer_hints("SeqScan(topics)", "Parallel(topics 8)")
995
1140
  # # SELECT /*+ SeqScan(topics) Parallel(topics 8) */ "topics".* FROM "topics"
996
1141
  def optimizer_hints(*args)
997
- check_if_method_has_arguments!(:optimizer_hints, args)
1142
+ check_if_method_has_arguments!(__callee__, args)
998
1143
  spawn.optimizer_hints!(*args)
999
1144
  end
1000
1145
 
@@ -1036,7 +1181,7 @@ module ActiveRecord
1036
1181
  #
1037
1182
  # The SQL block comment delimiters, "/*" and "*/", will be added automatically.
1038
1183
  def annotate(*args)
1039
- check_if_method_has_arguments!(:annotate, args)
1184
+ check_if_method_has_arguments!(__callee__, args)
1040
1185
  spawn.annotate!(*args)
1041
1186
  end
1042
1187
 
@@ -1054,6 +1199,47 @@ module ActiveRecord
1054
1199
  self
1055
1200
  end
1056
1201
 
1202
+ # Excludes the specified record (or collection of records) from the resulting
1203
+ # relation. For example:
1204
+ #
1205
+ # Post.excluding(post)
1206
+ # # SELECT "posts".* FROM "posts" WHERE "posts"."id" != 1
1207
+ #
1208
+ # Post.excluding(post_one, post_two)
1209
+ # # SELECT "posts".* FROM "posts" WHERE "posts"."id" NOT IN (1, 2)
1210
+ #
1211
+ # This can also be called on associations. As with the above example, either
1212
+ # a single record of collection thereof may be specified:
1213
+ #
1214
+ # post = Post.find(1)
1215
+ # comment = Comment.find(2)
1216
+ # post.comments.excluding(comment)
1217
+ # # SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1 AND "comments"."id" != 2
1218
+ #
1219
+ # This is short-hand for <tt>.where.not(id: post.id)</tt> and <tt>.where.not(id: [post_one.id, post_two.id])</tt>.
1220
+ #
1221
+ # An <tt>ArgumentError</tt> will be raised if either no records are
1222
+ # specified, or if any of the records in the collection (if a collection
1223
+ # is passed in) are not instances of the same model that the relation is
1224
+ # scoping.
1225
+ def excluding(*records)
1226
+ records.flatten!(1)
1227
+ records.compact!
1228
+
1229
+ unless records.all?(klass)
1230
+ raise ArgumentError, "You must only pass a single or collection of #{klass.name} objects to ##{__callee__}."
1231
+ end
1232
+
1233
+ spawn.excluding!(records)
1234
+ end
1235
+ alias :without :excluding
1236
+
1237
+ def excluding!(records) # :nodoc:
1238
+ predicates = [ predicate_builder[primary_key, records].invert ]
1239
+ self.where_clause += Relation::WhereClause.new(predicates)
1240
+ self
1241
+ end
1242
+
1057
1243
  # Returns the Arel object associated with the relation.
1058
1244
  def arel(aliases = nil) # :nodoc:
1059
1245
  @arel ||= build_arel(aliases)
@@ -1109,11 +1295,9 @@ module ActiveRecord
1109
1295
  nil
1110
1296
  end
1111
1297
 
1112
- def each_join_dependencies(join_dependencies = build_join_dependencies)
1298
+ def each_join_dependencies(join_dependencies = build_join_dependencies, &block)
1113
1299
  join_dependencies.each do |join_dependency|
1114
- join_dependency.each do |join|
1115
- yield join
1116
- end
1300
+ join_dependency.each(&block)
1117
1301
  end
1118
1302
  end
1119
1303
 
@@ -1133,7 +1317,7 @@ module ActiveRecord
1133
1317
  raise ImmutableRelation if defined?(@arel) && @arel
1134
1318
  end
1135
1319
 
1136
- def build_arel(aliases)
1320
+ def build_arel(aliases = nil)
1137
1321
  arel = Arel::SelectManager.new(table)
1138
1322
 
1139
1323
  build_joins(arel.join_sources, aliases)
@@ -1157,8 +1341,8 @@ module ActiveRecord
1157
1341
  annotates = annotates.uniq if annotates.size > 1
1158
1342
  unless annotates == annotate_values
1159
1343
  ActiveSupport::Deprecation.warn(<<-MSG.squish)
1160
- Duplicated query annotations are no longer shown in queries in Rails 6.2.
1161
- To migrate to Rails 6.2's behavior, use `uniq!(:annotate)` to deduplicate query annotations
1344
+ Duplicated query annotations are no longer shown in queries in Rails 7.0.
1345
+ To migrate to Rails 7.0's behavior, use `uniq!(:annotate)` to deduplicate query annotations
1162
1346
  (`#{klass.name&.tableize || klass.table_name}.uniq!(:annotate)`).
1163
1347
  MSG
1164
1348
  annotates = annotate_values
@@ -1170,8 +1354,7 @@ module ActiveRecord
1170
1354
  end
1171
1355
 
1172
1356
  def build_cast_value(name, value)
1173
- cast_value = ActiveModel::Attribute.with_cast_value(name, value, Type.default_value)
1174
- Arel::Nodes::BindParam.new(cast_value)
1357
+ ActiveModel::Attribute.with_cast_value(name, value, Type.default_value)
1175
1358
  end
1176
1359
 
1177
1360
  def build_from
@@ -1282,7 +1465,7 @@ module ActiveRecord
1282
1465
  def build_select(arel)
1283
1466
  if select_values.any?
1284
1467
  arel.project(*arel_columns(select_values))
1285
- elsif klass.ignored_columns.any?
1468
+ elsif klass.ignored_columns.any? || klass.enumerate_columns_in_select_statements
1286
1469
  arel.project(*klass.column_names.map { |field| table[field] })
1287
1470
  else
1288
1471
  arel.project(table[Arel.star])
@@ -1428,7 +1611,14 @@ module ActiveRecord
1428
1611
  end
1429
1612
 
1430
1613
  def column_references(order_args)
1431
- references = order_args.grep(String)
1614
+ references = order_args.flat_map do |arg|
1615
+ case arg
1616
+ when String, Symbol
1617
+ arg
1618
+ when Hash
1619
+ arg.keys
1620
+ end
1621
+ end
1432
1622
  references.map! { |arg| arg =~ /^\W?(\w+)\W?\./ && $1 }.compact!
1433
1623
  references
1434
1624
  end
@@ -1476,11 +1666,11 @@ module ActiveRecord
1476
1666
  # Post.references() # raises an error
1477
1667
  # Post.references([]) # does not raise an error
1478
1668
  #
1479
- # This particular method should be called with a method_name and the args
1669
+ # This particular method should be called with a method_name (__callee__) and the args
1480
1670
  # passed into that method as an input. For example:
1481
1671
  #
1482
1672
  # def references(*args)
1483
- # check_if_method_has_arguments!("references", args)
1673
+ # check_if_method_has_arguments!(__callee__, args)
1484
1674
  # ...
1485
1675
  # end
1486
1676
  def check_if_method_has_arguments!(method_name, args, message = nil)
@@ -1512,11 +1702,4 @@ module ActiveRecord
1512
1702
  end
1513
1703
  end
1514
1704
  end
1515
-
1516
- class Relation # :nodoc:
1517
- # No-op WhereClauseFactory to work Mashal.load(File.read("legacy_relation.dump")).
1518
- # TODO: Remove the class once Rails 6.1 has released.
1519
- class WhereClauseFactory # :nodoc:
1520
- end
1521
- end
1522
1705
  end