From 0e67f793cd67ad96abbe7e9b3ba1363fdedb8e71 Mon Sep 17 00:00:00 2001 From: Carlos Antonio da Silva Date: Thu, 6 Dec 2012 23:09:11 -0200 Subject: [PATCH] Unscope update_column(s) query to ignore default scope When applying default_scope to a class with a where clause, using update_column(s) could generate a query that would not properly update the record due to the where clause from the default_scope being applied to the update query. class User < ActiveRecord::Base default_scope where(active: true) end user = User.first user.active = false user.save! user.update_column(:active, true) # => false In this situation we want to skip the default_scope clause and just update the record based on the primary key. With this change: user.update_column(:active, true) # => true Fixes #8436. --- activerecord/CHANGELOG.md | 26 +++++++++++++++++++ activerecord/lib/active_record/persistence.rb | 2 +- activerecord/test/cases/persistence_test.rb | 16 ++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 4e48c386bd..e79c7cb64f 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,5 +1,31 @@ ## Rails 4.0.0 (unreleased) ## +* Unscope `update_column(s)` query to ignore default scope. + + When applying `default_scope` to a class with a where clause, using + `update_column(s)` could generate a query that would not properly update + the record due to the where clause from the `default_scope` being applied + to the update query. + + class User < ActiveRecord::Base + default_scope where(active: true) + end + + user = User.first + user.active = false + user.save! + + user.update_column(:active, true) # => false + + In this situation we want to skip the default_scope clause and just + update the record based on the primary key. With this change: + + user.update_column(:active, true) # => true + + Fixes #8436. + + *Carlos Antonio da Silva* + * SQLite adapter no longer corrupts binary data if the data contains `%00`. *Chris Feist* diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 94c109e72b..4d1a9c94b7 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -259,7 +259,7 @@ module ActiveRecord verify_readonly_attribute(key.to_s) end - updated_count = self.class.where(self.class.primary_key => id).update_all(attributes) + updated_count = self.class.unscoped.where(self.class.primary_key => id).update_all(attributes) attributes.each do |k, v| raw_write_attribute(k, v) diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index 02034c87b4..9e0423ab52 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -512,6 +512,14 @@ class PersistencesTest < ActiveRecord::TestCase assert_equal 'super_title', t.title end + def test_update_column_with_default_scope + developer = DeveloperCalledDavid.first + developer.name = 'John' + developer.save! + + assert developer.update_column(:name, 'Will'), 'did not update record due to default scope' + end + def test_update_columns topic = Topic.find(1) topic.update_columns({ "approved" => true, title: "Sebastian Topic" }) @@ -616,6 +624,14 @@ class PersistencesTest < ActiveRecord::TestCase assert_equal true, topic.update_columns(title: "New title") end + def test_update_columns_with_default_scope + developer = DeveloperCalledDavid.first + developer.name = 'John' + developer.save! + + assert developer.update_columns(name: 'Will'), 'did not update record due to default scope' + end + def test_update_attributes topic = Topic.find(1) assert !topic.approved?