activerecord 4.2.0 → 5.2.8.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 +5 -5
  2. data/CHANGELOG.md +640 -928
  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 +264 -247
  8. data/lib/active_record/association_relation.rb +24 -6
  9. data/lib/active_record/associations/alias_tracker.rb +29 -35
  10. data/lib/active_record/associations/association.rb +87 -41
  11. data/lib/active_record/associations/association_scope.rb +106 -132
  12. data/lib/active_record/associations/belongs_to_association.rb +55 -36
  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 +14 -23
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +50 -39
  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 +145 -266
  22. data/lib/active_record/associations/collection_proxy.rb +242 -138
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +35 -75
  25. data/lib/active_record/associations/has_many_through_association.rb +51 -69
  26. data/lib/active_record/associations/has_one_association.rb +39 -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 -81
  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 +134 -154
  32. data/lib/active_record/associations/preloader/association.rb +85 -116
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -74
  34. data/lib/active_record/associations/preloader.rb +83 -93
  35. data/lib/active_record/associations/singular_association.rb +27 -40
  36. data/lib/active_record/associations/through_association.rb +48 -23
  37. data/lib/active_record/associations.rb +1732 -1596
  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 +12 -5
  41. data/lib/active_record/attribute_methods/dirty.rb +94 -125
  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 +62 -36
  47. data/lib/active_record/attribute_methods/write.rb +31 -46
  48. data/lib/active_record/attribute_methods.rb +170 -117
  49. data/lib/active_record/attributes.rb +201 -74
  50. data/lib/active_record/autosave_association.rb +118 -45
  51. data/lib/active_record/base.rb +60 -48
  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 +37 -13
  55. data/lib/active_record/collection_cache_key.rb +53 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -284
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +254 -87
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +72 -22
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -52
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +6 -4
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +328 -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 +617 -212
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +139 -75
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +332 -191
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +567 -563
  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 +42 -195
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +46 -115
  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 -57
  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 +5 -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 -13
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +7 -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 +7 -9
  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 -1
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +65 -51
  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 +466 -280
  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 +439 -330
  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 -324
  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 +205 -202
  129. data/lib/active_record/counter_cache.rb +80 -37
  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 +136 -90
  133. data/lib/active_record/errors.rb +180 -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 +11 -6
  137. data/lib/active_record/fixture_set/file.rb +35 -9
  138. data/lib/active_record/fixtures.rb +193 -135
  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 +48 -0
  144. data/lib/active_record/locale/en.yml +3 -2
  145. data/lib/active_record/locking/optimistic.rb +92 -98
  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 +594 -267
  152. data/lib/active_record/model_schema.rb +292 -111
  153. data/lib/active_record/nested_attributes.rb +266 -214
  154. data/lib/active_record/no_touching.rb +8 -2
  155. data/lib/active_record/null_relation.rb +24 -37
  156. data/lib/active_record/persistence.rb +350 -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 +117 -35
  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 +160 -174
  163. data/lib/active_record/readonly_attributes.rb +5 -4
  164. data/lib/active_record/reflection.rb +447 -288
  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 +259 -244
  168. data/lib/active_record/relation/delegation.rb +67 -60
  169. data/lib/active_record/relation/finder_methods.rb +290 -253
  170. data/lib/active_record/relation/from_clause.rb +26 -0
  171. data/lib/active_record/relation/merger.rb +91 -68
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -23
  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 +118 -92
  180. data/lib/active_record/relation/query_attribute.rb +45 -0
  181. data/lib/active_record/relation/query_methods.rb +446 -389
  182. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  183. data/lib/active_record/relation/spawn_methods.rb +18 -16
  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 -339
  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 -19
  193. data/lib/active_record/scoping/default.rb +102 -84
  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 +136 -95
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +59 -89
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +84 -31
  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 +208 -123
  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 -41
  212. data/lib/active_record/type/date_time.rb +4 -38
  213. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  214. data/lib/active_record/type/hash_lookup_type_map.rb +13 -5
  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 +30 -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 +41 -32
  231. data/lib/active_record/validations.rb +38 -35
  232. data/lib/active_record/version.rb +3 -1
  233. data/lib/active_record.rb +36 -21
  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 -6
  238. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -7
  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.rb +7 -5
  243. metadata +77 -53
  244. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  245. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  246. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  247. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  248. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  249. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  250. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  251. data/lib/active_record/attribute.rb +0 -149
  252. data/lib/active_record/attribute_set/builder.rb +0 -86
  253. data/lib/active_record/attribute_set.rb +0 -77
  254. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  255. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  256. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  257. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  258. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  259. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  260. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  261. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  262. data/lib/active_record/type/big_integer.rb +0 -13
  263. data/lib/active_record/type/binary.rb +0 -50
  264. data/lib/active_record/type/boolean.rb +0 -30
  265. data/lib/active_record/type/decimal.rb +0 -40
  266. data/lib/active_record/type/decorator.rb +0 -14
  267. data/lib/active_record/type/float.rb +0 -19
  268. data/lib/active_record/type/integer.rb +0 -55
  269. data/lib/active_record/type/mutable.rb +0 -16
  270. data/lib/active_record/type/numeric.rb +0 -36
  271. data/lib/active_record/type/string.rb +0 -36
  272. data/lib/active_record/type/time_value.rb +0 -38
  273. data/lib/active_record/type/value.rb +0 -101
  274. /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -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,126 +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
- affected = @klass.connection.delete(stmt, 'SQL', bind_values)
400
+ stmt = Arel::DeleteManager.new
401
+ stmt.from(table)
478
402
 
479
- reset
480
- 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
481
407
  end
482
- end
483
408
 
484
- # Deletes the row with a primary key matching the +id+ argument, using a
485
- # SQL +DELETE+ statement, and returns the number of rows deleted. Active
486
- # Record objects are not instantiated, so the object's callbacks are not
487
- # executed, including any <tt>:dependent</tt> association options.
488
- #
489
- # You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
490
- #
491
- # Note: Although it is often much faster than the alternative,
492
- # <tt>#destroy</tt>, skipping callbacks might bypass business logic in
493
- # your application that ensures referential integrity or performs other
494
- # essential jobs.
495
- #
496
- # ==== Examples
497
- #
498
- # # Delete a single row
499
- # Todo.delete(1)
500
- #
501
- # # Delete multiple rows
502
- # Todo.delete([2,3,4])
503
- def delete(id_or_array)
504
- where(primary_key => id_or_array).delete_all
409
+ affected = @klass.connection.delete(stmt, "#{@klass} Destroy")
410
+
411
+ reset
412
+ affected
505
413
  end
506
414
 
507
415
  # Causes the records to be loaded from the database if they have not
@@ -510,8 +418,8 @@ module ActiveRecord
510
418
  # return value is the relation itself, not the records.
511
419
  #
512
420
  # Post.where(published: true).load # => #<ActiveRecord::Relation>
513
- def load
514
- exec_queries unless loaded?
421
+ def load(&block)
422
+ exec_queries(&block) unless loaded?
515
423
 
516
424
  self
517
425
  end
@@ -523,9 +431,9 @@ module ActiveRecord
523
431
  end
524
432
 
525
433
  def reset
526
- @last = @to_sql = @order_clause = @scope_for_create = @arel = @loaded = nil
527
- @should_eager_load = @join_dependency = nil
528
- @records = []
434
+ @delegate_to_klass = false
435
+ @to_sql = @arel = @loaded = @should_eager_load = nil
436
+ @records = [].freeze
529
437
  @offsets = {}
530
438
  self
531
439
  end
@@ -536,47 +444,28 @@ module ActiveRecord
536
444
  # # => SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
537
445
  def to_sql
538
446
  @to_sql ||= begin
539
- relation = self
540
- connection = klass.connection
541
- visitor = connection.visitor
542
-
543
- if eager_loading?
544
- find_with_associations { |rel| relation = rel }
545
- end
546
-
547
- arel = relation.arel
548
- binds = (arel.bind_values + relation.bind_values).dup
549
- binds.map! { |bv| connection.quote(*bv.reverse) }
550
- collect = visitor.accept(arel.ast, Arel::Collectors::Bind.new)
551
- collect.substitute_binds(binds).join
552
- 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
553
457
  end
554
458
 
555
459
  # Returns a hash of where conditions.
556
460
  #
557
461
  # User.where(name: 'Oscar').where_values_hash
558
462
  # # => {name: "Oscar"}
559
- def where_values_hash(relation_table_name = table_name)
560
- equalities = where_values.grep(Arel::Nodes::Equality).find_all { |node|
561
- node.left.relation.name == relation_table_name
562
- }
563
-
564
- binds = Hash[bind_values.find_all(&:first).map { |column, v| [column.name, v] }]
565
-
566
- Hash[equalities.map { |where|
567
- name = where.left.name
568
- [name, binds.fetch(name.to_s) {
569
- case where.right
570
- when Array then where.right.map(&:val)
571
- else
572
- where.right.val
573
- end
574
- }]
575
- }]
463
+ def where_values_hash(relation_table_name = klass.table_name)
464
+ where_clause.to_h(relation_table_name)
576
465
  end
577
466
 
578
467
  def scope_for_create
579
- @scope_for_create ||= where_values_hash.merge(create_with_value)
468
+ where_values_hash.merge!(create_with_value.stringify_keys)
580
469
  end
581
470
 
582
471
  # Returns true if relation needs eager loading.
@@ -594,88 +483,147 @@ module ActiveRecord
594
483
  includes_values & joins_values
595
484
  end
596
485
 
597
- # +uniq+ and +uniq!+ are silently deprecated. +uniq_value+ delegates to +distinct_value+
598
- # to maintain backwards compatibility. Use +distinct_value+ instead.
599
- def uniq_value
600
- distinct_value
601
- end
602
-
603
486
  # Compares two relations for equality.
604
487
  def ==(other)
605
488
  case other
606
489
  when Associations::CollectionProxy, AssociationRelation
607
- self == other.to_a
490
+ self == other.records
608
491
  when Relation
609
492
  other.to_sql == to_sql
610
493
  when Array
611
- to_a == other
494
+ records == other
612
495
  end
613
496
  end
614
497
 
615
498
  def pretty_print(q)
616
- q.pp(self.to_a)
499
+ q.pp(records)
617
500
  end
618
501
 
619
502
  # Returns true if relation is blank.
620
503
  def blank?
621
- to_a.blank?
504
+ records.blank?
622
505
  end
623
506
 
624
507
  def values
625
- Hash[@values]
508
+ @values.dup
626
509
  end
627
510
 
628
511
  def inspect
629
- entries = to_a.take([limit_value, 11].compact.min).map!(&:inspect)
630
- 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
631
516
 
632
517
  "#<#{self.class.name} [#{entries.join(', ')}]>"
633
518
  end
634
519
 
635
- 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
636
532
 
637
- def exec_queries
638
- @records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel, arel.bind_values + bind_values)
533
+ protected
639
534
 
640
- preload = preload_values
641
- preload += includes_values unless eager_loading?
642
- preloader = build_preloader
643
- preload.each do |associations|
644
- preloader.preload @records, associations
535
+ def load_records(records)
536
+ @records = records.freeze
537
+ @loaded = true
645
538
  end
646
539
 
647
- @records.each { |record| record.readonly! } if readonly_value
540
+ private
648
541
 
649
- @loaded = true
650
- @records
651
- end
542
+ def has_join_values?
543
+ joins_values.any? || left_outer_joins_values.any?
544
+ end
652
545
 
653
- def build_preloader
654
- ActiveRecord::Associations::Preloader.new
655
- 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
570
+
571
+ @records.each(&:readonly!) if readonly_value
656
572
 
657
- def references_eager_loaded_tables?
658
- joined_tables = arel.join_sources.map do |join|
659
- if join.is_a?(Arel::Nodes::StringJoin)
660
- tables_in_string(join.left)
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
661
583
  else
662
- [join.left.table_name, join.left.table_alias]
584
+ yield
663
585
  end
664
586
  end
665
587
 
666
- joined_tables += [table.name, table.table_alias]
588
+ def build_preloader
589
+ ActiveRecord::Associations::Preloader.new
590
+ end
667
591
 
668
- # always convert table names to downcase as in Oracle quoted table names are in uppercase
669
- 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
670
600
 
671
- (references_values - joined_tables).any?
672
- end
601
+ joined_tables += [table.name, table.table_alias]
673
602
 
674
- def tables_in_string(string)
675
- return [] if string.blank?
676
- # always convert table names to downcase as in Oracle quoted table names are in uppercase
677
- # ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
678
- string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map{ |s| s.downcase }.uniq - ['raw_sql_']
679
- 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
680
628
  end
681
629
  end