Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package rubygem-activerecord-8.0 for
openSUSE:Factory checked in at 2025-08-22 17:49:22
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/rubygem-activerecord-8.0 (Old)
and /work/SRC/openSUSE:Factory/.rubygem-activerecord-8.0.new.29662 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rubygem-activerecord-8.0"
Fri Aug 22 17:49:22 2025 rev:5 rq:1300933 version:8.0.2.1
Changes:
--------
---
/work/SRC/openSUSE:Factory/rubygem-activerecord-8.0/rubygem-activerecord-8.0.changes
2025-08-21 17:00:34.765106206 +0200
+++
/work/SRC/openSUSE:Factory/.rubygem-activerecord-8.0.new.29662/rubygem-activerecord-8.0.changes
2025-08-22 17:50:56.627798810 +0200
@@ -1,0 +2,6 @@
+Thu Aug 21 10:20:54 UTC 2025 - Marcus Rueckert <[email protected]>
+
+- Drop CVE-2025-55193.patch:
+ it is already handled in the version update
+
+-------------------------------------------------------------------
@@ -5,0 +12,6 @@
+
+-------------------------------------------------------------------
+Thu Aug 14 00:25:11 UTC 2025 - Marcus Rueckert <[email protected]>
+
+- Update to version 8.0.2.1:
+
https://rubyonrails.org/2025/8/13/Rails-Versions-8-0-2-1-7-2-2-2-and-7-1-5-2-have-been-released
Old:
----
CVE-2025-55193.patch
activerecord-8.0.1.gem
New:
----
activerecord-8.0.2.1.gem
----------(Old B)----------
Old:
- Drop CVE-2025-55193.patch:
it is already handled in the version update
----------(Old E)----------
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ rubygem-activerecord-8.0.spec ++++++
--- /var/tmp/diff_new_pack.gMLZfF/_old 2025-08-22 17:50:57.363829481 +0200
+++ /var/tmp/diff_new_pack.gMLZfF/_new 2025-08-22 17:50:57.367829648 +0200
@@ -1,7 +1,7 @@
#
# spec file for package rubygem-activerecord-8.0
#
-# Copyright (c) 2025 SUSE LLC
+# Copyright (c) 2025 SUSE LLC and contributors
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -24,7 +24,7 @@
#
Name: rubygem-activerecord-8.0
-Version: 8.0.1
+Version: 8.0.2.1
Release: 0
%define mod_name activerecord
%define mod_full_name %{mod_name}-%{version}
@@ -36,7 +36,6 @@
URL: https://rubyonrails.org
Source: https://rubygems.org/gems/%{mod_full_name}.gem
Source1: gem2rpm.yml
-Patch0: CVE-2025-55193.patch
Summary: Object-relational mapper framework (part of Rails)
License: MIT
@@ -46,10 +45,6 @@
aggregations, migrations, and testing come baked-in.
%prep
-%gem_unpack
-%patch -P 0 -p1
-find -type f -print0 | xargs -0 touch -r %{S:0}
-%gem_build
%build
++++++ activerecord-8.0.1.gem -> activerecord-8.0.2.1.gem ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/CHANGELOG.md new/CHANGELOG.md
--- old/CHANGELOG.md 2024-12-13 21:02:34.000000000 +0100
+++ new/CHANGELOG.md 1980-01-02 01:00:00.000000000 +0100
@@ -1,6 +1,151 @@
+## Rails 8.0.2.1 (August 13, 2025) ##
+
+* Call inspect on ids in RecordNotFound error
+
+ [CVE-2025-55193]
+
+ *Gannon McGibbon*, *John Hawthorn*
+
+## Rails 8.0.2 (March 12, 2025) ##
+
+* No changes.
+
+
+## Rails 8.0.2 (March 12, 2025) ##
+
+* Fix inverting `rename_enum_value` when `:from`/`:to` are provided.
+
+ *fatkodima*
+
+* Prevent persisting invalid record.
+
+ *Edouard Chin*
+
+* Fix inverting `drop_table` without options.
+
+ *fatkodima*
+
+* Fix count with group by qualified name on loaded relation.
+
+ *Ryuta Kamizono*
+
+* Fix `sum` with qualified name on loaded relation.
+
+ *Chris Gunther*
+
+* The SQLite3 adapter quotes non-finite Numeric values like "Infinity" and
"NaN".
+
+ *Mike Dalessio*
+
+* Handle libpq returning a database version of 0 on no/bad connection in
`PostgreSQLAdapter`.
+
+ Before, this version would be cached and an error would be raised during
connection configuration when
+ comparing it with the minimum required version for the adapter. This meant
that the connection could
+ never be successfully configured on subsequent reconnection attempts.
+
+ Now, this is treated as a connection failure consistent with libpq,
raising a `ActiveRecord::ConnectionFailed`
+ and ensuring the version isn't cached, which allows the version to be
retrieved on the next connection attempt.
+
+ *Joshua Young*, *Rian McGuire*
+
+* Fix error handling during connection configuration.
+
+ Active Record wasn't properly handling errors during the connection
configuration phase.
+ This could lead to a partially configured connection being used, resulting
in various exceptions,
+ the most common being with the PostgreSQLAdapter raising `undefined method
`key?' for nil`
+ or `TypeError: wrong argument type nil (expected PG::TypeMap)`.
+
+ *Jean Boussier*
+
+* Fix a case where a non-retryable query could be marked retryable.
+
+ *Hartley McGuire*
+
+* Handle circular references when autosaving associations.
+
+ *zzak*
+
+* PoolConfig no longer keeps a reference to the connection class.
+
+ Keeping a reference to the class caused subtle issues when combined with
reloading in
+ development. Fixes #54343.
+
+ *Mike Dalessio*
+
+* Fix SQL notifications sometimes not sent when using async queries.
+
+ ```ruby
+ Post.async_count
+ ActiveSupport::Notifications.subscribed(->(*) { "Will never reach here" })
do
+ Post.count
+ end
+ ```
+
+ In rare circumstances and under the right race condition, Active Support
notifications
+ would no longer be dispatched after using an asynchronous query.
+ This is now fixed.
+
+ *Edouard Chin*
+
+* Fix support for PostgreSQL enum types with commas in their name.
+
+ *Arthur Hess*
+
+* Fix inserts on MySQL with no RETURNING support for a table with multiple
auto populated columns.
+
+ *Nikita Vasilevsky*
+
+* Fix joining on a scoped association with string joins and bind parameters.
+
+ ```ruby
+ class Instructor < ActiveRecord::Base
+ has_many :instructor_roles, -> { active }
+ end
+
+ class InstructorRole < ActiveRecord::Base
+ scope :active, -> {
+ joins("JOIN students ON instructor_roles.student_id = students.id")
+ .where(students { status: 1 })
+ }
+ end
+
+ Instructor.joins(:instructor_roles).first
+ ```
+
+ The above example would result in `ActiveRecord::StatementInvalid` because
the
+ `active` scope bind parameters would be lost.
+
+ *Jean Boussier*
+
+* Fix a potential race condition with system tests and transactional
fixtures.
+
+ *Sjoerd Lagarde*
+
+* Fix autosave associations to no longer validated unmodified associated
records.
+
+ Active Record was incorrectly performing validation on associated record
that
+ weren't created nor modified as part of the transaction:
+
+ ```ruby
+ Post.create!(author: User.find(1)) # Fail if user is invalid
+ ```
+
+ *Jean Boussier*
+
+* Remember when a database connection has recently been verified (for
+ two seconds, by default), to avoid repeated reverifications during a
+ single request.
+
+ This should recreate a similar rate of verification as in Rails 7.1,
+ where connections are leased for the duration of a request, and thus
+ only verified once.
+
+ *Matthew Draper*
+
+
## Rails 8.0.1 (December 13, 2024) ##
-* Fix removing foreign keys with :restrict action for MySQ
+* Fix removing foreign keys with :restrict action for MySQL.
*fatkodima*
Binary files old/checksums.yaml.gz and new/checksums.yaml.gz differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/active_record/associations/alias_tracker.rb
new/lib/active_record/associations/alias_tracker.rb
--- old/lib/active_record/associations/alias_tracker.rb 2024-12-13
21:02:34.000000000 +0100
+++ new/lib/active_record/associations/alias_tracker.rb 1980-01-02
01:00:00.000000000 +0100
@@ -26,16 +26,18 @@
end
def self.initial_count_for(connection, name, table_joins)
- quoted_name = nil
+ quoted_name_escaped = nil
+ name_escaped = nil
counts = table_joins.map do |join|
if join.is_a?(Arel::Nodes::StringJoin)
- # quoted_name should be case ignored as some database adapters
(Oracle) return quoted name in uppercase
- quoted_name ||= connection.quote_table_name(name)
+ # quoted_name_escaped should be case ignored as some database
adapters (Oracle) return quoted name in uppercase
+ quoted_name_escaped ||=
Regexp.escape(connection.quote_table_name(name))
+ name_escaped ||= Regexp.escape(name)
# Table names + table aliases
join.left.scan(
- /JOIN(?:\s+\w+)?\s+(?:\S+\s+)?(?:#{quoted_name}|#{name})\sON/i
+
/JOIN(?:\s+\w+)?\s+(?:\S+\s+)?(?:#{quoted_name_escaped}|#{name_escaped})\sON/i
).size
elsif join.is_a?(Arel::Nodes::Join)
join.left.name == name ? 1 : 0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/lib/active_record/associations/join_dependency/join_association.rb
new/lib/active_record/associations/join_dependency/join_association.rb
--- old/lib/active_record/associations/join_dependency/join_association.rb
2024-12-13 21:02:34.000000000 +0100
+++ new/lib/active_record/associations/join_dependency/join_association.rb
1980-01-02 01:00:00.000000000 +0100
@@ -38,41 +38,39 @@
chain << [reflection, table]
end
- base_klass.with_connection do |connection|
- # The chain starts with the target table, but we want to end with
it here (makes
- # more sense in this context), so we reverse
- chain.reverse_each do |reflection, table|
- klass = reflection.klass
-
- scope = reflection.join_scope(table, foreign_table,
foreign_klass)
-
- unless scope.references_values.empty?
- associations = scope.eager_load_values | scope.includes_values
-
- unless associations.empty?
- scope.joins! scope.construct_join_dependency(associations,
Arel::Nodes::OuterJoin)
- end
- end
+ # The chain starts with the target table, but we want to end with it
here (makes
+ # more sense in this context), so we reverse
+ chain.reverse_each do |reflection, table|
+ klass = reflection.klass
+
+ scope = reflection.join_scope(table, foreign_table, foreign_klass)
- arel = scope.arel(alias_tracker.aliases)
- nodes = arel.constraints.first
+ unless scope.references_values.empty?
+ associations = scope.eager_load_values | scope.includes_values
- if nodes.is_a?(Arel::Nodes::And)
- others = nodes.children.extract! do |node|
- !Arel.fetch_attribute(node) { |attr| attr.relation.name ==
table.name }
- end
+ unless associations.empty?
+ scope.joins! scope.construct_join_dependency(associations,
Arel::Nodes::OuterJoin)
end
+ end
- joins << join_type.new(table, Arel::Nodes::On.new(nodes))
+ arel = scope.arel(alias_tracker.aliases)
+ nodes = arel.constraints.first
- if others && !others.empty?
- joins.concat arel.join_sources
- append_constraints(connection, joins.last, others)
+ if nodes.is_a?(Arel::Nodes::And)
+ others = nodes.children.extract! do |node|
+ !Arel.fetch_attribute(node) { |attr| attr.relation.name ==
table.name }
end
+ end
+
+ joins << join_type.new(table, Arel::Nodes::On.new(nodes))
- # The current table in this iteration becomes the foreign table
in the next
- foreign_table, foreign_klass = table, klass
+ if others && !others.empty?
+ joins.concat arel.join_sources
+ append_constraints(joins.last, others)
end
+
+ # The current table in this iteration becomes the foreign table in
the next
+ foreign_table, foreign_klass = table, klass
end
joins
@@ -91,10 +89,10 @@
end
private
- def append_constraints(connection, join, constraints)
+ def append_constraints(join, constraints)
if join.is_a?(Arel::Nodes::StringJoin)
join_string = Arel::Nodes::And.new(constraints.unshift join.left)
- join.left = Arel.sql(connection.visitor.compile(join_string))
+ join.left = join_string
else
right = join.right
right.expr = Arel::Nodes::And.new(constraints.unshift right.expr)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/active_record/attributes.rb
new/lib/active_record/attributes.rb
--- old/lib/active_record/attributes.rb 2024-12-13 21:02:34.000000000 +0100
+++ new/lib/active_record/attributes.rb 1980-01-02 01:00:00.000000000 +0100
@@ -178,8 +178,8 @@
# @currency_converter = currency_converter
# end
#
- # # value will be the result of +deserialize+ or
- # # +cast+. Assumed to be an instance of +Money+ in
+ # # value will be the result of #deserialize or
+ # # #cast. Assumed to be an instance of Money in
# # this case.
# def serialize(value)
# value_in_bitcoins =
@currency_converter.convert_to_bitcoins(value)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/active_record/autosave_association.rb
new/lib/active_record/autosave_association.rb
--- old/lib/active_record/autosave_association.rb 2024-12-13
21:02:34.000000000 +0100
+++ new/lib/active_record/autosave_association.rb 1980-01-02
01:00:00.000000000 +0100
@@ -372,19 +372,29 @@
return true if record.destroyed? || (association.options[:autosave] &&
record.marked_for_destruction?)
context = validation_context if custom_validation_context?
+ return true if record.valid?(context)
- unless valid = record.valid?(context)
- if association.options[:autosave]
- record.errors.each { |error|
- self.errors.objects.append(
- Associations::NestedError.new(association, error)
- )
- }
- else
- errors.add(association.reflection.name)
- end
+ if record.changed? || record.new_record? || context
+ associated_errors = record.errors.objects
+ else
+ # If there are existing invalid records in the DB, we should still
be able to reference them.
+ # Unless a record (no matter where in the association chain) is
invalid and is being changed.
+ associated_errors = record.errors.objects.select { |error|
error.is_a?(Associations::NestedError) }
end
- valid
+
+ if association.options[:autosave]
+ return if equal?(record)
+
+ associated_errors.each { |error|
+ errors.objects.append(
+ Associations::NestedError.new(association, error)
+ )
+ }
+ elsif associated_errors.any?
+ errors.add(association.reflection.name)
+ end
+
+ errors.any?
end
# Is used as an around_save callback to check while saving a collection
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/lib/active_record/connection_adapters/abstract/connection_handler.rb
new/lib/active_record/connection_adapters/abstract/connection_handler.rb
--- old/lib/active_record/connection_adapters/abstract/connection_handler.rb
2024-12-13 21:02:34.000000000 +0100
+++ new/lib/active_record/connection_adapters/abstract/connection_handler.rb
1980-01-02 01:00:00.000000000 +0100
@@ -54,19 +54,22 @@
# about the model. The model needs to pass a connection specification name
to the handler,
# in order to look up the correct connection pool.
class ConnectionHandler
- class StringConnectionName # :nodoc:
- attr_reader :name
-
- def initialize(name)
+ class ConnectionDescriptor # :nodoc:
+ def initialize(name, primary = false)
@name = name
+ @primary = primary
+ end
+
+ def name
+ primary_class? ? "ActiveRecord::Base" : @name
end
def primary_class?
- false
+ @primary
end
def current_preventing_writes
- false
+ ActiveRecord::Base.preventing_writes?(@name)
end
end
@@ -115,7 +118,7 @@
pool_config = resolve_pool_config(config, owner_name, role, shard)
db_config = pool_config.db_config
- pool_manager = set_pool_manager(pool_config.connection_name)
+ pool_manager = set_pool_manager(pool_config.connection_descriptor)
# If there is an existing pool with the same values as the pool_config
# don't remove the connection. Connections should only be removed if
we are
@@ -127,8 +130,8 @@
# Update the pool_config's connection class if it differs. This is
used
# for ensuring that ActiveRecord::Base and the
primary_abstract_class use
# the same pool. Without this granular swapping will not work
correctly.
- if owner_name.primary_class? &&
(existing_pool_config.connection_class != owner_name)
- existing_pool_config.connection_class = owner_name
+ if owner_name.primary_class? &&
(existing_pool_config.connection_descriptor != owner_name)
+ existing_pool_config.connection_descriptor = owner_name
end
existing_pool_config.pool
@@ -137,7 +140,7 @@
pool_manager.set_pool_config(role, shard, pool_config)
payload = {
- connection_name: pool_config.connection_name,
+ connection_name: pool_config.connection_descriptor.name,
role: role,
shard: shard,
config: db_config.configuration_hash
@@ -242,8 +245,8 @@
end
# Get the existing pool manager or initialize and assign a new one.
- def set_pool_manager(connection_name)
- connection_name_to_pool_manager[connection_name] ||= PoolManager.new
+ def set_pool_manager(connection_descriptor)
+ connection_name_to_pool_manager[connection_descriptor.name] ||=
PoolManager.new
end
def pool_managers
@@ -278,9 +281,9 @@
def determine_owner_name(owner_name, config)
if owner_name.is_a?(String) || owner_name.is_a?(Symbol)
- StringConnectionName.new(owner_name.to_s)
+ ConnectionDescriptor.new(owner_name.to_s)
elsif config.is_a?(Symbol)
- StringConnectionName.new(config.to_s)
+ ConnectionDescriptor.new(config.to_s)
else
owner_name
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/lib/active_record/connection_adapters/abstract/connection_pool.rb
new/lib/active_record/connection_adapters/abstract/connection_pool.rb
--- old/lib/active_record/connection_adapters/abstract/connection_pool.rb
2024-12-13 21:02:34.000000000 +0100
+++ new/lib/active_record/connection_adapters/abstract/connection_pool.rb
1980-01-02 01:00:00.000000000 +0100
@@ -36,7 +36,7 @@
end
def schema_cache; end
- def connection_class; end
+ def connection_descriptor; end
def checkin(_); end
def remove(_); end
def async_executor; end
@@ -364,8 +364,8 @@
clean
end
- def connection_class # :nodoc:
- pool_config.connection_class
+ def connection_descriptor # :nodoc:
+ pool_config.connection_descriptor
end
# Returns true if there is an open connection being used for the current
thread.
@@ -545,20 +545,25 @@
# Raises:
# - ActiveRecord::ConnectionTimeoutError no connection can be obtained
from the pool.
def checkout(checkout_timeout = @checkout_timeout)
- if @pinned_connection
- @pinned_connection.lock.synchronize do
- synchronize do
+ return checkout_and_verify(acquire_connection(checkout_timeout))
unless @pinned_connection
+
+ @pinned_connection.lock.synchronize do
+ synchronize do
+ # The pinned connection may have been cleaned up before we
synchronized, so check if it is still present
+ if @pinned_connection
@pinned_connection.verify!
+
# Any leased connection must be in @connections otherwise
# some methods like #connected? won't behave correctly
unless @connections.include?(@pinned_connection)
@connections << @pinned_connection
end
+
+ @pinned_connection
+ else
+ checkout_and_verify(acquire_connection(checkout_timeout))
end
end
- @pinned_connection
- else
- checkout_and_verify(acquire_connection(checkout_timeout))
end
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/lib/active_record/connection_adapters/abstract/schema_definitions.rb
new/lib/active_record/connection_adapters/abstract/schema_definitions.rb
--- old/lib/active_record/connection_adapters/abstract/schema_definitions.rb
2024-12-13 21:02:34.000000000 +0100
+++ new/lib/active_record/connection_adapters/abstract/schema_definitions.rb
1980-01-02 01:00:00.000000000 +0100
@@ -434,7 +434,7 @@
#
# == Examples
#
- # # Assuming +td+ is an instance of TableDefinition
+ # # Assuming `td` is an instance of TableDefinition
# td.column(:granted, :boolean, index: true)
#
# == Short-hand examples
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/lib/active_record/connection_adapters/abstract_adapter.rb
new/lib/active_record/connection_adapters/abstract_adapter.rb
--- old/lib/active_record/connection_adapters/abstract_adapter.rb
2024-12-13 21:02:34.000000000 +0100
+++ new/lib/active_record/connection_adapters/abstract_adapter.rb
1980-01-02 01:00:00.000000000 +0100
@@ -150,7 +150,6 @@
end
@owner = nil
- @instrumenter = ActiveSupport::Notifications.instrumenter
@pool = ActiveRecord::ConnectionAdapters::NullPool.new
@idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
@visitor = arel_visitor
@@ -168,6 +167,7 @@
@default_timezone =
self.class.validate_default_timezone(@config[:default_timezone])
@raw_connection_dirty = false
+ @last_activity = nil
@verified = false
end
@@ -190,19 +190,6 @@
end
end
- EXCEPTION_NEVER = { Exception => :never }.freeze # :nodoc:
- EXCEPTION_IMMEDIATE = { Exception => :immediate }.freeze # :nodoc:
- private_constant :EXCEPTION_NEVER, :EXCEPTION_IMMEDIATE
- def with_instrumenter(instrumenter, &block) # :nodoc:
- Thread.handle_interrupt(EXCEPTION_NEVER) do
- previous_instrumenter = @instrumenter
- @instrumenter = instrumenter
- Thread.handle_interrupt(EXCEPTION_IMMEDIATE, &block)
- ensure
- @instrumenter = previous_instrumenter
- end
- end
-
def check_if_write_query(sql) # :nodoc:
if preventing_writes? && write_query?(sql)
raise ActiveRecord::ReadOnlyError, "Write query attempted while in
readonly mode: #{sql}"
@@ -217,6 +204,10 @@
(@config[:connection_retries] || 1).to_i
end
+ def verify_timeout
+ (@config[:verify_timeout] || 2).to_i
+ end
+
def retry_deadline
if @config[:retry_deadline]
@config[:retry_deadline].to_f
@@ -235,9 +226,9 @@
# the value of +current_preventing_writes+.
def preventing_writes?
return true if replica?
- return false if connection_class.nil?
+ return false if connection_descriptor.nil?
- connection_class.current_preventing_writes
+ connection_descriptor.current_preventing_writes
end
def prepared_statements?
@@ -288,8 +279,8 @@
@owner = ActiveSupport::IsolatedExecutionState.context
end
- def connection_class # :nodoc:
- @pool.connection_class
+ def connection_descriptor # :nodoc:
+ @pool.connection_descriptor
end
# The role (e.g. +:writing+) for the current connection. In a
@@ -343,6 +334,13 @@
Process.clock_gettime(Process::CLOCK_MONOTONIC) - @idle_since
end
+ # Seconds since this connection last communicated with the server
+ def seconds_since_last_activity # :nodoc:
+ if @raw_connection && @last_activity
+ Process.clock_gettime(Process::CLOCK_MONOTONIC) - @last_activity
+ end
+ end
+
def unprepared_statement
cache = prepared_statements_disabled_cache.add?(object_id) if
@prepared_statements
yield
@@ -670,11 +668,12 @@
enable_lazy_transactions!
@raw_connection_dirty = false
+ @last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
@verified = true
reset_transaction(restore: restore_transactions) do
clear_cache!(new_connection: true)
- configure_connection
+ attempt_configure_connection
end
rescue => original_exception
translated_exception = translate_exception_class(original_exception,
nil, nil)
@@ -689,6 +688,7 @@
end
end
+ @last_activity = nil
@verified = false
raise translated_exception
@@ -726,7 +726,7 @@
def reset!
clear_cache!(new_connection: true)
reset_transaction
- configure_connection
+ attempt_configure_connection
end
# Removes the connection from the pool and disconnect it.
@@ -762,7 +762,8 @@
if @unconfigured_connection
@raw_connection = @unconfigured_connection
@unconfigured_connection = nil
- configure_connection
+ attempt_configure_connection
+ @last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
@verified = true
return
end
@@ -992,6 +993,9 @@
if @verified
# Cool, we're confident the connection's ready to use. (Note
this might have
# become true during the above #materialize_transactions.)
+ elsif (last_activity = seconds_since_last_activity) &&
last_activity < verify_timeout
+ # We haven't actually verified the connection since we acquired
it, but it
+ # has been used very recently. We're going to assume it's still
okay.
elsif reconnectable
if allow_retry
# Not sure about the connection yet, but if anything goes
wrong we can
@@ -1033,6 +1037,7 @@
# Barring a known-retryable error inside the query (regardless
of
# whether we were in a _position_ to retry it), we should
infer that
# there's likely a real problem with the connection.
+ @last_activity = nil
@verified = false
end
@@ -1047,6 +1052,7 @@
# `with_raw_connection` block only when the block is guaranteed to
# exercise the raw connection.
def verified!
+ @last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
@verified = true
end
@@ -1126,7 +1132,7 @@
end
def log(sql, name = "SQL", binds = [], type_casted_binds = [], async:
false, &block) # :doc:
- @instrumenter.instrument(
+ instrumenter.instrument(
"sql.active_record",
sql: sql,
name: name,
@@ -1142,6 +1148,10 @@
raise ex.set_query(sql, binds)
end
+ def instrumenter # :nodoc:
+ ActiveSupport::IsolatedExecutionState[:active_record_instrumenter]
||= ActiveSupport::Notifications.instrumenter
+ end
+
def translate_exception(exception, message:, sql:, binds:)
# override in derived class
case exception
@@ -1203,6 +1213,13 @@
check_version
end
+ def attempt_configure_connection
+ configure_connection
+ rescue
+ disconnect!
+ raise
+ end
+
def default_prepared_statements
true
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
new/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
--- old/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
2024-12-13 21:02:34.000000000 +0100
+++ new/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
1980-01-02 01:00:00.000000000 +0100
@@ -174,6 +174,10 @@
mariadb? && database_version >= "10.5.0"
end
+ def return_value_after_insert?(column) # :nodoc:
+ supports_insert_returning? ? column.auto_populated? :
column.auto_increment?
+ end
+
def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})")
== 1
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/lib/active_record/connection_adapters/mysql2_adapter.rb
new/lib/active_record/connection_adapters/mysql2_adapter.rb
--- old/lib/active_record/connection_adapters/mysql2_adapter.rb 2024-12-13
21:02:34.000000000 +0100
+++ new/lib/active_record/connection_adapters/mysql2_adapter.rb 1980-01-02
01:00:00.000000000 +0100
@@ -106,7 +106,14 @@
end
def active?
- connected? && @lock.synchronize { @raw_connection&.ping } || false
+ if connected?
+ @lock.synchronize do
+ if @raw_connection&.ping
+ verified!
+ true
+ end
+ end
+ end || false
end
alias :reset! :reconnect!
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/active_record/connection_adapters/pool_config.rb
new/lib/active_record/connection_adapters/pool_config.rb
--- old/lib/active_record/connection_adapters/pool_config.rb 2024-12-13
21:02:34.000000000 +0100
+++ new/lib/active_record/connection_adapters/pool_config.rb 1980-01-02
01:00:00.000000000 +0100
@@ -5,9 +5,8 @@
class PoolConfig # :nodoc:
include MonitorMixin
- attr_reader :db_config, :role, :shard
+ attr_reader :db_config, :role, :shard, :connection_descriptor
attr_writer :schema_reflection, :server_version
- attr_accessor :connection_class
def schema_reflection
@schema_reflection ||=
SchemaReflection.new(db_config.lazy_schema_cache_path)
@@ -29,7 +28,7 @@
def initialize(connection_class, db_config, role, shard)
super()
@server_version = nil
- @connection_class = connection_class
+ self.connection_descriptor = connection_class
@db_config = db_config
@role = role
@shard = shard
@@ -41,11 +40,12 @@
@server_version || synchronize { @server_version ||=
connection.get_database_version }
end
- def connection_name
- if connection_class.primary_class?
- "ActiveRecord::Base"
+ def connection_descriptor=(connection_descriptor)
+ case connection_descriptor
+ when ConnectionHandler::ConnectionDescriptor
+ @connection_descriptor = connection_descriptor
else
- connection_class.name
+ @connection_descriptor =
ConnectionHandler::ConnectionDescriptor.new(connection_descriptor.name,
connection_descriptor.primary_class?)
end
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/lib/active_record/connection_adapters/postgresql/schema_definitions.rb
new/lib/active_record/connection_adapters/postgresql/schema_definitions.rb
--- old/lib/active_record/connection_adapters/postgresql/schema_definitions.rb
2024-12-13 21:02:34.000000000 +0100
+++ new/lib/active_record/connection_adapters/postgresql/schema_definitions.rb
1980-01-02 01:00:00.000000000 +0100
@@ -308,8 +308,8 @@
# t.exclusion_constraint("price WITH =, availability_range WITH &&",
using: :gist, name: "price_check")
#
# See
{connection.add_exclusion_constraint}[rdoc-ref:SchemaStatements#add_exclusion_constraint]
- def exclusion_constraint(*args)
- @base.add_exclusion_constraint(name, *args)
+ def exclusion_constraint(...)
+ @base.add_exclusion_constraint(name, ...)
end
# Removes the given exclusion constraint from the table.
@@ -317,8 +317,8 @@
# t.remove_exclusion_constraint(name: "price_check")
#
# See
{connection.remove_exclusion_constraint}[rdoc-ref:SchemaStatements#remove_exclusion_constraint]
- def remove_exclusion_constraint(*args)
- @base.remove_exclusion_constraint(name, *args)
+ def remove_exclusion_constraint(...)
+ @base.remove_exclusion_constraint(name, ...)
end
# Adds a unique constraint.
@@ -326,8 +326,8 @@
# t.unique_constraint(:position, name: 'unique_position', deferrable:
:deferred, nulls_not_distinct: true)
#
# See
{connection.add_unique_constraint}[rdoc-ref:SchemaStatements#add_unique_constraint]
- def unique_constraint(*args)
- @base.add_unique_constraint(name, *args)
+ def unique_constraint(...)
+ @base.add_unique_constraint(name, ...)
end
# Removes the given unique constraint from the table.
@@ -335,8 +335,8 @@
# t.remove_unique_constraint(name: "unique_position")
#
# See
{connection.remove_unique_constraint}[rdoc-ref:SchemaStatements#remove_unique_constraint]
- def remove_unique_constraint(*args)
- @base.remove_unique_constraint(name, *args)
+ def remove_unique_constraint(...)
+ @base.remove_unique_constraint(name, ...)
end
# Validates the given constraint on the table.
@@ -345,8 +345,8 @@
# t.validate_constraint "price_check"
#
# See
{connection.validate_constraint}[rdoc-ref:SchemaStatements#validate_constraint]
- def validate_constraint(*args)
- @base.validate_constraint(name, *args)
+ def validate_constraint(...)
+ @base.validate_constraint(name, ...)
end
# Validates the given check constraint on the table
@@ -355,8 +355,8 @@
# t.validate_check_constraint name: "price_check"
#
# See
{connection.validate_check_constraint}[rdoc-ref:SchemaStatements#validate_check_constraint]
- def validate_check_constraint(*args)
- @base.validate_check_constraint(name, *args)
+ def validate_check_constraint(...)
+ @base.validate_check_constraint(name, ...)
end
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/lib/active_record/connection_adapters/postgresql/schema_dumper.rb
new/lib/active_record/connection_adapters/postgresql/schema_dumper.rb
--- old/lib/active_record/connection_adapters/postgresql/schema_dumper.rb
2024-12-13 21:02:34.000000000 +0100
+++ new/lib/active_record/connection_adapters/postgresql/schema_dumper.rb
1980-01-02 01:00:00.000000000 +0100
@@ -22,7 +22,7 @@
stream.puts " # Custom types defined in this database."
stream.puts " # Note that some types may not work with other
database engines. Be careful if changing database."
types.sort.each do |name, values|
- stream.puts " create_enum #{name.inspect},
#{values.split(",").inspect}"
+ stream.puts " create_enum #{name.inspect}, #{values.inspect}"
end
stream.puts
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/lib/active_record/connection_adapters/postgresql_adapter.rb
new/lib/active_record/connection_adapters/postgresql_adapter.rb
--- old/lib/active_record/connection_adapters/postgresql_adapter.rb
2024-12-13 21:02:34.000000000 +0100
+++ new/lib/active_record/connection_adapters/postgresql_adapter.rb
1980-01-02 01:00:00.000000000 +0100
@@ -349,6 +349,7 @@
@lock.synchronize do
return false unless @raw_connection
@raw_connection.query ";"
+ verified!
end
true
rescue PG::Error
@@ -520,7 +521,7 @@
type.typname AS name,
type.OID AS oid,
n.nspname AS schema,
- string_agg(enum.enumlabel, ',' ORDER BY enum.enumsortorder) AS
value
+ array_agg(enum.enumlabel ORDER BY enum.enumsortorder) AS value
FROM pg_enum AS enum
JOIN pg_type AS type ON (type.oid = enum.enumtypid)
JOIN pg_namespace n ON type.typnamespace = n.oid
@@ -633,7 +634,11 @@
# Returns the version of the connected PostgreSQL server.
def get_database_version # :nodoc:
with_raw_connection do |conn|
- conn.server_version
+ version = conn.server_version
+ if version == 0
+ raise ActiveRecord::ConnectionFailed, "Could not determine
PostgreSQL version"
+ end
+ version
end
end
alias :postgresql_version :database_version
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/lib/active_record/connection_adapters/sqlite3/quoting.rb
new/lib/active_record/connection_adapters/sqlite3/quoting.rb
--- old/lib/active_record/connection_adapters/sqlite3/quoting.rb
2024-12-13 21:02:34.000000000 +0100
+++ new/lib/active_record/connection_adapters/sqlite3/quoting.rb
1980-01-02 01:00:00.000000000 +0100
@@ -50,6 +50,19 @@
end
end
+ def quote(value) # :nodoc:
+ case value
+ when Numeric
+ if value.finite?
+ super
+ else
+ "'#{value}'"
+ end
+ else
+ super
+ end
+ end
+
def quote_string(s)
::SQLite3::Database.quote(s)
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/lib/active_record/connection_adapters/sqlite3_adapter.rb
new/lib/active_record/connection_adapters/sqlite3_adapter.rb
--- old/lib/active_record/connection_adapters/sqlite3_adapter.rb
2024-12-13 21:02:34.000000000 +0100
+++ new/lib/active_record/connection_adapters/sqlite3_adapter.rb
1980-01-02 01:00:00.000000000 +0100
@@ -207,7 +207,12 @@
!(@raw_connection.nil? || @raw_connection.closed?)
end
- alias_method :active?, :connected?
+ def active?
+ if connected?
+ verified!
+ true
+ end
+ end
alias :reset! :reconnect!
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/lib/active_record/connection_adapters/trilogy_adapter.rb
new/lib/active_record/connection_adapters/trilogy_adapter.rb
--- old/lib/active_record/connection_adapters/trilogy_adapter.rb
2024-12-13 21:02:34.000000000 +0100
+++ new/lib/active_record/connection_adapters/trilogy_adapter.rb
1980-01-02 01:00:00.000000000 +0100
@@ -121,7 +121,7 @@
end
def active?
- connected? && @lock.synchronize { @raw_connection&.ping } || false
+ connected? && @lock.synchronize { @raw_connection&.ping; verified! }
|| false
rescue ::Trilogy::Error
false
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/active_record/core.rb
new/lib/active_record/core.rb
--- old/lib/active_record/core.rb 2024-12-13 21:02:34.000000000 +0100
+++ new/lib/active_record/core.rb 1980-01-02 01:00:00.000000000 +0100
@@ -202,6 +202,17 @@
false
end
+ # Intended to behave like `.current_preventing_writes` given the class
name as input.
+ # See PoolConfig and ConnectionHandler::ConnectionDescriptor.
+ def self.preventing_writes?(class_name) # :nodoc:
+ connected_to_stack.reverse_each do |hash|
+ return hash[:prevent_writes] if !hash[:prevent_writes].nil? &&
hash[:klasses].include?(Base)
+ return hash[:prevent_writes] if !hash[:prevent_writes].nil? &&
hash[:klasses].any? { |klass| klass.name == class_name }
+ end
+
+ false
+ end
+
def self.connected_to_stack # :nodoc:
if connected_to_stack =
ActiveSupport::IsolatedExecutionState[:active_record_connected_to_stack]
connected_to_stack
@@ -266,7 +277,7 @@
return super if StatementCache.unsupported_value?(id)
cached_find_by([primary_key], [id]) ||
- raise(RecordNotFound.new("Couldn't find #{name} with
'#{primary_key}'=#{id}", name, primary_key, id))
+ raise(RecordNotFound.new("Couldn't find #{name} with
'#{primary_key}'=#{id.inspect}", name, primary_key, id))
end
def find_by(*args) # :nodoc:
@@ -727,11 +738,29 @@
@strict_loading_mode == :all
end
- # Marks this record as read only.
+ # Prevents records from being written to the database:
+ #
+ # customer = Customer.new
+ # customer.readonly!
+ # customer.save # raises ActiveRecord::ReadOnlyRecord
#
# customer = Customer.first
# customer.readonly!
- # customer.save # Raises an ActiveRecord::ReadOnlyRecord
+ # customer.update(name: 'New Name') # raises ActiveRecord::ReadOnlyRecord
+ #
+ # Read-only records cannot be deleted from the database either:
+ #
+ # customer = Customer.first
+ # customer.readonly!
+ # customer.destroy # raises ActiveRecord::ReadOnlyRecord
+ #
+ # Please, note that the objects themselves are still mutable in memory:
+ #
+ # customer = Customer.new
+ # customer.readonly!
+ # customer.name = 'New Name' # OK
+ #
+ # but you won't be able to persist the changes.
def readonly!
@readonly = true
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/active_record/counter_cache.rb
new/lib/active_record/counter_cache.rb
--- old/lib/active_record/counter_cache.rb 2024-12-13 21:02:34.000000000
+0100
+++ new/lib/active_record/counter_cache.rb 1980-01-02 01:00:00.000000000
+0100
@@ -28,7 +28,7 @@
# # For the Post with id #1, reset the comments_count
# Post.reset_counters(1, :comments)
#
- # # Like above, but also touch the +updated_at+ and/or +updated_on+
+ # # Like above, but also touch the updated_at and/or updated_on
# # attributes.
# Post.reset_counters(1, :comments, touch: true)
def reset_counters(id, *counters, touch: nil)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/active_record/delegated_type.rb
new/lib/active_record/delegated_type.rb
--- old/lib/active_record/delegated_type.rb 2024-12-13 21:02:34.000000000
+0100
+++ new/lib/active_record/delegated_type.rb 1980-01-02 01:00:00.000000000
+0100
@@ -181,16 +181,16 @@
# delegated_type :entryable, types: %w[ Message Comment ], dependent:
:destroy
# end
#
- # Entry#entryable_class # => +Message+ or +Comment+
- # Entry#entryable_name # => "message" or "comment"
- # Entry.messages # => Entry.where(entryable_type: "Message")
- # Entry#message? # => true when entryable_type == "Message"
- # Entry#message # => returns the message record, when
entryable_type == "Message", otherwise nil
- # Entry#message_id # => returns entryable_id, when entryable_type
== "Message", otherwise nil
- # Entry.comments # => Entry.where(entryable_type: "Comment")
- # Entry#comment? # => true when entryable_type == "Comment"
- # Entry#comment # => returns the comment record, when
entryable_type == "Comment", otherwise nil
- # Entry#comment_id # => returns entryable_id, when entryable_type
== "Comment", otherwise nil
+ # @entry.entryable_class # => Message or Comment
+ # @entry.entryable_name # => "message" or "comment"
+ # Entry.messages # => Entry.where(entryable_type: "Message")
+ # @entry.message? # => true when entryable_type == "Message"
+ # @entry.message # => returns the message record, when
entryable_type == "Message", otherwise nil
+ # @entry.message_id # => returns entryable_id, when entryable_type
== "Message", otherwise nil
+ # Entry.comments # => Entry.where(entryable_type: "Comment")
+ # @entry.comment? # => true when entryable_type == "Comment"
+ # @entry.comment # => returns the comment record, when
entryable_type == "Comment", otherwise nil
+ # @entry.comment_id # => returns entryable_id, when entryable_type
== "Comment", otherwise nil
#
# You can also declare namespaced types:
#
@@ -199,25 +199,25 @@
# end
#
# Entry.access_notice_messages
- # entry.access_notice_message
- # entry.access_notice_message?
+ # @entry.access_notice_message
+ # @entry.access_notice_message?
#
# === Options
#
# The +options+ are passed directly to the +belongs_to+ call, so this is
where you declare +dependent+ etc.
# The following options can be included to specialize the behavior of the
delegated type convenience methods.
#
- # [:foreign_key]
+ # [+:foreign_key+]
# Specify the foreign key used for the convenience methods. By default
this is guessed to be the passed
# +role+ with an "_id" suffix. So a class that defines a
# <tt>delegated_type :entryable, types: %w[ Message Comment ]</tt>
association will use "entryable_id" as
# the default <tt>:foreign_key</tt>.
- # [:foreign_type]
+ # [+:foreign_type+]
# Specify the column used to store the associated object's type. By
default this is inferred to be the passed
# +role+ with a "_type" suffix. A class that defines a
# <tt>delegated_type :entryable, types: %w[ Message Comment ]</tt>
association will use "entryable_type" as
# the default <tt>:foreign_type</tt>.
- # [:primary_key]
+ # [+:primary_key+]
# Specify the method that returns the primary key of associated object
used for the convenience methods.
# By default this is +id+.
#
@@ -226,8 +226,8 @@
# delegated_type :entryable, types: %w[ Message Comment ],
primary_key: :uuid, foreign_key: :entryable_uuid
# end
#
- # Entry#message_uuid # => returns entryable_uuid, when
entryable_type == "Message", otherwise nil
- # Entry#comment_uuid # => returns entryable_uuid, when
entryable_type == "Comment", otherwise nil
+ # @entry.message_uuid # => returns entryable_uuid, when entryable_type
== "Message", otherwise nil
+ # @entry.comment_uuid # => returns entryable_uuid, when entryable_type
== "Comment", otherwise nil
def delegated_type(role, types:, **options)
belongs_to role, options.delete(:scope), **options.merge(polymorphic:
true)
define_delegated_type_methods role, types: types, options: options
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/active_record/future_result.rb
new/lib/active_record/future_result.rb
--- old/lib/active_record/future_result.rb 2024-12-13 21:02:34.000000000
+0100
+++ new/lib/active_record/future_result.rb 1980-01-02 01:00:00.000000000
+0100
@@ -108,9 +108,9 @@
begin
if pending?
@event_buffer = EventBuffer.new(self, @instrumenter)
- connection.with_instrumenter(@event_buffer) do
- execute_query(connection, async: true)
- end
+
ActiveSupport::IsolatedExecutionState[:active_record_instrumenter] =
@event_buffer
+
+ execute_query(connection, async: true)
end
ensure
@mutex.unlock
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/active_record/gem_version.rb
new/lib/active_record/gem_version.rb
--- old/lib/active_record/gem_version.rb 2024-12-13 21:02:34.000000000
+0100
+++ new/lib/active_record/gem_version.rb 1980-01-02 01:00:00.000000000
+0100
@@ -9,8 +9,8 @@
module VERSION
MAJOR = 8
MINOR = 0
- TINY = 1
- PRE = nil
+ TINY = 2
+ PRE = "1"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/active_record/migration/command_recorder.rb
new/lib/active_record/migration/command_recorder.rb
--- old/lib/active_record/migration/command_recorder.rb 2024-12-13
21:02:34.000000000 +0100
+++ new/lib/active_record/migration/command_recorder.rb 1980-01-02
01:00:00.000000000 +0100
@@ -213,7 +213,9 @@
raise ActiveRecord::IrreversibleMigration, "To avoid mistakes,
drop_table is only reversible if given options or a block (can be empty)."
end
- super(args.push(options), &block)
+ args << options unless options.empty?
+
+ super(args, &block)
end
def invert_rename_table(args)
@@ -380,7 +382,8 @@
raise ActiveRecord::IrreversibleMigration, "rename_enum_value is
only reversible if given a :from and :to option."
end
- [:rename_enum_value, [type_name, from: options[:to], to:
options[:from]]]
+ options[:to], options[:from] = options[:from], options[:to]
+ [:rename_enum_value, [type_name, options]]
end
def invert_drop_virtual_table(args)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/active_record/railties/databases.rake
new/lib/active_record/railties/databases.rake
--- old/lib/active_record/railties/databases.rake 2024-12-13
21:02:34.000000000 +0100
+++ new/lib/active_record/railties/databases.rake 1980-01-02
01:00:00.000000000 +0100
@@ -160,7 +160,7 @@
end
end
- # desc 'Resets your database using your migrations for the current
environment'
+ desc "Resets your database using your migrations for the current
environment"
task reset: ["db:drop", "db:create", "db:schema:dump", "db:migrate"]
desc 'Run the "up" for a given migration VERSION.'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/active_record/relation/calculations.rb
new/lib/active_record/relation/calculations.rb
--- old/lib/active_record/relation/calculations.rb 2024-12-13
21:02:34.000000000 +0100
+++ new/lib/active_record/relation/calculations.rb 1980-01-02
01:00:00.000000000 +0100
@@ -410,6 +410,18 @@
async.ids
end
+ protected
+ def aggregate_column(column_name)
+ case column_name
+ when Arel::Expressions
+ column_name
+ when :all
+ Arel.star
+ else
+ arel_column(column_name)
+ end
+ end
+
private
def all_attributes?(column_names)
(column_names.map(&:to_s) - model.attribute_names -
model.attribute_aliases.keys).empty?
@@ -450,17 +462,6 @@
column_name.is_a?(::String) && /\bDISTINCT[\s(]/i.match?(column_name)
end
- def aggregate_column(column_name)
- case column_name
- when Arel::Expressions
- column_name
- when :all
- Arel.star
- else
- arel_column(column_name)
- end
- end
-
def operation_over_aggregate_column(column, operation, distinct)
operation == "count" ? column.count(distinct) :
column.public_send(operation)
end
@@ -476,7 +477,7 @@
# PostgreSQL doesn't like ORDER BY when there are no GROUP BY
relation = unscope(:order).distinct!(false)
- column = aggregate_column(column_name)
+ column = relation.aggregate_column(column_name)
select_value = operation_over_aggregate_column(column, operation,
distinct)
select_value.distinct = true if operation == "sum" && distinct
@@ -486,7 +487,11 @@
end
query_result = if relation.where_clause.contradiction?
- ActiveRecord::Result.empty
+ if @async
+ FutureResult.wrap(ActiveRecord::Result.empty)
+ else
+ ActiveRecord::Result.empty
+ end
else
skip_query_cache_if_necessary do
model.with_connection do |c|
@@ -515,7 +520,9 @@
associated = association && association.belongs_to? # only count
belongs_to associations
group_fields = Array(association.foreign_key) if associated
end
- group_fields = arel_columns(group_fields)
+
+ relation = except(:group).distinct!(false)
+ group_fields = relation.arel_columns(group_fields)
model.with_connection do |connection|
column_alias_tracker = ColumnAliasTracker.new(connection)
@@ -526,7 +533,7 @@
}
group_columns = group_aliases.zip(group_fields)
- column = aggregate_column(column_name)
+ column = relation.aggregate_column(column_name)
column_alias = column_alias_tracker.alias_for("#{operation}
#{column_name.to_s.downcase}")
select_value = operation_over_aggregate_column(column, operation,
distinct)
select_value.as(model.adapter_class.quote_column_name(column_alias))
@@ -543,7 +550,6 @@
end
}
- relation = except(:group).distinct!(false)
relation.group_values = group_fields
relation.select_values = select_values
@@ -659,7 +665,7 @@
relation.select_values = [ Arel.sql(FinderMethods::ONE_AS_ONE) ]
unless distinct
else
column_alias = Arel.sql("count_column")
- relation.select_values = [
aggregate_column(column_name).as(column_alias) ]
+ relation.select_values = [
relation.aggregate_column(column_name).as(column_alias) ]
end
subquery_alias = Arel.sql("subquery_for_count", retryable: true)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/active_record/relation/finder_methods.rb
new/lib/active_record/relation/finder_methods.rb
--- old/lib/active_record/relation/finder_methods.rb 2024-12-13
21:02:34.000000000 +0100
+++ new/lib/active_record/relation/finder_methods.rb 1980-01-02
01:00:00.000000000 +0100
@@ -424,12 +424,13 @@
error << " with#{conditions}" if conditions
raise RecordNotFound.new(error, name, key)
elsif Array.wrap(ids).size == 1
- error = "Couldn't find #{name} with '#{key}'=#{ids}#{conditions}"
+ id = Array.wrap(ids)[0]
+ error = "Couldn't find #{name} with
'#{key}'=#{id.inspect}#{conditions}"
raise RecordNotFound.new(error, name, key, ids)
else
error = +"Couldn't find all #{name.pluralize} with '#{key}': "
- error << "(#{ids.join(", ")})#{conditions} (found #{result_size}
results, but was looking for #{expected_size})."
- error << " Couldn't find #{name.pluralize(not_found_ids.size)} with
#{key.to_s.pluralize(not_found_ids.size)} #{not_found_ids.join(', ')}." if
not_found_ids
+ error << "(#{ids.map(&:inspect).join(", ")})#{conditions} (found
#{result_size} results, but was looking for #{expected_size})."
+ error << " Couldn't find #{name.pluralize(not_found_ids.size)} with
#{key.to_s.pluralize(not_found_ids.size)}
#{not_found_ids.map(&:inspect).join(', ')}." if not_found_ids
raise RecordNotFound.new(error, name, key, ids)
end
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/active_record/relation.rb
new/lib/active_record/relation.rb
--- old/lib/active_record/relation.rb 2024-12-13 21:02:34.000000000 +0100
+++ new/lib/active_record/relation.rb 1980-01-02 01:00:00.000000000 +0100
@@ -820,7 +820,7 @@
#
# [:returning]
# (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to
return for all successfully
- # inserted records, which by default is the primary key.
+ # upserted records, which by default is the primary key.
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt>
SQL
# clause entirely.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/active_record/signed_id.rb
new/lib/active_record/signed_id.rb
--- old/lib/active_record/signed_id.rb 2024-12-13 21:02:34.000000000 +0100
+++ new/lib/active_record/signed_id.rb 1980-01-02 01:00:00.000000000 +0100
@@ -76,8 +76,9 @@
end
# The verifier instance that all signed ids are generated and verified
from. By default, it'll be initialized
- # with the class-level +signed_id_verifier_secret+, which within \Rails
comes from the
- # Rails.application.key_generator. By default, it's SHA256 for the
digest and JSON for the serialization.
+ # with the class-level +signed_id_verifier_secret+, which within Rails
comes from
+ #
{Rails.application.key_generator}[rdoc-ref:Rails::Application#key_generator].
+ # By default, it's SHA256 for the digest and JSON for the serialization.
def signed_id_verifier
@signed_id_verifier ||= begin
secret = signed_id_verifier_secret
@@ -93,7 +94,7 @@
# Allows you to pass in a custom verifier used for the signed ids. This
also allows you to use different
# verifiers for different classes. This is also helpful if you need to
rotate keys, as you can prepare
- # your custom verifier for that in advance. See
+ActiveSupport::MessageVerifier+ for details.
+ # your custom verifier for that in advance. See
ActiveSupport::MessageVerifier for details.
def signed_id_verifier=(verifier)
@signed_id_verifier = verifier
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/active_record/statement_cache.rb
new/lib/active_record/statement_cache.rb
--- old/lib/active_record/statement_cache.rb 2024-12-13 21:02:34.000000000
+0100
+++ new/lib/active_record/statement_cache.rb 1980-01-02 01:00:00.000000000
+0100
@@ -74,13 +74,13 @@
self
end
- def add_bind(obj)
+ def add_bind(obj, &)
@binds << obj
@parts << Substitute.new
self
end
- def add_binds(binds, proc_for_binds = nil)
+ def add_binds(binds, proc_for_binds = nil, &)
@binds.concat proc_for_binds ? binds.map(&proc_for_binds) : binds
binds.size.times do |i|
@parts << ", " unless i == 0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/active_record/transactions.rb
new/lib/active_record/transactions.rb
--- old/lib/active_record/transactions.rb 2024-12-13 21:02:34.000000000
+0100
+++ new/lib/active_record/transactions.rb 1980-01-02 01:00:00.000000000
+0100
@@ -219,12 +219,11 @@
# database error will occur because the savepoint has already been
# automatically released. The following example demonstrates the problem:
#
- # Model.lease_connection.transaction do # BEGIN
- # Model.lease_connection.transaction(requires_new: true) do #
CREATE SAVEPOINT active_record_1
- # Model.lease_connection.create_table(...) #
active_record_1 now automatically released
- # end # RELEASE
SAVEPOINT active_record_1
- # # ^^^^ BOOM!
database error!
- # end
+ # Model.transaction do # BEGIN
+ # Model.transaction(requires_new: true) do # CREATE SAVEPOINT
active_record_1
+ # Model.lease_connection.create_table(...) # active_record_1 now
automatically released
+ # end # RELEASE SAVEPOINT
active_record_1
+ # end # ^^^^ BOOM! database
error!
#
# Note that "TRUNCATE" is also a MySQL DDL statement!
module ClassMethods
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/arel/collectors/bind.rb
new/lib/arel/collectors/bind.rb
--- old/lib/arel/collectors/bind.rb 2024-12-13 21:02:34.000000000 +0100
+++ new/lib/arel/collectors/bind.rb 1980-01-02 01:00:00.000000000 +0100
@@ -18,7 +18,7 @@
self
end
- def add_binds(binds, proc_for_binds = nil)
+ def add_binds(binds, proc_for_binds = nil, &)
@binds.concat proc_for_binds ? binds.map(&proc_for_binds) : binds
self
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/arel/collectors/sql_string.rb
new/lib/arel/collectors/sql_string.rb
--- old/lib/arel/collectors/sql_string.rb 2024-12-13 21:02:34.000000000
+0100
+++ new/lib/arel/collectors/sql_string.rb 1980-01-02 01:00:00.000000000
+0100
@@ -12,7 +12,7 @@
@bind_index = 1
end
- def add_bind(bind)
+ def add_bind(bind, &)
self << yield(@bind_index)
@bind_index += 1
self
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/arel/collectors/substitute_binds.rb
new/lib/arel/collectors/substitute_binds.rb
--- old/lib/arel/collectors/substitute_binds.rb 2024-12-13 21:02:34.000000000
+0100
+++ new/lib/arel/collectors/substitute_binds.rb 1980-01-02 01:00:00.000000000
+0100
@@ -15,12 +15,12 @@
self
end
- def add_bind(bind)
+ def add_bind(bind, &)
bind = bind.value_for_database if bind.respond_to?(:value_for_database)
self << quoter.quote(bind)
end
- def add_binds(binds, proc_for_binds = nil)
+ def add_binds(binds, proc_for_binds = nil, &)
self << binds.map { |bind| quoter.quote(bind) }.join(", ")
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/arel/nodes/binary.rb new/lib/arel/nodes/binary.rb
--- old/lib/arel/nodes/binary.rb 2024-12-13 21:02:34.000000000 +0100
+++ new/lib/arel/nodes/binary.rb 1980-01-02 01:00:00.000000000 +0100
@@ -30,7 +30,7 @@
end
module FetchAttribute
- def fetch_attribute
+ def fetch_attribute(&)
if left.is_a?(Arel::Attributes::Attribute)
yield left
elsif right.is_a?(Arel::Attributes::Attribute)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/arel/nodes/node.rb new/lib/arel/nodes/node.rb
--- old/lib/arel/nodes/node.rb 2024-12-13 21:02:34.000000000 +0100
+++ new/lib/arel/nodes/node.rb 1980-01-02 01:00:00.000000000 +0100
@@ -152,7 +152,7 @@
end
end
- def fetch_attribute
+ def fetch_attribute(&)
end
def equality?; false; end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/arel/nodes/sql_literal.rb
new/lib/arel/nodes/sql_literal.rb
--- old/lib/arel/nodes/sql_literal.rb 2024-12-13 21:02:34.000000000 +0100
+++ new/lib/arel/nodes/sql_literal.rb 1980-01-02 01:00:00.000000000 +0100
@@ -19,7 +19,7 @@
coder.scalar = self.to_s
end
- def fetch_attribute
+ def fetch_attribute(&)
end
def +(other)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/arel/visitors/to_sql.rb
new/lib/arel/visitors/to_sql.rb
--- old/lib/arel/visitors/to_sql.rb 2024-12-13 21:02:34.000000000 +0100
+++ new/lib/arel/visitors/to_sql.rb 1980-01-02 01:00:00.000000000 +0100
@@ -763,7 +763,7 @@
def visit_Arel_Nodes_SqlLiteral(o, collector)
collector.preparable = false
- collector.retryable = o.retryable
+ collector.retryable &&= o.retryable
collector << o.to_s
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/metadata new/metadata
--- old/metadata 2024-12-13 21:02:34.000000000 +0100
+++ new/metadata 1980-01-02 01:00:00.000000000 +0100
@@ -1,14 +1,13 @@
--- !ruby/object:Gem::Specification
name: activerecord
version: !ruby/object:Gem::Version
- version: 8.0.1
+ version: 8.0.2.1
platform: ruby
authors:
- David Heinemeier Hansson
-autorequire:
bindir: bin
cert_chain: []
-date: 2024-12-13 00:00:00.000000000 Z
+date: 1980-01-02 00:00:00.000000000 Z
dependencies:
- !ruby/object:Gem::Dependency
name: activesupport
@@ -16,28 +15,28 @@
requirements:
- - '='
- !ruby/object:Gem::Version
- version: 8.0.1
+ version: 8.0.2.1
type: :runtime
prerelease: false
version_requirements: !ruby/object:Gem::Requirement
requirements:
- - '='
- !ruby/object:Gem::Version
- version: 8.0.1
+ version: 8.0.2.1
- !ruby/object:Gem::Dependency
name: activemodel
requirement: !ruby/object:Gem::Requirement
requirements:
- - '='
- !ruby/object:Gem::Version
- version: 8.0.1
+ version: 8.0.2.1
type: :runtime
prerelease: false
version_requirements: !ruby/object:Gem::Requirement
requirements:
- - '='
- !ruby/object:Gem::Version
- version: 8.0.1
+ version: 8.0.2.1
- !ruby/object:Gem::Dependency
name: timeout
requirement: !ruby/object:Gem::Requirement
@@ -475,12 +474,11 @@
- MIT
metadata:
bug_tracker_uri: https://github.com/rails/rails/issues
- changelog_uri:
https://github.com/rails/rails/blob/v8.0.1/activerecord/CHANGELOG.md
- documentation_uri: https://api.rubyonrails.org/v8.0.1/
+ changelog_uri:
https://github.com/rails/rails/blob/v8.0.2.1/activerecord/CHANGELOG.md
+ documentation_uri: https://api.rubyonrails.org/v8.0.2.1/
mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
- source_code_uri: https://github.com/rails/rails/tree/v8.0.1/activerecord
+ source_code_uri: https://github.com/rails/rails/tree/v8.0.2.1/activerecord
rubygems_mfa_required: 'true'
-post_install_message:
rdoc_options:
- "--main"
- README.rdoc
@@ -497,8 +495,7 @@
- !ruby/object:Gem::Version
version: '0'
requirements: []
-rubygems_version: 3.5.22
-signing_key:
+rubygems_version: 3.6.9
specification_version: 4
summary: Object-relational mapper framework (part of Rails).
test_files: []