activerecord 7.0.8.7 → 7.2.2.1
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 +631 -1944
- data/MIT-LICENSE +1 -1
- data/README.rdoc +29 -29
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +2 -2
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +35 -12
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +23 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +22 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
- 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/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +26 -14
- data/lib/active_record/associations/collection_proxy.rb +29 -11
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +21 -14
- data/lib/active_record/associations/has_many_through_association.rb +17 -7
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +30 -27
- data/lib/active_record/associations/join_dependency.rb +10 -10
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +33 -8
- 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/preloader.rb +13 -10
- data/lib/active_record/associations/singular_association.rb +7 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +354 -485
- data/lib/active_record/attribute_assignment.rb +0 -4
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +53 -35
- data/lib/active_record/attribute_methods/primary_key.rb +45 -25
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +8 -7
- data/lib/active_record/attribute_methods/serialization.rb +131 -32
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -6
- data/lib/active_record/attribute_methods/write.rb +6 -6
- data/lib/active_record/attribute_methods.rb +148 -33
- data/lib/active_record/attributes.rb +64 -50
- data/lib/active_record/autosave_association.rb +69 -37
- data/lib/active_record/base.rb +9 -5
- data/lib/active_record/callbacks.rb +11 -25
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -42
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +123 -131
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +4 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +323 -88
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +160 -45
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +217 -63
- data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -63
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +307 -129
- data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
- data/lib/active_record/connection_adapters/abstract_adapter.rb +510 -111
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +278 -125
- data/lib/active_record/connection_adapters/column.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +26 -139
- data/lib/active_record/connection_adapters/mysql/quoting.rb +53 -54
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +25 -13
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +101 -68
- data/lib/active_record/connection_adapters/pool_config.rb +20 -10
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +14 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +100 -43
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +65 -61
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +151 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +370 -63
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +367 -201
- data/lib/active_record/connection_adapters/schema_cache.rb +302 -79
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +60 -43
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +45 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +14 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +50 -8
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +290 -110
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
- data/lib/active_record/connection_adapters.rb +124 -1
- data/lib/active_record/connection_handling.rb +96 -104
- data/lib/active_record/core.rb +251 -176
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
- data/lib/active_record/database_configurations/database_config.rb +26 -5
- data/lib/active_record/database_configurations/hash_config.rb +52 -34
- data/lib/active_record/database_configurations/url_config.rb +37 -12
- data/lib/active_record/database_configurations.rb +87 -34
- data/lib/active_record/delegated_type.rb +39 -10
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +3 -1
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
- data/lib/active_record/encryption/config.rb +25 -1
- data/lib/active_record/encryption/configurable.rb +12 -19
- data/lib/active_record/encryption/context.rb +10 -3
- data/lib/active_record/encryption/contexts.rb +5 -1
- data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
- data/lib/active_record/encryption/encryptable_record.rb +45 -21
- data/lib/active_record/encryption/encrypted_attribute_type.rb +47 -12
- data/lib/active_record/encryption/encryptor.rb +18 -3
- data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
- data/lib/active_record/encryption/key_generator.rb +12 -1
- 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 +6 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/properties.rb +3 -3
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +22 -21
- data/lib/active_record/encryption.rb +3 -0
- data/lib/active_record/enum.rb +129 -28
- data/lib/active_record/errors.rb +151 -31
- data/lib/active_record/explain.rb +21 -12
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +29 -8
- data/lib/active_record/fixtures.rb +167 -97
- data/lib/active_record/future_result.rb +47 -8
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +34 -18
- data/lib/active_record/insert_all.rb +72 -22
- data/lib/active_record/integration.rb +11 -8
- data/lib/active_record/internal_metadata.rb +124 -20
- data/lib/active_record/locking/optimistic.rb +8 -7
- data/lib/active_record/locking/pessimistic.rb +5 -2
- data/lib/active_record/log_subscriber.rb +18 -22
- data/lib/active_record/marshalling.rb +59 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
- data/lib/active_record/middleware/database_selector.rb +6 -8
- data/lib/active_record/middleware/shard_selector.rb +3 -1
- data/lib/active_record/migration/command_recorder.rb +106 -8
- data/lib/active_record/migration/compatibility.rb +147 -5
- data/lib/active_record/migration/default_strategy.rb +22 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +234 -117
- data/lib/active_record/model_schema.rb +90 -102
- data/lib/active_record/nested_attributes.rb +48 -11
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +168 -339
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +18 -25
- data/lib/active_record/query_logs.rb +92 -52
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +33 -8
- data/lib/active_record/railtie.rb +129 -85
- data/lib/active_record/railties/controller_runtime.rb +22 -7
- data/lib/active_record/railties/databases.rake +145 -154
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +32 -5
- data/lib/active_record/reflection.rb +267 -69
- data/lib/active_record/relation/batches/batch_enumerator.rb +20 -5
- data/lib/active_record/relation/batches.rb +198 -63
- data/lib/active_record/relation/calculations.rb +250 -93
- data/lib/active_record/relation/delegation.rb +30 -19
- data/lib/active_record/relation/finder_methods.rb +93 -18
- data/lib/active_record/relation/merger.rb +6 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +18 -3
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +28 -16
- data/lib/active_record/relation/query_attribute.rb +2 -1
- data/lib/active_record/relation/query_methods.rb +576 -107
- data/lib/active_record/relation/record_fetch_warning.rb +3 -0
- data/lib/active_record/relation/spawn_methods.rb +5 -4
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +580 -90
- data/lib/active_record/result.rb +49 -48
- data/lib/active_record/runtime_registry.rb +63 -1
- data/lib/active_record/sanitization.rb +70 -25
- data/lib/active_record/schema.rb +8 -7
- data/lib/active_record/schema_dumper.rb +63 -14
- data/lib/active_record/schema_migration.rb +75 -24
- data/lib/active_record/scoping/default.rb +15 -5
- data/lib/active_record/scoping/named.rb +3 -2
- data/lib/active_record/scoping.rb +2 -1
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/signed_id.rb +27 -6
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/store.rb +8 -8
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +1 -1
- data/lib/active_record/tasks/database_tasks.rb +190 -118
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
- data/lib/active_record/test_fixtures.rb +170 -155
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +31 -17
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +12 -7
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +106 -24
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +1 -3
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +9 -3
- data/lib/active_record/validations/numericality.rb +5 -4
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +61 -11
- data/lib/active_record/validations.rb +12 -5
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +247 -33
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +2 -0
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -7
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/homogeneous_in.rb +1 -9
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +115 -5
- data/lib/arel/nodes/sql_literal.rb +13 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes.rb +6 -2
- data/lib/arel/predications.rb +3 -1
- data/lib/arel/select_manager.rb +1 -1
- data/lib/arel/table.rb +9 -5
- data/lib/arel/tree_manager.rb +8 -3
- data/lib/arel/update_manager.rb +2 -1
- data/lib/arel/visitors/dot.rb +1 -0
- data/lib/arel/visitors/mysql.rb +17 -5
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/sqlite.rb +25 -0
- data/lib/arel/visitors/to_sql.rb +112 -34
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +21 -3
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- data/lib/rails/generators/active_record/migration.rb +3 -1
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
- metadata +54 -12
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
@@ -4,7 +4,7 @@ require "active_support/inflector"
|
|
4
4
|
require "active_support/core_ext/hash/indifferent_access"
|
5
5
|
|
6
6
|
module ActiveRecord
|
7
|
-
#
|
7
|
+
# = Single table inheritance
|
8
8
|
#
|
9
9
|
# Active Record allows inheritance by storing the name of the class in a column that by
|
10
10
|
# default is named "type" (can be changed by overwriting <tt>Base.inheritance_column</tt>).
|
@@ -32,8 +32,9 @@ module ActiveRecord
|
|
32
32
|
# be triggered. In that case, it'll work just like normal subclasses with no special magic
|
33
33
|
# for differentiating between them or reloading the right type with find.
|
34
34
|
#
|
35
|
-
# Note, all the attributes for all the cases are kept in the same table.
|
36
|
-
#
|
35
|
+
# Note, all the attributes for all the cases are kept in the same table.
|
36
|
+
# Read more:
|
37
|
+
# * https://www.martinfowler.com/eaaCatalog/singleTableInheritance.html
|
37
38
|
#
|
38
39
|
module Inheritance
|
39
40
|
extend ActiveSupport::Concern
|
@@ -93,14 +94,24 @@ module ActiveRecord
|
|
93
94
|
:true == (@finder_needs_type_condition ||= descends_from_active_record? ? :false : :true)
|
94
95
|
end
|
95
96
|
|
96
|
-
# Returns the class
|
97
|
-
#
|
97
|
+
# Returns the first class in the inheritance hierarchy that descends from either an
|
98
|
+
# abstract class or from <tt>ActiveRecord::Base</tt>.
|
98
99
|
#
|
99
|
-
#
|
100
|
-
# through some arbitrarily deep hierarchy, B.base_class will return A.
|
100
|
+
# Consider the following behaviour:
|
101
101
|
#
|
102
|
-
#
|
103
|
-
#
|
102
|
+
# class ApplicationRecord < ActiveRecord::Base
|
103
|
+
# self.abstract_class = true
|
104
|
+
# end
|
105
|
+
# class Shape < ApplicationRecord
|
106
|
+
# self.abstract_class = true
|
107
|
+
# end
|
108
|
+
# Polygon = Class.new(Shape)
|
109
|
+
# Square = Class.new(Polygon)
|
110
|
+
#
|
111
|
+
# ApplicationRecord.base_class # => ApplicationRecord
|
112
|
+
# Shape.base_class # => Shape
|
113
|
+
# Polygon.base_class # => Polygon
|
114
|
+
# Square.base_class # => Polygon
|
104
115
|
attr_reader :base_class
|
105
116
|
|
106
117
|
# Returns whether the class is a base class.
|
@@ -116,7 +127,7 @@ module ActiveRecord
|
|
116
127
|
# true.
|
117
128
|
# +ApplicationRecord+, for example, is generated as an abstract class.
|
118
129
|
#
|
119
|
-
# Consider the following default
|
130
|
+
# Consider the following default behavior:
|
120
131
|
#
|
121
132
|
# Shape = Class.new(ActiveRecord::Base)
|
122
133
|
# Polygon = Class.new(Shape)
|
@@ -154,7 +165,7 @@ module ActiveRecord
|
|
154
165
|
|
155
166
|
# Returns whether this class is an abstract class or not.
|
156
167
|
def abstract_class?
|
157
|
-
|
168
|
+
@abstract_class == true
|
158
169
|
end
|
159
170
|
|
160
171
|
# Sets the application record class for Active Record
|
@@ -191,7 +202,9 @@ module ActiveRecord
|
|
191
202
|
"The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " \
|
192
203
|
"This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " \
|
193
204
|
"Please rename this column if you didn't intend it to be used for storing the inheritance class " \
|
194
|
-
"or overwrite #{name}.inheritance_column to use another column for that information."
|
205
|
+
"or overwrite #{name}.inheritance_column to use another column for that information. " \
|
206
|
+
"If you wish to disable single-table inheritance for #{name} set " \
|
207
|
+
"#{name}.inheritance_column to nil"
|
195
208
|
end
|
196
209
|
|
197
210
|
# Returns the value to be stored in the polymorphic type column for Polymorphic Associations.
|
@@ -210,12 +223,6 @@ module ActiveRecord
|
|
210
223
|
end
|
211
224
|
end
|
212
225
|
|
213
|
-
def inherited(subclass)
|
214
|
-
subclass.set_base_class
|
215
|
-
subclass.instance_variable_set(:@_type_candidates_cache, Concurrent::Map.new)
|
216
|
-
super
|
217
|
-
end
|
218
|
-
|
219
226
|
def dup # :nodoc:
|
220
227
|
# `initialize_dup` / `initialize_copy` don't work when defined
|
221
228
|
# in the `singleton_class`.
|
@@ -277,6 +284,15 @@ module ActiveRecord
|
|
277
284
|
end
|
278
285
|
|
279
286
|
private
|
287
|
+
def inherited(subclass)
|
288
|
+
super
|
289
|
+
subclass.set_base_class
|
290
|
+
subclass.instance_variable_set(:@_type_candidates_cache, Concurrent::Map.new)
|
291
|
+
subclass.class_eval do
|
292
|
+
@finder_needs_type_condition = nil
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
280
296
|
# Called by +instantiate+ to decide which class to use for a new
|
281
297
|
# record instance. For single-table inheritance, we check the record
|
282
298
|
# for a +type+ column and return the corresponding class.
|
@@ -7,34 +7,47 @@ module ActiveRecord
|
|
7
7
|
attr_reader :model, :connection, :inserts, :keys
|
8
8
|
attr_reader :on_duplicate, :update_only, :returning, :unique_by, :update_sql
|
9
9
|
|
10
|
-
|
11
|
-
|
10
|
+
class << self
|
11
|
+
def execute(relation, ...)
|
12
|
+
relation.model.with_connection do |c|
|
13
|
+
new(relation, c, ...).execute
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
12
17
|
|
13
|
-
|
18
|
+
def initialize(relation, connection, inserts, on_duplicate:, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
|
19
|
+
@relation = relation
|
20
|
+
@model, @connection, @inserts = relation.model, connection, inserts.map(&:stringify_keys)
|
14
21
|
@on_duplicate, @update_only, @returning, @unique_by = on_duplicate, update_only, returning, unique_by
|
15
22
|
@record_timestamps = record_timestamps.nil? ? model.record_timestamps : record_timestamps
|
16
23
|
|
17
24
|
disallow_raw_sql!(on_duplicate)
|
18
25
|
disallow_raw_sql!(returning)
|
19
26
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
27
|
+
if @inserts.empty?
|
28
|
+
@keys = []
|
29
|
+
else
|
30
|
+
resolve_sti
|
31
|
+
resolve_attribute_aliases
|
32
|
+
@keys = @inserts.first.keys
|
25
33
|
end
|
34
|
+
|
35
|
+
@scope_attributes = relation.scope_for_create.except(@model.inheritance_column)
|
36
|
+
@keys |= @scope_attributes.keys
|
26
37
|
@keys = @keys.to_set
|
27
38
|
|
28
39
|
@returning = (connection.supports_insert_returning? ? primary_keys : false) if @returning.nil?
|
29
40
|
@returning = false if @returning == []
|
30
41
|
|
31
|
-
@unique_by = find_unique_index_for(unique_by)
|
32
|
-
@on_duplicate = :skip if @on_duplicate == :update && updatable_columns.empty?
|
42
|
+
@unique_by = find_unique_index_for(@unique_by)
|
33
43
|
|
44
|
+
configure_on_duplicate_update_logic
|
34
45
|
ensure_valid_options_for_connection!
|
35
46
|
end
|
36
47
|
|
37
48
|
def execute
|
49
|
+
return ActiveRecord::Result.empty if inserts.empty?
|
50
|
+
|
38
51
|
message = +"#{model} "
|
39
52
|
message << "Bulk " if inserts.many?
|
40
53
|
message << (on_duplicate == :update ? "Upsert" : "Insert")
|
@@ -46,10 +59,9 @@ module ActiveRecord
|
|
46
59
|
end
|
47
60
|
|
48
61
|
def primary_keys
|
49
|
-
Array(
|
62
|
+
Array(@model.schema_cache.primary_keys(model.table_name))
|
50
63
|
end
|
51
64
|
|
52
|
-
|
53
65
|
def skip_duplicates?
|
54
66
|
on_duplicate == :skip
|
55
67
|
end
|
@@ -61,7 +73,7 @@ module ActiveRecord
|
|
61
73
|
def map_key_with_value
|
62
74
|
inserts.map do |attributes|
|
63
75
|
attributes = attributes.stringify_keys
|
64
|
-
attributes.merge!(scope_attributes)
|
76
|
+
attributes.merge!(@scope_attributes)
|
65
77
|
attributes.reverse_merge!(timestamps_for_create) if record_timestamps?
|
66
78
|
|
67
79
|
verify_attributes(attributes)
|
@@ -76,7 +88,7 @@ module ActiveRecord
|
|
76
88
|
@record_timestamps
|
77
89
|
end
|
78
90
|
|
79
|
-
# TODO: Consider
|
91
|
+
# TODO: Consider renaming this method, as it only conditionally extends keys, not always
|
80
92
|
def keys_including_timestamps
|
81
93
|
@keys_including_timestamps ||= if record_timestamps?
|
82
94
|
keys + model.all_timestamp_attributes_in_model
|
@@ -86,7 +98,33 @@ module ActiveRecord
|
|
86
98
|
end
|
87
99
|
|
88
100
|
private
|
89
|
-
|
101
|
+
def has_attribute_aliases?(attributes)
|
102
|
+
attributes.keys.any? { |attribute| model.attribute_alias?(attribute) }
|
103
|
+
end
|
104
|
+
|
105
|
+
def resolve_sti
|
106
|
+
return if model.descends_from_active_record?
|
107
|
+
|
108
|
+
sti_type = model.sti_name
|
109
|
+
@inserts = @inserts.map do |insert|
|
110
|
+
insert.reverse_merge(model.inheritance_column.to_s => sti_type)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def resolve_attribute_aliases
|
115
|
+
return unless has_attribute_aliases?(@inserts.first)
|
116
|
+
|
117
|
+
@inserts = @inserts.map do |insert|
|
118
|
+
insert.transform_keys { |attribute| resolve_attribute_alias(attribute) }
|
119
|
+
end
|
120
|
+
|
121
|
+
@update_only = Array(@update_only).map { |attribute| resolve_attribute_alias(attribute) } if @update_only
|
122
|
+
@unique_by = Array(@unique_by).map { |attribute| resolve_attribute_alias(attribute) } if @unique_by
|
123
|
+
end
|
124
|
+
|
125
|
+
def resolve_attribute_alias(attribute)
|
126
|
+
model.attribute_alias(attribute) || attribute
|
127
|
+
end
|
90
128
|
|
91
129
|
def configure_on_duplicate_update_logic
|
92
130
|
if custom_update_sql_provided? && update_only.present?
|
@@ -99,6 +137,8 @@ module ActiveRecord
|
|
99
137
|
elsif custom_update_sql_provided?
|
100
138
|
@update_sql = on_duplicate
|
101
139
|
@on_duplicate = :update
|
140
|
+
elsif @on_duplicate == :update && updatable_columns.empty?
|
141
|
+
@on_duplicate = :skip
|
102
142
|
end
|
103
143
|
end
|
104
144
|
|
@@ -115,8 +155,9 @@ module ActiveRecord
|
|
115
155
|
|
116
156
|
name_or_columns = unique_by || model.primary_key
|
117
157
|
match = Array(name_or_columns).map(&:to_s)
|
158
|
+
sorted_match = match.sort
|
118
159
|
|
119
|
-
if index = unique_indexes.find { |i| match.include?(i.name) || i.columns ==
|
160
|
+
if index = unique_indexes.find { |i| match.include?(i.name) || Array(i.columns).sort == sorted_match }
|
120
161
|
index
|
121
162
|
elsif match == primary_keys
|
122
163
|
unique_by.nil? ? nil : ActiveRecord::ConnectionAdapters::IndexDefinition.new(model.table_name, "#{model.table_name}_primary_key", true, match)
|
@@ -126,10 +167,9 @@ module ActiveRecord
|
|
126
167
|
end
|
127
168
|
|
128
169
|
def unique_indexes
|
129
|
-
|
170
|
+
@model.schema_cache.indexes(model.table_name).select(&:unique)
|
130
171
|
end
|
131
172
|
|
132
|
-
|
133
173
|
def ensure_valid_options_for_connection!
|
134
174
|
if returning && !connection.supports_insert_returning?
|
135
175
|
raise ArgumentError, "#{connection.class} does not support :returning"
|
@@ -155,7 +195,7 @@ module ActiveRecord
|
|
155
195
|
|
156
196
|
|
157
197
|
def readonly_columns
|
158
|
-
primary_keys + model.readonly_attributes
|
198
|
+
primary_keys + model.readonly_attributes
|
159
199
|
end
|
160
200
|
|
161
201
|
def unique_by_columns
|
@@ -212,7 +252,13 @@ module ActiveRecord
|
|
212
252
|
if insert_all.returning.is_a?(String)
|
213
253
|
insert_all.returning
|
214
254
|
else
|
215
|
-
|
255
|
+
Array(insert_all.returning).map do |attribute|
|
256
|
+
if model.attribute_alias?(attribute)
|
257
|
+
"#{quote_column(model.attribute_alias(attribute))} AS #{quote_column(attribute)}"
|
258
|
+
else
|
259
|
+
quote_column(attribute)
|
260
|
+
end
|
261
|
+
end.join(",")
|
216
262
|
end
|
217
263
|
end
|
218
264
|
|
@@ -258,7 +304,7 @@ module ActiveRecord
|
|
258
304
|
end
|
259
305
|
|
260
306
|
def extract_types_from_columns_on(table_name, keys:)
|
261
|
-
columns =
|
307
|
+
columns = @model.schema_cache.columns_hash(table_name)
|
262
308
|
|
263
309
|
unknown_column = (keys - columns.keys).first
|
264
310
|
raise UnknownAttributeError.new(model.new, unknown_column) if unknown_column
|
@@ -271,7 +317,11 @@ module ActiveRecord
|
|
271
317
|
end
|
272
318
|
|
273
319
|
def quote_columns(columns)
|
274
|
-
columns.map(
|
320
|
+
columns.map { |column| quote_column(column) }
|
321
|
+
end
|
322
|
+
|
323
|
+
def quote_column(column)
|
324
|
+
connection.quote_column_name(column)
|
275
325
|
end
|
276
326
|
end
|
277
327
|
end
|
@@ -10,7 +10,7 @@ module ActiveRecord
|
|
10
10
|
##
|
11
11
|
# :singleton-method:
|
12
12
|
# Indicates the format used to generate the timestamp in the cache key, if
|
13
|
-
# versioning is off. Accepts any of the symbols in
|
13
|
+
# versioning is off. Accepts any of the symbols in +Time::DATE_FORMATS+.
|
14
14
|
#
|
15
15
|
# This is +:usec+, by default.
|
16
16
|
class_attribute :cache_timestamp_format, instance_writer: false, default: :usec
|
@@ -20,7 +20,7 @@ module ActiveRecord
|
|
20
20
|
# Indicates whether to use a stable #cache_key method that is accompanied
|
21
21
|
# by a changing version in the #cache_version method.
|
22
22
|
#
|
23
|
-
# This is +true+, by default on Rails 5.2 and above.
|
23
|
+
# This is +true+, by default on \Rails 5.2 and above.
|
24
24
|
class_attribute :cache_versioning, instance_writer: false, default: false
|
25
25
|
|
26
26
|
##
|
@@ -28,7 +28,7 @@ module ActiveRecord
|
|
28
28
|
# Indicates whether to use a stable #cache_key method that is accompanied
|
29
29
|
# by a changing version in the #cache_version method on collections.
|
30
30
|
#
|
31
|
-
# This is +false+, by default until Rails 6.1.
|
31
|
+
# This is +false+, by default until \Rails 6.1.
|
32
32
|
class_attribute :collection_cache_versioning, instance_writer: false, default: false
|
33
33
|
end
|
34
34
|
|
@@ -55,8 +55,8 @@ module ActiveRecord
|
|
55
55
|
# user = User.find_by(name: 'Phusion')
|
56
56
|
# user_path(user) # => "/users/Phusion"
|
57
57
|
def to_param
|
58
|
-
|
59
|
-
id
|
58
|
+
return unless id
|
59
|
+
Array(id).join(self.class.param_delimiter)
|
60
60
|
end
|
61
61
|
|
62
62
|
# Returns a stable cache key that can be used to identify this record.
|
@@ -64,7 +64,7 @@ module ActiveRecord
|
|
64
64
|
# Product.new.cache_key # => "products/new"
|
65
65
|
# Product.find(5).cache_key # => "products/5"
|
66
66
|
#
|
67
|
-
# If ActiveRecord::Base.cache_versioning is turned off, as it was in Rails 5.1 and earlier,
|
67
|
+
# If ActiveRecord::Base.cache_versioning is turned off, as it was in \Rails 5.1 and earlier,
|
68
68
|
# the cache key will also include a version.
|
69
69
|
#
|
70
70
|
# Product.cache_versioning = false
|
@@ -106,7 +106,7 @@ module ActiveRecord
|
|
106
106
|
timestamp.utc.to_fs(cache_timestamp_format)
|
107
107
|
end
|
108
108
|
elsif self.class.has_attribute?("updated_at")
|
109
|
-
raise ActiveModel::MissingAttributeError, "missing attribute
|
109
|
+
raise ActiveModel::MissingAttributeError, "missing attribute 'updated_at' for #{self.class}"
|
110
110
|
end
|
111
111
|
end
|
112
112
|
|
@@ -178,7 +178,10 @@ module ActiveRecord
|
|
178
178
|
def can_use_fast_cache_version?(timestamp)
|
179
179
|
timestamp.is_a?(String) &&
|
180
180
|
cache_timestamp_format == :usec &&
|
181
|
-
|
181
|
+
# FIXME: checking out a connection for this is wasteful
|
182
|
+
# we should store/cache this information in the schema cache
|
183
|
+
# or similar.
|
184
|
+
self.class.with_connection(&:default_timezone) == :utc &&
|
182
185
|
!updated_at_came_from_user?
|
183
186
|
end
|
184
187
|
|
@@ -9,38 +9,83 @@ module ActiveRecord
|
|
9
9
|
#
|
10
10
|
# This is enabled by default. To disable this functionality set
|
11
11
|
# `use_metadata_table` to false in your database configuration.
|
12
|
-
class InternalMetadata
|
13
|
-
|
12
|
+
class InternalMetadata # :nodoc:
|
13
|
+
class NullInternalMetadata # :nodoc:
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :arel_table
|
17
|
+
|
18
|
+
def initialize(pool)
|
19
|
+
@pool = pool
|
20
|
+
@arel_table = Arel::Table.new(table_name)
|
21
|
+
end
|
22
|
+
|
23
|
+
def primary_key
|
24
|
+
"key"
|
25
|
+
end
|
14
26
|
|
15
|
-
|
16
|
-
|
17
|
-
|
27
|
+
def value_key
|
28
|
+
"value"
|
29
|
+
end
|
30
|
+
|
31
|
+
def table_name
|
32
|
+
"#{ActiveRecord::Base.table_name_prefix}#{ActiveRecord::Base.internal_metadata_table_name}#{ActiveRecord::Base.table_name_suffix}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def enabled?
|
36
|
+
@pool.db_config.use_metadata_table?
|
37
|
+
end
|
38
|
+
|
39
|
+
def []=(key, value)
|
40
|
+
return unless enabled?
|
41
|
+
|
42
|
+
@pool.with_connection do |connection|
|
43
|
+
update_or_create_entry(connection, key, value)
|
18
44
|
end
|
45
|
+
end
|
19
46
|
|
20
|
-
|
21
|
-
|
47
|
+
def [](key)
|
48
|
+
return unless enabled?
|
49
|
+
|
50
|
+
@pool.with_connection do |connection|
|
51
|
+
if entry = select_entry(connection, key)
|
52
|
+
entry[value_key]
|
53
|
+
end
|
22
54
|
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def delete_all_entries
|
58
|
+
dm = Arel::DeleteManager.new(arel_table)
|
23
59
|
|
24
|
-
|
25
|
-
"#{
|
60
|
+
@pool.with_connection do |connection|
|
61
|
+
connection.delete(dm, "#{self.class} Destroy")
|
26
62
|
end
|
63
|
+
end
|
27
64
|
|
28
|
-
|
29
|
-
|
65
|
+
def count
|
66
|
+
sm = Arel::SelectManager.new(arel_table)
|
67
|
+
sm.project(*Arel::Nodes::Count.new([Arel.star]))
|
30
68
|
|
31
|
-
|
69
|
+
@pool.with_connection do |connection|
|
70
|
+
connection.select_values(sm, "#{self.class} Count").first
|
32
71
|
end
|
72
|
+
end
|
33
73
|
|
34
|
-
|
35
|
-
|
74
|
+
def create_table_and_set_flags(environment, schema_sha1 = nil)
|
75
|
+
return unless enabled?
|
36
76
|
|
37
|
-
|
77
|
+
@pool.with_connection do |connection|
|
78
|
+
create_table
|
79
|
+
update_or_create_entry(connection, :environment, environment)
|
80
|
+
update_or_create_entry(connection, :schema_sha1, schema_sha1) if schema_sha1
|
38
81
|
end
|
82
|
+
end
|
39
83
|
|
40
|
-
|
41
|
-
|
42
|
-
|
84
|
+
# Creates an internal metadata table with columns +key+ and +value+
|
85
|
+
def create_table
|
86
|
+
return unless enabled?
|
43
87
|
|
88
|
+
@pool.with_connection do |connection|
|
44
89
|
unless connection.table_exists?(table_name)
|
45
90
|
connection.create_table(table_name, id: false) do |t|
|
46
91
|
t.string :key, **connection.internal_string_options_for_primary_key
|
@@ -49,12 +94,71 @@ module ActiveRecord
|
|
49
94
|
end
|
50
95
|
end
|
51
96
|
end
|
97
|
+
end
|
52
98
|
|
53
|
-
|
54
|
-
|
99
|
+
def drop_table
|
100
|
+
return unless enabled?
|
55
101
|
|
102
|
+
@pool.with_connection do |connection|
|
56
103
|
connection.drop_table table_name, if_exists: true
|
57
104
|
end
|
58
105
|
end
|
106
|
+
|
107
|
+
def table_exists?
|
108
|
+
@pool.schema_cache.data_source_exists?(table_name)
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
def update_or_create_entry(connection, key, value)
|
113
|
+
entry = select_entry(connection, key)
|
114
|
+
|
115
|
+
if entry
|
116
|
+
if entry[value_key] != value
|
117
|
+
update_entry(connection, key, value)
|
118
|
+
else
|
119
|
+
entry[value_key]
|
120
|
+
end
|
121
|
+
else
|
122
|
+
create_entry(connection, key, value)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def current_time(connection)
|
127
|
+
connection.default_timezone == :utc ? Time.now.utc : Time.now
|
128
|
+
end
|
129
|
+
|
130
|
+
def create_entry(connection, key, value)
|
131
|
+
im = Arel::InsertManager.new(arel_table)
|
132
|
+
im.insert [
|
133
|
+
[arel_table[primary_key], key],
|
134
|
+
[arel_table[value_key], value],
|
135
|
+
[arel_table[:created_at], current_time(connection)],
|
136
|
+
[arel_table[:updated_at], current_time(connection)]
|
137
|
+
]
|
138
|
+
|
139
|
+
connection.insert(im, "#{self.class} Create", primary_key, key)
|
140
|
+
end
|
141
|
+
|
142
|
+
def update_entry(connection, key, new_value)
|
143
|
+
um = Arel::UpdateManager.new(arel_table)
|
144
|
+
um.set [
|
145
|
+
[arel_table[value_key], new_value],
|
146
|
+
[arel_table[:updated_at], current_time(connection)]
|
147
|
+
]
|
148
|
+
|
149
|
+
um.where(arel_table[primary_key].eq(key))
|
150
|
+
|
151
|
+
connection.update(um, "#{self.class} Update")
|
152
|
+
end
|
153
|
+
|
154
|
+
def select_entry(connection, key)
|
155
|
+
sm = Arel::SelectManager.new(arel_table)
|
156
|
+
sm.project(Arel::Nodes::SqlLiteral.new("*", retryable: true))
|
157
|
+
sm.where(arel_table[primary_key].eq(Arel::Nodes::BindParam.new(key)))
|
158
|
+
sm.order(arel_table[primary_key].asc)
|
159
|
+
sm.limit = 1
|
160
|
+
|
161
|
+
connection.select_all(sm, "#{self.class} Load").first
|
162
|
+
end
|
59
163
|
end
|
60
164
|
end
|
@@ -14,7 +14,7 @@ module ActiveRecord
|
|
14
14
|
# == Usage
|
15
15
|
#
|
16
16
|
# Active Record supports optimistic locking if the +lock_version+ field is present. Each update to the
|
17
|
-
# record increments the +lock_version+
|
17
|
+
# record increments the integer column +lock_version+ and the locking facilities ensure that records instantiated twice
|
18
18
|
# will let the last one saved raise a +StaleObjectError+ if the first was also updated. Example:
|
19
19
|
#
|
20
20
|
# p1 = Person.find(1)
|
@@ -182,14 +182,15 @@ module ActiveRecord
|
|
182
182
|
super
|
183
183
|
end
|
184
184
|
|
185
|
-
|
186
|
-
|
187
|
-
|
185
|
+
private
|
186
|
+
def hook_attribute_type(name, cast_type)
|
187
|
+
if lock_optimistically && name == locking_column
|
188
|
+
cast_type = LockingType.new(cast_type)
|
189
|
+
end
|
190
|
+
|
191
|
+
super
|
188
192
|
end
|
189
|
-
super
|
190
|
-
end
|
191
193
|
|
192
|
-
private
|
193
194
|
def inherited(base)
|
194
195
|
super
|
195
196
|
base.class_eval do
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Locking
|
5
|
+
# = \Pessimistic \Locking
|
6
|
+
#
|
5
7
|
# Locking::Pessimistic provides support for row-level locking using
|
6
8
|
# SELECT ... FOR UPDATE and other lock types.
|
7
9
|
#
|
@@ -71,6 +73,7 @@ module ActiveRecord
|
|
71
73
|
Locking a record with unpersisted changes is not supported. Use
|
72
74
|
`save` to persist the changes, or `reload` to discard them
|
73
75
|
explicitly.
|
76
|
+
Changed attributes: #{changed.map(&:inspect).join(', ')}.
|
74
77
|
MSG
|
75
78
|
end
|
76
79
|
|
@@ -79,8 +82,8 @@ module ActiveRecord
|
|
79
82
|
self
|
80
83
|
end
|
81
84
|
|
82
|
-
# Wraps the passed block in a transaction,
|
83
|
-
# before yielding. You can pass the SQL locking clause
|
85
|
+
# Wraps the passed block in a transaction, reloading the object with a
|
86
|
+
# lock before yielding. You can pass the SQL locking clause
|
84
87
|
# as an optional argument (see #lock!).
|
85
88
|
#
|
86
89
|
# You can also pass options like <tt>requires_new:</tt>, <tt>isolation:</tt>,
|