activerecord 7.1.1 → 7.1.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|