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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +208 -0
  3. data/lib/active_record/associations/alias_tracker.rb +6 -4
  4. data/lib/active_record/associations/belongs_to_association.rb +7 -1
  5. data/lib/active_record/associations/join_dependency/join_association.rb +25 -27
  6. data/lib/active_record/attribute_methods/primary_key.rb +2 -1
  7. data/lib/active_record/attribute_methods.rb +22 -17
  8. data/lib/active_record/attributes.rb +2 -2
  9. data/lib/active_record/autosave_association.rb +21 -11
  10. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +17 -14
  11. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +47 -34
  12. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +5 -1
  13. data/lib/active_record/connection_adapters/abstract_adapter.rb +39 -22
  14. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +14 -7
  15. data/lib/active_record/connection_adapters/mysql/quoting.rb +7 -1
  16. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +7 -0
  17. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +57 -11
  18. data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -1
  19. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  20. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +14 -12
  21. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +1 -1
  22. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +1 -1
  23. data/lib/active_record/connection_adapters/postgresql_adapter.rb +7 -2
  24. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +5 -1
  25. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +13 -0
  26. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +2 -0
  27. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +7 -2
  28. data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
  29. data/lib/active_record/connection_adapters/trilogy_adapter.rb +1 -1
  30. data/lib/active_record/core.rb +31 -2
  31. data/lib/active_record/counter_cache.rb +1 -1
  32. data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -1
  33. data/lib/active_record/delegated_type.rb +17 -17
  34. data/lib/active_record/future_result.rb +3 -3
  35. data/lib/active_record/gem_version.rb +2 -2
  36. data/lib/active_record/migration/command_recorder.rb +5 -2
  37. data/lib/active_record/railties/databases.rake +1 -1
  38. data/lib/active_record/relation/calculations.rb +23 -17
  39. data/lib/active_record/relation/predicate_builder/association_query_value.rb +2 -0
  40. data/lib/active_record/relation/query_attribute.rb +1 -1
  41. data/lib/active_record/relation/query_methods.rb +1 -1
  42. data/lib/active_record/relation.rb +1 -1
  43. data/lib/active_record/schema_dumper.rb +29 -11
  44. data/lib/active_record/signed_id.rb +4 -3
  45. data/lib/active_record/statement_cache.rb +2 -2
  46. data/lib/active_record/transactions.rb +5 -6
  47. data/lib/active_record.rb +4 -2
  48. data/lib/arel/collectors/bind.rb +1 -1
  49. data/lib/arel/collectors/sql_string.rb +1 -1
  50. data/lib/arel/collectors/substitute_binds.rb +2 -2
  51. data/lib/arel/nodes/binary.rb +1 -1
  52. data/lib/arel/nodes/node.rb +1 -1
  53. data/lib/arel/nodes/sql_literal.rb +1 -1
  54. data/lib/arel/visitors/to_sql.rb +1 -1
  55. 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.split(",").inspect}"
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
- string_agg(enum.enumlabel, ',' ORDER BY enum.enumsortorder) AS value
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
@@ -137,7 +137,11 @@ module ActiveRecord
137
137
  end
138
138
 
139
139
  def default_insert_value(column)
140
- column.default
140
+ if column.default_function
141
+ Arel.sql(column.default_function)
142
+ else
143
+ column.default
144
+ end
141
145
  end
142
146
  end
143
147
  end
@@ -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
- alias_method :active?, :connected?
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+\("(\w+)"\)\s+REFERENCES\s+"(\w+)"\s+\("(\w+)"\)/
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.
@@ -50,8 +50,10 @@ module ActiveRecord
50
50
  end
51
51
 
52
52
  def delete(key)
53
- dealloc cache[key]
54
- cache.delete(key)
53
+ if stmt = cache.delete(key)
54
+ dealloc(stmt)
55
+ end
56
+ stmt
55
57
  end
56
58
 
57
59
  private
@@ -121,7 +121,7 @@ module ActiveRecord
121
121
  end
122
122
 
123
123
  def active?
124
- connected? && @lock.synchronize { @raw_connection&.ping } || false
124
+ connected? && @lock.synchronize { @raw_connection&.ping; verified! } || false
125
125
  rescue ::Trilogy::Error
126
126
  false
127
127
  end
@@ -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
- # Marks this record as read only.
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.save # Raises an ActiveRecord::ReadOnlyRecord
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 +updated_at+ and/or +updated_on+
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 = ActiveRecord.protocol_adapters[adapter] || 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
- # 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
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
- # [:foreign_key]
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
- # [:foreign_type]
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
- # [:primary_key]
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
- # 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
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
- connection.with_instrumenter(@event_buffer) do
112
- execute_query(connection, async: true)
113
- end
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
@@ -9,8 +9,8 @@ module ActiveRecord
9
9
  module VERSION
10
10
  MAJOR = 8
11
11
  MINOR = 0
12
- TINY = 0
13
- PRE = "1"
12
+ TINY = 2
13
+ PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -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
- super(args.push(options), &block)
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
- [:rename_enum_value, [type_name, from: options[:to], to: options[:from]]]
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
- # desc 'Resets your database using your migrations for the current environment'
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
- ActiveRecord::Result.empty
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
- group_fields = arel_columns(group_fields)
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)
@@ -59,6 +59,8 @@ module ActiveRecord
59
59
  def convert_to_id(value)
60
60
  if primary_key.is_a?(Array)
61
61
  primary_key.map do |attribute|
62
+ next nil if value.nil?
63
+
62
64
  if attribute == "id"
63
65
  value.id_value
64
66
  else
@@ -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
- # inserted records, which by default is the primary key.
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
- add_check_constraint_statements = check_constraints.map do |check_constraint|
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
- if check_constraint.export_name_on_schema_dump?
286
- parts << "name: #{check_constraint.name.inspect}"
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
- parts << "validate: #{check_constraint.validate?.inspect}" unless check_constraint.validate?
290
-
291
- " #{parts.join(', ')}"
292
+ stream.puts check_constraint_statements.sort.join("\n")
292
293
  end
293
294
 
294
- stream.puts add_check_constraint_statements.sort.join("\n")
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 \Rails comes from the
80
- # Rails.application.key_generator. By default, it's SHA256 for the digest and JSON for the serialization.
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 +ActiveSupport::MessageVerifier+ for details.
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.lease_connection.transaction do # BEGIN
223
- # Model.lease_connection.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
- # # ^^^^ BOOM! database error!
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
- if active_connection.current_transaction.open? && active_connection.current_transaction.joinable?
552
- open_transactions << active_connection.current_transaction
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
@@ -18,7 +18,7 @@ module Arel # :nodoc: all
18
18
  self
19
19
  end
20
20
 
21
- def add_binds(binds, proc_for_binds = nil)
21
+ def add_binds(binds, proc_for_binds = nil, &)
22
22
  @binds.concat proc_for_binds ? binds.map(&proc_for_binds) : binds
23
23
  self
24
24
  end
@@ -12,7 +12,7 @@ module Arel # :nodoc: all
12
12
  @bind_index = 1
13
13
  end
14
14
 
15
- def add_bind(bind)
15
+ def add_bind(bind, &)
16
16
  self << yield(@bind_index)
17
17
  @bind_index += 1
18
18
  self