diff options
Diffstat (limited to 'activerecord')
19 files changed, 205 insertions, 101 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index b5dd19988d..0f99f907e8 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,11 @@ +* Correctly dump `serial` and `bigserial`. + + *Ryuta Kamizono* + +* Fix default `format` value in `ActiveRecord::Tasks::DatabaseTasks#schema_file`. + + *James Cox* + * Dont enroll records in the transaction if they dont have commit callbacks. That was causing a memory grow problem when creating a lot of records inside a transaction. diff --git a/activerecord/Rakefile b/activerecord/Rakefile index 976b559da9..f1facac21b 100644 --- a/activerecord/Rakefile +++ b/activerecord/Rakefile @@ -51,7 +51,7 @@ end t.libs << 'test' t.test_files = (Dir.glob( "test/cases/**/*_test.rb" ).reject { |x| x =~ /\/adapters\// - } + Dir.glob("test/cases/adapters/#{adapter_short}/**/*_test.rb")).sort + } + Dir.glob("test/cases/adapters/#{adapter_short}/**/*_test.rb")) t.warning = true t.verbose = true diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index e11c9490b7..685c3a5f17 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -976,6 +976,9 @@ module ActiveRecord # Equivalent to +delete_all+. The difference is that returns +self+, instead # of an array with the deleted objects, so methods can be chained. See # +delete_all+ for more information. + # Note that because +delete_all+ removes records by directly + # running an SQL query into the database, the +updated_at+ column of + # the object is not changed. def clear delete_all self diff --git a/activerecord/lib/active_record/associations/through_association.rb b/activerecord/lib/active_record/associations/through_association.rb index 3ce9cffdbc..af1bce523c 100644 --- a/activerecord/lib/active_record/associations/through_association.rb +++ b/activerecord/lib/active_record/associations/through_association.rb @@ -33,7 +33,7 @@ module ActiveRecord # Construct attributes for :through pointing to owner and associate. This is used by the # methods which create and delete records on the association. # - # We only support indirectly modifying through associations which has a belongs_to source. + # We only support indirectly modifying through associations which have a belongs_to source. # This is the "has_many :tags, through: :taggings" situation, where the join model # typically has a belongs_to on both side. In other words, associations which could also # be represented as has_and_belongs_to_many associations. diff --git a/activerecord/lib/active_record/attributes.rb b/activerecord/lib/active_record/attributes.rb index c8979a60d7..50339b6f69 100644 --- a/activerecord/lib/active_record/attributes.rb +++ b/activerecord/lib/active_record/attributes.rb @@ -28,15 +28,18 @@ module ActiveRecord # information about providing custom type objects. # # ==== Options + # # The following options are accepted: # # +default+ The default value to use when no value is provided. If this option # is not passed, the previous default value (if any) will be used. # Otherwise, the default will be +nil+. # - # +array+ (PG only) specifies that the type should be an array (see the examples below). + # +array+ (PG only) specifies that the type should be an array (see the + # examples below). # - # +range+ (PG only) specifies that the type should be a range (see the examples below). + # +range+ (PG only) specifies that the type should be a range (see the + # examples below). # # ==== Examples # @@ -101,12 +104,11 @@ module ActiveRecord # ==== Creating Custom Types # # Users may also define their own custom types, as long as they respond - # to the methods defined on the value type. The method - # +deserialize+ or +cast+ will be called on - # your type object, with raw input from the database or from your - # controllers. See ActiveRecord::Type::Value for the expected API. It is - # recommended that your type objects inherit from an existing type, or - # from ActiveRecord::Type::Value + # to the methods defined on the value type. The method +deserialize+ or + # +cast+ will be called on your type object, with raw input from the + # database or from your controllers. See ActiveRecord::Type::Value for the + # expected API. It is recommended that your type objects inherit from an + # existing type, or from ActiveRecord::Type::Value # # class MoneyType < ActiveRecord::Type::Integer # def cast(value) @@ -150,7 +152,7 @@ module ActiveRecord # end # # # value will be the result of +deserialize+ or - # # +cast+. Assumed to be in instance of +Money+ in + # # +cast+. Assumed to be an instance of +Money+ in # # this case. # def serialize(value) # value_in_bitcoins = @currency_converter.convert_to_bitcoins(value) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb index af7ef7cbaa..999cb0ec5a 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb @@ -24,7 +24,7 @@ module ActiveRecord def prepare_column_options(column) spec = {} spec[:name] = column.name.inspect - spec[:type] = column.type.to_s + spec[:type] = schema_type(column) spec[:null] = 'false' unless column.null limit = column.limit || native_database_types[column.type][:limit] @@ -45,6 +45,10 @@ module ActiveRecord private + def schema_type(column) + column.type.to_s + end + def schema_default(column) type = lookup_cast_type_from_column(column) default = type.deserialize(column.default) diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb index c084431588..9625fcf9b8 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -86,8 +86,8 @@ module ActiveRecord def column_spec_for_primary_key(column) spec = {} if column.auto_increment? - return unless column.limit == 8 - spec[:id] = ':bigint' + spec[:id] = ':bigint' if column.bigint? + return if spec.empty? else spec[:id] = column.type.inspect spec.merge!(prepare_column_options(column).delete_if { |key, _| [:name, :type, :null].include?(key) }) diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb index fa5ed07b8a..a67127bd71 100644 --- a/activerecord/lib/active_record/connection_adapters/column.rb +++ b/activerecord/lib/active_record/connection_adapters/column.rb @@ -31,7 +31,11 @@ module ActiveRecord end def has_default? - !default.nil? + !default.nil? || default_function + end + + def bigint? + /bigint/ === sql_type end # Returns the human name of the column name. diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/column.rb b/activerecord/lib/active_record/connection_adapters/postgresql/column.rb index 0eb4fb468c..be13ead120 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/column.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/column.rb @@ -6,7 +6,9 @@ module ActiveRecord alias :array? :array def serial? - default_function && default_function =~ /\Anextval\(.*\)\z/ + return unless default_function + + %r{\Anextval\('(?<table_name>.+)_#{name}_seq'::regclass\)\z} === default_function end end end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 6d25b53b21..92f470ae70 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -128,7 +128,7 @@ module ActiveRecord def column_spec_for_primary_key(column) spec = {} if column.serial? - return unless column.sql_type == 'bigint' + return unless column.bigint? spec[:id] = ':bigserial' elsif column.type == :uuid spec[:id] = ':uuid' @@ -145,7 +145,6 @@ module ActiveRecord def prepare_column_options(column) # :nodoc: spec = super spec[:array] = 'true' if column.array? - spec[:default] = "\"#{column.default_function}\"" if column.default_function spec end @@ -154,6 +153,26 @@ module ActiveRecord super + [:array] end + def schema_type(column) + return super unless column.serial? + + if column.bigint? + 'bigserial' + else + 'serial' + end + end + private :schema_type + + def schema_default(column) + if column.default_function + column.default_function.inspect unless column.serial? + else + super + end + end + private :schema_default + # Returns +true+, since this connection adapter supports prepared statement # caching. def supports_statement_cache? diff --git a/activerecord/lib/active_record/type/value.rb b/activerecord/lib/active_record/type/value.rb index fc3ef5e83b..6b9d147ecc 100644 --- a/activerecord/lib/active_record/type/value.rb +++ b/activerecord/lib/active_record/type/value.rb @@ -12,7 +12,7 @@ module ActiveRecord def type # :nodoc: end - # Convert a value from database input to the appropriate ruby type. The + # Converts a value from database input to the appropriate ruby type. The # return value of this method will be returned from # ActiveRecord::AttributeMethods::Read#read_attribute. The default # implementation just calls Value#cast. @@ -36,7 +36,7 @@ module ActiveRecord cast_value(value) unless value.nil? end - # Cast a value from the ruby type to a type that the database knows how + # Casts a value from the ruby type to a type that the database knows how # to understand. The returned value from this method should be a # +String+, +Numeric+, +Date+, +Time+, +Symbol+, +true+, +false+, or # +nil+. @@ -44,7 +44,7 @@ module ActiveRecord value end - # Type cast a value for schema dumping. This method is private, as we are + # Type casts a value for schema dumping. This method is private, as we are # hoping to remove it entirely. def type_cast_for_schema(value) # :nodoc: value.inspect diff --git a/activerecord/test/cases/adapters/postgresql/serial_test.rb b/activerecord/test/cases/adapters/postgresql/serial_test.rb new file mode 100644 index 0000000000..458a8dae6c --- /dev/null +++ b/activerecord/test/cases/adapters/postgresql/serial_test.rb @@ -0,0 +1,60 @@ +require "cases/helper" +require 'support/schema_dumping_helper' + +class PostgresqlSerialTest < ActiveRecord::TestCase + include SchemaDumpingHelper + + class PostgresqlSerial < ActiveRecord::Base; end + + setup do + @connection = ActiveRecord::Base.connection + @connection.create_table "postgresql_serials", force: true do |t| + t.serial :seq + end + end + + teardown do + @connection.drop_table "postgresql_serials", if_exists: true + end + + def test_serial_column + column = PostgresqlSerial.columns_hash["seq"] + assert_equal :integer, column.type + assert_equal "integer", column.sql_type + assert column.serial? + end + + def test_schema_dump_with_shorthand + output = dump_table_schema "postgresql_serials" + assert_match %r{t\.serial\s+"seq"}, output + end +end + +class PostgresqlBigSerialTest < ActiveRecord::TestCase + include SchemaDumpingHelper + + class PostgresqlBigSerial < ActiveRecord::Base; end + + setup do + @connection = ActiveRecord::Base.connection + @connection.create_table "postgresql_big_serials", force: true do |t| + t.bigserial :seq + end + end + + teardown do + @connection.drop_table "postgresql_big_serials", if_exists: true + end + + def test_bigserial_column + column = PostgresqlBigSerial.columns_hash["seq"] + assert_equal :integer, column.type + assert_equal "bigint", column.sql_type + assert column.serial? + end + + def test_schema_dump_with_shorthand + output = dump_table_schema "postgresql_big_serials" + assert_match %r{t\.bigserial\s+"seq"}, output + end +end diff --git a/activerecord/test/cases/adapters/postgresql/uuid_test.rb b/activerecord/test/cases/adapters/postgresql/uuid_test.rb index 1219e197ab..e9379a1019 100644 --- a/activerecord/test/cases/adapters/postgresql/uuid_test.rb +++ b/activerecord/test/cases/adapters/postgresql/uuid_test.rb @@ -135,26 +135,6 @@ class PostgresqlUUIDTest < ActiveRecord::TestCase end end -class PostgresqlLargeKeysTest < ActiveRecord::TestCase - include PostgresqlUUIDHelper - include SchemaDumpingHelper - - def setup - connection.create_table('big_serials', id: :bigserial) do |t| - t.string 'name' - end - end - - def test_omg - schema = dump_table_schema "big_serials" - assert_match "create_table \"big_serials\", id: :bigserial", schema - end - - def teardown - drop_table "big_serials" - end -end - class PostgresqlUUIDGenerationTest < ActiveRecord::TestCase include PostgresqlUUIDHelper include SchemaDumpingHelper diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 993350ebd6..4306738670 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- require "cases/helper" require 'active_support/concurrency/latch' @@ -1008,54 +1009,61 @@ class BasicsTest < ActiveRecord::TestCase end def test_switching_between_table_name + k = Class.new(Joke) + assert_difference("GoodJoke.count") do - Joke.table_name = "cold_jokes" - Joke.create + k.table_name = "cold_jokes" + k.create - Joke.table_name = "funny_jokes" - Joke.create + k.table_name = "funny_jokes" + k.create end end def test_clear_cash_when_setting_table_name - Joke.table_name = "cold_jokes" - before_columns = Joke.columns - before_seq = Joke.sequence_name + original_table_name = Joke.table_name Joke.table_name = "funny_jokes" + before_columns = Joke.columns + before_seq = Joke.sequence_name + + Joke.table_name = "cold_jokes" after_columns = Joke.columns - after_seq = Joke.sequence_name + after_seq = Joke.sequence_name assert_not_equal before_columns, after_columns assert_not_equal before_seq, after_seq unless before_seq.nil? && after_seq.nil? + ensure + Joke.table_name = original_table_name end def test_dont_clear_sequence_name_when_setting_explicitly - Joke.sequence_name = "black_jokes_seq" - Joke.table_name = "cold_jokes" - before_seq = Joke.sequence_name + k = Class.new(Joke) + k.sequence_name = "black_jokes_seq" + k.table_name = "cold_jokes" + before_seq = k.sequence_name - Joke.table_name = "funny_jokes" - after_seq = Joke.sequence_name + k.table_name = "funny_jokes" + after_seq = k.sequence_name assert_equal before_seq, after_seq unless before_seq.nil? && after_seq.nil? - ensure - Joke.reset_sequence_name end def test_dont_clear_inheritance_column_when_setting_explicitly - Joke.inheritance_column = "my_type" - before_inherit = Joke.inheritance_column + k = Class.new(Joke) + k.inheritance_column = "my_type" + before_inherit = k.inheritance_column - Joke.reset_column_information - after_inherit = Joke.inheritance_column + k.reset_column_information + after_inherit = k.inheritance_column assert_equal before_inherit, after_inherit unless before_inherit.blank? && after_inherit.blank? end def test_set_table_name_symbol_converted_to_string - Joke.table_name = :cold_jokes - assert_equal 'cold_jokes', Joke.table_name + k = Class.new(Joke) + k.table_name = :cold_jokes + assert_equal 'cold_jokes', k.table_name end def test_quoted_table_name_after_set_table_name diff --git a/activerecord/test/cases/enum_test.rb b/activerecord/test/cases/enum_test.rb index 3b7bbcf47a..eea184e530 100644 --- a/activerecord/test/cases/enum_test.rb +++ b/activerecord/test/cases/enum_test.rb @@ -341,4 +341,18 @@ class EnumTest < ActiveRecord::TestCase book2.status = :uploaded assert_equal ['drafted', 'uploaded'], book2.status_change end + + test "declare multiple enums at a time" do + klass = Class.new(ActiveRecord::Base) do + self.table_name = "books" + enum status: [:proposed, :written, :published], + nullable_status: [:single, :married] + end + + book1 = klass.proposed.create! + assert book1.proposed? + + book2 = klass.single.create! + assert book2.single? + end end diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb index f2ba28a32f..b118ecc125 100644 --- a/activerecord/test/cases/helper.rb +++ b/activerecord/test/cases/helper.rb @@ -203,8 +203,3 @@ module InTimeZone end require 'mocha/setup' # FIXME: stop using mocha - -# FIXME: we have tests that depend on run order, we should fix that and -# remove this method call. -require 'active_support/test_case' -ActiveSupport::TestCase.test_order = :sorted diff --git a/activerecord/test/cases/mixin_test.rb b/activerecord/test/cases/mixin_test.rb index 7ddb2bfee1..7ebdcac711 100644 --- a/activerecord/test/cases/mixin_test.rb +++ b/activerecord/test/cases/mixin_test.rb @@ -61,8 +61,6 @@ class TouchTest < ActiveRecord::TestCase # Make sure Mixin.record_timestamps gets reset, even if this test fails, # so that other tests do not fail because Mixin.record_timestamps == false - rescue Exception => e - raise e ensure Mixin.record_timestamps = true end diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index 2803ad2de0..7c281a95b7 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -366,6 +366,12 @@ class PersistenceTest < ActiveRecord::TestCase end end + def test_update_does_not_run_sql_if_record_has_not_changed + topic = Topic.create(title: 'Another New Topic') + assert_queries(0) { topic.update(title: 'Another New Topic') } + assert_queries(0) { topic.update_attributes(title: 'Another New Topic') } + end + def test_delete topic = Topic.find(1) assert_equal topic, topic.delete, 'topic.delete did not return self' diff --git a/activerecord/test/cases/validations/length_validation_test.rb b/activerecord/test/cases/validations/length_validation_test.rb index 952e1681a7..f95f8f0b8f 100644 --- a/activerecord/test/cases/validations/length_validation_test.rb +++ b/activerecord/test/cases/validations/length_validation_test.rb @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- require "cases/helper" require 'models/owner' require 'models/pet' @@ -5,49 +6,49 @@ require 'models/person' class LengthValidationTest < ActiveRecord::TestCase fixtures :owners - repair_validations(Owner) - def test_validates_size_of_association - repair_validations Owner do - assert_nothing_raised { Owner.validates_size_of :pets, :minimum => 1 } - o = Owner.new('name' => 'nopets') - assert !o.save - assert o.errors[:pets].any? - o.pets.build('name' => 'apet') - assert o.valid? + setup do + @owner = Class.new(Owner) do + def self.name; 'Owner'; end end end + + def test_validates_size_of_association + assert_nothing_raised { @owner.validates_size_of :pets, minimum: 1 } + o = @owner.new('name' => 'nopets') + assert !o.save + assert o.errors[:pets].any? + o.pets.build('name' => 'apet') + assert o.valid? + end + def test_validates_size_of_association_using_within - repair_validations Owner do - assert_nothing_raised { Owner.validates_size_of :pets, :within => 1..2 } - o = Owner.new('name' => 'nopets') - assert !o.save - assert o.errors[:pets].any? + assert_nothing_raised { @owner.validates_size_of :pets, within: 1..2 } + o = @owner.new('name' => 'nopets') + assert !o.save + assert o.errors[:pets].any? - o.pets.build('name' => 'apet') - assert o.valid? + o.pets.build('name' => 'apet') + assert o.valid? - 2.times { o.pets.build('name' => 'apet') } - assert !o.save - assert o.errors[:pets].any? - end + 2.times { o.pets.build('name' => 'apet') } + assert !o.save + assert o.errors[:pets].any? end def test_validates_size_of_association_utf8 - repair_validations Owner do - Owner.validates_size_of :pets, :minimum => 1 - o = Owner.new('name' => 'あいうえおかきくけこ') - assert !o.save - assert o.errors[:pets].any? - o.pets.build('name' => 'あいうえおかきくけこ') - assert o.valid? - end + @owner.validates_size_of :pets, minimum: 1 + o = @owner.new('name' => 'あいうえおかきくけこ') + assert !o.save + assert o.errors[:pets].any? + o.pets.build('name' => 'あいうえおかきくけこ') + assert o.valid? end def test_validates_size_of_respects_records_marked_for_destruction - Owner.validates_size_of :pets, minimum: 1 - owner = Owner.new + @owner.validates_size_of :pets, minimum: 1 + owner = @owner.new assert_not owner.save assert owner.errors[:pets].any? pet = owner.pets.build @@ -62,8 +63,8 @@ class LengthValidationTest < ActiveRecord::TestCase end def test_does_not_validate_length_of_if_parent_record_is_validate_false - Owner.validates_length_of :name, minimum: 1 - owner = Owner.new + @owner.validates_length_of :name, minimum: 1 + owner = @owner.new owner.save!(validate: false) assert owner.persisted? |