activerecord 7.2.4 → 8.1.4

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 (210) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1013 -878
  3. data/README.rdoc +1 -1
  4. data/lib/active_record/association_relation.rb +2 -1
  5. data/lib/active_record/associations/association.rb +69 -27
  6. data/lib/active_record/associations/association_scope.rb +1 -1
  7. data/lib/active_record/associations/belongs_to_association.rb +2 -2
  8. data/lib/active_record/associations/builder/association.rb +23 -11
  9. data/lib/active_record/associations/builder/belongs_to.rb +36 -8
  10. data/lib/active_record/associations/builder/collection_association.rb +7 -3
  11. data/lib/active_record/associations/builder/has_one.rb +1 -1
  12. data/lib/active_record/associations/builder/singular_association.rb +33 -5
  13. data/lib/active_record/associations/collection_association.rb +7 -3
  14. data/lib/active_record/associations/collection_proxy.rb +26 -4
  15. data/lib/active_record/associations/deprecation.rb +88 -0
  16. data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
  17. data/lib/active_record/associations/errors.rb +3 -0
  18. data/lib/active_record/associations/foreign_association.rb +3 -1
  19. data/lib/active_record/associations/has_many_through_association.rb +3 -2
  20. data/lib/active_record/associations/has_one_association.rb +1 -0
  21. data/lib/active_record/associations/join_dependency.rb +3 -2
  22. data/lib/active_record/associations/preloader/association.rb +3 -3
  23. data/lib/active_record/associations/preloader/batch.rb +7 -1
  24. data/lib/active_record/associations/preloader/branch.rb +1 -0
  25. data/lib/active_record/associations/singular_association.rb +8 -3
  26. data/lib/active_record/associations.rb +192 -24
  27. data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
  28. data/lib/active_record/attribute_methods/primary_key.rb +4 -8
  29. data/lib/active_record/attribute_methods/query.rb +34 -0
  30. data/lib/active_record/attribute_methods/serialization.rb +18 -5
  31. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -14
  32. data/lib/active_record/attributes.rb +3 -0
  33. data/lib/active_record/autosave_association.rb +69 -27
  34. data/lib/active_record/base.rb +1 -2
  35. data/lib/active_record/callbacks.rb +1 -2
  36. data/lib/active_record/coders/column_serializer.rb +1 -1
  37. data/lib/active_record/coders/json.rb +16 -6
  38. data/lib/active_record/coders/yaml_column.rb +2 -2
  39. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +35 -28
  40. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -4
  41. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +68 -13
  42. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +429 -93
  43. data/lib/active_record/connection_adapters/abstract/database_statements.rb +148 -74
  44. data/lib/active_record/connection_adapters/abstract/query_cache.rb +27 -5
  45. data/lib/active_record/connection_adapters/abstract/quoting.rb +16 -25
  46. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +11 -7
  47. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +32 -35
  48. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
  49. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +124 -34
  50. data/lib/active_record/connection_adapters/abstract/transaction.rb +40 -8
  51. data/lib/active_record/connection_adapters/abstract_adapter.rb +129 -63
  52. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +132 -65
  53. data/lib/active_record/connection_adapters/column.rb +14 -4
  54. data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
  55. data/lib/active_record/connection_adapters/mysql/quoting.rb +0 -8
  56. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
  57. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +41 -10
  58. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +79 -66
  59. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +89 -94
  60. data/lib/active_record/connection_adapters/mysql2_adapter.rb +2 -10
  61. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  62. data/lib/active_record/connection_adapters/postgresql/column.rb +8 -2
  63. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +79 -61
  64. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -3
  65. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
  66. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +46 -3
  67. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
  68. data/lib/active_record/connection_adapters/postgresql/quoting.rb +21 -10
  69. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
  70. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +9 -17
  71. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +14 -33
  72. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +71 -32
  73. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +156 -68
  74. data/lib/active_record/connection_adapters/postgresql_adapter.rb +78 -105
  75. data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
  76. data/lib/active_record/connection_adapters/sqlite3/column.rb +8 -2
  77. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +90 -98
  78. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +0 -8
  79. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
  80. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +27 -2
  81. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +15 -16
  82. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +104 -37
  83. data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
  84. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +41 -66
  85. data/lib/active_record/connection_adapters/trilogy_adapter.rb +15 -18
  86. data/lib/active_record/connection_adapters.rb +1 -56
  87. data/lib/active_record/connection_handling.rb +25 -2
  88. data/lib/active_record/core.rb +33 -17
  89. data/lib/active_record/counter_cache.rb +35 -8
  90. data/lib/active_record/database_configurations/database_config.rb +9 -1
  91. data/lib/active_record/database_configurations/hash_config.rb +74 -9
  92. data/lib/active_record/database_configurations/url_config.rb +15 -3
  93. data/lib/active_record/database_configurations.rb +7 -3
  94. data/lib/active_record/delegated_type.rb +1 -1
  95. data/lib/active_record/disable_joins_association_relation.rb +5 -1
  96. data/lib/active_record/dynamic_matchers.rb +54 -69
  97. data/lib/active_record/encryption/config.rb +3 -1
  98. data/lib/active_record/encryption/encryptable_record.rb +8 -8
  99. data/lib/active_record/encryption/encrypted_attribute_type.rb +11 -2
  100. data/lib/active_record/encryption/encrypted_fixtures.rb +12 -7
  101. data/lib/active_record/encryption/encryptor.rb +28 -8
  102. data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
  103. data/lib/active_record/encryption/scheme.rb +9 -2
  104. data/lib/active_record/enum.rb +33 -30
  105. data/lib/active_record/errors.rb +33 -9
  106. data/lib/active_record/explain.rb +1 -1
  107. data/lib/active_record/explain_registry.rb +51 -2
  108. data/lib/active_record/filter_attribute_handler.rb +76 -0
  109. data/lib/active_record/fixtures.rb +2 -4
  110. data/lib/active_record/future_result.rb +15 -9
  111. data/lib/active_record/gem_version.rb +2 -2
  112. data/lib/active_record/inheritance.rb +9 -4
  113. data/lib/active_record/insert_all.rb +14 -9
  114. data/lib/active_record/locking/optimistic.rb +8 -1
  115. data/lib/active_record/locking/pessimistic.rb +5 -0
  116. data/lib/active_record/log_subscriber.rb +31 -29
  117. data/lib/active_record/message_pack.rb +13 -0
  118. data/lib/active_record/middleware/shard_selector.rb +34 -17
  119. data/lib/active_record/migration/command_recorder.rb +56 -13
  120. data/lib/active_record/migration/compatibility.rb +37 -24
  121. data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
  122. data/lib/active_record/migration.rb +48 -42
  123. data/lib/active_record/model_schema.rb +40 -15
  124. data/lib/active_record/nested_attributes.rb +14 -12
  125. data/lib/active_record/persistence.rb +163 -133
  126. data/lib/active_record/query_cache.rb +22 -15
  127. data/lib/active_record/query_logs.rb +100 -52
  128. data/lib/active_record/query_logs_formatter.rb +17 -28
  129. data/lib/active_record/querying.rb +21 -9
  130. data/lib/active_record/railtie.rb +35 -30
  131. data/lib/active_record/railties/controller_runtime.rb +11 -6
  132. data/lib/active_record/railties/databases.rake +26 -38
  133. data/lib/active_record/railties/job_checkpoints.rb +15 -0
  134. data/lib/active_record/railties/job_runtime.rb +10 -11
  135. data/lib/active_record/reflection.rb +54 -22
  136. data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
  137. data/lib/active_record/relation/batches.rb +147 -73
  138. data/lib/active_record/relation/calculations.rb +60 -43
  139. data/lib/active_record/relation/delegation.rb +25 -15
  140. data/lib/active_record/relation/finder_methods.rb +73 -43
  141. data/lib/active_record/relation/merger.rb +12 -10
  142. data/lib/active_record/relation/predicate_builder/array_handler.rb +3 -1
  143. data/lib/active_record/relation/predicate_builder/association_query_value.rb +9 -9
  144. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +8 -8
  145. data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
  146. data/lib/active_record/relation/predicate_builder.rb +23 -8
  147. data/lib/active_record/relation/query_attribute.rb +3 -1
  148. data/lib/active_record/relation/query_methods.rb +200 -96
  149. data/lib/active_record/relation/spawn_methods.rb +7 -7
  150. data/lib/active_record/relation/where_clause.rb +2 -9
  151. data/lib/active_record/relation.rb +127 -87
  152. data/lib/active_record/result.rb +109 -24
  153. data/lib/active_record/runtime_registry.rb +42 -58
  154. data/lib/active_record/sanitization.rb +9 -6
  155. data/lib/active_record/schema.rb +1 -1
  156. data/lib/active_record/schema_dumper.rb +17 -10
  157. data/lib/active_record/schema_migration.rb +2 -1
  158. data/lib/active_record/scoping/named.rb +5 -2
  159. data/lib/active_record/scoping.rb +0 -1
  160. data/lib/active_record/signed_id.rb +44 -16
  161. data/lib/active_record/statement_cache.rb +24 -20
  162. data/lib/active_record/store.rb +53 -22
  163. data/lib/active_record/structured_event_subscriber.rb +85 -0
  164. data/lib/active_record/table_metadata.rb +6 -23
  165. data/lib/active_record/tasks/abstract_tasks.rb +83 -0
  166. data/lib/active_record/tasks/database_tasks.rb +85 -85
  167. data/lib/active_record/tasks/mysql_database_tasks.rb +4 -58
  168. data/lib/active_record/tasks/postgresql_database_tasks.rb +7 -40
  169. data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -28
  170. data/lib/active_record/test_databases.rb +18 -4
  171. data/lib/active_record/test_fixtures.rb +52 -6
  172. data/lib/active_record/testing/query_assertions.rb +8 -2
  173. data/lib/active_record/timestamp.rb +4 -2
  174. data/lib/active_record/token_for.rb +1 -1
  175. data/lib/active_record/transaction.rb +2 -5
  176. data/lib/active_record/transactions.rb +56 -32
  177. data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
  178. data/lib/active_record/type/internal/timezone.rb +7 -0
  179. data/lib/active_record/type/json.rb +13 -2
  180. data/lib/active_record/type/serialized.rb +16 -4
  181. data/lib/active_record/type/type_map.rb +1 -1
  182. data/lib/active_record/type_caster/connection.rb +2 -1
  183. data/lib/active_record/validations/associated.rb +1 -1
  184. data/lib/active_record/validations/uniqueness.rb +8 -8
  185. data/lib/active_record.rb +85 -50
  186. data/lib/arel/alias_predication.rb +2 -0
  187. data/lib/arel/collectors/bind.rb +2 -2
  188. data/lib/arel/collectors/sql_string.rb +1 -1
  189. data/lib/arel/collectors/substitute_binds.rb +2 -2
  190. data/lib/arel/crud.rb +6 -11
  191. data/lib/arel/nodes/binary.rb +1 -1
  192. data/lib/arel/nodes/count.rb +2 -2
  193. data/lib/arel/nodes/function.rb +4 -10
  194. data/lib/arel/nodes/named_function.rb +2 -2
  195. data/lib/arel/nodes/node.rb +2 -2
  196. data/lib/arel/nodes/sql_literal.rb +1 -1
  197. data/lib/arel/nodes.rb +0 -2
  198. data/lib/arel/predications.rb +1 -3
  199. data/lib/arel/select_manager.rb +7 -2
  200. data/lib/arel/table.rb +3 -7
  201. data/lib/arel/visitors/dot.rb +0 -3
  202. data/lib/arel/visitors/postgresql.rb +55 -0
  203. data/lib/arel/visitors/sqlite.rb +55 -8
  204. data/lib/arel/visitors/to_sql.rb +4 -22
  205. data/lib/arel.rb +3 -1
  206. data/lib/rails/generators/active_record/application_record/USAGE +1 -1
  207. metadata +15 -12
  208. data/lib/active_record/explain_subscriber.rb +0 -34
  209. data/lib/active_record/normalization.rb +0 -163
  210. data/lib/active_record/relation/record_fetch_warning.rb +0 -52
@@ -92,10 +92,11 @@ module ActiveRecord
92
92
  @scope.joins!(association)
93
93
  end
94
94
 
95
+ association_conditions = Array(reflection.association_primary_key).index_with(nil)
95
96
  if reflection.options[:class_name]
96
- self.not(association => { reflection.association_primary_key => nil })
97
+ self.not(association => association_conditions)
97
98
  else
98
- self.not(reflection.table_name => { reflection.association_primary_key => nil })
99
+ self.not(reflection.table_name => association_conditions)
99
100
  end
100
101
  end
101
102
 
@@ -124,10 +125,11 @@ module ActiveRecord
124
125
  associations.each do |association|
125
126
  reflection = scope_association_reflection(association)
126
127
  @scope.left_outer_joins!(association)
128
+ association_conditions = Array(reflection.association_primary_key).index_with(nil)
127
129
  if reflection.options[:class_name]
128
- @scope.where!(association => { reflection.association_primary_key => nil })
130
+ @scope.where!(association => association_conditions)
129
131
  else
130
- @scope.where!(reflection.table_name => { reflection.association_primary_key => nil })
132
+ @scope.where!(reflection.table_name => association_conditions)
131
133
  end
132
134
  end
133
135
 
@@ -136,9 +138,10 @@ module ActiveRecord
136
138
 
137
139
  private
138
140
  def scope_association_reflection(association)
139
- reflection = @scope.klass._reflect_on_association(association)
141
+ model = @scope.model
142
+ reflection = model._reflect_on_association(association)
140
143
  unless reflection
141
- raise ArgumentError.new("An association named `:#{association}` does not exist on the model `#{@scope.name}`.")
144
+ raise ArgumentError.new("An association named `:#{association}` does not exist on the model `#{model.name}`.")
142
145
  end
143
146
  reflection
144
147
  end
@@ -254,6 +257,10 @@ module ActiveRecord
254
257
  self
255
258
  end
256
259
 
260
+ def all # :nodoc:
261
+ spawn
262
+ end
263
+
257
264
  # Specify associations +args+ to be eager loaded using a <tt>LEFT OUTER JOIN</tt>.
258
265
  # Performs a single query joining all specified associations. For example:
259
266
  #
@@ -500,7 +507,7 @@ module ActiveRecord
500
507
  #
501
508
  # Post.with_recursive(post_and_replies: [Post.where(id: 42), Post.joins('JOIN post_and_replies ON posts.in_reply_to_id = post_and_replies.id')])
502
509
  # # => ActiveRecord::Relation
503
- # # WITH post_and_replies AS (
510
+ # # WITH RECURSIVE post_and_replies AS (
504
511
  # # (SELECT * FROM posts WHERE id = 42)
505
512
  # # UNION ALL
506
513
  # # (SELECT * FROM posts JOIN post_and_replies ON posts.in_reply_to_id = post_and_replies.id)
@@ -569,7 +576,7 @@ module ActiveRecord
569
576
  end
570
577
 
571
578
  def group!(*args) # :nodoc:
572
- self.group_values += args
579
+ self.group_values |= args
573
580
  self
574
581
  end
575
582
 
@@ -698,26 +705,47 @@ module ActiveRecord
698
705
  # # WHEN "conversations"."status" = 0 THEN 3
699
706
  # # END ASC
700
707
  #
701
- def in_order_of(column, values)
702
- klass.disallow_raw_sql!([column], permit: model.adapter_class.column_name_with_order_matcher)
708
+ # +filter+ can be set to +false+ to include all results instead of only the ones specified in +values+.
709
+ #
710
+ # Conversation.in_order_of(:status, [:archived, :active], filter: false)
711
+ # # SELECT "conversations".* FROM "conversations"
712
+ # # ORDER BY CASE
713
+ # # WHEN "conversations"."status" = 1 THEN 1
714
+ # # WHEN "conversations"."status" = 0 THEN 2
715
+ # # ELSE 3
716
+ # # END ASC
717
+ def in_order_of(column, values, filter: true)
718
+ model.disallow_raw_sql!([column], permit: model.adapter_class.column_name_with_order_matcher)
703
719
  return spawn.none! if values.empty?
704
720
 
705
721
  references = column_references([column])
706
722
  self.references_values |= references unless references.empty?
707
723
 
708
- values = values.map { |value| type_caster.type_cast_for_database(column, value) }
709
- arel_column = column.is_a?(Arel::Nodes::SqlLiteral) ? column : order_column(column.to_s)
724
+ if column.is_a?(Arel::Nodes::SqlLiteral)
725
+ arel_column = column
726
+ else
727
+ arel_column = order_column(column.to_s)
710
728
 
711
- where_clause =
712
- if values.include?(nil)
713
- arel_column.in(values.compact).or(arel_column.eq(nil))
714
- else
715
- arel_column.in(values)
729
+ unless arel_column.is_a?(Arel::Nodes::SqlLiteral)
730
+ values = cast_values_for_in_order_of(values, arel_column.type_caster)
716
731
  end
732
+ return spawn.none! if values.empty?
733
+ end
717
734
 
718
- spawn
719
- .order!(build_case_for_value_position(arel_column, values))
720
- .where!(where_clause)
735
+ scope = spawn.order!(build_case_for_value_position(arel_column, values, filter: filter))
736
+
737
+ if filter
738
+ where_clause =
739
+ if values.include?(nil)
740
+ arel_column.in(values.compact).or(arel_column.eq(nil))
741
+ else
742
+ arel_column.in(values)
743
+ end
744
+
745
+ scope = scope.where!(where_clause)
746
+ end
747
+
748
+ scope
721
749
  end
722
750
 
723
751
  # Replaces any existing order defined on the relation with the specified order.
@@ -789,7 +817,7 @@ module ActiveRecord
789
817
  end
790
818
 
791
819
  def unscope!(*args) # :nodoc:
792
- self.unscope_values += args
820
+ self.unscope_values |= args
793
821
 
794
822
  args.each do |scope|
795
823
  case scope
@@ -817,6 +845,26 @@ module ActiveRecord
817
845
  self
818
846
  end
819
847
 
848
+ def table_name_qualified_unscope_values
849
+ self.unscope_values.map do |scope|
850
+ case scope
851
+ when Hash
852
+ scope.transform_values do |target_value|
853
+ case target_value
854
+ when Array
855
+ target_value.map { |value| qualify_attribute_with_table_name(value) }
856
+ when Symbol, String
857
+ qualify_attribute_with_table_name(target_value)
858
+ else
859
+ target_value
860
+ end
861
+ end
862
+ else
863
+ scope
864
+ end
865
+ end
866
+ end
867
+
820
868
  # Performs JOINs on +args+. The given symbol(s) should match the name of
821
869
  # the association(s).
822
870
  #
@@ -1028,13 +1076,13 @@ module ActiveRecord
1028
1076
  # Allows you to change a previously set where condition for a given attribute, instead of appending to that condition.
1029
1077
  #
1030
1078
  # Post.where(trashed: true).where(trashed: false)
1031
- # # WHERE `trashed` = 1 AND `trashed` = 0
1079
+ # # WHERE `trashed` = true AND `trashed` = false
1032
1080
  #
1033
1081
  # Post.where(trashed: true).rewhere(trashed: false)
1034
- # # WHERE `trashed` = 0
1082
+ # # WHERE `trashed` = false
1035
1083
  #
1036
1084
  # Post.where(active: true).where(trashed: true).rewhere(trashed: false)
1037
- # # WHERE `active` = 1 AND `trashed` = 0
1085
+ # # WHERE `active` = true AND `trashed` = false
1038
1086
  #
1039
1087
  # This is short-hand for <tt>unscope(where: conditions.keys).where(conditions)</tt>.
1040
1088
  # Note that unlike reorder, we're only unscoping the named conditions -- not the entire where statement.
@@ -1056,16 +1104,16 @@ module ActiveRecord
1056
1104
  # end
1057
1105
  #
1058
1106
  # User.where(accepted: true)
1059
- # # WHERE `accepted` = 1
1107
+ # # WHERE `accepted` = true
1060
1108
  #
1061
1109
  # User.where(accepted: true).invert_where
1062
- # # WHERE `accepted` != 1
1110
+ # # WHERE `accepted` != true
1063
1111
  #
1064
1112
  # User.active
1065
- # # WHERE `accepted` = 1 AND `locked` = 0
1113
+ # # WHERE `accepted` = true AND `locked` = false
1066
1114
  #
1067
1115
  # User.active.invert_where
1068
- # # WHERE NOT (`accepted` = 1 AND `locked` = 0)
1116
+ # # WHERE NOT (`accepted` = true AND `locked` = false)
1069
1117
  #
1070
1118
  # Be careful because this inverts all conditions before +invert_where+ call.
1071
1119
  #
@@ -1076,7 +1124,7 @@ module ActiveRecord
1076
1124
  #
1077
1125
  # # It also inverts `where(role: 'admin')` unexpectedly.
1078
1126
  # User.where(role: 'admin').inactive
1079
- # # WHERE NOT (`role` = 'admin' AND `accepted` = 1 AND `locked` = 0)
1127
+ # # WHERE NOT (`role` = 'admin' AND `accepted` = true AND `locked` = false)
1080
1128
  #
1081
1129
  def invert_where
1082
1130
  spawn.invert_where!
@@ -1193,6 +1241,7 @@ module ActiveRecord
1193
1241
  end
1194
1242
 
1195
1243
  def limit!(value) # :nodoc:
1244
+ value = Integer(value) unless value.nil?
1196
1245
  self.limit_value = value
1197
1246
  self
1198
1247
  end
@@ -1445,7 +1494,7 @@ module ActiveRecord
1445
1494
  modules << Module.new(&block) if block
1446
1495
  modules.flatten!
1447
1496
 
1448
- self.extending_values += modules
1497
+ self.extending_values |= modules
1449
1498
  extend(*extending_values) if extending_values.any?
1450
1499
 
1451
1500
  self
@@ -1513,7 +1562,7 @@ module ActiveRecord
1513
1562
 
1514
1563
  # Like #annotate, but modifies relation in place.
1515
1564
  def annotate!(*args) # :nodoc:
1516
- self.annotate_values += args
1565
+ self.annotate_values |= args
1517
1566
  self
1518
1567
  end
1519
1568
 
@@ -1556,8 +1605,8 @@ module ActiveRecord
1556
1605
  records.flatten!(1)
1557
1606
  records.compact!
1558
1607
 
1559
- unless records.all?(klass) && relations.all? { |relation| relation.klass == klass }
1560
- raise ArgumentError, "You must only pass a single or collection of #{klass.name} objects to ##{__callee__}."
1608
+ unless records.all?(model) && relations.all? { |relation| relation.model == model }
1609
+ raise ArgumentError, "You must only pass a single or collection of #{model.name} objects to ##{__callee__}."
1561
1610
  end
1562
1611
 
1563
1612
  spawn.excluding!(records + relations.flat_map(&:ids))
@@ -1565,19 +1614,19 @@ module ActiveRecord
1565
1614
  alias :without :excluding
1566
1615
 
1567
1616
  def excluding!(records) # :nodoc:
1568
- predicates = [ predicate_builder[primary_key, records].invert ]
1569
- self.where_clause += Relation::WhereClause.new(predicates)
1617
+ ids = records.map { |record| record.is_a?(model) ? record.id : record }
1618
+ self.where_clause += build_where_clause(primary_key => ids).invert
1570
1619
  self
1571
1620
  end
1572
1621
 
1573
1622
  # Returns the Arel object associated with the relation.
1574
1623
  def arel(aliases = nil) # :nodoc:
1575
- @arel ||= with_connection { |c| build_arel(c, aliases) }
1624
+ @arel ||= build_arel(aliases)
1576
1625
  end
1577
1626
 
1578
1627
  def construct_join_dependency(associations, join_type) # :nodoc:
1579
1628
  ActiveRecord::Associations::JoinDependency.new(
1580
- klass, table, associations, join_type
1629
+ model, table, associations, join_type
1581
1630
  )
1582
1631
  end
1583
1632
 
@@ -1606,15 +1655,15 @@ module ActiveRecord
1606
1655
  elsif opts.include?("?")
1607
1656
  parts = [build_bound_sql_literal(opts, rest)]
1608
1657
  else
1609
- parts = [klass.sanitize_sql(rest.empty? ? opts : [opts, *rest])]
1658
+ parts = [Arel.sql(model.sanitize_sql([opts, *rest]))]
1610
1659
  end
1611
1660
  when Hash
1612
1661
  opts = opts.transform_keys do |key|
1613
1662
  if key.is_a?(Array)
1614
- key.map { |k| klass.attribute_aliases[k.to_s] || k.to_s }
1663
+ key.map { |k| model.attribute_aliases[k.to_s] || k.to_s }
1615
1664
  else
1616
1665
  key = key.to_s
1617
- klass.attribute_aliases[key] || key
1666
+ model.attribute_aliases[key] || key
1618
1667
  end
1619
1668
  end
1620
1669
  references = PredicateBuilder.references(opts)
@@ -1633,21 +1682,16 @@ module ActiveRecord
1633
1682
  end
1634
1683
  alias :build_having_clause :build_where_clause
1635
1684
 
1636
- def async!
1685
+ def async! # :nodoc:
1637
1686
  @async = true
1638
1687
  self
1639
1688
  end
1640
1689
 
1641
- protected
1642
- def arel_columns(columns)
1690
+ def arel_columns(columns) # :nodoc:
1643
1691
  columns.flat_map do |field|
1644
1692
  case field
1645
- when Symbol
1646
- arel_column(field.to_s) do |attr_name|
1647
- adapter_class.quote_table_name(attr_name)
1648
- end
1649
- when String
1650
- arel_column(field, &:itself)
1693
+ when Symbol, String
1694
+ arel_column(field)
1651
1695
  when Proc
1652
1696
  field.call
1653
1697
  when Hash
@@ -1731,32 +1775,27 @@ module ActiveRecord
1731
1775
  raise UnmodifiableRelation if @loaded || @arel
1732
1776
  end
1733
1777
 
1734
- def build_arel(connection, aliases = nil)
1778
+ def build_arel(aliases)
1735
1779
  arel = Arel::SelectManager.new(table)
1736
1780
 
1737
1781
  build_joins(arel.join_sources, aliases)
1738
1782
 
1739
1783
  arel.where(where_clause.ast) unless where_clause.empty?
1740
1784
  arel.having(having_clause.ast) unless having_clause.empty?
1741
- arel.take(build_cast_value("LIMIT", connection.sanitize_limit(limit_value))) if limit_value
1785
+ arel.take(build_cast_value("LIMIT", limit_value)) if limit_value
1742
1786
  arel.skip(build_cast_value("OFFSET", offset_value.to_i)) if offset_value
1743
- arel.group(*arel_columns(group_values.uniq)) unless group_values.empty?
1787
+ arel.group(*arel_columns(group_values)) unless group_values.empty?
1744
1788
 
1745
1789
  build_order(arel)
1746
1790
  build_with(arel)
1747
1791
  build_select(arel)
1748
1792
 
1749
1793
  arel.optimizer_hints(*optimizer_hints_values) unless optimizer_hints_values.empty?
1794
+ arel.comment(*annotate_values) unless annotate_values.empty?
1750
1795
  arel.distinct(distinct_value)
1751
1796
  arel.from(build_from) unless from_clause.empty?
1752
1797
  arel.lock(lock_value) if lock_value
1753
1798
 
1754
- unless annotate_values.empty?
1755
- annotates = annotate_values
1756
- annotates = annotates.uniq if annotates.size > 1
1757
- arel.comment(*annotates)
1758
- end
1759
-
1760
1799
  arel
1761
1800
  end
1762
1801
 
@@ -1830,7 +1869,7 @@ module ActiveRecord
1830
1869
 
1831
1870
  joins = joins_values.dup
1832
1871
  if joins.last.is_a?(ActiveRecord::Associations::JoinDependency)
1833
- stashed_eager_load = joins.pop if joins.last.base_klass == klass
1872
+ stashed_eager_load = joins.pop if joins.last.base_klass == model
1834
1873
  end
1835
1874
 
1836
1875
  joins.each_with_index do |join, i|
@@ -1887,8 +1926,8 @@ module ActiveRecord
1887
1926
  def build_select(arel)
1888
1927
  if select_values.any?
1889
1928
  arel.project(*arel_columns(select_values))
1890
- elsif klass.ignored_columns.any? || klass.enumerate_columns_in_select_statements
1891
- arel.project(*klass.column_names.map { |field| table[field] })
1929
+ elsif model.ignored_columns.any? || model.enumerate_columns_in_select_statements || model.only_columns.any?
1930
+ arel.project(*model.column_names.map { |field| table[field] })
1892
1931
  else
1893
1932
  arel.project(table[Arel.star])
1894
1933
  end
@@ -1912,7 +1951,8 @@ module ActiveRecord
1912
1951
 
1913
1952
  def build_with_expression_from_value(value, nested = false)
1914
1953
  case value
1915
- when Arel::Nodes::SqlLiteral then Arel::Nodes::Grouping.new(value)
1954
+ when Arel::Nodes::SqlLiteral, Arel::Nodes::BoundSqlLiteral
1955
+ Arel::Nodes::Grouping.new(value)
1916
1956
  when ActiveRecord::Relation
1917
1957
  if nested
1918
1958
  value.arel.ast
@@ -1939,37 +1979,76 @@ module ActiveRecord
1939
1979
  with_table = Arel::Table.new(name)
1940
1980
 
1941
1981
  table.join(with_table, kind).on(
1942
- with_table[klass.model_name.to_s.foreign_key].eq(table[klass.primary_key])
1982
+ with_table[model.model_name.to_s.foreign_key].eq(table[model.primary_key])
1943
1983
  ).join_sources.first
1944
1984
  end
1945
1985
 
1986
+ def arel_columns_from_hash(fields)
1987
+ fields.flat_map do |table_name, columns|
1988
+ table_name = table_name.name if table_name.is_a?(Symbol)
1989
+ case columns
1990
+ when Symbol, String
1991
+ arel_column_with_table(table_name, columns)
1992
+ when Array
1993
+ columns.map do |column|
1994
+ arel_column_with_table(table_name, column)
1995
+ end
1996
+ else
1997
+ raise TypeError, "Expected Symbol, String or Array, got: #{columns.class}"
1998
+ end
1999
+ end
2000
+ end
2001
+
2002
+ def arel_column_with_table(table_name, column_name)
2003
+ self.references_values |= [Arel.sql(table_name, retryable: true)]
2004
+
2005
+ if column_name.is_a?(Symbol) || !column_name.match?(/\W/)
2006
+ predicate_builder.resolve_arel_attribute(table_name, column_name) do
2007
+ lookup_table_klass_from_join_dependencies(table_name)
2008
+ end
2009
+ else
2010
+ Arel.sql("#{model.adapter_class.quote_table_name(table_name)}.#{column_name}")
2011
+ end
2012
+ end
2013
+
1946
2014
  def arel_column(field)
1947
- field = klass.attribute_aliases[field] || field
2015
+ field = field.name if is_symbol = field.is_a?(Symbol)
2016
+
2017
+ field = model.attribute_aliases[field] || field
1948
2018
  from = from_clause.name || from_clause.value
1949
2019
 
1950
- if klass.columns_hash.key?(field) && (!from || table_name_matches?(from))
2020
+ if model.columns_hash.key?(field) && (!from || table_name_matches?(from))
1951
2021
  table[field]
1952
2022
  elsif /\A(?<table>(?:\w+\.)?\w+)\.(?<column>\w+)\z/ =~ field
1953
- self.references_values |= [Arel.sql(table, retryable: true)]
1954
- predicate_builder.resolve_arel_attribute(table, column) do
1955
- lookup_table_klass_from_join_dependencies(table)
1956
- end
1957
- else
2023
+ arel_column_with_table(table, column)
2024
+ elsif block_given?
1958
2025
  yield field
2026
+ elsif Arel.arel_node?(field)
2027
+ field
2028
+ elsif is_symbol
2029
+ Arel.sql(model.adapter_class.quote_table_name(field), retryable: true)
2030
+ else
2031
+ Arel.sql(field)
1959
2032
  end
1960
2033
  end
1961
2034
 
1962
2035
  def table_name_matches?(from)
1963
2036
  table_name = Regexp.escape(table.name)
1964
- quoted_table_name = Regexp.escape(adapter_class.quote_table_name(table.name))
2037
+ quoted_table_name = Regexp.escape(model.adapter_class.quote_table_name(table.name))
1965
2038
  /(?:\A|(?<!FROM)\s)(?:\b#{table_name}\b|#{quoted_table_name})(?!\.)/i.match?(from.to_s)
1966
2039
  end
1967
2040
 
1968
2041
  def reverse_sql_order(order_query)
1969
2042
  if order_query.empty?
1970
- return [table[primary_key].desc] if primary_key
1971
- raise IrreversibleOrderError,
1972
- "Relation has no current order and table has no primary key to be used as default order"
2043
+ if !_reverse_order_columns.empty?
2044
+ return _reverse_order_columns.map { |column| table[column].desc }
2045
+ end
2046
+
2047
+ raise IrreversibleOrderError, <<~MSG.squish
2048
+ Relation has no order values, and #{model} has no order columns to use as a default.
2049
+ Set at least one of `implicit_order_column`, or `primary_key` on the model when no
2050
+ `order `is specified on the relation.
2051
+ MSG
1973
2052
  end
1974
2053
 
1975
2054
  order_query.flat_map do |o|
@@ -1994,6 +2073,13 @@ module ActiveRecord
1994
2073
  end
1995
2074
  end
1996
2075
 
2076
+ def _reverse_order_columns
2077
+ roc = []
2078
+ roc << model.implicit_order_column if model.implicit_order_column
2079
+ roc << model.primary_key if model.primary_key
2080
+ roc.flatten.uniq.compact
2081
+ end
2082
+
1997
2083
  def does_not_support_reverse?(order)
1998
2084
  # Account for String subclasses like Arel::Nodes::SqlLiteral that
1999
2085
  # override methods like #count.
@@ -2032,7 +2118,7 @@ module ActiveRecord
2032
2118
  end
2033
2119
 
2034
2120
  def preprocess_order_args(order_args)
2035
- @klass.disallow_raw_sql!(
2121
+ model.disallow_raw_sql!(
2036
2122
  flattened_args(order_args),
2037
2123
  permit: model.adapter_class.column_name_with_order_matcher
2038
2124
  )
@@ -2070,7 +2156,7 @@ module ActiveRecord
2070
2156
 
2071
2157
  def sanitize_order_arguments(order_args)
2072
2158
  order_args.map! do |arg|
2073
- klass.sanitize_sql_for_order(arg)
2159
+ model.sanitize_sql_for_order(arg)
2074
2160
  end
2075
2161
  end
2076
2162
 
@@ -2108,17 +2194,18 @@ module ActiveRecord
2108
2194
  if attr_name == "count" && !group_values.empty?
2109
2195
  table[attr_name]
2110
2196
  else
2111
- Arel.sql(adapter_class.quote_table_name(attr_name), retryable: true)
2197
+ Arel.sql(model.adapter_class.quote_table_name(attr_name), retryable: true)
2112
2198
  end
2113
2199
  end
2114
2200
  end
2115
2201
 
2116
- def build_case_for_value_position(column, values)
2202
+ def build_case_for_value_position(column, values, filter: true)
2117
2203
  node = Arel::Nodes::Case.new
2118
2204
  values.each.with_index(1) do |value, order|
2119
2205
  node.when(column.eq(value)).then(order)
2120
2206
  end
2121
2207
 
2208
+ node = node.else(values.length + 1) unless filter
2122
2209
  Arel::Nodes::Ascending.new(node)
2123
2210
  end
2124
2211
 
@@ -2146,6 +2233,10 @@ module ActiveRecord
2146
2233
  end
2147
2234
  end
2148
2235
 
2236
+ def qualify_attribute_with_table_name(attr)
2237
+ attr.to_s.include?(".") ? attr : predicate_builder.resolve_arel_attribute(table_name, attr)
2238
+ end
2239
+
2149
2240
  # Checks to make sure that the arguments are not blank. Note that if some
2150
2241
  # blank-like object were initially passed into the query method, then this
2151
2242
  # method will not raise an error.
@@ -2176,34 +2267,47 @@ module ActiveRecord
2176
2267
  def process_select_args(fields)
2177
2268
  fields.flat_map do |field|
2178
2269
  if field.is_a?(Hash)
2179
- arel_columns_from_hash(field)
2270
+ arel_column_aliases_from_hash(field)
2180
2271
  else
2181
2272
  field
2182
2273
  end
2183
2274
  end
2184
2275
  end
2185
2276
 
2186
- def arel_columns_from_hash(fields)
2277
+ def cast_values_for_in_order_of(values, type_caster)
2278
+ bad_value = Symbol # use some object that can not be a valid value
2279
+
2280
+ values = values.map do |value|
2281
+ serialized = type_caster.serialize(value) if type_caster.serializable?(value)
2282
+
2283
+ if value.nil?
2284
+ nil
2285
+ elsif !serialized.nil?
2286
+ serialized
2287
+ else
2288
+ bad_value
2289
+ end
2290
+ end
2291
+
2292
+ values.without(bad_value)
2293
+ end
2294
+
2295
+ def arel_column_aliases_from_hash(fields)
2187
2296
  fields.flat_map do |key, columns_aliases|
2297
+ table_name = key.is_a?(Symbol) ? key.name : key
2188
2298
  case columns_aliases
2189
2299
  when Hash
2190
2300
  columns_aliases.map do |column, column_alias|
2191
- if values[:joins]&.include?(key)
2192
- references = PredicateBuilder.references({ key.to_s => fields[key] })
2193
- self.references_values |= references unless references.empty?
2194
- end
2195
- arel_column("#{key}.#{column}") do
2196
- predicate_builder.resolve_arel_attribute(key.to_s, column)
2197
- end.as(column_alias.to_s)
2301
+ arel_column_with_table(table_name, column)
2302
+ .as(model.adapter_class.quote_column_name(column_alias.to_s))
2198
2303
  end
2199
2304
  when Array
2200
2305
  columns_aliases.map do |column|
2201
- arel_column("#{key}.#{column}", &:itself)
2306
+ arel_column_with_table(table_name, column)
2202
2307
  end
2203
2308
  when String, Symbol
2204
- arel_column(key.to_s) do
2205
- predicate_builder.resolve_arel_attribute(klass.table_name, key.to_s)
2206
- end.as(columns_aliases.to_s)
2309
+ arel_column(key)
2310
+ .as(model.adapter_class.quote_column_name(columns_aliases.to_s))
2207
2311
  end
2208
2312
  end
2209
2313
  end
@@ -2224,11 +2328,11 @@ module ActiveRecord
2224
2328
  values = other.values
2225
2329
  STRUCTURAL_VALUE_METHODS.reject do |method|
2226
2330
  v1, v2 = @values[method], values[method]
2227
- if v1.is_a?(Array)
2228
- next true unless v2.is_a?(Array)
2229
- v1 = v1.uniq
2230
- v2 = v2.uniq
2231
- end
2331
+
2332
+ # `and`/`or` are focused to combine where-like clauses, so it relaxes
2333
+ # the difference when other's multi values are uninitialized.
2334
+ next true if v1.is_a?(Array) && v2.nil?
2335
+
2232
2336
  v1 == v2
2233
2337
  end
2234
2338
  end
@@ -7,7 +7,7 @@ require "active_record/relation/merger"
7
7
  module ActiveRecord
8
8
  module SpawnMethods
9
9
  def spawn # :nodoc:
10
- already_in_scope?(klass.scope_registry) ? klass.all : clone
10
+ already_in_scope?(model.scope_registry) ? model.all : clone
11
11
  end
12
12
 
13
13
  # Merges in the conditions from <tt>other</tt>, if <tt>other</tt> is an ActiveRecord::Relation.
@@ -52,18 +52,18 @@ module ActiveRecord
52
52
  end
53
53
  end
54
54
 
55
- # Removes from the query the condition(s) specified in +skips+.
55
+ # Removes the condition(s) specified in +skips+ from the query.
56
56
  #
57
- # Post.order('id asc').except(:order) # discards the order condition
58
- # Post.where('id > 10').order('id asc').except(:where) # discards the where condition but keeps the order
57
+ # Post.order('id asc').except(:order) # removes the order condition
58
+ # Post.where('id > 10').order('id asc').except(:where) # removes the where condition but keeps the order
59
59
  def except(*skips)
60
60
  relation_with values.except(*skips)
61
61
  end
62
62
 
63
- # Removes any condition from the query other than the one(s) specified in +onlies+.
63
+ # Keeps only the condition(s) specified in +onlies+ in the query, removing all others.
64
64
  #
65
- # Post.order('id asc').only(:where) # discards the order condition
66
- # Post.order('id asc').only(:where, :order) # uses the specified order
65
+ # Post.order('id asc').only(:where) # keeps only the where condition, removes the order
66
+ # Post.order('id asc').only(:where, :order) # keeps only the where and order conditions
67
67
  def only(*onlies)
68
68
  relation_with values.slice(*onlies)
69
69
  end
@@ -182,7 +182,7 @@ module ActiveRecord
182
182
  non_attrs = columns.extract! { |node| node.is_a?(Arel::Predications) }
183
183
 
184
184
  predicates.reject do |node|
185
- if !non_attrs.empty? && node.equality? && node.left.is_a?(Arel::Predications)
185
+ if !non_attrs.empty? && equality_node?(node) && node.left.is_a?(Arel::Predications)
186
186
  non_attrs.include?(node.left)
187
187
  end || Arel.fetch_attribute(node) do |attr|
188
188
  attrs.include?(attr) || columns.include?(attr.name.to_s)
@@ -194,7 +194,7 @@ module ActiveRecord
194
194
  non_empty_predicates.map do |node|
195
195
  case node
196
196
  when Arel::Nodes::SqlLiteral, ::String
197
- wrap_sql_literal(node)
197
+ Arel::Nodes::Grouping.new(node)
198
198
  else node
199
199
  end
200
200
  end
@@ -205,13 +205,6 @@ module ActiveRecord
205
205
  predicates - ARRAY_WITH_EMPTY_STRING
206
206
  end
207
207
 
208
- def wrap_sql_literal(node)
209
- if ::String === node
210
- node = Arel.sql(node)
211
- end
212
- Arel::Nodes::Grouping.new(node)
213
- end
214
-
215
208
  def extract_node_value(node)
216
209
  if node.respond_to?(:value_before_type_cast)
217
210
  node.value_before_type_cast