diff options
Diffstat (limited to 'activerecord')
22 files changed, 84 insertions, 499 deletions
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index d8f68d7c28..348248e849 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,4 +1,4 @@ -*Rails 3.0.0 [beta 4/release candidate] (unreleased)* +*Rails 3.0.0 [beta 4] (June 8th, 2010)* * Add index length support for MySQL. #1852 [Emili Parreno, Pratik Naik] @@ -12,8 +12,6 @@ * find_or_create_by_attr(value, ...) works when attr is protected. #4457 [Santiago Pastorino, Marc-André Lafortune] -* New callbacks: after_commit and after_rollback. Do expensive operations like image thumbnailing after_commit instead of after_save. #2991 [Brian Durand] - * Serialized attributes are not converted to YAML if they are any of the formats that can be serialized to XML (like Hash, Array and Strings). [José Valim] * Destroy uses optimistic locking. If lock_version on the record you're destroying doesn't match lock_version in the database, a StaleObjectError is raised. #1966 [Curtis Hawthorne] diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec index 44c8fb83e1..5aea992801 100644 --- a/activerecord/activerecord.gemspec +++ b/activerecord/activerecord.gemspec @@ -23,6 +23,6 @@ Gem::Specification.new do |s| s.add_dependency('activesupport', version) s.add_dependency('activemodel', version) - s.add_dependency('arel', '~> 0.3.3') + s.add_dependency('arel', '~> 0.4.0') s.add_dependency('tzinfo', '~> 0.3.16') end diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 284ae6695b..c1e16d08cb 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1756,7 +1756,8 @@ module ActiveRecord end def count_aliases_from_table_joins(name) - quoted_name = join_base.active_record.connection.quote_table_name(name.downcase) + # quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase + quoted_name = join_base.active_record.connection.quote_table_name(name.downcase).downcase join_sql = join_base.table_joins.to_s.downcase join_sql.blank? ? 0 : # Table names @@ -1902,7 +1903,7 @@ module ActiveRecord end def ==(other) - other.is_a?(JoinBase) && + other.class == self.class && other.active_record == active_record && other.table_joins == table_joins end @@ -1973,7 +1974,7 @@ module ActiveRecord end def ==(other) - other.is_a?(JoinAssociation) && + other.class == self.class && other.reflection == reflection && other.parent == parent 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 b9fb452eee..0c87e052c4 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb @@ -122,8 +122,6 @@ module ActiveRecord requires_new = options[:requires_new] || !last_transaction_joinable transaction_open = false - @_current_transaction_records ||= [] - begin if block_given? if requires_new || open_transactions == 0 @@ -134,7 +132,6 @@ module ActiveRecord end increment_open_transactions transaction_open = true - @_current_transaction_records.push([]) end yield end @@ -144,10 +141,8 @@ module ActiveRecord decrement_open_transactions if open_transactions == 0 rollback_db_transaction - rollback_transaction_records(true) else rollback_to_savepoint - rollback_transaction_records(false) end end raise unless database_transaction_rollback.is_a?(ActiveRecord::Rollback) @@ -162,35 +157,20 @@ module ActiveRecord begin if open_transactions == 0 commit_db_transaction - commit_transaction_records else release_savepoint - save_point_records = @_current_transaction_records.pop - unless save_point_records.blank? - @_current_transaction_records.push([]) if @_current_transaction_records.empty? - @_current_transaction_records.last.concat(save_point_records) - end end rescue Exception => database_transaction_rollback if open_transactions == 0 rollback_db_transaction - rollback_transaction_records(true) else rollback_to_savepoint - rollback_transaction_records(false) end raise end end end - # Register a record with the current transaction so that its after_commit and after_rollback callbacks - # can be called. - def add_transaction_record(record) - last_batch = @_current_transaction_records.last - last_batch << record if last_batch - end - # Begins the transaction (and turns off auto-committing). def begin_db_transaction() end @@ -288,42 +268,6 @@ module ActiveRecord limit.to_i end end - - # Send a rollback message to all records after they have been rolled back. If rollback - # is false, only rollback records since the last save point. - def rollback_transaction_records(rollback) #:nodoc - if rollback - records = @_current_transaction_records.flatten - @_current_transaction_records.clear - else - records = @_current_transaction_records.pop - end - - unless records.blank? - records.uniq.each do |record| - begin - record.rolledback!(rollback) - rescue Exception => e - record.logger.error(e) if record.respond_to?(:logger) - end - end - end - end - - # Send a commit message to all records after they have been committed. - def commit_transaction_records #:nodoc - records = @_current_transaction_records.flatten - @_current_transaction_records.clear - unless records.blank? - records.uniq.each do |record| - begin - record.committed! - rescue Exception => e - record.logger.error(e) if record.respond_to?(:logger) - end - end - end - end end end end diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index e8a45bb3c6..deb62e3802 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -34,6 +34,10 @@ module ActiveRecord end def binary_to_string(value) + if value.respond_to?(:force_encoding) && value.encoding != Encoding::ASCII_8BIT + value = value.force_encoding(Encoding::ASCII_8BIT) + end + value.gsub(/%00|%25/n) do |b| case b when "%00" then "\0" diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 4e62187449..99c914d7fc 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -356,13 +356,16 @@ module ActiveRecord end def references_eager_loaded_tables? - joined_tables = (tables_in_string(arel.joins(arel)) + [table.name, table.table_alias]).compact.uniq + # always convert table names to downcase as in Oracle quoted table names are in uppercase + joined_tables = (tables_in_string(arel.joins(arel)) + [table.name, table.table_alias]).compact.map(&:downcase).uniq (tables_in_string(to_sql) - joined_tables).any? end def tables_in_string(string) return [] if string.blank? - string.scan(/([a-zA-Z_][\.\w]+).?\./).flatten.uniq + # always convert table names to downcase as in Oracle quoted table names are in uppercase + # ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries + string.scan(/([a-zA-Z_][\.\w]+).?\./).flatten.map(&:downcase).uniq - ['raw_sql_'] end end diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb index 5a8e2ce880..3f2c1911e7 100644 --- a/activerecord/lib/active_record/transactions.rb +++ b/activerecord/lib/active_record/transactions.rb @@ -8,11 +8,6 @@ module ActiveRecord class TransactionError < ActiveRecordError # :nodoc: end - included do - define_model_callbacks :commit, :commit_on_update, :commit_on_create, :commit_on_destroy, :only => :after - define_model_callbacks :rollback, :rollback_on_update, :rollback_on_create, :rollback_on_destroy - end - # Transactions are protective blocks where SQL statements are only permanent # if they can all succeed as one atomic action. The classic example is a # transfer between two accounts where you can only have a deposit if the @@ -163,21 +158,6 @@ module ActiveRecord # http://dev.mysql.com/doc/refman/5.0/en/savepoints.html # for more information about savepoints. # - # === Callbacks - # - # There are two types of callbacks associated with committing and rolling back transactions: - # +after_commit+ and +after_rollback+. - # - # +after_commit+ callbacks are called on every record saved or destroyed within a - # transaction immediately after the transaction is committed. +after_rollback+ callbacks - # are called on every record saved or destroyed within a transaction immediately after the - # transaction or savepoint is rolled back. - # - # These callbacks are useful for interacting with other systems since you will be guaranteed - # that the callback is only executed when the database is in a permanent state. For example, - # +after_commit+ is a good spot to put in a hook to clearing a cache since clearing it from - # within a transaction could trigger the cache to be regenerated before the database is updated. - # # === Caveats # # If you're on MySQL, then do not use DDL operations in nested transactions @@ -225,50 +205,19 @@ module ActiveRecord # Reset id and @new_record if the transaction rolls back. def rollback_active_record_state! - remember_transaction_record_state + id_present = has_attribute?(self.class.primary_key) + previous_id = id + previous_new_record = new_record? yield rescue Exception - restore_transaction_record_state - raise - ensure - clear_transaction_record_state - end - - # Call the after_commit callbacks - def committed! #:nodoc: - if transaction_record_state(:new_record) - _run_commit_on_create_callbacks - elsif transaction_record_state(:destroyed) - _run_commit_on_destroy_callbacks + @new_record = previous_new_record + if id_present + self.id = previous_id else - _run_commit_on_update_callbacks - end - _run_commit_callbacks - ensure - clear_transaction_record_state - end - - # Call the after rollback callbacks. The restore_state argument indicates if the record - # state should be rolled back to the beginning or just to the last savepoint. - def rolledback!(force_restore_state = false) #:nodoc: - if transaction_record_state(:new_record) - _run_rollback_on_create_callbacks - elsif transaction_record_state(:destroyed) - _run_rollback_on_destroy_callbacks - else - _run_rollback_on_update_callbacks - end - _run_rollback_callbacks - ensure - restore_transaction_record_state(force_restore_state) - end - - # Add the record to the current transaction so that the :after_rollback and :after_commit callbacks - # can be called. - def add_to_transaction - if self.class.connection.add_transaction_record(self) - remember_transaction_record_state + @attributes.delete(self.class.primary_key) + @attributes_cache.delete(self.class.primary_key) end + raise end # Executes +method+ within a transaction and captures its return value as a @@ -280,59 +229,10 @@ module ActiveRecord def with_transaction_returning_status status = nil self.class.transaction do - add_to_transaction status = yield raise ActiveRecord::Rollback unless status end status end - - protected - - # Save the new record state and id of a record so it can be restored later if a transaction fails. - def remember_transaction_record_state #:nodoc - @_start_transaction_state ||= {} - unless @_start_transaction_state.include?(:new_record) - @_start_transaction_state[:id] = id if has_attribute?(self.class.primary_key) - @_start_transaction_state[:new_record] = @new_record - end - unless @_start_transaction_state.include?(:destroyed) - @_start_transaction_state[:destroyed] = @new_record - end - @_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 - if defined?(@_start_transaction_state) - @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1 - remove_instance_variable(:@_start_transaction_state) if @_start_transaction_state[:level] < 1 - end - 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 - if defined?(@_start_transaction_state) - @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1 - if @_start_transaction_state[:level] < 1 - restore_state = remove_instance_variable(:@_start_transaction_state) - if restore_state - @new_record = restore_state[:new_record] - @destroyed = restore_state[:destroyed] - if restore_state[:id] - self.id = restore_state[:id] - else - @attributes.delete(self.class.primary_key) - @attributes_cache.delete(self.class.primary_key) - end - end - end - end - end - - # Determine if a record was created or destroyed in a transaction. State should be one of :new_record or :destroyed. - def transaction_record_state(state) #:nodoc - @_start_transaction_state[state] if defined?(@_start_transaction_state) - end end end diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb index bf6b995a37..d18fed0131 100644 --- a/activerecord/lib/active_record/version.rb +++ b/activerecord/lib/active_record/version.rb @@ -3,7 +3,7 @@ module ActiveRecord MAJOR = 3 MINOR = 0 TINY = 0 - BUILD = "beta3" + BUILD = "beta4" STRING = [MAJOR, MINOR, TINY, BUILD].join('.') end diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb index 9b28766405..fc08c2178a 100644 --- a/activerecord/test/cases/adapter_test.rb +++ b/activerecord/test/cases/adapter_test.rb @@ -145,22 +145,13 @@ class AdapterTest < ActiveRecord::TestCase def test_add_limit_offset_should_sanitize_sql_injection_for_limit_without_comas sql_inject = "1 select * from schema" - assert_equal " LIMIT 1", @connection.add_limit_offset!("", :limit => sql_inject) - if current_adapter?(:MysqlAdapter) - assert_equal " LIMIT 7, 1", @connection.add_limit_offset!("", :limit => sql_inject, :offset => 7) - else - assert_equal " LIMIT 1 OFFSET 7", @connection.add_limit_offset!("", :limit => sql_inject, :offset => 7) - end + assert_no_match(/schema/, @connection.add_limit_offset!("", :limit=>sql_inject)) + assert_no_match(/schema/, @connection.add_limit_offset!("", :limit=>sql_inject, :offset=>7)) end def test_add_limit_offset_should_sanitize_sql_injection_for_limit_with_comas sql_inject = "1, 7 procedure help()" - if current_adapter?(:MysqlAdapter) - assert_equal " LIMIT 1,7", @connection.add_limit_offset!("", :limit => sql_inject) - assert_equal " LIMIT 7, 1", @connection.add_limit_offset!("", :limit => '1 ; DROP TABLE USERS', :offset => 7) - else - assert_equal " LIMIT 1,7", @connection.add_limit_offset!("", :limit => sql_inject) - assert_equal " LIMIT 1,7 OFFSET 7", @connection.add_limit_offset!("", :limit => sql_inject, :offset => 7) - end + assert_no_match(/procedure/, @connection.add_limit_offset!("", :limit=>sql_inject)) + assert_no_match(/procedure/, @connection.add_limit_offset!("", :limit=>sql_inject, :offset=>7)) end end diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index 9258c987ef..fb1e6e7e70 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -37,6 +37,10 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase if current_adapter?(:MysqlAdapter) assert_no_match(/`firm_with_primary_keys_companies`\.`id`/, sql) assert_match(/`firm_with_primary_keys_companies`\.`name`/, sql) + elsif current_adapter?(:OracleAdapter) + # on Oracle aliases are truncated to 30 characters and are quoted in uppercase + assert_no_match(/"firm_with_primary_keys_compani"\."id"/i, sql) + assert_match(/"firm_with_primary_keys_compani"\."name"/i, sql) else assert_no_match(/"firm_with_primary_keys_companies"\."id"/, sql) assert_match(/"firm_with_primary_keys_companies"\."name"/, sql) diff --git a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb index fe558f9d3b..7a4a33d177 100644 --- a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb +++ b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb @@ -7,9 +7,10 @@ require 'models/categorization' require 'models/company' require 'models/topic' require 'models/reply' +require 'models/person' class CascadedEagerLoadingTest < ActiveRecord::TestCase - fixtures :authors, :mixins, :companies, :posts, :topics, :accounts, :comments, :categorizations + fixtures :authors, :mixins, :companies, :posts, :topics, :accounts, :comments, :categorizations, :people def test_eager_association_loading_with_cascaded_two_levels authors = Author.find(:all, :include=>{:posts=>:comments}, :order=>"authors.id") @@ -38,6 +39,13 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase assert_equal 9, assert_no_queries { authors[0].comments.size } end + def test_eager_association_loading_grafts_stashed_associations_to_correct_parent + assert_nothing_raised do + Person.eager_load(:primary_contact => :primary_contact).where('primary_contacts_people_2.first_name = ?', 'Susan').all + end + assert_equal people(:michael), Person.eager_load(:primary_contact => :primary_contact).where('primary_contacts_people_2.first_name = ?', 'Susan').first + end + def test_eager_association_loading_with_cascaded_two_levels_with_two_has_many_associations authors = Author.find(:all, :include=>{:posts=>[:comments, :categorizations]}, :order=>"authors.id") assert_equal 2, authors.size diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 8e5bc56008..45c7498013 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -511,7 +511,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase end def test_deleting_updates_counter_cache - topic = Topic.first + topic = Topic.first(:order => "id ASC") assert_equal topic.replies.to_a.size, topic.replies_count topic.replies.delete(topic.replies.first) diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 1d883f7ea8..36c572b5e7 100755 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -430,14 +430,14 @@ class BasicsTest < ActiveRecord::TestCase end def test_preserving_date_objects - if current_adapter?(:SybaseAdapter, :OracleAdapter) + if current_adapter?(:SybaseAdapter) # Sybase ctlib does not (yet?) support the date type; use datetime instead. - # Oracle treats all dates/times as Time. assert_kind_of( Time, Topic.find(1).last_read, "The last_read attribute should be of the Time class" ) else + # Oracle enhanced adapter allows to define Date attributes in model class (see topic.rb) assert_kind_of( Date, Topic.find(1).last_read, "The last_read attribute should be of the Date class" @@ -2125,10 +2125,11 @@ class BasicsTest < ActiveRecord::TestCase assert_equal "integer", xml.elements["//parent-id"].attributes['type'] assert_equal "true", xml.elements["//parent-id"].attributes['nil'] - if current_adapter?(:SybaseAdapter, :OracleAdapter) + if current_adapter?(:SybaseAdapter) assert_equal last_read_in_current_timezone, xml.elements["//last-read"].text assert_equal "datetime" , xml.elements["//last-read"].attributes['type'] else + # Oracle enhanced adapter allows to define Date attributes in model class (see topic.rb) assert_equal "2004-04-15", xml.elements["//last-read"].text assert_equal "date" , xml.elements["//last-read"].attributes['type'] end diff --git a/activerecord/test/cases/date_time_test.rb b/activerecord/test/cases/date_time_test.rb index 36e1caa0b6..a8b4b7a096 100644 --- a/activerecord/test/cases/date_time_test.rb +++ b/activerecord/test/cases/date_time_test.rb @@ -5,7 +5,9 @@ require 'models/task' class DateTimeTest < ActiveRecord::TestCase def test_saves_both_date_and_time time_values = [1807, 2, 10, 15, 30, 45] - now = DateTime.civil(*time_values) + # create DateTime value with local time zone offset + local_offset = Rational(Time.local_time(*time_values).utc_offset, 86400) + now = DateTime.civil(*(time_values + [local_offset])) task = Task.new task.starting = now diff --git a/activerecord/test/cases/json_serialization_test.rb b/activerecord/test/cases/json_serialization_test.rb index a3145d2c04..c275557da8 100644 --- a/activerecord/test/cases/json_serialization_test.rb +++ b/activerecord/test/cases/json_serialization_test.rb @@ -8,7 +8,7 @@ require 'models/comment' class JsonSerializationTest < ActiveRecord::TestCase class NamespacedContact < Contact - column :name, :string + column :name, :string end def setup @@ -23,16 +23,12 @@ class JsonSerializationTest < ActiveRecord::TestCase end def test_should_demodulize_root_in_json - NamespacedContact.include_root_in_json = true @contact = NamespacedContact.new :name => 'whatever' json = @contact.to_json assert_match %r{^\{"namespaced_contact":\{}, json - ensure - NamespacedContact.include_root_in_json = false end def test_should_include_root_in_json - Contact.include_root_in_json = true json = @contact.to_json assert_match %r{^\{"contact":\{}, json @@ -41,8 +37,6 @@ class JsonSerializationTest < ActiveRecord::TestCase assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))})) assert_match %r{"awesome":true}, json assert_match %r{"preferences":\{"shows":"anime"\}}, json - ensure - Contact.include_root_in_json = false end def test_should_encode_all_encodable_attributes @@ -170,15 +164,19 @@ class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase end def test_should_allow_only_option_for_list_of_authors + ActiveRecord::Base.include_root_in_json = false authors = [@david, @mary] - assert_equal %([{"name":"David"},{"name":"Mary"}]), ActiveSupport::JSON.encode(authors, :only => :name) + ensure + ActiveRecord::Base.include_root_in_json = true end def test_should_allow_except_option_for_list_of_authors + ActiveRecord::Base.include_root_in_json = false authors = [@david, @mary] - assert_equal %([{"id":1},{"id":2}]), ActiveSupport::JSON.encode(authors, :except => [:name, :author_address_id, :author_address_extra_id]) + ensure + ActiveRecord::Base.include_root_in_json = true end def test_should_allow_includes_for_list_of_authors @@ -201,7 +199,6 @@ class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase 1 => @david, 2 => @mary } - - assert_equal %({"1":{"name":"David"}}), ActiveSupport::JSON.encode(authors_hash, :only => [1, :name]) + assert_equal %({"1":{"author":{"name":"David"}}}), ActiveSupport::JSON.encode(authors_hash, :only => [1, :name]) end end diff --git a/activerecord/test/cases/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb index 6ba84fa57b..1f544b4211 100644 --- a/activerecord/test/cases/log_subscriber_test.rb +++ b/activerecord/test/cases/log_subscriber_test.rb @@ -27,7 +27,7 @@ class LogSubscriberTest < ActiveSupport::TestCase wait assert_equal 1, @logger.logged(:debug).size assert_match(/Developer Load/, @logger.logged(:debug).last) - assert_match(/SELECT .*?FROM .?developers.?/, @logger.logged(:debug).last) + assert_match(/SELECT .*?FROM .?developers.?/i, @logger.logged(:debug).last) end def test_cached_queries @@ -38,6 +38,6 @@ class LogSubscriberTest < ActiveSupport::TestCase wait assert_equal 2, @logger.logged(:debug).size assert_match(/CACHE/, @logger.logged(:debug).last) - assert_match(/SELECT .*?FROM .?developers.?/, @logger.logged(:debug).last) + assert_match(/SELECT .*?FROM .?developers.?/i, @logger.logged(:debug).last) end end diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index b5fa258f7b..1edec66c25 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -30,13 +30,14 @@ if ActiveRecord::Base.connection.supports_migrations? conn = ActiveRecord::Base.connection conn.drop_table(ActiveRecord::Migrator.schema_migrations_table_name) if conn.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name) - ActiveRecord::Base.table_name_prefix = 'foo_' - ActiveRecord::Base.table_name_suffix = '_bar' + # Use shorter prefix and suffix as in Oracle database identifier cannot be larger than 30 characters + ActiveRecord::Base.table_name_prefix = 'p_' + ActiveRecord::Base.table_name_suffix = '_s' conn.drop_table(ActiveRecord::Migrator.schema_migrations_table_name) if conn.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name) conn.initialize_schema_migrations_table - assert_equal "foo_unique_schema_migrations_bar", conn.indexes(ActiveRecord::Migrator.schema_migrations_table_name)[0][:name] + assert_equal "p_unique_schema_migrations_s", conn.indexes(ActiveRecord::Migrator.schema_migrations_table_name)[0][:name] ensure ActiveRecord::Base.table_name_prefix = "" ActiveRecord::Base.table_name_suffix = "" @@ -83,13 +84,17 @@ if ActiveRecord::Base.connection.supports_migrations? # Orcl nds shrt indx nms. Sybs 2. # OpenBase does not have named indexes. You must specify a single column name - unless current_adapter?(:OracleAdapter, :SybaseAdapter, :OpenBaseAdapter) + unless current_adapter?(:SybaseAdapter, :OpenBaseAdapter) assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) } assert_nothing_raised { Person.connection.remove_index("people", :column => ["last_name", "first_name"]) } - assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) } - assert_nothing_raised { Person.connection.remove_index("people", :name => "index_people_on_last_name_and_first_name") } - assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) } - assert_nothing_raised { Person.connection.remove_index("people", "last_name_and_first_name") } + # Oracle adapter cannot have specified index name larger than 30 characters + # Oracle adapter is shortening index name when just column list is given + unless current_adapter?(:OracleAdapter) + assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) } + assert_nothing_raised { Person.connection.remove_index("people", :name => "index_people_on_last_name_and_first_name") } + assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) } + assert_nothing_raised { Person.connection.remove_index("people", "last_name_and_first_name") } + end assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) } assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) } assert_nothing_raised { Person.connection.add_index("people", ["last_name"], :length => 10) } @@ -736,13 +741,7 @@ if ActiveRecord::Base.connection.supports_migrations? table.column :hat_size, :integer table.column :hat_style, :string, :limit => 100 end - # Oracle index names should be 30 or less characters - if current_adapter?(:OracleAdapter) - ActiveRecord::Base.connection.add_index "hats", ["hat_style", "hat_size"], :unique => true, - :name => 'index_hats_on_hat_style_size' - else - ActiveRecord::Base.connection.add_index "hats", ["hat_style", "hat_size"], :unique => true - end + ActiveRecord::Base.connection.add_index "hats", ["hat_style", "hat_size"], :unique => true assert_nothing_raised { Person.connection.remove_column("hats", "hat_size") } ensure @@ -1375,8 +1374,8 @@ if ActiveRecord::Base.connection.supports_migrations? return unless current_adapter? :OracleAdapter # table name is 29 chars, the standard sequence name will - # be 33 chars and fail - assert_raise(ActiveRecord::StatementInvalid) do + # be 33 chars and should be shortened + assert_nothing_raised do begin Person.connection.create_table :table_with_name_thats_just_ok do |t| t.column :foo, :string, :null => false diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 4097c5119e..43519db976 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -114,7 +114,7 @@ class RelationTest < ActiveRecord::TestCase def test_select_with_block even_ids = Developer.scoped.select {|d| d.id % 2 == 0 }.map(&:id) - assert_equal [2, 4, 6, 8, 10], even_ids + assert_equal [2, 4, 6, 8, 10], even_ids.sort end def test_finding_with_hash_conditions_on_joined_table diff --git a/activerecord/test/cases/transaction_callbacks_test.rb b/activerecord/test/cases/transaction_callbacks_test.rb deleted file mode 100644 index ebc16653cb..0000000000 --- a/activerecord/test/cases/transaction_callbacks_test.rb +++ /dev/null @@ -1,240 +0,0 @@ -require "cases/helper" -require 'models/topic' -require 'models/reply' - -class TransactionCallbacksTest < ActiveRecord::TestCase - self.use_transactional_fixtures = false - fixtures :topics - - class TopicWithCallbacks < ActiveRecord::Base - set_table_name :topics - - after_commit{|record| record.send(:do_after_commit, nil)} - after_commit(:on => :create){|record| record.send(:do_after_commit, :create)} - after_commit(:on => :update){|record| record.send(:do_after_commit, :update)} - after_commit(:on => :destroy){|record| record.send(:do_after_commit, :destroy)} - after_rollback{|record| record.send(:do_after_rollback, nil)} - after_rollback(:on => :create){|record| record.send(:do_after_rollback, :create)} - after_rollback(:on => :update){|record| record.send(:do_after_rollback, :update)} - after_rollback(:on => :destroy){|record| record.send(:do_after_rollback, :destroy)} - - def history - @history ||= [] - end - - def after_commit_block(on = nil, &block) - @after_commit ||= {} - @after_commit[on] ||= [] - @after_commit[on] << block - end - - def after_rollback_block(on = nil, &block) - @after_rollback ||= {} - @after_rollback[on] ||= [] - @after_rollback[on] << block - end - - def do_after_commit(on) - blocks = @after_commit[on] if defined?(@after_commit) - blocks.each{|b| b.call(self)} if blocks - end - - def do_after_rollback(on) - blocks = @after_rollback[on] if defined?(@after_rollback) - blocks.each{|b| b.call(self)} if blocks - end - end - - def setup - @first, @second = TopicWithCallbacks.find(1, 3).sort_by { |t| t.id } - end - - def test_call_after_commit_after_transaction_commits - @first.after_commit_block{|r| r.history << :after_commit} - @first.after_rollback_block{|r| r.history << :after_rollback} - - @first.save! - assert_equal [:after_commit], @first.history - end - - def test_only_call_after_commit_on_update_after_transaction_commits_for_existing_record - @first.after_commit_block(:create){|r| r.history << :commit_on_create} - @first.after_commit_block(:update){|r| r.history << :commit_on_update} - @first.after_commit_block(:destroy){|r| r.history << :commit_on_destroy} - @first.after_rollback_block(:create){|r| r.history << :rollback_on_create} - @first.after_rollback_block(:update){|r| r.history << :rollback_on_update} - @first.after_rollback_block(:destroy){|r| r.history << :rollback_on_destroy} - - @first.save! - assert_equal [:commit_on_update], @first.history - end - - def test_only_call_after_commit_on_destroy_after_transaction_commits_for_destroyed_record - @first.after_commit_block(:create){|r| r.history << :commit_on_create} - @first.after_commit_block(:update){|r| r.history << :commit_on_update} - @first.after_commit_block(:destroy){|r| r.history << :commit_on_destroy} - @first.after_rollback_block(:create){|r| r.history << :rollback_on_create} - @first.after_rollback_block(:update){|r| r.history << :rollback_on_update} - @first.after_rollback_block(:destroy){|r| r.history << :rollback_on_destroy} - - @first.destroy - assert_equal [:commit_on_destroy], @first.history - end - - def test_only_call_after_commit_on_create_after_transaction_commits_for_new_record - @new_record = TopicWithCallbacks.new(:title => "New topic", :written_on => Date.today) - @new_record.after_commit_block(:create){|r| r.history << :commit_on_create} - @new_record.after_commit_block(:update){|r| r.history << :commit_on_update} - @new_record.after_commit_block(:destroy){|r| r.history << :commit_on_destroy} - @new_record.after_rollback_block(:create){|r| r.history << :rollback_on_create} - @new_record.after_rollback_block(:update){|r| r.history << :rollback_on_update} - @new_record.after_rollback_block(:destroy){|r| r.history << :rollback_on_destroy} - - @new_record.save! - assert_equal [:commit_on_create], @new_record.history - end - - def test_call_after_rollback_after_transaction_rollsback - @first.after_commit_block{|r| r.history << :after_commit} - @first.after_rollback_block{|r| r.history << :after_rollback} - - Topic.transaction do - @first.save! - raise ActiveRecord::Rollback - end - - assert_equal [:after_rollback], @first.history - end - - def test_only_call_after_rollback_on_update_after_transaction_rollsback_for_existing_record - @first.after_commit_block(:create){|r| r.history << :commit_on_create} - @first.after_commit_block(:update){|r| r.history << :commit_on_update} - @first.after_commit_block(:destroy){|r| r.history << :commit_on_destroy} - @first.after_rollback_block(:create){|r| r.history << :rollback_on_create} - @first.after_rollback_block(:update){|r| r.history << :rollback_on_update} - @first.after_rollback_block(:destroy){|r| r.history << :rollback_on_destroy} - - Topic.transaction do - @first.save! - raise ActiveRecord::Rollback - end - - assert_equal [:rollback_on_update], @first.history - end - - def test_only_call_after_rollback_on_destroy_after_transaction_rollsback_for_destroyed_record - @first.after_commit_block(:create){|r| r.history << :commit_on_create} - @first.after_commit_block(:update){|r| r.history << :commit_on_update} - @first.after_commit_block(:destroy){|r| r.history << :commit_on_update} - @first.after_rollback_block(:create){|r| r.history << :rollback_on_create} - @first.after_rollback_block(:update){|r| r.history << :rollback_on_update} - @first.after_rollback_block(:destroy){|r| r.history << :rollback_on_destroy} - - Topic.transaction do - @first.destroy - raise ActiveRecord::Rollback - end - - assert_equal [:rollback_on_destroy], @first.history - end - - def test_only_call_after_rollback_on_create_after_transaction_rollsback_for_new_record - @new_record = TopicWithCallbacks.new(:title => "New topic", :written_on => Date.today) - @new_record.after_commit_block(:create){|r| r.history << :commit_on_create} - @new_record.after_commit_block(:update){|r| r.history << :commit_on_update} - @new_record.after_commit_block(:destroy){|r| r.history << :commit_on_destroy} - @new_record.after_rollback_block(:create){|r| r.history << :rollback_on_create} - @new_record.after_rollback_block(:update){|r| r.history << :rollback_on_update} - @new_record.after_rollback_block(:destroy){|r| r.history << :rollback_on_destroy} - - Topic.transaction do - @new_record.save! - raise ActiveRecord::Rollback - end - - assert_equal [:rollback_on_create], @new_record.history - end - - def test_call_after_rollback_when_commit_fails - @first.connection.class.send(:alias_method, :real_method_commit_db_transaction, :commit_db_transaction) - begin - @first.connection.class.class_eval do - def commit_db_transaction; raise "boom!"; end - end - - @first.after_commit_block{|r| r.history << :after_commit} - @first.after_rollback_block{|r| r.history << :after_rollback} - - assert !@first.save rescue nil - assert_equal [:after_rollback], @first.history - ensure - @first.connection.class.send(:remove_method, :commit_db_transaction) - @first.connection.class.send(:alias_method, :commit_db_transaction, :real_method_commit_db_transaction) - end - end - - def test_only_call_after_rollback_on_records_rolled_back_to_a_savepoint - def @first.rollbacks(i=0); @rollbacks ||= 0; @rollbacks += i if i; end - def @first.commits(i=0); @commits ||= 0; @commits += i if i; end - @first.after_rollback_block{|r| r.rollbacks(1)} - @first.after_commit_block{|r| r.commits(1)} - - def @second.rollbacks(i=0); @rollbacks ||= 0; @rollbacks += i if i; end - def @second.commits(i=0); @commits ||= 0; @commits += i if i; end - @second.after_rollback_block{|r| r.rollbacks(1)} - @second.after_commit_block{|r| r.commits(1)} - - Topic.transaction do - @first.save! - Topic.transaction(:requires_new => true) do - @second.save! - raise ActiveRecord::Rollback - end - end - - assert_equal 1, @first.commits - assert_equal 0, @first.rollbacks - assert_equal 0, @second.commits - assert_equal 1, @second.rollbacks - end - - def test_only_call_after_rollback_on_records_rolled_back_to_a_savepoint_when_release_savepoint_fails - def @first.rollbacks(i=0); @rollbacks ||= 0; @rollbacks += i if i; end - def @first.commits(i=0); @commits ||= 0; @commits += i if i; end - - @first.after_rollback_block{|r| r.rollbacks(1)} - @first.after_commit_block{|r| r.commits(1)} - - Topic.transaction do - @first.save - Topic.transaction(:requires_new => true) do - @first.save! - raise ActiveRecord::Rollback - end - Topic.transaction(:requires_new => true) do - @first.save! - raise ActiveRecord::Rollback - end - end - - assert_equal 1, @first.commits - assert_equal 2, @first.rollbacks - end - - def test_after_transaction_callbacks_should_not_raise_errors - def @first.last_after_transaction_error=(e); @last_transaction_error = e; end - def @first.last_after_transaction_error; @last_transaction_error; end - @first.after_commit_block{|r| r.last_after_transaction_error = :commit; raise "fail!";} - @first.after_rollback_block{|r| r.last_after_transaction_error = :rollback; raise "fail!";} - - @first.save! - assert_equal :commit, @first.last_after_transaction_error - - Topic.transaction do - @first.save! - raise ActiveRecord::Rollback - end - - assert_equal :rollback, @first.last_after_transaction_error - end -end diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb index 958a4e4f94..00f3b527d7 100644 --- a/activerecord/test/cases/transactions_test.rb +++ b/activerecord/test/cases/transactions_test.rb @@ -320,33 +320,6 @@ class TransactionTest < ActiveRecord::TestCase end end - def test_restore_active_record_state_for_all_records_in_a_transaction - topic_1 = Topic.new(:title => 'test_1') - topic_2 = Topic.new(:title => 'test_2') - Topic.transaction do - assert topic_1.save - assert topic_2.save - @first.save - @second.destroy - assert_equal false, topic_1.new_record? - assert_not_nil topic_1.id - assert_equal false, topic_2.new_record? - assert_not_nil topic_2.id - assert_equal false, @first.new_record? - assert_not_nil @first.id - assert_equal true, @second.destroyed? - raise ActiveRecord::Rollback - end - - assert_equal true, topic_1.new_record? - assert_nil topic_1.id - assert_equal true, topic_2.new_record? - assert_nil topic_2.id - assert_equal false, @first.new_record? - assert_not_nil @first.id - assert_equal false, @second.destroyed? - end - if current_adapter?(:PostgreSQLAdapter) && defined?(PGconn::PQTRANS_IDLE) def test_outside_transaction_works assert Topic.connection.outside_transaction? @@ -409,12 +382,6 @@ class TransactionTest < ActiveRecord::TestCase end private - def define_callback_method(callback_method) - define_method(callback_method) do - self.history << [callback_method, :method] - end - end - def add_exception_raising_after_save_callback_to_topic Topic.class_eval <<-eoruby, __FILE__, __LINE__ + 1 remove_method(:after_save_for_transaction) diff --git a/activerecord/test/connections/native_oracle/connection.rb b/activerecord/test/connections/native_oracle/connection.rb index c8183dc0fb..bb4040058f 100644 --- a/activerecord/test/connections/native_oracle/connection.rb +++ b/activerecord/test/connections/native_oracle/connection.rb @@ -53,7 +53,7 @@ Course.establish_connection 'arunit2' # for assert_queries test helper ActiveRecord::Base.connection.class.class_eval do - IGNORED_SELECT_SQL = [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/, /^\s*select .* from all_tab_columns/im] + IGNORED_SELECT_SQL = [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from ((all|user)_tab_columns|(all|user)_triggers|(all|user)_constraints)/im] def select_with_query_record(sql, name = nil, return_column_names = false) $queries_executed ||= [] diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb index 91fc7c9416..617f01b47d 100644 --- a/activerecord/test/models/topic.rb +++ b/activerecord/test/models/topic.rb @@ -43,6 +43,12 @@ class Topic < ActiveRecord::Base before_create :default_written_on before_destroy :destroy_children + # Explicitly define as :date column so that returned Oracle DATE values would be typecasted to Date and not Time. + # Some tests depend on assumption that this attribute will have Date values. + if current_adapter?(:OracleEnhancedAdapter) + set_date_columns :last_read + end + def parent Topic.find(parent_id) end |