Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

collection.<< counter cache doesn't respect validations #3085

Closed
masterkain opened this issue Sep 21, 2011 · 6 comments
Closed

collection.<< counter cache doesn't respect validations #3085

masterkain opened this issue Sep 21, 2011 · 6 comments

Comments

@masterkain
Copy link
Contributor

Please see and run the brief test script, it should demonstrate the issue.

In substance given a has_many :through and validations on the join model ActiveRecord correctly respects them when adding objects to the collection, but the automatic counter cache slips through

UPDATE "nodes" SET "tracks_count" = COALESCE("tracks_count", 0) + 1 WHERE "nodes"."id" = 1

and gets fired in any case.

require 'active_record'
require 'logger'

puts "Using ActiveRecord #{ActiveRecord::VERSION::STRING}"

ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Base.establish_connection(
  :adapter  => 'sqlite3',
  :database => ':memory:'
)

# Create the minimal database schema necessary to reproduce the bug
ActiveRecord::Schema.define do
  create_table :nodes, :force => true do |t|
    t.integer :tracks_count, :default => 0
  end

  create_table :node_tracks, :force => true do |t|
    t.integer :node_id
    t.integer :track_id
  end

  create_table :tracks, :force => true do |t|
  end
end

class Node < ActiveRecord::Base
  has_many :node_tracks
  has_many :tracks, through: :node_tracks
  # validates_associated :node_tracks # tried with this also
end

class NodeTrack < ActiveRecord::Base
  belongs_to :node
  belongs_to :track

  validates :node_id, presence: true
  validates :track_id, presence: true, uniqueness: { scope: :node_id } # avoid adding same tracks over and over
end

class Track < ActiveRecord::Base
  has_many :node_tracks
  has_many :nodes, through: :node_tracks
end

node = Node.create!
track = Track.create!

puts node.inspect # tracks_count correctly initialized to 0

node.tracks << [track, track]

puts node.inspect # tracks_count incorrectly set to 2, should be 1
puts node.tracks(true).length # returns 1 which is correct, eventhough I had to force collection reload, without it reports 2
@ghost ghost assigned jonleighton Sep 21, 2011
@jonleighton
Copy link
Member

Thanks for the great bug report :) What versions are affected? Is this a regression from 3.0?

@masterkain
Copy link
Contributor Author

Hi @jonleighton, sorry for not mentioning the version: I have tested it on 3.1.0 (current gem release) and current master branch, I'll try later to run it on 3.0, will report back!

@masterkain
Copy link
Contributor Author

I tried the 3-0-stable branch:

[ruby-1.9.3-preview1] 11:46 ~ $ ruby -I src/rails/activerecord/lib test.rb 

it bombs, but maybe it's my setup, not sure:

/Users/kain/.rvm/gems/ruby-1.9.3-preview1/gems/arel-2.2.1/lib/arel/tree_manager.rb:19:in `visitor': undefined method `visitor' for #<ActiveRecord::ConnectionAdapters::SQLite3Adapter:0x007fdec3bd1998> (NoMethodError)
from /Users/kain/.rvm/gems/ruby-1.9.3-preview1/gems/arel-2.2.1/lib/arel/tree_manager.rb:23:in `to_sql'
from /Users/kain/.rvm/gems/ruby-1.9.3-preview1/gems/arel-2.2.1/lib/arel/select_manager.rb:279:in `insert'
from /Users/kain/src/rails/activerecord/lib/active_record/relation.rb:14:in `insert'
from /Users/kain/src/rails/activerecord/lib/active_record/persistence.rb:281:in `create'
from /Users/kain/src/rails/activerecord/lib/active_record/timestamp.rb:47:in `create'
from /Users/kain/src/rails/activerecord/lib/active_record/callbacks.rb:277:in `block in create'
from /Users/kain/src/rails/activesupport/lib/active_support/callbacks.rb:414:in `_run_create_callbacks'
from /Users/kain/src/rails/activerecord/lib/active_record/callbacks.rb:277:in `create'
from /Users/kain/src/rails/activerecord/lib/active_record/persistence.rb:257:in `create_or_update'
from /Users/kain/src/rails/activerecord/lib/active_record/callbacks.rb:273:in `block in create_or_update'
from /Users/kain/src/rails/activesupport/lib/active_support/callbacks.rb:419:in `_run_save_callbacks'
from /Users/kain/src/rails/activerecord/lib/active_record/callbacks.rb:273:in `create_or_update'
from /Users/kain/src/rails/activerecord/lib/active_record/persistence.rb:60:in `save!'
from /Users/kain/src/rails/activerecord/lib/active_record/validations.rb:49:in `save!'
from /Users/kain/src/rails/activerecord/lib/active_record/attribute_methods/dirty.rb:30:in `save!'
from /Users/kain/src/rails/activerecord/lib/active_record/transactions.rb:245:in `block in save!'
from /Users/kain/src/rails/activerecord/lib/active_record/transactions.rb:292:in `block in with_transaction_returning_status'
from /Users/kain/src/rails/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:139:in `transaction'
from /Users/kain/src/rails/activerecord/lib/active_record/transactions.rb:207:in `transaction'
from /Users/kain/src/rails/activerecord/lib/active_record/transactions.rb:290:in `with_transaction_returning_status'
from /Users/kain/src/rails/activerecord/lib/active_record/transactions.rb:245:in `save!'
from /Users/kain/src/rails/activerecord/lib/active_record/validations.rb:34:in `create!'
from test.rb:46:in `<main>'

@masterkain
Copy link
Contributor Author

Tried also with the 3.0.10 gem, this is the result, better than the previous but the same code raises, this is different behaviour from 3.1.0 and master tests in the first post:

/Users/kain/.rvm/gems/ruby-1.9.3-preview1/gems/activerecord-3.0.10/lib/active_record/validations.rb:49:in `save!': Validation failed: Track has already been taken (ActiveRecord::RecordInvalid)
from /Users/kain/.rvm/gems/ruby-1.9.3-preview1/gems/activerecord-3.0.10/lib/active_record/attribute_methods/dirty.rb:30:in `save!'
from /Users/kain/.rvm/gems/ruby-1.9.3-preview1/gems/activerecord-3.0.10/lib/active_record/transactions.rb:245:in `block in save!'
from /Users/kain/.rvm/gems/ruby-1.9.3-preview1/gems/activerecord-3.0.10/lib/active_record/transactions.rb:292:in `block in with_transaction_returning_status'
from /Users/kain/.rvm/gems/ruby-1.9.3-preview1/gems/activerecord-3.0.10/lib/active_record/connection_adapters/abstract/database_statements.rb:139:in `transaction'
from /Users/kain/.rvm/gems/ruby-1.9.3-preview1/gems/activerecord-3.0.10/lib/active_record/transactions.rb:207:in `transaction'
from /Users/kain/.rvm/gems/ruby-1.9.3-preview1/gems/activerecord-3.0.10/lib/active_record/transactions.rb:290:in `with_transaction_returning_status'
from /Users/kain/.rvm/gems/ruby-1.9.3-preview1/gems/activerecord-3.0.10/lib/active_record/transactions.rb:245:in `save!'
from /Users/kain/.rvm/gems/ruby-1.9.3-preview1/gems/activerecord-3.0.10/lib/active_record/associations/association_collection.rb:281:in `block in create!'
from /Users/kain/.rvm/gems/ruby-1.9.3-preview1/gems/activerecord-3.0.10/lib/active_record/associations/association_collection.rb:503:in `block in create_record'
from /Users/kain/.rvm/gems/ruby-1.9.3-preview1/gems/activerecord-3.0.10/lib/active_record/associations/association_collection.rb:480:in `add_record_to_target_with_callbacks'
from /Users/kain/.rvm/gems/ruby-1.9.3-preview1/gems/activerecord-3.0.10/lib/active_record/associations/association_collection.rb:503:in `create_record'
from /Users/kain/.rvm/gems/ruby-1.9.3-preview1/gems/activerecord-3.0.10/lib/active_record/associations/association_collection.rb:279:in `create!'
from /Users/kain/.rvm/gems/ruby-1.9.3-preview1/gems/activerecord-3.0.10/lib/active_record/associations/has_many_through_association.rb:71:in `insert_record'
from /Users/kain/.rvm/gems/ruby-1.9.3-preview1/gems/activerecord-3.0.10/lib/active_record/associations/association_collection.rb:136:in `block (3 levels) in <<'
from /Users/kain/.rvm/gems/ruby-1.9.3-preview1/gems/activerecord-3.0.10/lib/active_record/associations/association_collection.rb:480:in `add_record_to_target_with_callbacks'
from /Users/kain/.rvm/gems/ruby-1.9.3-preview1/gems/activerecord-3.0.10/lib/active_record/associations/association_collection.rb:135:in `block (2 levels) in <<'
from /Users/kain/.rvm/gems/ruby-1.9.3-preview1/gems/activerecord-3.0.10/lib/active_record/associations/association_collection.rb:133:in `each'
from /Users/kain/.rvm/gems/ruby-1.9.3-preview1/gems/activerecord-3.0.10/lib/active_record/associations/association_collection.rb:133:in `block in <<'
from /Users/kain/.rvm/gems/ruby-1.9.3-preview1/gems/activerecord-3.0.10/lib/active_record/associations/association_collection.rb:158:in `block in transaction'
from /Users/kain/.rvm/gems/ruby-1.9.3-preview1/gems/activerecord-3.0.10/lib/active_record/connection_adapters/abstract/database_statements.rb:139:in `transaction'
from /Users/kain/.rvm/gems/ruby-1.9.3-preview1/gems/activerecord-3.0.10/lib/active_record/transactions.rb:207:in `transaction'
from /Users/kain/.rvm/gems/ruby-1.9.3-preview1/gems/activerecord-3.0.10/lib/active_record/associations/association_collection.rb:157:in `transaction'
from /Users/kain/.rvm/gems/ruby-1.9.3-preview1/gems/activerecord-3.0.10/lib/active_record/associations/association_collection.rb:132:in `<<'
from test.rb:51:in `<main>'

@masterkain
Copy link
Contributor Author

I changed a lot of code and I took a look back at this ticket, it seems ok in Rails 3.2.2, validation failed exception is always thrown but counter cache behavior seems in check in my specs.

@chabgood
Copy link

I am on 4.2.5 here is an issue:
server.instances.create(appenv_id: 65)
server.instances_count
=> 6
[10] pry(main)> server.instances.count
(0.4ms) SELECT COUNT(*) FROM instances WHERE instances.server_id = 1170
=> 5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants