activerecord 7.2.0.beta2 → 7.2.0.rc1
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 +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
|