activerecord 6.0.6.1 → 6.1.7.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (243) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1152 -779
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -2
  5. data/lib/active_record/aggregations.rb +5 -5
  6. data/lib/active_record/association_relation.rb +30 -12
  7. data/lib/active_record/associations/alias_tracker.rb +19 -15
  8. data/lib/active_record/associations/association.rb +49 -26
  9. data/lib/active_record/associations/association_scope.rb +18 -20
  10. data/lib/active_record/associations/belongs_to_association.rb +23 -10
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -3
  12. data/lib/active_record/associations/builder/association.rb +32 -5
  13. data/lib/active_record/associations/builder/belongs_to.rb +10 -7
  14. data/lib/active_record/associations/builder/collection_association.rb +5 -4
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -1
  16. data/lib/active_record/associations/builder/has_many.rb +6 -2
  17. data/lib/active_record/associations/builder/has_one.rb +11 -14
  18. data/lib/active_record/associations/builder/singular_association.rb +1 -1
  19. data/lib/active_record/associations/collection_association.rb +32 -18
  20. data/lib/active_record/associations/collection_proxy.rb +12 -5
  21. data/lib/active_record/associations/foreign_association.rb +13 -0
  22. data/lib/active_record/associations/has_many_association.rb +24 -2
  23. data/lib/active_record/associations/has_many_through_association.rb +10 -4
  24. data/lib/active_record/associations/has_one_association.rb +15 -1
  25. data/lib/active_record/associations/join_dependency/join_association.rb +37 -21
  26. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  27. data/lib/active_record/associations/join_dependency.rb +63 -49
  28. data/lib/active_record/associations/preloader/association.rb +14 -8
  29. data/lib/active_record/associations/preloader/through_association.rb +1 -1
  30. data/lib/active_record/associations/preloader.rb +5 -3
  31. data/lib/active_record/associations/singular_association.rb +1 -1
  32. data/lib/active_record/associations.rb +118 -11
  33. data/lib/active_record/attribute_assignment.rb +10 -8
  34. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
  35. data/lib/active_record/attribute_methods/dirty.rb +1 -11
  36. data/lib/active_record/attribute_methods/primary_key.rb +6 -2
  37. data/lib/active_record/attribute_methods/query.rb +3 -6
  38. data/lib/active_record/attribute_methods/read.rb +8 -11
  39. data/lib/active_record/attribute_methods/serialization.rb +11 -5
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -13
  41. data/lib/active_record/attribute_methods/write.rb +12 -20
  42. data/lib/active_record/attribute_methods.rb +64 -54
  43. data/lib/active_record/attributes.rb +33 -8
  44. data/lib/active_record/autosave_association.rb +47 -30
  45. data/lib/active_record/base.rb +2 -14
  46. data/lib/active_record/callbacks.rb +152 -22
  47. data/lib/active_record/coders/yaml_column.rb +1 -1
  48. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +185 -134
  49. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  50. data/lib/active_record/connection_adapters/abstract/database_statements.rb +66 -23
  51. data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -8
  52. data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
  53. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  54. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
  55. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +114 -26
  56. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +228 -83
  58. data/lib/active_record/connection_adapters/abstract/transaction.rb +92 -33
  59. data/lib/active_record/connection_adapters/abstract_adapter.rb +52 -76
  60. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +123 -87
  61. data/lib/active_record/connection_adapters/column.rb +15 -1
  62. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  63. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +24 -24
  65. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
  66. data/lib/active_record/connection_adapters/mysql/quoting.rb +18 -3
  67. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -6
  68. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  69. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +5 -2
  70. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +7 -4
  71. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  72. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -12
  73. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  74. data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
  75. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  76. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +14 -53
  77. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  78. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  79. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
  80. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
  84. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  86. data/lib/active_record/connection_adapters/postgresql/quoting.rb +30 -4
  87. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  88. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
  89. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
  90. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  91. data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -64
  92. data/lib/active_record/connection_adapters/schema_cache.rb +130 -15
  93. data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
  94. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +32 -5
  95. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
  96. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  97. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +36 -3
  98. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +48 -50
  99. data/lib/active_record/connection_adapters.rb +52 -0
  100. data/lib/active_record/connection_handling.rb +218 -71
  101. data/lib/active_record/core.rb +264 -63
  102. data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -0
  103. data/lib/active_record/database_configurations/database_config.rb +52 -9
  104. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  105. data/lib/active_record/database_configurations/url_config.rb +15 -40
  106. data/lib/active_record/database_configurations.rb +125 -85
  107. data/lib/active_record/delegated_type.rb +209 -0
  108. data/lib/active_record/destroy_association_async_job.rb +36 -0
  109. data/lib/active_record/enum.rb +69 -34
  110. data/lib/active_record/errors.rb +47 -12
  111. data/lib/active_record/explain.rb +9 -4
  112. data/lib/active_record/explain_subscriber.rb +1 -1
  113. data/lib/active_record/fixture_set/file.rb +10 -17
  114. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  115. data/lib/active_record/fixture_set/render_context.rb +1 -1
  116. data/lib/active_record/fixture_set/table_row.rb +2 -2
  117. data/lib/active_record/fixtures.rb +58 -9
  118. data/lib/active_record/gem_version.rb +3 -3
  119. data/lib/active_record/inheritance.rb +40 -18
  120. data/lib/active_record/insert_all.rb +38 -5
  121. data/lib/active_record/integration.rb +3 -5
  122. data/lib/active_record/internal_metadata.rb +18 -7
  123. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  124. data/lib/active_record/locking/optimistic.rb +24 -17
  125. data/lib/active_record/locking/pessimistic.rb +6 -2
  126. data/lib/active_record/log_subscriber.rb +27 -8
  127. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  128. data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
  129. data/lib/active_record/middleware/database_selector.rb +4 -1
  130. data/lib/active_record/migration/command_recorder.rb +47 -27
  131. data/lib/active_record/migration/compatibility.rb +72 -18
  132. data/lib/active_record/migration.rb +114 -84
  133. data/lib/active_record/model_schema.rb +89 -14
  134. data/lib/active_record/nested_attributes.rb +2 -3
  135. data/lib/active_record/no_touching.rb +1 -1
  136. data/lib/active_record/persistence.rb +50 -45
  137. data/lib/active_record/query_cache.rb +15 -5
  138. data/lib/active_record/querying.rb +11 -6
  139. data/lib/active_record/railtie.rb +64 -44
  140. data/lib/active_record/railties/console_sandbox.rb +2 -4
  141. data/lib/active_record/railties/databases.rake +279 -101
  142. data/lib/active_record/readonly_attributes.rb +4 -0
  143. data/lib/active_record/reflection.rb +60 -44
  144. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  145. data/lib/active_record/relation/batches.rb +38 -31
  146. data/lib/active_record/relation/calculations.rb +104 -43
  147. data/lib/active_record/relation/finder_methods.rb +44 -14
  148. data/lib/active_record/relation/from_clause.rb +1 -1
  149. data/lib/active_record/relation/merger.rb +20 -23
  150. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  151. data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
  152. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -6
  153. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  154. data/lib/active_record/relation/predicate_builder.rb +61 -38
  155. data/lib/active_record/relation/query_methods.rb +322 -196
  156. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  157. data/lib/active_record/relation/spawn_methods.rb +8 -7
  158. data/lib/active_record/relation/where_clause.rb +111 -61
  159. data/lib/active_record/relation.rb +100 -81
  160. data/lib/active_record/result.rb +41 -33
  161. data/lib/active_record/runtime_registry.rb +2 -2
  162. data/lib/active_record/sanitization.rb +6 -17
  163. data/lib/active_record/schema_dumper.rb +34 -4
  164. data/lib/active_record/schema_migration.rb +2 -8
  165. data/lib/active_record/scoping/default.rb +1 -3
  166. data/lib/active_record/scoping/named.rb +1 -17
  167. data/lib/active_record/secure_token.rb +16 -8
  168. data/lib/active_record/serialization.rb +5 -3
  169. data/lib/active_record/signed_id.rb +116 -0
  170. data/lib/active_record/statement_cache.rb +20 -4
  171. data/lib/active_record/store.rb +8 -3
  172. data/lib/active_record/suppressor.rb +2 -2
  173. data/lib/active_record/table_metadata.rb +42 -51
  174. data/lib/active_record/tasks/database_tasks.rb +140 -113
  175. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
  176. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
  177. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
  178. data/lib/active_record/test_databases.rb +5 -4
  179. data/lib/active_record/test_fixtures.rb +79 -31
  180. data/lib/active_record/timestamp.rb +4 -6
  181. data/lib/active_record/touch_later.rb +21 -21
  182. data/lib/active_record/transactions.rb +19 -66
  183. data/lib/active_record/type/serialized.rb +6 -2
  184. data/lib/active_record/type.rb +8 -1
  185. data/lib/active_record/type_caster/connection.rb +0 -1
  186. data/lib/active_record/type_caster/map.rb +8 -5
  187. data/lib/active_record/validations/associated.rb +1 -1
  188. data/lib/active_record/validations/numericality.rb +35 -0
  189. data/lib/active_record/validations/uniqueness.rb +24 -4
  190. data/lib/active_record/validations.rb +1 -0
  191. data/lib/active_record.rb +7 -14
  192. data/lib/arel/attributes/attribute.rb +4 -0
  193. data/lib/arel/collectors/bind.rb +5 -0
  194. data/lib/arel/collectors/composite.rb +8 -0
  195. data/lib/arel/collectors/sql_string.rb +7 -0
  196. data/lib/arel/collectors/substitute_binds.rb +7 -0
  197. data/lib/arel/nodes/binary.rb +82 -8
  198. data/lib/arel/nodes/bind_param.rb +8 -0
  199. data/lib/arel/nodes/casted.rb +21 -9
  200. data/lib/arel/nodes/equality.rb +6 -9
  201. data/lib/arel/nodes/grouping.rb +3 -0
  202. data/lib/arel/nodes/homogeneous_in.rb +76 -0
  203. data/lib/arel/nodes/in.rb +8 -1
  204. data/lib/arel/nodes/infix_operation.rb +13 -1
  205. data/lib/arel/nodes/join_source.rb +1 -1
  206. data/lib/arel/nodes/node.rb +7 -6
  207. data/lib/arel/nodes/ordering.rb +27 -0
  208. data/lib/arel/nodes/sql_literal.rb +3 -0
  209. data/lib/arel/nodes/table_alias.rb +7 -3
  210. data/lib/arel/nodes/unary.rb +0 -1
  211. data/lib/arel/nodes.rb +3 -1
  212. data/lib/arel/predications.rb +12 -18
  213. data/lib/arel/select_manager.rb +1 -2
  214. data/lib/arel/table.rb +13 -5
  215. data/lib/arel/visitors/dot.rb +14 -2
  216. data/lib/arel/visitors/mysql.rb +11 -1
  217. data/lib/arel/visitors/postgresql.rb +15 -4
  218. data/lib/arel/visitors/to_sql.rb +89 -78
  219. data/lib/arel/visitors.rb +0 -7
  220. data/lib/arel.rb +5 -13
  221. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  222. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  223. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +3 -3
  224. data/lib/rails/generators/active_record/migration.rb +6 -1
  225. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  226. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  227. metadata +25 -26
  228. data/lib/active_record/advisory_lock_base.rb +0 -18
  229. data/lib/active_record/attribute_decorators.rb +0 -88
  230. data/lib/active_record/connection_adapters/connection_specification.rb +0 -296
  231. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  232. data/lib/active_record/define_callbacks.rb +0 -22
  233. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  234. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  235. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  236. data/lib/arel/attributes.rb +0 -22
  237. data/lib/arel/visitors/depth_first.rb +0 -203
  238. data/lib/arel/visitors/ibm_db.rb +0 -34
  239. data/lib/arel/visitors/informix.rb +0 -62
  240. data/lib/arel/visitors/mssql.rb +0 -156
  241. data/lib/arel/visitors/oracle.rb +0 -158
  242. data/lib/arel/visitors/oracle12.rb +0 -65
  243. data/lib/arel/visitors/where_sql.rb +0 -22
@@ -112,12 +112,12 @@ module ActiveRecord
112
112
  case association.macro
113
113
  when :belongs_to
114
114
  # Do not replace association name with association foreign key if they are named the same
115
- fk_name = (association.options[:foreign_key] || "#{association.name}_id").to_s
115
+ fk_name = association.join_foreign_key
116
116
 
117
117
  if association.name.to_s != fk_name && value = @row.delete(association.name.to_s)
118
118
  if association.polymorphic? && value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
119
119
  # support polymorphic belongs_to as "label (Type)"
120
- @row[association.foreign_type] = $1
120
+ @row[association.join_foreign_type] = $1
121
121
  end
122
122
 
123
123
  fk_type = reflection_class.type_for_attribute(fk_name).type
@@ -10,7 +10,6 @@ require "active_record/fixture_set/file"
10
10
  require "active_record/fixture_set/render_context"
11
11
  require "active_record/fixture_set/table_rows"
12
12
  require "active_record/test_fixtures"
13
- require "active_record/errors"
14
13
 
15
14
  module ActiveRecord
16
15
  class FixtureClassNotFound < ActiveRecord::ActiveRecordError #:nodoc:
@@ -41,7 +40,7 @@ module ActiveRecord
41
40
  # separated by a blank line for your viewing pleasure.
42
41
  #
43
42
  # Note: Fixtures are unordered. If you want ordered fixtures, use the omap YAML type.
44
- # See http://yaml.org/type/omap.html
43
+ # See https://yaml.org/type/omap.html
45
44
  # for the specification. You will need ordered fixtures when you have foreign key constraints
46
45
  # on keys in the same table. This is commonly needed for tree structures. Example:
47
46
  #
@@ -60,7 +59,7 @@ module ActiveRecord
60
59
  # Since fixtures are a testing construct, we use them in our unit and functional tests. There
61
60
  # are two ways to use the fixtures, but first let's take a look at a sample unit test:
62
61
  #
63
- # require 'test_helper'
62
+ # require "test_helper"
64
63
  #
65
64
  # class WebSiteTest < ActiveSupport::TestCase
66
65
  # test "web_site_count" do
@@ -182,7 +181,7 @@ module ActiveRecord
182
181
  # end
183
182
  # end
184
183
  #
185
- # If you preload your test database with all fixture data (probably by running `rails db:fixtures:load`)
184
+ # If you preload your test database with all fixture data (probably by running <tt>bin/rails db:fixtures:load</tt>)
186
185
  # and use transactional tests, then you may omit all fixtures declarations in your test cases since
187
186
  # all the data's already there and every case rolls back its changes.
188
187
  #
@@ -420,12 +419,35 @@ module ActiveRecord
420
419
  #
421
420
  # Any fixture labeled "DEFAULTS" is safely ignored.
422
421
  #
422
+ # Besides using "DEFAULTS", you can also specify what fixtures will
423
+ # be ignored by setting "ignore" in "_fixture" section.
424
+ #
425
+ # # users.yml
426
+ # _fixture:
427
+ # ignore:
428
+ # - base
429
+ # # or use "ignore: base" when there is only one fixture needs to be ignored.
430
+ #
431
+ # base: &base
432
+ # admin: false
433
+ # introduction: "This is a default description"
434
+ #
435
+ # admin:
436
+ # <<: *base
437
+ # admin: true
438
+ #
439
+ # visitor:
440
+ # <<: *base
441
+ #
442
+ # In the above example, 'base' will be ignored when creating fixtures.
443
+ # This can be used for common attributes inheriting.
444
+ #
423
445
  # == Configure the fixture model class
424
446
  #
425
447
  # It's possible to set the fixture's model class directly in the YAML file.
426
448
  # This is helpful when fixtures are loaded outside tests and
427
449
  # +set_fixture_class+ is not available (e.g.
428
- # when running <tt>rails db:fixtures:load</tt>).
450
+ # when running <tt>bin/rails db:fixtures:load</tt>).
429
451
  #
430
452
  # _fixture:
431
453
  # model_class: User
@@ -563,6 +585,14 @@ module ActiveRecord
563
585
  end
564
586
  end
565
587
 
588
+ def signed_global_id(fixture_set_name, label, column_type: :integer, **options)
589
+ identifier = identify(label, column_type)
590
+ model_name = default_fixture_model_name(fixture_set_name)
591
+ uri = URI::GID.build([GlobalID.app, model_name, identifier, {}])
592
+
593
+ SignedGlobalID.new(uri, **options)
594
+ end
595
+
566
596
  # Superclass for the evaluation contexts used by ERB fixtures.
567
597
  def context_class
568
598
  @context_class ||= Class.new
@@ -619,7 +649,7 @@ module ActiveRecord
619
649
  end
620
650
  end
621
651
 
622
- attr_reader :table_name, :name, :fixtures, :model_class, :config
652
+ attr_reader :table_name, :name, :fixtures, :model_class, :ignored_fixtures, :config
623
653
 
624
654
  def initialize(_, name, class_name, path, config = ActiveRecord::Base)
625
655
  @name = name
@@ -652,8 +682,8 @@ module ActiveRecord
652
682
  # Returns a hash of rows to be inserted. The key is the table, the value is
653
683
  # a list of rows to insert to that table.
654
684
  def table_rows
655
- # allow a standard key to be used for doing defaults in YAML
656
- fixtures.delete("DEFAULTS")
685
+ # allow specifying fixtures to be ignored by setting `ignore` in `_fixture` section
686
+ fixtures.except!(*ignored_fixtures)
657
687
 
658
688
  TableRows.new(
659
689
  table_name,
@@ -672,6 +702,21 @@ module ActiveRecord
672
702
  end
673
703
  end
674
704
 
705
+ def ignored_fixtures=(base)
706
+ @ignored_fixtures =
707
+ case base
708
+ when Array
709
+ base
710
+ when String
711
+ [base]
712
+ else
713
+ []
714
+ end
715
+
716
+ @ignored_fixtures << "DEFAULTS" unless @ignored_fixtures.include?("DEFAULTS")
717
+ @ignored_fixtures.compact
718
+ end
719
+
675
720
  # Loads the fixtures from the YAML file at +path+.
676
721
  # If the file sets the +model_class+ and current instance value is not set,
677
722
  # it uses the file value.
@@ -683,6 +728,7 @@ module ActiveRecord
683
728
  yaml_files.each_with_object({}) do |file, fixtures|
684
729
  FixtureSet::File.open(file) do |fh|
685
730
  self.model_class ||= fh.model_class if fh.model_class
731
+ self.ignored_fixtures ||= fh.ignored_fixtures
686
732
  fh.each do |fixture_name, row|
687
733
  fixtures[fixture_name] = ActiveRecord::Fixture.new(row, model_class)
688
734
  end
@@ -727,9 +773,12 @@ module ActiveRecord
727
773
 
728
774
  def find
729
775
  raise FixtureClassNotFound, "No class attached to find." unless model_class
730
- model_class.unscoped do
776
+ object = model_class.unscoped do
731
777
  model_class.find(fixture[model_class.primary_key])
732
778
  end
779
+ # Fixtures can't be eagerly loaded
780
+ object.instance_variable_set(:@strict_loading, false)
781
+ object
733
782
  end
734
783
  end
735
784
  end
@@ -8,9 +8,9 @@ module ActiveRecord
8
8
 
9
9
  module VERSION
10
10
  MAJOR = 6
11
- MINOR = 0
12
- TINY = 6
13
- PRE = "1"
11
+ MINOR = 1
12
+ TINY = 7
13
+ PRE = "6"
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -38,6 +38,8 @@ module ActiveRecord
38
38
  extend ActiveSupport::Concern
39
39
 
40
40
  included do
41
+ class_attribute :store_full_class_name, instance_writer: false, default: true
42
+
41
43
  # Determines whether to store the full constant name including namespace when using STI.
42
44
  # This is true, by default.
43
45
  class_attribute :store_full_sti_class, instance_writer: false, default: true
@@ -52,7 +54,7 @@ module ActiveRecord
52
54
  raise NotImplementedError, "#{self} is an abstract class and cannot be instantiated."
53
55
  end
54
56
 
55
- if has_attribute?(inheritance_column)
57
+ if _has_attribute?(inheritance_column)
56
58
  subclass = subclass_from_attributes(attributes)
57
59
 
58
60
  if subclass.nil? && scope_attributes = current_scope&.scope_for_create
@@ -162,12 +164,42 @@ module ActiveRecord
162
164
  defined?(@abstract_class) && @abstract_class == true
163
165
  end
164
166
 
167
+ # Returns the value to be stored in the inheritance column for STI.
165
168
  def sti_name
166
- store_full_sti_class ? name : name.demodulize
169
+ store_full_sti_class && store_full_class_name ? name : name.demodulize
170
+ end
171
+
172
+ # Returns the class for the provided +type_name+.
173
+ #
174
+ # It is used to find the class correspondent to the value stored in the inheritance column.
175
+ def sti_class_for(type_name)
176
+ if store_full_sti_class && store_full_class_name
177
+ ActiveSupport::Dependencies.constantize(type_name)
178
+ else
179
+ compute_type(type_name)
180
+ end
181
+ rescue NameError
182
+ raise SubclassNotFound,
183
+ "The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " \
184
+ "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " \
185
+ "Please rename this column if you didn't intend it to be used for storing the inheritance class " \
186
+ "or overwrite #{name}.inheritance_column to use another column for that information."
167
187
  end
168
188
 
189
+ # Returns the value to be stored in the polymorphic type column for Polymorphic Associations.
169
190
  def polymorphic_name
170
- base_class.name
191
+ store_full_class_name ? base_class.name : base_class.name.demodulize
192
+ end
193
+
194
+ # Returns the class for the provided +name+.
195
+ #
196
+ # It is used to find the class correspondent to the value stored in the polymorphic type column.
197
+ def polymorphic_class_for(name)
198
+ if store_full_class_name
199
+ ActiveSupport::Dependencies.constantize(name)
200
+ else
201
+ compute_type(name)
202
+ end
171
203
  end
172
204
 
173
205
  def inherited(subclass)
@@ -219,32 +251,22 @@ module ActiveRecord
219
251
  end
220
252
 
221
253
  def using_single_table_inheritance?(record)
222
- record[inheritance_column].present? && has_attribute?(inheritance_column)
254
+ record[inheritance_column].present? && _has_attribute?(inheritance_column)
223
255
  end
224
256
 
225
257
  def find_sti_class(type_name)
226
258
  type_name = base_class.type_for_attribute(inheritance_column).cast(type_name)
227
- subclass = begin
228
- if store_full_sti_class
229
- ActiveSupport::Dependencies.constantize(type_name)
230
- else
231
- compute_type(type_name)
232
- end
233
- rescue NameError
234
- raise SubclassNotFound,
235
- "The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " \
236
- "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " \
237
- "Please rename this column if you didn't intend it to be used for storing the inheritance class " \
238
- "or overwrite #{name}.inheritance_column to use another column for that information."
239
- end
259
+ subclass = sti_class_for(type_name)
260
+
240
261
  unless subclass == self || descendants.include?(subclass)
241
262
  raise SubclassNotFound, "Invalid single-table inheritance type: #{subclass.name} is not a subclass of #{name}"
242
263
  end
264
+
243
265
  subclass
244
266
  end
245
267
 
246
268
  def type_condition(table = arel_table)
247
- sti_column = arel_attribute(inheritance_column, table)
269
+ sti_column = table[inheritance_column]
248
270
  sti_names = ([self] + descendants).map(&:sti_name)
249
271
 
250
272
  predicate_builder.build(sti_column, sti_names)
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/enumerable"
4
+
3
5
  module ActiveRecord
4
6
  class InsertAll # :nodoc:
5
7
  attr_reader :model, :connection, :inserts, :keys
@@ -8,13 +10,19 @@ module ActiveRecord
8
10
  def initialize(model, inserts, on_duplicate:, returning: nil, unique_by: nil)
9
11
  raise ArgumentError, "Empty list of attributes passed" if inserts.blank?
10
12
 
11
- @model, @connection, @inserts, @keys = model, model.connection, inserts, inserts.first.keys.map(&:to_s).to_set
13
+ @model, @connection, @inserts, @keys = model, model.connection, inserts, inserts.first.keys.map(&:to_s)
12
14
  @on_duplicate, @returning, @unique_by = on_duplicate, returning, unique_by
13
15
 
16
+ if model.scope_attributes?
17
+ @scope_attributes = model.scope_attributes
18
+ @keys |= @scope_attributes.keys
19
+ end
20
+ @keys = @keys.to_set
21
+
14
22
  @returning = (connection.supports_insert_returning? ? primary_keys : false) if @returning.nil?
15
23
  @returning = false if @returning == []
16
24
 
17
- @unique_by = find_unique_index_for(unique_by) if unique_by
25
+ @unique_by = find_unique_index_for(unique_by)
18
26
  @on_duplicate = :skip if @on_duplicate == :update && updatable_columns.empty?
19
27
 
20
28
  ensure_valid_options_for_connection!
@@ -32,7 +40,7 @@ module ActiveRecord
32
40
  end
33
41
 
34
42
  def primary_keys
35
- Array(model.primary_key)
43
+ Array(connection.schema_cache.primary_keys(model.table_name))
36
44
  end
37
45
 
38
46
 
@@ -47,6 +55,8 @@ module ActiveRecord
47
55
  def map_key_with_value
48
56
  inserts.map do |attributes|
49
57
  attributes = attributes.stringify_keys
58
+ attributes.merge!(scope_attributes) if scope_attributes
59
+
50
60
  verify_attributes(attributes)
51
61
 
52
62
  keys.map do |key|
@@ -56,13 +66,24 @@ module ActiveRecord
56
66
  end
57
67
 
58
68
  private
69
+ attr_reader :scope_attributes
70
+
59
71
  def find_unique_index_for(unique_by)
60
- match = Array(unique_by).map(&:to_s)
72
+ if !connection.supports_insert_conflict_target?
73
+ return if unique_by.nil?
74
+
75
+ raise ArgumentError, "#{connection.class} does not support :unique_by"
76
+ end
77
+
78
+ name_or_columns = unique_by || model.primary_key
79
+ match = Array(name_or_columns).map(&:to_s)
61
80
 
62
81
  if index = unique_indexes.find { |i| match.include?(i.name) || i.columns == match }
63
82
  index
83
+ elsif match == primary_keys
84
+ unique_by.nil? ? nil : ActiveRecord::ConnectionAdapters::IndexDefinition.new(model.table_name, "#{model.table_name}_primary_key", true, match)
64
85
  else
65
- raise ArgumentError, "No unique index found for #{unique_by}"
86
+ raise ArgumentError, "No unique index found for #{name_or_columns}"
66
87
  end
67
88
  end
68
89
 
@@ -151,9 +172,21 @@ module ActiveRecord
151
172
  quote_columns(insert_all.updatable_columns)
152
173
  end
153
174
 
175
+ def touch_model_timestamps_unless(&block)
176
+ model.send(:timestamp_attributes_for_update_in_model).map do |column_name|
177
+ if touch_timestamp_attribute?(column_name)
178
+ "#{column_name}=(CASE WHEN (#{updatable_columns.map(&block).join(" AND ")}) THEN #{model.quoted_table_name}.#{column_name} ELSE CURRENT_TIMESTAMP END),"
179
+ end
180
+ end.compact.join
181
+ end
182
+
154
183
  private
155
184
  attr_reader :connection, :insert_all
156
185
 
186
+ def touch_timestamp_attribute?(column_name)
187
+ update_duplicates? && !insert_all.updatable_columns.include?(column_name)
188
+ end
189
+
157
190
  def columns_list
158
191
  format_columns(insert_all.keys)
159
192
  end
@@ -93,7 +93,7 @@ module ActiveRecord
93
93
  # cache_version, but this method can be overwritten to return something else.
94
94
  #
95
95
  # Note, this method will return nil if ActiveRecord::Base.cache_versioning is set to
96
- # +false+ (which it is by default until Rails 6.0).
96
+ # +false+.
97
97
  def cache_version
98
98
  return unless cache_versioning
99
99
 
@@ -104,10 +104,8 @@ module ActiveRecord
104
104
  elsif timestamp = updated_at
105
105
  timestamp.utc.to_s(cache_timestamp_format)
106
106
  end
107
- else
108
- if self.class.has_attribute?("updated_at")
109
- raise ActiveModel::MissingAttributeError, "missing attribute: updated_at"
110
- end
107
+ elsif self.class.has_attribute?("updated_at")
108
+ raise ActiveModel::MissingAttributeError, "missing attribute: updated_at"
111
109
  end
112
110
  end
113
111
 
@@ -6,8 +6,17 @@ require "active_record/scoping/named"
6
6
  module ActiveRecord
7
7
  # This class is used to create a table that keeps track of values and keys such
8
8
  # as which environment migrations were run in.
9
+ #
10
+ # This is enabled by default. To disable this functionality set
11
+ # `use_metadata_table` to false in your database configuration.
9
12
  class InternalMetadata < ActiveRecord::Base # :nodoc:
13
+ self.record_timestamps = true
14
+
10
15
  class << self
16
+ def enabled?
17
+ ActiveRecord::Base.connection.use_metadata_table?
18
+ end
19
+
11
20
  def _internal?
12
21
  true
13
22
  end
@@ -21,24 +30,24 @@ module ActiveRecord
21
30
  end
22
31
 
23
32
  def []=(key, value)
33
+ return unless enabled?
34
+
24
35
  find_or_initialize_by(key: key).update!(value: value)
25
36
  end
26
37
 
27
38
  def [](key)
28
- where(key: key).pluck(:value).first
29
- end
39
+ return unless enabled?
30
40
 
31
- def table_exists?
32
- connection.table_exists?(table_name)
41
+ where(key: key).pluck(:value).first
33
42
  end
34
43
 
35
44
  # Creates an internal metadata table with columns +key+ and +value+
36
45
  def create_table
37
- unless table_exists?
38
- key_options = connection.internal_string_options_for_primary_key
46
+ return unless enabled?
39
47
 
48
+ unless connection.table_exists?(table_name)
40
49
  connection.create_table(table_name, id: false) do |t|
41
- t.string :key, **key_options
50
+ t.string :key, **connection.internal_string_options_for_primary_key
42
51
  t.string :value
43
52
  t.timestamps
44
53
  end
@@ -46,6 +55,8 @@ module ActiveRecord
46
55
  end
47
56
 
48
57
  def drop_table
58
+ return unless enabled?
59
+
49
60
  connection.drop_table table_name, if_exists: true
50
61
  end
51
62
  end
@@ -1,13 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecord
4
- module LegacyYamlAdapter
4
+ module LegacyYamlAdapter # :nodoc:
5
5
  def self.convert(klass, coder)
6
6
  return coder unless coder.is_a?(Psych::Coder)
7
7
 
8
8
  case coder["active_record_yaml_version"]
9
9
  when 1, 2 then coder
10
10
  else
11
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
12
+ YAML loading from legacy format older than Rails 5.0 is deprecated
13
+ and will be removed in Rails 7.0.
14
+ MSG
11
15
  if coder["attributes"].is_a?(ActiveModel::AttributeSet)
12
16
  Rails420.convert(klass, coder)
13
17
  else
@@ -16,7 +20,7 @@ module ActiveRecord
16
20
  end
17
21
  end
18
22
 
19
- module Rails420
23
+ module Rails420 # :nodoc:
20
24
  def self.convert(klass, coder)
21
25
  attribute_set = coder["attributes"]
22
26
 
@@ -32,7 +36,7 @@ module ActiveRecord
32
36
  end
33
37
  end
34
38
 
35
- module Rails41
39
+ module Rails41 # :nodoc:
36
40
  def self.convert(klass, coder)
37
41
  attributes = klass.attributes_builder
38
42
  .build_from_database(coder["attributes"])
@@ -89,7 +89,10 @@ module ActiveRecord
89
89
 
90
90
  begin
91
91
  locking_column = self.class.locking_column
92
- previous_lock_value = read_attribute_before_type_cast(locking_column)
92
+ lock_attribute_was = @attributes[locking_column]
93
+ lock_value_for_database = _lock_value_for_database(locking_column)
94
+
95
+ attribute_names = attribute_names.dup if attribute_names.frozen?
93
96
  attribute_names << locking_column
94
97
 
95
98
  self[locking_column] += 1
@@ -97,7 +100,7 @@ module ActiveRecord
97
100
  affected_rows = self.class._update_record(
98
101
  attributes_with_values(attribute_names),
99
102
  @primary_key => id_in_database,
100
- locking_column => previous_lock_value
103
+ locking_column => lock_value_for_database
101
104
  )
102
105
 
103
106
  if affected_rows != 1
@@ -108,7 +111,7 @@ module ActiveRecord
108
111
 
109
112
  # If something went wrong, revert the locking_column value.
110
113
  rescue Exception
111
- self[locking_column] = previous_lock_value.to_i
114
+ @attributes[locking_column] = lock_attribute_was
112
115
  raise
113
116
  end
114
117
  end
@@ -120,7 +123,7 @@ module ActiveRecord
120
123
 
121
124
  affected_rows = self.class._delete_record(
122
125
  @primary_key => id_in_database,
123
- locking_column => read_attribute_before_type_cast(locking_column)
126
+ locking_column => _lock_value_for_database(locking_column)
124
127
  )
125
128
 
126
129
  if affected_rows != 1
@@ -130,6 +133,14 @@ module ActiveRecord
130
133
  affected_rows
131
134
  end
132
135
 
136
+ def _lock_value_for_database(locking_column)
137
+ if will_save_change_to_attribute?(locking_column)
138
+ @attributes[locking_column].value_for_database
139
+ else
140
+ @attributes[locking_column].original_value_for_database
141
+ end
142
+ end
143
+
133
144
  module ClassMethods
134
145
  DEFAULT_LOCKING_COLUMN = "lock_version"
135
146
 
@@ -164,20 +175,12 @@ module ActiveRecord
164
175
  super
165
176
  end
166
177
 
167
- private
168
- # We need to apply this decorator here, rather than on module inclusion. The closure
169
- # created by the matcher would otherwise evaluate for `ActiveRecord::Base`, not the
170
- # sub class being decorated. As such, changes to `lock_optimistically`, or
171
- # `locking_column` would not be picked up.
172
- def inherited(subclass)
173
- subclass.class_eval do
174
- is_lock_column = ->(name, _) { lock_optimistically && name == locking_column }
175
- decorate_matching_attribute_types(is_lock_column, "_optimistic_locking") do |type|
176
- LockingType.new(type)
177
- end
178
- end
179
- super
178
+ def define_attribute(name, cast_type, **) # :nodoc:
179
+ if lock_optimistically && name == locking_column
180
+ cast_type = LockingType.new(cast_type)
180
181
  end
182
+ super
183
+ end
181
184
  end
182
185
  end
183
186
 
@@ -185,6 +188,10 @@ module ActiveRecord
185
188
  # `nil` values to `lock_version`, and not result in `ActiveRecord::StaleObjectError`
186
189
  # during update record.
187
190
  class LockingType < DelegateClass(Type::Value) # :nodoc:
191
+ def self.new(subtype)
192
+ self === subtype ? subtype : super
193
+ end
194
+
188
195
  def deserialize(value)
189
196
  super.to_i
190
197
  end
@@ -53,8 +53,12 @@ module ActiveRecord
53
53
  # end
54
54
  #
55
55
  # Database-specific information on row locking:
56
- # MySQL: https://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html
57
- # PostgreSQL: https://www.postgresql.org/docs/current/interactive/sql-select.html#SQL-FOR-UPDATE-SHARE
56
+ #
57
+ # [MySQL]
58
+ # https://dev.mysql.com/doc/refman/en/innodb-locking-reads.html
59
+ #
60
+ # [PostgreSQL]
61
+ # https://www.postgresql.org/docs/current/interactive/sql-select.html#SQL-FOR-UPDATE-SHARE
58
62
  module Pessimistic
59
63
  # Obtain a row lock on this record. Reloads the record to obtain the requested
60
64
  # lock. Pass an SQL locking clause to append the end of the SELECT statement
@@ -19,6 +19,16 @@ module ActiveRecord
19
19
  rt
20
20
  end
21
21
 
22
+ def strict_loading_violation(event)
23
+ debug do
24
+ owner = event.payload[:owner]
25
+ association = event.payload[:reflection].klass
26
+ name = event.payload[:reflection].name
27
+
28
+ color("Strict loading violation: #{owner} is marked for strict loading. The #{association} association named :#{name} cannot be lazily loaded.", RED)
29
+ end
30
+ end
31
+
22
32
  def sql(event)
23
33
  self.class.runtime += event.duration
24
34
  return unless logger.debug?
@@ -32,11 +42,15 @@ module ActiveRecord
32
42
  sql = payload[:sql]
33
43
  binds = nil
34
44
 
35
- unless (payload[:binds] || []).empty?
45
+ if payload[:binds]&.any?
36
46
  casted_params = type_casted_binds(payload[:type_casted_binds])
37
- binds = " " + payload[:binds].zip(casted_params).map { |attr, value|
38
- render_bind(attr, value)
39
- }.inspect
47
+
48
+ binds = []
49
+ payload[:binds].each_with_index do |attr, i|
50
+ binds << render_bind(attr, casted_params[i])
51
+ end
52
+ binds = binds.inspect
53
+ binds.prepend(" ")
40
54
  end
41
55
 
42
56
  name = colorize_payload_name(name, payload[:name])
@@ -51,13 +65,18 @@ module ActiveRecord
51
65
  end
52
66
 
53
67
  def render_bind(attr, value)
54
- if attr.is_a?(Array)
68
+ case attr
69
+ when ActiveModel::Attribute
70
+ if attr.type.binary? && attr.value
71
+ value = "<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
72
+ end
73
+ when Array
55
74
  attr = attr.first
56
- elsif attr.type.binary? && attr.value
57
- value = "<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
75
+ else
76
+ attr = nil
58
77
  end
59
78
 
60
- [attr && attr.name, value]
79
+ [attr&.name, value]
61
80
  end
62
81
 
63
82
  def colorize_payload_name(name, payload_name)
@@ -38,6 +38,9 @@ module ActiveRecord
38
38
  def update_last_write_timestamp
39
39
  session[:last_write] = self.class.convert_time_to_timestamp(Time.now)
40
40
  end
41
+
42
+ def save(response)
43
+ end
41
44
  end
42
45
  end
43
46
  end