Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

ActiveRecord Hstore bug: can't update a key in the hash #6127

Closed
joevandyk opened this Issue · 27 comments

22 participants

Joe Van Dyk Toshinori Kajihara Andrew Gertig Matt Jones Aaron Patterson Philip Arndt Neeraj Singh bloodycelt प्रथमेश Robin Dupret Peter Cooper Rafael Mendonça França William T. Nelson Ernesto Tagwerker George Deglin Fabio Kreusch Kyle Chad W. Taylor Yves Senn Saurabh Nanda Shu Uesugi Joost Baaij
Joe Van Dyk

joevandyk@f2318b2 shows a failing test.

  def test_updating_key                                                                             
    x = Hstore.create! :tags => { "key1" => "old value 1", "key2" => "old value 2" }                
    x.reload                                                                                        
    assert_equal "old value 1", x.tags["key1"]                                                      

    # Nothing gets saved/updated here.
    x.tags["key1"] = "new"                                                                          
    x.save!                                                                                         

    assert_equal "new", x.reload.tags["key1"]                                                       
    assert_equal "old value 2",   x.reload.tags["key2"]                                             
  end
  1) Failure:
test_updating_key(PostgresqlHstoreTest) [cases/adapters/postgresql/hstore_test.rb:55]:
Expected: "new"
  Actual: "old value 1"
Toshinori Kajihara
Collaborator

I can reproduce this issue.
It seems that any query weren't executed when executing save!
I guess:
x.tags method returns a Hash object, and we can't track dirty flag.

Andrew Gertig

I can also reproduce this issue.

Matt Jones

A quick workaround would be to call (in your example) x.tags_will_change! before setting the new value.

Historically, this has always been a problem with serialize columns, and AR's approach in that case is to always save serialized columns. HStore columns might need the same treatment.

Aaron Patterson
Owner

@al2o3cr ya, I've been looking at this. I'd like to treat the hstore columns as serialized columns, but it's going to take a little refactoring.

Philip Arndt

@tenderlove has there been any refactoring to date that would have helped this?

Neeraj Singh
Collaborator

Here is another version and this one does not break the API. It is based on feedback given by @pixeltrix

https://github.com/neerajdotname/rails/compare/6127b

I'll clean up the code a bit , add tests and will delete some code that is not needed regarding hstore etc if there is any interest in moving hstore to be on top of serialization.

bloodycelt

@neerjdotname Forgive me if this seems naive, but wouldn't it make more sense to specify using hstore in the database.yml (serialize using hstore), what would happen if that model was using sqlite instead?

Neeraj Singh
Collaborator

@bloodycelt sqlite does not support hstore. So if you are using sqlite you should not add this line in your model .

serialize :preferences, ActiveRecord::Coders::HstoreColumn.new
प्रथमेश

Bump. Any updates on this?

Robin Dupret
Collaborator

Isn't it related to #12395 ?

Peter Cooper

Pushed back another point release?

Rafael Mendonça França

Yes. :disappointed:

William T. Nelson

For a quick fix, I added #attr_name_will_change! before each change to hstore attributes in my application.

Yves Senn senny added the PostgreSQL label
Ernesto Tagwerker

Bump. I just reproduced this bug in 4.0.4

I tried using #attr_name_will_change! before each change but it's not working for me.

George Deglin

Yikes, this is a nasty one for anyone using hstore. Just got bit by this...

Example of problematic code

def example
  player = Player.last
  player.tags["foo"] = "bar"
  puts "Initial: #{player.tags['foo']}"       # "bar"
  player.save
  puts "Reload: #{player.reload.tags['foo']}" # null (???)
  player.tags["foo"] = "bar"
  player.tags_will_change! # Workaround...
  player.save
  puts "Now?: #{player.reload.tags['foo']}" # "bar"
end
example

Output:

Initial: bar
Reload: 
Now?: bar

Version info:

ruby 2.1.0p0 (2013-12-25 revision 44422) [x86_64-darwin13.0]
Rails 4.0.4

Column info for "tags":

 tags               | hstore                      | not null default ''::hstore
Matt Jones

@gdeglin - I'd recommend one change to the above: player.tags_will_change! should be before the assignment. That's why it's "will" and not "did" change :)

Fabio Kreusch

This is a monkey patch I have been using to make that a default for hstores and arrays:

module Ext::ActiveRecord::Dirty
  private

  # Private: Overwrites the default implementation to include arrays and hstores
  # in the list of attributes that should always be saved.
  #
  # Without this, arrays and stores don't get their values updated in
  # the database.
  #
  # Relates to this issue: https://github.com/rails/rails/issues/6127
  def keys_for_partial_write
    super | self.class.columns.select do |c|
      c.try(:array) || c.type == :hstore
    end.map(&:name)
  end
end

ActiveSupport.on_load :active_record do
  include Ext::ActiveRecord::Dirty
end
Kyle

@gdeglin I have just tested with @al2o3cr suggestion and you should execute tags_will_change! first then set tags.

Chad W. Taylor

+1 This is an issue for me too!

Yves Senn
Owner

This is the current expected behavior. You need to call XYZ_will_change! when modifying hstore or json attributes in place. It is related to the discussion on serialized attributes (they are always saved even when they don't change). A fix will benefit both scenarios.

@joevandyk thank you for reporting.

Yves Senn senny closed this
Saurabh Nanda

Just faced this. What is the final call:
(a) Fix serialized columns -- should be saved only when changed, OR
(b) Make sure Hstore column is always saved, whether changed or not?

Saurabh Nanda

Btw, in Postgres, hstore allows you to change the column partially, eg.

UPDATE mytable SET properties = (properties - 'deleted_key') || hstore('added_key', 'added_value')

Yves Senn
Owner

@saurabhnanda we keep the current behavior until we have a working solution for serialized attributes.

Shu Uesugi

Came here from Stack Overflow: http://stackoverflow.com/questions/20251296/how-can-i-update-a-data-records-value-with-ruby-on-rails-4-0-1-postgresql-hstor

I'm fine with the current behavior but I wish this was documented better. Does Rails 4 have a documentation page on Hstore related stuff?

Yves Senn
Owner

@chibicode I'm working on a PostgreSQL specific guide. See my current draft it does go into the details and will be updated in the future.

We are also working on resolving the situation with serialized attributes. Once we finished that, we can use that same behavior for json and hstore columns.

Shu Uesugi

@senny wow, that's very useful. Thank you!

Joost Baaij

For what it's worth, this behavior has been fixed as of #15674 and will be part of 4.2.

Sean Griffin sgrif locked and limited conversation to collaborators
Jonathan Allard joallard referenced this issue from a commit in joallard/trasto
Jonathan Allard joallard Fix mutable bug in AR 4.1 90aa86d
Jonathan Allard joallard referenced this issue from a commit in joallard/trasto
Jonathan Allard joallard Fix mutable bug in AR 4.1 6bff0ec
Jonathan Allard joallard referenced this issue from a commit in joallard/trasto
Jonathan Allard joallard Fix mutable bug in AR 4.1 405ce5e
Jonathan Allard joallard referenced this issue from a commit in joallard/trasto
Jonathan Allard joallard Fix mutable bug in AR 4.1 ac5ef19
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Something went wrong with that request. Please try again.