activerecord 6.1.4 → 7.0.0.rc1

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 (234) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1049 -977
  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 +33 -17
  8. data/lib/active_record/associations/association_scope.rb +1 -3
  9. data/lib/active_record/associations/belongs_to_association.rb +15 -4
  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 +10 -3
  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 +34 -27
  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 +187 -55
  25. data/lib/active_record/associations/preloader/batch.rb +48 -0
  26. data/lib/active_record/associations/preloader/branch.rb +147 -0
  27. data/lib/active_record/associations/preloader/through_association.rb +49 -13
  28. data/lib/active_record/associations/preloader.rb +39 -113
  29. data/lib/active_record/associations/singular_association.rb +8 -2
  30. data/lib/active_record/associations/through_association.rb +3 -3
  31. data/lib/active_record/associations.rb +90 -82
  32. data/lib/active_record/asynchronous_queries_tracker.rb +60 -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 +49 -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 +13 -14
  43. data/lib/active_record/attributes.rb +24 -35
  44. data/lib/active_record/autosave_association.rb +6 -21
  45. data/lib/active_record/base.rb +19 -1
  46. data/lib/active_record/callbacks.rb +2 -2
  47. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
  48. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
  49. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
  50. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +47 -561
  51. data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -17
  52. data/lib/active_record/connection_adapters/abstract/database_statements.rb +46 -22
  53. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
  54. data/lib/active_record/connection_adapters/abstract/quoting.rb +42 -72
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +34 -9
  57. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +69 -18
  58. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
  59. data/lib/active_record/connection_adapters/abstract_adapter.rb +149 -74
  60. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +97 -81
  61. data/lib/active_record/connection_adapters/column.rb +4 -0
  62. data/lib/active_record/connection_adapters/mysql/database_statements.rb +35 -23
  63. data/lib/active_record/connection_adapters/mysql/quoting.rb +35 -21
  64. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +4 -1
  65. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
  66. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  67. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -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 +50 -50
  78. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -0
  79. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
  80. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
  81. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
  82. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +27 -16
  83. data/lib/active_record/connection_adapters/postgresql_adapter.rb +205 -105
  84. data/lib/active_record/connection_adapters/schema_cache.rb +29 -4
  85. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +25 -19
  86. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +15 -16
  87. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -2
  88. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -30
  89. data/lib/active_record/connection_adapters.rb +6 -5
  90. data/lib/active_record/connection_handling.rb +47 -53
  91. data/lib/active_record/core.rb +122 -132
  92. data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
  93. data/lib/active_record/database_configurations/database_config.rb +12 -9
  94. data/lib/active_record/database_configurations/hash_config.rb +63 -5
  95. data/lib/active_record/database_configurations/url_config.rb +2 -2
  96. data/lib/active_record/database_configurations.rb +16 -32
  97. data/lib/active_record/delegated_type.rb +52 -11
  98. data/lib/active_record/destroy_association_async_job.rb +1 -1
  99. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  100. data/lib/active_record/dynamic_matchers.rb +1 -1
  101. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  102. data/lib/active_record/encryption/cipher.rb +53 -0
  103. data/lib/active_record/encryption/config.rb +44 -0
  104. data/lib/active_record/encryption/configurable.rb +61 -0
  105. data/lib/active_record/encryption/context.rb +35 -0
  106. data/lib/active_record/encryption/contexts.rb +72 -0
  107. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  108. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  109. data/lib/active_record/encryption/encryptable_record.rb +208 -0
  110. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  111. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  112. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  113. data/lib/active_record/encryption/encryptor.rb +155 -0
  114. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  115. data/lib/active_record/encryption/errors.rb +15 -0
  116. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  117. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  118. data/lib/active_record/encryption/key.rb +28 -0
  119. data/lib/active_record/encryption/key_generator.rb +42 -0
  120. data/lib/active_record/encryption/key_provider.rb +46 -0
  121. data/lib/active_record/encryption/message.rb +33 -0
  122. data/lib/active_record/encryption/message_serializer.rb +90 -0
  123. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  124. data/lib/active_record/encryption/properties.rb +76 -0
  125. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  126. data/lib/active_record/encryption/scheme.rb +99 -0
  127. data/lib/active_record/encryption.rb +55 -0
  128. data/lib/active_record/enum.rb +49 -42
  129. data/lib/active_record/errors.rb +67 -4
  130. data/lib/active_record/explain_registry.rb +11 -6
  131. data/lib/active_record/fixture_set/file.rb +15 -1
  132. data/lib/active_record/fixture_set/table_row.rb +41 -6
  133. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  134. data/lib/active_record/fixtures.rb +17 -20
  135. data/lib/active_record/future_result.rb +139 -0
  136. data/lib/active_record/gem_version.rb +4 -4
  137. data/lib/active_record/inheritance.rb +55 -17
  138. data/lib/active_record/insert_all.rb +80 -14
  139. data/lib/active_record/integration.rb +4 -3
  140. data/lib/active_record/internal_metadata.rb +3 -5
  141. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  142. data/lib/active_record/locking/optimistic.rb +10 -9
  143. data/lib/active_record/locking/pessimistic.rb +9 -3
  144. data/lib/active_record/log_subscriber.rb +14 -3
  145. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  146. data/lib/active_record/middleware/database_selector.rb +8 -3
  147. data/lib/active_record/middleware/shard_selector.rb +60 -0
  148. data/lib/active_record/migration/command_recorder.rb +4 -4
  149. data/lib/active_record/migration/compatibility.rb +83 -1
  150. data/lib/active_record/migration/join_table.rb +1 -1
  151. data/lib/active_record/migration.rb +109 -79
  152. data/lib/active_record/model_schema.rb +45 -58
  153. data/lib/active_record/nested_attributes.rb +13 -12
  154. data/lib/active_record/no_touching.rb +3 -3
  155. data/lib/active_record/null_relation.rb +2 -6
  156. data/lib/active_record/persistence.rb +219 -52
  157. data/lib/active_record/query_cache.rb +2 -2
  158. data/lib/active_record/query_logs.rb +138 -0
  159. data/lib/active_record/querying.rb +15 -5
  160. data/lib/active_record/railtie.rb +127 -17
  161. data/lib/active_record/railties/controller_runtime.rb +1 -1
  162. data/lib/active_record/railties/databases.rake +66 -129
  163. data/lib/active_record/readonly_attributes.rb +11 -0
  164. data/lib/active_record/reflection.rb +67 -50
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  166. data/lib/active_record/relation/batches.rb +3 -3
  167. data/lib/active_record/relation/calculations.rb +40 -36
  168. data/lib/active_record/relation/delegation.rb +6 -6
  169. data/lib/active_record/relation/finder_methods.rb +31 -35
  170. data/lib/active_record/relation/merger.rb +20 -13
  171. data/lib/active_record/relation/predicate_builder.rb +1 -6
  172. data/lib/active_record/relation/query_attribute.rb +5 -11
  173. data/lib/active_record/relation/query_methods.rb +235 -61
  174. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  175. data/lib/active_record/relation/spawn_methods.rb +2 -2
  176. data/lib/active_record/relation/where_clause.rb +10 -19
  177. data/lib/active_record/relation.rb +171 -84
  178. data/lib/active_record/result.rb +17 -7
  179. data/lib/active_record/runtime_registry.rb +9 -13
  180. data/lib/active_record/sanitization.rb +11 -7
  181. data/lib/active_record/schema_dumper.rb +10 -3
  182. data/lib/active_record/schema_migration.rb +0 -4
  183. data/lib/active_record/scoping/default.rb +61 -12
  184. data/lib/active_record/scoping/named.rb +3 -11
  185. data/lib/active_record/scoping.rb +64 -34
  186. data/lib/active_record/serialization.rb +1 -1
  187. data/lib/active_record/signed_id.rb +1 -1
  188. data/lib/active_record/suppressor.rb +11 -15
  189. data/lib/active_record/tasks/database_tasks.rb +116 -58
  190. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  191. data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -12
  192. data/lib/active_record/test_databases.rb +1 -1
  193. data/lib/active_record/test_fixtures.rb +4 -4
  194. data/lib/active_record/timestamp.rb +3 -4
  195. data/lib/active_record/transactions.rb +9 -14
  196. data/lib/active_record/translation.rb +2 -2
  197. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  198. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  199. data/lib/active_record/type/internal/timezone.rb +2 -2
  200. data/lib/active_record/type/serialized.rb +1 -1
  201. data/lib/active_record/type/type_map.rb +17 -20
  202. data/lib/active_record/type.rb +1 -2
  203. data/lib/active_record/validations/associated.rb +1 -1
  204. data/lib/active_record/validations/uniqueness.rb +1 -1
  205. data/lib/active_record.rb +204 -28
  206. data/lib/arel/attributes/attribute.rb +0 -8
  207. data/lib/arel/crud.rb +28 -22
  208. data/lib/arel/delete_manager.rb +18 -4
  209. data/lib/arel/filter_predications.rb +9 -0
  210. data/lib/arel/insert_manager.rb +2 -3
  211. data/lib/arel/nodes/casted.rb +1 -1
  212. data/lib/arel/nodes/delete_statement.rb +12 -13
  213. data/lib/arel/nodes/filter.rb +10 -0
  214. data/lib/arel/nodes/function.rb +1 -0
  215. data/lib/arel/nodes/insert_statement.rb +2 -2
  216. data/lib/arel/nodes/select_core.rb +2 -2
  217. data/lib/arel/nodes/select_statement.rb +2 -2
  218. data/lib/arel/nodes/update_statement.rb +8 -3
  219. data/lib/arel/nodes.rb +1 -0
  220. data/lib/arel/predications.rb +11 -3
  221. data/lib/arel/select_manager.rb +10 -4
  222. data/lib/arel/table.rb +0 -1
  223. data/lib/arel/tree_manager.rb +0 -12
  224. data/lib/arel/update_manager.rb +18 -4
  225. data/lib/arel/visitors/dot.rb +80 -90
  226. data/lib/arel/visitors/mysql.rb +8 -2
  227. data/lib/arel/visitors/postgresql.rb +0 -10
  228. data/lib/arel/visitors/to_sql.rb +58 -2
  229. data/lib/arel.rb +2 -1
  230. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  231. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  232. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  233. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  234. metadata +56 -13
@@ -51,30 +51,25 @@ module ActiveRecord
51
51
  @rewhere = rewhere
52
52
  end
53
53
 
54
- NORMAL_VALUES = Relation::VALUE_METHODS -
55
- Relation::CLAUSE_METHODS -
56
- [:includes, :preload, :joins, :left_outer_joins, :order, :reverse_order, :lock, :create_with, :reordering] # :nodoc:
57
-
58
- def normal_values
59
- NORMAL_VALUES
60
- end
54
+ NORMAL_VALUES = Relation::VALUE_METHODS - Relation::CLAUSE_METHODS -
55
+ [
56
+ :select, :includes, :preload, :joins, :left_outer_joins,
57
+ :order, :reverse_order, :lock, :create_with, :reordering
58
+ ]
61
59
 
62
60
  def merge
63
- normal_values.each do |name|
61
+ NORMAL_VALUES.each do |name|
64
62
  value = values[name]
65
63
  # The unless clause is here mostly for performance reasons (since the `send` call might be moderately
66
64
  # expensive), most of the time the value is going to be `nil` or `.blank?`, the only catch is that
67
65
  # `false.blank?` returns `true`, so there needs to be an extra check so that explicit `false` values
68
66
  # don't fall through the cracks.
69
67
  unless value.nil? || (value.blank? && false != value)
70
- if name == :select
71
- relation._select!(*value)
72
- else
73
- relation.public_send("#{name}!", *value)
74
- end
68
+ relation.public_send(:"#{name}!", *value)
75
69
  end
76
70
  end
77
71
 
72
+ merge_select_values
78
73
  merge_multi_values
79
74
  merge_single_values
80
75
  merge_clauses
@@ -86,6 +81,18 @@ module ActiveRecord
86
81
  end
87
82
 
88
83
  private
84
+ def merge_select_values
85
+ return if other.select_values.empty?
86
+
87
+ if other.klass == relation.klass
88
+ relation.select_values |= other.select_values
89
+ else
90
+ relation.select_values |= other.instance_eval do
91
+ arel_columns(select_values)
92
+ end
93
+ end
94
+ end
95
+
89
96
  def merge_preloads
90
97
  return if other.preload_values.empty? && other.includes_values.empty?
91
98
 
@@ -9,10 +9,6 @@ module ActiveRecord
9
9
  require "active_record/relation/predicate_builder/association_query_value"
10
10
  require "active_record/relation/predicate_builder/polymorphic_array_value"
11
11
 
12
- # No-op BaseHandler to work Mashal.load(File.read("legacy_relation.dump")).
13
- # TODO: Remove the constant alias once Rails 6.1 has released.
14
- BaseHandler = BasicObjectHandler
15
-
16
12
  def initialize(table)
17
13
  @table = table
18
14
  @handlers = []
@@ -69,8 +65,7 @@ module ActiveRecord
69
65
  end
70
66
 
71
67
  def build_bind_attribute(column_name, value)
72
- attr = Relation::QueryAttribute.new(column_name, value, table.type(column_name))
73
- Arel::Nodes::BindParam.new(attr)
68
+ Relation::QueryAttribute.new(column_name, value, table.type(column_name))
74
69
  end
75
70
 
76
71
  def resolve_arel_attribute(table_name, column_name, &block)
@@ -20,25 +20,19 @@ module ActiveRecord
20
20
  def nil?
21
21
  unless value_before_type_cast.is_a?(StatementCache::Substitute)
22
22
  value_before_type_cast.nil? ||
23
- type.respond_to?(:subtype, true) && value_for_database.nil?
23
+ type.respond_to?(:subtype) && serializable? && value_for_database.nil?
24
24
  end
25
- rescue ::RangeError
26
25
  end
27
26
 
28
27
  def infinite?
29
- infinity?(value_before_type_cast) || infinity?(value_for_database)
30
- rescue ::RangeError
28
+ infinity?(value_before_type_cast) || serializable? && infinity?(value_for_database)
31
29
  end
32
30
 
33
31
  def unboundable?
34
- if defined?(@_unboundable)
35
- @_unboundable
36
- else
37
- value_for_database unless value_before_type_cast.is_a?(StatementCache::Substitute)
38
- @_unboundable = nil
32
+ unless defined?(@_unboundable)
33
+ serializable? { |value| @_unboundable = value <=> 0 } && @_unboundable = nil
39
34
  end
40
- rescue ::RangeError
41
- @_unboundable = type.cast(value_before_type_cast) <=> 0
35
+ @_unboundable
42
36
  end
43
37
 
44
38
  private
@@ -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,24 @@ 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
+ values = values.map { |value| type_caster.type_cast_for_database(column, value) }
436
+ column = order_column(column.to_s) if column.is_a?(Symbol)
437
+
438
+ spawn.order!(connection.field_ordered_value(column, values))
439
+ end
440
+
365
441
  # Replaces any existing order defined on the relation with the specified order.
366
442
  #
367
443
  # User.order('email DESC').reorder('id ASC') # generated SQL has 'ORDER BY id ASC'
@@ -372,15 +448,15 @@ module ActiveRecord
372
448
  #
373
449
  # generates a query with 'ORDER BY id ASC, name ASC'.
374
450
  def reorder(*args)
375
- check_if_method_has_arguments!(:reorder, args) do
376
- sanitize_order_arguments(args) unless args.all?(&:blank?)
451
+ check_if_method_has_arguments!(__callee__, args) do
452
+ sanitize_order_arguments(args)
377
453
  end
378
454
  spawn.reorder!(*args)
379
455
  end
380
456
 
381
457
  # Same as #reorder but operates on relation in-place instead of copying.
382
458
  def reorder!(*args) # :nodoc:
383
- preprocess_order_args(args) unless args.all?(&:blank?)
459
+ preprocess_order_args(args)
384
460
  args.uniq!
385
461
  self.reordering_value = true
386
462
  self.order_values = args
@@ -425,7 +501,7 @@ module ActiveRecord
425
501
  # has_many :comments, -> { unscope(where: :trashed) }
426
502
  #
427
503
  def unscope(*args)
428
- check_if_method_has_arguments!(:unscope, args)
504
+ check_if_method_has_arguments!(__callee__, args)
429
505
  spawn.unscope!(*args)
430
506
  end
431
507
 
@@ -458,7 +534,7 @@ module ActiveRecord
458
534
  self
459
535
  end
460
536
 
461
- # Performs a joins on +args+. The given symbol(s) should match the name of
537
+ # Performs JOINs on +args+. The given symbol(s) should match the name of
462
538
  # the association(s).
463
539
  #
464
540
  # User.joins(:posts)
@@ -487,7 +563,7 @@ module ActiveRecord
487
563
  # User.joins("LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id")
488
564
  # # SELECT "users".* FROM "users" LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id
489
565
  def joins(*args)
490
- check_if_method_has_arguments!(:joins, args)
566
+ check_if_method_has_arguments!(__callee__, args)
491
567
  spawn.joins!(*args)
492
568
  end
493
569
 
@@ -496,7 +572,7 @@ module ActiveRecord
496
572
  self
497
573
  end
498
574
 
499
- # Performs a left outer joins on +args+:
575
+ # Performs LEFT OUTER JOINs on +args+:
500
576
  #
501
577
  # User.left_outer_joins(:posts)
502
578
  # => SELECT "users".* FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"
@@ -578,13 +654,13 @@ module ActiveRecord
578
654
  #
579
655
  # Fields can be symbols or strings. Values can be single values, arrays, or ranges.
580
656
  #
581
- # User.where({ name: "Joe", email: "joe@example.com" })
657
+ # User.where(name: "Joe", email: "joe@example.com")
582
658
  # # SELECT * FROM users WHERE name = 'Joe' AND email = 'joe@example.com'
583
659
  #
584
- # User.where({ name: ["Alice", "Bob"]})
660
+ # User.where(name: ["Alice", "Bob"])
585
661
  # # SELECT * FROM users WHERE name IN ('Alice', 'Bob')
586
662
  #
587
- # User.where({ created_at: (Time.now.midnight - 1.day)..Time.now.midnight })
663
+ # User.where(created_at: (Time.now.midnight - 1.day)..Time.now.midnight)
588
664
  # # SELECT * FROM users WHERE (created_at BETWEEN '2012-06-09 07:00:00.000000' AND '2012-06-10 07:00:00.000000')
589
665
  #
590
666
  # In the case of a belongs_to relationship, an association key can be used
@@ -614,8 +690,8 @@ module ActiveRecord
614
690
  #
615
691
  # For hash conditions, you can either use the table name in the key, or use a sub-hash.
616
692
  #
617
- # User.joins(:posts).where({ "posts.published" => true })
618
- # User.joins(:posts).where({ posts: { published: true } })
693
+ # User.joins(:posts).where("posts.published" => true)
694
+ # User.joins(:posts).where(posts: { published: true })
619
695
  #
620
696
  # === no argument
621
697
  #
@@ -668,6 +744,59 @@ module ActiveRecord
668
744
  scope
669
745
  end
670
746
 
747
+ # Allows you to invert an entire where clause instead of manually applying conditions.
748
+ #
749
+ # class User
750
+ # scope :active, -> { where(accepted: true, locked: false) }
751
+ # end
752
+ #
753
+ # User.where(accepted: true)
754
+ # # WHERE `accepted` = 1
755
+ #
756
+ # User.where(accepted: true).invert_where
757
+ # # WHERE `accepted` != 1
758
+ #
759
+ # User.active
760
+ # # WHERE `accepted` = 1 AND `locked` = 0
761
+ #
762
+ # User.active.invert_where
763
+ # # WHERE NOT (`accepted` = 1 AND `locked` = 0)
764
+ #
765
+ # Be careful because this inverts all conditions before +invert_where+ call.
766
+ #
767
+ # class User
768
+ # scope :active, -> { where(accepted: true, locked: false) }
769
+ # scope :inactive, -> { active.invert_where } # Do not attempt it
770
+ # end
771
+ #
772
+ # # It also inverts `where(role: 'admin')` unexpectedly.
773
+ # User.where(role: 'admin').inactive
774
+ # # WHERE NOT (`role` = 'admin' AND `accepted` = 1 AND `locked` = 0)
775
+ #
776
+ def invert_where
777
+ spawn.invert_where!
778
+ end
779
+
780
+ def invert_where! # :nodoc:
781
+ self.where_clause = where_clause.invert
782
+ self
783
+ end
784
+
785
+ # Checks whether the given relation is structurally compatible with this relation, to determine
786
+ # if it's possible to use the #and and #or methods without raising an error. Structurally
787
+ # compatible is defined as: they must be scoping the same model, and they must differ only by
788
+ # #where (if no #group has been defined) or #having (if a #group is present).
789
+ #
790
+ # Post.where("id = 1").structurally_compatible?(Post.where("author_id = 3"))
791
+ # # => true
792
+ #
793
+ # Post.joins(:comments).structurally_compatible?(Post.where("id = 1"))
794
+ # # => false
795
+ #
796
+ def structurally_compatible?(other)
797
+ structurally_incompatible_values_for(other).empty?
798
+ end
799
+
671
800
  # Returns a new relation, which is the logical intersection of this relation and the one passed
672
801
  # as an argument.
673
802
  #
@@ -886,7 +1015,7 @@ module ActiveRecord
886
1015
  self
887
1016
  end
888
1017
 
889
- # Specifies table from which the records will be fetched. For example:
1018
+ # Specifies the table from which the records will be fetched. For example:
890
1019
  #
891
1020
  # Topic.select('title').from('posts')
892
1021
  # # SELECT title FROM posts
@@ -896,9 +1025,26 @@ module ActiveRecord
896
1025
  # Topic.select('title').from(Topic.approved)
897
1026
  # # SELECT title FROM (SELECT * FROM topics WHERE approved = 't') subquery
898
1027
  #
1028
+ # Passing a second argument (string or symbol), creates the alias for the SQL from clause. Otherwise the alias "subquery" is used:
1029
+ #
899
1030
  # Topic.select('a.title').from(Topic.approved, :a)
900
1031
  # # SELECT a.title FROM (SELECT * FROM topics WHERE approved = 't') a
901
1032
  #
1033
+ # It does not add multiple arguments to the SQL from clause. The last +from+ chained is the one used:
1034
+ #
1035
+ # Topic.select('title').from(Topic.approved).from(Topic.inactive)
1036
+ # # SELECT title FROM (SELECT topics.* FROM topics WHERE topics.active = 'f') subquery
1037
+ #
1038
+ # For multiple arguments for the SQL from clause, you can pass a string with the exact elements in the SQL from list:
1039
+ #
1040
+ # color = "red"
1041
+ # Color
1042
+ # .from("colors c, JSONB_ARRAY_ELEMENTS(colored_things) AS colorvalues(colorvalue)")
1043
+ # .where("colorvalue->>'color' = ?", color)
1044
+ # .select("c.*").to_a
1045
+ # # SELECT c.*
1046
+ # # FROM colors c, JSONB_ARRAY_ELEMENTS(colored_things) AS colorvalues(colorvalue)
1047
+ # # WHERE (colorvalue->>'color' = 'red')
902
1048
  def from(value, subquery_name = nil)
903
1049
  spawn.from!(value, subquery_name)
904
1050
  end
@@ -994,7 +1140,7 @@ module ActiveRecord
994
1140
  # Topic.optimizer_hints("SeqScan(topics)", "Parallel(topics 8)")
995
1141
  # # SELECT /*+ SeqScan(topics) Parallel(topics 8) */ "topics".* FROM "topics"
996
1142
  def optimizer_hints(*args)
997
- check_if_method_has_arguments!(:optimizer_hints, args)
1143
+ check_if_method_has_arguments!(__callee__, args)
998
1144
  spawn.optimizer_hints!(*args)
999
1145
  end
1000
1146
 
@@ -1036,7 +1182,7 @@ module ActiveRecord
1036
1182
  #
1037
1183
  # The SQL block comment delimiters, "/*" and "*/", will be added automatically.
1038
1184
  def annotate(*args)
1039
- check_if_method_has_arguments!(:annotate, args)
1185
+ check_if_method_has_arguments!(__callee__, args)
1040
1186
  spawn.annotate!(*args)
1041
1187
  end
1042
1188
 
@@ -1054,6 +1200,47 @@ module ActiveRecord
1054
1200
  self
1055
1201
  end
1056
1202
 
1203
+ # Excludes the specified record (or collection of records) from the resulting
1204
+ # relation. For example:
1205
+ #
1206
+ # Post.excluding(post)
1207
+ # # SELECT "posts".* FROM "posts" WHERE "posts"."id" != 1
1208
+ #
1209
+ # Post.excluding(post_one, post_two)
1210
+ # # SELECT "posts".* FROM "posts" WHERE "posts"."id" NOT IN (1, 2)
1211
+ #
1212
+ # This can also be called on associations. As with the above example, either
1213
+ # a single record of collection thereof may be specified:
1214
+ #
1215
+ # post = Post.find(1)
1216
+ # comment = Comment.find(2)
1217
+ # post.comments.excluding(comment)
1218
+ # # SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1 AND "comments"."id" != 2
1219
+ #
1220
+ # This is short-hand for <tt>.where.not(id: post.id)</tt> and <tt>.where.not(id: [post_one.id, post_two.id])</tt>.
1221
+ #
1222
+ # An <tt>ArgumentError</tt> will be raised if either no records are
1223
+ # specified, or if any of the records in the collection (if a collection
1224
+ # is passed in) are not instances of the same model that the relation is
1225
+ # scoping.
1226
+ def excluding(*records)
1227
+ records.flatten!(1)
1228
+ records.compact!
1229
+
1230
+ unless records.all?(klass)
1231
+ raise ArgumentError, "You must only pass a single or collection of #{klass.name} objects to ##{__callee__}."
1232
+ end
1233
+
1234
+ spawn.excluding!(records)
1235
+ end
1236
+ alias :without :excluding
1237
+
1238
+ def excluding!(records) # :nodoc:
1239
+ predicates = [ predicate_builder[primary_key, records].invert ]
1240
+ self.where_clause += Relation::WhereClause.new(predicates)
1241
+ self
1242
+ end
1243
+
1057
1244
  # Returns the Arel object associated with the relation.
1058
1245
  def arel(aliases = nil) # :nodoc:
1059
1246
  @arel ||= build_arel(aliases)
@@ -1109,11 +1296,9 @@ module ActiveRecord
1109
1296
  nil
1110
1297
  end
1111
1298
 
1112
- def each_join_dependencies(join_dependencies = build_join_dependencies)
1299
+ def each_join_dependencies(join_dependencies = build_join_dependencies, &block)
1113
1300
  join_dependencies.each do |join_dependency|
1114
- join_dependency.each do |join|
1115
- yield join
1116
- end
1301
+ join_dependency.each(&block)
1117
1302
  end
1118
1303
  end
1119
1304
 
@@ -1155,14 +1340,6 @@ module ActiveRecord
1155
1340
  unless annotate_values.empty?
1156
1341
  annotates = annotate_values
1157
1342
  annotates = annotates.uniq if annotates.size > 1
1158
- unless annotates == annotate_values
1159
- 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
1162
- (`#{klass.name&.tableize || klass.table_name}.uniq!(:annotate)`).
1163
- MSG
1164
- annotates = annotate_values
1165
- end
1166
1343
  arel.comment(*annotates)
1167
1344
  end
1168
1345
 
@@ -1170,8 +1347,7 @@ module ActiveRecord
1170
1347
  end
1171
1348
 
1172
1349
  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)
1350
+ ActiveModel::Attribute.with_cast_value(name, value, Type.default_value)
1175
1351
  end
1176
1352
 
1177
1353
  def build_from
@@ -1282,7 +1458,7 @@ module ActiveRecord
1282
1458
  def build_select(arel)
1283
1459
  if select_values.any?
1284
1460
  arel.project(*arel_columns(select_values))
1285
- elsif klass.ignored_columns.any?
1461
+ elsif klass.ignored_columns.any? || klass.enumerate_columns_in_select_statements
1286
1462
  arel.project(*klass.column_names.map { |field| table[field] })
1287
1463
  else
1288
1464
  arel.project(table[Arel.star])
@@ -1423,12 +1599,17 @@ module ActiveRecord
1423
1599
  order_args.map! do |arg|
1424
1600
  klass.sanitize_sql_for_order(arg)
1425
1601
  end
1426
- order_args.flatten!
1427
- order_args.compact_blank!
1428
1602
  end
1429
1603
 
1430
1604
  def column_references(order_args)
1431
- references = order_args.grep(String)
1605
+ references = order_args.flat_map do |arg|
1606
+ case arg
1607
+ when String, Symbol
1608
+ arg
1609
+ when Hash
1610
+ arg.keys
1611
+ end
1612
+ end
1432
1613
  references.map! { |arg| arg =~ /^\W?(\w+)\W?\./ && $1 }.compact!
1433
1614
  references
1434
1615
  end
@@ -1476,19 +1657,19 @@ module ActiveRecord
1476
1657
  # Post.references() # raises an error
1477
1658
  # Post.references([]) # does not raise an error
1478
1659
  #
1479
- # This particular method should be called with a method_name and the args
1660
+ # This particular method should be called with a method_name (__callee__) and the args
1480
1661
  # passed into that method as an input. For example:
1481
1662
  #
1482
1663
  # def references(*args)
1483
- # check_if_method_has_arguments!("references", args)
1664
+ # check_if_method_has_arguments!(__callee__, args)
1484
1665
  # ...
1485
1666
  # end
1486
1667
  def check_if_method_has_arguments!(method_name, args, message = nil)
1487
1668
  if args.blank?
1488
1669
  raise ArgumentError, message || "The method .#{method_name}() must contain arguments."
1489
- elsif block_given?
1490
- yield args
1491
1670
  else
1671
+ yield args if block_given?
1672
+
1492
1673
  args.flatten!
1493
1674
  args.compact_blank!
1494
1675
  end
@@ -1512,11 +1693,4 @@ module ActiveRecord
1512
1693
  end
1513
1694
  end
1514
1695
  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
1696
  end