activerecord
Advanced tools
+58
-0
@@ -0,1 +1,59 @@ | ||
| ## Rails 8.1.3 (March 24, 2026) ## | ||
| * Fix `insert_all` and `upsert_all` log message when called on anonymous classes. | ||
| *Gabriel Sobrinho* | ||
| * Respect `ActiveRecord::SchemaDumper.ignore_tables` when dumping SQLite virtual tables. | ||
| *Hans Schnedlitz* | ||
| * Restore previous instrumenter after `execute_or_skip` | ||
| `FutureResult#execute_or_skip` replaces the thread's instrumenter with an | ||
| `EventBuffer` to collect events published during async query execution. | ||
| If the global async executor is saturated and the `caller_runs` fallback | ||
| executes the task on the calling thread, we need to make sure the previous | ||
| instrumenter is restored or the stale `EventBuffer` would stay in place and | ||
| permanently swallow all subsequent `sql.active_record` notifications on | ||
| that thread. | ||
| *Rosa Gutierrez* | ||
| * Bump the minimum PostgreSQL version to 9.5, due to usage of `array_position` function. | ||
| *Ivan Kuchin* | ||
| * Fix Ruby 4.0 delegator warning when calling inspect on ActiveRecord::Type::Serialized. | ||
| *Hammad Khan* | ||
| * Fix support for table names containing hyphens. | ||
| *Evgeniy Demin* | ||
| * Fix column deduplication for SQLite3 and PostgreSQL virtual (generated) columns. | ||
| `Column#==` and `Column#hash` now account for `virtual?` so that the | ||
| `Deduplicable` registry does not treat a generated column and a regular | ||
| column with the same name and type as identical. Previously, if a | ||
| generated column was registered first, a regular column on a different | ||
| table could be deduplicated to the generated instance, silently | ||
| excluding it from INSERT/UPDATE statements. | ||
| *Jay Huber* | ||
| * Fix PostgreSQL schema dumping to handle schema-qualified table names in foreign_key references that span different schemas. | ||
| # before | ||
| add_foreign_key "hst.event_log_attributes", "hst.event_logs" # emits correctly because they're in the same schema (hst) | ||
| add_foreign_key "hst.event_log_attributes", "hst.usr.user_profiles", column: "created_by_id" # emits hst.user.* when user.* is expected | ||
| # after | ||
| add_foreign_key "hst.event_log_attributes", "hst.event_logs" | ||
| add_foreign_key "hst.event_log_attributes", "usr.user_profiles", column: "created_by_id" | ||
| *Chiperific* | ||
| ## Rails 8.1.2.1 (March 23, 2026) ## | ||
@@ -2,0 +60,0 @@ |
@@ -361,3 +361,3 @@ # frozen_string_literal: true | ||
| singleton_class.attr_accessor :raise_on_missing_required_finder_order_columns | ||
| self.run_after_transaction_callbacks_in_order_defined = false | ||
| self.raise_on_missing_required_finder_order_columns = false | ||
@@ -364,0 +364,0 @@ singleton_class.attr_accessor :application_record_class |
@@ -129,3 +129,3 @@ # frozen_string_literal: true | ||
| def self.dbconsole(config, options = {}) | ||
| raise NotImplementedError.new("#{self.class} should define `dbconsole` that accepts a db config and options to implement connecting to the db console") | ||
| raise NotImplementedError.new("#{self} should define `dbconsole` that accepts a db config and options to implement connecting to the db console") | ||
| end | ||
@@ -132,0 +132,0 @@ |
@@ -132,3 +132,3 @@ # frozen_string_literal: true | ||
| # https://bugs.ruby-lang.org/issues/20688 | ||
| if ObjectSpace.const_defined?(:WeakKeyMap) && RUBY_VERSION >= "3.3.5" | ||
| if ObjectSpace.const_defined?(:WeakKeyMap) && Gem::Version.new(RUBY_VERSION) >= "3.3.5" | ||
| WeakThreadKeyMap = ObjectSpace::WeakKeyMap | ||
@@ -530,3 +530,3 @@ else | ||
| end | ||
| @connections = [] | ||
| @connections = @pinned_connection ? [@pinned_connection] : [] | ||
| @leases.clear | ||
@@ -533,0 +533,0 @@ @available.clear |
@@ -756,3 +756,3 @@ # frozen_string_literal: true | ||
| def extract_table_ref_from_insert_sql(sql) | ||
| if sql =~ /into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im | ||
| if sql =~ /into\s("[-A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im | ||
| $1.delete('"').strip | ||
@@ -759,0 +759,0 @@ end |
@@ -685,4 +685,4 @@ # frozen_string_literal: true | ||
| def check_version # :nodoc: | ||
| if database_version < 9_03_00 # < 9.3 | ||
| raise "Your version of PostgreSQL (#{database_version}) is too old. Active Record supports PostgreSQL >= 9.3." | ||
| if database_version < 9_05_00 # < 9.5 | ||
| raise "Your version of PostgreSQL (#{database_version}) is too old. Active Record supports PostgreSQL >= 9.5." | ||
| end | ||
@@ -689,0 +689,0 @@ end |
@@ -72,3 +72,4 @@ # frozen_string_literal: true | ||
| identity? == other.identity? && | ||
| serial? == other.serial? | ||
| serial? == other.serial? && | ||
| virtual? == other.virtual? | ||
| end | ||
@@ -81,3 +82,4 @@ alias :eql? :== | ||
| identity?.hash ^ | ||
| serial?.hash | ||
| serial?.hash ^ | ||
| virtual?.hash | ||
| end | ||
@@ -84,0 +86,0 @@ end |
@@ -157,2 +157,4 @@ # frozen_string_literal: true | ||
| name | ||
| elsif name.include?(".") | ||
| name # Already schema-qualified, don't add another prefix | ||
| else | ||
@@ -159,0 +161,0 @@ "#{schema_name}.#{name}" |
@@ -322,3 +322,3 @@ # frozen_string_literal: true | ||
| VIRTUAL_TABLE_REGEX = /USING\s+(\w+)\s*\((.+)\)/i | ||
| VIRTUAL_TABLE_REGEX = /USING\s+(\w+)\s*\((.*)\)/i | ||
@@ -325,0 +325,0 @@ # Returns a list of defined virtual tables |
@@ -49,3 +49,5 @@ # frozen_string_literal: true | ||
| super && | ||
| auto_increment? == other.auto_increment? | ||
| auto_increment? == other.auto_increment? && | ||
| rowid == other.rowid && | ||
| generated_type == other.generated_type | ||
| end | ||
@@ -58,4 +60,8 @@ alias :eql? :== | ||
| auto_increment?.hash ^ | ||
| rowid.hash | ||
| rowid.hash ^ | ||
| virtual?.hash | ||
| end | ||
| protected | ||
| attr_reader :generated_type | ||
| end | ||
@@ -62,0 +68,0 @@ end |
@@ -9,3 +9,3 @@ # frozen_string_literal: true | ||
| def virtual_tables(stream) | ||
| virtual_tables = @connection.virtual_tables | ||
| virtual_tables = @connection.virtual_tables.reject { |name, _| ignored?(name) } | ||
| if virtual_tables.any? | ||
@@ -12,0 +12,0 @@ stream.puts |
@@ -108,2 +108,3 @@ # frozen_string_literal: true | ||
| return unless @mutex.try_lock | ||
| previous_instrumenter = ActiveSupport::IsolatedExecutionState[:active_record_instrumenter] | ||
| begin | ||
@@ -117,2 +118,3 @@ if pending? | ||
| ensure | ||
| ActiveSupport::IsolatedExecutionState[:active_record_instrumenter] = previous_instrumenter | ||
| @mutex.unlock | ||
@@ -119,0 +121,0 @@ end |
@@ -12,4 +12,4 @@ # frozen_string_literal: true | ||
| MINOR = 1 | ||
| TINY = 2 | ||
| PRE = "1" | ||
| TINY = 3 | ||
| PRE = nil | ||
@@ -16,0 +16,0 @@ STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".") |
@@ -42,3 +42,3 @@ # frozen_string_literal: true | ||
| @unique_by = find_unique_index_for(@unique_by) | ||
| @unique_by = find_unique_index_for(@unique_by) if @on_duplicate != :raise | ||
@@ -52,3 +52,3 @@ configure_on_duplicate_update_logic | ||
| message = +"#{model} " | ||
| message = +"#{model.name} " | ||
| message << "Bulk " if inserts.many? | ||
@@ -55,0 +55,0 @@ message << (on_duplicate == :update ? "Upsert" : "Insert") |
@@ -145,3 +145,3 @@ # frozen_string_literal: true | ||
| commands = recorder.commands | ||
| @commands << [:change_table, [table_name], -> t { bulk_change_table(table_name, commands) }] | ||
| @commands << [:change_table, [table_name], -> t { bulk_change_table(t.name, commands.reverse) }] | ||
| else | ||
@@ -148,0 +148,0 @@ yield delegate.update_table_definition(table_name, self) |
@@ -444,4 +444,4 @@ # frozen_string_literal: true | ||
| # Efficiently peak at the last value for the next batch using offset and limit. | ||
| values_size = batch_limit | ||
| values_last = batch_relation.offset(batch_limit - 1).pick(*cursor) | ||
| values_size = remaining ? [batch_limit, remaining].min : batch_limit | ||
| values_last = batch_relation.offset(values_size - 1).pick(*cursor) | ||
@@ -448,0 +448,0 @@ # If the last value is not found using offset, there is at most one more batch of size < batch_limit. |
@@ -34,3 +34,5 @@ # frozen_string_literal: true | ||
| array_predicates = ranges.map! { |range| predicate_builder.build(attribute, range) } | ||
| array_predicates.inject(values_predicate, &:or) | ||
| values_predicate.or( | ||
| Arel::Nodes::Grouping.new Arel::Nodes::Or.new(array_predicates) | ||
| ) | ||
| end | ||
@@ -37,0 +39,0 @@ end |
@@ -28,6 +28,4 @@ # frozen_string_literal: true | ||
| JSON_ENCODER = ActiveSupport::JSON::Encoding.json_encoder.new(escape: false) | ||
| def serialize(value) | ||
| JSON_ENCODER.encode(value) unless value.nil? | ||
| ActiveSupport::JSON::Encoding.encode_without_escape(value) unless value.nil? | ||
| end | ||
@@ -34,0 +32,0 @@ |
@@ -68,2 +68,7 @@ # frozen_string_literal: true | ||
| private | ||
| # Prevent Ruby 4.0 "delegator does not forward private method" warning. | ||
| # Kernel#inspect calls instance_variables_to_inspect which, without this, | ||
| # triggers Delegator#respond_to_missing? for a private method. | ||
| define_method(:instance_variables_to_inspect, Kernel.instance_method(:instance_variables)) | ||
| def default_value?(value) | ||
@@ -70,0 +75,0 @@ value == coder.load(nil) |
@@ -234,5 +234,3 @@ # frozen_string_literal: true | ||
| nodes = others.map { |expr| send(method_id, expr, *extras) } | ||
| Nodes::Grouping.new nodes.inject { |memo, node| | ||
| Nodes::Or.new([memo, node]) | ||
| } | ||
| Nodes::Grouping.new Nodes::Or.new(nodes) | ||
| end | ||
@@ -239,0 +237,0 @@ |
Sorry, the diff of this file is too big to display