activerecord 6.0.6.1 → 6.1.7.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1152 -779
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -2
- data/lib/active_record/aggregations.rb +5 -5
- data/lib/active_record/association_relation.rb +30 -12
- data/lib/active_record/associations/alias_tracker.rb +19 -15
- data/lib/active_record/associations/association.rb +49 -26
- data/lib/active_record/associations/association_scope.rb +18 -20
- data/lib/active_record/associations/belongs_to_association.rb +23 -10
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -3
- data/lib/active_record/associations/builder/association.rb +32 -5
- data/lib/active_record/associations/builder/belongs_to.rb +10 -7
- data/lib/active_record/associations/builder/collection_association.rb +5 -4
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -1
- data/lib/active_record/associations/builder/has_many.rb +6 -2
- data/lib/active_record/associations/builder/has_one.rb +11 -14
- data/lib/active_record/associations/builder/singular_association.rb +1 -1
- data/lib/active_record/associations/collection_association.rb +32 -18
- data/lib/active_record/associations/collection_proxy.rb +12 -5
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +24 -2
- data/lib/active_record/associations/has_many_through_association.rb +10 -4
- data/lib/active_record/associations/has_one_association.rb +15 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +37 -21
- data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +63 -49
- data/lib/active_record/associations/preloader/association.rb +14 -8
- data/lib/active_record/associations/preloader/through_association.rb +1 -1
- data/lib/active_record/associations/preloader.rb +5 -3
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations.rb +118 -11
- data/lib/active_record/attribute_assignment.rb +10 -8
- data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
- data/lib/active_record/attribute_methods/dirty.rb +1 -11
- data/lib/active_record/attribute_methods/primary_key.rb +6 -2
- data/lib/active_record/attribute_methods/query.rb +3 -6
- data/lib/active_record/attribute_methods/read.rb +8 -11
- data/lib/active_record/attribute_methods/serialization.rb +11 -5
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -13
- data/lib/active_record/attribute_methods/write.rb +12 -20
- data/lib/active_record/attribute_methods.rb +64 -54
- data/lib/active_record/attributes.rb +33 -8
- data/lib/active_record/autosave_association.rb +47 -30
- data/lib/active_record/base.rb +2 -14
- data/lib/active_record/callbacks.rb +152 -22
- data/lib/active_record/coders/yaml_column.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +185 -134
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +66 -23
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -8
- data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +114 -26
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +228 -83
- data/lib/active_record/connection_adapters/abstract/transaction.rb +92 -33
- data/lib/active_record/connection_adapters/abstract_adapter.rb +52 -76
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +123 -87
- data/lib/active_record/connection_adapters/column.rb +15 -1
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +24 -24
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +18 -3
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -6
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +5 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +7 -4
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -12
- data/lib/active_record/connection_adapters/pool_config.rb +73 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +14 -53
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +30 -4
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -64
- data/lib/active_record/connection_adapters/schema_cache.rb +130 -15
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +32 -5
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +36 -3
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +48 -50
- data/lib/active_record/connection_adapters.rb +52 -0
- data/lib/active_record/connection_handling.rb +218 -71
- data/lib/active_record/core.rb +264 -63
- data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -0
- data/lib/active_record/database_configurations/database_config.rb +52 -9
- data/lib/active_record/database_configurations/hash_config.rb +54 -8
- data/lib/active_record/database_configurations/url_config.rb +15 -40
- data/lib/active_record/database_configurations.rb +125 -85
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/enum.rb +69 -34
- data/lib/active_record/errors.rb +47 -12
- data/lib/active_record/explain.rb +9 -4
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +10 -17
- data/lib/active_record/fixture_set/model_metadata.rb +1 -2
- data/lib/active_record/fixture_set/render_context.rb +1 -1
- data/lib/active_record/fixture_set/table_row.rb +2 -2
- data/lib/active_record/fixtures.rb +58 -9
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +40 -18
- data/lib/active_record/insert_all.rb +38 -5
- data/lib/active_record/integration.rb +3 -5
- data/lib/active_record/internal_metadata.rb +18 -7
- data/lib/active_record/legacy_yaml_adapter.rb +7 -3
- data/lib/active_record/locking/optimistic.rb +24 -17
- data/lib/active_record/locking/pessimistic.rb +6 -2
- data/lib/active_record/log_subscriber.rb +27 -8
- data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
- data/lib/active_record/middleware/database_selector.rb +4 -1
- data/lib/active_record/migration/command_recorder.rb +47 -27
- data/lib/active_record/migration/compatibility.rb +72 -18
- data/lib/active_record/migration.rb +114 -84
- data/lib/active_record/model_schema.rb +89 -14
- data/lib/active_record/nested_attributes.rb +2 -3
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +50 -45
- data/lib/active_record/query_cache.rb +15 -5
- data/lib/active_record/querying.rb +11 -6
- data/lib/active_record/railtie.rb +64 -44
- data/lib/active_record/railties/console_sandbox.rb +2 -4
- data/lib/active_record/railties/databases.rake +279 -101
- data/lib/active_record/readonly_attributes.rb +4 -0
- data/lib/active_record/reflection.rb +60 -44
- data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
- data/lib/active_record/relation/batches.rb +38 -31
- data/lib/active_record/relation/calculations.rb +104 -43
- data/lib/active_record/relation/finder_methods.rb +44 -14
- data/lib/active_record/relation/from_clause.rb +1 -1
- data/lib/active_record/relation/merger.rb +20 -23
- data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -6
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +61 -38
- data/lib/active_record/relation/query_methods.rb +322 -196
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +8 -7
- data/lib/active_record/relation/where_clause.rb +111 -61
- data/lib/active_record/relation.rb +100 -81
- data/lib/active_record/result.rb +41 -33
- data/lib/active_record/runtime_registry.rb +2 -2
- data/lib/active_record/sanitization.rb +6 -17
- data/lib/active_record/schema_dumper.rb +34 -4
- data/lib/active_record/schema_migration.rb +2 -8
- data/lib/active_record/scoping/default.rb +1 -3
- data/lib/active_record/scoping/named.rb +1 -17
- data/lib/active_record/secure_token.rb +16 -8
- data/lib/active_record/serialization.rb +5 -3
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +20 -4
- data/lib/active_record/store.rb +8 -3
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +42 -51
- data/lib/active_record/tasks/database_tasks.rb +140 -113
- data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
- data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
- data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
- data/lib/active_record/test_databases.rb +5 -4
- data/lib/active_record/test_fixtures.rb +79 -31
- data/lib/active_record/timestamp.rb +4 -6
- data/lib/active_record/touch_later.rb +21 -21
- data/lib/active_record/transactions.rb +19 -66
- data/lib/active_record/type/serialized.rb +6 -2
- data/lib/active_record/type.rb +8 -1
- data/lib/active_record/type_caster/connection.rb +0 -1
- data/lib/active_record/type_caster/map.rb +8 -5
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +24 -4
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record.rb +7 -14
- data/lib/arel/attributes/attribute.rb +4 -0
- data/lib/arel/collectors/bind.rb +5 -0
- data/lib/arel/collectors/composite.rb +8 -0
- data/lib/arel/collectors/sql_string.rb +7 -0
- data/lib/arel/collectors/substitute_binds.rb +7 -0
- data/lib/arel/nodes/binary.rb +82 -8
- data/lib/arel/nodes/bind_param.rb +8 -0
- data/lib/arel/nodes/casted.rb +21 -9
- data/lib/arel/nodes/equality.rb +6 -9
- data/lib/arel/nodes/grouping.rb +3 -0
- data/lib/arel/nodes/homogeneous_in.rb +76 -0
- data/lib/arel/nodes/in.rb +8 -1
- data/lib/arel/nodes/infix_operation.rb +13 -1
- data/lib/arel/nodes/join_source.rb +1 -1
- data/lib/arel/nodes/node.rb +7 -6
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/sql_literal.rb +3 -0
- data/lib/arel/nodes/table_alias.rb +7 -3
- data/lib/arel/nodes/unary.rb +0 -1
- data/lib/arel/nodes.rb +3 -1
- data/lib/arel/predications.rb +12 -18
- data/lib/arel/select_manager.rb +1 -2
- data/lib/arel/table.rb +13 -5
- data/lib/arel/visitors/dot.rb +14 -2
- data/lib/arel/visitors/mysql.rb +11 -1
- data/lib/arel/visitors/postgresql.rb +15 -4
- data/lib/arel/visitors/to_sql.rb +89 -78
- data/lib/arel/visitors.rb +0 -7
- data/lib/arel.rb +5 -13
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +3 -3
- data/lib/rails/generators/active_record/migration.rb +6 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- metadata +25 -26
- data/lib/active_record/advisory_lock_base.rb +0 -18
- data/lib/active_record/attribute_decorators.rb +0 -88
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -296
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
- data/lib/active_record/relation/where_clause_factory.rb +0 -33
- data/lib/arel/attributes.rb +0 -22
- data/lib/arel/visitors/depth_first.rb +0 -203
- data/lib/arel/visitors/ibm_db.rb +0 -34
- data/lib/arel/visitors/informix.rb +0 -62
- data/lib/arel/visitors/mssql.rb +0 -156
- data/lib/arel/visitors/oracle.rb +0 -158
- data/lib/arel/visitors/oracle12.rb +0 -65
- 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 =
|
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.
|
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
|
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
|
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
|
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
|
656
|
-
fixtures.
|
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
|
@@ -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
|
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? &&
|
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 =
|
228
|
-
|
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 =
|
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)
|
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)
|
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.
|
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
|
-
|
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 #{
|
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
|
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
|
-
|
108
|
-
|
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
|
-
|
29
|
-
end
|
39
|
+
return unless enabled?
|
30
40
|
|
31
|
-
|
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
|
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, **
|
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
|
-
|
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 =>
|
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
|
-
|
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 =>
|
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
|
-
|
168
|
-
|
169
|
-
|
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
|
-
#
|
57
|
-
#
|
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
|
-
|
45
|
+
if payload[:binds]&.any?
|
36
46
|
casted_params = type_casted_binds(payload[:type_casted_binds])
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
-
|
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
|
-
|
57
|
-
|
75
|
+
else
|
76
|
+
attr = nil
|
58
77
|
end
|
59
78
|
|
60
|
-
[attr
|
79
|
+
[attr&.name, value]
|
61
80
|
end
|
62
81
|
|
63
82
|
def colorize_payload_name(name, payload_name)
|