activerecord 8.0.0.1 → 8.0.2
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 +208 -0
- data/lib/active_record/associations/alias_tracker.rb +6 -4
- data/lib/active_record/associations/belongs_to_association.rb +7 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +25 -27
- data/lib/active_record/attribute_methods/primary_key.rb +2 -1
- data/lib/active_record/attribute_methods.rb +22 -17
- data/lib/active_record/attributes.rb +2 -2
- data/lib/active_record/autosave_association.rb +21 -11
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +17 -14
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +47 -34
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +5 -1
- data/lib/active_record/connection_adapters/abstract_adapter.rb +39 -22
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +14 -7
- data/lib/active_record/connection_adapters/mysql/quoting.rb +7 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +7 -0
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +57 -11
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -1
- data/lib/active_record/connection_adapters/pool_config.rb +7 -7
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +14 -12
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +7 -2
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +2 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +7 -2
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +1 -1
- data/lib/active_record/core.rb +31 -2
- data/lib/active_record/counter_cache.rb +1 -1
- data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -1
- data/lib/active_record/delegated_type.rb +17 -17
- data/lib/active_record/future_result.rb +3 -3
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/migration/command_recorder.rb +5 -2
- data/lib/active_record/railties/databases.rake +1 -1
- data/lib/active_record/relation/calculations.rb +23 -17
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +2 -0
- data/lib/active_record/relation/query_attribute.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +1 -1
- data/lib/active_record/relation.rb +1 -1
- data/lib/active_record/schema_dumper.rb +29 -11
- data/lib/active_record/signed_id.rb +4 -3
- data/lib/active_record/statement_cache.rb +2 -2
- data/lib/active_record/transactions.rb +5 -6
- data/lib/active_record.rb +4 -2
- data/lib/arel/collectors/bind.rb +1 -1
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +2 -2
- data/lib/arel/nodes/binary.rb +1 -1
- data/lib/arel/nodes/node.rb +1 -1
- data/lib/arel/nodes/sql_literal.rb +1 -1
- data/lib/arel/visitors/to_sql.rb +1 -1
- metadata +10 -13
@@ -22,7 +22,7 @@ module ActiveRecord
|
|
22
22
|
stream.puts " # Custom types defined in this database."
|
23
23
|
stream.puts " # Note that some types may not work with other database engines. Be careful if changing database."
|
24
24
|
types.sort.each do |name, values|
|
25
|
-
stream.puts " create_enum #{name.inspect}, #{values.
|
25
|
+
stream.puts " create_enum #{name.inspect}, #{values.inspect}"
|
26
26
|
end
|
27
27
|
stream.puts
|
28
28
|
end
|
@@ -922,7 +922,7 @@ module ActiveRecord
|
|
922
922
|
#
|
923
923
|
# validate_check_constraint :products, name: "price_check"
|
924
924
|
#
|
925
|
-
# The +options+ hash accepts the same keys as add_check_constraint[rdoc-ref:ConnectionAdapters::SchemaStatements#add_check_constraint].
|
925
|
+
# The +options+ hash accepts the same keys as {add_check_constraint}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_check_constraint].
|
926
926
|
def validate_check_constraint(table_name, **options)
|
927
927
|
chk_name_to_validate = check_constraint_for!(table_name, **options).name
|
928
928
|
|
@@ -349,6 +349,7 @@ module ActiveRecord
|
|
349
349
|
@lock.synchronize do
|
350
350
|
return false unless @raw_connection
|
351
351
|
@raw_connection.query ";"
|
352
|
+
verified!
|
352
353
|
end
|
353
354
|
true
|
354
355
|
rescue PG::Error
|
@@ -520,7 +521,7 @@ module ActiveRecord
|
|
520
521
|
type.typname AS name,
|
521
522
|
type.OID AS oid,
|
522
523
|
n.nspname AS schema,
|
523
|
-
|
524
|
+
array_agg(enum.enumlabel ORDER BY enum.enumsortorder) AS value
|
524
525
|
FROM pg_enum AS enum
|
525
526
|
JOIN pg_type AS type ON (type.oid = enum.enumtypid)
|
526
527
|
JOIN pg_namespace n ON type.typnamespace = n.oid
|
@@ -633,7 +634,11 @@ module ActiveRecord
|
|
633
634
|
# Returns the version of the connected PostgreSQL server.
|
634
635
|
def get_database_version # :nodoc:
|
635
636
|
with_raw_connection do |conn|
|
636
|
-
conn.server_version
|
637
|
+
version = conn.server_version
|
638
|
+
if version == 0
|
639
|
+
raise ActiveRecord::ConnectionFailed, "Could not determine PostgreSQL version"
|
640
|
+
end
|
641
|
+
version
|
637
642
|
end
|
638
643
|
end
|
639
644
|
alias :postgresql_version :database_version
|
@@ -50,6 +50,19 @@ module ActiveRecord
|
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
+
def quote(value) # :nodoc:
|
54
|
+
case value
|
55
|
+
when Numeric
|
56
|
+
if value.finite?
|
57
|
+
super
|
58
|
+
else
|
59
|
+
"'#{value}'"
|
60
|
+
end
|
61
|
+
else
|
62
|
+
super
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
53
66
|
def quote_string(s)
|
54
67
|
::SQLite3::Database.quote(s)
|
55
68
|
end
|
@@ -27,6 +27,7 @@ module ActiveRecord
|
|
27
27
|
col["name"]
|
28
28
|
end
|
29
29
|
|
30
|
+
where = where.sub(/\s*\/\*.*\*\/\z/, "") if where
|
30
31
|
orders = {}
|
31
32
|
|
32
33
|
if columns.any?(&:nil?) # index created with an expression
|
@@ -74,6 +75,7 @@ module ActiveRecord
|
|
74
75
|
Base.pluralize_table_names ? table.pluralize : table
|
75
76
|
end
|
76
77
|
table = strip_table_name_prefix_and_suffix(table)
|
78
|
+
options = options.slice(*fk.options.keys)
|
77
79
|
fk_to_table = strip_table_name_prefix_and_suffix(fk.to_table)
|
78
80
|
fk_to_table == table && options.all? { |k, v| fk.options[k].to_s == v.to_s }
|
79
81
|
end || raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{to_table || options}")
|
@@ -207,7 +207,12 @@ module ActiveRecord
|
|
207
207
|
!(@raw_connection.nil? || @raw_connection.closed?)
|
208
208
|
end
|
209
209
|
|
210
|
-
|
210
|
+
def active?
|
211
|
+
if connected?
|
212
|
+
verified!
|
213
|
+
true
|
214
|
+
end
|
215
|
+
end
|
211
216
|
|
212
217
|
alias :reset! :reconnect!
|
213
218
|
|
@@ -407,7 +412,7 @@ module ActiveRecord
|
|
407
412
|
end
|
408
413
|
alias :add_belongs_to :add_reference
|
409
414
|
|
410
|
-
FK_REGEX = /.*FOREIGN KEY\s+\("(
|
415
|
+
FK_REGEX = /.*FOREIGN KEY\s+\("([^"]+)"\)\s+REFERENCES\s+"(\w+)"\s+\("(\w+)"\)/
|
411
416
|
DEFERRABLE_REGEX = /DEFERRABLE INITIALLY (\w+)/
|
412
417
|
def foreign_keys(table_name)
|
413
418
|
# SQLite returns 1 row for each column of composite foreign keys.
|
data/lib/active_record/core.rb
CHANGED
@@ -202,6 +202,17 @@ module ActiveRecord
|
|
202
202
|
false
|
203
203
|
end
|
204
204
|
|
205
|
+
# Intended to behave like `.current_preventing_writes` given the class name as input.
|
206
|
+
# See PoolConfig and ConnectionHandler::ConnectionDescriptor.
|
207
|
+
def self.preventing_writes?(class_name) # :nodoc:
|
208
|
+
connected_to_stack.reverse_each do |hash|
|
209
|
+
return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(Base)
|
210
|
+
return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].any? { |klass| klass.name == class_name }
|
211
|
+
end
|
212
|
+
|
213
|
+
false
|
214
|
+
end
|
215
|
+
|
205
216
|
def self.connected_to_stack # :nodoc:
|
206
217
|
if connected_to_stack = ActiveSupport::IsolatedExecutionState[:active_record_connected_to_stack]
|
207
218
|
connected_to_stack
|
@@ -727,11 +738,29 @@ module ActiveRecord
|
|
727
738
|
@strict_loading_mode == :all
|
728
739
|
end
|
729
740
|
|
730
|
-
#
|
741
|
+
# Prevents records from being written to the database:
|
742
|
+
#
|
743
|
+
# customer = Customer.new
|
744
|
+
# customer.readonly!
|
745
|
+
# customer.save # raises ActiveRecord::ReadOnlyRecord
|
731
746
|
#
|
732
747
|
# customer = Customer.first
|
733
748
|
# customer.readonly!
|
734
|
-
# customer.
|
749
|
+
# customer.update(name: 'New Name') # raises ActiveRecord::ReadOnlyRecord
|
750
|
+
#
|
751
|
+
# Read-only records cannot be deleted from the database either:
|
752
|
+
#
|
753
|
+
# customer = Customer.first
|
754
|
+
# customer.readonly!
|
755
|
+
# customer.destroy # raises ActiveRecord::ReadOnlyRecord
|
756
|
+
#
|
757
|
+
# Please, note that the objects themselves are still mutable in memory:
|
758
|
+
#
|
759
|
+
# customer = Customer.new
|
760
|
+
# customer.readonly!
|
761
|
+
# customer.name = 'New Name' # OK
|
762
|
+
#
|
763
|
+
# but you won't be able to persist the changes.
|
735
764
|
def readonly!
|
736
765
|
@readonly = true
|
737
766
|
end
|
@@ -28,7 +28,7 @@ module ActiveRecord
|
|
28
28
|
# # For the Post with id #1, reset the comments_count
|
29
29
|
# Post.reset_counters(1, :comments)
|
30
30
|
#
|
31
|
-
# # Like above, but also touch the
|
31
|
+
# # Like above, but also touch the updated_at and/or updated_on
|
32
32
|
# # attributes.
|
33
33
|
# Post.reset_counters(1, :comments, touch: true)
|
34
34
|
def reset_counters(id, *counters, touch: nil)
|
@@ -81,7 +81,9 @@ module ActiveRecord
|
|
81
81
|
|
82
82
|
def resolved_adapter
|
83
83
|
adapter = uri.scheme && @uri.scheme.tr("-", "_")
|
84
|
-
adapter
|
84
|
+
if adapter && ActiveRecord.protocol_adapters[adapter]
|
85
|
+
adapter = ActiveRecord.protocol_adapters[adapter]
|
86
|
+
end
|
85
87
|
adapter
|
86
88
|
end
|
87
89
|
|
@@ -181,16 +181,16 @@ module ActiveRecord
|
|
181
181
|
# delegated_type :entryable, types: %w[ Message Comment ], dependent: :destroy
|
182
182
|
# end
|
183
183
|
#
|
184
|
-
#
|
185
|
-
#
|
186
|
-
# Entry.messages
|
187
|
-
#
|
188
|
-
#
|
189
|
-
#
|
190
|
-
# Entry.comments
|
191
|
-
#
|
192
|
-
#
|
193
|
-
#
|
184
|
+
# @entry.entryable_class # => Message or Comment
|
185
|
+
# @entry.entryable_name # => "message" or "comment"
|
186
|
+
# Entry.messages # => Entry.where(entryable_type: "Message")
|
187
|
+
# @entry.message? # => true when entryable_type == "Message"
|
188
|
+
# @entry.message # => returns the message record, when entryable_type == "Message", otherwise nil
|
189
|
+
# @entry.message_id # => returns entryable_id, when entryable_type == "Message", otherwise nil
|
190
|
+
# Entry.comments # => Entry.where(entryable_type: "Comment")
|
191
|
+
# @entry.comment? # => true when entryable_type == "Comment"
|
192
|
+
# @entry.comment # => returns the comment record, when entryable_type == "Comment", otherwise nil
|
193
|
+
# @entry.comment_id # => returns entryable_id, when entryable_type == "Comment", otherwise nil
|
194
194
|
#
|
195
195
|
# You can also declare namespaced types:
|
196
196
|
#
|
@@ -199,25 +199,25 @@ module ActiveRecord
|
|
199
199
|
# end
|
200
200
|
#
|
201
201
|
# Entry.access_notice_messages
|
202
|
-
# entry.access_notice_message
|
203
|
-
# entry.access_notice_message?
|
202
|
+
# @entry.access_notice_message
|
203
|
+
# @entry.access_notice_message?
|
204
204
|
#
|
205
205
|
# === Options
|
206
206
|
#
|
207
207
|
# The +options+ are passed directly to the +belongs_to+ call, so this is where you declare +dependent+ etc.
|
208
208
|
# The following options can be included to specialize the behavior of the delegated type convenience methods.
|
209
209
|
#
|
210
|
-
# [
|
210
|
+
# [+:foreign_key+]
|
211
211
|
# Specify the foreign key used for the convenience methods. By default this is guessed to be the passed
|
212
212
|
# +role+ with an "_id" suffix. So a class that defines a
|
213
213
|
# <tt>delegated_type :entryable, types: %w[ Message Comment ]</tt> association will use "entryable_id" as
|
214
214
|
# the default <tt>:foreign_key</tt>.
|
215
|
-
# [
|
215
|
+
# [+:foreign_type+]
|
216
216
|
# Specify the column used to store the associated object's type. By default this is inferred to be the passed
|
217
217
|
# +role+ with a "_type" suffix. A class that defines a
|
218
218
|
# <tt>delegated_type :entryable, types: %w[ Message Comment ]</tt> association will use "entryable_type" as
|
219
219
|
# the default <tt>:foreign_type</tt>.
|
220
|
-
# [
|
220
|
+
# [+:primary_key+]
|
221
221
|
# Specify the method that returns the primary key of associated object used for the convenience methods.
|
222
222
|
# By default this is +id+.
|
223
223
|
#
|
@@ -226,8 +226,8 @@ module ActiveRecord
|
|
226
226
|
# delegated_type :entryable, types: %w[ Message Comment ], primary_key: :uuid, foreign_key: :entryable_uuid
|
227
227
|
# end
|
228
228
|
#
|
229
|
-
#
|
230
|
-
#
|
229
|
+
# @entry.message_uuid # => returns entryable_uuid, when entryable_type == "Message", otherwise nil
|
230
|
+
# @entry.comment_uuid # => returns entryable_uuid, when entryable_type == "Comment", otherwise nil
|
231
231
|
def delegated_type(role, types:, **options)
|
232
232
|
belongs_to role, options.delete(:scope), **options.merge(polymorphic: true)
|
233
233
|
define_delegated_type_methods role, types: types, options: options
|
@@ -108,9 +108,9 @@ module ActiveRecord
|
|
108
108
|
begin
|
109
109
|
if pending?
|
110
110
|
@event_buffer = EventBuffer.new(self, @instrumenter)
|
111
|
-
|
112
|
-
|
113
|
-
|
111
|
+
ActiveSupport::IsolatedExecutionState[:active_record_instrumenter] = @event_buffer
|
112
|
+
|
113
|
+
execute_query(connection, async: true)
|
114
114
|
end
|
115
115
|
ensure
|
116
116
|
@mutex.unlock
|
@@ -213,7 +213,9 @@ module ActiveRecord
|
|
213
213
|
raise ActiveRecord::IrreversibleMigration, "To avoid mistakes, drop_table is only reversible if given options or a block (can be empty)."
|
214
214
|
end
|
215
215
|
|
216
|
-
|
216
|
+
args << options unless options.empty?
|
217
|
+
|
218
|
+
super(args, &block)
|
217
219
|
end
|
218
220
|
|
219
221
|
def invert_rename_table(args)
|
@@ -380,7 +382,8 @@ module ActiveRecord
|
|
380
382
|
raise ActiveRecord::IrreversibleMigration, "rename_enum_value is only reversible if given a :from and :to option."
|
381
383
|
end
|
382
384
|
|
383
|
-
[:
|
385
|
+
options[:to], options[:from] = options[:from], options[:to]
|
386
|
+
[:rename_enum_value, [type_name, options]]
|
384
387
|
end
|
385
388
|
|
386
389
|
def invert_drop_virtual_table(args)
|
@@ -160,7 +160,7 @@ db_namespace = namespace :db do
|
|
160
160
|
end
|
161
161
|
end
|
162
162
|
|
163
|
-
|
163
|
+
desc "Resets your database using your migrations for the current environment"
|
164
164
|
task reset: ["db:drop", "db:create", "db:schema:dump", "db:migrate"]
|
165
165
|
|
166
166
|
desc 'Run the "up" for a given migration VERSION.'
|
@@ -410,6 +410,18 @@ module ActiveRecord
|
|
410
410
|
async.ids
|
411
411
|
end
|
412
412
|
|
413
|
+
protected
|
414
|
+
def aggregate_column(column_name)
|
415
|
+
case column_name
|
416
|
+
when Arel::Expressions
|
417
|
+
column_name
|
418
|
+
when :all
|
419
|
+
Arel.star
|
420
|
+
else
|
421
|
+
arel_column(column_name)
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
413
425
|
private
|
414
426
|
def all_attributes?(column_names)
|
415
427
|
(column_names.map(&:to_s) - model.attribute_names - model.attribute_aliases.keys).empty?
|
@@ -450,17 +462,6 @@ module ActiveRecord
|
|
450
462
|
column_name.is_a?(::String) && /\bDISTINCT[\s(]/i.match?(column_name)
|
451
463
|
end
|
452
464
|
|
453
|
-
def aggregate_column(column_name)
|
454
|
-
case column_name
|
455
|
-
when Arel::Expressions
|
456
|
-
column_name
|
457
|
-
when :all
|
458
|
-
Arel.star
|
459
|
-
else
|
460
|
-
arel_column(column_name)
|
461
|
-
end
|
462
|
-
end
|
463
|
-
|
464
465
|
def operation_over_aggregate_column(column, operation, distinct)
|
465
466
|
operation == "count" ? column.count(distinct) : column.public_send(operation)
|
466
467
|
end
|
@@ -476,7 +477,7 @@ module ActiveRecord
|
|
476
477
|
# PostgreSQL doesn't like ORDER BY when there are no GROUP BY
|
477
478
|
relation = unscope(:order).distinct!(false)
|
478
479
|
|
479
|
-
column = aggregate_column(column_name)
|
480
|
+
column = relation.aggregate_column(column_name)
|
480
481
|
select_value = operation_over_aggregate_column(column, operation, distinct)
|
481
482
|
select_value.distinct = true if operation == "sum" && distinct
|
482
483
|
|
@@ -486,7 +487,11 @@ module ActiveRecord
|
|
486
487
|
end
|
487
488
|
|
488
489
|
query_result = if relation.where_clause.contradiction?
|
489
|
-
|
490
|
+
if @async
|
491
|
+
FutureResult.wrap(ActiveRecord::Result.empty)
|
492
|
+
else
|
493
|
+
ActiveRecord::Result.empty
|
494
|
+
end
|
490
495
|
else
|
491
496
|
skip_query_cache_if_necessary do
|
492
497
|
model.with_connection do |c|
|
@@ -515,7 +520,9 @@ module ActiveRecord
|
|
515
520
|
associated = association && association.belongs_to? # only count belongs_to associations
|
516
521
|
group_fields = Array(association.foreign_key) if associated
|
517
522
|
end
|
518
|
-
|
523
|
+
|
524
|
+
relation = except(:group).distinct!(false)
|
525
|
+
group_fields = relation.arel_columns(group_fields)
|
519
526
|
|
520
527
|
model.with_connection do |connection|
|
521
528
|
column_alias_tracker = ColumnAliasTracker.new(connection)
|
@@ -526,7 +533,7 @@ module ActiveRecord
|
|
526
533
|
}
|
527
534
|
group_columns = group_aliases.zip(group_fields)
|
528
535
|
|
529
|
-
column = aggregate_column(column_name)
|
536
|
+
column = relation.aggregate_column(column_name)
|
530
537
|
column_alias = column_alias_tracker.alias_for("#{operation} #{column_name.to_s.downcase}")
|
531
538
|
select_value = operation_over_aggregate_column(column, operation, distinct)
|
532
539
|
select_value.as(model.adapter_class.quote_column_name(column_alias))
|
@@ -543,7 +550,6 @@ module ActiveRecord
|
|
543
550
|
end
|
544
551
|
}
|
545
552
|
|
546
|
-
relation = except(:group).distinct!(false)
|
547
553
|
relation.group_values = group_fields
|
548
554
|
relation.select_values = select_values
|
549
555
|
|
@@ -659,7 +665,7 @@ module ActiveRecord
|
|
659
665
|
relation.select_values = [ Arel.sql(FinderMethods::ONE_AS_ONE) ] unless distinct
|
660
666
|
else
|
661
667
|
column_alias = Arel.sql("count_column")
|
662
|
-
relation.select_values = [ aggregate_column(column_name).as(column_alias) ]
|
668
|
+
relation.select_values = [ relation.aggregate_column(column_name).as(column_alias) ]
|
663
669
|
end
|
664
670
|
|
665
671
|
subquery_alias = Arel.sql("subquery_for_count", retryable: true)
|
@@ -35,7 +35,7 @@ module ActiveRecord
|
|
35
35
|
def nil?
|
36
36
|
unless value_before_type_cast.is_a?(StatementCache::Substitute)
|
37
37
|
value_before_type_cast.nil? ||
|
38
|
-
type.respond_to?(:subtype) && serializable? && value_for_database.nil?
|
38
|
+
(type.respond_to?(:subtype) || type.respond_to?(:normalizer)) && serializable? && value_for_database.nil?
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
@@ -1990,7 +1990,7 @@ module ActiveRecord
|
|
1990
1990
|
def arel_column(field)
|
1991
1991
|
field = field.name if is_symbol = field.is_a?(Symbol)
|
1992
1992
|
|
1993
|
-
field = model.attribute_aliases[field] || field
|
1993
|
+
field = model.attribute_aliases[field] || field.to_s
|
1994
1994
|
from = from_clause.name || from_clause.value
|
1995
1995
|
|
1996
1996
|
if model.columns_hash.key?(field) && (!from || table_name_matches?(from))
|
@@ -820,7 +820,7 @@ module ActiveRecord
|
|
820
820
|
#
|
821
821
|
# [:returning]
|
822
822
|
# (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
|
823
|
-
#
|
823
|
+
# upserted records, which by default is the primary key.
|
824
824
|
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
825
825
|
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
826
826
|
# clause entirely.
|
@@ -207,12 +207,17 @@ module ActiveRecord
|
|
207
207
|
end
|
208
208
|
|
209
209
|
indexes_in_create(table, tbl)
|
210
|
-
check_constraints_in_create(table, tbl) if @connection.supports_check_constraints?
|
210
|
+
remaining = check_constraints_in_create(table, tbl) if @connection.supports_check_constraints?
|
211
211
|
exclusion_constraints_in_create(table, tbl) if @connection.supports_exclusion_constraints?
|
212
212
|
unique_constraints_in_create(table, tbl) if @connection.supports_unique_constraints?
|
213
213
|
|
214
214
|
tbl.puts " end"
|
215
215
|
|
216
|
+
if remaining
|
217
|
+
tbl.puts
|
218
|
+
tbl.print remaining.string
|
219
|
+
end
|
220
|
+
|
216
221
|
stream.print tbl.string
|
217
222
|
rescue => e
|
218
223
|
stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}"
|
@@ -277,24 +282,37 @@ module ActiveRecord
|
|
277
282
|
|
278
283
|
def check_constraints_in_create(table, stream)
|
279
284
|
if (check_constraints = @connection.check_constraints(table)).any?
|
280
|
-
|
281
|
-
parts = [
|
282
|
-
"t.check_constraint #{check_constraint.expression.inspect}"
|
283
|
-
]
|
285
|
+
check_valid, check_invalid = check_constraints.partition { |chk| chk.validate? }
|
284
286
|
|
285
|
-
|
286
|
-
|
287
|
+
unless check_valid.empty?
|
288
|
+
check_constraint_statements = check_valid.map do |check|
|
289
|
+
" t.check_constraint #{check_parts(check).join(', ')}"
|
287
290
|
end
|
288
291
|
|
289
|
-
|
290
|
-
|
291
|
-
" #{parts.join(', ')}"
|
292
|
+
stream.puts check_constraint_statements.sort.join("\n")
|
292
293
|
end
|
293
294
|
|
294
|
-
|
295
|
+
unless check_invalid.empty?
|
296
|
+
remaining = StringIO.new
|
297
|
+
table_name = remove_prefix_and_suffix(table).inspect
|
298
|
+
|
299
|
+
add_check_constraint_statements = check_invalid.map do |check|
|
300
|
+
" add_check_constraint #{([table_name] + check_parts(check)).join(', ')}"
|
301
|
+
end
|
302
|
+
|
303
|
+
remaining.puts add_check_constraint_statements.sort.join("\n")
|
304
|
+
remaining
|
305
|
+
end
|
295
306
|
end
|
296
307
|
end
|
297
308
|
|
309
|
+
def check_parts(check)
|
310
|
+
check_parts = [ check.expression.inspect ]
|
311
|
+
check_parts << "name: #{check.name.inspect}" if check.export_name_on_schema_dump?
|
312
|
+
check_parts << "validate: #{check.validate?.inspect}" unless check.validate?
|
313
|
+
check_parts
|
314
|
+
end
|
315
|
+
|
298
316
|
def foreign_keys(table, stream)
|
299
317
|
if (foreign_keys = @connection.foreign_keys(table)).any?
|
300
318
|
add_foreign_key_statements = foreign_keys.map do |foreign_key|
|
@@ -76,8 +76,9 @@ module ActiveRecord
|
|
76
76
|
end
|
77
77
|
|
78
78
|
# The verifier instance that all signed ids are generated and verified from. By default, it'll be initialized
|
79
|
-
# with the class-level +signed_id_verifier_secret+, which within
|
80
|
-
# Rails.application.key_generator.
|
79
|
+
# with the class-level +signed_id_verifier_secret+, which within Rails comes from
|
80
|
+
# {Rails.application.key_generator}[rdoc-ref:Rails::Application#key_generator].
|
81
|
+
# By default, it's SHA256 for the digest and JSON for the serialization.
|
81
82
|
def signed_id_verifier
|
82
83
|
@signed_id_verifier ||= begin
|
83
84
|
secret = signed_id_verifier_secret
|
@@ -93,7 +94,7 @@ module ActiveRecord
|
|
93
94
|
|
94
95
|
# Allows you to pass in a custom verifier used for the signed ids. This also allows you to use different
|
95
96
|
# verifiers for different classes. This is also helpful if you need to rotate keys, as you can prepare
|
96
|
-
# your custom verifier for that in advance. See
|
97
|
+
# your custom verifier for that in advance. See ActiveSupport::MessageVerifier for details.
|
97
98
|
def signed_id_verifier=(verifier)
|
98
99
|
@signed_id_verifier = verifier
|
99
100
|
end
|
@@ -74,13 +74,13 @@ module ActiveRecord
|
|
74
74
|
self
|
75
75
|
end
|
76
76
|
|
77
|
-
def add_bind(obj)
|
77
|
+
def add_bind(obj, &)
|
78
78
|
@binds << obj
|
79
79
|
@parts << Substitute.new
|
80
80
|
self
|
81
81
|
end
|
82
82
|
|
83
|
-
def add_binds(binds, proc_for_binds = nil)
|
83
|
+
def add_binds(binds, proc_for_binds = nil, &)
|
84
84
|
@binds.concat proc_for_binds ? binds.map(&proc_for_binds) : binds
|
85
85
|
binds.size.times do |i|
|
86
86
|
@parts << ", " unless i == 0
|
@@ -219,12 +219,11 @@ module ActiveRecord
|
|
219
219
|
# database error will occur because the savepoint has already been
|
220
220
|
# automatically released. The following example demonstrates the problem:
|
221
221
|
#
|
222
|
-
# Model.
|
223
|
-
# Model.
|
224
|
-
# Model.lease_connection.create_table(...)
|
225
|
-
# end
|
226
|
-
#
|
227
|
-
# end
|
222
|
+
# Model.transaction do # BEGIN
|
223
|
+
# Model.transaction(requires_new: true) do # CREATE SAVEPOINT active_record_1
|
224
|
+
# Model.lease_connection.create_table(...) # active_record_1 now automatically released
|
225
|
+
# end # RELEASE SAVEPOINT active_record_1
|
226
|
+
# end # ^^^^ BOOM! database error!
|
228
227
|
#
|
229
228
|
# Note that "TRUNCATE" is also a MySQL DDL statement!
|
230
229
|
module ClassMethods
|
data/lib/active_record.rb
CHANGED
@@ -548,8 +548,10 @@ module ActiveRecord
|
|
548
548
|
open_transactions = []
|
549
549
|
Base.connection_handler.each_connection_pool do |pool|
|
550
550
|
if active_connection = pool.active_connection
|
551
|
-
|
552
|
-
|
551
|
+
current_transaction = active_connection.current_transaction
|
552
|
+
|
553
|
+
if current_transaction.open? && current_transaction.joinable? && !current_transaction.state.invalidated?
|
554
|
+
open_transactions << current_transaction
|
553
555
|
end
|
554
556
|
end
|
555
557
|
end
|
data/lib/arel/collectors/bind.rb
CHANGED