diff options
Diffstat (limited to 'activerecord')
26 files changed, 212 insertions, 127 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index ec3ec06c2b..554bec17d6 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,25 @@ +* Correct query for PostgreSQL 8.2 compatibility. + + *Ben Murphy*, *Matthew Draper* + +* `bin/rake db:migrate` uses + `ActiveRecord::Tasks::DatabaseTasks.migrations_paths` instead of + `Migrator.migrations_paths`. + + *Tobias Bielohlawek* + +* Support dropping indexes concurrently in PostgreSQL. + + See http://www.postgresql.org/docs/9.4/static/sql-dropindex.html for more + details. + + *Grey Baker* + +* Deprecate passing conditions to `ActiveRecord::Relation#delete_all` + and `ActiveRecord::Relation#destroy_all`. + + *Wojciech Wnętrzak* + * PostgreSQL, `create_schema`, `drop_schema` and `rename_table` now quote schema names. @@ -25,7 +47,7 @@ * Uniqueness validator raises descriptive error when running on a persisted record without primary key. - Closes #21304. + Fixes #21304. *Yves Senn* @@ -41,7 +63,7 @@ * Descriptive error message when fixtures contain a missing column. - Closes #21201. + Fixes #21201. *Yves Senn* @@ -65,11 +87,11 @@ sleep 10 # Throttles the delete queries end - Closes #20933. + Fixes #20933. *Sina Siadat* -* Added methods for PostgreSQL geometric data types to use in migrations +* Added methods for PostgreSQL geometric data types to use in migrations. Example: @@ -1095,7 +1117,7 @@ * `eager_load` preserves readonly flag for associations. - Closes #15853. + Fixes #15853. *Takashi Kokubun* @@ -1151,7 +1173,7 @@ * Fix bug with `ActiveRecord::Type::Numeric` that caused negative values to be marked as having changed when set to the same negative value. - Closes #18161. + Fixes #18161. *Daniel Fox* @@ -1166,7 +1188,7 @@ before loading the schema. This is left for the user to do. `db:test:prepare` will still purge the database. - Closes #17945. + Fixes #17945. *Yves Senn* diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index a30945d0ee..7974ff3710 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -23,6 +23,11 @@ module ActiveRecord table_name[0...table_alias_length].tr('.', '_') end + # Returns an array of table names defined in the database. + def tables(name = nil) + raise NotImplementedError, "#tables is not implemented" + end + # Checks to see if the table +table_name+ exists on the database. # # table_exists?(:developers) @@ -607,10 +612,7 @@ module ActiveRecord # remove_index :accounts, name: :by_branch_party # def remove_index(table_name, options = {}) - remove_index!(table_name, index_name_for_remove(table_name, options)) - end - - def remove_index!(table_name, index_name) #:nodoc: + index_name = index_name_for_remove(table_name, options) execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}" end diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb index ff43c7ec42..734b384e80 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb @@ -1,6 +1,6 @@ require 'active_record/connection_adapters/abstract_mysql_adapter' -gem 'mysql2', '~> 0.3.18' +gem 'mysql2', '>= 0.3.18', '< 0.5' require 'mysql2' module ActiveRecord diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb index 191c828e60..6155e53632 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb @@ -36,7 +36,7 @@ module ActiveRecord WHERE t.typname IN (%s) OR t.typtype IN (%s) - OR t.typinput::varchar = 'array_in' + OR t.typinput = 'array_in(cstring,oid,integer)'::regprocedure OR t.typelem != 0 SQL end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb index a3fc8fbc51..69aa02ccf4 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -101,15 +101,19 @@ module ActiveRecord # Verifies existence of an index with a given name. def index_name_exists?(table_name, index_name, default) + table = Utils.extract_schema_qualified_name(table_name.to_s) + index = Utils.extract_schema_qualified_name(index_name.to_s) + select_value(<<-SQL, 'SCHEMA').to_i > 0 SELECT COUNT(*) FROM pg_class t INNER JOIN pg_index d ON t.oid = d.indrelid INNER JOIN pg_class i ON d.indexrelid = i.oid + LEFT JOIN pg_namespace n ON n.oid = i.relnamespace WHERE i.relkind = 'i' - AND i.relname = '#{index_name}' - AND t.relname = '#{table_name}' - AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = ANY (current_schemas(false)) ) + AND i.relname = '#{index.identifier}' + AND t.relname = '#{table.identifier}' + AND n.nspname = #{index.schema ? "'#{index.schema}'" : 'ANY (current_schemas(false))'} SQL end @@ -447,8 +451,15 @@ module ActiveRecord execute "CREATE #{index_type} INDEX #{index_algorithm} #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} #{index_using} (#{index_columns})#{index_options}" end - def remove_index!(table_name, index_name) #:nodoc: - execute "DROP INDEX #{quote_table_name(index_name)}" + def remove_index(table_name, options = {}) #:nodoc: + index_name = index_name_for_remove(table_name, options) + algorithm = + if Hash === options && options.key?(:algorithm) + index_algorithms.fetch(options[:algorithm]) do + raise ArgumentError.new("Algorithm must be one of the following: #{index_algorithms.keys.map(&:inspect).join(', ')}") + end + end + execute "DROP INDEX #{algorithm} #{quote_table_name(index_name)}" end # Renames an index of a table. Raises error if length of new diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index 358039723f..24fc67938d 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -375,7 +375,8 @@ module ActiveRecord pks[0]['name'] end - def remove_index!(table_name, index_name) #:nodoc: + def remove_index(table_name, options = {}) #:nodoc: + index_name = index_name_for_remove(table_name, options) exec_query "DROP INDEX #{quote_column_name(index_name)}" end diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb index e613d157aa..c26842014d 100644 --- a/activerecord/lib/active_record/inheritance.rb +++ b/activerecord/lib/active_record/inheritance.rb @@ -82,7 +82,7 @@ module ActiveRecord # Returns the class descending directly from ActiveRecord::Base, or # an abstract class, if any, in the inheritance hierarchy. # - # If A extends AR::Base, A.base_class will return A. If B descends from A + # If A extends ActiveRecord::Base, A.base_class will return A. If B descends from A # through some arbitrarily deep hierarchy, B.base_class will return A. # # If B < A and C < B and if A is an abstract_class then both B.base_class diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb index 5a6f42ba09..2b0c755ef4 100644 --- a/activerecord/lib/active_record/model_schema.rb +++ b/activerecord/lib/active_record/model_schema.rb @@ -240,7 +240,7 @@ module ActiveRecord end # Returns a hash where the keys are column names and the values are - # default values when instantiating the AR object for this table. + # default values when instantiating the Active Record object for this table. def column_defaults load_schema _default_attributes.to_hash diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 6a72d528b4..63ea305eae 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -229,7 +229,7 @@ db_namespace = namespace :db do end namespace :schema do - desc 'Creates a db/schema.rb file that is portable against any DB supported by AR' + desc 'Creates a db/schema.rb file that is portable against any DB supported by Active Record' task :dump => [:environment, :load_config] do require 'active_record/schema_dumper' filename = ENV['SCHEMA'] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, 'schema.rb') diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 5360db6a19..f8913eba06 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -32,6 +32,7 @@ module ActiveRecord end def self.add_reflection(ar, name, reflection) + ar.clear_reflections_cache ar._reflections = ar._reflections.merge(name.to_s => reflection) end @@ -67,18 +68,22 @@ module ActiveRecord # # @api public def reflections - ref = {} - _reflections.each do |name, reflection| - parent_reflection = reflection.parent_reflection + @__reflections ||= begin + ref = {} - if parent_reflection - parent_name = parent_reflection.name - ref[parent_name.to_s] = parent_reflection - else - ref[name] = reflection + _reflections.each do |name, reflection| + parent_reflection = reflection.parent_reflection + + if parent_reflection + parent_name = parent_reflection.name + ref[parent_name.to_s] = parent_reflection + else + ref[name] = reflection + end end + + ref end - ref end # Returns an array of AssociationReflection objects for all the @@ -118,6 +123,10 @@ module ActiveRecord def reflect_on_all_autosave_associations reflections.values.select { |reflection| reflection.options[:autosave] } end + + def clear_reflections_cache #:nodoc: + @__reflections = nil + end end # Holds all the methods that are shared between MacroReflection, AssociationReflection diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index e47b7b1ed9..bf08cdbbf3 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -418,7 +418,7 @@ module ActiveRecord end end - # Destroys the records matching +conditions+ by instantiating each + # Destroys the records by instantiating each # record and calling its +destroy+ method. Each object's callbacks are # executed (including <tt>:dependent</tt> association options). Returns the # collection of objects that were destroyed; each will be frozen, to @@ -431,20 +431,15 @@ module ActiveRecord # rows quickly, without concern for their associations or callbacks, use # +delete_all+ instead. # - # ==== Parameters - # - # * +conditions+ - A string, array, or hash that specifies which records - # to destroy. If omitted, all records are destroyed. See the - # Conditions section in the introduction to ActiveRecord::Base for - # more information. - # # ==== Examples # - # Person.destroy_all("last_login < '2004-04-04'") - # Person.destroy_all(status: "inactive") # Person.where(age: 0..18).destroy_all def destroy_all(conditions = nil) if conditions + ActiveSupport::Deprecation.warn(<<-MESSAGE.squish) + Passing conditions to destroy_all is deprecated and will be removed in Rails 5.1. + To achieve the same use where(conditions).destroy_all + MESSAGE where(conditions).destroy_all else to_a.each(&:destroy).tap { reset } @@ -478,15 +473,13 @@ module ActiveRecord end end - # Deletes the records matching +conditions+ without instantiating the records + # Deletes the records without instantiating the records # first, and hence not calling the +destroy+ method nor invoking callbacks. This # is a single SQL DELETE statement that goes straight to the database, much more # efficient than +destroy_all+. Be careful with relations though, in particular # <tt>:dependent</tt> rules defined on associations are not honored. Returns the # number of rows affected. # - # Post.delete_all("person_id = 5 AND (category = 'Something' OR category = 'Else')") - # Post.delete_all(["person_id = ? AND (category = ? OR category = ?)", 5, 'Something', 'Else']) # Post.where(person_id: 5).where(category: ['Something', 'Else']).delete_all # # Both calls delete the affected posts all at once with a single DELETE statement. @@ -512,6 +505,10 @@ module ActiveRecord end if conditions + ActiveSupport::Deprecation.warn(<<-MESSAGE.squish) + Passing conditions to delete_all is deprecated and will be removed in Rails 5.1. + To achieve the same use where(conditions).delete_all + MESSAGE where(conditions).delete_all else stmt = Arel::DeleteManager.new diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index d26db7d4cf..e232516b0c 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -24,12 +24,12 @@ module ActiveRecord end def build_from_hash(attributes) - attributes = convert_dot_notation_to_hash(attributes.stringify_keys) + attributes = convert_dot_notation_to_hash(attributes) expand_from_hash(attributes) end def create_binds(attributes) - attributes = convert_dot_notation_to_hash(attributes.stringify_keys) + attributes = convert_dot_notation_to_hash(attributes) create_binds_for_hash(attributes) end diff --git a/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb index 159889d3b8..e81be63cd3 100644 --- a/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb +++ b/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb @@ -10,10 +10,10 @@ module ActiveRecord table = value.associated_table if value.base_class - queries[table.association_foreign_type] = value.base_class.name + queries[table.association_foreign_type.to_s] = value.base_class.name end - queries[table.association_foreign_key] = value.ids + queries[table.association_foreign_key.to_s] = value.ids predicate_builder.build_from_hash(queries) end diff --git a/activerecord/lib/active_record/relation/where_clause_factory.rb b/activerecord/lib/active_record/relation/where_clause_factory.rb index 0430922be3..23eaab4699 100644 --- a/activerecord/lib/active_record/relation/where_clause_factory.rb +++ b/activerecord/lib/active_record/relation/where_clause_factory.rb @@ -15,6 +15,7 @@ module ActiveRecord when Hash attributes = predicate_builder.resolve_column_aliases(opts) attributes = klass.send(:expand_hash_conditions_for_aggregates, attributes) + attributes.stringify_keys! attributes, binds = predicate_builder.create_binds(attributes) diff --git a/activerecord/lib/active_record/scoping/default.rb b/activerecord/lib/active_record/scoping/default.rb index a1adf8e3ee..fac566e12b 100644 --- a/activerecord/lib/active_record/scoping/default.rb +++ b/activerecord/lib/active_record/scoping/default.rb @@ -6,8 +6,10 @@ module ActiveRecord included do # Stores the default scope for the class. class_attribute :default_scopes, instance_writer: false, instance_predicate: false + class_attribute :default_scope_override, instance_predicate: false self.default_scopes = [] + self.default_scope_override = nil end module ClassMethods @@ -99,12 +101,18 @@ module ActiveRecord self.default_scopes += [scope] end - def build_default_scope(base_rel = relation) # :nodoc: + def build_default_scope(base_rel = nil) # :nodoc: return if abstract_class? - if !Base.is_a?(method(:default_scope).owner) + + if self.default_scope_override.nil? + self.default_scope_override = !Base.is_a?(method(:default_scope).owner) + end + + if self.default_scope_override # The user has defined their own default scope method, so call that evaluate_default_scope { default_scope } elsif default_scopes.any? + base_rel ||= relation evaluate_default_scope do default_scopes.inject(base_rel) do |default_scope, scope| default_scope.merge(base_rel.scoping { scope.call }) diff --git a/activerecord/lib/active_record/table_metadata.rb b/activerecord/lib/active_record/table_metadata.rb index 41f1c55c3c..f9bb1cf5e0 100644 --- a/activerecord/lib/active_record/table_metadata.rb +++ b/activerecord/lib/active_record/table_metadata.rb @@ -10,13 +10,15 @@ module ActiveRecord end def resolve_column_aliases(hash) - hash = hash.dup - hash.keys.grep(Symbol) do |key| - if klass.attribute_alias? key - hash[klass.attribute_alias(key)] = hash.delete key + # This method is a hot spot, so for now, use Hash[] to dup the hash. + # https://bugs.ruby-lang.org/issues/7166 + new_hash = Hash[hash] + hash.each do |key, _| + if (key.is_a?(Symbol)) && klass.attribute_alias?(key) + new_hash[klass.attribute_alias(key)] = new_hash.delete(key) end end - hash + new_hash end def arel_attribute(column_name) diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb index 683741768b..0b5dc6ed33 100644 --- a/activerecord/lib/active_record/tasks/database_tasks.rb +++ b/activerecord/lib/active_record/tasks/database_tasks.rb @@ -134,7 +134,7 @@ module ActiveRecord version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil scope = ENV['SCOPE'] verbose_was, Migration.verbose = Migration.verbose, verbose - Migrator.migrate(Migrator.migrations_paths, version) do |migration| + Migrator.migrate(migrations_paths, version) do |migration| scope.blank? || scope == migration.scope end ensure diff --git a/activerecord/test/cases/adapters/postgresql/active_schema_test.rb b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb index dc7ba314c6..24def31e36 100644 --- a/activerecord/test/cases/adapters/postgresql/active_schema_test.rb +++ b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb @@ -25,7 +25,7 @@ class PostgresqlActiveSchemaTest < ActiveRecord::PostgreSQLTestCase def test_add_index # add_index calls index_name_exists? which can't work since execute is stubbed - ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.stubs(:index_name_exists?).returns(false) + ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send(:define_method, :index_name_exists?) { |*| false } expected = %(CREATE UNIQUE INDEX "index_people_on_last_name" ON "people" ("last_name") WHERE state = 'active') assert_equal expected, add_index(:people, :last_name, :unique => true, :where => "state = 'active'") @@ -49,6 +49,22 @@ class PostgresqlActiveSchemaTest < ActiveRecord::PostgreSQLTestCase expected = %(CREATE UNIQUE INDEX "index_people_on_last_name" ON "people" USING gist ("last_name") WHERE state = 'active') assert_equal expected, add_index(:people, :last_name, :unique => true, :where => "state = 'active'", :using => :gist) + + ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send :remove_method, :index_name_exists? + end + + def test_remove_index + # remove_index calls index_name_exists? which can't work since execute is stubbed + ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send(:define_method, :index_name_exists?) { |*| true } + + expected = %(DROP INDEX CONCURRENTLY "index_people_on_last_name") + assert_equal expected, remove_index(:people, name: "index_people_on_last_name", algorithm: :concurrently) + + assert_raise ArgumentError do + add_index(:people, :last_name, algorithm: :copy) + end + + ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send :remove_method, :index_name_exists? end private diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb index ea7e5ac587..bee612d8d3 100644 --- a/activerecord/test/cases/adapters/postgresql/schema_test.rb +++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb @@ -321,11 +321,11 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase def test_with_uppercase_index_name @connection.execute "CREATE INDEX \"things_Index\" ON #{SCHEMA_NAME}.things (name)" - assert_nothing_raised { @connection.remove_index! "things", "#{SCHEMA_NAME}.things_Index"} + assert_nothing_raised { @connection.remove_index "things", name: "#{SCHEMA_NAME}.things_Index"} @connection.execute "CREATE INDEX \"things_Index\" ON #{SCHEMA_NAME}.things (name)" with_schema_search_path SCHEMA_NAME do - assert_nothing_raised { @connection.remove_index! "things", "things_Index"} + assert_nothing_raised { @connection.remove_index "things", name: "things_Index"} end end diff --git a/activerecord/test/cases/adapters/postgresql/view_test.rb b/activerecord/test/cases/adapters/postgresql/view_test.rb deleted file mode 100644 index 2dd6ec5fe6..0000000000 --- a/activerecord/test/cases/adapters/postgresql/view_test.rb +++ /dev/null @@ -1,64 +0,0 @@ -require "cases/helper" -require "cases/view_test" - -class UpdateableViewTest < ActiveRecord::PostgreSQLTestCase - fixtures :books - - class PrintedBook < ActiveRecord::Base - self.primary_key = "id" - end - - setup do - @connection = ActiveRecord::Base.connection - @connection.execute <<-SQL - CREATE VIEW printed_books - AS SELECT id, name, status, format FROM books WHERE format = 'paperback' - SQL - end - - teardown do - @connection.execute "DROP VIEW printed_books" if @connection.table_exists? "printed_books" - end - - def test_update_record - book = PrintedBook.first - book.name = "AWDwR" - book.save! - book.reload - assert_equal "AWDwR", book.name - end - - def test_insert_record - PrintedBook.create! name: "Rails in Action", status: 0, format: "paperback" - - new_book = PrintedBook.last - assert_equal "Rails in Action", new_book.name - end - - def test_update_record_to_fail_view_conditions - book = PrintedBook.first - book.format = "ebook" - book.save! - - assert_raises ActiveRecord::RecordNotFound do - book.reload - end - end -end - -if ActiveRecord::Base.connection.respond_to?(:supports_materialized_views?) && - ActiveRecord::Base.connection.supports_materialized_views? -class MaterializedViewTest < ActiveRecord::PostgreSQLTestCase - include ViewBehavior - - private - def create_view(name, query) - @connection.execute "CREATE MATERIALIZED VIEW #{name} AS #{query}" - end - - def drop_view(name) - @connection.execute "DROP MATERIALIZED VIEW #{name}" if @connection.table_exists? name - - end -end -end diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index cdf63957f4..7f14082a9a 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -126,7 +126,7 @@ class PersistenceTest < ActiveRecord::TestCase assert ! topics_by_mary.empty? assert_difference('Topic.count', -topics_by_mary.size) do - destroyed = Topic.destroy_all(conditions).sort_by(&:id) + destroyed = Topic.where(conditions).destroy_all.sort_by(&:id) assert_equal topics_by_mary, destroyed assert destroyed.all?(&:frozen?), "destroyed topics should be frozen" end diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 5f48c2b40f..8256762f96 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -931,6 +931,12 @@ class RelationTest < ActiveRecord::TestCase assert davids.loaded? end + def test_destroy_all_with_conditions_is_deprecated + assert_deprecated do + assert_difference('Author.count', -1) { Author.destroy_all(name: 'David') } + end + end + def test_delete_all davids = Author.where(:name => 'David') @@ -938,6 +944,12 @@ class RelationTest < ActiveRecord::TestCase assert ! davids.loaded? end + def test_delete_all_with_conditions_is_deprecated + assert_deprecated do + assert_difference('Author.count', -1) { Author.delete_all(name: 'David') } + end + end + def test_delete_all_loaded davids = Author.where(:name => 'David') diff --git a/activerecord/test/cases/tasks/database_tasks_test.rb b/activerecord/test/cases/tasks/database_tasks_test.rb index 38164b2228..c8f4179313 100644 --- a/activerecord/test/cases/tasks/database_tasks_test.rb +++ b/activerecord/test/cases/tasks/database_tasks_test.rb @@ -277,12 +277,14 @@ module ActiveRecord def test_migrate_receives_correct_env_vars verbose, version = ENV['VERBOSE'], ENV['VERSION'] + ActiveRecord::Tasks::DatabaseTasks.migrations_paths = 'custom/path' ENV['VERBOSE'] = 'false' ENV['VERSION'] = '4' - ActiveRecord::Migrator.expects(:migrate).with(ActiveRecord::Migrator.migrations_paths, 4) + ActiveRecord::Migrator.expects(:migrate).with('custom/path', 4) ActiveRecord::Tasks::DatabaseTasks.migrate ensure + ActiveRecord::Tasks::DatabaseTasks.migrations_paths = nil ENV['VERBOSE'], ENV['VERSION'] = verbose, version end end diff --git a/activerecord/test/cases/view_test.rb b/activerecord/test/cases/view_test.rb index f9dca1e196..1eb1430065 100644 --- a/activerecord/test/cases/view_test.rb +++ b/activerecord/test/cases/view_test.rb @@ -110,4 +110,70 @@ class ViewWithoutPrimaryKeyTest < ActiveRecord::TestCase assert_nil Paperback.primary_key end end + +# sqlite dose not support CREATE, INSERT, and DELETE for VIEW +if current_adapter?(:MysqlAdapter, :Mysql2Adapter, :PostgreSQLAdapter) +class UpdateableViewTest < ActiveRecord::TestCase + self.use_transactional_tests = false + fixtures :books + + class PrintedBook < ActiveRecord::Base + self.primary_key = "id" + end + + setup do + @connection = ActiveRecord::Base.connection + @connection.execute <<-SQL + CREATE VIEW printed_books + AS SELECT id, name, status, format FROM books WHERE format = 'paperback' + SQL + end + + teardown do + @connection.execute "DROP VIEW printed_books" if @connection.table_exists? "printed_books" + end + + def test_update_record + book = PrintedBook.first + book.name = "AWDwR" + book.save! + book.reload + assert_equal "AWDwR", book.name + end + + def test_insert_record + PrintedBook.create! name: "Rails in Action", status: 0, format: "paperback" + + new_book = PrintedBook.last + assert_equal "Rails in Action", new_book.name + end + + def test_update_record_to_fail_view_conditions + book = PrintedBook.first + book.format = "ebook" + book.save! + + assert_raises ActiveRecord::RecordNotFound do + book.reload + end + end +end +end # end fo `if current_adapter?(:MysqlAdapter, :Mysql2Adapter, :PostgreSQLAdapter)` +end # end fo `if ActiveRecord::Base.connection.supports_views?` + +if ActiveRecord::Base.connection.respond_to?(:supports_materialized_views?) && + ActiveRecord::Base.connection.supports_materialized_views? +class MaterializedViewTest < ActiveRecord::PostgreSQLTestCase + include ViewBehavior + + private + def create_view(name, query) + @connection.execute "CREATE MATERIALIZED VIEW #{name} AS #{query}" + end + + def drop_view(name) + @connection.execute "DROP MATERIALIZED VIEW #{name}" if @connection.table_exists? name + + end +end end diff --git a/activerecord/test/models/face.rb b/activerecord/test/models/face.rb index 91e46f83e5..af76fea52c 100644 --- a/activerecord/test/models/face.rb +++ b/activerecord/test/models/face.rb @@ -1,7 +1,7 @@ class Face < ActiveRecord::Base belongs_to :man, :inverse_of => :face belongs_to :polymorphic_man, :polymorphic => true, :inverse_of => :polymorphic_face - # Oracle identifier lengh is limited to 30 bytes or less, `polymorphic` renamed `poly` + # Oracle identifier length is limited to 30 bytes or less, `polymorphic` renamed `poly` belongs_to :poly_man_without_inverse, :polymorphic => true # These is a "broken" inverse_of for the purposes of testing belongs_to :horrible_man, :class_name => 'Man', :inverse_of => :horrible_face diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb index d17270021a..176bc79dc7 100644 --- a/activerecord/test/models/topic.rb +++ b/activerecord/test/models/topic.rb @@ -86,7 +86,7 @@ class Topic < ActiveRecord::Base end def destroy_children - self.class.delete_all "parent_id = #{id}" + self.class.where("parent_id = #{id}").delete_all end def set_email_address |