activerecord 7.2.3 → 8.1.3
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 +612 -1055
- data/README.rdoc +1 -1
- data/lib/active_record/association_relation.rb +2 -1
- data/lib/active_record/associations/association.rb +35 -11
- data/lib/active_record/associations/builder/association.rb +23 -11
- data/lib/active_record/associations/builder/belongs_to.rb +17 -4
- data/lib/active_record/associations/builder/collection_association.rb +7 -3
- data/lib/active_record/associations/builder/has_one.rb +1 -1
- data/lib/active_record/associations/builder/singular_association.rb +33 -5
- data/lib/active_record/associations/collection_association.rb +1 -1
- data/lib/active_record/associations/collection_proxy.rb +22 -4
- data/lib/active_record/associations/deprecation.rb +88 -0
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/errors.rb +3 -0
- data/lib/active_record/associations/has_many_through_association.rb +3 -2
- data/lib/active_record/associations/join_dependency.rb +4 -2
- data/lib/active_record/associations/preloader/association.rb +2 -2
- data/lib/active_record/associations/preloader/batch.rb +7 -1
- data/lib/active_record/associations/preloader/branch.rb +1 -0
- data/lib/active_record/associations/singular_association.rb +8 -3
- data/lib/active_record/associations.rb +192 -24
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_methods/primary_key.rb +4 -8
- data/lib/active_record/attribute_methods/query.rb +34 -0
- data/lib/active_record/attribute_methods/serialization.rb +16 -3
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -14
- data/lib/active_record/attributes.rb +3 -0
- data/lib/active_record/autosave_association.rb +69 -27
- data/lib/active_record/base.rb +1 -2
- data/lib/active_record/coders/json.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +35 -28
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -4
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +51 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +412 -88
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +137 -75
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +27 -5
- data/lib/active_record/connection_adapters/abstract/quoting.rb +16 -25
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +11 -7
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +32 -35
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +122 -32
- data/lib/active_record/connection_adapters/abstract/transaction.rb +40 -8
- data/lib/active_record/connection_adapters/abstract_adapter.rb +150 -91
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +63 -52
- data/lib/active_record/connection_adapters/column.rb +17 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
- data/lib/active_record/connection_adapters/mysql/quoting.rb +0 -8
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +41 -10
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +73 -46
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +89 -94
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +2 -10
- data/lib/active_record/connection_adapters/pool_config.rb +7 -7
- data/lib/active_record/connection_adapters/postgresql/column.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +76 -45
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +21 -10
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +9 -17
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +14 -33
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +71 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +139 -63
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +78 -105
- data/lib/active_record/connection_adapters/schema_cache.rb +3 -5
- data/lib/active_record/connection_adapters/sqlite3/column.rb +8 -2
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +90 -98
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +0 -8
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +27 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +13 -14
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +102 -37
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +38 -67
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +1 -18
- data/lib/active_record/connection_adapters.rb +1 -56
- data/lib/active_record/connection_handling.rb +25 -2
- data/lib/active_record/core.rb +33 -17
- data/lib/active_record/counter_cache.rb +33 -8
- data/lib/active_record/database_configurations/database_config.rb +9 -1
- data/lib/active_record/database_configurations/hash_config.rb +67 -9
- data/lib/active_record/database_configurations/url_config.rb +13 -3
- data/lib/active_record/database_configurations.rb +7 -3
- data/lib/active_record/delegated_type.rb +1 -1
- data/lib/active_record/dynamic_matchers.rb +54 -69
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +8 -8
- data/lib/active_record/encryption/encrypted_attribute_type.rb +11 -2
- data/lib/active_record/encryption/encryptor.rb +28 -8
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- data/lib/active_record/encryption/scheme.rb +9 -2
- data/lib/active_record/enum.rb +33 -30
- data/lib/active_record/errors.rb +33 -9
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/explain_registry.rb +51 -2
- data/lib/active_record/filter_attribute_handler.rb +73 -0
- data/lib/active_record/fixtures.rb +2 -4
- data/lib/active_record/future_result.rb +15 -9
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +1 -1
- data/lib/active_record/insert_all.rb +14 -9
- data/lib/active_record/locking/optimistic.rb +8 -1
- data/lib/active_record/locking/pessimistic.rb +5 -0
- data/lib/active_record/log_subscriber.rb +3 -13
- data/lib/active_record/middleware/shard_selector.rb +34 -17
- data/lib/active_record/migration/command_recorder.rb +45 -12
- data/lib/active_record/migration/compatibility.rb +37 -24
- data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
- data/lib/active_record/migration.rb +48 -42
- data/lib/active_record/model_schema.rb +38 -13
- data/lib/active_record/nested_attributes.rb +6 -6
- data/lib/active_record/persistence.rb +162 -133
- data/lib/active_record/query_cache.rb +22 -15
- data/lib/active_record/query_logs.rb +100 -52
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +8 -8
- data/lib/active_record/railtie.rb +35 -30
- data/lib/active_record/railties/controller_runtime.rb +11 -6
- data/lib/active_record/railties/databases.rake +26 -38
- data/lib/active_record/railties/job_checkpoints.rb +15 -0
- data/lib/active_record/railties/job_runtime.rb +10 -11
- data/lib/active_record/reflection.rb +53 -21
- data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
- data/lib/active_record/relation/batches.rb +147 -73
- data/lib/active_record/relation/calculations.rb +52 -40
- data/lib/active_record/relation/delegation.rb +25 -15
- data/lib/active_record/relation/finder_methods.rb +40 -24
- data/lib/active_record/relation/merger.rb +8 -8
- data/lib/active_record/relation/predicate_builder/array_handler.rb +3 -1
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +9 -9
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +8 -8
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +22 -7
- data/lib/active_record/relation/query_attribute.rb +3 -1
- data/lib/active_record/relation/query_methods.rb +140 -86
- data/lib/active_record/relation/spawn_methods.rb +7 -7
- data/lib/active_record/relation/where_clause.rb +2 -9
- data/lib/active_record/relation.rb +107 -75
- data/lib/active_record/result.rb +109 -24
- data/lib/active_record/runtime_registry.rb +42 -58
- data/lib/active_record/sanitization.rb +9 -6
- data/lib/active_record/schema_dumper.rb +18 -11
- data/lib/active_record/schema_migration.rb +2 -1
- data/lib/active_record/scoping/named.rb +5 -2
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/signed_id.rb +43 -15
- data/lib/active_record/statement_cache.rb +24 -20
- data/lib/active_record/store.rb +51 -22
- data/lib/active_record/structured_event_subscriber.rb +85 -0
- data/lib/active_record/table_metadata.rb +6 -23
- data/lib/active_record/tasks/abstract_tasks.rb +76 -0
- data/lib/active_record/tasks/database_tasks.rb +85 -85
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -42
- data/lib/active_record/tasks/postgresql_database_tasks.rb +7 -40
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -28
- data/lib/active_record/test_databases.rb +14 -4
- data/lib/active_record/test_fixtures.rb +39 -2
- data/lib/active_record/testing/query_assertions.rb +8 -2
- data/lib/active_record/timestamp.rb +4 -2
- data/lib/active_record/token_for.rb +1 -1
- data/lib/active_record/transaction.rb +2 -5
- data/lib/active_record/transactions.rb +37 -16
- data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
- data/lib/active_record/type/internal/timezone.rb +7 -0
- data/lib/active_record/type/json.rb +13 -2
- data/lib/active_record/type/serialized.rb +16 -4
- data/lib/active_record/type/type_map.rb +1 -1
- data/lib/active_record/type_caster/connection.rb +2 -1
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record/validations/uniqueness.rb +8 -8
- data/lib/active_record.rb +84 -49
- data/lib/arel/alias_predication.rb +2 -0
- data/lib/arel/collectors/bind.rb +2 -2
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +2 -2
- data/lib/arel/crud.rb +6 -11
- data/lib/arel/nodes/binary.rb +1 -1
- data/lib/arel/nodes/count.rb +2 -2
- data/lib/arel/nodes/function.rb +4 -10
- data/lib/arel/nodes/named_function.rb +2 -2
- data/lib/arel/nodes/node.rb +2 -2
- data/lib/arel/nodes/sql_literal.rb +1 -1
- data/lib/arel/nodes.rb +0 -2
- data/lib/arel/predications.rb +1 -3
- data/lib/arel/select_manager.rb +7 -2
- data/lib/arel/table.rb +3 -7
- data/lib/arel/visitors/dot.rb +0 -3
- data/lib/arel/visitors/postgresql.rb +55 -0
- data/lib/arel/visitors/sqlite.rb +55 -8
- data/lib/arel/visitors/to_sql.rb +3 -21
- data/lib/arel.rb +3 -1
- data/lib/rails/generators/active_record/application_record/USAGE +1 -1
- metadata +16 -13
- data/lib/active_record/explain_subscriber.rb +0 -34
- data/lib/active_record/normalization.rb +0 -163
- data/lib/active_record/relation/record_fetch_warning.rb +0 -52
|
@@ -31,8 +31,11 @@ module ActiveRecord
|
|
|
31
31
|
class Substitute; end # :nodoc:
|
|
32
32
|
|
|
33
33
|
class Query # :nodoc:
|
|
34
|
-
|
|
34
|
+
attr_reader :retryable
|
|
35
|
+
|
|
36
|
+
def initialize(sql, retryable:)
|
|
35
37
|
@sql = sql
|
|
38
|
+
@retryable = retryable
|
|
36
39
|
end
|
|
37
40
|
|
|
38
41
|
def sql_for(binds, connection)
|
|
@@ -41,11 +44,12 @@ module ActiveRecord
|
|
|
41
44
|
end
|
|
42
45
|
|
|
43
46
|
class PartialQuery < Query # :nodoc:
|
|
44
|
-
def initialize(values)
|
|
47
|
+
def initialize(values, retryable:)
|
|
45
48
|
@values = values
|
|
46
49
|
@indexes = values.each_with_index.find_all { |thing, i|
|
|
47
50
|
Substitute === thing
|
|
48
51
|
}.map(&:last)
|
|
52
|
+
@retryable = retryable
|
|
49
53
|
end
|
|
50
54
|
|
|
51
55
|
def sql_for(binds, connection)
|
|
@@ -74,13 +78,13 @@ module ActiveRecord
|
|
|
74
78
|
self
|
|
75
79
|
end
|
|
76
80
|
|
|
77
|
-
def add_bind(obj)
|
|
81
|
+
def add_bind(obj, &)
|
|
78
82
|
@binds << obj
|
|
79
83
|
@parts << Substitute.new
|
|
80
84
|
self
|
|
81
85
|
end
|
|
82
86
|
|
|
83
|
-
def add_binds(binds, proc_for_binds = nil)
|
|
87
|
+
def add_binds(binds, proc_for_binds = nil, &)
|
|
84
88
|
@binds.concat proc_for_binds ? binds.map(&proc_for_binds) : binds
|
|
85
89
|
binds.size.times do |i|
|
|
86
90
|
@parts << ", " unless i == 0
|
|
@@ -94,12 +98,12 @@ module ActiveRecord
|
|
|
94
98
|
end
|
|
95
99
|
end
|
|
96
100
|
|
|
97
|
-
def self.query(
|
|
98
|
-
Query.new(
|
|
101
|
+
def self.query(...)
|
|
102
|
+
Query.new(...)
|
|
99
103
|
end
|
|
100
104
|
|
|
101
|
-
def self.partial_query(
|
|
102
|
-
PartialQuery.new(
|
|
105
|
+
def self.partial_query(...)
|
|
106
|
+
PartialQuery.new(...)
|
|
103
107
|
end
|
|
104
108
|
|
|
105
109
|
def self.partial_query_collector
|
|
@@ -133,23 +137,26 @@ module ActiveRecord
|
|
|
133
137
|
relation = (callable || block).call Params.new
|
|
134
138
|
query_builder, binds = connection.cacheable_query(self, relation.arel)
|
|
135
139
|
bind_map = BindMap.new(binds)
|
|
136
|
-
new(query_builder, bind_map, relation.
|
|
140
|
+
new(query_builder, bind_map, relation.model)
|
|
137
141
|
end
|
|
138
142
|
|
|
139
|
-
def initialize(query_builder, bind_map,
|
|
143
|
+
def initialize(query_builder, bind_map, model)
|
|
140
144
|
@query_builder = query_builder
|
|
141
145
|
@bind_map = bind_map
|
|
142
|
-
@
|
|
146
|
+
@model = model
|
|
143
147
|
end
|
|
144
148
|
|
|
145
|
-
def execute(params, connection,
|
|
146
|
-
bind_values = bind_map.bind params
|
|
147
|
-
|
|
148
|
-
sql = query_builder.sql_for bind_values, connection
|
|
149
|
+
def execute(params, connection, async: false, &block)
|
|
150
|
+
bind_values = @bind_map.bind params
|
|
151
|
+
sql = @query_builder.sql_for bind_values, connection
|
|
149
152
|
|
|
150
|
-
|
|
153
|
+
if async
|
|
154
|
+
@model.async_find_by_sql(sql, bind_values, preparable: true, allow_retry: @query_builder.retryable, &block)
|
|
155
|
+
else
|
|
156
|
+
@model.find_by_sql(sql, bind_values, preparable: true, allow_retry: @query_builder.retryable, &block)
|
|
157
|
+
end
|
|
151
158
|
rescue ::RangeError
|
|
152
|
-
[]
|
|
159
|
+
async ? Promise.wrap([]) : []
|
|
153
160
|
end
|
|
154
161
|
|
|
155
162
|
def self.unsupported_value?(value)
|
|
@@ -157,8 +164,5 @@ module ActiveRecord
|
|
|
157
164
|
when NilClass, Array, Range, Hash, Relation, Base then true
|
|
158
165
|
end
|
|
159
166
|
end
|
|
160
|
-
|
|
161
|
-
private
|
|
162
|
-
attr_reader :query_builder, :bind_map, :klass
|
|
163
167
|
end
|
|
164
168
|
end
|
data/lib/active_record/store.rb
CHANGED
|
@@ -25,8 +25,8 @@ module ActiveRecord
|
|
|
25
25
|
# You can set custom coder to encode/decode your serialized attributes to/from different formats.
|
|
26
26
|
# JSON, YAML, Marshal are supported out of the box. Generally it can be any wrapper that provides +load+ and +dump+.
|
|
27
27
|
#
|
|
28
|
-
# NOTE: If you are using structured database data types (e.g. PostgreSQL +hstore+/+json+,
|
|
29
|
-
# +json+) there is no need for the serialization provided by {.store}[rdoc-ref:rdoc-ref:ClassMethods#store].
|
|
28
|
+
# NOTE: If you are using structured database data types (e.g. PostgreSQL +hstore+/+json+, MySQL 5.7+
|
|
29
|
+
# +json+, or SQLite 3.38+ +json+) there is no need for the serialization provided by {.store}[rdoc-ref:rdoc-ref:ClassMethods#store].
|
|
30
30
|
# Simply use {.store_accessor}[rdoc-ref:ClassMethods#store_accessor] instead to generate
|
|
31
31
|
# the accessor methods. Be aware that these columns use a string keyed hash and do not allow access
|
|
32
32
|
# using a symbol.
|
|
@@ -146,37 +146,43 @@ module ActiveRecord
|
|
|
146
146
|
define_method("#{accessor_key}_changed?") do
|
|
147
147
|
return false unless attribute_changed?(store_attribute)
|
|
148
148
|
prev_store, new_store = changes[store_attribute]
|
|
149
|
-
|
|
149
|
+
accessor = store_accessor_for(store_attribute)
|
|
150
|
+
accessor.get(prev_store, key) != accessor.get(new_store, key)
|
|
150
151
|
end
|
|
151
152
|
|
|
152
153
|
define_method("#{accessor_key}_change") do
|
|
153
154
|
return unless attribute_changed?(store_attribute)
|
|
154
155
|
prev_store, new_store = changes[store_attribute]
|
|
155
|
-
|
|
156
|
+
accessor = store_accessor_for(store_attribute)
|
|
157
|
+
[accessor.get(prev_store, key), accessor.get(new_store, key)]
|
|
156
158
|
end
|
|
157
159
|
|
|
158
160
|
define_method("#{accessor_key}_was") do
|
|
159
161
|
return unless attribute_changed?(store_attribute)
|
|
160
162
|
prev_store, _new_store = changes[store_attribute]
|
|
161
|
-
|
|
163
|
+
accessor = store_accessor_for(store_attribute)
|
|
164
|
+
accessor.get(prev_store, key)
|
|
162
165
|
end
|
|
163
166
|
|
|
164
167
|
define_method("saved_change_to_#{accessor_key}?") do
|
|
165
168
|
return false unless saved_change_to_attribute?(store_attribute)
|
|
166
169
|
prev_store, new_store = saved_changes[store_attribute]
|
|
167
|
-
|
|
170
|
+
accessor = store_accessor_for(store_attribute)
|
|
171
|
+
accessor.get(prev_store, key) != accessor.get(new_store, key)
|
|
168
172
|
end
|
|
169
173
|
|
|
170
174
|
define_method("saved_change_to_#{accessor_key}") do
|
|
171
175
|
return unless saved_change_to_attribute?(store_attribute)
|
|
172
176
|
prev_store, new_store = saved_changes[store_attribute]
|
|
173
|
-
|
|
177
|
+
accessor = store_accessor_for(store_attribute)
|
|
178
|
+
[accessor.get(prev_store, key), accessor.get(new_store, key)]
|
|
174
179
|
end
|
|
175
180
|
|
|
176
181
|
define_method("#{accessor_key}_before_last_save") do
|
|
177
182
|
return unless saved_change_to_attribute?(store_attribute)
|
|
178
183
|
prev_store, _new_store = saved_changes[store_attribute]
|
|
179
|
-
|
|
184
|
+
accessor = store_accessor_for(store_attribute)
|
|
185
|
+
accessor.get(prev_store, key)
|
|
180
186
|
end
|
|
181
187
|
end
|
|
182
188
|
end
|
|
@@ -217,43 +223,66 @@ module ActiveRecord
|
|
|
217
223
|
end
|
|
218
224
|
|
|
219
225
|
def store_accessor_for(store_attribute)
|
|
220
|
-
type_for_attribute(store_attribute).
|
|
226
|
+
type_for_attribute(store_attribute).tap do |type|
|
|
227
|
+
unless type.respond_to?(:accessor)
|
|
228
|
+
raise ConfigurationError, "the column '#{store_attribute}' has not been configured as a store. Please make sure the column is declared serializable via 'ActiveRecord.store' or, if your database supports it, use a structured column type like hstore or json."
|
|
229
|
+
end
|
|
230
|
+
end.accessor
|
|
221
231
|
end
|
|
222
232
|
|
|
223
233
|
class HashAccessor # :nodoc:
|
|
234
|
+
def self.get(store_object, key)
|
|
235
|
+
if store_object
|
|
236
|
+
store_object[key]
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
224
240
|
def self.read(object, attribute, key)
|
|
225
|
-
prepare(object, attribute)
|
|
226
|
-
|
|
241
|
+
store_object = prepare(object, attribute)
|
|
242
|
+
store_object[key]
|
|
227
243
|
end
|
|
228
244
|
|
|
229
245
|
def self.write(object, attribute, key, value)
|
|
230
|
-
prepare(object, attribute)
|
|
231
|
-
|
|
246
|
+
store_object = prepare(object, attribute)
|
|
247
|
+
store_object[key] = value if value != store_object[key]
|
|
232
248
|
end
|
|
233
249
|
|
|
234
250
|
def self.prepare(object, attribute)
|
|
235
|
-
|
|
251
|
+
store_object = object.public_send(attribute)
|
|
252
|
+
|
|
253
|
+
if store_object.nil?
|
|
254
|
+
store_object = {}
|
|
255
|
+
object.public_send(:"#{attribute}=", store_object)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
store_object
|
|
236
259
|
end
|
|
237
260
|
end
|
|
238
261
|
|
|
239
262
|
class StringKeyedHashAccessor < HashAccessor # :nodoc:
|
|
263
|
+
def self.get(store_object, key)
|
|
264
|
+
super store_object, Symbol === key ? key.name : key.to_s
|
|
265
|
+
end
|
|
266
|
+
|
|
240
267
|
def self.read(object, attribute, key)
|
|
241
|
-
super object, attribute, key.to_s
|
|
268
|
+
super object, attribute, Symbol === key ? key.name : key.to_s
|
|
242
269
|
end
|
|
243
270
|
|
|
244
271
|
def self.write(object, attribute, key, value)
|
|
245
|
-
super object, attribute, key.to_s, value
|
|
272
|
+
super object, attribute, Symbol === key ? key.name : key.to_s, value
|
|
246
273
|
end
|
|
247
274
|
end
|
|
248
275
|
|
|
249
276
|
class IndifferentHashAccessor < ActiveRecord::Store::HashAccessor # :nodoc:
|
|
250
|
-
def self.prepare(object,
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
277
|
+
def self.prepare(object, attribute)
|
|
278
|
+
store_object = object.public_send(attribute)
|
|
279
|
+
|
|
280
|
+
unless store_object.is_a?(ActiveSupport::HashWithIndifferentAccess)
|
|
281
|
+
store_object = IndifferentCoder.as_indifferent_hash(store_object)
|
|
282
|
+
object.public_send :"#{attribute}=", store_object
|
|
255
283
|
end
|
|
256
|
-
|
|
284
|
+
|
|
285
|
+
store_object
|
|
257
286
|
end
|
|
258
287
|
end
|
|
259
288
|
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/structured_event_subscriber"
|
|
4
|
+
|
|
5
|
+
module ActiveRecord
|
|
6
|
+
class StructuredEventSubscriber < ActiveSupport::StructuredEventSubscriber # :nodoc:
|
|
7
|
+
IGNORE_PAYLOAD_NAMES = ["SCHEMA", "EXPLAIN"]
|
|
8
|
+
|
|
9
|
+
def strict_loading_violation(event)
|
|
10
|
+
owner = event.payload[:owner]
|
|
11
|
+
reflection = event.payload[:reflection]
|
|
12
|
+
|
|
13
|
+
emit_debug_event("active_record.strict_loading_violation",
|
|
14
|
+
owner: owner.name,
|
|
15
|
+
class: reflection.polymorphic? ? nil : reflection.klass.name,
|
|
16
|
+
name: reflection.name,
|
|
17
|
+
)
|
|
18
|
+
end
|
|
19
|
+
debug_only :strict_loading_violation
|
|
20
|
+
|
|
21
|
+
def sql(event)
|
|
22
|
+
payload = event.payload
|
|
23
|
+
|
|
24
|
+
return if IGNORE_PAYLOAD_NAMES.include?(payload[:name])
|
|
25
|
+
|
|
26
|
+
binds = nil
|
|
27
|
+
|
|
28
|
+
if payload[:binds]&.any?
|
|
29
|
+
casted_params = type_casted_binds(payload[:type_casted_binds])
|
|
30
|
+
|
|
31
|
+
binds = []
|
|
32
|
+
payload[:binds].each_with_index do |attr, i|
|
|
33
|
+
attribute_name = if attr.respond_to?(:name)
|
|
34
|
+
attr.name
|
|
35
|
+
elsif attr.respond_to?(:[]) && attr[i].respond_to?(:name)
|
|
36
|
+
attr[i].name
|
|
37
|
+
else
|
|
38
|
+
nil
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
filtered_params = filter(attribute_name, casted_params[i])
|
|
42
|
+
|
|
43
|
+
binds << render_bind(attr, filtered_params)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
emit_debug_event("active_record.sql",
|
|
48
|
+
async: payload[:async],
|
|
49
|
+
name: payload[:name],
|
|
50
|
+
sql: payload[:sql],
|
|
51
|
+
cached: payload[:cached],
|
|
52
|
+
lock_wait: payload[:lock_wait],
|
|
53
|
+
binds: binds,
|
|
54
|
+
duration_ms: event.duration.round(2),
|
|
55
|
+
)
|
|
56
|
+
end
|
|
57
|
+
debug_only :sql
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
def type_casted_binds(casted_binds)
|
|
61
|
+
casted_binds.respond_to?(:call) ? casted_binds.call : casted_binds
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def render_bind(attr, value)
|
|
65
|
+
case attr
|
|
66
|
+
when ActiveModel::Attribute
|
|
67
|
+
if attr.type.binary? && attr.value
|
|
68
|
+
value = "<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
|
|
69
|
+
end
|
|
70
|
+
when Array
|
|
71
|
+
attr = attr.first
|
|
72
|
+
else
|
|
73
|
+
attr = nil
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
[attr&.name, value]
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def filter(name, value)
|
|
80
|
+
ActiveRecord::Base.inspection_filter.filter_param(name, value)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
ActiveRecord::StructuredEventSubscriber.attach_to :active_record
|
|
@@ -2,12 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
module ActiveRecord
|
|
4
4
|
class TableMetadata # :nodoc:
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def initialize(klass, arel_table, reflection = nil)
|
|
5
|
+
def initialize(klass, arel_table)
|
|
8
6
|
@klass = klass
|
|
9
7
|
@arel_table = arel_table
|
|
10
|
-
@reflection = reflection
|
|
11
8
|
end
|
|
12
9
|
|
|
13
10
|
def primary_key
|
|
@@ -22,7 +19,7 @@ module ActiveRecord
|
|
|
22
19
|
klass&.columns_hash&.key?(column_name)
|
|
23
20
|
end
|
|
24
21
|
|
|
25
|
-
def associated_with
|
|
22
|
+
def associated_with(table_name)
|
|
26
23
|
klass&._reflect_on_association(table_name)
|
|
27
24
|
end
|
|
28
25
|
|
|
@@ -42,26 +39,14 @@ module ActiveRecord
|
|
|
42
39
|
if association_klass
|
|
43
40
|
arel_table = association_klass.arel_table
|
|
44
41
|
arel_table = arel_table.alias(table_name) if arel_table.name != table_name
|
|
45
|
-
TableMetadata.new(association_klass, arel_table
|
|
42
|
+
TableMetadata.new(association_klass, arel_table)
|
|
46
43
|
else
|
|
47
44
|
type_caster = TypeCaster::Connection.new(klass, table_name)
|
|
48
45
|
arel_table = Arel::Table.new(table_name, type_caster: type_caster)
|
|
49
|
-
TableMetadata.new(nil, arel_table
|
|
46
|
+
TableMetadata.new(nil, arel_table)
|
|
50
47
|
end
|
|
51
48
|
end
|
|
52
49
|
|
|
53
|
-
def polymorphic_association?
|
|
54
|
-
reflection&.polymorphic?
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def polymorphic_name_association
|
|
58
|
-
reflection&.polymorphic_name
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def through_association?
|
|
62
|
-
reflection&.through_reflection?
|
|
63
|
-
end
|
|
64
|
-
|
|
65
50
|
def reflect_on_aggregation(aggregation_name)
|
|
66
51
|
klass&.reflect_on_aggregation(aggregation_name)
|
|
67
52
|
end
|
|
@@ -69,9 +54,7 @@ module ActiveRecord
|
|
|
69
54
|
|
|
70
55
|
def predicate_builder
|
|
71
56
|
if klass
|
|
72
|
-
|
|
73
|
-
predicate_builder.instance_variable_set(:@table, self)
|
|
74
|
-
predicate_builder
|
|
57
|
+
klass.predicate_builder.with(self)
|
|
75
58
|
else
|
|
76
59
|
PredicateBuilder.new(self)
|
|
77
60
|
end
|
|
@@ -80,6 +63,6 @@ module ActiveRecord
|
|
|
80
63
|
attr_reader :arel_table
|
|
81
64
|
|
|
82
65
|
private
|
|
83
|
-
attr_reader :klass
|
|
66
|
+
attr_reader :klass
|
|
84
67
|
end
|
|
85
68
|
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module Tasks # :nodoc:
|
|
5
|
+
class AbstractTasks # :nodoc:
|
|
6
|
+
def self.using_database_configurations?
|
|
7
|
+
true
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def initialize(db_config)
|
|
11
|
+
@db_config = db_config
|
|
12
|
+
@configuration_hash = db_config.configuration_hash
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def charset
|
|
16
|
+
connection.encoding
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def collation
|
|
20
|
+
connection.collation
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def check_current_protected_environment!(db_config, migration_class)
|
|
24
|
+
with_temporary_pool(db_config, migration_class) do |pool|
|
|
25
|
+
migration_context = pool.migration_context
|
|
26
|
+
current = migration_context.current_environment
|
|
27
|
+
stored = migration_context.last_stored_environment
|
|
28
|
+
|
|
29
|
+
if migration_context.protected_environment?
|
|
30
|
+
raise ActiveRecord::ProtectedEnvironmentError.new(stored)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
if stored && stored != current
|
|
34
|
+
raise ActiveRecord::EnvironmentMismatchError.new(current: current, stored: stored)
|
|
35
|
+
end
|
|
36
|
+
rescue ActiveRecord::NoDatabaseError
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
attr_reader :db_config, :configuration_hash
|
|
42
|
+
|
|
43
|
+
def connection
|
|
44
|
+
ActiveRecord::Base.lease_connection
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def establish_connection(config = db_config)
|
|
48
|
+
ActiveRecord::Base.establish_connection(config)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def configuration_hash_without_database
|
|
52
|
+
configuration_hash.merge(database: nil)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def run_cmd(cmd, *args, **opts)
|
|
56
|
+
fail run_cmd_error(cmd, args) unless Kernel.system(cmd, *args, opts)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def run_cmd_error(cmd, args)
|
|
60
|
+
msg = +"failed to execute:\n"
|
|
61
|
+
msg << "#{cmd} #{args.join(' ')}\n\n"
|
|
62
|
+
msg << "Please check the output above for any errors and make sure that `#{cmd}` is installed in your PATH and has proper permissions.\n\n"
|
|
63
|
+
msg
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def with_temporary_pool(db_config, migration_class, clobber: false)
|
|
67
|
+
original_db_config = migration_class.connection_db_config
|
|
68
|
+
pool = migration_class.connection_handler.establish_connection(db_config, clobber: clobber)
|
|
69
|
+
|
|
70
|
+
yield pool
|
|
71
|
+
ensure
|
|
72
|
+
migration_class.connection_handler.establish_connection(original_db_config, clobber: clobber)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|