activerecord 5.2.4.5 → 6.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (241) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +299 -739
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +1 -1
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +2 -1
  7. data/lib/active_record/aggregations.rb +4 -2
  8. data/lib/active_record/associations.rb +16 -12
  9. data/lib/active_record/associations/association.rb +35 -19
  10. data/lib/active_record/associations/association_scope.rb +4 -6
  11. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  13. data/lib/active_record/associations/builder/belongs_to.rb +14 -50
  14. data/lib/active_record/associations/builder/collection_association.rb +3 -3
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
  16. data/lib/active_record/associations/collection_association.rb +11 -25
  17. data/lib/active_record/associations/collection_proxy.rb +32 -6
  18. data/lib/active_record/associations/foreign_association.rb +7 -0
  19. data/lib/active_record/associations/has_many_association.rb +1 -1
  20. data/lib/active_record/associations/has_many_through_association.rb +25 -18
  21. data/lib/active_record/associations/has_one_association.rb +28 -30
  22. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  23. data/lib/active_record/associations/join_dependency.rb +15 -20
  24. data/lib/active_record/associations/join_dependency/join_association.rb +11 -26
  25. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  26. data/lib/active_record/associations/preloader.rb +32 -29
  27. data/lib/active_record/associations/preloader/association.rb +1 -2
  28. data/lib/active_record/associations/singular_association.rb +2 -16
  29. data/lib/active_record/attribute_assignment.rb +7 -10
  30. data/lib/active_record/attribute_methods.rb +34 -56
  31. data/lib/active_record/attribute_methods/dirty.rb +64 -26
  32. data/lib/active_record/attribute_methods/primary_key.rb +8 -7
  33. data/lib/active_record/attribute_methods/read.rb +16 -48
  34. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  35. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
  36. data/lib/active_record/attribute_methods/write.rb +15 -16
  37. data/lib/active_record/autosave_association.rb +7 -21
  38. data/lib/active_record/base.rb +2 -2
  39. data/lib/active_record/callbacks.rb +3 -17
  40. data/lib/active_record/collection_cache_key.rb +1 -1
  41. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +13 -36
  42. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  43. data/lib/active_record/connection_adapters/abstract/database_statements.rb +25 -84
  44. data/lib/active_record/connection_adapters/abstract/query_cache.rb +17 -14
  45. data/lib/active_record/connection_adapters/abstract/quoting.rb +5 -11
  46. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -11
  47. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -13
  48. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +0 -2
  49. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +41 -27
  50. data/lib/active_record/connection_adapters/abstract/transaction.rb +81 -52
  51. data/lib/active_record/connection_adapters/abstract_adapter.rb +95 -31
  52. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +65 -90
  53. data/lib/active_record/connection_adapters/connection_specification.rb +52 -42
  54. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +5 -9
  55. data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -7
  56. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  57. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
  58. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +65 -10
  59. data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -4
  60. data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
  61. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +16 -1
  62. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  63. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  64. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
  65. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
  66. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  67. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
  68. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  69. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  70. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +11 -36
  71. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +9 -2
  72. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +38 -20
  73. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -1
  74. data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -56
  75. data/lib/active_record/connection_adapters/schema_cache.rb +5 -0
  76. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +5 -5
  77. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +14 -9
  78. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +95 -62
  79. data/lib/active_record/connection_handling.rb +132 -26
  80. data/lib/active_record/core.rb +76 -43
  81. data/lib/active_record/counter_cache.rb +4 -29
  82. data/lib/active_record/database_configurations.rb +184 -0
  83. data/lib/active_record/database_configurations/database_config.rb +37 -0
  84. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  85. data/lib/active_record/database_configurations/url_config.rb +74 -0
  86. data/lib/active_record/enum.rb +22 -7
  87. data/lib/active_record/errors.rb +24 -21
  88. data/lib/active_record/explain.rb +1 -1
  89. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  90. data/lib/active_record/fixture_set/render_context.rb +17 -0
  91. data/lib/active_record/fixture_set/table_row.rb +153 -0
  92. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  93. data/lib/active_record/fixtures.rb +140 -472
  94. data/lib/active_record/gem_version.rb +4 -4
  95. data/lib/active_record/inheritance.rb +12 -2
  96. data/lib/active_record/integration.rb +56 -16
  97. data/lib/active_record/internal_metadata.rb +5 -1
  98. data/lib/active_record/locking/optimistic.rb +2 -2
  99. data/lib/active_record/locking/pessimistic.rb +3 -3
  100. data/lib/active_record/log_subscriber.rb +7 -26
  101. data/lib/active_record/migration.rb +38 -37
  102. data/lib/active_record/migration/command_recorder.rb +35 -5
  103. data/lib/active_record/migration/compatibility.rb +34 -16
  104. data/lib/active_record/model_schema.rb +30 -9
  105. data/lib/active_record/nested_attributes.rb +2 -2
  106. data/lib/active_record/no_touching.rb +7 -0
  107. data/lib/active_record/persistence.rb +18 -7
  108. data/lib/active_record/query_cache.rb +11 -4
  109. data/lib/active_record/querying.rb +19 -11
  110. data/lib/active_record/railtie.rb +71 -42
  111. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  112. data/lib/active_record/railties/controller_runtime.rb +30 -35
  113. data/lib/active_record/railties/databases.rake +94 -43
  114. data/lib/active_record/reflection.rb +60 -44
  115. data/lib/active_record/relation.rb +150 -69
  116. data/lib/active_record/relation/batches.rb +13 -10
  117. data/lib/active_record/relation/calculations.rb +38 -28
  118. data/lib/active_record/relation/delegation.rb +4 -13
  119. data/lib/active_record/relation/finder_methods.rb +12 -25
  120. data/lib/active_record/relation/merger.rb +2 -6
  121. data/lib/active_record/relation/predicate_builder.rb +4 -6
  122. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  123. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  124. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  125. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  126. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  127. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  128. data/lib/active_record/relation/query_attribute.rb +15 -12
  129. data/lib/active_record/relation/query_methods.rb +29 -52
  130. data/lib/active_record/relation/where_clause.rb +4 -0
  131. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  132. data/lib/active_record/result.rb +30 -11
  133. data/lib/active_record/sanitization.rb +2 -39
  134. data/lib/active_record/schema.rb +1 -10
  135. data/lib/active_record/schema_dumper.rb +12 -6
  136. data/lib/active_record/schema_migration.rb +4 -0
  137. data/lib/active_record/scoping.rb +9 -8
  138. data/lib/active_record/scoping/default.rb +10 -3
  139. data/lib/active_record/scoping/named.rb +10 -14
  140. data/lib/active_record/statement_cache.rb +32 -5
  141. data/lib/active_record/store.rb +39 -8
  142. data/lib/active_record/table_metadata.rb +1 -4
  143. data/lib/active_record/tasks/database_tasks.rb +89 -23
  144. data/lib/active_record/tasks/mysql_database_tasks.rb +2 -4
  145. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
  146. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
  147. data/lib/active_record/test_databases.rb +38 -0
  148. data/lib/active_record/test_fixtures.rb +224 -0
  149. data/lib/active_record/timestamp.rb +4 -6
  150. data/lib/active_record/transactions.rb +3 -22
  151. data/lib/active_record/translation.rb +1 -1
  152. data/lib/active_record/type.rb +3 -4
  153. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  154. data/lib/active_record/type_caster/connection.rb +1 -6
  155. data/lib/active_record/type_caster/map.rb +1 -4
  156. data/lib/active_record/validations/uniqueness.rb +13 -25
  157. data/lib/arel.rb +44 -0
  158. data/lib/arel/alias_predication.rb +9 -0
  159. data/lib/arel/attributes.rb +22 -0
  160. data/lib/arel/attributes/attribute.rb +37 -0
  161. data/lib/arel/collectors/bind.rb +24 -0
  162. data/lib/arel/collectors/composite.rb +31 -0
  163. data/lib/arel/collectors/plain_string.rb +20 -0
  164. data/lib/arel/collectors/sql_string.rb +20 -0
  165. data/lib/arel/collectors/substitute_binds.rb +28 -0
  166. data/lib/arel/crud.rb +42 -0
  167. data/lib/arel/delete_manager.rb +18 -0
  168. data/lib/arel/errors.rb +9 -0
  169. data/lib/arel/expressions.rb +29 -0
  170. data/lib/arel/factory_methods.rb +49 -0
  171. data/lib/arel/insert_manager.rb +49 -0
  172. data/lib/arel/math.rb +45 -0
  173. data/lib/arel/nodes.rb +67 -0
  174. data/lib/arel/nodes/and.rb +32 -0
  175. data/lib/arel/nodes/ascending.rb +23 -0
  176. data/lib/arel/nodes/binary.rb +52 -0
  177. data/lib/arel/nodes/bind_param.rb +36 -0
  178. data/lib/arel/nodes/case.rb +55 -0
  179. data/lib/arel/nodes/casted.rb +50 -0
  180. data/lib/arel/nodes/count.rb +12 -0
  181. data/lib/arel/nodes/delete_statement.rb +45 -0
  182. data/lib/arel/nodes/descending.rb +23 -0
  183. data/lib/arel/nodes/equality.rb +18 -0
  184. data/lib/arel/nodes/extract.rb +24 -0
  185. data/lib/arel/nodes/false.rb +16 -0
  186. data/lib/arel/nodes/full_outer_join.rb +8 -0
  187. data/lib/arel/nodes/function.rb +44 -0
  188. data/lib/arel/nodes/grouping.rb +8 -0
  189. data/lib/arel/nodes/in.rb +8 -0
  190. data/lib/arel/nodes/infix_operation.rb +80 -0
  191. data/lib/arel/nodes/inner_join.rb +8 -0
  192. data/lib/arel/nodes/insert_statement.rb +37 -0
  193. data/lib/arel/nodes/join_source.rb +20 -0
  194. data/lib/arel/nodes/matches.rb +18 -0
  195. data/lib/arel/nodes/named_function.rb +23 -0
  196. data/lib/arel/nodes/node.rb +50 -0
  197. data/lib/arel/nodes/node_expression.rb +13 -0
  198. data/lib/arel/nodes/outer_join.rb +8 -0
  199. data/lib/arel/nodes/over.rb +15 -0
  200. data/lib/arel/nodes/regexp.rb +16 -0
  201. data/lib/arel/nodes/right_outer_join.rb +8 -0
  202. data/lib/arel/nodes/select_core.rb +63 -0
  203. data/lib/arel/nodes/select_statement.rb +41 -0
  204. data/lib/arel/nodes/sql_literal.rb +16 -0
  205. data/lib/arel/nodes/string_join.rb +11 -0
  206. data/lib/arel/nodes/table_alias.rb +27 -0
  207. data/lib/arel/nodes/terminal.rb +16 -0
  208. data/lib/arel/nodes/true.rb +16 -0
  209. data/lib/arel/nodes/unary.rb +44 -0
  210. data/lib/arel/nodes/unary_operation.rb +20 -0
  211. data/lib/arel/nodes/unqualified_column.rb +22 -0
  212. data/lib/arel/nodes/update_statement.rb +41 -0
  213. data/lib/arel/nodes/values.rb +16 -0
  214. data/lib/arel/nodes/values_list.rb +24 -0
  215. data/lib/arel/nodes/window.rb +126 -0
  216. data/lib/arel/nodes/with.rb +11 -0
  217. data/lib/arel/order_predications.rb +13 -0
  218. data/lib/arel/predications.rb +257 -0
  219. data/lib/arel/select_manager.rb +271 -0
  220. data/lib/arel/table.rb +110 -0
  221. data/lib/arel/tree_manager.rb +72 -0
  222. data/lib/arel/update_manager.rb +34 -0
  223. data/lib/arel/visitors.rb +20 -0
  224. data/lib/arel/visitors/depth_first.rb +199 -0
  225. data/lib/arel/visitors/dot.rb +292 -0
  226. data/lib/arel/visitors/ibm_db.rb +21 -0
  227. data/lib/arel/visitors/informix.rb +56 -0
  228. data/lib/arel/visitors/mssql.rb +143 -0
  229. data/lib/arel/visitors/mysql.rb +83 -0
  230. data/lib/arel/visitors/oracle.rb +159 -0
  231. data/lib/arel/visitors/oracle12.rb +67 -0
  232. data/lib/arel/visitors/postgresql.rb +116 -0
  233. data/lib/arel/visitors/sqlite.rb +39 -0
  234. data/lib/arel/visitors/to_sql.rb +913 -0
  235. data/lib/arel/visitors/visitor.rb +42 -0
  236. data/lib/arel/visitors/where_sql.rb +23 -0
  237. data/lib/arel/window_predications.rb +9 -0
  238. data/lib/rails/generators/active_record/migration.rb +14 -1
  239. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  240. data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
  241. metadata +104 -26
@@ -13,33 +13,37 @@ module ActiveRecord
13
13
  class_attribute :aggregate_reflections, instance_writer: false, default: {}
14
14
  end
15
15
 
16
- def self.create(macro, name, scope, options, ar)
17
- klass = \
18
- case macro
19
- when :composed_of
20
- AggregateReflection
21
- when :has_many
22
- HasManyReflection
23
- when :has_one
24
- HasOneReflection
25
- when :belongs_to
26
- BelongsToReflection
27
- else
28
- raise "Unsupported Macro: #{macro}"
29
- end
16
+ class << self
17
+ def create(macro, name, scope, options, ar)
18
+ reflection = reflection_class_for(macro).new(name, scope, options, ar)
19
+ options[:through] ? ThroughReflection.new(reflection) : reflection
20
+ end
30
21
 
31
- reflection = klass.new(name, scope, options, ar)
32
- options[:through] ? ThroughReflection.new(reflection) : reflection
33
- end
22
+ def add_reflection(ar, name, reflection)
23
+ ar.clear_reflections_cache
24
+ name = name.to_s
25
+ ar._reflections = ar._reflections.except(name).merge!(name => reflection)
26
+ end
34
27
 
35
- def self.add_reflection(ar, name, reflection)
36
- ar.clear_reflections_cache
37
- name = name.to_s
38
- ar._reflections = ar._reflections.except(name).merge!(name => reflection)
39
- end
28
+ def add_aggregate_reflection(ar, name, reflection)
29
+ ar.aggregate_reflections = ar.aggregate_reflections.merge(name.to_s => reflection)
30
+ end
40
31
 
41
- def self.add_aggregate_reflection(ar, name, reflection)
42
- ar.aggregate_reflections = ar.aggregate_reflections.merge(name.to_s => reflection)
32
+ private
33
+ def reflection_class_for(macro)
34
+ case macro
35
+ when :composed_of
36
+ AggregateReflection
37
+ when :has_many
38
+ HasManyReflection
39
+ when :has_one
40
+ HasOneReflection
41
+ when :belongs_to
42
+ BelongsToReflection
43
+ else
44
+ raise "Unsupported Macro: #{macro}"
45
+ end
46
+ end
43
47
  end
44
48
 
45
49
  # \Reflection enables the ability to examine the associations and aggregations of
@@ -174,22 +178,26 @@ module ActiveRecord
174
178
  scope ? [scope] : []
175
179
  end
176
180
 
177
- def join_scope(table, foreign_table, foreign_klass)
178
- predicate_builder = predicate_builder(table)
179
- scope_chain_items = join_scopes(table, predicate_builder)
180
- klass_scope = klass_join_scope(table, predicate_builder)
181
-
181
+ def build_join_constraint(table, foreign_table)
182
182
  key = join_keys.key
183
183
  foreign_key = join_keys.foreign_key
184
184
 
185
- klass_scope.where!(table[key].eq(foreign_table[foreign_key]))
185
+ constraint = table[key].eq(foreign_table[foreign_key])
186
186
 
187
- if type
188
- klass_scope.where!(type => foreign_klass.polymorphic_name)
187
+ if klass.finder_needs_type_condition?
188
+ table.create_and([constraint, klass.send(:type_condition, table)])
189
+ else
190
+ constraint
189
191
  end
192
+ end
190
193
 
191
- if klass.finder_needs_type_condition?
192
- klass_scope.where!(klass.send(:type_condition, table))
194
+ def join_scope(table, foreign_klass)
195
+ predicate_builder = predicate_builder(table)
196
+ scope_chain_items = join_scopes(table, predicate_builder)
197
+ klass_scope = klass_join_scope(table, predicate_builder)
198
+
199
+ if type
200
+ klass_scope.where!(type => foreign_klass.polymorphic_name)
193
201
  end
194
202
 
195
203
  scope_chain_items.inject(klass_scope, &:merge!)
@@ -413,7 +421,7 @@ module ActiveRecord
413
421
  class AssociationReflection < MacroReflection #:nodoc:
414
422
  def compute_class(name)
415
423
  if polymorphic?
416
- raise ArgumentError, "Polymorphic association does not support to compute class."
424
+ raise ArgumentError, "Polymorphic associations do not support computing the class."
417
425
  end
418
426
  active_record.send(:compute_type, name)
419
427
  end
@@ -604,9 +612,21 @@ module ActiveRecord
604
612
 
605
613
  # returns either +nil+ or the inverse association name that it finds.
606
614
  def automatic_inverse_of
607
- if can_find_inverse_of_automatically?(self)
608
- inverse_name = ActiveSupport::Inflector.underscore(options[:as] || active_record.name.demodulize).to_sym
615
+ return unless can_find_inverse_of_automatically?(self)
616
+
617
+ inverse_name_candidates =
618
+ if options[:as]
619
+ [options[:as]]
620
+ else
621
+ active_record_name = active_record.name.demodulize
622
+ [active_record_name, ActiveSupport::Inflector.pluralize(active_record_name)]
623
+ end
609
624
 
625
+ inverse_name_candidates.map! do |candidate|
626
+ ActiveSupport::Inflector.underscore(candidate).to_sym
627
+ end
628
+
629
+ inverse_name_candidates.detect do |inverse_name|
610
630
  begin
611
631
  reflection = klass._reflect_on_association(inverse_name)
612
632
  rescue NameError
@@ -615,9 +635,7 @@ module ActiveRecord
615
635
  reflection = false
616
636
  end
617
637
 
618
- if valid_inverse_reflection?(reflection)
619
- return inverse_name
620
- end
638
+ valid_inverse_reflection?(reflection)
621
639
  end
622
640
  end
623
641
 
@@ -961,16 +979,14 @@ module ActiveRecord
961
979
  collect_join_reflections(seed + [self])
962
980
  end
963
981
 
964
- # TODO Change this to private once we've dropped Ruby 2.2 support.
965
- # Workaround for Ruby 2.2 "private attribute?" warning.
966
982
  protected
967
- attr_reader :delegate_reflection
968
-
969
983
  def actual_source_reflection # FIXME: this is a horrible name
970
984
  source_reflection.actual_source_reflection
971
985
  end
972
986
 
973
987
  private
988
+ attr_reader :delegate_reflection
989
+
974
990
  def collect_join_reflections(seed)
975
991
  a = source_reflection.add_as_source seed
976
992
  if options[:source_type]
@@ -9,6 +9,7 @@ module ActiveRecord
9
9
 
10
10
  SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :reordering,
11
11
  :reverse_order, :distinct, :create_with, :skip_query_cache]
12
+
12
13
  CLAUSE_METHODS = [:where, :having, :from]
13
14
  INVALID_METHODS_FOR_DELETE_ALL = [:distinct, :group, :having]
14
15
 
@@ -18,6 +19,7 @@ module ActiveRecord
18
19
  include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation
19
20
 
20
21
  attr_reader :table, :klass, :loaded, :predicate_builder
22
+ attr_accessor :skip_preloading_value
21
23
  alias :model :klass
22
24
  alias :loaded? :loaded
23
25
  alias :locked? :lock_value
@@ -41,6 +43,17 @@ module ActiveRecord
41
43
  klass.arel_attribute(name, table)
42
44
  end
43
45
 
46
+ def bind_attribute(name, value) # :nodoc:
47
+ if reflection = klass._reflect_on_association(name)
48
+ name = reflection.foreign_key
49
+ value = value.read_attribute(reflection.klass.primary_key) unless value.nil?
50
+ end
51
+
52
+ attr = arel_attribute(name)
53
+ bind = predicate_builder.build_bind_attribute(attr.name, value)
54
+ yield attr, bind
55
+ end
56
+
44
57
  # Initializes new record from relation while maintaining the current
45
58
  # scope.
46
59
  #
@@ -54,7 +67,7 @@ module ActiveRecord
54
67
  # user = users.new { |user| user.name = 'Oscar' }
55
68
  # user.name # => Oscar
56
69
  def new(attributes = nil, &block)
57
- scoping { klass.new(values_for_create(attributes), &block) }
70
+ scoping { klass.new(attributes, &block) }
58
71
  end
59
72
 
60
73
  alias build new
@@ -79,11 +92,7 @@ module ActiveRecord
79
92
  # users.create(name: nil) # validation on name
80
93
  # # => #<User id: nil, name: nil, ...>
81
94
  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
95
+ scoping { klass.create(attributes, &block) }
87
96
  end
88
97
 
89
98
  # Similar to #create, but calls
@@ -93,11 +102,7 @@ module ActiveRecord
93
102
  # Expects arguments in the same format as
94
103
  # {ActiveRecord::Base.create!}[rdoc-ref:Persistence::ClassMethods#create!].
95
104
  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
105
+ scoping { klass.create!(attributes, &block) }
101
106
  end
102
107
 
103
108
  def first_or_create(attributes = nil, &block) # :nodoc:
@@ -143,23 +148,12 @@ module ActiveRecord
143
148
  # failed due to validation errors it won't be persisted, you get what
144
149
  # #create returns in such situation.
145
150
  #
146
- # Please note *this method is not atomic*, it runs first a SELECT, and if
151
+ # Please note <b>this method is not atomic</b>, it runs first a SELECT, and if
147
152
  # there are no results an INSERT is attempted. If there are other threads
148
153
  # or processes there is a race condition between both calls and it could
149
154
  # be the case that you end up with two similar records.
150
155
  #
151
- # Whether that is a problem or not depends on the logic of the
152
- # application, but in the particular case in which rows have a UNIQUE
153
- # constraint an exception may be raised, just retry:
154
- #
155
- # begin
156
- # CreditAccount.transaction(requires_new: true) do
157
- # CreditAccount.find_or_create_by(user_id: user.id)
158
- # end
159
- # rescue ActiveRecord::RecordNotUnique
160
- # retry
161
- # end
162
- #
156
+ # If this might be a problem for your application, please see #create_or_find_by.
163
157
  def find_or_create_by(attributes, &block)
164
158
  find_by(attributes) || create(attributes, &block)
165
159
  end
@@ -171,6 +165,47 @@ module ActiveRecord
171
165
  find_by(attributes) || create!(attributes, &block)
172
166
  end
173
167
 
168
+ # Attempts to create a record with the given attributes in a table that has a unique constraint
169
+ # on one or several of its columns. If a row already exists with one or several of these
170
+ # unique constraints, the exception such an insertion would normally raise is caught,
171
+ # and the existing record with those attributes is found using #find_by!.
172
+ #
173
+ # This is similar to #find_or_create_by, but avoids the problem of stale reads between the SELECT
174
+ # and the INSERT, as that method needs to first query the table, then attempt to insert a row
175
+ # if none is found.
176
+ #
177
+ # There are several drawbacks to #create_or_find_by, though:
178
+ #
179
+ # * The underlying table must have the relevant columns defined with unique constraints.
180
+ # * A unique constraint violation may be triggered by only one, or at least less than all,
181
+ # of the given attributes. This means that the subsequent #find_by! may fail to find a
182
+ # matching record, which will then raise an <tt>ActiveRecord::RecordNotFound</tt> exception,
183
+ # rather than a record with the given attributes.
184
+ # * While we avoid the race condition between SELECT -> INSERT from #find_or_create_by,
185
+ # we actually have another race condition between INSERT -> SELECT, which can be triggered
186
+ # if a DELETE between those two statements is run by another client. But for most applications,
187
+ # that's a significantly less likely condition to hit.
188
+ # * It relies on exception handling to handle control flow, which may be marginally slower.
189
+ #
190
+ # This method will return a record if all given attributes are covered by unique constraints
191
+ # (unless the INSERT -> DELETE -> SELECT race condition is triggered), but if creation was attempted
192
+ # and failed due to validation errors it won't be persisted, you get what #create returns in
193
+ # such situation.
194
+ def create_or_find_by(attributes, &block)
195
+ transaction(requires_new: true) { create(attributes, &block) }
196
+ rescue ActiveRecord::RecordNotUnique
197
+ find_by!(attributes)
198
+ end
199
+
200
+ # Like #create_or_find_by, but calls
201
+ # {create!}[rdoc-ref:Persistence::ClassMethods#create!] so an exception
202
+ # is raised if the created record is invalid.
203
+ def create_or_find_by!(attributes, &block)
204
+ transaction(requires_new: true) { create!(attributes, &block) }
205
+ rescue ActiveRecord::RecordNotUnique
206
+ find_by!(attributes)
207
+ end
208
+
174
209
  # Like #find_or_create_by, but calls {new}[rdoc-ref:Core#new]
175
210
  # instead of {create}[rdoc-ref:Persistence::ClassMethods#create].
176
211
  def find_or_initialize_by(attributes, &block)
@@ -185,7 +220,7 @@ module ActiveRecord
185
220
  # are needed by the next ones when eager loading is going on.
186
221
  #
187
222
  # Please see further details in the
188
- # {Active Record Query Interface guide}[http://guides.rubyonrails.org/active_record_querying.html#running-explain].
223
+ # {Active Record Query Interface guide}[https://guides.rubyonrails.org/active_record_querying.html#running-explain].
189
224
  def explain
190
225
  exec_explain(collecting_queries_for_explain { exec_queries })
191
226
  end
@@ -277,10 +312,7 @@ module ActiveRecord
277
312
  # Please check unscoped if you want to remove all previous scopes (including
278
313
  # the default_scope) during the execution of a block.
279
314
  def scoping
280
- previous, klass.current_scope = klass.current_scope(true), self unless @delegate_to_klass
281
- yield
282
- ensure
283
- klass.current_scope = previous unless @delegate_to_klass
315
+ @delegate_to_klass ? yield : klass._scoping(self) { yield }
284
316
  end
285
317
 
286
318
  def _exec_scope(*args, &block) # :nodoc:
@@ -321,17 +353,17 @@ module ActiveRecord
321
353
  end
322
354
 
323
355
  stmt = Arel::UpdateManager.new
324
-
325
- stmt.set Arel.sql(@klass.sanitize_sql_for_assignment(updates))
326
- stmt.table(table)
327
-
328
- if has_join_values? || offset_value
329
- @klass.connection.join_to_update(stmt, arel, arel_attribute(primary_key))
356
+ stmt.table(arel.join_sources.empty? ? table : arel.source)
357
+ stmt.key = arel_attribute(primary_key)
358
+ stmt.take(arel.limit)
359
+ stmt.offset(arel.offset)
360
+ stmt.order(*arel.orders)
361
+ stmt.wheres = arel.constraints
362
+
363
+ if updates.is_a?(Hash)
364
+ stmt.set _substitute_values(updates)
330
365
  else
331
- stmt.key = arel_attribute(primary_key)
332
- stmt.take(arel.limit)
333
- stmt.order(*arel.orders)
334
- stmt.wheres = arel.constraints
366
+ stmt.set Arel.sql(klass.sanitize_sql_for_assignment(updates, table.name))
335
367
  end
336
368
 
337
369
  @klass.connection.update stmt, "#{@klass} Update All"
@@ -345,6 +377,59 @@ module ActiveRecord
345
377
  end
346
378
  end
347
379
 
380
+ def update_counters(counters) # :nodoc:
381
+ touch = counters.delete(:touch)
382
+
383
+ updates = {}
384
+ counters.each do |counter_name, value|
385
+ attr = arel_attribute(counter_name)
386
+ bind = predicate_builder.build_bind_attribute(attr.name, value.abs)
387
+ expr = table.coalesce(Arel::Nodes::UnqualifiedColumn.new(attr), 0)
388
+ expr = value < 0 ? expr - bind : expr + bind
389
+ updates[counter_name] = expr.expr
390
+ end
391
+
392
+ if touch
393
+ names = touch if touch != true
394
+ touch_updates = klass.touch_attributes_with_time(*names)
395
+ updates.merge!(touch_updates) unless touch_updates.empty?
396
+ end
397
+
398
+ update_all updates
399
+ end
400
+
401
+ # Touches all records in the current relation without instantiating records first with the updated_at/on attributes
402
+ # set to the current time or the time specified.
403
+ # This method can be passed attribute names and an optional time argument.
404
+ # If attribute names are passed, they are updated along with updated_at/on attributes.
405
+ # If no time argument is passed, the current time is used as default.
406
+ #
407
+ # === Examples
408
+ #
409
+ # # Touch all records
410
+ # Person.all.touch_all
411
+ # # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670'"
412
+ #
413
+ # # Touch multiple records with a custom attribute
414
+ # Person.all.touch_all(:created_at)
415
+ # # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670', \"created_at\" = '2018-01-04 22:55:23.132670'"
416
+ #
417
+ # # Touch multiple records with a specified time
418
+ # Person.all.touch_all(time: Time.new(2020, 5, 16, 0, 0, 0))
419
+ # # => "UPDATE \"people\" SET \"updated_at\" = '2020-05-16 00:00:00'"
420
+ #
421
+ # # Touch records with scope
422
+ # Person.where(name: 'David').touch_all
423
+ # # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670' WHERE \"people\".\"name\" = 'David'"
424
+ def touch_all(*names, time: nil)
425
+ if klass.locking_enabled?
426
+ names << { time: time }
427
+ update_counters(klass.locking_column => 1, touch: names)
428
+ else
429
+ update_all klass.touch_attributes_with_time(*names, time: time)
430
+ end
431
+ end
432
+
348
433
  # Destroys the records by instantiating each
349
434
  # record and calling its {#destroy}[rdoc-ref:Persistence#destroy] method.
350
435
  # Each object's callbacks are executed (including <tt>:dependent</tt> association options).
@@ -398,13 +483,12 @@ module ActiveRecord
398
483
  end
399
484
 
400
485
  stmt = Arel::DeleteManager.new
401
- stmt.from(table)
402
-
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
407
- end
486
+ stmt.from(arel.join_sources.empty? ? table : arel.source)
487
+ stmt.key = arel_attribute(primary_key)
488
+ stmt.take(arel.limit)
489
+ stmt.offset(arel.offset)
490
+ stmt.order(*arel.orders)
491
+ stmt.wheres = arel.constraints
408
492
 
409
493
  affected = @klass.connection.delete(stmt, "#{@klass} Destroy")
410
494
 
@@ -530,6 +614,16 @@ module ActiveRecord
530
614
  ActiveRecord::Associations::AliasTracker.create(connection, table.name, joins)
531
615
  end
532
616
 
617
+ def preload_associations(records) # :nodoc:
618
+ preload = preload_values
619
+ preload += includes_values unless eager_loading?
620
+ preloader = nil
621
+ preload.each do |associations|
622
+ preloader ||= build_preloader
623
+ preloader.preload records, associations
624
+ end
625
+ end
626
+
533
627
  protected
534
628
 
535
629
  def load_records(records)
@@ -538,9 +632,15 @@ module ActiveRecord
538
632
  end
539
633
 
540
634
  private
541
-
542
- def has_join_values?
543
- joins_values.any? || left_outer_joins_values.any?
635
+ def _substitute_values(values)
636
+ values.map do |name, value|
637
+ attr = arel_attribute(name)
638
+ unless Arel.arel_node?(value)
639
+ type = klass.type_for_attribute(attr.name)
640
+ value = predicate_builder.build_bind_attribute(attr.name, type.cast(value))
641
+ end
642
+ [attr, value]
643
+ end
544
644
  end
545
645
 
546
646
  def exec_queries(&block)
@@ -560,13 +660,7 @@ module ActiveRecord
560
660
  klass.find_by_sql(arel, &block).freeze
561
661
  end
562
662
 
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
663
+ preload_associations(@records) unless skip_preloading_value
570
664
 
571
665
  @records.each(&:readonly!) if readonly_value
572
666
 
@@ -612,18 +706,5 @@ module ActiveRecord
612
706
  # ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
613
707
  string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map(&:downcase).uniq - ["raw_sql_"]
614
708
  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
628
709
  end
629
710
  end