activerecord 4.2.11 → 5.2.4.1

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 (274) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +580 -1626
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +10 -11
  5. data/examples/performance.rb +32 -31
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record/aggregations.rb +263 -249
  8. data/lib/active_record/association_relation.rb +11 -6
  9. data/lib/active_record/associations/alias_tracker.rb +29 -35
  10. data/lib/active_record/associations/association.rb +77 -43
  11. data/lib/active_record/associations/association_scope.rb +106 -133
  12. data/lib/active_record/associations/belongs_to_association.rb +52 -41
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
  14. data/lib/active_record/associations/builder/association.rb +29 -38
  15. data/lib/active_record/associations/builder/belongs_to.rb +77 -30
  16. data/lib/active_record/associations/builder/collection_association.rb +9 -22
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +42 -35
  18. data/lib/active_record/associations/builder/has_many.rb +6 -4
  19. data/lib/active_record/associations/builder/has_one.rb +13 -6
  20. data/lib/active_record/associations/builder/singular_association.rb +15 -11
  21. data/lib/active_record/associations/collection_association.rb +139 -280
  22. data/lib/active_record/associations/collection_proxy.rb +231 -133
  23. data/lib/active_record/associations/foreign_association.rb +3 -1
  24. data/lib/active_record/associations/has_many_association.rb +34 -89
  25. data/lib/active_record/associations/has_many_through_association.rb +49 -76
  26. data/lib/active_record/associations/has_one_association.rb +38 -24
  27. data/lib/active_record/associations/has_one_through_association.rb +18 -9
  28. data/lib/active_record/associations/join_dependency/join_association.rb +40 -87
  29. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +12 -12
  31. data/lib/active_record/associations/join_dependency.rb +133 -159
  32. data/lib/active_record/associations/preloader/association.rb +85 -120
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -74
  34. data/lib/active_record/associations/preloader.rb +81 -91
  35. data/lib/active_record/associations/singular_association.rb +27 -34
  36. data/lib/active_record/associations/through_association.rb +38 -18
  37. data/lib/active_record/associations.rb +1732 -1597
  38. data/lib/active_record/attribute_assignment.rb +58 -182
  39. data/lib/active_record/attribute_decorators.rb +39 -15
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +10 -8
  41. data/lib/active_record/attribute_methods/dirty.rb +94 -135
  42. data/lib/active_record/attribute_methods/primary_key.rb +86 -71
  43. data/lib/active_record/attribute_methods/query.rb +4 -2
  44. data/lib/active_record/attribute_methods/read.rb +45 -63
  45. data/lib/active_record/attribute_methods/serialization.rb +40 -20
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +58 -36
  47. data/lib/active_record/attribute_methods/write.rb +30 -45
  48. data/lib/active_record/attribute_methods.rb +166 -109
  49. data/lib/active_record/attributes.rb +201 -82
  50. data/lib/active_record/autosave_association.rb +94 -36
  51. data/lib/active_record/base.rb +57 -44
  52. data/lib/active_record/callbacks.rb +97 -57
  53. data/lib/active_record/coders/json.rb +3 -1
  54. data/lib/active_record/coders/yaml_column.rb +24 -12
  55. data/lib/active_record/collection_cache_key.rb +53 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -290
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +237 -90
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +71 -21
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +118 -52
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +318 -217
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +81 -36
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +570 -228
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +138 -70
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +325 -202
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +542 -601
  69. data/lib/active_record/connection_adapters/column.rb +50 -41
  70. data/lib/active_record/connection_adapters/connection_specification.rb +147 -135
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +41 -180
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +45 -114
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -58
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +10 -6
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -22
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
  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 -11
  97. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +5 -7
  99. data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +55 -53
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +5 -3
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +107 -47
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +144 -90
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -284
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +432 -323
  117. data/lib/active_record/connection_adapters/schema_cache.rb +48 -24
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +269 -308
  126. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  127. data/lib/active_record/connection_handling.rb +40 -27
  128. data/lib/active_record/core.rb +178 -198
  129. data/lib/active_record/counter_cache.rb +79 -36
  130. data/lib/active_record/define_callbacks.rb +22 -0
  131. data/lib/active_record/dynamic_matchers.rb +87 -105
  132. data/lib/active_record/enum.rb +135 -88
  133. data/lib/active_record/errors.rb +179 -52
  134. data/lib/active_record/explain.rb +23 -11
  135. data/lib/active_record/explain_registry.rb +4 -2
  136. data/lib/active_record/explain_subscriber.rb +10 -5
  137. data/lib/active_record/fixture_set/file.rb +35 -9
  138. data/lib/active_record/fixtures.rb +188 -132
  139. data/lib/active_record/gem_version.rb +5 -3
  140. data/lib/active_record/inheritance.rb +148 -112
  141. data/lib/active_record/integration.rb +70 -28
  142. data/lib/active_record/internal_metadata.rb +45 -0
  143. data/lib/active_record/legacy_yaml_adapter.rb +21 -3
  144. data/lib/active_record/locale/en.yml +3 -2
  145. data/lib/active_record/locking/optimistic.rb +88 -96
  146. data/lib/active_record/locking/pessimistic.rb +15 -3
  147. data/lib/active_record/log_subscriber.rb +95 -33
  148. data/lib/active_record/migration/command_recorder.rb +133 -90
  149. data/lib/active_record/migration/compatibility.rb +217 -0
  150. data/lib/active_record/migration/join_table.rb +8 -6
  151. data/lib/active_record/migration.rb +581 -282
  152. data/lib/active_record/model_schema.rb +290 -111
  153. data/lib/active_record/nested_attributes.rb +264 -222
  154. data/lib/active_record/no_touching.rb +7 -1
  155. data/lib/active_record/null_relation.rb +24 -37
  156. data/lib/active_record/persistence.rb +347 -119
  157. data/lib/active_record/query_cache.rb +13 -24
  158. data/lib/active_record/querying.rb +19 -17
  159. data/lib/active_record/railtie.rb +94 -32
  160. data/lib/active_record/railties/console_sandbox.rb +2 -0
  161. data/lib/active_record/railties/controller_runtime.rb +9 -3
  162. data/lib/active_record/railties/databases.rake +149 -156
  163. data/lib/active_record/readonly_attributes.rb +5 -4
  164. data/lib/active_record/reflection.rb +414 -267
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  166. data/lib/active_record/relation/batches.rb +204 -55
  167. data/lib/active_record/relation/calculations.rb +256 -248
  168. data/lib/active_record/relation/delegation.rb +67 -60
  169. data/lib/active_record/relation/finder_methods.rb +288 -239
  170. data/lib/active_record/relation/from_clause.rb +26 -0
  171. data/lib/active_record/relation/merger.rb +86 -86
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -24
  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 +19 -0
  175. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
  176. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  177. data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  179. data/lib/active_record/relation/predicate_builder.rb +116 -119
  180. data/lib/active_record/relation/query_attribute.rb +45 -0
  181. data/lib/active_record/relation/query_methods.rb +448 -393
  182. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  183. data/lib/active_record/relation/spawn_methods.rb +11 -13
  184. data/lib/active_record/relation/where_clause.rb +186 -0
  185. data/lib/active_record/relation/where_clause_factory.rb +34 -0
  186. data/lib/active_record/relation.rb +287 -340
  187. data/lib/active_record/result.rb +54 -36
  188. data/lib/active_record/runtime_registry.rb +6 -4
  189. data/lib/active_record/sanitization.rb +155 -124
  190. data/lib/active_record/schema.rb +30 -24
  191. data/lib/active_record/schema_dumper.rb +91 -87
  192. data/lib/active_record/schema_migration.rb +19 -16
  193. data/lib/active_record/scoping/default.rb +102 -85
  194. data/lib/active_record/scoping/named.rb +81 -32
  195. data/lib/active_record/scoping.rb +45 -26
  196. data/lib/active_record/secure_token.rb +40 -0
  197. data/lib/active_record/serialization.rb +5 -5
  198. data/lib/active_record/statement_cache.rb +45 -35
  199. data/lib/active_record/store.rb +42 -36
  200. data/lib/active_record/suppressor.rb +61 -0
  201. data/lib/active_record/table_metadata.rb +82 -0
  202. data/lib/active_record/tasks/database_tasks.rb +134 -96
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +56 -100
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +83 -41
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -16
  206. data/lib/active_record/timestamp.rb +70 -38
  207. data/lib/active_record/touch_later.rb +64 -0
  208. data/lib/active_record/transactions.rb +199 -124
  209. data/lib/active_record/translation.rb +2 -0
  210. data/lib/active_record/type/adapter_specific_registry.rb +136 -0
  211. data/lib/active_record/type/date.rb +4 -45
  212. data/lib/active_record/type/date_time.rb +4 -49
  213. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  214. data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
  215. data/lib/active_record/type/internal/timezone.rb +17 -0
  216. data/lib/active_record/type/json.rb +30 -0
  217. data/lib/active_record/type/serialized.rb +24 -15
  218. data/lib/active_record/type/text.rb +2 -2
  219. data/lib/active_record/type/time.rb +11 -16
  220. data/lib/active_record/type/type_map.rb +15 -17
  221. data/lib/active_record/type/unsigned_integer.rb +9 -7
  222. data/lib/active_record/type.rb +79 -23
  223. data/lib/active_record/type_caster/connection.rb +33 -0
  224. data/lib/active_record/type_caster/map.rb +23 -0
  225. data/lib/active_record/type_caster.rb +9 -0
  226. data/lib/active_record/validations/absence.rb +25 -0
  227. data/lib/active_record/validations/associated.rb +13 -4
  228. data/lib/active_record/validations/length.rb +26 -0
  229. data/lib/active_record/validations/presence.rb +14 -13
  230. data/lib/active_record/validations/uniqueness.rb +40 -41
  231. data/lib/active_record/validations.rb +38 -35
  232. data/lib/active_record/version.rb +3 -1
  233. data/lib/active_record.rb +34 -22
  234. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  235. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  236. data/lib/rails/generators/active_record/migration/migration_generator.rb +43 -35
  237. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +8 -3
  238. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -1
  239. data/lib/rails/generators/active_record/migration.rb +18 -1
  240. data/lib/rails/generators/active_record/model/model_generator.rb +18 -22
  241. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +3 -0
  242. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  243. data/lib/rails/generators/active_record.rb +7 -5
  244. metadata +72 -50
  245. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  246. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  247. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  248. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  249. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  250. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  251. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  252. data/lib/active_record/attribute.rb +0 -163
  253. data/lib/active_record/attribute_set/builder.rb +0 -106
  254. data/lib/active_record/attribute_set.rb +0 -81
  255. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  256. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  257. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  258. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  259. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  260. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  261. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  262. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  263. data/lib/active_record/type/big_integer.rb +0 -13
  264. data/lib/active_record/type/binary.rb +0 -50
  265. data/lib/active_record/type/boolean.rb +0 -31
  266. data/lib/active_record/type/decimal.rb +0 -64
  267. data/lib/active_record/type/decorator.rb +0 -14
  268. data/lib/active_record/type/float.rb +0 -19
  269. data/lib/active_record/type/integer.rb +0 -59
  270. data/lib/active_record/type/mutable.rb +0 -16
  271. data/lib/active_record/type/numeric.rb +0 -36
  272. data/lib/active_record/type/string.rb +0 -40
  273. data/lib/active_record/type/time_value.rb +0 -38
  274. data/lib/active_record/type/value.rb +0 -110
@@ -1,113 +1,50 @@
1
- # -*- coding: utf-8 -*-
2
- require 'arel/collectors/bind'
1
+ # frozen_string_literal: true
3
2
 
4
3
  module ActiveRecord
5
- # = Active Record Relation
4
+ # = Active Record \Relation
6
5
  class Relation
7
6
  MULTI_VALUE_METHODS = [:includes, :eager_load, :preload, :select, :group,
8
- :order, :joins, :where, :having, :bind, :references,
7
+ :order, :joins, :left_outer_joins, :references,
9
8
  :extending, :unscope]
10
9
 
11
- SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :from, :reordering,
12
- :reverse_order, :distinct, :create_with, :uniq]
13
- INVALID_METHODS_FOR_DELETE_ALL = [:limit, :distinct, :offset, :group, :having]
10
+ SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :reordering,
11
+ :reverse_order, :distinct, :create_with, :skip_query_cache]
12
+ CLAUSE_METHODS = [:where, :having, :from]
13
+ INVALID_METHODS_FOR_DELETE_ALL = [:distinct, :group, :having]
14
14
 
15
- VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS
15
+ VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS + CLAUSE_METHODS
16
16
 
17
+ include Enumerable
17
18
  include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation
18
19
 
19
- attr_reader :table, :klass, :loaded
20
+ attr_reader :table, :klass, :loaded, :predicate_builder
20
21
  alias :model :klass
21
22
  alias :loaded? :loaded
23
+ alias :locked? :lock_value
22
24
 
23
- def initialize(klass, table, values = {})
25
+ def initialize(klass, table: klass.arel_table, predicate_builder: klass.predicate_builder, values: {})
24
26
  @klass = klass
25
27
  @table = table
26
28
  @values = values
27
29
  @offsets = {}
28
30
  @loaded = false
31
+ @predicate_builder = predicate_builder
32
+ @delegate_to_klass = false
29
33
  end
30
34
 
31
35
  def initialize_copy(other)
32
- # This method is a hot spot, so for now, use Hash[] to dup the hash.
33
- # https://bugs.ruby-lang.org/issues/7166
34
- @values = Hash[@values]
35
- @values[:bind] = @values[:bind].dup if @values.key? :bind
36
+ @values = @values.dup
36
37
  reset
37
38
  end
38
39
 
39
- def insert(values) # :nodoc:
40
- primary_key_value = nil
41
-
42
- if primary_key && Hash === values
43
- primary_key_value = values[values.keys.find { |k|
44
- k.name == primary_key
45
- }]
46
-
47
- if !primary_key_value && connection.prefetch_primary_key?(klass.table_name)
48
- primary_key_value = connection.next_sequence_value(klass.sequence_name)
49
- values[klass.arel_table[klass.primary_key]] = primary_key_value
50
- end
51
- end
52
-
53
- im = arel.create_insert
54
- im.into @table
55
-
56
- substitutes, binds = substitute_values values
57
-
58
- if values.empty? # empty insert
59
- im.values = Arel.sql(connection.empty_insert_statement_value)
60
- else
61
- im.insert substitutes
62
- end
63
-
64
- @klass.connection.insert(
65
- im,
66
- 'SQL',
67
- primary_key,
68
- primary_key_value,
69
- nil,
70
- binds)
71
- end
72
-
73
- def _update_record(values, id, id_was) # :nodoc:
74
- substitutes, binds = substitute_values values
75
-
76
- scope = @klass.unscoped
77
-
78
- if @klass.finder_needs_type_condition?
79
- scope.unscope!(where: @klass.inheritance_column)
80
- end
81
-
82
- relation = scope.where(@klass.primary_key => (id_was || id))
83
- bvs = binds + relation.bind_values
84
- um = relation
85
- .arel
86
- .compile_update(substitutes, @klass.primary_key)
87
-
88
- @klass.connection.update(
89
- um,
90
- 'SQL',
91
- bvs,
92
- )
93
- end
94
-
95
- def substitute_values(values) # :nodoc:
96
- binds = values.map do |arel_attr, value|
97
- [@klass.columns_hash[arel_attr.name], value]
98
- end
99
-
100
- substitutes = values.each_with_index.map do |(arel_attr, _), i|
101
- [arel_attr, @klass.connection.substitute_at(binds[i][0])]
102
- end
103
-
104
- [substitutes, binds]
40
+ def arel_attribute(name) # :nodoc:
41
+ klass.arel_attribute(name, table)
105
42
  end
106
43
 
107
44
  # Initializes new record from relation while maintaining the current
108
45
  # scope.
109
46
  #
110
- # Expects arguments in the same format as +Base.new+.
47
+ # Expects arguments in the same format as {ActiveRecord::Base.new}[rdoc-ref:Core.new].
111
48
  #
112
49
  # users = User.where(name: 'DHH')
113
50
  # user = users.new # => #<User id: nil, name: "DHH", created_at: nil, updated_at: nil>
@@ -116,8 +53,8 @@ module ActiveRecord
116
53
  #
117
54
  # user = users.new { |user| user.name = 'Oscar' }
118
55
  # user.name # => Oscar
119
- def new(*args, &block)
120
- scoping { @klass.new(*args, &block) }
56
+ def new(attributes = nil, &block)
57
+ scoping { klass.new(values_for_create(attributes), &block) }
121
58
  end
122
59
 
123
60
  alias build new
@@ -125,30 +62,42 @@ module ActiveRecord
125
62
  # Tries to create a new record with the same scoped attributes
126
63
  # defined in the relation. Returns the initialized object if validation fails.
127
64
  #
128
- # Expects arguments in the same format as +Base.create+.
65
+ # Expects arguments in the same format as
66
+ # {ActiveRecord::Base.create}[rdoc-ref:Persistence::ClassMethods#create].
129
67
  #
130
68
  # ==== Examples
69
+ #
131
70
  # users = User.where(name: 'Oscar')
132
- # users.create # #<User id: 3, name: "oscar", ...>
71
+ # users.create # => #<User id: 3, name: "Oscar", ...>
133
72
  #
134
73
  # users.create(name: 'fxn')
135
- # users.create # #<User id: 4, name: "fxn", ...>
74
+ # users.create # => #<User id: 4, name: "fxn", ...>
136
75
  #
137
76
  # users.create { |user| user.name = 'tenderlove' }
138
- # # #<User id: 5, name: "tenderlove", ...>
77
+ # # => #<User id: 5, name: "tenderlove", ...>
139
78
  #
140
79
  # users.create(name: nil) # validation on name
141
- # # #<User id: nil, name: nil, ...>
142
- def create(*args, &block)
143
- scoping { @klass.create(*args, &block) }
80
+ # # => #<User id: nil, name: nil, ...>
81
+ def create(attributes = nil, &block)
82
+ if attributes.is_a?(Array)
83
+ attributes.collect { |attr| create(attr, &block) }
84
+ else
85
+ scoping { klass.create(values_for_create(attributes), &block) }
86
+ end
144
87
  end
145
88
 
146
- # Similar to #create, but calls +create!+ on the base class. Raises
147
- # an exception if a validation error occurs.
89
+ # Similar to #create, but calls
90
+ # {create!}[rdoc-ref:Persistence::ClassMethods#create!]
91
+ # on the base class. Raises an exception if a validation error occurs.
148
92
  #
149
- # Expects arguments in the same format as <tt>Base.create!</tt>.
150
- def create!(*args, &block)
151
- scoping { @klass.create!(*args, &block) }
93
+ # Expects arguments in the same format as
94
+ # {ActiveRecord::Base.create!}[rdoc-ref:Persistence::ClassMethods#create!].
95
+ def create!(attributes = nil, &block)
96
+ if attributes.is_a?(Array)
97
+ attributes.collect { |attr| create!(attr, &block) }
98
+ else
99
+ scoping { klass.create!(values_for_create(attributes), &block) }
100
+ end
152
101
  end
153
102
 
154
103
  def first_or_create(attributes = nil, &block) # :nodoc:
@@ -180,7 +129,7 @@ module ActiveRecord
180
129
  # User.create_with(last_name: 'Johansson').find_or_create_by(first_name: 'Scarlett')
181
130
  # # => #<User id: 2, first_name: "Scarlett", last_name: "Johansson">
182
131
  #
183
- # This method accepts a block, which is passed down to +create+. The last example
132
+ # This method accepts a block, which is passed down to #create. The last example
184
133
  # above can be alternatively written this way:
185
134
  #
186
135
  # # Find the first user named "Scarlett" or create a new one with a
@@ -192,7 +141,7 @@ module ActiveRecord
192
141
  #
193
142
  # This method always returns a record, but if creation was attempted and
194
143
  # failed due to validation errors it won't be persisted, you get what
195
- # +create+ returns in such situation.
144
+ # #create returns in such situation.
196
145
  #
197
146
  # Please note *this method is not atomic*, it runs first a SELECT, and if
198
147
  # there are no results an INSERT is attempted. If there are other threads
@@ -204,7 +153,9 @@ module ActiveRecord
204
153
  # constraint an exception may be raised, just retry:
205
154
  #
206
155
  # begin
207
- # CreditAccount.find_or_create_by(user_id: user.id)
156
+ # CreditAccount.transaction(requires_new: true) do
157
+ # CreditAccount.find_or_create_by(user_id: user.id)
158
+ # end
208
159
  # rescue ActiveRecord::RecordNotUnique
209
160
  # retry
210
161
  # end
@@ -213,13 +164,15 @@ module ActiveRecord
213
164
  find_by(attributes) || create(attributes, &block)
214
165
  end
215
166
 
216
- # Like <tt>find_or_create_by</tt>, but calls <tt>create!</tt> so an exception
167
+ # Like #find_or_create_by, but calls
168
+ # {create!}[rdoc-ref:Persistence::ClassMethods#create!] so an exception
217
169
  # is raised if the created record is invalid.
218
170
  def find_or_create_by!(attributes, &block)
219
171
  find_by(attributes) || create!(attributes, &block)
220
172
  end
221
173
 
222
- # Like <tt>find_or_create_by</tt>, but calls <tt>new</tt> instead of <tt>create</tt>.
174
+ # Like #find_or_create_by, but calls {new}[rdoc-ref:Core#new]
175
+ # instead of {create}[rdoc-ref:Persistence::ClassMethods#create].
223
176
  def find_or_initialize_by(attributes, &block)
224
177
  find_by(attributes) || new(attributes, &block)
225
178
  end
@@ -234,23 +187,23 @@ module ActiveRecord
234
187
  # Please see further details in the
235
188
  # {Active Record Query Interface guide}[http://guides.rubyonrails.org/active_record_querying.html#running-explain].
236
189
  def explain
237
- #TODO: Fix for binds.
238
190
  exec_explain(collecting_queries_for_explain { exec_queries })
239
191
  end
240
192
 
241
193
  # Converts relation objects to Array.
242
- def to_a
194
+ def to_ary
195
+ records.dup
196
+ end
197
+ alias to_a to_ary
198
+
199
+ def records # :nodoc:
243
200
  load
244
201
  @records
245
202
  end
246
203
 
247
204
  # Serializes the relation objects Array.
248
205
  def encode_with(coder)
249
- coder.represent_seq(nil, to_a)
250
- end
251
-
252
- def as_json(options = nil) #:nodoc:
253
- to_a.as_json(options)
206
+ coder.represent_seq(nil, records)
254
207
  end
255
208
 
256
209
  # Returns size of the records.
@@ -261,31 +214,57 @@ module ActiveRecord
261
214
  # Returns true if there are no records.
262
215
  def empty?
263
216
  return @records.empty? if loaded?
217
+ !exists?
218
+ end
264
219
 
265
- if limit_value == 0
266
- true
267
- else
268
- c = count(:all)
269
- c.respond_to?(:zero?) ? c.zero? : c.empty?
270
- end
220
+ # Returns true if there are no records.
221
+ def none?
222
+ return super if block_given?
223
+ empty?
271
224
  end
272
225
 
273
226
  # Returns true if there are any records.
274
227
  def any?
275
- if block_given?
276
- to_a.any? { |*block_args| yield(*block_args) }
277
- else
278
- !empty?
279
- end
228
+ return super if block_given?
229
+ !empty?
230
+ end
231
+
232
+ # Returns true if there is exactly one record.
233
+ def one?
234
+ return super if block_given?
235
+ limit_value ? records.one? : size == 1
280
236
  end
281
237
 
282
238
  # Returns true if there is more than one record.
283
239
  def many?
284
- if block_given?
285
- to_a.many? { |*block_args| yield(*block_args) }
286
- else
287
- limit_value ? to_a.many? : size > 1
288
- end
240
+ return super if block_given?
241
+ limit_value ? records.many? : size > 1
242
+ end
243
+
244
+ # Returns a cache key that can be used to identify the records fetched by
245
+ # this query. The cache key is built with a fingerprint of the sql query,
246
+ # the number of records matched by the query and a timestamp of the last
247
+ # updated record. When a new record comes to match the query, or any of
248
+ # the existing records is updated or deleted, the cache key changes.
249
+ #
250
+ # Product.where("name like ?", "%Cosmic Encounter%").cache_key
251
+ # # => "products/query-1850ab3d302391b85b8693e941286659-1-20150714212553907087000"
252
+ #
253
+ # If the collection is loaded, the method will iterate through the records
254
+ # to generate the timestamp, otherwise it will trigger one SQL query like:
255
+ #
256
+ # SELECT COUNT(*), MAX("products"."updated_at") FROM "products" WHERE (name like '%Cosmic Encounter%')
257
+ #
258
+ # You can also pass a custom timestamp column to fetch the timestamp of the
259
+ # last updated record.
260
+ #
261
+ # Product.where("name like ?", "%Game%").cache_key(:last_reviewed_at)
262
+ #
263
+ # You can customize the strategy to generate the key on a per model basis
264
+ # overriding ActiveRecord::Base#collection_cache_key.
265
+ def cache_key(timestamp_column = :updated_at)
266
+ @cache_keys ||= {}
267
+ @cache_keys[timestamp_column] ||= @klass.collection_cache_key(self, timestamp_column)
289
268
  end
290
269
 
291
270
  # Scope all queries to the current scope.
@@ -298,17 +277,23 @@ module ActiveRecord
298
277
  # Please check unscoped if you want to remove all previous scopes (including
299
278
  # the default_scope) during the execution of a block.
300
279
  def scoping
301
- previous, klass.current_scope = klass.current_scope, self
280
+ previous, klass.current_scope = klass.current_scope(true), self unless @delegate_to_klass
302
281
  yield
303
282
  ensure
304
- klass.current_scope = previous
283
+ klass.current_scope = previous unless @delegate_to_klass
284
+ end
285
+
286
+ def _exec_scope(*args, &block) # :nodoc:
287
+ @delegate_to_klass = true
288
+ instance_exec(*args, &block) || self
289
+ ensure
290
+ @delegate_to_klass = false
305
291
  end
306
292
 
307
293
  # Updates all records in the current relation with details given. This method constructs a single SQL UPDATE
308
294
  # statement and sends it straight to the database. It does not instantiate the involved models and it does not
309
- # trigger Active Record callbacks or validations. Values passed to `update_all` will not go through
310
- # ActiveRecord's type-casting behavior. It should receive only values that can be passed as-is to the SQL
311
- # database.
295
+ # trigger Active Record callbacks or validations. However, values passed to #update_all will still go through
296
+ # Active Record's normal type casting and serialization.
312
297
  #
313
298
  # ==== Parameters
314
299
  #
@@ -324,57 +309,46 @@ module ActiveRecord
324
309
  #
325
310
  # # Update all books that match conditions, but limit it to 5 ordered by date
326
311
  # Book.where('title LIKE ?', '%Rails%').order(:created_at).limit(5).update_all(author: 'David')
312
+ #
313
+ # # Update all invoices and set the number column to its id value.
314
+ # Invoice.update_all('number = id')
327
315
  def update_all(updates)
328
316
  raise ArgumentError, "Empty list of attributes to change" if updates.blank?
329
317
 
330
- stmt = Arel::UpdateManager.new(arel.engine)
318
+ if eager_loading?
319
+ relation = apply_join_dependency
320
+ return relation.update_all(updates)
321
+ end
322
+
323
+ stmt = Arel::UpdateManager.new
331
324
 
332
- stmt.set Arel.sql(@klass.send(:sanitize_sql_for_assignment, updates))
325
+ stmt.set Arel.sql(@klass.sanitize_sql_for_assignment(updates))
333
326
  stmt.table(table)
334
- stmt.key = table[primary_key]
335
327
 
336
- if joins_values.any?
337
- @klass.connection.join_to_update(stmt, arel)
328
+ if has_join_values? || offset_value
329
+ @klass.connection.join_to_update(stmt, arel, arel_attribute(primary_key))
338
330
  else
331
+ stmt.key = arel_attribute(primary_key)
339
332
  stmt.take(arel.limit)
340
333
  stmt.order(*arel.orders)
341
334
  stmt.wheres = arel.constraints
342
335
  end
343
336
 
344
- bvs = arel.bind_values + bind_values
345
- @klass.connection.update stmt, 'SQL', bvs
337
+ @klass.connection.update stmt, "#{@klass} Update All"
346
338
  end
347
339
 
348
- # Updates an object (or multiple objects) and saves it to the database, if validations pass.
349
- # The resulting object is returned whether the object was saved successfully to the database or not.
350
- #
351
- # ==== Parameters
352
- #
353
- # * +id+ - This should be the id or an array of ids to be updated.
354
- # * +attributes+ - This should be a hash of attributes or an array of hashes.
355
- #
356
- # ==== Examples
357
- #
358
- # # Updates one record
359
- # Person.update(15, user_name: 'Samuel', group: 'expert')
360
- #
361
- # # Updates multiple records
362
- # people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
363
- # Person.update(people.keys, people.values)
364
- def update(id, attributes)
365
- if id.is_a?(Array)
366
- id.map.with_index { |one_id, idx| update(one_id, attributes[idx]) }
340
+ def update(id = :all, attributes) # :nodoc:
341
+ if id == :all
342
+ each { |record| record.update(attributes) }
367
343
  else
368
- object = find(id)
369
- object.update(attributes)
370
- object
344
+ klass.update(id, attributes)
371
345
  end
372
346
  end
373
347
 
374
- # Destroys the records matching +conditions+ by instantiating each
375
- # record and calling its +destroy+ method. Each object's callbacks are
376
- # executed (including <tt>:dependent</tt> association options). Returns the
377
- # collection of objects that were destroyed; each will be frozen, to
348
+ # Destroys the records by instantiating each
349
+ # record and calling its {#destroy}[rdoc-ref:Persistence#destroy] method.
350
+ # Each object's callbacks are executed (including <tt>:dependent</tt> association options).
351
+ # Returns the collection of objects that were destroyed; each will be frozen, to
378
352
  # reflect that no changes should be made (since they can't be persisted).
379
353
  #
380
354
  # Note: Instantiation, callback execution, and deletion of each
@@ -382,127 +356,60 @@ module ActiveRecord
382
356
  # once. It generates at least one SQL +DELETE+ query per record (or
383
357
  # possibly more, to enforce your callbacks). If you want to delete many
384
358
  # rows quickly, without concern for their associations or callbacks, use
385
- # +delete_all+ instead.
386
- #
387
- # ==== Parameters
388
- #
389
- # * +conditions+ - A string, array, or hash that specifies which records
390
- # to destroy. If omitted, all records are destroyed. See the
391
- # Conditions section in the introduction to ActiveRecord::Base for
392
- # more information.
359
+ # #delete_all instead.
393
360
  #
394
361
  # ==== Examples
395
362
  #
396
- # Person.destroy_all("last_login < '2004-04-04'")
397
- # Person.destroy_all(status: "inactive")
398
363
  # Person.where(age: 0..18).destroy_all
399
- def destroy_all(conditions = nil)
400
- if conditions
401
- where(conditions).destroy_all
402
- else
403
- to_a.each {|object| object.destroy }.tap { reset }
404
- end
405
- end
406
-
407
- # Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
408
- # therefore all callbacks and filters are fired off before the object is deleted. This method is
409
- # less efficient than ActiveRecord#delete but allows cleanup methods and other actions to be run.
410
- #
411
- # This essentially finds the object (or multiple objects) with the given id, creates a new object
412
- # from the attributes, and then calls destroy on it.
413
- #
414
- # ==== Parameters
415
- #
416
- # * +id+ - Can be either an Integer or an Array of Integers.
417
- #
418
- # ==== Examples
419
- #
420
- # # Destroy a single object
421
- # Todo.destroy(1)
422
- #
423
- # # Destroy multiple objects
424
- # todos = [1,2,3]
425
- # Todo.destroy(todos)
426
- def destroy(id)
427
- if id.is_a?(Array)
428
- id.map { |one_id| destroy(one_id) }
429
- else
430
- find(id).destroy
431
- end
364
+ def destroy_all
365
+ records.each(&:destroy).tap { reset }
432
366
  end
433
367
 
434
- # Deletes the records matching +conditions+ without instantiating the records
435
- # first, and hence not calling the +destroy+ method nor invoking callbacks. This
436
- # is a single SQL DELETE statement that goes straight to the database, much more
437
- # efficient than +destroy_all+. Be careful with relations though, in particular
368
+ # Deletes the records without instantiating the records
369
+ # first, and hence not calling the {#destroy}[rdoc-ref:Persistence#destroy]
370
+ # method nor invoking callbacks.
371
+ # This is a single SQL DELETE statement that goes straight to the database, much more
372
+ # efficient than #destroy_all. Be careful with relations though, in particular
438
373
  # <tt>:dependent</tt> rules defined on associations are not honored. Returns the
439
374
  # number of rows affected.
440
375
  #
441
- # Post.delete_all("person_id = 5 AND (category = 'Something' OR category = 'Else')")
442
- # Post.delete_all(["person_id = ? AND (category = ? OR category = ?)", 5, 'Something', 'Else'])
443
376
  # Post.where(person_id: 5).where(category: ['Something', 'Else']).delete_all
444
377
  #
445
378
  # Both calls delete the affected posts all at once with a single DELETE statement.
446
379
  # If you need to destroy dependent associations or call your <tt>before_*</tt> or
447
- # +after_destroy+ callbacks, use the +destroy_all+ method instead.
380
+ # +after_destroy+ callbacks, use the #destroy_all method instead.
448
381
  #
449
- # If an invalid method is supplied, +delete_all+ raises an ActiveRecord error:
382
+ # If an invalid method is supplied, #delete_all raises an ActiveRecordError:
450
383
  #
451
- # Post.limit(100).delete_all
452
- # # => ActiveRecord::ActiveRecordError: delete_all doesn't support limit
453
- def delete_all(conditions = nil)
454
- invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select { |method|
455
- if MULTI_VALUE_METHODS.include?(method)
456
- send("#{method}_values").any?
457
- else
458
- send("#{method}_value")
459
- end
460
- }
384
+ # Post.distinct.delete_all
385
+ # # => ActiveRecord::ActiveRecordError: delete_all doesn't support distinct
386
+ def delete_all
387
+ invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select do |method|
388
+ value = get_value(method)
389
+ SINGLE_VALUE_METHODS.include?(method) ? value : value.any?
390
+ end
461
391
  if invalid_methods.any?
462
392
  raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
463
393
  end
464
394
 
465
- if conditions
466
- where(conditions).delete_all
467
- else
468
- stmt = Arel::DeleteManager.new(arel.engine)
469
- stmt.from(table)
470
-
471
- if joins_values.any?
472
- @klass.connection.join_to_delete(stmt, arel, table[primary_key])
473
- else
474
- stmt.wheres = arel.constraints
475
- end
395
+ if eager_loading?
396
+ relation = apply_join_dependency
397
+ return relation.delete_all
398
+ end
476
399
 
477
- bvs = arel.bind_values + bind_values
478
- affected = @klass.connection.delete(stmt, 'SQL', bvs)
400
+ stmt = Arel::DeleteManager.new
401
+ stmt.from(table)
479
402
 
480
- reset
481
- affected
403
+ if has_join_values? || has_limit_or_offset?
404
+ @klass.connection.join_to_delete(stmt, arel, arel_attribute(primary_key))
405
+ else
406
+ stmt.wheres = arel.constraints
482
407
  end
483
- end
484
408
 
485
- # Deletes the row with a primary key matching the +id+ argument, using a
486
- # SQL +DELETE+ statement, and returns the number of rows deleted. Active
487
- # Record objects are not instantiated, so the object's callbacks are not
488
- # executed, including any <tt>:dependent</tt> association options.
489
- #
490
- # You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
491
- #
492
- # Note: Although it is often much faster than the alternative,
493
- # <tt>#destroy</tt>, skipping callbacks might bypass business logic in
494
- # your application that ensures referential integrity or performs other
495
- # essential jobs.
496
- #
497
- # ==== Examples
498
- #
499
- # # Delete a single row
500
- # Todo.delete(1)
501
- #
502
- # # Delete multiple rows
503
- # Todo.delete([2,3,4])
504
- def delete(id_or_array)
505
- where(primary_key => id_or_array).delete_all
409
+ affected = @klass.connection.delete(stmt, "#{@klass} Destroy")
410
+
411
+ reset
412
+ affected
506
413
  end
507
414
 
508
415
  # Causes the records to be loaded from the database if they have not
@@ -511,8 +418,8 @@ module ActiveRecord
511
418
  # return value is the relation itself, not the records.
512
419
  #
513
420
  # Post.where(published: true).load # => #<ActiveRecord::Relation>
514
- def load
515
- exec_queries unless loaded?
421
+ def load(&block)
422
+ exec_queries(&block) unless loaded?
516
423
 
517
424
  self
518
425
  end
@@ -524,9 +431,9 @@ module ActiveRecord
524
431
  end
525
432
 
526
433
  def reset
527
- @last = @to_sql = @order_clause = @scope_for_create = @arel = @loaded = nil
528
- @should_eager_load = @join_dependency = nil
529
- @records = []
434
+ @delegate_to_klass = false
435
+ @to_sql = @arel = @loaded = @should_eager_load = nil
436
+ @records = [].freeze
530
437
  @offsets = {}
531
438
  self
532
439
  end
@@ -537,47 +444,28 @@ module ActiveRecord
537
444
  # # => SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
538
445
  def to_sql
539
446
  @to_sql ||= begin
540
- relation = self
541
- connection = klass.connection
542
- visitor = connection.visitor
543
-
544
- if eager_loading?
545
- find_with_associations { |rel| relation = rel }
546
- end
547
-
548
- arel = relation.arel
549
- binds = (arel.bind_values + relation.bind_values).dup
550
- binds.map! { |bv| connection.quote(*bv.reverse) }
551
- collect = visitor.accept(arel.ast, Arel::Collectors::Bind.new)
552
- collect.substitute_binds(binds).join
553
- end
447
+ if eager_loading?
448
+ apply_join_dependency do |relation, join_dependency|
449
+ relation = join_dependency.apply_column_aliases(relation)
450
+ relation.to_sql
451
+ end
452
+ else
453
+ conn = klass.connection
454
+ conn.unprepared_statement { conn.to_sql(arel) }
455
+ end
456
+ end
554
457
  end
555
458
 
556
459
  # Returns a hash of where conditions.
557
460
  #
558
461
  # User.where(name: 'Oscar').where_values_hash
559
462
  # # => {name: "Oscar"}
560
- def where_values_hash(relation_table_name = table_name)
561
- equalities = where_values.grep(Arel::Nodes::Equality).find_all { |node|
562
- node.left.relation.name == relation_table_name
563
- }
564
-
565
- binds = Hash[bind_values.find_all(&:first).map { |column, v| [column.name, v] }]
566
-
567
- Hash[equalities.map { |where|
568
- name = where.left.name
569
- [name, binds.fetch(name.to_s) {
570
- case where.right
571
- when Array then where.right.map(&:val)
572
- when Arel::Nodes::Casted
573
- where.right.val
574
- end
575
- }]
576
- }]
463
+ def where_values_hash(relation_table_name = klass.table_name)
464
+ where_clause.to_h(relation_table_name)
577
465
  end
578
466
 
579
467
  def scope_for_create
580
- @scope_for_create ||= where_values_hash.merge(create_with_value)
468
+ where_values_hash.merge!(create_with_value.stringify_keys)
581
469
  end
582
470
 
583
471
  # Returns true if relation needs eager loading.
@@ -595,88 +483,147 @@ module ActiveRecord
595
483
  includes_values & joins_values
596
484
  end
597
485
 
598
- # +uniq+ and +uniq!+ are silently deprecated. +uniq_value+ delegates to +distinct_value+
599
- # to maintain backwards compatibility. Use +distinct_value+ instead.
600
- def uniq_value
601
- distinct_value
602
- end
603
-
604
486
  # Compares two relations for equality.
605
487
  def ==(other)
606
488
  case other
607
489
  when Associations::CollectionProxy, AssociationRelation
608
- self == other.to_a
490
+ self == other.records
609
491
  when Relation
610
492
  other.to_sql == to_sql
611
493
  when Array
612
- to_a == other
494
+ records == other
613
495
  end
614
496
  end
615
497
 
616
498
  def pretty_print(q)
617
- q.pp(self.to_a)
499
+ q.pp(records)
618
500
  end
619
501
 
620
502
  # Returns true if relation is blank.
621
503
  def blank?
622
- to_a.blank?
504
+ records.blank?
623
505
  end
624
506
 
625
507
  def values
626
- Hash[@values]
508
+ @values.dup
627
509
  end
628
510
 
629
511
  def inspect
630
- entries = to_a.take([limit_value, 11].compact.min).map!(&:inspect)
631
- entries[10] = '...' if entries.size == 11
512
+ subject = loaded? ? records : self
513
+ entries = subject.take([limit_value, 11].compact.min).map!(&:inspect)
514
+
515
+ entries[10] = "..." if entries.size == 11
632
516
 
633
517
  "#<#{self.class.name} [#{entries.join(', ')}]>"
634
518
  end
635
519
 
636
- private
520
+ def empty_scope? # :nodoc:
521
+ @values == klass.unscoped.values
522
+ end
523
+
524
+ def has_limit_or_offset? # :nodoc:
525
+ limit_value || offset_value
526
+ end
527
+
528
+ def alias_tracker(joins = [], aliases = nil) # :nodoc:
529
+ joins += [aliases] if aliases
530
+ ActiveRecord::Associations::AliasTracker.create(connection, table.name, joins)
531
+ end
637
532
 
638
- def exec_queries
639
- @records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel, arel.bind_values + bind_values)
533
+ protected
640
534
 
641
- preload = preload_values
642
- preload += includes_values unless eager_loading?
643
- preloader = build_preloader
644
- preload.each do |associations|
645
- preloader.preload @records, associations
535
+ def load_records(records)
536
+ @records = records.freeze
537
+ @loaded = true
646
538
  end
647
539
 
648
- @records.each { |record| record.readonly! } if readonly_value
540
+ private
649
541
 
650
- @loaded = true
651
- @records
652
- end
542
+ def has_join_values?
543
+ joins_values.any? || left_outer_joins_values.any?
544
+ end
653
545
 
654
- def build_preloader
655
- ActiveRecord::Associations::Preloader.new
656
- end
546
+ def exec_queries(&block)
547
+ skip_query_cache_if_necessary do
548
+ @records =
549
+ if eager_loading?
550
+ apply_join_dependency do |relation, join_dependency|
551
+ if ActiveRecord::NullRelation === relation
552
+ []
553
+ else
554
+ relation = join_dependency.apply_column_aliases(relation)
555
+ rows = connection.select_all(relation.arel, "SQL")
556
+ join_dependency.instantiate(rows, &block)
557
+ end.freeze
558
+ end
559
+ else
560
+ klass.find_by_sql(arel, &block).freeze
561
+ end
562
+
563
+ preload = preload_values
564
+ preload += includes_values unless eager_loading?
565
+ preloader = nil
566
+ preload.each do |associations|
567
+ preloader ||= build_preloader
568
+ preloader.preload @records, associations
569
+ end
657
570
 
658
- def references_eager_loaded_tables?
659
- joined_tables = arel.join_sources.map do |join|
660
- if join.is_a?(Arel::Nodes::StringJoin)
661
- tables_in_string(join.left)
571
+ @records.each(&:readonly!) if readonly_value
572
+
573
+ @loaded = true
574
+ @records
575
+ end
576
+ end
577
+
578
+ def skip_query_cache_if_necessary
579
+ if skip_query_cache_value
580
+ uncached do
581
+ yield
582
+ end
662
583
  else
663
- [join.left.table_name, join.left.table_alias]
584
+ yield
664
585
  end
665
586
  end
666
587
 
667
- joined_tables += [table.name, table.table_alias]
588
+ def build_preloader
589
+ ActiveRecord::Associations::Preloader.new
590
+ end
668
591
 
669
- # always convert table names to downcase as in Oracle quoted table names are in uppercase
670
- joined_tables = joined_tables.flatten.compact.map { |t| t.downcase }.uniq
592
+ def references_eager_loaded_tables?
593
+ joined_tables = arel.join_sources.map do |join|
594
+ if join.is_a?(Arel::Nodes::StringJoin)
595
+ tables_in_string(join.left)
596
+ else
597
+ [join.left.table_name, join.left.table_alias]
598
+ end
599
+ end
671
600
 
672
- (references_values - joined_tables).any?
673
- end
601
+ joined_tables += [table.name, table.table_alias]
674
602
 
675
- def tables_in_string(string)
676
- return [] if string.blank?
677
- # always convert table names to downcase as in Oracle quoted table names are in uppercase
678
- # ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
679
- string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map{ |s| s.downcase }.uniq - ['raw_sql_']
680
- end
603
+ # always convert table names to downcase as in Oracle quoted table names are in uppercase
604
+ joined_tables = joined_tables.flatten.compact.map(&:downcase).uniq
605
+
606
+ (references_values - joined_tables).any?
607
+ end
608
+
609
+ def tables_in_string(string)
610
+ return [] if string.blank?
611
+ # always convert table names to downcase as in Oracle quoted table names are in uppercase
612
+ # ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
613
+ string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map(&:downcase).uniq - ["raw_sql_"]
614
+ end
615
+
616
+ def values_for_create(attributes = nil)
617
+ result = attributes ? where_values_hash.merge!(attributes) : where_values_hash
618
+
619
+ # NOTE: if there are same keys in both create_with and result, create_with should be used.
620
+ # This is to make sure nested attributes don't get passed to the klass.new,
621
+ # while keeping the precedence of the duplicate keys in create_with.
622
+ create_with_value.stringify_keys.each do |k, v|
623
+ result[k] = v if result.key?(k)
624
+ end
625
+
626
+ result
627
+ end
681
628
  end
682
629
  end