activerecord 7.1.5.1 → 8.0.2
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +369 -2484
- data/README.rdoc +15 -15
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +2 -1
- data/lib/active_record/associations/alias_tracker.rb +31 -23
- data/lib/active_record/associations/association.rb +43 -12
- data/lib/active_record/associations/belongs_to_association.rb +21 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +7 -6
- data/lib/active_record/associations/builder/belongs_to.rb +1 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/collection_association.rb +17 -9
- data/lib/active_record/associations/collection_proxy.rb +14 -1
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +4 -3
- data/lib/active_record/associations/preloader/branch.rb +7 -1
- data/lib/active_record/associations/preloader/through_association.rb +1 -3
- data/lib/active_record/associations/singular_association.rb +14 -3
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +92 -295
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/primary_key.rb +25 -61
- data/lib/active_record/attribute_methods/read.rb +1 -13
- data/lib/active_record/attribute_methods/serialization.rb +4 -24
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +9 -18
- data/lib/active_record/attribute_methods.rb +71 -75
- data/lib/active_record/attributes.rb +63 -49
- data/lib/active_record/autosave_association.rb +92 -57
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +48 -122
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +286 -77
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +119 -55
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +197 -76
- data/lib/active_record/connection_adapters/abstract/quoting.rb +66 -92
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +12 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +48 -12
- data/lib/active_record/connection_adapters/abstract/transaction.rb +140 -67
- data/lib/active_record/connection_adapters/abstract_adapter.rb +85 -90
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +71 -52
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -57
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +56 -45
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +92 -101
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +13 -31
- data/lib/active_record/connection_adapters/pool_config.rb +14 -13
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +86 -41
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -11
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +36 -20
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +75 -28
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +73 -113
- data/lib/active_record/connection_adapters/schema_cache.rb +124 -131
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +81 -97
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +29 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +35 -3
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +183 -87
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +39 -69
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -65
- data/lib/active_record/connection_adapters.rb +65 -0
- data/lib/active_record/connection_handling.rb +74 -37
- data/lib/active_record/core.rb +132 -51
- data/lib/active_record/counter_cache.rb +19 -10
- data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
- data/lib/active_record/database_configurations/database_config.rb +23 -4
- data/lib/active_record/database_configurations/hash_config.rb +46 -34
- data/lib/active_record/database_configurations/url_config.rb +20 -1
- data/lib/active_record/database_configurations.rb +1 -1
- data/lib/active_record/delegated_type.rb +41 -17
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +7 -7
- data/lib/active_record/encryption/encrypted_attribute_type.rb +33 -4
- data/lib/active_record/encryption/encryptor.rb +28 -6
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- data/lib/active_record/encryption/key_provider.rb +1 -1
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +4 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +8 -1
- data/lib/active_record/enum.rb +20 -16
- data/lib/active_record/errors.rb +54 -20
- data/lib/active_record/explain.rb +13 -24
- data/lib/active_record/fixtures.rb +37 -33
- data/lib/active_record/future_result.rb +21 -13
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +4 -2
- data/lib/active_record/insert_all.rb +19 -16
- data/lib/active_record/integration.rb +4 -1
- data/lib/active_record/internal_metadata.rb +48 -34
- data/lib/active_record/locking/optimistic.rb +8 -7
- data/lib/active_record/log_subscriber.rb +5 -32
- data/lib/active_record/message_pack.rb +1 -1
- data/lib/active_record/migration/command_recorder.rb +33 -14
- data/lib/active_record/migration/compatibility.rb +8 -3
- data/lib/active_record/migration/default_strategy.rb +4 -5
- data/lib/active_record/migration/pending_migration_connection.rb +2 -2
- data/lib/active_record/migration.rb +104 -98
- data/lib/active_record/model_schema.rb +32 -70
- data/lib/active_record/nested_attributes.rb +15 -9
- data/lib/active_record/normalization.rb +3 -7
- data/lib/active_record/persistence.rb +127 -451
- data/lib/active_record/query_cache.rb +19 -8
- data/lib/active_record/query_logs.rb +104 -37
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +24 -12
- data/lib/active_record/railtie.rb +26 -68
- data/lib/active_record/railties/controller_runtime.rb +13 -4
- data/lib/active_record/railties/databases.rake +43 -61
- data/lib/active_record/reflection.rb +112 -53
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +138 -72
- data/lib/active_record/relation/calculations.rb +122 -82
- data/lib/active_record/relation/delegation.rb +30 -22
- data/lib/active_record/relation/finder_methods.rb +32 -18
- data/lib/active_record/relation/merger.rb +12 -14
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +10 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +16 -3
- data/lib/active_record/relation/query_attribute.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +317 -101
- data/lib/active_record/relation/spawn_methods.rb +3 -19
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +561 -119
- data/lib/active_record/result.rb +95 -46
- data/lib/active_record/runtime_registry.rb +39 -0
- data/lib/active_record/sanitization.rb +31 -25
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +53 -20
- data/lib/active_record/schema_migration.rb +31 -14
- data/lib/active_record/scoping/named.rb +6 -2
- data/lib/active_record/signed_id.rb +24 -4
- data/lib/active_record/statement_cache.rb +19 -19
- data/lib/active_record/store.rb +7 -3
- data/lib/active_record/table_metadata.rb +2 -13
- data/lib/active_record/tasks/database_tasks.rb +87 -58
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -3
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +4 -3
- data/lib/active_record/test_fixtures.rb +98 -89
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +2 -2
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +72 -17
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/serialized.rb +1 -3
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/associated.rb +9 -3
- data/lib/active_record/validations/uniqueness.rb +23 -18
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +138 -57
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +4 -2
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +2 -2
- data/lib/arel/collectors/substitute_binds.rb +3 -3
- data/lib/arel/nodes/binary.rb +1 -7
- data/lib/arel/nodes/bound_sql_literal.rb +9 -5
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +5 -4
- data/lib/arel/nodes/sql_literal.rb +8 -1
- data/lib/arel/nodes.rb +2 -2
- data/lib/arel/predications.rb +1 -1
- data/lib/arel/select_manager.rb +1 -1
- data/lib/arel/table.rb +3 -7
- data/lib/arel/tree_manager.rb +3 -2
- data/lib/arel/update_manager.rb +2 -1
- data/lib/arel/visitors/dot.rb +1 -0
- data/lib/arel/visitors/mysql.rb +9 -4
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/sqlite.rb +25 -0
- data/lib/arel/visitors/to_sql.rb +29 -16
- data/lib/arel.rb +7 -3
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- metadata +18 -16
- data/lib/active_record/relation/record_fetch_warning.rb +0 -49
data/README.rdoc
CHANGED
@@ -34,7 +34,7 @@ A short rundown of some of the major features:
|
|
34
34
|
This would also define the following accessors: <tt>Product#name</tt> and
|
35
35
|
<tt>Product#name=(new_name)</tt>.
|
36
36
|
|
37
|
-
{Learn more}[
|
37
|
+
{Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Base.html]
|
38
38
|
|
39
39
|
* Associations between objects defined by simple class methods.
|
40
40
|
|
@@ -44,7 +44,7 @@ A short rundown of some of the major features:
|
|
44
44
|
belongs_to :conglomerate
|
45
45
|
end
|
46
46
|
|
47
|
-
{Learn more}[
|
47
|
+
{Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html]
|
48
48
|
|
49
49
|
|
50
50
|
* Aggregations of value objects.
|
@@ -56,7 +56,7 @@ A short rundown of some of the major features:
|
|
56
56
|
mapping: [%w(address_street street), %w(address_city city)]
|
57
57
|
end
|
58
58
|
|
59
|
-
{Learn more}[
|
59
|
+
{Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Aggregations/ClassMethods.html]
|
60
60
|
|
61
61
|
|
62
62
|
* Validation rules that can differ for new or existing objects.
|
@@ -68,7 +68,7 @@ A short rundown of some of the major features:
|
|
68
68
|
validates :password, :email_address, confirmation: true, on: :create
|
69
69
|
end
|
70
70
|
|
71
|
-
{Learn more}[
|
71
|
+
{Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Validations.html]
|
72
72
|
|
73
73
|
|
74
74
|
* Callbacks available for the entire life cycle (instantiation, saving, destroying, validating, etc.).
|
@@ -78,7 +78,7 @@ A short rundown of some of the major features:
|
|
78
78
|
# the `invalidate_payment_plan` method gets called just before Person#destroy
|
79
79
|
end
|
80
80
|
|
81
|
-
{Learn more}[
|
81
|
+
{Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html]
|
82
82
|
|
83
83
|
|
84
84
|
* Inheritance hierarchies.
|
@@ -88,7 +88,7 @@ A short rundown of some of the major features:
|
|
88
88
|
class Client < Company; end
|
89
89
|
class PriorityClient < Client; end
|
90
90
|
|
91
|
-
{Learn more}[
|
91
|
+
{Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Base.html]
|
92
92
|
|
93
93
|
|
94
94
|
* Transactions.
|
@@ -99,7 +99,7 @@ A short rundown of some of the major features:
|
|
99
99
|
mary.deposit(100)
|
100
100
|
end
|
101
101
|
|
102
|
-
{Learn more}[
|
102
|
+
{Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html]
|
103
103
|
|
104
104
|
|
105
105
|
* Reflections on columns, associations, and aggregations.
|
@@ -108,7 +108,7 @@ A short rundown of some of the major features:
|
|
108
108
|
reflection.klass # => Client (class)
|
109
109
|
Firm.columns # Returns an array of column descriptors for the firms table
|
110
110
|
|
111
|
-
{Learn more}[
|
111
|
+
{Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Reflection/ClassMethods.html]
|
112
112
|
|
113
113
|
|
114
114
|
* Database abstraction through simple adapters.
|
@@ -125,13 +125,13 @@ A short rundown of some of the major features:
|
|
125
125
|
database: 'activerecord'
|
126
126
|
)
|
127
127
|
|
128
|
-
{Learn more}[
|
129
|
-
MySQL[
|
130
|
-
PostgreSQL[
|
131
|
-
SQLite3[
|
128
|
+
{Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Base.html] and read about the built-in support for
|
129
|
+
MySQL[https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Mysql2Adapter.html],
|
130
|
+
PostgreSQL[https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapter.html], and
|
131
|
+
SQLite3[https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SQLite3Adapter.html].
|
132
132
|
|
133
133
|
|
134
|
-
* Logging support for Log4r[https://github.com/colbygk/log4r] and Logger[https://ruby-
|
134
|
+
* Logging support for Log4r[https://github.com/colbygk/log4r] and Logger[https://docs.ruby-lang.org/en/master/Logger.html].
|
135
135
|
|
136
136
|
ActiveRecord::Base.logger = ActiveSupport::Logger.new(STDOUT)
|
137
137
|
ActiveRecord::Base.logger = Log4r::Logger.new('Application Log')
|
@@ -139,7 +139,7 @@ A short rundown of some of the major features:
|
|
139
139
|
|
140
140
|
* Database agnostic schema management with Migrations.
|
141
141
|
|
142
|
-
class AddSystemSettings < ActiveRecord::Migration[
|
142
|
+
class AddSystemSettings < ActiveRecord::Migration[8.0]
|
143
143
|
def up
|
144
144
|
create_table :system_settings do |t|
|
145
145
|
t.string :name
|
@@ -157,7 +157,7 @@ A short rundown of some of the major features:
|
|
157
157
|
end
|
158
158
|
end
|
159
159
|
|
160
|
-
{Learn more}[
|
160
|
+
{Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Migration.html]
|
161
161
|
|
162
162
|
|
163
163
|
== Philosophy
|
data/examples/performance.rb
CHANGED
@@ -176,10 +176,10 @@ Benchmark.ips(TIME) do |x|
|
|
176
176
|
end
|
177
177
|
|
178
178
|
x.report "Model.log" do
|
179
|
-
Exhibit.
|
179
|
+
Exhibit.lease_connection.send(:log, "hello", "world") { }
|
180
180
|
end
|
181
181
|
|
182
182
|
x.report "AR.execute(query)" do
|
183
|
-
ActiveRecord::Base.
|
183
|
+
ActiveRecord::Base.lease_connection.execute("SELECT * FROM exhibits WHERE id = #{(rand * 1000 + 1).to_i}")
|
184
184
|
end
|
185
185
|
end
|
@@ -22,7 +22,7 @@ module ActiveRecord
|
|
22
22
|
raise ArgumentError, "Bulk insert or upsert is currently not supported for has_many through association"
|
23
23
|
end
|
24
24
|
|
25
|
-
|
25
|
+
super
|
26
26
|
end
|
27
27
|
RUBY
|
28
28
|
end
|
@@ -43,6 +43,7 @@ module ActiveRecord
|
|
43
43
|
def exec_queries
|
44
44
|
super do |record|
|
45
45
|
@association.set_inverse_instance_from_queries(record)
|
46
|
+
@association.set_strict_loading(record)
|
46
47
|
yield record if block_given?
|
47
48
|
end
|
48
49
|
end
|
@@ -6,34 +6,38 @@ module ActiveRecord
|
|
6
6
|
module Associations
|
7
7
|
# Keeps track of table aliases for ActiveRecord::Associations::JoinDependency
|
8
8
|
class AliasTracker # :nodoc:
|
9
|
-
def self.create(
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
9
|
+
def self.create(pool, initial_table, joins, aliases = nil)
|
10
|
+
pool.with_connection do |connection|
|
11
|
+
if joins.empty?
|
12
|
+
aliases ||= Hash.new(0)
|
13
|
+
elsif aliases
|
14
|
+
default_proc = aliases.default_proc || proc { 0 }
|
15
|
+
aliases.default_proc = proc { |h, k|
|
16
|
+
h[k] = initial_count_for(connection, k, joins) + default_proc.call(h, k)
|
17
|
+
}
|
18
|
+
else
|
19
|
+
aliases = Hash.new { |h, k|
|
20
|
+
h[k] = initial_count_for(connection, k, joins)
|
21
|
+
}
|
22
|
+
end
|
23
|
+
aliases[initial_table] = 1
|
24
|
+
new(connection.table_alias_length, aliases)
|
21
25
|
end
|
22
|
-
aliases[initial_table] = 1
|
23
|
-
new(connection, aliases)
|
24
26
|
end
|
25
27
|
|
26
28
|
def self.initial_count_for(connection, name, table_joins)
|
27
|
-
|
29
|
+
quoted_name_escaped = nil
|
30
|
+
name_escaped = nil
|
28
31
|
|
29
32
|
counts = table_joins.map do |join|
|
30
33
|
if join.is_a?(Arel::Nodes::StringJoin)
|
31
|
-
#
|
32
|
-
|
34
|
+
# quoted_name_escaped should be case ignored as some database adapters (Oracle) return quoted name in uppercase
|
35
|
+
quoted_name_escaped ||= Regexp.escape(connection.quote_table_name(name))
|
36
|
+
name_escaped ||= Regexp.escape(name)
|
33
37
|
|
34
38
|
# Table names + table aliases
|
35
39
|
join.left.scan(
|
36
|
-
/JOIN(?:\s+\w+)?\s+(?:\S+\s+)?(?:#{
|
40
|
+
/JOIN(?:\s+\w+)?\s+(?:\S+\s+)?(?:#{quoted_name_escaped}|#{name_escaped})\sON/i
|
37
41
|
).size
|
38
42
|
elsif join.is_a?(Arel::Nodes::Join)
|
39
43
|
join.left.name == name ? 1 : 0
|
@@ -46,9 +50,9 @@ module ActiveRecord
|
|
46
50
|
end
|
47
51
|
|
48
52
|
# table_joins is an array of arel joins which might conflict with the aliases we assign here
|
49
|
-
def initialize(
|
50
|
-
@aliases
|
51
|
-
@
|
53
|
+
def initialize(table_alias_length, aliases)
|
54
|
+
@aliases = aliases
|
55
|
+
@table_alias_length = table_alias_length
|
52
56
|
end
|
53
57
|
|
54
58
|
def aliased_table_for(arel_table, table_name = nil)
|
@@ -60,7 +64,7 @@ module ActiveRecord
|
|
60
64
|
arel_table = arel_table.alias(table_name) if arel_table.name != table_name
|
61
65
|
else
|
62
66
|
# Otherwise, we need to use an alias
|
63
|
-
aliased_name =
|
67
|
+
aliased_name = table_alias_for(yield)
|
64
68
|
|
65
69
|
# Update the count
|
66
70
|
count = aliases[aliased_name] += 1
|
@@ -76,8 +80,12 @@ module ActiveRecord
|
|
76
80
|
attr_reader :aliases
|
77
81
|
|
78
82
|
private
|
83
|
+
def table_alias_for(table_name)
|
84
|
+
table_name[0...@table_alias_length].tr(".", "_")
|
85
|
+
end
|
86
|
+
|
79
87
|
def truncate(name)
|
80
|
-
name.slice(0, @
|
88
|
+
name.slice(0, @table_alias_length - 2)
|
81
89
|
end
|
82
90
|
end
|
83
91
|
end
|
@@ -34,7 +34,7 @@ module ActiveRecord
|
|
34
34
|
# the <tt>reflection</tt> object represents a <tt>:has_many</tt> macro.
|
35
35
|
class Association # :nodoc:
|
36
36
|
attr_accessor :owner
|
37
|
-
attr_reader :
|
37
|
+
attr_reader :reflection, :disable_joins
|
38
38
|
|
39
39
|
delegate :options, to: :reflection
|
40
40
|
|
@@ -50,10 +50,16 @@ module ActiveRecord
|
|
50
50
|
@skip_strict_loading = nil
|
51
51
|
end
|
52
52
|
|
53
|
+
def target
|
54
|
+
if @target.is_a?(Promise)
|
55
|
+
@target = @target.value
|
56
|
+
end
|
57
|
+
@target
|
58
|
+
end
|
59
|
+
|
53
60
|
# Resets the \loaded flag to +false+ and sets the \target to +nil+.
|
54
61
|
def reset
|
55
62
|
@loaded = false
|
56
|
-
@target = nil
|
57
63
|
@stale_state = nil
|
58
64
|
end
|
59
65
|
|
@@ -64,7 +70,7 @@ module ActiveRecord
|
|
64
70
|
# Reloads the \target and returns +self+ on success.
|
65
71
|
# The QueryCache is cleared if +force+ is true.
|
66
72
|
def reload(force = false)
|
67
|
-
klass.
|
73
|
+
klass.connection_pool.clear_query_cache if force && klass
|
68
74
|
reset
|
69
75
|
reset_scope
|
70
76
|
load_target
|
@@ -114,6 +120,14 @@ module ActiveRecord
|
|
114
120
|
@association_scope = nil
|
115
121
|
end
|
116
122
|
|
123
|
+
def set_strict_loading(record)
|
124
|
+
if owner.strict_loading_n_plus_one_only? && reflection.macro == :has_many
|
125
|
+
record.strict_loading!
|
126
|
+
else
|
127
|
+
record.strict_loading!(false, mode: owner.strict_loading_mode)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
117
131
|
# Set the inverse association, if possible
|
118
132
|
def set_inverse_instance(record)
|
119
133
|
if inverse = inverse_association_for(record)
|
@@ -173,7 +187,7 @@ module ActiveRecord
|
|
173
187
|
# ActiveRecord::RecordNotFound is rescued within the method, and it is
|
174
188
|
# not reraised. The proxy is \reset and +nil+ is the return value.
|
175
189
|
def load_target
|
176
|
-
@target = find_target if (@stale_state && stale_target?) || find_target?
|
190
|
+
@target = find_target(async: false) if (@stale_state && stale_target?) || find_target?
|
177
191
|
|
178
192
|
loaded! unless loaded?
|
179
193
|
target
|
@@ -181,6 +195,13 @@ module ActiveRecord
|
|
181
195
|
reset
|
182
196
|
end
|
183
197
|
|
198
|
+
def async_load_target # :nodoc:
|
199
|
+
@target = find_target(async: true) if (@stale_state && stale_target?) || find_target?
|
200
|
+
|
201
|
+
loaded! unless loaded?
|
202
|
+
nil
|
203
|
+
end
|
204
|
+
|
184
205
|
# We can't dump @reflection and @through_reflection since it contains the scope proc
|
185
206
|
def marshal_dump
|
186
207
|
ivars = (instance_variables - [:@reflection, :@through_reflection]).map { |name| [name, instance_variable_get(name)] }
|
@@ -211,6 +232,12 @@ module ActiveRecord
|
|
211
232
|
_create_record(attributes, true, &block)
|
212
233
|
end
|
213
234
|
|
235
|
+
# Whether the association represent a single record
|
236
|
+
# or a collection of records.
|
237
|
+
def collection?
|
238
|
+
false
|
239
|
+
end
|
240
|
+
|
214
241
|
private
|
215
242
|
# Reader and writer methods call this so that consistent errors are presented
|
216
243
|
# when the association target class does not exist.
|
@@ -218,13 +245,19 @@ module ActiveRecord
|
|
218
245
|
klass
|
219
246
|
end
|
220
247
|
|
221
|
-
def find_target
|
248
|
+
def find_target(async: false)
|
222
249
|
if violates_strict_loading?
|
223
250
|
Base.strict_loading_violation!(owner: owner.class, reflection: reflection)
|
224
251
|
end
|
225
252
|
|
226
253
|
scope = self.scope
|
227
|
-
|
254
|
+
if skip_statement_cache?(scope)
|
255
|
+
if async
|
256
|
+
return scope.load_async.then(&:to_a)
|
257
|
+
else
|
258
|
+
return scope.to_a
|
259
|
+
end
|
260
|
+
end
|
228
261
|
|
229
262
|
sc = reflection.association_scope_cache(klass, owner) do |params|
|
230
263
|
as = AssociationScope.create { params.bind }
|
@@ -232,12 +265,10 @@ module ActiveRecord
|
|
232
265
|
end
|
233
266
|
|
234
267
|
binds = AssociationScope.get_bind_values(owner, reflection.chain)
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
record
|
239
|
-
else
|
240
|
-
record.strict_loading!(false, mode: owner.strict_loading_mode)
|
268
|
+
klass.with_connection do |c|
|
269
|
+
sc.execute(binds, c, async: async) do |record|
|
270
|
+
set_inverse_instance(record)
|
271
|
+
set_strict_loading(record)
|
241
272
|
end
|
242
273
|
end
|
243
274
|
end
|
@@ -19,10 +19,16 @@ module ActiveRecord
|
|
19
19
|
id = owner.public_send(reflection.foreign_key)
|
20
20
|
end
|
21
21
|
|
22
|
+
association_class = if reflection.polymorphic?
|
23
|
+
owner.public_send(reflection.foreign_type)
|
24
|
+
else
|
25
|
+
reflection.klass
|
26
|
+
end
|
27
|
+
|
22
28
|
enqueue_destroy_association(
|
23
29
|
owner_model_name: owner.class.to_s,
|
24
30
|
owner_id: owner.id,
|
25
|
-
association_class:
|
31
|
+
association_class: association_class.to_s,
|
26
32
|
association_ids: [id],
|
27
33
|
association_primary_key_column: primary_key_column,
|
28
34
|
ensuring_owner_was_method: options.fetch(:ensuring_owner_was, nil)
|
@@ -124,12 +130,20 @@ module ActiveRecord
|
|
124
130
|
end
|
125
131
|
|
126
132
|
def replace_keys(record, force: false)
|
127
|
-
|
128
|
-
reflection_fk
|
133
|
+
reflection_fk = reflection.foreign_key
|
134
|
+
if reflection_fk.is_a?(Array)
|
135
|
+
target_key_values = record ? Array(primary_key(record.class)).map { |key| record._read_attribute(key) } : []
|
136
|
+
|
137
|
+
if force || reflection_fk.map { |fk| owner._read_attribute(fk) } != target_key_values
|
138
|
+
reflection_fk.each_with_index do |key, index|
|
139
|
+
owner[key] = target_key_values[index]
|
140
|
+
end
|
141
|
+
end
|
142
|
+
else
|
143
|
+
target_key_value = record ? record._read_attribute(primary_key(record.class)) : nil
|
129
144
|
|
130
|
-
|
131
|
-
|
132
|
-
owner[key] = value
|
145
|
+
if force || owner._read_attribute(reflection_fk) != target_key_value
|
146
|
+
owner[reflection_fk] = target_key_value
|
133
147
|
end
|
134
148
|
end
|
135
149
|
end
|
@@ -148,8 +162,7 @@ module ActiveRecord
|
|
148
162
|
end
|
149
163
|
|
150
164
|
def stale_state
|
151
|
-
|
152
|
-
result && result.to_s
|
165
|
+
owner._read_attribute(reflection.foreign_key) { |n| owner.send(:missing_attribute, n, caller) }
|
153
166
|
end
|
154
167
|
end
|
155
168
|
end
|
@@ -30,10 +30,10 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
30
30
|
end
|
31
31
|
|
32
32
|
reflection = create_reflection(model, name, scope, options, &block)
|
33
|
-
define_accessors
|
34
|
-
define_callbacks
|
35
|
-
define_validations
|
36
|
-
define_change_tracking_methods
|
33
|
+
define_accessors(model, reflection)
|
34
|
+
define_callbacks(model, reflection)
|
35
|
+
define_validations(model, reflection)
|
36
|
+
define_change_tracking_methods(model, reflection)
|
37
37
|
reflection
|
38
38
|
end
|
39
39
|
|
@@ -71,6 +71,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
71
71
|
end
|
72
72
|
|
73
73
|
def self.define_extensions(model, name)
|
74
|
+
# noop
|
74
75
|
end
|
75
76
|
|
76
77
|
def self.define_callbacks(model, reflection)
|
@@ -81,7 +82,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
81
82
|
end
|
82
83
|
|
83
84
|
Association.extensions.each do |extension|
|
84
|
-
extension.build
|
85
|
+
extension.build(model, reflection)
|
85
86
|
end
|
86
87
|
end
|
87
88
|
|
@@ -131,7 +132,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
131
132
|
err_message = "A valid destroy_association_async_job is required to use `dependent: :destroy_async` on associations"
|
132
133
|
raise ActiveRecord::ConfigurationError, err_message
|
133
134
|
end
|
134
|
-
unless valid_dependent_options.include?
|
135
|
+
unless valid_dependent_options.include?(dependent)
|
135
136
|
raise ArgumentError, "The :dependent option must be one of #{valid_dependent_options}, but is :#{dependent}"
|
136
137
|
end
|
137
138
|
end
|
@@ -38,6 +38,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
38
38
|
|
39
39
|
klass = reflection.class_name.safe_constantize
|
40
40
|
klass._counter_cache_columns |= [cache_column] if klass && klass.respond_to?(:_counter_cache_columns)
|
41
|
+
model.counter_cached_association_names |= [reflection.name]
|
41
42
|
end
|
42
43
|
|
43
44
|
def self.touch_record(o, changes, foreign_key, name, touch) # :nodoc:
|
@@ -42,8 +42,8 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
42
42
|
self.right_reflection = _reflect_on_association(rhs_name)
|
43
43
|
end
|
44
44
|
|
45
|
-
def self.
|
46
|
-
left_model.
|
45
|
+
def self.connection_pool
|
46
|
+
left_model.connection_pool
|
47
47
|
end
|
48
48
|
}
|
49
49
|
|
@@ -7,11 +7,10 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def self.valid_options(options)
|
10
|
-
valid = super + [:counter_cache, :join_table, :index_errors]
|
11
|
-
valid += [:
|
12
|
-
valid += [:
|
10
|
+
valid = super + [:counter_cache, :join_table, :index_errors, :as, :through]
|
11
|
+
valid += [:foreign_type] if options[:as]
|
12
|
+
valid += [:source, :source_type, :disable_joins] if options[:through]
|
13
13
|
valid += [:ensuring_owner_was] if options[:dependent] == :destroy_async
|
14
|
-
valid += [:disable_joins] if options[:disable_joins] && options[:through]
|
15
14
|
valid
|
16
15
|
end
|
17
16
|
|
@@ -7,11 +7,10 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def self.valid_options(options)
|
10
|
-
valid = super
|
11
|
-
valid += [:
|
10
|
+
valid = super + [:as, :through]
|
11
|
+
valid += [:foreign_type] if options[:as]
|
12
12
|
valid += [:ensuring_owner_was] if options[:dependent] == :destroy_async
|
13
|
-
valid += [:
|
14
|
-
valid += [:disable_joins] if options[:disable_joins] && options[:through]
|
13
|
+
valid += [:source, :source_type, :disable_joins] if options[:through]
|
15
14
|
valid
|
16
15
|
end
|
17
16
|
|
@@ -28,6 +28,8 @@ module ActiveRecord
|
|
28
28
|
# If you need to work on all current children, new and existing records,
|
29
29
|
# +load_target+ and the +loaded+ flag are your friends.
|
30
30
|
class CollectionAssociation < Association # :nodoc:
|
31
|
+
attr_accessor :nested_attributes_target
|
32
|
+
|
31
33
|
# Implements the reader method, e.g. foo.items for Foo.has_many :items
|
32
34
|
def reader
|
33
35
|
ensure_klass_exists!
|
@@ -92,7 +94,7 @@ module ActiveRecord
|
|
92
94
|
def find(*args)
|
93
95
|
if options[:inverse_of] && loaded?
|
94
96
|
args_flatten = args.flatten
|
95
|
-
model = scope.
|
97
|
+
model = scope.model
|
96
98
|
|
97
99
|
if args_flatten.blank?
|
98
100
|
error_message = "Couldn't find #{model.name} without an ID"
|
@@ -228,7 +230,7 @@ module ActiveRecord
|
|
228
230
|
# loaded and you are going to fetch the records anyway it is better to
|
229
231
|
# check <tt>collection.length.zero?</tt>.
|
230
232
|
def empty?
|
231
|
-
if loaded? || @association_ids || reflection.
|
233
|
+
if loaded? || @association_ids || reflection.has_active_cached_counter?
|
232
234
|
size.zero?
|
233
235
|
else
|
234
236
|
target.empty? && !scope.exists?
|
@@ -254,14 +256,16 @@ module ActiveRecord
|
|
254
256
|
end
|
255
257
|
|
256
258
|
def include?(record)
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
259
|
+
klass = reflection.klass
|
260
|
+
return false unless record.is_a?(klass)
|
261
|
+
|
262
|
+
if record.new_record?
|
263
|
+
include_in_memory?(record)
|
264
|
+
elsif loaded?
|
265
|
+
target.include?(record)
|
263
266
|
else
|
264
|
-
|
267
|
+
record_id = klass.composite_primary_key? ? klass.primary_key.zip(record.id).to_h : record.id
|
268
|
+
scope.exists?(record_id)
|
265
269
|
end
|
266
270
|
end
|
267
271
|
|
@@ -309,6 +313,10 @@ module ActiveRecord
|
|
309
313
|
target.any? { |record| record.new_record? || record.changed? }
|
310
314
|
end
|
311
315
|
|
316
|
+
def collection?
|
317
|
+
true
|
318
|
+
end
|
319
|
+
|
312
320
|
private
|
313
321
|
def transaction(&block)
|
314
322
|
reflection.klass.transaction(&block)
|
@@ -928,7 +928,20 @@ module ActiveRecord
|
|
928
928
|
!!@association.include?(record)
|
929
929
|
end
|
930
930
|
|
931
|
-
|
931
|
+
# Returns the association object for the collection.
|
932
|
+
#
|
933
|
+
# class Person < ActiveRecord::Base
|
934
|
+
# has_many :pets
|
935
|
+
# end
|
936
|
+
#
|
937
|
+
# person.pets.proxy_association
|
938
|
+
# # => #<ActiveRecord::Associations::HasManyAssociation owner="#<Person:0x00>">
|
939
|
+
#
|
940
|
+
# Returns the same object as <tt>person.association(:pets)</tt>,
|
941
|
+
# allowing you to make calls like <tt>person.pets.proxy_association.owner</tt>.
|
942
|
+
#
|
943
|
+
# See Associations::ClassMethods@Association+extensions for more.
|
944
|
+
def proxy_association
|
932
945
|
@association
|
933
946
|
end
|
934
947
|
|
@@ -47,7 +47,7 @@ module ActiveRecord
|
|
47
47
|
end
|
48
48
|
|
49
49
|
if scope.order_values.empty? && ordered
|
50
|
-
split_scope = DisableJoinsAssociationRelation.create(scope.
|
50
|
+
split_scope = DisableJoinsAssociationRelation.create(scope.model, key, join_ids)
|
51
51
|
split_scope.where_clause += scope.where_clause
|
52
52
|
split_scope
|
53
53
|
else
|