aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG.md52
-rw-r--r--activerecord/Rakefile4
-rw-r--r--activerecord/lib/active_record/associations/association.rb7
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb10
-rw-r--r--activerecord/lib/active_record/attribute_methods/dirty.rb20
-rw-r--r--activerecord/lib/active_record/attribute_methods/serialization.rb10
-rw-r--r--activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb15
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/column.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb4
-rw-r--r--activerecord/lib/active_record/persistence.rb4
-rw-r--r--activerecord/lib/active_record/railtie.rb2
-rw-r--r--activerecord/lib/active_record/relation/batches.rb13
-rw-r--r--activerecord/lib/active_record/tasks/mysql_database_tasks.rb15
-rw-r--r--activerecord/test/cases/associations/extension_test.rb7
-rw-r--r--activerecord/test/cases/batches_test.rb15
-rw-r--r--activerecord/test/cases/callbacks_test.rb49
-rw-r--r--activerecord/test/cases/dirty_test.rb25
-rw-r--r--activerecord/test/cases/hot_compatibility_test.rb54
-rw-r--r--activerecord/test/cases/tasks/mysql_rake_test.rb46
-rw-r--r--activerecord/test/cases/transaction_isolation_test.rb6
-rw-r--r--activerecord/test/models/post.rb5
23 files changed, 270 insertions, 107 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index aee8f8d1f7..67bec5f38e 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,5 +1,22 @@
## Rails 4.0.0 (unreleased) ##
+* Support for partial inserts.
+
+ When inserting new records, only the fields which have been changed
+ from the defaults will actually be included in the INSERT statement.
+ The other fields will be populated by the database.
+
+ This is more efficient, and also means that it will be safe to
+ remove database columns without getting subsequent errors in running
+ app processes (so long as the code in those processes doesn't
+ contain any references to the removed column).
+
+ *Jon Leighton*
+
+* Allow before and after validations to take an array of lifecycle events
+
+ *John Foley*
+
* Support for specifying transaction isolation level
If your database supports setting the isolation level for a transaction, you can set
@@ -76,21 +93,6 @@
*kennyj*
-* Fix `find_in_batches` when primary_key is set other than id.
- You can now use this method with the primary key which is not integer-based.
-
- Example:
-
- class Post < ActiveRecord::Base
- self.primary_key = :title
- end
-
- Post.find_in_batches(start: 'My First Post') do |batch|
- batch.each { |post| post.author.greeting }
- end
-
- *Toshiyuki Kawanishi*
-
* You can now override the generated accessor methods for stored attributes
and reuse the original behavior with `read_store_attribute` and `write_store_attribute`,
which are counterparts to `read_attribute` and `write_attribute`.
@@ -299,6 +301,15 @@
*Jon Leighton*
+* `Relation#order`: make new order prepend old one.
+
+ User.order("name asc").order("created_at desc")
+ # SELECT * FROM users ORDER BY created_at desc, name asc
+
+ This also affects order defined in `default_scope` or any kind of associations.
+
+ *Bogdan Gusiev*
+
* `Model.all` now returns an `ActiveRecord::Relation`, rather than an
array of records. Use `Relation#to_a` if you really want an array.
@@ -328,6 +339,17 @@
*Jon Leighton*
+* Added `#update_columns` method which updates the attributes from
+ the passed-in hash without calling save, hence skipping validations and
+ callbacks. `ActiveRecordError` will be raised when called on new objects
+ or when at least one of the attributes is marked as read only.
+
+ post.attributes # => {"id"=>2, "title"=>"My title", "body"=>"My content", "author"=>"Peter"}
+ post.update_columns(title: 'New title', author: 'Sebastian') # => true
+ post.attributes # => {"id"=>2, "title"=>"New title", "body"=>"My content", "author"=>"Sebastian"}
+
+ *Sebastian Martinez + Rafael Mendonça França*
+
* The migration generator now creates a join table with (commented) indexes every time
the migration name contains the word `join_table`:
diff --git a/activerecord/Rakefile b/activerecord/Rakefile
index a29d7b0e99..53ddff420e 100644
--- a/activerecord/Rakefile
+++ b/activerecord/Rakefile
@@ -112,8 +112,8 @@ namespace :postgresql do
desc 'Build the PostgreSQL test databases'
task :build_databases do
config = ARTest.config['connections']['postgresql']
- %x( createdb -E UTF8 #{config['arunit']['database']} )
- %x( createdb -E UTF8 #{config['arunit2']['database']} )
+ %x( createdb -E UTF8 -T template0 #{config['arunit']['database']} )
+ %x( createdb -E UTF8 -T template0 #{config['arunit2']['database']} )
# prepare hstore
version = %x( createdb --version ).strip.gsub(/(.*)(\d\.\d\.\d)$/, "\\2")
diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb
index 495f0cde59..ba75c8be41 100644
--- a/activerecord/lib/active_record/associations/association.rb
+++ b/activerecord/lib/active_record/associations/association.rb
@@ -154,11 +154,8 @@ module ActiveRecord
# We can't dump @reflection since it contains the scope proc
def marshal_dump
- reflection = @reflection
- @reflection = nil
-
- ivars = instance_variables.map { |name| [name, instance_variable_get(name)] }
- [reflection.name, ivars]
+ ivars = (instance_variables - [:@reflection]).map { |name| [name, instance_variable_get(name)] }
+ [@reflection.name, ivars]
end
def marshal_load(data)
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index ced15bc330..0aff2562b8 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -207,8 +207,8 @@ module ActiveRecord
value
end
- def arel_attributes_with_values_for_create(pk_attribute_allowed)
- arel_attributes_with_values(attributes_for_create(pk_attribute_allowed))
+ def arel_attributes_with_values_for_create(attribute_names)
+ arel_attributes_with_values(attributes_for_create(attribute_names))
end
def arel_attributes_with_values_for_update(attribute_names)
@@ -242,9 +242,9 @@ module ActiveRecord
# Filters out the primary keys, from the attribute names, when the primary
# key is to be generated (e.g. the id attribute has no value).
- def attributes_for_create(pk_attribute_allowed)
- @attributes.keys.select do |name|
- column_for_attribute(name) && (pk_attribute_allowed || !pk_attribute?(name))
+ def attributes_for_create(attribute_names)
+ attribute_names.select do |name|
+ column_for_attribute(name) && !(pk_attribute?(name) && id.nil?)
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb
index ba32f1ec51..7a5bb9e863 100644
--- a/activerecord/lib/active_record/attribute_methods/dirty.rb
+++ b/activerecord/lib/active_record/attribute_methods/dirty.rb
@@ -64,15 +64,29 @@ module ActiveRecord
end
def update(*)
+ partial_updates? ? super(keys_for_partial_update) : super
+ end
+
+ def create(*)
if partial_updates?
- # Serialized attributes should always be written in case they've been
- # changed in place.
- super(changed | (attributes.keys & self.class.serialized_attributes.keys))
+ keys = keys_for_partial_update
+
+ # This is an extremely bloody annoying necessity to work around mysql being crap.
+ # See test_mysql_text_not_null_defaults
+ keys.concat self.class.columns.select(&:explicit_default?).map(&:name)
+
+ super keys
else
super
end
end
+ # Serialized attributes should always be written in case they've been
+ # changed in place.
+ def keys_for_partial_update
+ changed | (attributes.keys & self.class.serialized_attributes.keys)
+ end
+
def _field_changed?(attr, old, value)
if column = column_for_attribute(attr)
if column.number? && (changes_from_nil_to_empty_string?(column, old, value) ||
diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb
index d2cfcbbaf8..9994a81ede 100644
--- a/activerecord/lib/active_record/attribute_methods/serialization.rb
+++ b/activerecord/lib/active_record/attribute_methods/serialization.rb
@@ -101,16 +101,6 @@ module ActiveRecord
attributes
end
-
- private
-
- def attribute_cast_code(attr_name)
- if serialized_attributes.include?(attr_name)
- "v.unserialized_value"
- else
- super
- end
- end
end
def type_cast_attribute_for_write(column, value)
diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
index 9647d03be4..b9a69cdb0a 100644
--- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
+++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
@@ -34,21 +34,6 @@ module ActiveRecord
module ClassMethods
protected
- # The enhanced read method automatically converts the UTC time stored in the database to the time
- # zone stored in Time.zone.
- def attribute_cast_code(attr_name)
- column = columns_hash[attr_name]
-
- if create_time_zone_conversion_attribute?(attr_name, column)
- typecast = "v = #{super}"
- time_zone_conversion = "v.acts_like?(:time) ? v.in_time_zone : v"
-
- "((#{typecast}) && (#{time_zone_conversion}))"
- else
- super
- end
- end
-
# Defined for all +datetime+ and +timestamp+ attributes when +time_zone_aware_attributes+ are enabled.
# This enhanced write method will automatically convert the time passed to it to the zone stored in Time.zone.
def define_method_attribute=(attr_name)
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 793f58d4d3..0d7046a705 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -299,7 +299,7 @@ module ActiveRecord
end
def empty_insert_statement_value
- "VALUES(DEFAULT)"
+ "DEFAULT VALUES"
end
def case_sensitive_equality_operator
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 1783b036a2..8c83c4f5db 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -30,6 +30,10 @@ module ActiveRecord
super
end
+ def explicit_default?
+ !null && (sql_type =~ /blob/i || type == :text)
+ end
+
# Must return the relevant concrete adapter
def adapter
raise NotImplementedError
@@ -320,6 +324,10 @@ module ActiveRecord
end
end
+ def empty_insert_statement_value
+ "VALUES ()"
+ end
+
# SCHEMA STATEMENTS ========================================
def structure_dump #:nodoc:
diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb
index 816b5e17c1..2028abf6f0 100644
--- a/activerecord/lib/active_record/connection_adapters/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/column.rb
@@ -53,6 +53,10 @@ module ActiveRecord
!default.nil?
end
+ def explicit_default?
+ false
+ end
+
# Returns the Ruby class that corresponds to the abstract data type.
def klass
case type
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index 4a48812807..4d5cb72c67 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -490,10 +490,6 @@ module ActiveRecord
alter_table(table_name, :rename => {column_name.to_s => new_column_name.to_s})
end
- def empty_insert_statement_value
- "VALUES(NULL)"
- end
-
protected
def select(sql, name = nil, binds = []) #:nodoc:
exec_query(sql, name, binds)
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 2eaad1d469..f81eb5f5d1 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -385,8 +385,8 @@ module ActiveRecord
# Creates a record with values matching those of the instance attributes
# and returns its id.
- def create
- attributes_values = arel_attributes_with_values_for_create(!id.nil?)
+ def create(attribute_names = @attributes.keys)
+ attributes_values = arel_attributes_with_values_for_create(attribute_names)
new_id = self.class.unscoped.insert attributes_values
self.id ||= new_id if self.class.primary_key
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index e293f641ee..b11483de8c 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -82,7 +82,7 @@ module ActiveRecord
if cache.version == ActiveRecord::Migrator.current_version
ActiveRecord::Model.connection.schema_cache = cache
else
- warn "schema_cache.dump is expired. Current version is #{ActiveRecord::Migrator.current_version}, but cache version is #{cache.version}."
+ warn "Ignoring db/schema_cache.dump because it has expired. The current schema version is #{ActiveRecord::Migrator.current_version}, but the one in the cache is #{cache.version}."
end
end
end
diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb
index d32048cce1..28aab6d92b 100644
--- a/activerecord/lib/active_record/relation/batches.rb
+++ b/activerecord/lib/active_record/relation/batches.rb
@@ -36,12 +36,12 @@ module ActiveRecord
# want multiple workers dealing with the same processing queue. You can
# make worker 1 handle all the records between id 0 and 10,000 and
# worker 2 handle from 10,000 and beyond (by setting the +:start+
- # option on that worker). You can also use non-integer-based primary keys
- # if start point is set.
+ # option on that worker).
#
# It's not possible to set the order. That is automatically set to
- # ascending on the primary key (e.g. "id ASC") to make the batch ordering
- # work. You can't set the limit either, that's used to control
+ # ascending on the primary key ("id ASC") to make the batch ordering
+ # work. This also mean that this method only works with integer-based
+ # primary keys. You can't set the limit either, that's used to control
# the batch sizes.
#
# Person.where("age > 21").find_in_batches do |group|
@@ -62,8 +62,7 @@ module ActiveRecord
ActiveRecord::Base.logger.warn("Scoped order and limit are ignored, it's forced to be batch order and batch size")
end
- start = options.delete(:start)
- start ||= 0
+ start = options.delete(:start) || 0
batch_size = options.delete(:batch_size) || 1000
relation = relation.reorder(batch_order).limit(batch_size)
@@ -71,7 +70,7 @@ module ActiveRecord
while records.any?
records_size = records.size
- primary_key_offset = records.last.send(primary_key)
+ primary_key_offset = records.last.id
yield records
diff --git a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
index 7abbf08571..3d27c97254 100644
--- a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
@@ -73,13 +73,16 @@ module ActiveRecord
configuration.merge('database' => nil)
end
- # If neither encoding nor collation is specified, use the utf-8 defaults.
def creation_options
- options = configuration.slice('encoding', 'collation').symbolize_keys
- if options.empty?
- { charset: DEFAULT_CHARSET, collation: DEFAULT_COLLATION }
- else
- options
+ Hash.new.tap do |options|
+ options[:charset] = configuration['encoding'] if configuration.include? 'encoding'
+ options[:collation] = configuration['collation'] if configuration.include? 'collation'
+
+ # Set default charset only when collation isn't set.
+ options[:charset] ||= DEFAULT_CHARSET unless options[:collation]
+
+ # Set default collation only when charset is also default.
+ options[:collation] ||= DEFAULT_COLLATION if options[:charset] == DEFAULT_CHARSET
end
end
diff --git a/activerecord/test/cases/associations/extension_test.rb b/activerecord/test/cases/associations/extension_test.rb
index bd5a426ca8..da767a2a7e 100644
--- a/activerecord/test/cases/associations/extension_test.rb
+++ b/activerecord/test/cases/associations/extension_test.rb
@@ -40,9 +40,12 @@ class AssociationsExtensionsTest < ActiveRecord::TestCase
assert_equal projects(:action_controller), david.projects.find_most_recent
marshalled = Marshal.dump(david)
- david = Marshal.load(marshalled)
- assert_equal projects(:action_controller), david.projects.find_most_recent
+ # Marshaling an association shouldn't make it unusable by wiping its reflection.
+ assert_not_nil david.association(:projects).reflection
+
+ david_too = Marshal.load(marshalled)
+ assert_equal projects(:action_controller), david_too.projects.find_most_recent
end
def test_marshalling_named_extensions
diff --git a/activerecord/test/cases/batches_test.rb b/activerecord/test/cases/batches_test.rb
index 3b4ff83725..12d5245fbd 100644
--- a/activerecord/test/cases/batches_test.rb
+++ b/activerecord/test/cases/batches_test.rb
@@ -1,8 +1,9 @@
require 'cases/helper'
require 'models/post'
+require 'models/subscriber'
class EachTest < ActiveRecord::TestCase
- fixtures :posts
+ fixtures :posts, :subscribers
def setup
@posts = Post.order("id asc")
@@ -125,14 +126,14 @@ class EachTest < ActiveRecord::TestCase
end
def test_find_in_batches_should_use_any_column_as_primary_key
- title_order_posts = Post.order('title asc')
- start_title = title_order_posts.first.title
+ nick_order_subscribers = Subscriber.order('nick asc')
+ start_nick = nick_order_subscribers.second.nick
- posts = []
- PostWithTitlePrimaryKey.find_in_batches(:batch_size => 1, :start => start_title) do |batch|
- posts.concat(batch)
+ subscribers = []
+ Subscriber.find_in_batches(:batch_size => 1, :start => start_nick) do |batch|
+ subscribers.concat(batch)
end
- assert_equal title_order_posts.map(&:id), posts.map(&:id)
+ assert_equal nick_order_subscribers[1..-1].map(&:id), subscribers.map(&:id)
end
end
diff --git a/activerecord/test/cases/callbacks_test.rb b/activerecord/test/cases/callbacks_test.rb
index deeef3a3fd..7457bafd4e 100644
--- a/activerecord/test/cases/callbacks_test.rb
+++ b/activerecord/test/cases/callbacks_test.rb
@@ -137,6 +137,32 @@ class OnCallbacksDeveloper < ActiveRecord::Base
end
end
+class ContextualCallbacksDeveloper < ActiveRecord::Base
+ self.table_name = 'developers'
+
+ before_validation { history << :before_validation }
+ before_validation :before_validation_on_create_and_update, :on => [ :create, :update ]
+
+ validate do
+ history << :validate
+ end
+
+ after_validation { history << :after_validation }
+ after_validation :after_validation_on_create_and_update, :on => [ :create, :update ]
+
+ def before_validation_on_create_and_update
+ history << "before_validation_on_#{self.validation_context}".to_sym
+ end
+
+ def after_validation_on_create_and_update
+ history << "after_validation_on_#{self.validation_context}".to_sym
+ end
+
+ def history
+ @history ||= []
+ end
+end
+
class CallbackCancellationDeveloper < ActiveRecord::Base
self.table_name = 'developers'
@@ -285,6 +311,17 @@ class CallbacksTest < ActiveRecord::TestCase
], david.history
end
+ def test_validate_on_contextual_create
+ david = ContextualCallbacksDeveloper.create('name' => 'David', 'salary' => 1000000)
+ assert_equal [
+ :before_validation,
+ :before_validation_on_create,
+ :validate,
+ :after_validation,
+ :after_validation_on_create
+ ], david.history
+ end
+
def test_update
david = CallbackDeveloper.find(1)
david.save
@@ -344,6 +381,18 @@ class CallbacksTest < ActiveRecord::TestCase
], david.history
end
+ def test_validate_on_contextual_update
+ david = ContextualCallbacksDeveloper.find(1)
+ david.save
+ assert_equal [
+ :before_validation,
+ :before_validation_on_update,
+ :validate,
+ :after_validation,
+ :after_validation_on_update
+ ], david.history
+ end
+
def test_destroy
david = CallbackDeveloper.find(1)
david.destroy
diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb
index 9a2a5a4e3c..75a52544d3 100644
--- a/activerecord/test/cases/dirty_test.rb
+++ b/activerecord/test/cases/dirty_test.rb
@@ -3,6 +3,7 @@ require 'models/topic' # For booleans
require 'models/pirate' # For timestamps
require 'models/parrot'
require 'models/person' # For optimistic locking
+require 'models/aircraft'
class Pirate # Just reopening it, not defining it
attr_accessor :detected_changes_in_after_update # Boolean for if changes are detected
@@ -550,6 +551,30 @@ class DirtyTest < ActiveRecord::TestCase
end
end
+ test "partial insert" do
+ with_partial_updates Person do
+ jon = nil
+ assert_sql(/first_name/) do
+ jon = Person.create! first_name: 'Jon'
+ end
+
+ assert ActiveRecord::SQLCounter.log_all.none? { |sql| sql =~ /followers_count/ }
+
+ jon.reload
+ assert_equal 'Jon', jon.first_name
+ assert_equal 0, jon.followers_count
+ assert_not_nil jon.id
+ end
+ end
+
+ test "partial insert with empty values" do
+ with_partial_updates Aircraft do
+ a = Aircraft.create!
+ a.reload
+ assert_not_nil a.id
+ end
+ end
+
private
def with_partial_updates(klass, on = true)
old = klass.partial_updates?
diff --git a/activerecord/test/cases/hot_compatibility_test.rb b/activerecord/test/cases/hot_compatibility_test.rb
new file mode 100644
index 0000000000..96e581ab4c
--- /dev/null
+++ b/activerecord/test/cases/hot_compatibility_test.rb
@@ -0,0 +1,54 @@
+require 'cases/helper'
+
+class HotCompatibilityTest < ActiveRecord::TestCase
+ self.use_transactional_fixtures = false
+
+ setup do
+ @klass = Class.new(ActiveRecord::Base) do
+ connection.create_table :hot_compatibilities do |t|
+ t.string :foo
+ t.string :bar
+ end
+
+ def self.name; 'HotCompatibility'; end
+ end
+ end
+
+ teardown do
+ @klass.connection.drop_table :hot_compatibilities
+ end
+
+ test "insert after remove_column" do
+ # warm cache
+ @klass.create!
+
+ # we have 3 columns
+ assert_equal 3, @klass.columns.length
+
+ # remove one of them
+ @klass.connection.remove_column :hot_compatibilities, :bar
+
+ # we still have 3 columns in the cache
+ assert_equal 3, @klass.columns.length
+
+ # but we can successfully create a record so long as we don't
+ # reference the removed column
+ record = @klass.create! foo: 'foo'
+ record.reload
+ assert_equal 'foo', record.foo
+ end
+
+ test "update after remove_column" do
+ record = @klass.create! foo: 'foo'
+ assert_equal 3, @klass.columns.length
+ @klass.connection.remove_column :hot_compatibilities, :bar
+ assert_equal 3, @klass.columns.length
+
+ record.reload
+ assert_equal 'foo', record.foo
+ record.foo = 'bar'
+ record.save!
+ record.reload
+ assert_equal 'bar', record.foo
+ end
+end
diff --git a/activerecord/test/cases/tasks/mysql_rake_test.rb b/activerecord/test/cases/tasks/mysql_rake_test.rb
index 46b97a1274..69a049fcfa 100644
--- a/activerecord/test/cases/tasks/mysql_rake_test.rb
+++ b/activerecord/test/cases/tasks/mysql_rake_test.rb
@@ -20,20 +20,32 @@ module ActiveRecord
ActiveRecord::Tasks::DatabaseTasks.create @configuration
end
- def test_creates_database_with_default_options
+ def test_creates_database_with_default_encoding_and_collation
@connection.expects(:create_database).
- with('my-app-db', {:charset => 'utf8', :collation => 'utf8_unicode_ci'})
+ with('my-app-db', charset: 'utf8', collation: 'utf8_unicode_ci')
ActiveRecord::Tasks::DatabaseTasks.create @configuration
end
- def test_creates_database_with_given_options
+ def test_creates_database_with_given_encoding_and_default_collation
@connection.expects(:create_database).
- with('my-app-db', {:charset => 'latin', :collation => 'latin_ci'})
+ with('my-app-db', charset: 'utf8', collation: 'utf8_unicode_ci')
- ActiveRecord::Tasks::DatabaseTasks.create @configuration.merge(
- 'encoding' => 'latin', 'collation' => 'latin_ci'
- )
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration.merge('encoding' => 'utf8')
+ end
+
+ def test_creates_database_with_given_encoding_and_no_collation
+ @connection.expects(:create_database).
+ with('my-app-db', charset: 'latin1')
+
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration.merge('encoding' => 'latin1')
+ end
+
+ def test_creates_database_with_given_collation_and_no_encoding
+ @connection.expects(:create_database).
+ with('my-app-db', collation: 'latin1_swedish_ci')
+
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration.merge('collation' => 'latin1_swedish_ci')
end
def test_establishes_connection_to_database
@@ -62,8 +74,9 @@ module ActiveRecord
$stdout.stubs(:print).returns(nil)
@error.stubs(:errno).returns(1045)
ActiveRecord::Base.stubs(:connection).returns(@connection)
- ActiveRecord::Base.stubs(:establish_connection).raises(@error).then.
- returns(true)
+ ActiveRecord::Base.stubs(:establish_connection).
+ raises(@error).
+ then.returns(true)
end
def test_root_password_is_requested
@@ -74,12 +87,12 @@ module ActiveRecord
end
def test_connection_established_as_root
- ActiveRecord::Base.expects(:establish_connection).with({
+ ActiveRecord::Base.expects(:establish_connection).with(
'adapter' => 'mysql',
'database' => nil,
'username' => 'root',
'password' => 'secret'
- })
+ )
ActiveRecord::Tasks::DatabaseTasks.create @configuration
end
@@ -99,12 +112,12 @@ module ActiveRecord
def test_connection_established_as_normal_user
ActiveRecord::Base.expects(:establish_connection).returns do
- ActiveRecord::Base.expects(:establish_connection).with({
+ ActiveRecord::Base.expects(:establish_connection).with(
'adapter' => 'mysql',
'database' => 'my-app-db',
'username' => 'pat',
'password' => 'secret'
- })
+ )
raise @error
end
@@ -166,18 +179,17 @@ module ActiveRecord
def test_recreates_database_with_the_default_options
@connection.expects(:recreate_database).
- with('test-db', {:charset => 'utf8', :collation => 'utf8_unicode_ci'})
+ with('test-db', charset: 'utf8', collation: 'utf8_unicode_ci')
ActiveRecord::Tasks::DatabaseTasks.purge @configuration
end
def test_recreates_database_with_the_given_options
@connection.expects(:recreate_database).
- with('test-db', {:charset => 'latin', :collation => 'latin_ci'})
+ with('test-db', charset: 'latin', collation: 'latin1_swedish_ci')
ActiveRecord::Tasks::DatabaseTasks.purge @configuration.merge(
- 'encoding' => 'latin', 'collation' => 'latin_ci'
- )
+ 'encoding' => 'latin', 'collation' => 'latin1_swedish_ci')
end
end
diff --git a/activerecord/test/cases/transaction_isolation_test.rb b/activerecord/test/cases/transaction_isolation_test.rb
index 1e34f93d8f..a396da6645 100644
--- a/activerecord/test/cases/transaction_isolation_test.rb
+++ b/activerecord/test/cases/transaction_isolation_test.rb
@@ -44,6 +44,9 @@ class TransactionIsolationTest < ActiveRecord::TestCase
# specifies what must not happen at a certain level, not what must happen. At
# the read uncommitted level, there is nothing that must not happen.
test "read uncommitted" do
+ unless ActiveRecord::Base.connection.transaction_isolation_levels.include?(:read_uncommitted)
+ skip "database does not support read uncommitted isolation level"
+ end
Tag.transaction(isolation: :read_uncommitted) do
assert_equal 0, Tag.count
Tag2.create
@@ -67,6 +70,9 @@ class TransactionIsolationTest < ActiveRecord::TestCase
# We are testing that a nonrepeatable read does not happen
test "repeatable read" do
+ unless ActiveRecord::Base.connection.transaction_isolation_levels.include?(:repeatable_read)
+ skip "database does not support repeatable read isolation level"
+ end
tag = Tag.create(name: 'jon')
Tag.transaction(isolation: :repeatable_read) do
diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb
index 9858f68c4a..c995f59a15 100644
--- a/activerecord/test/models/post.rb
+++ b/activerecord/test/models/post.rb
@@ -186,8 +186,3 @@ class SpecialPostWithDefaultScope < ActiveRecord::Base
self.table_name = 'posts'
default_scope { where(:id => [1, 5,6]) }
end
-
-class PostWithTitlePrimaryKey < ActiveRecord::Base
- self.table_name = 'posts'
- self.primary_key = :title
-end