diff options
Diffstat (limited to 'activerecord')
8 files changed, 72 insertions, 119 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index c0c8096084..4c55a3edea 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,5 +1,7 @@ ## Rails 4.0.0 (unreleased) ## +* Fix eagerly loading associations without primary keys. Fixes #4976. *Kelley Reynolds* + * Rails now raise an exception when you're trying to run a migration that has an invalid file name. Only lower case letters, numbers, and '_' are allowed in migration's file name. Please see #7419 for more details. @@ -645,104 +647,4 @@ * PostgreSQL hstore types are automatically deserialized from the database. -## Rails 3.2.8 (Aug 9, 2012) ## - -* Do not consider the numeric attribute as changed if the old value is zero and the new value - is not a string. - Fixes #7237. - - *Rafael Mendonça França* - -* Removes the deprecation of `update_attribute`. *fxn* - -* Reverted the deprecation of `composed_of`. *Rafael Mendonça França* - -* Reverted the deprecation of `*_sql` association options. They will - be deprecated in 4.0 instead. *Jon Leighton* - -* Do not eager load AR session store. ActiveRecord::SessionStore depends on the abstract store - in Action Pack. Eager loading this class would break client code that eager loads Active Record - standalone. - Fixes #7160 - - *Xavier Noria* - -* Do not set RAILS_ENV to "development" when using `db:test:prepare` and related rake tasks. - This was causing the truncation of the development database data when using RSpec. - Fixes #7175. - - *Rafael Mendonça França* - - -## Rails 3.2.7 (Jul 26, 2012) ## - -* `:finder_sql` and `:counter_sql` options on collection associations - are deprecated. Please transition to using scopes. - - *Jon Leighton* - -* `:insert_sql` and `:delete_sql` options on `has_and_belongs_to_many` - associations are deprecated. Please transition to using `has_many - :through` - - *Jon Leighton* - -* `composed_of` has been deprecated. You'll have to write your own accessor - and mutator methods if you'd like to use value objects to represent some - portion of your models. - - *Steve Klabnik* - -* `update_attribute` has been deprecated. Use `update_column` if - you want to bypass mass-assignment protection, validations, callbacks, - and touching of updated_at. Otherwise please use `update_attributes`. - - *Steve Klabnik* - - -## Rails 3.2.6 (Jun 12, 2012) ## - -* protect against the nesting of hashes changing the - table context in the next call to build_from_hash. This fix - covers this case as well. - - CVE-2012-2695 - -* Revert earlier 'perf fix' (see 3.2.4 changelog / GH #6289). This - change introduced a regression (GH #6609). assoc.clear and - assoc.delete_all have loaded the association before doing the delete - since at least Rails 2.3. Doing the delete without loading the - records means that the `before_remove` and `after_remove` callbacks do - not get invoked. Therefore, this change was less a fix a more an - optimisation, which should only have gone into master. - - *Jon Leighton* - - -## Rails 3.2.5 (Jun 1, 2012) ## - -* Restore behavior of Active Record 3.2.3 scopes. - A series of commits relating to preloading and scopes caused a regression. - - *Andrew White* - - -## Rails 3.2.4 (May 31, 2012) ## - -* Perf fix: Don't load the records when doing assoc.delete_all. - GH #6289. *Jon Leighton* - -* Association preloading shouldn't be affected by the current scoping. - This could cause infinite recursion and potentially other problems. - See GH #5667. *Jon Leighton* - -* Datetime attributes are forced to be changed. GH #3965 - -* Fix attribute casting. GH #5549 - -* Fix #5667. Preloading should ignore scoping. - -* Predicate builder should not recurse for determining where columns. - Thanks to Ben Murphy for reporting this! CVE-2012-2661 - Please check [3-2-stable](https://github.com/rails/rails/blob/3-2-stable/activerecord/CHANGELOG.md) for previous changes. diff --git a/activerecord/lib/active_record/associations/join_dependency/join_part.rb b/activerecord/lib/active_record/associations/join_dependency/join_part.rb index 2b1d888a9a..711f7b3ce1 100644 --- a/activerecord/lib/active_record/associations/join_dependency/join_part.rb +++ b/activerecord/lib/active_record/associations/join_dependency/join_part.rb @@ -54,7 +54,7 @@ module ActiveRecord unless @column_names_with_alias @column_names_with_alias = [] - ([primary_key] + (column_names - [primary_key])).each_with_index do |column_name, i| + ([primary_key] + (column_names - [primary_key])).compact.each_with_index do |column_name, i| @column_names_with_alias << [column_name, "#{aliased_prefix}_r#{i}"] end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb index 4e1f0e1d62..11e4d34de2 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb @@ -193,7 +193,8 @@ module ActiveRecord rescue Exception => database_transaction_rollback if transaction_open && !outside_transaction? transaction_open = false - decrement_open_transactions + txn = decrement_open_transactions + txn.aborted! if open_transactions == 0 rollback_db_transaction rollback_transaction_records(true) @@ -208,9 +209,10 @@ module ActiveRecord @transaction_joinable = last_transaction_joinable if outside_transaction? - @open_transactions = 0 + @current_transaction = nil elsif transaction_open - decrement_open_transactions + txn = decrement_open_transactions + txn.committed! begin if open_transactions == 0 commit_db_transaction diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index b3f9187429..27700e4fd2 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -69,6 +69,7 @@ module ActiveRecord @last_use = false @logger = logger @open_transactions = 0 + @current_transaction = nil @pool = pool @query_cache = Hash.new { |h,sql| h[sql] = {} } @query_cache_enabled = false @@ -236,14 +237,30 @@ module ActiveRecord @connection end - attr_reader :open_transactions + def open_transactions + count = 0 + txn = current_transaction + + while txn + count += 1 + txn = txn.next + end + + count + end + + attr_reader :current_transaction def increment_open_transactions - @open_transactions += 1 + @current_transaction = Transaction.new(current_transaction) end def decrement_open_transactions - @open_transactions -= 1 + return unless current_transaction + + txn = current_transaction + @current_transaction = txn.next + txn end def transaction_joinable=(joinable) diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index aad21b8e37..791ff4eaec 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -393,6 +393,7 @@ module ActiveRecord @marked_for_destruction = false @new_record = true @mass_assignment_options = nil + @txn = nil @_start_transaction_state = {} end end diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb index 09318879d5..e008b32170 100644 --- a/activerecord/lib/active_record/transactions.rb +++ b/activerecord/lib/active_record/transactions.rb @@ -1,6 +1,32 @@ require 'thread' module ActiveRecord + class Transaction + attr_reader :next + + def initialize(txn = nil) + @next = txn + @committed = false + @aborted = false + end + + def committed! + @committed = true + end + + def aborted! + @aborted = true + end + + def committed? + @committed + end + + def aborted? + @aborted + end + end + # See ActiveRecord::Transactions::ClassMethods for documentation. module Transactions extend ActiveSupport::Concern @@ -307,11 +333,11 @@ module ActiveRecord def with_transaction_returning_status status = nil self.class.transaction do + @txn = self.class.connection.current_transaction add_to_transaction begin status = yield rescue ActiveRecord::Rollback - @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1 status = nil end @@ -327,20 +353,17 @@ module ActiveRecord @_start_transaction_state[:id] = id if has_attribute?(self.class.primary_key) @_start_transaction_state[:new_record] = @new_record @_start_transaction_state[:destroyed] = @destroyed - @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) + 1 end # Clear the new record state and id of a record. def clear_transaction_record_state #:nodoc: - @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1 - @_start_transaction_state.clear if @_start_transaction_state[:level] < 1 + @_start_transaction_state.clear if @txn.committed? end # Restore the new record state and id of a record that was previously saved by a call to save_record_state. def restore_transaction_record_state(force = false) #:nodoc: unless @_start_transaction_state.empty? - @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1 - if @_start_transaction_state[:level] < 1 || force + if @txn.aborted? || force restore_state = @_start_transaction_state was_frozen = @attributes.frozen? @attributes = @attributes.dup if was_frozen diff --git a/activerecord/test/cases/associations/join_dependency_test.rb b/activerecord/test/cases/associations/join_dependency_test.rb new file mode 100644 index 0000000000..08c166dc33 --- /dev/null +++ b/activerecord/test/cases/associations/join_dependency_test.rb @@ -0,0 +1,8 @@ +require "cases/helper" +require 'models/edge' + +class JoinDependencyTest < ActiveRecord::TestCase + def test_column_names_with_alias_handles_nil_primary_key + assert_equal Edge.column_names, ActiveRecord::Associations::JoinDependency::JoinBase.new(Edge).column_names_with_alias.map(&:first) + end +end
\ No newline at end of file diff --git a/activerecord/test/cases/xml_serialization_test.rb b/activerecord/test/cases/xml_serialization_test.rb index 7249ae9e4d..68fa15de50 100644 --- a/activerecord/test/cases/xml_serialization_test.rb +++ b/activerecord/test/cases/xml_serialization_test.rb @@ -185,18 +185,18 @@ class NilXmlSerializationTest < ActiveRecord::TestCase end def test_should_serialize_string - assert_match %r{<name nil="true"></name>}, @xml + assert_match %r{<name nil="true"/>}, @xml end def test_should_serialize_integer - assert %r{<age (.*)></age>}.match(@xml) + assert %r{<age (.*)/>}.match(@xml) attributes = $1 assert_match %r{nil="true"}, attributes assert_match %r{type="integer"}, attributes end def test_should_serialize_binary - assert %r{<avatar (.*)></avatar>}.match(@xml) + assert %r{<avatar (.*)/>}.match(@xml) attributes = $1 assert_match %r{type="binary"}, attributes assert_match %r{encoding="base64"}, attributes @@ -204,21 +204,21 @@ class NilXmlSerializationTest < ActiveRecord::TestCase end def test_should_serialize_datetime - assert %r{<created-at (.*)></created-at>}.match(@xml) + assert %r{<created-at (.*)/>}.match(@xml) attributes = $1 assert_match %r{nil="true"}, attributes assert_match %r{type="dateTime"}, attributes end def test_should_serialize_boolean - assert %r{<awesome (.*)></awesome>}.match(@xml) + assert %r{<awesome (.*)/>}.match(@xml) attributes = $1 assert_match %r{type="boolean"}, attributes assert_match %r{nil="true"}, attributes end def test_should_serialize_yaml - assert_match %r{<preferences nil=\"true\"></preferences>}, @xml + assert_match %r{<preferences nil=\"true\"/>}, @xml end end |