activerecord 7.1.1 → 7.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 +177 -0
- data/README.rdoc +1 -0
- data/lib/active_record/associations/association.rb +2 -1
- data/lib/active_record/associations/preloader/association.rb +4 -1
- data/lib/active_record/associations.rb +15 -15
- data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
- data/lib/active_record/attribute_methods/dirty.rb +13 -9
- data/lib/active_record/attribute_methods.rb +1 -1
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +11 -8
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +4 -2
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -1
- data/lib/active_record/connection_adapters/abstract_adapter.rb +13 -4
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +3 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +4 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +12 -5
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +32 -33
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -3
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +1 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +9 -1
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +41 -7
- data/lib/active_record/delegated_type.rb +1 -1
- data/lib/active_record/encryption/encryptable_record.rb +7 -1
- data/lib/active_record/encryption/encrypted_attribute_type.rb +4 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +0 -15
- data/lib/active_record/enum.rb +6 -9
- data/lib/active_record/errors.rb +5 -4
- data/lib/active_record/fixtures.rb +16 -0
- data/lib/active_record/future_result.rb +1 -0
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/insert_all.rb +3 -3
- data/lib/active_record/internal_metadata.rb +1 -1
- data/lib/active_record/middleware/database_selector.rb +1 -1
- data/lib/active_record/migration/compatibility.rb +8 -0
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +9 -5
- data/lib/active_record/model_schema.rb +5 -5
- data/lib/active_record/nested_attributes.rb +3 -3
- data/lib/active_record/normalization.rb +8 -0
- data/lib/active_record/persistence.rb +4 -3
- data/lib/active_record/promise.rb +1 -1
- data/lib/active_record/railties/controller_runtime.rb +2 -1
- data/lib/active_record/railties/databases.rake +5 -5
- data/lib/active_record/reflection.rb +13 -1
- data/lib/active_record/relation/calculations.rb +28 -1
- data/lib/active_record/relation/delegation.rb +1 -1
- data/lib/active_record/relation.rb +18 -3
- data/lib/active_record/runtime_registry.rb +15 -1
- data/lib/active_record/schema_migration.rb +1 -1
- data/lib/active_record/secure_token.rb +1 -1
- data/lib/active_record/tasks/database_tasks.rb +5 -5
- data/lib/arel/nodes/homogeneous_in.rb +1 -1
- metadata +10 -9
@@ -108,10 +108,11 @@ module ActiveRecord
|
|
108
108
|
# but significantly increases the risk of data loss if the database
|
109
109
|
# crashes. As a result, this should not be used in production
|
110
110
|
# environments. If you would like all created tables to be unlogged in
|
111
|
-
# the test environment you can add the following
|
112
|
-
# file:
|
111
|
+
# the test environment you can add the following to your test.rb file:
|
113
112
|
#
|
114
|
-
#
|
113
|
+
# ActiveSupport.on_load(:active_record_postgresqladapter) do
|
114
|
+
# self.create_unlogged_tables = true
|
115
|
+
# end
|
115
116
|
class_attribute :create_unlogged_tables, default: false
|
116
117
|
|
117
118
|
##
|
@@ -277,6 +278,10 @@ module ActiveRecord
|
|
277
278
|
database_version >= 12_00_00 # >= 12.0
|
278
279
|
end
|
279
280
|
|
281
|
+
def supports_identity_columns? # :nodoc:
|
282
|
+
database_version >= 10_00_00 # >= 10.0
|
283
|
+
end
|
284
|
+
|
280
285
|
def supports_nulls_not_distinct?
|
281
286
|
database_version >= 15_00_00 # >= 15.0
|
282
287
|
end
|
@@ -535,7 +540,7 @@ module ActiveRecord
|
|
535
540
|
END
|
536
541
|
$$;
|
537
542
|
SQL
|
538
|
-
internal_exec_query(query)
|
543
|
+
internal_exec_query(query).tap { reload_type_map }
|
539
544
|
end
|
540
545
|
|
541
546
|
# Drops an enum type.
|
@@ -551,7 +556,7 @@ module ActiveRecord
|
|
551
556
|
query = <<~SQL
|
552
557
|
DROP TYPE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(name)};
|
553
558
|
SQL
|
554
|
-
internal_exec_query(query)
|
559
|
+
internal_exec_query(query).tap { reload_type_map }
|
555
560
|
end
|
556
561
|
|
557
562
|
# Rename an existing enum type to something else.
|
@@ -596,14 +601,6 @@ module ActiveRecord
|
|
596
601
|
@max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
|
597
602
|
end
|
598
603
|
|
599
|
-
# Returns the maximum length of a table name.
|
600
|
-
def table_name_length
|
601
|
-
# PostgreSQL automatically creates an index for PRIMARY KEY with name consisting of
|
602
|
-
# truncated table name and "_pkey" suffix fitting into max_identifier_length number of characters.
|
603
|
-
# We allow smaller table names to be able to correctly rename this index when renaming the table.
|
604
|
-
max_identifier_length - "_pkey".length
|
605
|
-
end
|
606
|
-
|
607
604
|
# Set the authorized user for this session
|
608
605
|
def session_auth=(user)
|
609
606
|
clear_cache!
|
@@ -894,7 +891,9 @@ module ActiveRecord
|
|
894
891
|
type_casted_binds = type_casted_binds(binds)
|
895
892
|
log(sql, name, binds, type_casted_binds, async: async) do
|
896
893
|
with_raw_connection do |conn|
|
897
|
-
conn.exec_params(sql, type_casted_binds)
|
894
|
+
result = conn.exec_params(sql, type_casted_binds)
|
895
|
+
verified!
|
896
|
+
result
|
898
897
|
end
|
899
898
|
end
|
900
899
|
end
|
@@ -904,12 +903,14 @@ module ActiveRecord
|
|
904
903
|
|
905
904
|
update_typemap_for_default_timezone
|
906
905
|
|
907
|
-
stmt_key = prepare_statement(sql, binds)
|
908
|
-
type_casted_binds = type_casted_binds(binds)
|
909
|
-
|
910
906
|
with_raw_connection do |conn|
|
907
|
+
stmt_key = prepare_statement(sql, binds, conn)
|
908
|
+
type_casted_binds = type_casted_binds(binds)
|
909
|
+
|
911
910
|
log(sql, name, binds, type_casted_binds, stmt_key, async: async) do
|
912
|
-
conn.exec_prepared(stmt_key, type_casted_binds)
|
911
|
+
result = conn.exec_prepared(stmt_key, type_casted_binds)
|
912
|
+
verified!
|
913
|
+
result
|
913
914
|
end
|
914
915
|
end
|
915
916
|
rescue ActiveRecord::StatementInvalid => e
|
@@ -957,22 +958,20 @@ module ActiveRecord
|
|
957
958
|
|
958
959
|
# Prepare the statement if it hasn't been prepared, return
|
959
960
|
# the statement key.
|
960
|
-
def prepare_statement(sql, binds)
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
raise translate_exception_class(e, sql, binds)
|
969
|
-
end
|
970
|
-
# Clear the queue
|
971
|
-
conn.get_last_result
|
972
|
-
@statements[sql_key] = nextkey
|
961
|
+
def prepare_statement(sql, binds, conn)
|
962
|
+
sql_key = sql_key(sql)
|
963
|
+
unless @statements.key? sql_key
|
964
|
+
nextkey = @statements.next_key
|
965
|
+
begin
|
966
|
+
conn.prepare nextkey, sql
|
967
|
+
rescue => e
|
968
|
+
raise translate_exception_class(e, sql, binds)
|
973
969
|
end
|
974
|
-
|
970
|
+
# Clear the queue
|
971
|
+
conn.get_last_result
|
972
|
+
@statements[sql_key] = nextkey
|
975
973
|
end
|
974
|
+
@statements[sql_key]
|
976
975
|
end
|
977
976
|
|
978
977
|
# Connects to a PostgreSQL server and sets up the adapter depending on the
|
@@ -1076,7 +1075,7 @@ module ActiveRecord
|
|
1076
1075
|
SELECT a.attname, format_type(a.atttypid, a.atttypmod),
|
1077
1076
|
pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
|
1078
1077
|
c.collname, col_description(a.attrelid, a.attnum) AS comment,
|
1079
|
-
|
1078
|
+
#{supports_identity_columns? ? 'attidentity' : quote('')} AS identity,
|
1080
1079
|
#{supports_virtual_columns? ? 'attgenerated' : quote('')} as attgenerated
|
1081
1080
|
FROM pg_attribute a
|
1082
1081
|
LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
|
@@ -50,6 +50,7 @@ module ActiveRecord
|
|
50
50
|
stmt.bind_params(type_casted_binds)
|
51
51
|
records = stmt.to_a
|
52
52
|
end
|
53
|
+
verified!
|
53
54
|
|
54
55
|
build_result(columns: cols, rows: records)
|
55
56
|
end
|
@@ -76,7 +77,9 @@ module ActiveRecord
|
|
76
77
|
def begin_db_transaction # :nodoc:
|
77
78
|
log("begin transaction", "TRANSACTION") do
|
78
79
|
with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
|
79
|
-
conn.transaction
|
80
|
+
result = conn.transaction
|
81
|
+
verified!
|
82
|
+
result
|
80
83
|
end
|
81
84
|
end
|
82
85
|
end
|
@@ -112,7 +115,9 @@ module ActiveRecord
|
|
112
115
|
def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: false)
|
113
116
|
log(sql, name, async: async) do
|
114
117
|
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
115
|
-
conn.execute(sql)
|
118
|
+
result = conn.execute(sql)
|
119
|
+
verified!
|
120
|
+
result
|
116
121
|
end
|
117
122
|
end
|
118
123
|
end
|
@@ -133,7 +138,9 @@ module ActiveRecord
|
|
133
138
|
|
134
139
|
log(sql, name) do
|
135
140
|
with_raw_connection do |conn|
|
136
|
-
conn.execute_batch2(sql)
|
141
|
+
result = conn.execute_batch2(sql)
|
142
|
+
verified!
|
143
|
+
result
|
137
144
|
end
|
138
145
|
end
|
139
146
|
end
|
@@ -57,7 +57,7 @@ module ActiveRecord
|
|
57
57
|
def new_client(config)
|
58
58
|
config[:ssl_mode] = parse_ssl_mode(config[:ssl_mode]) if config[:ssl_mode]
|
59
59
|
::Trilogy.new(config)
|
60
|
-
rescue ::Trilogy::
|
60
|
+
rescue ::Trilogy::Error => error
|
61
61
|
raise translate_connect_error(config, error)
|
62
62
|
end
|
63
63
|
|
@@ -99,6 +99,14 @@ module ActiveRecord
|
|
99
99
|
end
|
100
100
|
end
|
101
101
|
|
102
|
+
def initialize(...)
|
103
|
+
super
|
104
|
+
|
105
|
+
# Trilogy ignore `socket` if `host is set. We want the opposite to allow
|
106
|
+
# configuring UNIX domain sockets via `DATABASE_URL`.
|
107
|
+
@config.delete(:host) if @config[:socket]
|
108
|
+
end
|
109
|
+
|
102
110
|
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
|
103
111
|
|
104
112
|
def supports_json?
|
@@ -256,7 +256,7 @@ module ActiveRecord
|
|
256
256
|
|
257
257
|
attr_writer :connection_specification_name
|
258
258
|
|
259
|
-
#
|
259
|
+
# Returns the connection specification name from the current class or its parent.
|
260
260
|
def connection_specification_name
|
261
261
|
if !defined?(@connection_specification_name) || @connection_specification_name.nil?
|
262
262
|
return self == Base ? Base.name : superclass.connection_specification_name
|
data/lib/active_record/core.rb
CHANGED
@@ -15,9 +15,10 @@ module ActiveRecord
|
|
15
15
|
##
|
16
16
|
# :singleton-method:
|
17
17
|
#
|
18
|
-
# Accepts a logger conforming to the interface of Log4r
|
19
|
-
# passed on to any new database
|
20
|
-
#
|
18
|
+
# Accepts a logger conforming to the interface of Log4r or the default
|
19
|
+
# Ruby +Logger+ class, which is then passed on to any new database
|
20
|
+
# connections made. You can retrieve this logger by calling +logger+ on
|
21
|
+
# either an Active Record model class or an Active Record model instance.
|
21
22
|
class_attribute :logger, instance_writer: false
|
22
23
|
|
23
24
|
class_attribute :_destroy_association_async_job, instance_accessor: false, default: "ActiveRecord::DestroyAssociationAsyncJob"
|
@@ -271,10 +272,25 @@ module ActiveRecord
|
|
271
272
|
elsif reflection.belongs_to? && !reflection.polymorphic?
|
272
273
|
key = reflection.join_foreign_key
|
273
274
|
pkey = reflection.join_primary_key
|
274
|
-
|
275
|
+
|
276
|
+
if pkey.is_a?(Array)
|
277
|
+
if pkey.all? { |attribute| value.respond_to?(attribute) }
|
278
|
+
value = pkey.map do |attribute|
|
279
|
+
if attribute == "id"
|
280
|
+
value.id_value
|
281
|
+
else
|
282
|
+
value.public_send(attribute)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
composite_primary_key = true
|
286
|
+
end
|
287
|
+
else
|
288
|
+
value = value.public_send(pkey) if value.respond_to?(pkey)
|
289
|
+
end
|
275
290
|
end
|
276
291
|
|
277
|
-
if !
|
292
|
+
if !composite_primary_key &&
|
293
|
+
(!columns_hash.key?(key) || StatementCache.unsupported_value?(value))
|
278
294
|
return super
|
279
295
|
end
|
280
296
|
|
@@ -401,12 +417,18 @@ module ActiveRecord
|
|
401
417
|
|
402
418
|
def cached_find_by(keys, values)
|
403
419
|
statement = cached_find_by_statement(keys) { |params|
|
404
|
-
wheres = keys.index_with
|
420
|
+
wheres = keys.index_with do |key|
|
421
|
+
if key.is_a?(Array)
|
422
|
+
[key.map { params.bind }]
|
423
|
+
else
|
424
|
+
params.bind
|
425
|
+
end
|
426
|
+
end
|
405
427
|
where(wheres).limit(1)
|
406
428
|
}
|
407
429
|
|
408
430
|
begin
|
409
|
-
statement.execute(values, connection).first
|
431
|
+
statement.execute(values.flatten, connection).first
|
410
432
|
rescue TypeError
|
411
433
|
raise ActiveRecord::StatementInvalid
|
412
434
|
end
|
@@ -544,6 +566,10 @@ module ActiveRecord
|
|
544
566
|
# Returns a hash of the given methods with their names as keys and returned
|
545
567
|
# values as values.
|
546
568
|
#
|
569
|
+
# topic = Topic.new(title: "Budget", author_name: "Jason")
|
570
|
+
# topic.slice(:title, :author_name)
|
571
|
+
# => { "title" => "Budget", "author_name" => "Jason" }
|
572
|
+
#
|
547
573
|
#--
|
548
574
|
# Implemented by ActiveModel::Access#slice.
|
549
575
|
|
@@ -554,6 +580,10 @@ module ActiveRecord
|
|
554
580
|
#
|
555
581
|
# Returns an array of the values returned by the given methods.
|
556
582
|
#
|
583
|
+
# topic = Topic.new(title: "Budget", author_name: "Jason")
|
584
|
+
# topic.values_at(:title, :author_name)
|
585
|
+
# => ["Budget", "Jason"]
|
586
|
+
#
|
557
587
|
#--
|
558
588
|
# Implemented by ActiveModel::Access#values_at.
|
559
589
|
|
@@ -672,6 +702,10 @@ module ActiveRecord
|
|
672
702
|
end
|
673
703
|
|
674
704
|
# Marks this record as read only.
|
705
|
+
#
|
706
|
+
# customer = Customer.first
|
707
|
+
# customer.readonly!
|
708
|
+
# customer.save # Raises an ActiveRecord::ReadOnlyRecord
|
675
709
|
def readonly!
|
676
710
|
@readonly = true
|
677
711
|
end
|
@@ -138,7 +138,7 @@ module ActiveRecord
|
|
138
138
|
#
|
139
139
|
# Now you can list a bunch of entries, call <tt>Entry#title</tt>, and polymorphism will provide you with the answer.
|
140
140
|
#
|
141
|
-
# == Nested Attributes
|
141
|
+
# == Nested \Attributes
|
142
142
|
#
|
143
143
|
# Enabling nested attributes on a delegated_type association allows you to
|
144
144
|
# create the entry and message in one go:
|
@@ -144,7 +144,13 @@ module ActiveRecord
|
|
144
144
|
|
145
145
|
# Returns whether a given attribute is encrypted or not.
|
146
146
|
def encrypted_attribute?(attribute_name)
|
147
|
-
|
147
|
+
name = attribute_name.to_s
|
148
|
+
name = self.class.attribute_aliases[name] || name
|
149
|
+
|
150
|
+
return false unless self.class.encrypted_attributes&.include? name.to_sym
|
151
|
+
|
152
|
+
type = type_for_attribute(name)
|
153
|
+
type.encrypted? read_attribute_before_type_cast(name)
|
148
154
|
end
|
149
155
|
|
150
156
|
# Returns the ciphertext for +attribute_name+.
|
@@ -44,6 +44,10 @@ module ActiveRecord
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
+
def encrypted?(value)
|
48
|
+
with_context { encryptor.encrypted? value }
|
49
|
+
end
|
50
|
+
|
47
51
|
def changed_in_place?(raw_old_value, new_value)
|
48
52
|
old_value = raw_old_value.nil? ? nil : deserialize(raw_old_value)
|
49
53
|
old_value != new_value
|
@@ -28,7 +28,6 @@ module ActiveRecord
|
|
28
28
|
ActiveRecord::Relation.prepend(RelationQueries)
|
29
29
|
ActiveRecord::Base.include(CoreQueries)
|
30
30
|
ActiveRecord::Encryption::EncryptedAttributeType.prepend(ExtendedEncryptableType)
|
31
|
-
Arel::Nodes::HomogeneousIn.prepend(InWithAdditionalValues)
|
32
31
|
end
|
33
32
|
|
34
33
|
# When modifying this file run performance tests in
|
@@ -153,20 +152,6 @@ module ActiveRecord
|
|
153
152
|
end
|
154
153
|
end
|
155
154
|
end
|
156
|
-
|
157
|
-
module InWithAdditionalValues
|
158
|
-
def proc_for_binds
|
159
|
-
-> value { ActiveModel::Attribute.with_cast_value(attribute.name, value, encryption_aware_type_caster) }
|
160
|
-
end
|
161
|
-
|
162
|
-
def encryption_aware_type_caster
|
163
|
-
if attribute.type_caster.is_a?(ActiveRecord::Encryption::EncryptedAttributeType)
|
164
|
-
attribute.type_caster.cast_type
|
165
|
-
else
|
166
|
-
attribute.type_caster
|
167
|
-
end
|
168
|
-
end
|
169
|
-
end
|
170
155
|
end
|
171
156
|
end
|
172
157
|
end
|
data/lib/active_record/enum.rb
CHANGED
@@ -167,15 +167,6 @@ module ActiveRecord
|
|
167
167
|
base.class_attribute(:defined_enums, instance_writer: false, default: {})
|
168
168
|
end
|
169
169
|
|
170
|
-
def load_schema! # :nodoc:
|
171
|
-
attributes_to_define_after_schema_loads.each do |name, (cast_type, _default)|
|
172
|
-
unless columns_hash.key?(name)
|
173
|
-
cast_type = cast_type[type_for_attribute(name)] if Proc === cast_type
|
174
|
-
raise "Unknown enum attribute '#{name}' for #{self.name}" if Enum::EnumType === cast_type
|
175
|
-
end
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
170
|
class EnumType < Type::Value # :nodoc:
|
180
171
|
delegate :type, to: :subtype
|
181
172
|
|
@@ -255,6 +246,12 @@ module ActiveRecord
|
|
255
246
|
detect_enum_conflict!(name, "#{name}=")
|
256
247
|
|
257
248
|
attribute(name, **options) do |subtype|
|
249
|
+
if subtype == ActiveModel::Type.default_value
|
250
|
+
raise "Undeclared attribute type for enum '#{name}'. Enums must be" \
|
251
|
+
" backed by a database column or declared with an explicit type" \
|
252
|
+
" via `attribute`."
|
253
|
+
end
|
254
|
+
|
258
255
|
subtype = subtype.subtype if EnumType === subtype
|
259
256
|
EnumType.new(name, enum_values, subtype, raise_on_invalid_values: !validate)
|
260
257
|
end
|
data/lib/active_record/errors.rb
CHANGED
@@ -318,14 +318,15 @@ module ActiveRecord
|
|
318
318
|
class << self
|
319
319
|
def db_error(db_name)
|
320
320
|
NoDatabaseError.new(<<~MSG)
|
321
|
-
We could not find your database: #{db_name}. Available database configurations can be found in config/database.yml
|
321
|
+
We could not find your database: #{db_name}. Available database configurations can be found in config/database.yml.
|
322
322
|
|
323
323
|
To resolve this error:
|
324
324
|
|
325
|
-
- Did you create the database
|
326
|
-
- Has the database name changed? Check your database.yml config has the correct database name.
|
325
|
+
- Did you not create the database, or did you delete it? To create the database, run:
|
327
326
|
|
328
|
-
|
327
|
+
bin/rails db:create
|
328
|
+
|
329
|
+
- Has the database name changed? Verify that config/database.yml contains the correct database name.
|
329
330
|
MSG
|
330
331
|
end
|
331
332
|
end
|
@@ -268,6 +268,8 @@ module ActiveRecord
|
|
268
268
|
# name: Reginald the Pirate
|
269
269
|
# monkey_id: 1
|
270
270
|
#
|
271
|
+
# <code></code>
|
272
|
+
#
|
271
273
|
# ### in monkeys.yml
|
272
274
|
#
|
273
275
|
# george:
|
@@ -285,6 +287,8 @@ module ActiveRecord
|
|
285
287
|
# name: Reginald the Pirate
|
286
288
|
# monkey: george
|
287
289
|
#
|
290
|
+
# <code></code>
|
291
|
+
#
|
288
292
|
# ### in monkeys.yml
|
289
293
|
#
|
290
294
|
# george:
|
@@ -306,6 +310,8 @@ module ActiveRecord
|
|
306
310
|
#
|
307
311
|
# belongs_to :eater, polymorphic: true
|
308
312
|
#
|
313
|
+
# <code></code>
|
314
|
+
#
|
309
315
|
# ### in fruits.yml
|
310
316
|
#
|
311
317
|
# apple:
|
@@ -331,6 +337,8 @@ module ActiveRecord
|
|
331
337
|
# id: 1
|
332
338
|
# name: George the Monkey
|
333
339
|
#
|
340
|
+
# <code></code>
|
341
|
+
#
|
334
342
|
# ### in fruits.yml
|
335
343
|
#
|
336
344
|
# apple:
|
@@ -345,6 +353,8 @@ module ActiveRecord
|
|
345
353
|
# id: 3
|
346
354
|
# name: grape
|
347
355
|
#
|
356
|
+
# <code></code>
|
357
|
+
#
|
348
358
|
# ### in fruits_monkeys.yml
|
349
359
|
#
|
350
360
|
# apple_george:
|
@@ -368,6 +378,8 @@ module ActiveRecord
|
|
368
378
|
# name: George the Monkey
|
369
379
|
# fruits: apple, orange, grape
|
370
380
|
#
|
381
|
+
# <code></code>
|
382
|
+
#
|
371
383
|
# ### in fruits.yml
|
372
384
|
#
|
373
385
|
# apple:
|
@@ -467,6 +479,8 @@ module ActiveRecord
|
|
467
479
|
# belongs_to :author
|
468
480
|
# end
|
469
481
|
#
|
482
|
+
# <code></code>
|
483
|
+
#
|
470
484
|
# # books.yml
|
471
485
|
# alices_adventure_in_wonderland:
|
472
486
|
# author_id: <%= ActiveRecord::FixtureSet.identify(:lewis_carroll) %>
|
@@ -482,6 +496,8 @@ module ActiveRecord
|
|
482
496
|
# belongs_to :book, query_constraints: [:author_id, :book_id]
|
483
497
|
# end
|
484
498
|
#
|
499
|
+
# <code></code>
|
500
|
+
#
|
485
501
|
# # book_orders.yml
|
486
502
|
# alices_adventure_in_wonderland_in_books:
|
487
503
|
# author: lewis_carroll
|
@@ -23,8 +23,6 @@ module ActiveRecord
|
|
23
23
|
@keys = @inserts.first.keys
|
24
24
|
end
|
25
25
|
|
26
|
-
configure_on_duplicate_update_logic
|
27
|
-
|
28
26
|
if model.scope_attributes?
|
29
27
|
@scope_attributes = model.scope_attributes
|
30
28
|
@keys |= @scope_attributes.keys
|
@@ -35,8 +33,8 @@ module ActiveRecord
|
|
35
33
|
@returning = false if @returning == []
|
36
34
|
|
37
35
|
@unique_by = find_unique_index_for(@unique_by)
|
38
|
-
@on_duplicate = :skip if @on_duplicate == :update && updatable_columns.empty?
|
39
36
|
|
37
|
+
configure_on_duplicate_update_logic
|
40
38
|
ensure_valid_options_for_connection!
|
41
39
|
end
|
42
40
|
|
@@ -135,6 +133,8 @@ module ActiveRecord
|
|
135
133
|
elsif custom_update_sql_provided?
|
136
134
|
@update_sql = on_duplicate
|
137
135
|
@on_duplicate = :update
|
136
|
+
elsif @on_duplicate == :update && updatable_columns.empty?
|
137
|
+
@on_duplicate = :skip
|
138
138
|
end
|
139
139
|
end
|
140
140
|
|
@@ -10,7 +10,7 @@ module ActiveRecord
|
|
10
10
|
# This is enabled by default. To disable this functionality set
|
11
11
|
# `use_metadata_table` to false in your database configuration.
|
12
12
|
class InternalMetadata # :nodoc:
|
13
|
-
class NullInternalMetadata
|
13
|
+
class NullInternalMetadata # :nodoc:
|
14
14
|
end
|
15
15
|
|
16
16
|
attr_reader :connection, :arel_table
|
@@ -24,7 +24,7 @@ module ActiveRecord
|
|
24
24
|
# To use the DatabaseSelector in your application with default settings,
|
25
25
|
# run the provided generator.
|
26
26
|
#
|
27
|
-
# bin/rails g active_record:multi_db
|
27
|
+
# $ bin/rails g active_record:multi_db
|
28
28
|
#
|
29
29
|
# This will create a file named +config/initializers/multi_db.rb+ with the
|
30
30
|
# following contents:
|
@@ -61,8 +61,10 @@ module ActiveRecord
|
|
61
61
|
column_name.is_a?(String) && /\W/.match?(column_name)
|
62
62
|
end
|
63
63
|
end
|
64
|
+
|
64
65
|
module TableDefinition
|
65
66
|
include LegacyIndexName
|
67
|
+
|
66
68
|
def column(name, type, **options)
|
67
69
|
options[:_skip_validate_options] = true
|
68
70
|
super
|
@@ -95,6 +97,12 @@ module ActiveRecord
|
|
95
97
|
super
|
96
98
|
end
|
97
99
|
|
100
|
+
def add_reference(table_name, ref_name, **options)
|
101
|
+
options[:_skip_validate_options] = true
|
102
|
+
super
|
103
|
+
end
|
104
|
+
alias :add_belongs_to :add_reference
|
105
|
+
|
98
106
|
def create_table(table_name, **options)
|
99
107
|
options[:_uses_legacy_table_name] = true
|
100
108
|
options[:_skip_validate_options] = true
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
class PendingMigrationConnection # :nodoc:
|
5
|
+
def self.establish_temporary_connection(db_config, &block)
|
6
|
+
pool = ActiveRecord::Base.connection_handler.establish_connection(db_config, owner_name: self)
|
7
|
+
|
8
|
+
yield pool.connection
|
9
|
+
ensure
|
10
|
+
ActiveRecord::Base.connection_handler.remove_connection_pool(self.name)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.primary_class?
|
14
|
+
false
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.current_preventing_writes
|
18
|
+
false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -7,6 +7,7 @@ require "active_support/core_ext/array/access"
|
|
7
7
|
require "active_support/core_ext/enumerable"
|
8
8
|
require "active_support/core_ext/module/attribute_accessors"
|
9
9
|
require "active_support/actionable_error"
|
10
|
+
require "active_record/migration/pending_migration_connection"
|
10
11
|
|
11
12
|
module ActiveRecord
|
12
13
|
class MigrationError < ActiveRecordError # :nodoc:
|
@@ -370,7 +371,8 @@ module ActiveRecord
|
|
370
371
|
# The \Rails package has several tools to help create and apply migrations.
|
371
372
|
#
|
372
373
|
# To generate a new migration, you can use
|
373
|
-
#
|
374
|
+
#
|
375
|
+
# $ bin/rails generate migration MyNewMigration
|
374
376
|
#
|
375
377
|
# where MyNewMigration is the name of your migration. The generator will
|
376
378
|
# create an empty migration file <tt>timestamp_my_new_migration.rb</tt>
|
@@ -379,7 +381,7 @@ module ActiveRecord
|
|
379
381
|
#
|
380
382
|
# There is a special syntactic shortcut to generate migrations that add fields to a table.
|
381
383
|
#
|
382
|
-
# bin/rails generate migration add_fieldname_to_tablename fieldname:string
|
384
|
+
# $ bin/rails generate migration add_fieldname_to_tablename fieldname:string
|
383
385
|
#
|
384
386
|
# This will generate the file <tt>timestamp_add_fieldname_to_tablename.rb</tt>, which will look like this:
|
385
387
|
# class AddFieldnameToTablename < ActiveRecord::Migration[7.1]
|
@@ -768,9 +770,11 @@ module ActiveRecord
|
|
768
770
|
def pending_migrations
|
769
771
|
pending_migrations = []
|
770
772
|
|
771
|
-
ActiveRecord::
|
772
|
-
|
773
|
-
|
773
|
+
ActiveRecord::Base.configurations.configs_for(env_name: env).each do |db_config|
|
774
|
+
ActiveRecord::PendingMigrationConnection.establish_temporary_connection(db_config) do |conn|
|
775
|
+
if pending = conn.migration_context.open.pending_migrations
|
776
|
+
pending_migrations << pending
|
777
|
+
end
|
774
778
|
end
|
775
779
|
end
|
776
780
|
|