activerecord 6.0.5.1 → 6.1.0.rc1

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 (242) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +764 -916
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +3 -3
  5. data/lib/active_record/aggregations.rb +1 -1
  6. data/lib/active_record/association_relation.rb +22 -14
  7. data/lib/active_record/associations/alias_tracker.rb +19 -15
  8. data/lib/active_record/associations/association.rb +39 -27
  9. data/lib/active_record/associations/association_scope.rb +11 -15
  10. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  12. data/lib/active_record/associations/builder/association.rb +9 -3
  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 +19 -13
  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 +29 -14
  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 +13 -5
  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 +114 -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 +4 -4
  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 +52 -48
  43. data/lib/active_record/attributes.rb +27 -7
  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 +32 -22
  47. data/lib/active_record/coders/yaml_column.rb +2 -14
  48. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +180 -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 +65 -22
  51. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -7
  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 +110 -30
  56. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +224 -85
  58. data/lib/active_record/connection_adapters/abstract/transaction.rb +66 -24
  59. data/lib/active_record/connection_adapters/abstract_adapter.rb +31 -70
  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 +31 -0
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +22 -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 +1 -1
  67. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +33 -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 +1 -1
  70. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +3 -3
  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 +63 -0
  74. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  75. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  76. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +12 -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 -10
  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/money.rb +2 -2
  84. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
  86. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  87. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  88. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  89. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
  90. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
  91. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  92. data/lib/active_record/connection_adapters/postgresql_adapter.rb +72 -55
  93. data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
  94. data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
  95. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +30 -5
  96. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
  97. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  98. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +36 -3
  99. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +48 -50
  100. data/lib/active_record/connection_adapters.rb +50 -0
  101. data/lib/active_record/connection_handling.rb +210 -71
  102. data/lib/active_record/core.rb +214 -58
  103. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  104. data/lib/active_record/database_configurations/database_config.rb +52 -9
  105. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  106. data/lib/active_record/database_configurations/url_config.rb +15 -40
  107. data/lib/active_record/database_configurations.rb +124 -85
  108. data/lib/active_record/delegated_type.rb +209 -0
  109. data/lib/active_record/destroy_association_async_job.rb +36 -0
  110. data/lib/active_record/enum.rb +33 -23
  111. data/lib/active_record/errors.rb +47 -12
  112. data/lib/active_record/explain.rb +9 -4
  113. data/lib/active_record/explain_subscriber.rb +1 -1
  114. data/lib/active_record/fixture_set/file.rb +10 -17
  115. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  116. data/lib/active_record/fixture_set/render_context.rb +1 -1
  117. data/lib/active_record/fixture_set/table_row.rb +2 -2
  118. data/lib/active_record/fixtures.rb +54 -8
  119. data/lib/active_record/gem_version.rb +3 -3
  120. data/lib/active_record/inheritance.rb +40 -18
  121. data/lib/active_record/insert_all.rb +32 -5
  122. data/lib/active_record/integration.rb +3 -5
  123. data/lib/active_record/internal_metadata.rb +15 -4
  124. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  125. data/lib/active_record/locking/optimistic.rb +13 -16
  126. data/lib/active_record/locking/pessimistic.rb +6 -2
  127. data/lib/active_record/log_subscriber.rb +26 -8
  128. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  129. data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
  130. data/lib/active_record/middleware/database_selector.rb +4 -1
  131. data/lib/active_record/migration/command_recorder.rb +47 -27
  132. data/lib/active_record/migration/compatibility.rb +67 -17
  133. data/lib/active_record/migration.rb +113 -83
  134. data/lib/active_record/model_schema.rb +88 -42
  135. data/lib/active_record/nested_attributes.rb +2 -3
  136. data/lib/active_record/no_touching.rb +1 -1
  137. data/lib/active_record/persistence.rb +50 -45
  138. data/lib/active_record/query_cache.rb +15 -5
  139. data/lib/active_record/querying.rb +11 -6
  140. data/lib/active_record/railtie.rb +61 -59
  141. data/lib/active_record/railties/databases.rake +253 -98
  142. data/lib/active_record/readonly_attributes.rb +4 -0
  143. data/lib/active_record/reflection.rb +59 -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 +100 -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 +2 -2
  152. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +3 -3
  153. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  154. data/lib/active_record/relation/predicate_builder.rb +57 -33
  155. data/lib/active_record/relation/query_methods.rb +319 -196
  156. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  157. data/lib/active_record/relation/spawn_methods.rb +6 -5
  158. data/lib/active_record/relation/where_clause.rb +104 -57
  159. data/lib/active_record/relation.rb +90 -64
  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 +0 -4
  165. data/lib/active_record/scoping/named.rb +1 -17
  166. data/lib/active_record/secure_token.rb +16 -8
  167. data/lib/active_record/serialization.rb +5 -3
  168. data/lib/active_record/signed_id.rb +116 -0
  169. data/lib/active_record/statement_cache.rb +20 -4
  170. data/lib/active_record/store.rb +2 -2
  171. data/lib/active_record/suppressor.rb +2 -2
  172. data/lib/active_record/table_metadata.rb +36 -52
  173. data/lib/active_record/tasks/database_tasks.rb +139 -113
  174. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
  175. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
  176. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
  177. data/lib/active_record/test_databases.rb +5 -4
  178. data/lib/active_record/test_fixtures.rb +36 -33
  179. data/lib/active_record/timestamp.rb +4 -6
  180. data/lib/active_record/touch_later.rb +21 -21
  181. data/lib/active_record/transactions.rb +15 -64
  182. data/lib/active_record/type/serialized.rb +6 -2
  183. data/lib/active_record/type.rb +8 -1
  184. data/lib/active_record/type_caster/connection.rb +0 -1
  185. data/lib/active_record/type_caster/map.rb +8 -5
  186. data/lib/active_record/validations/associated.rb +1 -1
  187. data/lib/active_record/validations/numericality.rb +35 -0
  188. data/lib/active_record/validations/uniqueness.rb +24 -4
  189. data/lib/active_record/validations.rb +1 -0
  190. data/lib/active_record.rb +7 -14
  191. data/lib/arel/attributes/attribute.rb +4 -0
  192. data/lib/arel/collectors/bind.rb +5 -0
  193. data/lib/arel/collectors/composite.rb +8 -0
  194. data/lib/arel/collectors/sql_string.rb +7 -0
  195. data/lib/arel/collectors/substitute_binds.rb +7 -0
  196. data/lib/arel/nodes/binary.rb +82 -8
  197. data/lib/arel/nodes/bind_param.rb +8 -0
  198. data/lib/arel/nodes/casted.rb +21 -9
  199. data/lib/arel/nodes/equality.rb +6 -9
  200. data/lib/arel/nodes/grouping.rb +3 -0
  201. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  202. data/lib/arel/nodes/in.rb +8 -1
  203. data/lib/arel/nodes/infix_operation.rb +13 -1
  204. data/lib/arel/nodes/join_source.rb +1 -1
  205. data/lib/arel/nodes/node.rb +7 -6
  206. data/lib/arel/nodes/ordering.rb +27 -0
  207. data/lib/arel/nodes/sql_literal.rb +3 -0
  208. data/lib/arel/nodes/table_alias.rb +7 -3
  209. data/lib/arel/nodes/unary.rb +0 -1
  210. data/lib/arel/nodes.rb +3 -1
  211. data/lib/arel/predications.rb +12 -18
  212. data/lib/arel/select_manager.rb +1 -2
  213. data/lib/arel/table.rb +13 -5
  214. data/lib/arel/visitors/dot.rb +14 -2
  215. data/lib/arel/visitors/mysql.rb +11 -1
  216. data/lib/arel/visitors/postgresql.rb +15 -4
  217. data/lib/arel/visitors/to_sql.rb +89 -78
  218. data/lib/arel/visitors.rb +0 -7
  219. data/lib/arel.rb +5 -13
  220. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  221. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  222. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +3 -3
  223. data/lib/rails/generators/active_record/migration.rb +6 -1
  224. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  225. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  226. metadata +30 -32
  227. data/lib/active_record/advisory_lock_base.rb +0 -18
  228. data/lib/active_record/attribute_decorators.rb +0 -88
  229. data/lib/active_record/connection_adapters/connection_specification.rb +0 -296
  230. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  231. data/lib/active_record/define_callbacks.rb +0 -22
  232. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  233. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  234. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  235. data/lib/arel/attributes.rb +0 -22
  236. data/lib/arel/visitors/depth_first.rb +0 -203
  237. data/lib/arel/visitors/ibm_db.rb +0 -34
  238. data/lib/arel/visitors/informix.rb +0 -62
  239. data/lib/arel/visitors/mssql.rb +0 -156
  240. data/lib/arel/visitors/oracle.rb +0 -158
  241. data/lib/arel/visitors/oracle12.rb +0 -65
  242. data/lib/arel/visitors/where_sql.rb +0 -22
@@ -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 `bin/rails db:fixtures:load`)
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
@@ -8,9 +8,9 @@ module ActiveRecord
8
8
 
9
9
  module VERSION
10
10
  MAJOR = 6
11
- MINOR = 0
12
- TINY = 5
13
- PRE = "1"
11
+ MINOR = 1
12
+ TINY = 0
13
+ PRE = "rc1"
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,18 @@ 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
+ name_or_columns = unique_by || model.primary_key
73
+ match = Array(name_or_columns).map(&:to_s)
61
74
 
62
75
  if index = unique_indexes.find { |i| match.include?(i.name) || i.columns == match }
63
76
  index
77
+ elsif match == primary_keys
78
+ unique_by.nil? ? nil : ActiveRecord::ConnectionAdapters::IndexDefinition.new(model.table_name, "#{model.table_name}_primary_key", true, match)
64
79
  else
65
- raise ArgumentError, "No unique index found for #{unique_by}"
80
+ raise ArgumentError, "No unique index found for #{name_or_columns}"
66
81
  end
67
82
  end
68
83
 
@@ -151,9 +166,21 @@ module ActiveRecord
151
166
  quote_columns(insert_all.updatable_columns)
152
167
  end
153
168
 
169
+ def touch_model_timestamps_unless(&block)
170
+ model.send(:timestamp_attributes_for_update_in_model).map do |column_name|
171
+ if touch_timestamp_attribute?(column_name)
172
+ "#{column_name}=(CASE WHEN (#{updatable_columns.map(&block).join(" AND ")}) THEN #{model.quoted_table_name}.#{column_name} ELSE CURRENT_TIMESTAMP END),"
173
+ end
174
+ end.compact.join
175
+ end
176
+
154
177
  private
155
178
  attr_reader :connection, :insert_all
156
179
 
180
+ def touch_timestamp_attribute?(column_name)
181
+ update_duplicates? && !insert_all.updatable_columns.include?(column_name)
182
+ end
183
+
157
184
  def columns_list
158
185
  format_columns(insert_all.keys)
159
186
  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,15 @@ 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:
10
13
  class << self
14
+ def enabled?
15
+ ActiveRecord::Base.connection.use_metadata_table?
16
+ end
17
+
11
18
  def _internal?
12
19
  true
13
20
  end
@@ -21,19 +28,21 @@ module ActiveRecord
21
28
  end
22
29
 
23
30
  def []=(key, value)
31
+ return unless enabled?
32
+
24
33
  find_or_initialize_by(key: key).update!(value: value)
25
34
  end
26
35
 
27
36
  def [](key)
28
- where(key: key).pluck(:value).first
29
- end
37
+ return unless enabled?
30
38
 
31
- def table_exists?
32
- connection.table_exists?(table_name)
39
+ where(key: key).pluck(:value).first
33
40
  end
34
41
 
35
42
  # Creates an internal metadata table with columns +key+ and +value+
36
43
  def create_table
44
+ return unless enabled?
45
+
37
46
  unless table_exists?
38
47
  key_options = connection.internal_string_options_for_primary_key
39
48
 
@@ -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 6.2.
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,8 @@ 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
+ previous_lock_value = attribute_before_type_cast(locking_column)
93
+ attribute_names = attribute_names.dup if attribute_names.frozen?
93
94
  attribute_names << locking_column
94
95
 
95
96
  self[locking_column] += 1
@@ -97,7 +98,7 @@ module ActiveRecord
97
98
  affected_rows = self.class._update_record(
98
99
  attributes_with_values(attribute_names),
99
100
  @primary_key => id_in_database,
100
- locking_column => previous_lock_value
101
+ locking_column => @attributes[locking_column].original_value_for_database
101
102
  )
102
103
 
103
104
  if affected_rows != 1
@@ -120,7 +121,7 @@ module ActiveRecord
120
121
 
121
122
  affected_rows = self.class._delete_record(
122
123
  @primary_key => id_in_database,
123
- locking_column => read_attribute_before_type_cast(locking_column)
124
+ locking_column => attribute_before_type_cast(locking_column)
124
125
  )
125
126
 
126
127
  if affected_rows != 1
@@ -164,20 +165,12 @@ module ActiveRecord
164
165
  super
165
166
  end
166
167
 
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
168
+ def define_attribute(name, cast_type, **) # :nodoc:
169
+ if lock_optimistically && name == locking_column
170
+ cast_type = LockingType.new(cast_type)
180
171
  end
172
+ super
173
+ end
181
174
  end
182
175
  end
183
176
 
@@ -185,6 +178,10 @@ module ActiveRecord
185
178
  # `nil` values to `lock_version`, and not result in `ActiveRecord::StaleObjectError`
186
179
  # during update record.
187
180
  class LockingType < DelegateClass(Type::Value) # :nodoc:
181
+ def self.new(subtype)
182
+ self === subtype ? subtype : super
183
+ end
184
+
188
185
  def deserialize(value)
189
186
  super.to_i
190
187
  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,15 @@ 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[:association]
26
+
27
+ color("Strict loading violation: #{association} lazily loaded on #{owner}.", RED)
28
+ end
29
+ end
30
+
22
31
  def sql(event)
23
32
  self.class.runtime += event.duration
24
33
  return unless logger.debug?
@@ -32,11 +41,15 @@ module ActiveRecord
32
41
  sql = payload[:sql]
33
42
  binds = nil
34
43
 
35
- unless (payload[:binds] || []).empty?
44
+ if payload[:binds]&.any?
36
45
  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
46
+
47
+ binds = []
48
+ payload[:binds].each_with_index do |attr, i|
49
+ binds << render_bind(attr, casted_params[i])
50
+ end
51
+ binds = binds.inspect
52
+ binds.prepend(" ")
40
53
  end
41
54
 
42
55
  name = colorize_payload_name(name, payload[:name])
@@ -51,13 +64,18 @@ module ActiveRecord
51
64
  end
52
65
 
53
66
  def render_bind(attr, value)
54
- if attr.is_a?(Array)
67
+ case attr
68
+ when ActiveModel::Attribute
69
+ if attr.type.binary? && attr.value
70
+ value = "<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
71
+ end
72
+ when Array
55
73
  attr = attr.first
56
- elsif attr.type.binary? && attr.value
57
- value = "<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
74
+ else
75
+ attr = nil
58
76
  end
59
77
 
60
- [attr && attr.name, value]
78
+ [attr&.name, value]
61
79
  end
62
80
 
63
81
  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
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_record/middleware/database_selector/resolver/session"
4
+ require "active_support/core_ext/numeric/time"
4
5
 
5
6
  module ActiveRecord
6
7
  module Middleware
@@ -43,6 +44,10 @@ module ActiveRecord
43
44
  write_to_primary(&blk)
44
45
  end
45
46
 
47
+ def update_context(response)
48
+ context.save(response)
49
+ end
50
+
46
51
  private
47
52
  def read_from_primary(&blk)
48
53
  ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role, prevent_writes: true) do
@@ -59,11 +59,14 @@ module ActiveRecord
59
59
  context = context_klass.call(request)
60
60
  resolver = resolver_klass.call(context, options)
61
61
 
62
- if reading_request?(request)
62
+ response = if reading_request?(request)
63
63
  resolver.read(&blk)
64
64
  else
65
65
  resolver.write(&blk)
66
66
  end
67
+
68
+ resolver.update_context(response)
69
+ response
67
70
  end
68
71
 
69
72
  def reading_request?(request)