Always request `primary_key` in `RETURNING` if no other columns requested

Prior 7.1 Rails always included `primary_key` in `RETURNING` clause on
record creation. This was changed in 7.1 to include more auto-populated
columns if such columns exist. This change lead to situations where
no columns were requested in `RETURNING` clause, even the `primary_key`.

This change brings back the old behavior of always requesting the
`primary_key` in `RETURNING` clause if no other columns are requested.
This commit is contained in:
Nikita Vasilevsky 2024-01-17 17:36:11 +00:00
parent 25b7f64074
commit c2c861f98a
No known key found for this signature in database
GPG Key ID: 0FF5725CD31059E4
5 changed files with 51 additions and 2 deletions

View File

@ -431,8 +431,12 @@ module ActiveRecord
end
def _returning_columns_for_insert # :nodoc:
@_returning_columns_for_insert ||= columns.filter_map do |c|
c.name if connection.return_value_after_insert?(c)
@_returning_columns_for_insert ||= begin
auto_populated_columns = columns.filter_map do |c|
c.name if connection.return_value_after_insert?(c)
end
auto_populated_columns.empty? ? Array(primary_key) : auto_populated_columns
end
end

View File

@ -25,6 +25,7 @@ require "models/cpk"
require "models/chat_message"
require "models/default"
require "models/post_with_prefetched_pk"
require "models/pk_autopopulated_by_a_trigger_record"
class PersistenceTest < ActiveRecord::TestCase
fixtures :topics, :companies, :developers, :accounts, :minimalistics, :authors, :author_addresses,
@ -1574,6 +1575,13 @@ class PersistenceTest < ActiveRecord::TestCase
assert_equal("blue", ClothingItem.find_by(id: clothing_item.id).color)
end
def test_model_with_no_auto_populated_fields_still_returns_primary_key_after_insert
record = PkAutopopulatedByATriggerRecord.create
assert_not_nil record.id
assert record.id > 0
end if current_adapter?(:PostgreSQLAdapter)
end
class QueryConstraintsTest < ActiveRecord::TestCase

View File

@ -193,6 +193,12 @@ if ActiveRecord::Base.connection.supports_views?
assert_equal "Rails in Action", new_book.name
end
def test_insert_record_populates_primary_key
book = PrintedBook.create! name: "Rails in Action", status: 0, format: "paperback"
assert_not_nil book.id
assert book.id > 0
end if current_adapter?(:PostgreSQLAdapter, :SQLite3Adapter) && supports_insert_returning?
def test_update_record_to_fail_view_conditions
book = PrintedBook.first
book.format = "ebook"

View File

@ -0,0 +1,5 @@
# frozen_string_literal: true
class PkAutopopulatedByATriggerRecord < ActiveRecord::Base
self.primary_key = :id
end

View File

@ -1483,6 +1483,32 @@ ActiveRecord::Schema.define do
end
end
if ActiveRecord::TestCase.current_adapter?(:PostgreSQLAdapter)
ActiveRecord::Base.connection.create_table :pk_autopopulated_by_a_trigger_records, force: true, id: false do |t|
t.integer :id, null: false
end
ActiveRecord::Base.connection.execute(
<<-SQL
CREATE OR REPLACE FUNCTION populate_column()
RETURNS TRIGGER AS $$
DECLARE
max_value INTEGER;
BEGIN
SELECT MAX(id) INTO max_value FROM pk_autopopulated_by_a_trigger_records;
NEW.id = COALESCE(max_value, 0) + 1;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER before_insert_trigger
BEFORE INSERT ON pk_autopopulated_by_a_trigger_records
FOR EACH ROW
EXECUTE FUNCTION populate_column();
SQL
)
end
Course.connection.create_table :courses, force: true do |t|
t.column :name, :string, null: false
t.column :college_id, :integer, index: true