activerecord 5.1.7 → 5.2.0.beta1

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 (259) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +221 -900
  3. data/README.rdoc +3 -3
  4. data/examples/performance.rb +2 -0
  5. data/examples/simple.rb +2 -0
  6. data/lib/active_record.rb +10 -3
  7. data/lib/active_record/aggregations.rb +2 -0
  8. data/lib/active_record/association_relation.rb +2 -0
  9. data/lib/active_record/associations.rb +13 -42
  10. data/lib/active_record/associations/alias_tracker.rb +17 -17
  11. data/lib/active_record/associations/association.rb +11 -22
  12. data/lib/active_record/associations/association_scope.rb +32 -44
  13. data/lib/active_record/associations/belongs_to_association.rb +6 -4
  14. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -1
  15. data/lib/active_record/associations/builder/association.rb +2 -5
  16. data/lib/active_record/associations/builder/belongs_to.rb +7 -12
  17. data/lib/active_record/associations/builder/collection_association.rb +1 -1
  18. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -1
  19. data/lib/active_record/associations/builder/has_many.rb +2 -0
  20. data/lib/active_record/associations/builder/has_one.rb +2 -0
  21. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  22. data/lib/active_record/associations/collection_association.rb +41 -33
  23. data/lib/active_record/associations/collection_proxy.rb +11 -14
  24. data/lib/active_record/associations/foreign_association.rb +2 -0
  25. data/lib/active_record/associations/has_many_association.rb +4 -2
  26. data/lib/active_record/associations/has_many_through_association.rb +4 -2
  27. data/lib/active_record/associations/has_one_association.rb +3 -1
  28. data/lib/active_record/associations/has_one_through_association.rb +3 -1
  29. data/lib/active_record/associations/join_dependency.rb +22 -40
  30. data/lib/active_record/associations/join_dependency/join_association.rb +17 -56
  31. data/lib/active_record/associations/join_dependency/join_base.rb +9 -8
  32. data/lib/active_record/associations/join_dependency/join_part.rb +2 -9
  33. data/lib/active_record/associations/preloader.rb +17 -37
  34. data/lib/active_record/associations/preloader/association.rb +42 -58
  35. data/lib/active_record/associations/preloader/through_association.rb +71 -79
  36. data/lib/active_record/associations/singular_association.rb +14 -10
  37. data/lib/active_record/associations/through_association.rb +3 -1
  38. data/lib/active_record/attribute_assignment.rb +2 -0
  39. data/lib/active_record/attribute_decorators.rb +3 -2
  40. data/lib/active_record/attribute_methods.rb +47 -7
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -0
  42. data/lib/active_record/attribute_methods/dirty.rb +25 -214
  43. data/lib/active_record/attribute_methods/primary_key.rb +7 -6
  44. data/lib/active_record/attribute_methods/query.rb +2 -0
  45. data/lib/active_record/attribute_methods/read.rb +8 -2
  46. data/lib/active_record/attribute_methods/serialization.rb +23 -0
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +6 -8
  48. data/lib/active_record/attribute_methods/write.rb +21 -9
  49. data/lib/active_record/attributes.rb +7 -6
  50. data/lib/active_record/autosave_association.rb +5 -11
  51. data/lib/active_record/base.rb +2 -0
  52. data/lib/active_record/callbacks.rb +6 -8
  53. data/lib/active_record/coders/json.rb +2 -0
  54. data/lib/active_record/coders/yaml_column.rb +2 -0
  55. data/lib/active_record/collection_cache_key.rb +10 -5
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +110 -35
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -0
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +120 -28
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +7 -2
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +14 -33
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +13 -5
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +40 -2
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +103 -63
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +45 -9
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +62 -90
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +75 -138
  69. data/lib/active_record/connection_adapters/column.rb +3 -1
  70. data/lib/active_record/connection_adapters/connection_specification.rb +17 -3
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +2 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +2 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +3 -1
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +2 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +9 -10
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +5 -3
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +7 -6
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +30 -30
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +91 -1
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +2 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -2
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +2 -0
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +6 -0
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +2 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -1
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -11
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +2 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +2 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -1
  97. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -1
  99. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +2 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +3 -5
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +4 -2
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +3 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
  107. data/lib/active_record/connection_adapters/postgresql/quoting.rb +10 -0
  108. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
  109. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +2 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +11 -7
  111. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +20 -13
  112. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +79 -65
  113. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -0
  114. data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -0
  115. data/lib/active_record/connection_adapters/postgresql_adapter.rb +47 -82
  116. data/lib/active_record/connection_adapters/schema_cache.rb +2 -0
  117. data/lib/active_record/connection_adapters/sql_type_metadata.rb +2 -0
  118. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +2 -0
  119. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +19 -2
  120. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +2 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -15
  122. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +3 -2
  123. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +71 -1
  124. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +34 -89
  125. data/lib/active_record/connection_adapters/statement_pool.rb +2 -0
  126. data/lib/active_record/connection_handling.rb +4 -2
  127. data/lib/active_record/core.rb +27 -57
  128. data/lib/active_record/counter_cache.rb +15 -12
  129. data/lib/active_record/define_callbacks.rb +5 -3
  130. data/lib/active_record/dynamic_matchers.rb +9 -9
  131. data/lib/active_record/enum.rb +15 -13
  132. data/lib/active_record/errors.rb +54 -21
  133. data/lib/active_record/explain.rb +3 -1
  134. data/lib/active_record/explain_registry.rb +2 -0
  135. data/lib/active_record/explain_subscriber.rb +2 -0
  136. data/lib/active_record/fixture_set/file.rb +2 -0
  137. data/lib/active_record/fixtures.rb +40 -24
  138. data/lib/active_record/gem_version.rb +5 -3
  139. data/lib/active_record/inheritance.rb +6 -5
  140. data/lib/active_record/integration.rb +58 -19
  141. data/lib/active_record/internal_metadata.rb +2 -0
  142. data/lib/active_record/legacy_yaml_adapter.rb +3 -1
  143. data/lib/active_record/locking/optimistic.rb +31 -20
  144. data/lib/active_record/locking/pessimistic.rb +10 -7
  145. data/lib/active_record/log_subscriber.rb +2 -0
  146. data/lib/active_record/migration.rb +47 -21
  147. data/lib/active_record/migration/command_recorder.rb +11 -9
  148. data/lib/active_record/migration/compatibility.rb +20 -2
  149. data/lib/active_record/migration/join_table.rb +2 -0
  150. data/lib/active_record/model_schema.rb +29 -38
  151. data/lib/active_record/nested_attributes.rb +18 -6
  152. data/lib/active_record/no_touching.rb +3 -1
  153. data/lib/active_record/null_relation.rb +2 -0
  154. data/lib/active_record/persistence.rb +184 -40
  155. data/lib/active_record/query_cache.rb +17 -12
  156. data/lib/active_record/querying.rb +3 -1
  157. data/lib/active_record/railtie.rb +54 -1
  158. data/lib/active_record/railties/console_sandbox.rb +2 -0
  159. data/lib/active_record/railties/controller_runtime.rb +2 -0
  160. data/lib/active_record/railties/databases.rake +41 -28
  161. data/lib/active_record/readonly_attributes.rb +3 -2
  162. data/lib/active_record/reflection.rb +100 -182
  163. data/lib/active_record/relation.rb +61 -193
  164. data/lib/active_record/relation/batches.rb +20 -5
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
  166. data/lib/active_record/relation/calculations.rb +40 -23
  167. data/lib/active_record/relation/delegation.rb +10 -27
  168. data/lib/active_record/relation/finder_methods.rb +53 -49
  169. data/lib/active_record/relation/from_clause.rb +2 -8
  170. data/lib/active_record/relation/merger.rb +22 -19
  171. data/lib/active_record/relation/predicate_builder.rb +42 -79
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -7
  173. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  174. data/lib/active_record/relation/predicate_builder/base_handler.rb +2 -2
  175. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +12 -1
  176. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +54 -0
  177. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -6
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
  179. data/lib/active_record/relation/query_attribute.rb +9 -2
  180. data/lib/active_record/relation/query_methods.rb +80 -69
  181. data/lib/active_record/relation/record_fetch_warning.rb +2 -0
  182. data/lib/active_record/relation/spawn_methods.rb +2 -0
  183. data/lib/active_record/relation/where_clause.rb +50 -67
  184. data/lib/active_record/relation/where_clause_factory.rb +4 -46
  185. data/lib/active_record/result.rb +2 -0
  186. data/lib/active_record/runtime_registry.rb +2 -0
  187. data/lib/active_record/sanitization.rb +15 -9
  188. data/lib/active_record/schema.rb +3 -1
  189. data/lib/active_record/schema_dumper.rb +24 -23
  190. data/lib/active_record/schema_migration.rb +2 -0
  191. data/lib/active_record/scoping.rb +9 -8
  192. data/lib/active_record/scoping/default.rb +6 -7
  193. data/lib/active_record/scoping/named.rb +15 -7
  194. data/lib/active_record/secure_token.rb +2 -0
  195. data/lib/active_record/serialization.rb +2 -0
  196. data/lib/active_record/statement_cache.rb +22 -12
  197. data/lib/active_record/store.rb +2 -0
  198. data/lib/active_record/suppressor.rb +2 -0
  199. data/lib/active_record/table_metadata.rb +3 -1
  200. data/lib/active_record/tasks/database_tasks.rb +23 -12
  201. data/lib/active_record/tasks/mysql_database_tasks.rb +9 -48
  202. data/lib/active_record/tasks/postgresql_database_tasks.rb +10 -2
  203. data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -3
  204. data/lib/active_record/timestamp.rb +5 -12
  205. data/lib/active_record/touch_later.rb +2 -0
  206. data/lib/active_record/transactions.rb +9 -7
  207. data/lib/active_record/translation.rb +2 -0
  208. data/lib/active_record/type.rb +4 -1
  209. data/lib/active_record/type/adapter_specific_registry.rb +2 -0
  210. data/lib/active_record/type/date.rb +2 -0
  211. data/lib/active_record/type/date_time.rb +2 -0
  212. data/lib/active_record/type/decimal_without_scale.rb +2 -0
  213. data/lib/active_record/type/hash_lookup_type_map.rb +2 -0
  214. data/lib/active_record/type/internal/timezone.rb +2 -0
  215. data/lib/active_record/type/json.rb +30 -0
  216. data/lib/active_record/type/serialized.rb +2 -4
  217. data/lib/active_record/type/text.rb +2 -0
  218. data/lib/active_record/type/time.rb +2 -0
  219. data/lib/active_record/type/type_map.rb +2 -0
  220. data/lib/active_record/type/unsigned_integer.rb +2 -0
  221. data/lib/active_record/type_caster.rb +2 -0
  222. data/lib/active_record/type_caster/connection.rb +2 -0
  223. data/lib/active_record/type_caster/map.rb +2 -0
  224. data/lib/active_record/validations.rb +2 -0
  225. data/lib/active_record/validations/absence.rb +2 -0
  226. data/lib/active_record/validations/associated.rb +2 -0
  227. data/lib/active_record/validations/length.rb +2 -0
  228. data/lib/active_record/validations/presence.rb +2 -0
  229. data/lib/active_record/validations/uniqueness.rb +36 -6
  230. data/lib/active_record/version.rb +2 -0
  231. data/lib/rails/generators/active_record.rb +3 -1
  232. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  233. data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
  234. data/lib/rails/generators/active_record/migration.rb +2 -0
  235. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -1
  236. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +0 -0
  237. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +0 -0
  238. data/lib/rails/generators/active_record/model/model_generator.rb +2 -23
  239. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +0 -0
  240. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  241. metadata +25 -38
  242. data/lib/active_record/associations/preloader/belongs_to.rb +0 -15
  243. data/lib/active_record/associations/preloader/collection_association.rb +0 -17
  244. data/lib/active_record/associations/preloader/has_many.rb +0 -15
  245. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  246. data/lib/active_record/associations/preloader/has_one.rb +0 -15
  247. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  248. data/lib/active_record/associations/preloader/singular_association.rb +0 -18
  249. data/lib/active_record/attribute.rb +0 -240
  250. data/lib/active_record/attribute/user_provided_default.rb +0 -30
  251. data/lib/active_record/attribute_mutation_tracker.rb +0 -122
  252. data/lib/active_record/attribute_set.rb +0 -113
  253. data/lib/active_record/attribute_set/builder.rb +0 -126
  254. data/lib/active_record/attribute_set/yaml_encoder.rb +0 -41
  255. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -10
  256. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  257. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
  258. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -59
  259. data/lib/active_record/type/internal/abstract_json.rb +0 -37
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Delegation # :nodoc:
3
5
  module DelegateCache # :nodoc:
@@ -36,13 +38,12 @@ module ActiveRecord
36
38
  # may vary depending on the klass of a relation, so we create a subclass of Relation
37
39
  # for each different klass, and the delegations are compiled into that subclass only.
38
40
 
39
- delegate :to_xml, :encode_with, :length, :each, :uniq, :to_ary, :join,
40
- :[], :&, :|, :+, :-, :sample, :reverse, :compact, :in_groups, :in_groups_of,
41
+ delegate :to_xml, :encode_with, :length, :each, :uniq, :join,
42
+ :[], :&, :|, :+, :-, :sample, :reverse, :rotate, :compact, :in_groups, :in_groups_of,
41
43
  :to_sentence, :to_formatted_s, :as_json,
42
- :shuffle, :split, :index, to: :records
44
+ :shuffle, :split, :slice, :index, :rindex, to: :records
43
45
 
44
- delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,
45
- :connection, :columns_hash, to: :klass
46
+ delegate :primary_key, :connection, to: :klass
46
47
 
47
48
  module ClassSpecificRelation # :nodoc:
48
49
  extend ActiveSupport::Concern
@@ -73,13 +74,6 @@ module ActiveRecord
73
74
  end
74
75
  end
75
76
  end
76
-
77
- def delegate(method, opts = {})
78
- @delegation_mutex.synchronize do
79
- return if method_defined?(method)
80
- super
81
- end
82
- end
83
77
  end
84
78
 
85
79
  private
@@ -89,7 +83,8 @@ module ActiveRecord
89
83
  self.class.delegate_to_scoped_klass(method)
90
84
  scoping { @klass.public_send(method, *args, &block) }
91
85
  elsif arel.respond_to?(method)
92
- self.class.delegate method, to: :arel
86
+ ActiveSupport::Deprecation.warn \
87
+ "Delegating #{method} to arel is deprecated and will be removed in Rails 6.0."
93
88
  arel.public_send(method, *args, &block)
94
89
  else
95
90
  super
@@ -109,21 +104,9 @@ module ActiveRecord
109
104
  end
110
105
  end
111
106
 
112
- def respond_to_missing?(method, include_private = false)
113
- super || @klass.respond_to?(method, include_private) ||
114
- arel.respond_to?(method, include_private)
115
- end
116
-
117
107
  private
118
-
119
- def method_missing(method, *args, &block)
120
- if @klass.respond_to?(method)
121
- scoping { @klass.public_send(method, *args, &block) }
122
- elsif arel.respond_to?(method)
123
- arel.public_send(method, *args, &block)
124
- else
125
- super
126
- end
108
+ def respond_to_missing?(method, _)
109
+ super || @klass.respond_to?(method) || arel.respond_to?(method)
127
110
  end
128
111
  end
129
112
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext/string/filters"
2
4
 
3
5
  module ActiveRecord
@@ -86,7 +88,7 @@ module ActiveRecord
86
88
  where(arg, *args).take!
87
89
  rescue ::RangeError
88
90
  raise RecordNotFound.new("Couldn't find #{@klass.name} with an out of range value",
89
- @klass.name)
91
+ @klass.name, @klass.primary_key)
90
92
  end
91
93
 
92
94
  # Gives a record (or N records if a parameter is supplied) without any implied
@@ -147,8 +149,7 @@ module ActiveRecord
147
149
  def last(limit = nil)
148
150
  return find_last(limit) if loaded? || limit_value
149
151
 
150
- result = limit(limit)
151
- result.order!(arel_attribute(primary_key)) if order_values.empty? && primary_key
152
+ result = ordered_relation.limit(limit)
152
153
  result = result.reverse_order!
153
154
 
154
155
  limit ? result.reverse : result.first
@@ -283,7 +284,7 @@ module ActiveRecord
283
284
  # * Hash - Finds the record that matches these +find+-style conditions
284
285
  # (such as <tt>{name: 'David'}</tt>).
285
286
  # * +false+ - Returns always +false+.
286
- # * No args - Returns +false+ if the table is empty, +true+ otherwise.
287
+ # * No args - Returns +false+ if the relation is empty, +true+ otherwise.
287
288
  #
288
289
  # For more information about specifying conditions as a hash or array,
289
290
  # see the Conditions section in the introduction to ActiveRecord::Base.
@@ -299,6 +300,7 @@ module ActiveRecord
299
300
  # Person.exists?(name: 'David')
300
301
  # Person.exists?(false)
301
302
  # Person.exists?
303
+ # Person.where(name: 'Spartacus', rating: 4).exists?
302
304
  def exists?(conditions = :none)
303
305
  if Base === conditions
304
306
  raise ArgumentError, <<-MSG.squish
@@ -309,14 +311,14 @@ module ActiveRecord
309
311
 
310
312
  return false if !conditions || limit_value == 0
311
313
 
312
- relation = self unless eager_loading?
313
- relation ||= apply_join_dependency(self, construct_join_dependency(eager_loading: false))
314
-
315
- return false if ActiveRecord::NullRelation === relation
314
+ if eager_loading?
315
+ relation = apply_join_dependency(construct_join_dependency(eager_loading: false))
316
+ return relation.exists?(conditions)
317
+ end
316
318
 
317
- relation = construct_relation_for_exists(relation, conditions)
319
+ relation = construct_relation_for_exists(conditions)
318
320
 
319
- connection.select_value(relation, "#{name} Exists", relation.bound_attributes) ? true : false
321
+ skip_query_cache_if_necessary { connection.select_value(relation.arel, "#{name} Exists") } ? true : false
320
322
  rescue ::RangeError
321
323
  false
322
324
  end
@@ -329,23 +331,23 @@ module ActiveRecord
329
331
  # of results obtained should be provided in the +result_size+ argument and
330
332
  # the expected number of results should be provided in the +expected_size+
331
333
  # argument.
332
- def raise_record_not_found_exception!(ids = nil, result_size = nil, expected_size = nil, key = primary_key) # :nodoc:
333
- conditions = arel.where_sql(@klass.arel_engine)
334
+ def raise_record_not_found_exception!(ids = nil, result_size = nil, expected_size = nil, key = primary_key, not_found_ids = nil) # :nodoc:
335
+ conditions = arel.where_sql(@klass)
334
336
  conditions = " [#{conditions}]" if conditions
335
337
  name = @klass.name
336
338
 
337
339
  if ids.nil?
338
- error = "Couldn't find #{name}"
340
+ error = "Couldn't find #{name}".dup
339
341
  error << " with#{conditions}" if conditions
340
- raise RecordNotFound.new(error, name)
342
+ raise RecordNotFound.new(error, name, key)
341
343
  elsif Array(ids).size == 1
342
344
  error = "Couldn't find #{name} with '#{key}'=#{ids}#{conditions}"
343
345
  raise RecordNotFound.new(error, name, key, ids)
344
346
  else
345
- error = "Couldn't find all #{name.pluralize} with '#{key}': "
346
- error << "(#{ids.join(", ")})#{conditions} (found #{result_size} results, but was looking for #{expected_size})"
347
-
348
- raise RecordNotFound.new(error, name, primary_key, ids)
347
+ error = "Couldn't find all #{name.pluralize} with '#{key}': ".dup
348
+ error << "(#{ids.join(", ")})#{conditions} (found #{result_size} results, but was looking for #{expected_size})."
349
+ error << " Couldn't find #{name.pluralize(not_found_ids.size)} with #{key.to_s.pluralize(not_found_ids.size)} #{not_found_ids.join(', ')}." if not_found_ids
350
+ raise RecordNotFound.new(error, name, key, ids)
349
351
  end
350
352
  end
351
353
 
@@ -365,21 +367,20 @@ module ActiveRecord
365
367
  # preexisting join in joins_values to categorizations (by way of
366
368
  # the `has_many :through` for categories).
367
369
  #
368
- join_dependency = construct_join_dependency(joins_values)
370
+ join_dependency = construct_join_dependency
369
371
 
370
- aliases = join_dependency.aliases
371
- relation = select aliases.columns
372
- relation = apply_join_dependency(relation, join_dependency)
372
+ relation = apply_join_dependency(join_dependency)
373
+ relation._select!(join_dependency.aliases.columns)
373
374
 
374
375
  yield relation, join_dependency
375
376
  end
376
377
 
377
- def construct_relation_for_exists(relation, conditions)
378
- relation = relation.except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1)
378
+ def construct_relation_for_exists(conditions)
379
+ relation = except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1)
379
380
 
380
381
  case conditions
381
382
  when Array, Hash
382
- relation.where!(conditions) unless conditions.empty?
383
+ relation.where!(conditions)
383
384
  else
384
385
  relation.where!(primary_key => conditions) unless conditions == :none
385
386
  end
@@ -387,17 +388,15 @@ module ActiveRecord
387
388
  relation
388
389
  end
389
390
 
390
- def construct_join_dependency(joins = [], eager_loading: true)
391
+ def construct_join_dependency(eager_loading: true)
391
392
  including = eager_load_values + includes_values
392
- ActiveRecord::Associations::JoinDependency.new(@klass, including, joins, eager_loading: eager_loading)
393
- end
394
-
395
- def construct_relation_for_association_calculations
396
- apply_join_dependency(self, construct_join_dependency(joins_values))
393
+ ActiveRecord::Associations::JoinDependency.new(
394
+ klass, table, including, alias_tracker(joins_values), eager_loading: eager_loading
395
+ )
397
396
  end
398
397
 
399
- def apply_join_dependency(relation, join_dependency)
400
- relation = relation.except(:includes, :eager_load, :preload).joins!(join_dependency)
398
+ def apply_join_dependency(join_dependency = construct_join_dependency)
399
+ relation = except(:includes, :eager_load, :preload).joins!(join_dependency)
401
400
 
402
401
  if using_limitable_reflections?(join_dependency.reflections)
403
402
  relation
@@ -412,12 +411,13 @@ module ActiveRecord
412
411
 
413
412
  def limited_ids_for(relation)
414
413
  values = @klass.connection.columns_for_distinct(
415
- "#{quoted_table_name}.#{quoted_primary_key}", relation.order_values)
414
+ connection.column_name_from_arel_node(arel_attribute(primary_key)),
415
+ relation.order_values
416
+ )
416
417
 
417
418
  relation = relation.except(:select).select(values).distinct!
418
- arel = relation.arel
419
419
 
420
- id_rows = @klass.connection.select_all(arel, "SQL", relation.bound_attributes)
420
+ id_rows = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, "SQL") }
421
421
  id_rows.map { |row| row[primary_key] }
422
422
  end
423
423
 
@@ -429,13 +429,16 @@ module ActiveRecord
429
429
  raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
430
430
 
431
431
  expects_array = ids.first.kind_of?(Array)
432
- return [] if expects_array && ids.first.empty?
432
+ return ids.first if expects_array && ids.first.empty?
433
433
 
434
434
  ids = ids.flatten.compact.uniq
435
435
 
436
+ model_name = @klass.name
437
+
436
438
  case ids.size
437
439
  when 0
438
- raise RecordNotFound, "Couldn't find #{@klass.name} without an ID"
440
+ error_message = "Couldn't find #{model_name} without an ID"
441
+ raise RecordNotFound.new(error_message, model_name, primary_key)
439
442
  when 1
440
443
  result = find_one(ids.first)
441
444
  expects_array ? [ result ] : result
@@ -443,7 +446,8 @@ module ActiveRecord
443
446
  find_some(ids)
444
447
  end
445
448
  rescue ::RangeError
446
- raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range ID"
449
+ error_message = "Couldn't find #{model_name} with an out of range ID"
450
+ raise RecordNotFound.new(error_message, model_name, primary_key, ids)
447
451
  end
448
452
 
449
453
  def find_one(id)
@@ -525,11 +529,7 @@ module ActiveRecord
525
529
  if loaded?
526
530
  records[index, limit] || []
527
531
  else
528
- relation = if order_values.empty? && primary_key
529
- order(arel_attribute(primary_key).asc)
530
- else
531
- self
532
- end
532
+ relation = ordered_relation
533
533
 
534
534
  if limit_value.nil? || index < limit_value
535
535
  relation = relation.offset(offset_index + index) unless index.zero?
@@ -544,11 +544,7 @@ module ActiveRecord
544
544
  if loaded?
545
545
  records[-index]
546
546
  else
547
- relation = if order_values.empty? && primary_key
548
- order(arel_attribute(primary_key).asc)
549
- else
550
- self
551
- end
547
+ relation = ordered_relation
552
548
 
553
549
  relation.to_a[-index]
554
550
  # TODO: can be made more performant on large result sets by
@@ -562,5 +558,13 @@ module ActiveRecord
562
558
  def find_last(limit)
563
559
  limit ? records.last(limit) : records.last
564
560
  end
561
+
562
+ def ordered_relation
563
+ if order_values.empty? && primary_key
564
+ order(arel_attribute(primary_key).asc)
565
+ else
566
+ self
567
+ end
568
+ end
565
569
  end
566
570
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  class Relation
3
5
  class FromClause # :nodoc:
@@ -8,14 +10,6 @@ module ActiveRecord
8
10
  @name = name
9
11
  end
10
12
 
11
- def binds
12
- if value.is_a?(Relation)
13
- value.bound_attributes
14
- else
15
- []
16
- end
17
- end
18
-
19
13
  def merge(other)
20
14
  self
21
15
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext/hash/keys"
2
4
 
3
5
  module ActiveRecord
@@ -110,21 +112,20 @@ module ActiveRecord
110
112
  if other.klass == relation.klass
111
113
  relation.joins!(*other.joins_values)
112
114
  else
113
- joins_dependency, rest = other.joins_values.partition do |join|
115
+ alias_tracker = nil
116
+ joins_dependency = other.joins_values.map do |join|
114
117
  case join
115
118
  when Hash, Symbol, Array
116
- true
119
+ alias_tracker ||= other.alias_tracker
120
+ ActiveRecord::Associations::JoinDependency.new(
121
+ other.klass, other.table, join, alias_tracker
122
+ )
117
123
  else
118
- false
124
+ join
119
125
  end
120
126
  end
121
127
 
122
- join_dependency = ActiveRecord::Associations::JoinDependency.new(other.klass,
123
- joins_dependency,
124
- [])
125
- relation.joins! rest
126
-
127
- @relation = relation.joins join_dependency
128
+ relation.joins!(*joins_dependency)
128
129
  end
129
130
  end
130
131
 
@@ -132,19 +133,17 @@ module ActiveRecord
132
133
  if other.reordering_value
133
134
  # override any order specified in the original relation
134
135
  relation.reorder! other.order_values
135
- elsif other.order_values
136
+ elsif other.order_values.any?
136
137
  # merge in order_values from relation
137
138
  relation.order! other.order_values
138
139
  end
139
140
 
140
- relation.extend(*other.extending_values) unless other.extending_values.blank?
141
+ extensions = other.extensions - relation.extensions
142
+ relation.extending!(*extensions) if extensions.any?
141
143
  end
142
144
 
143
145
  def merge_single_values
144
- if relation.from_clause.empty?
145
- relation.from_clause = other.from_clause
146
- end
147
- relation.lock_value ||= other.lock_value
146
+ relation.lock_value ||= other.lock_value if other.lock_value
148
147
 
149
148
  unless other.create_with_value.blank?
150
149
  relation.create_with_value = (relation.create_with_value || {}).merge(other.create_with_value)
@@ -152,11 +151,15 @@ module ActiveRecord
152
151
  end
153
152
 
154
153
  def merge_clauses
155
- CLAUSE_METHODS.each do |method|
156
- clause = relation.get_value(method)
157
- other_clause = other.get_value(method)
158
- relation.set_value(method, clause.merge(other_clause))
154
+ if relation.from_clause.empty? && !other.from_clause.empty?
155
+ relation.from_clause = other.from_clause
159
156
  end
157
+
158
+ where_clause = relation.where_clause.merge(other.where_clause)
159
+ relation.where_clause = where_clause unless where_clause.empty?
160
+
161
+ having_clause = relation.having_clause.merge(other.having_clause)
162
+ relation.having_clause = having_clause unless having_clause.empty?
160
163
  end
161
164
  end
162
165
  end
@@ -1,27 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  class PredicateBuilder # :nodoc:
3
- require "active_record/relation/predicate_builder/array_handler"
4
- require "active_record/relation/predicate_builder/association_query_handler"
5
- require "active_record/relation/predicate_builder/base_handler"
6
- require "active_record/relation/predicate_builder/basic_object_handler"
7
- require "active_record/relation/predicate_builder/polymorphic_array_handler"
8
- require "active_record/relation/predicate_builder/range_handler"
9
- require "active_record/relation/predicate_builder/relation_handler"
10
-
11
5
  delegate :resolve_column_aliases, to: :table
12
6
 
13
7
  def initialize(table)
14
8
  @table = table
15
9
  @handlers = []
16
10
 
17
- register_handler(BasicObject, BasicObjectHandler.new)
11
+ register_handler(BasicObject, BasicObjectHandler.new(self))
18
12
  register_handler(Base, BaseHandler.new(self))
19
- register_handler(Range, RangeHandler.new)
20
- register_handler(RangeHandler::RangeWithBinds, RangeHandler.new)
13
+ register_handler(Range, RangeHandler.new(self))
21
14
  register_handler(Relation, RelationHandler.new)
22
15
  register_handler(Array, ArrayHandler.new(self))
23
- register_handler(AssociationQueryValue, AssociationQueryHandler.new(self))
24
- register_handler(PolymorphicArrayValue, PolymorphicArrayHandler.new(self))
16
+ register_handler(Set, ArrayHandler.new(self))
25
17
  end
26
18
 
27
19
  def build_from_hash(attributes)
@@ -29,11 +21,6 @@ module ActiveRecord
29
21
  expand_from_hash(attributes)
30
22
  end
31
23
 
32
- def create_binds(attributes)
33
- attributes = convert_dot_notation_to_hash(attributes)
34
- create_binds_for_hash(attributes)
35
- end
36
-
37
24
  def self.references(attributes)
38
25
  attributes.map do |key, value|
39
26
  if value.is_a?(Hash)
@@ -64,8 +51,11 @@ module ActiveRecord
64
51
  handler_for(value).call(attribute, value)
65
52
  end
66
53
 
67
- # TODO Change this to private once we've dropped Ruby 2.2 support.
68
- # Workaround for Ruby 2.2 "private attribute?" warning.
54
+ def build_bind_attribute(column_name, value)
55
+ attr = Relation::QueryAttribute.new(column_name.to_s, value, table.type(column_name))
56
+ Arel::Nodes::BindParam.new(attr)
57
+ end
58
+
69
59
  protected
70
60
 
71
61
  attr_reader :table
@@ -76,56 +66,34 @@ module ActiveRecord
76
66
  attributes.flat_map do |key, value|
77
67
  if value.is_a?(Hash) && !table.has_column?(key)
78
68
  associated_predicate_builder(key).expand_from_hash(value)
79
- else
80
- build(table.arel_attribute(key), value)
81
- end
82
- end
83
- end
84
-
85
- def create_binds_for_hash(attributes)
86
- result = attributes.dup
87
- binds = []
88
-
89
- attributes.each do |column_name, value|
90
- case
91
- when value.is_a?(Hash) && !table.has_column?(column_name)
92
- attrs, bvs = associated_predicate_builder(column_name).create_binds_for_hash(value)
93
- result[column_name] = attrs
94
- binds += bvs
95
- next
96
- when value.is_a?(Relation)
97
- binds += value.bound_attributes
98
- when value.is_a?(Range) && !table.type(column_name).force_equality?(value)
99
- first = value.begin
100
- last = value.end
101
- unless first.respond_to?(:infinite?) && first.infinite?
102
- binds << build_bind_param(column_name, first)
103
- first = Arel::Nodes::BindParam.new
104
- end
105
- unless last.respond_to?(:infinite?) && last.infinite?
106
- binds << build_bind_param(column_name, last)
107
- last = Arel::Nodes::BindParam.new
69
+ elsif table.associated_with?(key)
70
+ # Find the foreign key when using queries such as:
71
+ # Post.where(author: author)
72
+ #
73
+ # For polymorphic relationships, find the foreign key and type:
74
+ # PriceEstimate.where(estimate_of: treasure)
75
+ associated_table = table.associated_table(key)
76
+ if associated_table.polymorphic_association?
77
+ case value.is_a?(Array) ? value.first : value
78
+ when Base, Relation
79
+ value = [value] unless value.is_a?(Array)
80
+ klass = PolymorphicArrayValue
81
+ end
108
82
  end
109
83
 
110
- result[column_name] = RangeHandler::RangeWithBinds.new(first, last, value.exclude_end?)
111
- else
112
- if can_be_bound?(column_name, value)
113
- result[column_name] = Arel::Nodes::BindParam.new
114
- binds << build_bind_param(column_name, value)
84
+ klass ||= AssociationQueryValue
85
+ queries = klass.new(associated_table, value).queries.map do |query|
86
+ expand_from_hash(query).reduce(&:and)
115
87
  end
116
- end
117
-
118
- # Find the foreign key when using queries such as:
119
- # Post.where(author: author)
120
- #
121
- # For polymorphic relationships, find the foreign key and type:
122
- # PriceEstimate.where(estimate_of: treasure)
123
- if table.associated_with?(column_name)
124
- result[column_name] = AssociationQueryHandler.value_for(table, column_name, value)
88
+ queries.reduce(&:or)
89
+ # FIXME: Deprecate this and provide a public API to force equality
90
+ elsif (value.is_a?(Range) || value.is_a?(Array)) &&
91
+ table.type(key.to_s).respond_to?(:subtype)
92
+ BasicObjectHandler.new(self).call(table.arel_attribute(key), value)
93
+ else
94
+ build(table.arel_attribute(key), value)
125
95
  end
126
96
  end
127
-
128
- [result, binds]
129
97
  end
130
98
 
131
99
  private
@@ -153,19 +121,14 @@ module ActiveRecord
153
121
  def handler_for(object)
154
122
  @handlers.detect { |klass, _| klass === object }.last
155
123
  end
156
-
157
- def can_be_bound?(column_name, value)
158
- return if table.associated_with?(column_name)
159
- case value
160
- when Array, Range
161
- table.type(column_name).force_equality?(value)
162
- else
163
- !value.nil? && handler_for(value).is_a?(BasicObjectHandler)
164
- end
165
- end
166
-
167
- def build_bind_param(column_name, value)
168
- Relation::QueryAttribute.new(column_name.to_s, value, table.type(column_name))
169
- end
170
124
  end
171
125
  end
126
+
127
+ require "active_record/relation/predicate_builder/array_handler"
128
+ require "active_record/relation/predicate_builder/base_handler"
129
+ require "active_record/relation/predicate_builder/basic_object_handler"
130
+ require "active_record/relation/predicate_builder/range_handler"
131
+ require "active_record/relation/predicate_builder/relation_handler"
132
+
133
+ require "active_record/relation/predicate_builder/association_query_value"
134
+ require "active_record/relation/predicate_builder/polymorphic_array_value"