activerecord 7.2.0.beta2 → 7.2.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +45 -1
- data/lib/active_record/associations/association.rb +6 -0
- data/lib/active_record/associations/collection_association.rb +7 -3
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/nested_error.rb +1 -1
- data/lib/active_record/associations.rb +2 -264
- data/lib/active_record/attribute_methods/dirty.rb +1 -1
- data/lib/active_record/attribute_methods/read.rb +3 -3
- data/lib/active_record/attribute_methods/write.rb +3 -3
- data/lib/active_record/attribute_methods.rb +8 -6
- data/lib/active_record/autosave_association.rb +3 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +27 -11
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/transaction.rb +67 -9
- data/lib/active_record/connection_adapters/abstract_adapter.rb +8 -1
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +1 -1
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +5 -10
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +28 -10
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +3 -9
- data/lib/active_record/database_configurations/database_config.rb +4 -0
- data/lib/active_record/errors.rb +32 -11
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/railtie.rb +2 -3
- data/lib/active_record/railties/databases.rake +1 -1
- data/lib/active_record/relation/batches.rb +7 -1
- data/lib/active_record/relation/calculations.rb +1 -1
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +9 -3
- data/lib/active_record/relation/query_methods.rb +25 -13
- data/lib/active_record/signed_id.rb +9 -0
- data/lib/active_record/tasks/database_tasks.rb +19 -8
- data/lib/active_record/test_fixtures.rb +10 -3
- data/lib/active_record/timestamp.rb +1 -1
- data/lib/active_record/transaction.rb +56 -55
- data/lib/active_record/transactions.rb +1 -1
- data/lib/active_record.rb +1 -1
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- metadata +13 -12
@@ -8,11 +8,11 @@ module ActiveRecord
|
|
8
8
|
|
9
9
|
module ClassMethods # :nodoc:
|
10
10
|
private
|
11
|
-
def define_method_attribute(
|
11
|
+
def define_method_attribute(canonical_name, owner:, as: canonical_name)
|
12
12
|
ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
|
13
|
-
owner,
|
13
|
+
owner, canonical_name
|
14
14
|
) do |temp_method_name, attr_name_expr|
|
15
|
-
owner.define_cached_method(
|
15
|
+
owner.define_cached_method(temp_method_name, as: as, namespace: :active_record) do |batch|
|
16
16
|
batch <<
|
17
17
|
"def #{temp_method_name}" <<
|
18
18
|
" _read_attribute(#{attr_name_expr}) { |n| missing_attribute(n, caller) }" <<
|
@@ -12,11 +12,11 @@ module ActiveRecord
|
|
12
12
|
|
13
13
|
module ClassMethods # :nodoc:
|
14
14
|
private
|
15
|
-
def define_method_attribute=(
|
15
|
+
def define_method_attribute=(canonical_name, owner:, as: canonical_name)
|
16
16
|
ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
|
17
|
-
owner,
|
17
|
+
owner, canonical_name, writer: true,
|
18
18
|
) do |temp_method_name, attr_name_expr|
|
19
|
-
owner.define_cached_method("#{
|
19
|
+
owner.define_cached_method(temp_method_name, as: "#{as}=", namespace: :active_record) do |batch|
|
20
20
|
batch <<
|
21
21
|
"def #{temp_method_name}(value)" <<
|
22
22
|
" _write_attribute(#{attr_name_expr}, value)" <<
|
@@ -77,6 +77,13 @@ module ActiveRecord
|
|
77
77
|
# alias attributes in Active Record are lazily generated
|
78
78
|
end
|
79
79
|
|
80
|
+
def generate_alias_attribute_methods(code_generator, new_name, old_name) # :nodoc:
|
81
|
+
attribute_method_patterns.each do |pattern|
|
82
|
+
alias_attribute_method_definition(code_generator, pattern, new_name, old_name)
|
83
|
+
end
|
84
|
+
attribute_method_patterns_cache.clear
|
85
|
+
end
|
86
|
+
|
80
87
|
def alias_attribute_method_definition(code_generator, pattern, new_name, old_name)
|
81
88
|
old_name = old_name.to_s
|
82
89
|
|
@@ -84,12 +91,7 @@ module ActiveRecord
|
|
84
91
|
raise ArgumentError, "#{self.name} model aliases `#{old_name}`, but `#{old_name}` is not an attribute. " \
|
85
92
|
"Use `alias_method :#{new_name}, :#{old_name}` or define the method manually."
|
86
93
|
else
|
87
|
-
|
88
|
-
parameters = pattern.parameters
|
89
|
-
|
90
|
-
define_proxy_call(code_generator, method_name, pattern.proxy_target, parameters, old_name,
|
91
|
-
namespace: :proxy_alias_attribute
|
92
|
-
)
|
94
|
+
define_attribute_method_pattern(pattern, old_name, owner: code_generator, as: new_name, override: true)
|
93
95
|
end
|
94
96
|
end
|
95
97
|
|
@@ -428,7 +428,9 @@ module ActiveRecord
|
|
428
428
|
# ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
|
429
429
|
def save_has_one_association(reflection)
|
430
430
|
association = association_instance_get(reflection.name)
|
431
|
-
|
431
|
+
return unless association && association.loaded?
|
432
|
+
|
433
|
+
record = association.load_target
|
432
434
|
|
433
435
|
if record && !record.destroyed?
|
434
436
|
autosave = reflection.options[:autosave]
|
@@ -253,6 +253,7 @@ module ActiveRecord
|
|
253
253
|
|
254
254
|
@available = ConnectionLeasingQueue.new self
|
255
255
|
@pinned_connection = nil
|
256
|
+
@pinned_connections_depth = 0
|
256
257
|
|
257
258
|
@async_executor = build_async_executor
|
258
259
|
|
@@ -262,6 +263,13 @@ module ActiveRecord
|
|
262
263
|
@reaper.run
|
263
264
|
end
|
264
265
|
|
266
|
+
def inspect # :nodoc:
|
267
|
+
name_field = " name=#{db_config.name.inspect}" unless db_config.name == "primary"
|
268
|
+
shard_field = " shard=#{@shard.inspect}" unless @shard == :default
|
269
|
+
|
270
|
+
"#<#{self.class.name} env_name=#{db_config.env_name.inspect}#{name_field} role=#{role.inspect}#{shard_field}>"
|
271
|
+
end
|
272
|
+
|
265
273
|
def schema_cache
|
266
274
|
@schema_cache ||= BoundSchemaReflection.new(schema_reflection, self)
|
267
275
|
end
|
@@ -311,9 +319,9 @@ module ActiveRecord
|
|
311
319
|
end
|
312
320
|
|
313
321
|
def pin_connection!(lock_thread) # :nodoc:
|
314
|
-
|
322
|
+
@pinned_connection ||= (connection_lease&.connection || checkout)
|
323
|
+
@pinned_connections_depth += 1
|
315
324
|
|
316
|
-
@pinned_connection = (connection_lease&.connection || checkout)
|
317
325
|
# Any leased connection must be in @connections otherwise
|
318
326
|
# some methods like #connected? won't behave correctly
|
319
327
|
unless @connections.include?(@pinned_connection)
|
@@ -330,7 +338,10 @@ module ActiveRecord
|
|
330
338
|
|
331
339
|
clean = true
|
332
340
|
@pinned_connection.lock.synchronize do
|
333
|
-
|
341
|
+
@pinned_connections_depth -= 1
|
342
|
+
connection = @pinned_connection
|
343
|
+
@pinned_connection = nil if @pinned_connections_depth.zero?
|
344
|
+
|
334
345
|
if connection.transaction_open?
|
335
346
|
connection.rollback_transaction
|
336
347
|
else
|
@@ -338,8 +349,11 @@ module ActiveRecord
|
|
338
349
|
clean = false
|
339
350
|
connection.reset!
|
340
351
|
end
|
341
|
-
|
342
|
-
|
352
|
+
|
353
|
+
if @pinned_connection.nil?
|
354
|
+
connection.lock_thread = nil
|
355
|
+
checkin(connection)
|
356
|
+
end
|
343
357
|
end
|
344
358
|
|
345
359
|
clean
|
@@ -527,12 +541,14 @@ module ActiveRecord
|
|
527
541
|
# - ActiveRecord::ConnectionTimeoutError no connection can be obtained from the pool.
|
528
542
|
def checkout(checkout_timeout = @checkout_timeout)
|
529
543
|
if @pinned_connection
|
530
|
-
synchronize do
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
@connections
|
544
|
+
@pinned_connection.lock.synchronize do
|
545
|
+
synchronize do
|
546
|
+
@pinned_connection.verify!
|
547
|
+
# Any leased connection must be in @connections otherwise
|
548
|
+
# some methods like #connected? won't behave correctly
|
549
|
+
unless @connections.include?(@pinned_connection)
|
550
|
+
@connections << @pinned_connection
|
551
|
+
end
|
536
552
|
end
|
537
553
|
end
|
538
554
|
@pinned_connection
|
@@ -356,7 +356,7 @@ module ActiveRecord
|
|
356
356
|
if isolation
|
357
357
|
raise ActiveRecord::TransactionIsolationError, "cannot set isolation when joining a transaction"
|
358
358
|
end
|
359
|
-
yield current_transaction
|
359
|
+
yield current_transaction.user_transaction
|
360
360
|
else
|
361
361
|
transaction_manager.within_new_transaction(isolation: isolation, joinable: joinable, &block)
|
362
362
|
end
|
@@ -277,7 +277,7 @@ module ActiveRecord
|
|
277
277
|
type_casted_binds: -> { type_casted_binds(binds) },
|
278
278
|
name: name,
|
279
279
|
connection: self,
|
280
|
-
transaction: current_transaction.presence,
|
280
|
+
transaction: current_transaction.user_transaction.presence,
|
281
281
|
cached: true
|
282
282
|
}
|
283
283
|
end
|
@@ -91,7 +91,9 @@ module ActiveRecord
|
|
91
91
|
raise InstrumentationAlreadyStartedError.new("Called start on an already started transaction") if @started
|
92
92
|
@started = true
|
93
93
|
|
94
|
-
|
94
|
+
ActiveSupport::Notifications.instrument("start_transaction.active_record", @base_payload)
|
95
|
+
|
96
|
+
@payload = @base_payload.dup # We dup because the payload for a given event is mutated later to add the outcome.
|
95
97
|
@handle = ActiveSupport::Notifications.instrumenter.build_handle("transaction.active_record", @payload)
|
96
98
|
@handle.start
|
97
99
|
end
|
@@ -106,10 +108,8 @@ module ActiveRecord
|
|
106
108
|
end
|
107
109
|
|
108
110
|
class NullTransaction # :nodoc:
|
109
|
-
def initialize; end
|
110
111
|
def state; end
|
111
112
|
def closed?; true; end
|
112
|
-
alias_method :blank?, :closed?
|
113
113
|
def open?; false; end
|
114
114
|
def joinable?; false; end
|
115
115
|
def add_record(record, _ = true); end
|
@@ -121,12 +121,31 @@ module ActiveRecord
|
|
121
121
|
def materialized?; false; end
|
122
122
|
def before_commit; yield; end
|
123
123
|
def after_commit; yield; end
|
124
|
-
def after_rollback; end
|
125
|
-
def
|
124
|
+
def after_rollback; end
|
125
|
+
def user_transaction; ActiveRecord::Transaction::NULL_TRANSACTION; end
|
126
126
|
end
|
127
127
|
|
128
|
-
class Transaction
|
129
|
-
|
128
|
+
class Transaction # :nodoc:
|
129
|
+
class Callback # :nodoc:
|
130
|
+
def initialize(event, callback)
|
131
|
+
@event = event
|
132
|
+
@callback = callback
|
133
|
+
end
|
134
|
+
|
135
|
+
def before_commit
|
136
|
+
@callback.call if @event == :before_commit
|
137
|
+
end
|
138
|
+
|
139
|
+
def after_commit
|
140
|
+
@callback.call if @event == :after_commit
|
141
|
+
end
|
142
|
+
|
143
|
+
def after_rollback
|
144
|
+
@callback.call if @event == :after_rollback
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
attr_reader :connection, :state, :savepoint_name, :isolation_level, :user_transaction
|
130
149
|
attr_accessor :written
|
131
150
|
|
132
151
|
delegate :invalidate!, :invalidated?, to: :@state
|
@@ -135,6 +154,7 @@ module ActiveRecord
|
|
135
154
|
super()
|
136
155
|
@connection = connection
|
137
156
|
@state = TransactionState.new
|
157
|
+
@callbacks = nil
|
138
158
|
@records = nil
|
139
159
|
@isolation_level = isolation
|
140
160
|
@materialized = false
|
@@ -142,7 +162,8 @@ module ActiveRecord
|
|
142
162
|
@run_commit_callbacks = run_commit_callbacks
|
143
163
|
@lazy_enrollment_records = nil
|
144
164
|
@dirty = false
|
145
|
-
@
|
165
|
+
@user_transaction = joinable ? ActiveRecord::Transaction.new(self) : ActiveRecord::Transaction::NULL_TRANSACTION
|
166
|
+
@instrumenter = TransactionInstrumenter.new(connection: connection, transaction: @user_transaction)
|
146
167
|
end
|
147
168
|
|
148
169
|
def dirty!
|
@@ -153,6 +174,14 @@ module ActiveRecord
|
|
153
174
|
@dirty
|
154
175
|
end
|
155
176
|
|
177
|
+
def open?
|
178
|
+
true
|
179
|
+
end
|
180
|
+
|
181
|
+
def closed?
|
182
|
+
false
|
183
|
+
end
|
184
|
+
|
156
185
|
def add_record(record, ensure_finalize = true)
|
157
186
|
@records ||= []
|
158
187
|
if ensure_finalize
|
@@ -163,6 +192,30 @@ module ActiveRecord
|
|
163
192
|
end
|
164
193
|
end
|
165
194
|
|
195
|
+
def before_commit(&block)
|
196
|
+
if @state.finalized?
|
197
|
+
raise ActiveRecordError, "Cannot register callbacks on a finalized transaction"
|
198
|
+
end
|
199
|
+
|
200
|
+
(@callbacks ||= []) << Callback.new(:before_commit, block)
|
201
|
+
end
|
202
|
+
|
203
|
+
def after_commit(&block)
|
204
|
+
if @state.finalized?
|
205
|
+
raise ActiveRecordError, "Cannot register callbacks on a finalized transaction"
|
206
|
+
end
|
207
|
+
|
208
|
+
(@callbacks ||= []) << Callback.new(:after_commit, block)
|
209
|
+
end
|
210
|
+
|
211
|
+
def after_rollback(&block)
|
212
|
+
if @state.finalized?
|
213
|
+
raise ActiveRecordError, "Cannot register callbacks on a finalized transaction"
|
214
|
+
end
|
215
|
+
|
216
|
+
(@callbacks ||= []) << Callback.new(:after_rollback, block)
|
217
|
+
end
|
218
|
+
|
166
219
|
def records
|
167
220
|
if @lazy_enrollment_records
|
168
221
|
@records.concat @lazy_enrollment_records.values
|
@@ -274,6 +327,11 @@ module ActiveRecord
|
|
274
327
|
def full_rollback?; true; end
|
275
328
|
def joinable?; @joinable; end
|
276
329
|
|
330
|
+
protected
|
331
|
+
def append_callbacks(callbacks) # :nodoc:
|
332
|
+
(@callbacks ||= []).concat(callbacks)
|
333
|
+
end
|
334
|
+
|
277
335
|
private
|
278
336
|
def unique_records
|
279
337
|
records.uniq(&:__id__)
|
@@ -555,7 +613,7 @@ module ActiveRecord
|
|
555
613
|
@connection.lock.synchronize do
|
556
614
|
transaction = begin_transaction(isolation: isolation, joinable: joinable)
|
557
615
|
begin
|
558
|
-
yield transaction
|
616
|
+
yield transaction.user_transaction
|
559
617
|
rescue Exception => error
|
560
618
|
rollback_transaction
|
561
619
|
after_failure_actions(transaction, error)
|
@@ -172,6 +172,13 @@ module ActiveRecord
|
|
172
172
|
@verified = false
|
173
173
|
end
|
174
174
|
|
175
|
+
def inspect # :nodoc:
|
176
|
+
name_field = " name=#{pool.db_config.name.inspect}" unless pool.db_config.name == "primary"
|
177
|
+
shard_field = " shard=#{shard.inspect}" unless shard == :default
|
178
|
+
|
179
|
+
"#<#{self.class.name}:#{'%#016x' % (object_id << 1)} env_name=#{pool.db_config.env_name.inspect}#{name_field} role=#{role.inspect}#{shard_field}>"
|
180
|
+
end
|
181
|
+
|
175
182
|
def lock_thread=(lock_thread) # :nodoc:
|
176
183
|
@lock =
|
177
184
|
case lock_thread
|
@@ -1118,7 +1125,7 @@ module ActiveRecord
|
|
1118
1125
|
statement_name: statement_name,
|
1119
1126
|
async: async,
|
1120
1127
|
connection: self,
|
1121
|
-
transaction: current_transaction.presence,
|
1128
|
+
transaction: current_transaction.user_transaction.presence,
|
1122
1129
|
row_count: 0,
|
1123
1130
|
&block
|
1124
1131
|
)
|
@@ -644,7 +644,7 @@ module ActiveRecord
|
|
644
644
|
# MySQL 8.0.19 replaces `VALUES(<expression>)` clauses with row and column alias names, see https://dev.mysql.com/worklog/task/?id=6312 .
|
645
645
|
# then MySQL 8.0.20 deprecates the `VALUES(<expression>)` see https://dev.mysql.com/worklog/task/?id=13325 .
|
646
646
|
if supports_insert_raw_alias_syntax?
|
647
|
-
values_alias = quote_table_name("#{insert.model.table_name}_values")
|
647
|
+
values_alias = quote_table_name("#{insert.model.table_name.parameterize}_values")
|
648
648
|
sql = +"INSERT #{insert.into} #{insert.values_list} AS #{values_alias}"
|
649
649
|
|
650
650
|
if insert.skip_duplicates?
|
@@ -6,16 +6,11 @@ module ActiveRecord
|
|
6
6
|
module DatabaseStatements
|
7
7
|
# Returns an ActiveRecord::Result instance.
|
8
8
|
def select_all(*, **) # :nodoc:
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
else
|
14
|
-
super
|
15
|
-
end
|
16
|
-
conn.abandon_results!
|
9
|
+
if ExplainRegistry.collect? && prepared_statements
|
10
|
+
unprepared_statement { super }
|
11
|
+
else
|
12
|
+
super
|
17
13
|
end
|
18
|
-
result
|
19
14
|
end
|
20
15
|
|
21
16
|
def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false) # :nodoc:
|
@@ -60,7 +55,6 @@ module ActiveRecord
|
|
60
55
|
combine_multi_statements(statements).each do |statement|
|
61
56
|
with_raw_connection do |conn|
|
62
57
|
raw_execute(statement, name)
|
63
|
-
conn.abandon_results!
|
64
58
|
end
|
65
59
|
end
|
66
60
|
end
|
@@ -102,6 +96,7 @@ module ActiveRecord
|
|
102
96
|
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
103
97
|
sync_timezone_changes(conn)
|
104
98
|
result = conn.query(sql)
|
99
|
+
conn.abandon_results!
|
105
100
|
verified!
|
106
101
|
handle_warnings(sql)
|
107
102
|
notification_payload[:row_count] = result&.size || 0
|
@@ -472,11 +472,7 @@ module ActiveRecord
|
|
472
472
|
end
|
473
473
|
|
474
474
|
def table_structure(table_name)
|
475
|
-
structure =
|
476
|
-
internal_exec_query("PRAGMA table_xinfo(#{quote_table_name(table_name)})", "SCHEMA")
|
477
|
-
else
|
478
|
-
internal_exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
|
479
|
-
end
|
475
|
+
structure = table_info(table_name)
|
480
476
|
raise ActiveRecord::StatementInvalid.new("Could not find table '#{table_name}'", connection_pool: @pool) if structure.empty?
|
481
477
|
table_structure_with_collation(table_name, structure)
|
482
478
|
end
|
@@ -679,7 +675,7 @@ module ActiveRecord
|
|
679
675
|
auto_increments = {}
|
680
676
|
generated_columns = {}
|
681
677
|
|
682
|
-
column_strings = table_structure_sql(table_name)
|
678
|
+
column_strings = table_structure_sql(table_name, basic_structure.map { |column| column["name"] })
|
683
679
|
|
684
680
|
if column_strings.any?
|
685
681
|
column_strings.each do |column_string|
|
@@ -712,7 +708,15 @@ module ActiveRecord
|
|
712
708
|
end
|
713
709
|
end
|
714
710
|
|
715
|
-
|
711
|
+
UNQUOTED_OPEN_PARENS_REGEX = /\((?![^'"]*['"][^'"]*$)/
|
712
|
+
FINAL_CLOSE_PARENS_REGEX = /\);*\z/
|
713
|
+
|
714
|
+
def table_structure_sql(table_name, column_names = nil)
|
715
|
+
unless column_names
|
716
|
+
column_info = table_info(table_name)
|
717
|
+
column_names = column_info.map { |column| column["name"] }
|
718
|
+
end
|
719
|
+
|
716
720
|
sql = <<~SQL
|
717
721
|
SELECT sql FROM
|
718
722
|
(SELECT * FROM sqlite_master UNION ALL
|
@@ -722,16 +726,30 @@ module ActiveRecord
|
|
722
726
|
|
723
727
|
# Result will have following sample string
|
724
728
|
# CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
725
|
-
# "password_digest" varchar COLLATE "NOCASE"
|
729
|
+
# "password_digest" varchar COLLATE "NOCASE",
|
730
|
+
# "o_id" integer,
|
731
|
+
# CONSTRAINT "fk_rails_78146ddd2e" FOREIGN KEY ("o_id") REFERENCES "os" ("id"));
|
726
732
|
result = query_value(sql, "SCHEMA")
|
727
733
|
|
728
734
|
return [] unless result
|
729
735
|
|
730
736
|
# Splitting with left parentheses and discarding the first part will return all
|
731
737
|
# columns separated with comma(,).
|
732
|
-
|
738
|
+
result.partition(UNQUOTED_OPEN_PARENS_REGEX)
|
739
|
+
.last
|
740
|
+
.sub(FINAL_CLOSE_PARENS_REGEX, "")
|
741
|
+
# column definitions can have a comma in them, so split on commas followed
|
742
|
+
# by a space and a column name in quotes or followed by the keyword CONSTRAINT
|
743
|
+
.split(/,(?=\s(?:CONSTRAINT|"(?:#{Regexp.union(column_names).source})"))/i)
|
744
|
+
.map(&:strip)
|
745
|
+
end
|
733
746
|
|
734
|
-
|
747
|
+
def table_info(table_name)
|
748
|
+
if supports_virtual_columns?
|
749
|
+
internal_exec_query("PRAGMA table_xinfo(#{quote_table_name(table_name)})", "SCHEMA")
|
750
|
+
else
|
751
|
+
internal_exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
|
752
|
+
end
|
735
753
|
end
|
736
754
|
|
737
755
|
def arel_visitor
|
@@ -4,14 +4,6 @@ module ActiveRecord
|
|
4
4
|
module ConnectionAdapters
|
5
5
|
module Trilogy
|
6
6
|
module DatabaseStatements
|
7
|
-
def select_all(*, **) # :nodoc:
|
8
|
-
result = super
|
9
|
-
with_raw_connection do |conn|
|
10
|
-
conn.next_result while conn.more_results_exist?
|
11
|
-
end
|
12
|
-
result
|
13
|
-
end
|
14
|
-
|
15
7
|
def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false) # :nodoc:
|
16
8
|
sql = transform_query(sql)
|
17
9
|
check_if_write_query(sql)
|
@@ -47,6 +39,9 @@ module ActiveRecord
|
|
47
39
|
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
48
40
|
sync_timezone_changes(conn)
|
49
41
|
result = conn.query(sql)
|
42
|
+
while conn.more_results_exist?
|
43
|
+
conn.next_result
|
44
|
+
end
|
50
45
|
verified!
|
51
46
|
handle_warnings(sql)
|
52
47
|
notification_payload[:row_count] = result.count
|
@@ -77,7 +72,6 @@ module ActiveRecord
|
|
77
72
|
combine_multi_statements(statements).each do |statement|
|
78
73
|
with_raw_connection do |conn|
|
79
74
|
raw_execute(statement, name)
|
80
|
-
conn.next_result while conn.more_results_exist?
|
81
75
|
end
|
82
76
|
end
|
83
77
|
end
|
@@ -18,6 +18,10 @@ module ActiveRecord
|
|
18
18
|
@adapter_class ||= ActiveRecord::ConnectionAdapters.resolve(adapter)
|
19
19
|
end
|
20
20
|
|
21
|
+
def inspect # :nodoc:
|
22
|
+
"#<#{self.class.name} env_name=#{@env_name} name=#{@name} adapter_class=#{adapter_class}>"
|
23
|
+
end
|
24
|
+
|
21
25
|
def new_connection
|
22
26
|
adapter_class.new(configuration_hash)
|
23
27
|
end
|
data/lib/active_record/errors.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/deprecation"
|
4
|
+
|
3
5
|
module ActiveRecord
|
6
|
+
include ActiveSupport::Deprecation::DeprecatedConstantAccessor
|
7
|
+
|
4
8
|
# = Active Record Errors
|
5
9
|
#
|
6
10
|
# Generic Active Record exception class.
|
@@ -130,9 +134,17 @@ module ActiveRecord
|
|
130
134
|
|
131
135
|
# Raised by {ActiveRecord::Base#save!}[rdoc-ref:Persistence#save!] and
|
132
136
|
# {ActiveRecord::Base.update_attribute!}[rdoc-ref:Persistence#update_attribute!]
|
133
|
-
# methods when a record
|
137
|
+
# methods when a record failed to validate or cannot be saved due to any of the
|
134
138
|
# <tt>before_*</tt> callbacks throwing +:abort+. See
|
135
|
-
# ActiveRecord::Callbacks for further details
|
139
|
+
# ActiveRecord::Callbacks for further details.
|
140
|
+
#
|
141
|
+
# class Product < ActiveRecord::Base
|
142
|
+
# before_save do
|
143
|
+
# throw :abort if price < 0
|
144
|
+
# end
|
145
|
+
# end
|
146
|
+
#
|
147
|
+
# Product.create! # => raises an ActiveRecord::RecordNotSaved
|
136
148
|
class RecordNotSaved < ActiveRecordError
|
137
149
|
attr_reader :record
|
138
150
|
|
@@ -143,15 +155,17 @@ module ActiveRecord
|
|
143
155
|
end
|
144
156
|
|
145
157
|
# Raised by {ActiveRecord::Base#destroy!}[rdoc-ref:Persistence#destroy!]
|
146
|
-
# when a
|
147
|
-
#
|
158
|
+
# when a record cannot be destroyed due to any of the
|
159
|
+
# <tt>before_destroy</tt> callbacks throwing +:abort+. See
|
160
|
+
# ActiveRecord::Callbacks for further details.
|
148
161
|
#
|
149
|
-
#
|
150
|
-
#
|
151
|
-
#
|
152
|
-
#
|
162
|
+
# class User < ActiveRecord::Base
|
163
|
+
# before_destroy do
|
164
|
+
# throw :abort if still_active?
|
165
|
+
# end
|
153
166
|
# end
|
154
167
|
#
|
168
|
+
# User.first.destroy! # => raises an ActiveRecord::RecordNotDestroyed
|
155
169
|
class RecordNotDestroyed < ActiveRecordError
|
156
170
|
attr_reader :record
|
157
171
|
|
@@ -466,10 +480,15 @@ module ActiveRecord
|
|
466
480
|
# relation.loaded? # => true
|
467
481
|
#
|
468
482
|
# # Methods which try to mutate a loaded relation fail.
|
469
|
-
# relation.where!(title: 'TODO') # => ActiveRecord::
|
470
|
-
# relation.limit!(5) # => ActiveRecord::
|
471
|
-
class
|
483
|
+
# relation.where!(title: 'TODO') # => ActiveRecord::UnmodifiableRelation
|
484
|
+
# relation.limit!(5) # => ActiveRecord::UnmodifiableRelation
|
485
|
+
class UnmodifiableRelation < ActiveRecordError
|
472
486
|
end
|
487
|
+
deprecate_constant(
|
488
|
+
:ImmutableRelation,
|
489
|
+
"ActiveRecord::UnmodifiableRelation",
|
490
|
+
deprecator: ActiveRecord.deprecator
|
491
|
+
)
|
473
492
|
|
474
493
|
# TransactionIsolationError will be raised under the following conditions:
|
475
494
|
#
|
@@ -583,3 +602,5 @@ module ActiveRecord
|
|
583
602
|
class DatabaseVersionError < ActiveRecordError
|
584
603
|
end
|
585
604
|
end
|
605
|
+
|
606
|
+
require "active_record/associations/errors"
|
@@ -241,8 +241,7 @@ To keep using the current cache store, you can turn off cache versioning entirel
|
|
241
241
|
end
|
242
242
|
|
243
243
|
ActiveSupport.on_load(:active_record) do
|
244
|
-
|
245
|
-
configs = configs.except(
|
244
|
+
configs_used_in_other_initializers = configs.except(
|
246
245
|
:migration_error,
|
247
246
|
:database_selector,
|
248
247
|
:database_resolver,
|
@@ -259,7 +258,7 @@ To keep using the current cache store, you can turn off cache versioning entirel
|
|
259
258
|
:postgresql_adapter_decode_dates,
|
260
259
|
)
|
261
260
|
|
262
|
-
|
261
|
+
configs_used_in_other_initializers.each do |k, v|
|
263
262
|
next if k == :encryption
|
264
263
|
setter = "#{k}="
|
265
264
|
# Some existing initializers might rely on Active Record configuration
|
@@ -89,7 +89,7 @@ db_namespace = namespace :db do
|
|
89
89
|
task migrate: :load_config do
|
90
90
|
db_configs = ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env)
|
91
91
|
|
92
|
-
if db_configs.size == 1
|
92
|
+
if db_configs.size == 1 && db_configs.first.primary?
|
93
93
|
ActiveRecord::Tasks::DatabaseTasks.migrate
|
94
94
|
else
|
95
95
|
mapped_versions = ActiveRecord::Tasks::DatabaseTasks.db_configs_with_versions
|
@@ -341,7 +341,13 @@ module ActiveRecord
|
|
341
341
|
|
342
342
|
if start || finish
|
343
343
|
records = records.filter do |record|
|
344
|
-
|
344
|
+
id = record.id
|
345
|
+
|
346
|
+
if order == :asc
|
347
|
+
(start.nil? || id >= start) && (finish.nil? || id <= finish)
|
348
|
+
else
|
349
|
+
(start.nil? || id <= start) && (finish.nil? || id >= finish)
|
350
|
+
end
|
345
351
|
end
|
346
352
|
end
|
347
353
|
|
@@ -604,7 +604,7 @@ module ActiveRecord
|
|
604
604
|
klass.attribute_types.fetch(name = result.columns[i]) do
|
605
605
|
join_dependencies ||= build_join_dependencies
|
606
606
|
lookup_cast_type_from_join_dependencies(name, join_dependencies) ||
|
607
|
-
result.column_types[
|
607
|
+
result.column_types[i] || Type.default_value
|
608
608
|
end
|
609
609
|
end
|
610
610
|
end
|
@@ -57,9 +57,15 @@ module ActiveRecord
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def convert_to_id(value)
|
60
|
-
|
61
|
-
|
62
|
-
|
60
|
+
if primary_key.is_a?(Array)
|
61
|
+
primary_key.map do |attribute|
|
62
|
+
if attribute == "id"
|
63
|
+
value.id_value
|
64
|
+
else
|
65
|
+
value.public_send(attribute)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
elsif value.respond_to?(primary_key)
|
63
69
|
value.public_send(primary_key)
|
64
70
|
else
|
65
71
|
value
|