From 1dca75c2c8f930b58d86cd2216af5e14307b3e53 Mon Sep 17 00:00:00 2001 From: Greg Navis Date: Sat, 13 May 2017 03:09:58 +0200 Subject: Add support for PostgreSQL operator classes to add_index Add support for specifying non-default operator classes in PostgreSQL indexes. An example CREATE INDEX query that becomes possible is: CREATE INDEX users_name ON users USING gist (name gist_trgm_ops); Previously it was possible to specify the `gist` index but not the custom operator class. The `add_index` call for the above query is: add_index :users, :name, using: :gist, opclasses: {name: :gist_trgm_ops} --- .../adapters/postgresql/active_schema_test.rb | 3 ++ .../adapters/postgresql/postgresql_adapter_test.rb | 10 +++---- .../test/cases/adapters/postgresql/schema_test.rb | 32 ++++++++++++++++++++++ 3 files changed, 40 insertions(+), 5 deletions(-) (limited to 'activerecord/test/cases') diff --git a/activerecord/test/cases/adapters/postgresql/active_schema_test.rb b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb index 9929237546..99c53dadeb 100644 --- a/activerecord/test/cases/adapters/postgresql/active_schema_test.rb +++ b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb @@ -59,6 +59,9 @@ class PostgresqlActiveSchemaTest < ActiveRecord::PostgreSQLTestCase assert_equal expected, add_index(:people, "lower(last_name)", using: type, unique: true) end + expected = %(CREATE INDEX "index_people_on_last_name" ON "people" USING gist ("last_name" bpchar_pattern_ops)) + assert_equal expected, add_index(:people, :last_name, using: :gist, opclass: { last_name: :bpchar_pattern_ops }) + assert_raise ArgumentError do add_index(:people, :last_name, algorithm: :copy) end diff --git a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb index f199519d86..1951230c8a 100644 --- a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb +++ b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb @@ -248,12 +248,12 @@ module ActiveRecord def test_index_with_opclass with_example_table do - @connection.add_index "ex", "data varchar_pattern_ops" - index = @connection.indexes("ex").find { |idx| idx.name == "index_ex_on_data_varchar_pattern_ops" } - assert_equal "data varchar_pattern_ops", index.columns + @connection.add_index "ex", "data", opclass: "varchar_pattern_ops" + index = @connection.indexes("ex").find { |idx| idx.name == "index_ex_on_data" } + assert_equal ["data"], index.columns - @connection.remove_index "ex", "data varchar_pattern_ops" - assert_not @connection.indexes("ex").find { |idx| idx.name == "index_ex_on_data_varchar_pattern_ops" } + @connection.remove_index "ex", "data" + assert_not @connection.indexes("ex").find { |idx| idx.name == "index_ex_on_data" } end end diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb index 5a64da028b..a6ae18a826 100644 --- a/activerecord/test/cases/adapters/postgresql/schema_test.rb +++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb @@ -500,6 +500,38 @@ class SchemaForeignKeyTest < ActiveRecord::PostgreSQLTestCase end end +class SchemaIndexOpclassTest < ActiveRecord::PostgreSQLTestCase + include SchemaDumpingHelper + + setup do + @connection = ActiveRecord::Base.connection + @connection.create_table "trains" do |t| + t.string :name + t.text :description + end + end + + teardown do + @connection.drop_table "trains", if_exists: true + end + + def test_string_opclass_is_dumped + @connection.execute "CREATE INDEX trains_name_and_description ON trains USING btree(name text_pattern_ops, description text_pattern_ops)" + + output = dump_table_schema "trains" + + assert_match(/opclass: "text_pattern_ops"/, output) + end + + def test_non_default_opclass_is_dumped + @connection.execute "CREATE INDEX trains_name_and_description ON trains USING btree(name, description text_pattern_ops)" + + output = dump_table_schema "trains" + + assert_match(/opclass: \{"description"=>"text_pattern_ops"\}/, output) + end +end + class DefaultsUsingMultipleSchemasAndDomainTest < ActiveRecord::PostgreSQLTestCase setup do @connection = ActiveRecord::Base.connection -- cgit v1.2.3 From 0dc3b6535dfb8127f568bb4abc2fb09118ce4b96 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Fri, 1 Dec 2017 11:49:36 +0900 Subject: Maintain raising `RecordNotFound` for class level `update` and` destroy` In 35836019, class level `update` and `destroy` suppressed `RecordNotFound` to ensure returning affected objects. But `RecordNotFound` is a common exception caught by a `rescue_from` handler. So changing the behavior when a typical `params[:id]` is passed has a compatibility problem. The previous behavior should not be changed. Fixes #31301. --- activerecord/test/cases/persistence_test.rb | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'activerecord/test/cases') diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index f088c064f5..643032e7cf 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -473,10 +473,18 @@ class PersistenceTest < ActiveRecord::TestCase assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) } end - def test_record_not_found_exception + def test_find_raises_record_not_found_exception assert_raise(ActiveRecord::RecordNotFound) { Topic.find(99999) } end + def test_update_raises_record_not_found_exception + assert_raise(ActiveRecord::RecordNotFound) { Topic.update(99999, approved: true) } + end + + def test_destroy_raises_record_not_found_exception + assert_raise(ActiveRecord::RecordNotFound) { Topic.destroy(99999) } + end + def test_update_all assert_equal Topic.count, Topic.update_all("content = 'bulk updated!'") assert_equal "bulk updated!", Topic.find(1).content @@ -938,7 +946,9 @@ class PersistenceTest < ActiveRecord::TestCase should_not_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world") Topic.find(1).replies << should_not_be_destroyed_reply - assert_nil Topic.where("1=0").scoping { Topic.destroy(1) } + assert_raise(ActiveRecord::RecordNotFound) do + Topic.where("1=0").scoping { Topic.destroy(1) } + end assert_nothing_raised { Topic.find(1) } assert_nothing_raised { Reply.find(should_not_be_destroyed_reply.id) } -- cgit v1.2.3 From 695ec1fef0b7fdb3125a3e2e4364a7f449265c1b Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Fri, 1 Dec 2017 18:21:21 +0900 Subject: Class level `update` and `destroy` checks all the records exist before making changes (#31306) It makes more sense than ignoring invalid IDs. --- activerecord/test/cases/persistence_test.rb | 41 ++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 4 deletions(-) (limited to 'activerecord/test/cases') diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index 643032e7cf..4aea275d51 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -70,7 +70,7 @@ class PersistenceTest < ActiveRecord::TestCase end def test_update_many - topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" }, nil => {} } + topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } } updated = Topic.update(topic_data.keys, topic_data.values) assert_equal [1, 2], updated.map(&:id) @@ -78,10 +78,33 @@ class PersistenceTest < ActiveRecord::TestCase assert_equal "2 updated", Topic.find(2).content end + def test_update_many_with_duplicated_ids + updated = Topic.update([1, 1, 2], [ + { "content" => "1 duplicated" }, { "content" => "1 updated" }, { "content" => "2 updated" } + ]) + + assert_equal [1, 1, 2], updated.map(&:id) + assert_equal "1 updated", Topic.find(1).content + assert_equal "2 updated", Topic.find(2).content + end + + def test_update_many_with_invalid_id + topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" }, 99999 => {} } + + assert_raise(ActiveRecord::RecordNotFound) do + Topic.where("1=0").scoping { Topic.update(topic_data.keys, topic_data.values) } + end + + assert_not_equal "1 updated", Topic.find(1).content + assert_not_equal "2 updated", Topic.find(2).content + end + def test_class_level_update_is_affected_by_scoping topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } } - assert_equal [], Topic.where("1=0").scoping { Topic.update(topic_data.keys, topic_data.values) } + assert_raise(ActiveRecord::RecordNotFound) do + Topic.where("1=0").scoping { Topic.update(topic_data.keys, topic_data.values) } + end assert_not_equal "1 updated", Topic.find(1).content assert_not_equal "2 updated", Topic.find(2).content @@ -175,15 +198,25 @@ class PersistenceTest < ActiveRecord::TestCase end def test_destroy_many - clients = Client.all.merge!(order: "id").find([2, 3]) + clients = Client.find([2, 3]) assert_difference("Client.count", -2) do - destroyed = Client.destroy([2, 3, nil]).sort_by(&:id) + destroyed = Client.destroy([2, 3]) assert_equal clients, destroyed assert destroyed.all?(&:frozen?), "destroyed clients should be frozen" end end + def test_destroy_many_with_invalid_id + clients = Client.find([2, 3]) + + assert_raise(ActiveRecord::RecordNotFound) do + Client.destroy([2, 3, 99999]) + end + + assert_equal clients, Client.find([2, 3]) + end + def test_becomes assert_kind_of Reply, topics(:first).becomes(Reply) assert_equal "The First Topic", topics(:first).becomes(Reply).title -- cgit v1.2.3 From 05d1e9e413a87bee5b0c1c200fa9f84dba0e1d15 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Fri, 1 Dec 2017 18:28:31 +0900 Subject: Remove unnecessary scoping --- activerecord/test/cases/persistence_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/test/cases') diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index 4aea275d51..4cc66a2e49 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -92,7 +92,7 @@ class PersistenceTest < ActiveRecord::TestCase topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" }, 99999 => {} } assert_raise(ActiveRecord::RecordNotFound) do - Topic.where("1=0").scoping { Topic.update(topic_data.keys, topic_data.values) } + Topic.update(topic_data.keys, topic_data.values) end assert_not_equal "1 updated", Topic.find(1).content -- cgit v1.2.3 From 8203482a9ef9bd60d5014745fd7af868d9954b1d Mon Sep 17 00:00:00 2001 From: Travis Hunter Date: Fri, 20 Jan 2017 16:18:46 -0500 Subject: Add support for invalid foreign keys in Postgres Add validate_constraint and update naming --- .../test/cases/migration/foreign_key_test.rb | 68 ++++++++++++++++++++++ 1 file changed, 68 insertions(+) (limited to 'activerecord/test/cases') diff --git a/activerecord/test/cases/migration/foreign_key_test.rb b/activerecord/test/cases/migration/foreign_key_test.rb index 499d072de5..079be04946 100644 --- a/activerecord/test/cases/migration/foreign_key_test.rb +++ b/activerecord/test/cases/migration/foreign_key_test.rb @@ -227,6 +227,74 @@ if ActiveRecord::Base.connection.supports_foreign_keys? end end + if ActiveRecord::Base.connection.supports_validate_constraints? + def test_add_invalid_foreign_key + @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", validate: false + + foreign_keys = @connection.foreign_keys("astronauts") + assert_equal 1, foreign_keys.size + + fk = foreign_keys.first + refute fk.validated? + end + + def test_validate_foreign_key_infers_column + @connection.add_foreign_key :astronauts, :rockets, validate: false + refute @connection.foreign_keys("astronauts").first.validated? + + @connection.validate_foreign_key :astronauts, :rockets + assert @connection.foreign_keys("astronauts").first.validated? + end + + def test_validate_foreign_key_by_column + @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", validate: false + refute @connection.foreign_keys("astronauts").first.validated? + + @connection.validate_foreign_key :astronauts, column: "rocket_id" + assert @connection.foreign_keys("astronauts").first.validated? + end + + def test_validate_foreign_key_by_symbol_column + @connection.add_foreign_key :astronauts, :rockets, column: :rocket_id, validate: false + refute @connection.foreign_keys("astronauts").first.validated? + + @connection.validate_foreign_key :astronauts, column: :rocket_id + assert @connection.foreign_keys("astronauts").first.validated? + end + + def test_validate_foreign_key_by_name + @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", name: "fancy_named_fk", validate: false + refute @connection.foreign_keys("astronauts").first.validated? + + @connection.validate_foreign_key :astronauts, name: "fancy_named_fk" + assert @connection.foreign_keys("astronauts").first.validated? + end + + def test_validate_foreign_non_existing_foreign_key_raises + assert_raises ArgumentError do + @connection.validate_foreign_key :astronauts, :rockets + end + end + + def test_validate_constraint_by_name + @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", name: "fancy_named_fk", validate: false + + @connection.validate_constraint :astronauts, "fancy_named_fk" + assert @connection.foreign_keys("astronauts").first.validated? + end + else + # Foreign key should still be created, but should not be invalid + def test_add_invalid_foreign_key + @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", validate: false + + foreign_keys = @connection.foreign_keys("astronauts") + assert_equal 1, foreign_keys.size + + fk = foreign_keys.first + assert fk.validated? + end + end + def test_schema_dumping @connection.add_foreign_key :astronauts, :rockets output = dump_table_schema "astronauts" -- cgit v1.2.3 From dd6338a0699f2301d4b2fc8653688b4c4183cee5 Mon Sep 17 00:00:00 2001 From: Dinah Shi Date: Sat, 2 Dec 2017 14:30:10 +0800 Subject: Extract sql fragment generators for alter table from PostgreSQL adapter --- .../test/cases/adapters/mysql2/connection_test.rb | 4 ++-- activerecord/test/cases/migration/compatibility_test.rb | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) (limited to 'activerecord/test/cases') diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb index e61c70848a..13b4096671 100644 --- a/activerecord/test/cases/adapters/mysql2/connection_test.rb +++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb @@ -174,10 +174,10 @@ class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase assert_equal "SCHEMA", @subscriber.logged[0][1] end - def test_logs_name_rename_column_sql + def test_logs_name_rename_column_for_alter @connection.execute "CREATE TABLE `bar_baz` (`foo` varchar(255))" @subscriber.logged.clear - @connection.send(:rename_column_sql, "bar_baz", "foo", "foo2") + @connection.send(:rename_column_for_alter, "bar_baz", "foo", "foo2") assert_equal "SCHEMA", @subscriber.logged[0][1] ensure @connection.execute "DROP TABLE `bar_baz`" diff --git a/activerecord/test/cases/migration/compatibility_test.rb b/activerecord/test/cases/migration/compatibility_test.rb index 2fef2f796e..0fd55e6ae4 100644 --- a/activerecord/test/cases/migration/compatibility_test.rb +++ b/activerecord/test/cases/migration/compatibility_test.rb @@ -126,6 +126,23 @@ module ActiveRecord end assert_match(/LegacyMigration < ActiveRecord::Migration\[4\.2\]/, e.message) end + + if current_adapter?(:PostgreSQLAdapter) + class Testing < ActiveRecord::Base + end + + def test_legacy_change_column_with_null_executes_update + migration = Class.new(ActiveRecord::Migration[5.1]) { + def migrate(x) + change_column :testings, :foo, :string, null: false, default: "foobar" + end + }.new + + t = Testing.create! + ActiveRecord::Migrator.new(:up, [migration]).migrate + assert_equal ["foobar"], Testing.all.map(&:foo) + end + end end end end -- cgit v1.2.3 From 01fb0907b2dafcecf63eedb80408d4cbff5f4d23 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Sun, 3 Dec 2017 04:57:04 +0900 Subject: Refactor `length`, `order`, and `opclass` index options dumping --- .../test/cases/adapters/postgresql/schema_test.rb | 6 +++--- activerecord/test/cases/schema_dumper_test.rb | 24 +++++++++++++++++++--- 2 files changed, 24 insertions(+), 6 deletions(-) (limited to 'activerecord/test/cases') diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb index a6ae18a826..1126908761 100644 --- a/activerecord/test/cases/adapters/postgresql/schema_test.rb +++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb @@ -459,7 +459,7 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase assert_equal :btree, index_d.using assert_equal :gin, index_e.using - assert_equal :desc, index_d.orders[INDEX_D_COLUMN] + assert_equal :desc, index_d.orders end end @@ -520,7 +520,7 @@ class SchemaIndexOpclassTest < ActiveRecord::PostgreSQLTestCase output = dump_table_schema "trains" - assert_match(/opclass: "text_pattern_ops"/, output) + assert_match(/opclass: :text_pattern_ops/, output) end def test_non_default_opclass_is_dumped @@ -528,7 +528,7 @@ class SchemaIndexOpclassTest < ActiveRecord::PostgreSQLTestCase output = dump_table_schema "trains" - assert_match(/opclass: \{"description"=>"text_pattern_ops"\}/, output) + assert_match(/opclass: \{ description: :text_pattern_ops \}/, output) end end diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index ac5092f1c1..a612ce9bb2 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -177,14 +177,14 @@ class SchemaDumperTest < ActiveRecord::TestCase def test_schema_dumps_index_columns_in_right_order index_definition = dump_table_schema("companies").split(/\n/).grep(/t\.index.*company_index/).first.strip - if current_adapter?(:PostgreSQLAdapter, :SQLite3Adapter) - assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index", order: { rating: :desc }', index_definition - elsif current_adapter?(:Mysql2Adapter) + if current_adapter?(:Mysql2Adapter) if ActiveRecord::Base.connection.supports_index_sort_order? assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index", length: { type: 10 }, order: { rating: :desc }', index_definition else assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index", length: { type: 10 }', index_definition end + elsif ActiveRecord::Base.connection.supports_index_sort_order? + assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index", order: { rating: :desc }', index_definition else assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index"', index_definition end @@ -199,6 +199,24 @@ class SchemaDumperTest < ActiveRecord::TestCase end end + def test_schema_dumps_index_sort_order + index_definition = dump_table_schema("companies").split(/\n/).grep(/t\.index.*_name_and_rating/).first.strip + if ActiveRecord::Base.connection.supports_index_sort_order? + assert_equal 't.index ["name", "rating"], name: "index_companies_on_name_and_rating", order: :desc', index_definition + else + assert_equal 't.index ["name", "rating"], name: "index_companies_on_name_and_rating"', index_definition + end + end + + def test_schema_dumps_index_length + index_definition = dump_table_schema("companies").split(/\n/).grep(/t\.index.*_name_and_description/).first.strip + if current_adapter?(:Mysql2Adapter) + assert_equal 't.index ["name", "description"], name: "index_companies_on_name_and_description", length: 10', index_definition + else + assert_equal 't.index ["name", "description"], name: "index_companies_on_name_and_description"', index_definition + end + end + def test_schema_dump_should_honor_nonstandard_primary_keys output = standard_dump match = output.match(%r{create_table "movies"(.*)do}) -- cgit v1.2.3 From 3040446cece8e7a6d9e29219e636e13f180a1e03 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Sun, 3 Dec 2017 06:33:56 +0900 Subject: Fix warning: assigned but unused variable - t --- activerecord/test/cases/migration/compatibility_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/test/cases') diff --git a/activerecord/test/cases/migration/compatibility_test.rb b/activerecord/test/cases/migration/compatibility_test.rb index 0fd55e6ae4..f1defe1a8a 100644 --- a/activerecord/test/cases/migration/compatibility_test.rb +++ b/activerecord/test/cases/migration/compatibility_test.rb @@ -138,7 +138,7 @@ module ActiveRecord end }.new - t = Testing.create! + Testing.create! ActiveRecord::Migrator.new(:up, [migration]).migrate assert_equal ["foobar"], Testing.all.map(&:foo) end -- cgit v1.2.3 From 5358f2b67bd6fb12d708527a4a70dcab65513c5e Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Sun, 3 Dec 2017 07:55:34 +0900 Subject: Fix `test_add_column_with_timestamp_type` failure This test failed due to dirty schema cache. It is needed to call `clear_cache!` when using same named table with different definition. https://travis-ci.org/rails/rails/jobs/310627767#L769-L772 --- activerecord/test/cases/migration/change_schema_test.rb | 13 ++++++------- activerecord/test/cases/migration/compatibility_test.rb | 2 ++ 2 files changed, 8 insertions(+), 7 deletions(-) (limited to 'activerecord/test/cases') diff --git a/activerecord/test/cases/migration/change_schema_test.rb b/activerecord/test/cases/migration/change_schema_test.rb index 7b0644e9c0..43aac5da75 100644 --- a/activerecord/test/cases/migration/change_schema_test.rb +++ b/activerecord/test/cases/migration/change_schema_test.rb @@ -264,19 +264,18 @@ module ActiveRecord t.column :foo, :timestamp end - klass = Class.new(ActiveRecord::Base) - klass.table_name = "testings" + column = connection.columns(:testings).find { |c| c.name == "foo" } - assert_equal :datetime, klass.columns_hash["foo"].type + assert_equal :datetime, column.type if current_adapter?(:PostgreSQLAdapter) - assert_equal "timestamp without time zone", klass.columns_hash["foo"].sql_type + assert_equal "timestamp without time zone", column.sql_type elsif current_adapter?(:Mysql2Adapter) - assert_equal "timestamp", klass.columns_hash["foo"].sql_type + assert_equal "timestamp", column.sql_type elsif current_adapter?(:OracleAdapter) - assert_equal "TIMESTAMP(6)", klass.columns_hash["foo"].sql_type + assert_equal "TIMESTAMP(6)", column.sql_type else - assert_equal klass.connection.type_to_sql("datetime"), klass.columns_hash["foo"].sql_type + assert_equal klass.connection.type_to_sql("datetime"), column.sql_type end end diff --git a/activerecord/test/cases/migration/compatibility_test.rb b/activerecord/test/cases/migration/compatibility_test.rb index f1defe1a8a..e6ca252369 100644 --- a/activerecord/test/cases/migration/compatibility_test.rb +++ b/activerecord/test/cases/migration/compatibility_test.rb @@ -141,6 +141,8 @@ module ActiveRecord Testing.create! ActiveRecord::Migrator.new(:up, [migration]).migrate assert_equal ["foobar"], Testing.all.map(&:foo) + ensure + ActiveRecord::Base.clear_cache! end end end -- cgit v1.2.3 From 32516983a67f10d513370edd67b41ef0e011b175 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Sun, 3 Dec 2017 08:05:32 +0900 Subject: Fix `s/klass.connection/connection/` `klass` has removed in 5358f2b67bd6fb12d708527a4a70dcab65513c5e. --- activerecord/test/cases/migration/change_schema_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/test/cases') diff --git a/activerecord/test/cases/migration/change_schema_test.rb b/activerecord/test/cases/migration/change_schema_test.rb index 43aac5da75..38a906c8f5 100644 --- a/activerecord/test/cases/migration/change_schema_test.rb +++ b/activerecord/test/cases/migration/change_schema_test.rb @@ -275,7 +275,7 @@ module ActiveRecord elsif current_adapter?(:OracleAdapter) assert_equal "TIMESTAMP(6)", column.sql_type else - assert_equal klass.connection.type_to_sql("datetime"), column.sql_type + assert_equal connection.type_to_sql("datetime"), column.sql_type end end -- cgit v1.2.3 From d0f5dce492696019ddf409892829f89bee5f45ef Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Sun, 3 Dec 2017 15:00:05 +0900 Subject: `change_column_default` should be executed after type changing If do not execute a type changing first, filling in default value may be failed. ``` % ARCONN=postgresql be ruby -w -Itest test/cases/migration/compatibility_test.rb -n test_legacy_change_column_with_null_executes_update Using postgresql Run options: -n test_legacy_change_column_with_null_executes_update --seed 20459 E Error: ActiveRecord::Migration::CompatibilityTest#test_legacy_change_column_with_null_executes_update: StandardError: An error has occurred, this and all later migrations canceled: PG::StringDataRightTruncation: ERROR: value too long for type character varying(5) : UPDATE "testings" SET "foo"='foobar' WHERE "foo" IS NULL ``` --- activerecord/test/cases/migration/compatibility_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord/test/cases') diff --git a/activerecord/test/cases/migration/compatibility_test.rb b/activerecord/test/cases/migration/compatibility_test.rb index e6ca252369..cc2391f349 100644 --- a/activerecord/test/cases/migration/compatibility_test.rb +++ b/activerecord/test/cases/migration/compatibility_test.rb @@ -16,7 +16,7 @@ module ActiveRecord ActiveRecord::Migration.verbose = false connection.create_table :testings do |t| - t.column :foo, :string, limit: 100 + t.column :foo, :string, limit: 5 t.column :bar, :string, limit: 100 end end @@ -134,7 +134,7 @@ module ActiveRecord def test_legacy_change_column_with_null_executes_update migration = Class.new(ActiveRecord::Migration[5.1]) { def migrate(x) - change_column :testings, :foo, :string, null: false, default: "foobar" + change_column :testings, :foo, :string, limit: 10, null: false, default: "foobar" end }.new -- cgit v1.2.3 From 70fa9e9ab7fd89589664ecd7ee367448ef45f9d8 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Sun, 3 Dec 2017 15:28:10 +0900 Subject: Emulate JSON types for SQLite3 adapter (#29664) Actually SQLite3 doesn't have JSON storage class (so it is stored as a TEXT like Date and Time). But emulating JSON types is convinient for making database agnostic migrations. --- activerecord/test/cases/adapters/sqlite3/json_test.rb | 4 ++-- activerecord/test/cases/json_shared_test_cases.rb | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'activerecord/test/cases') diff --git a/activerecord/test/cases/adapters/sqlite3/json_test.rb b/activerecord/test/cases/adapters/sqlite3/json_test.rb index 568a524058..6f247fcd22 100644 --- a/activerecord/test/cases/adapters/sqlite3/json_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/json_test.rb @@ -9,8 +9,8 @@ class SQLite3JSONTest < ActiveRecord::SQLite3TestCase def setup super @connection.create_table("json_data_type") do |t| - t.column "payload", :json, default: {} - t.column "settings", :json + t.json "payload", default: {} + t.json "settings" end end diff --git a/activerecord/test/cases/json_shared_test_cases.rb b/activerecord/test/cases/json_shared_test_cases.rb index a71485982c..56ec8c8a82 100644 --- a/activerecord/test/cases/json_shared_test_cases.rb +++ b/activerecord/test/cases/json_shared_test_cases.rb @@ -30,7 +30,6 @@ module JSONSharedTestCases end def test_change_table_supports_json - skip unless @connection.supports_json? @connection.change_table("json_data_type") do |t| t.public_send column_type, "users" end @@ -41,7 +40,6 @@ module JSONSharedTestCases end def test_schema_dumping - skip unless @connection.supports_json? output = dump_table_schema("json_data_type") assert_match(/t\.#{column_type}\s+"settings"/, output) end -- cgit v1.2.3 From 9b823c02b62a91216ef1ad0c9d4fca095377afb6 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Sun, 3 Dec 2017 15:45:40 +0900 Subject: SQLite3 valid integer value should be 8 bytes (64-bit signed integer) (#28379) This is a regression since Rails 4.2. SQLite3 integer is stored in 1, 2, 3, 4, 6, or 8 bytes depending on the magnitude of the value. Assuming default valid value as 4 bytes caused that actual valid value in INTEGER storage class cannot be stored and existing value cannot be found. https://www.sqlite.org/datatype3.html We should allow valid value in INTEGER storage class in SQLite3 to fix the regression. Fixes #22594. --- activerecord/test/cases/base_test.rb | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'activerecord/test/cases') diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index d79afa2ee9..3497f6aae4 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -891,11 +891,9 @@ class BasicsTest < ActiveRecord::TestCase assert_equal 2147483648, company.rating end - unless current_adapter?(:SQLite3Adapter) - def test_bignum_pk - company = Company.create!(id: 2147483648, name: "foo") - assert_equal company, Company.find(company.id) - end + def test_bignum_pk + company = Company.create!(id: 2147483648, name: "foo") + assert_equal company, Company.find(company.id) end # TODO: extend defaults tests to other databases! -- cgit v1.2.3 From 29d081c47a43cc5d979836541cb73a816c95981d Mon Sep 17 00:00:00 2001 From: Yasuo Honda Date: Tue, 5 Dec 2017 19:16:11 +0000 Subject: Execute `JsonAttributeTest` only if `supports_json?` returns `true` Oracle enhanced adapter does not fully support JSON datatype then `supports_json?` returns `false`. I wanted to skip known failures and errors when tested with Oracle enhanced adapter. --- activerecord/test/cases/json_attribute_test.rb | 46 ++++++++++++++------------ 1 file changed, 24 insertions(+), 22 deletions(-) (limited to 'activerecord/test/cases') diff --git a/activerecord/test/cases/json_attribute_test.rb b/activerecord/test/cases/json_attribute_test.rb index 63f3c77fc3..a6fd4f34dc 100644 --- a/activerecord/test/cases/json_attribute_test.rb +++ b/activerecord/test/cases/json_attribute_test.rb @@ -3,33 +3,35 @@ require "cases/helper" require "cases/json_shared_test_cases" -class JsonAttributeTest < ActiveRecord::TestCase - include JSONSharedTestCases - self.use_transactional_tests = false +if ActiveRecord::Base.connection.supports_json? + class JsonAttributeTest < ActiveRecord::TestCase + include JSONSharedTestCases + self.use_transactional_tests = false - class JsonDataTypeOnText < ActiveRecord::Base - self.table_name = "json_data_type" + class JsonDataTypeOnText < ActiveRecord::Base + self.table_name = "json_data_type" - attribute :payload, :json - attribute :settings, :json + attribute :payload, :json + attribute :settings, :json - store_accessor :settings, :resolution - end - - def setup - super - @connection.create_table("json_data_type") do |t| - t.text "payload" - t.text "settings" + store_accessor :settings, :resolution end - end - private - def column_type - :text + def setup + super + @connection.create_table("json_data_type") do |t| + t.text "payload" + t.text "settings" + end end - def klass - JsonDataTypeOnText - end + private + def column_type + :text + end + + def klass + JsonDataTypeOnText + end + end end -- cgit v1.2.3 From d2321aacc7451f6b4f154d894adcb76820abba3c Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Wed, 6 Dec 2017 08:30:27 +0900 Subject: Revert "Merge pull request #31341 from yahonda/skip_json_attribute_test" This reverts commit 23226d04f921b79f0077ba38c5a5a923b6d43f89, reversing changes made to 7544cf7603959f25100b21f70b5e70354bed7e45. --- activerecord/test/cases/json_attribute_test.rb | 46 ++++++++++++-------------- 1 file changed, 22 insertions(+), 24 deletions(-) (limited to 'activerecord/test/cases') diff --git a/activerecord/test/cases/json_attribute_test.rb b/activerecord/test/cases/json_attribute_test.rb index a6fd4f34dc..63f3c77fc3 100644 --- a/activerecord/test/cases/json_attribute_test.rb +++ b/activerecord/test/cases/json_attribute_test.rb @@ -3,35 +3,33 @@ require "cases/helper" require "cases/json_shared_test_cases" -if ActiveRecord::Base.connection.supports_json? - class JsonAttributeTest < ActiveRecord::TestCase - include JSONSharedTestCases - self.use_transactional_tests = false +class JsonAttributeTest < ActiveRecord::TestCase + include JSONSharedTestCases + self.use_transactional_tests = false - class JsonDataTypeOnText < ActiveRecord::Base - self.table_name = "json_data_type" + class JsonDataTypeOnText < ActiveRecord::Base + self.table_name = "json_data_type" - attribute :payload, :json - attribute :settings, :json + attribute :payload, :json + attribute :settings, :json - store_accessor :settings, :resolution - end + store_accessor :settings, :resolution + end - def setup - super - @connection.create_table("json_data_type") do |t| - t.text "payload" - t.text "settings" - end + def setup + super + @connection.create_table("json_data_type") do |t| + t.text "payload" + t.text "settings" end + end - private - def column_type - :text - end + private + def column_type + :text + end - def klass - JsonDataTypeOnText - end - end + def klass + JsonDataTypeOnText + end end -- cgit v1.2.3 From d721344280346f67c7d2dbffa9eaf9341e73673d Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Wed, 6 Dec 2017 08:33:16 +0900 Subject: Use `:string` instead of `:text` for `JsonAttributeTest` Since CLOB data type has many limitations in Oracle SELECT WHERE clause. --- activerecord/test/cases/json_attribute_test.rb | 6 +++--- activerecord/test/cases/json_shared_test_cases.rb | 9 +++++++-- 2 files changed, 10 insertions(+), 5 deletions(-) (limited to 'activerecord/test/cases') diff --git a/activerecord/test/cases/json_attribute_test.rb b/activerecord/test/cases/json_attribute_test.rb index 63f3c77fc3..afc39d0420 100644 --- a/activerecord/test/cases/json_attribute_test.rb +++ b/activerecord/test/cases/json_attribute_test.rb @@ -19,14 +19,14 @@ class JsonAttributeTest < ActiveRecord::TestCase def setup super @connection.create_table("json_data_type") do |t| - t.text "payload" - t.text "settings" + t.string "payload" + t.string "settings" end end private def column_type - :text + :string end def klass diff --git a/activerecord/test/cases/json_shared_test_cases.rb b/activerecord/test/cases/json_shared_test_cases.rb index 56ec8c8a82..012aaffde1 100644 --- a/activerecord/test/cases/json_shared_test_cases.rb +++ b/activerecord/test/cases/json_shared_test_cases.rb @@ -23,7 +23,7 @@ module JSONSharedTestCases def test_column column = klass.columns_hash["payload"] assert_equal column_type, column.type - assert_equal column_type.to_s, column.sql_type + assert_type_match column_type, column.sql_type type = klass.type_for_attribute("payload") assert_not type.binary? @@ -36,7 +36,7 @@ module JSONSharedTestCases klass.reset_column_information column = klass.columns_hash["users"] assert_equal column_type, column.type - assert_equal column_type.to_s, column.sql_type + assert_type_match column_type, column.sql_type end def test_schema_dumping @@ -253,4 +253,9 @@ module JSONSharedTestCases def klass JsonDataType end + + def assert_type_match(type, sql_type) + native_type = ActiveRecord::Base.connection.native_database_types[type][:name] + assert_match %r(\A#{native_type}\b), sql_type + end end -- cgit v1.2.3 From b34838cbe29434212eb81ca1e31bb0ab0765e32a Mon Sep 17 00:00:00 2001 From: Yasuo Honda Date: Wed, 6 Dec 2017 13:09:51 +0000 Subject: Address `ActiveRecord::NotNullViolation: OCIError: ORA-01400` for Oracle database which requires primary key value mentioned in insert statement explicitly. --- activerecord/test/cases/json_shared_test_cases.rb | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'activerecord/test/cases') diff --git a/activerecord/test/cases/json_shared_test_cases.rb b/activerecord/test/cases/json_shared_test_cases.rb index 012aaffde1..b0c0f2c283 100644 --- a/activerecord/test/cases/json_shared_test_cases.rb +++ b/activerecord/test/cases/json_shared_test_cases.rb @@ -66,26 +66,26 @@ module JSONSharedTestCases end def test_rewrite - @connection.execute(%q|insert into json_data_type (payload) VALUES ('{"k":"v"}')|) + @connection.execute(insert_statement_per_database('{"k":"v"}')) x = klass.first x.payload = { '"a\'' => "b" } assert x.save! end def test_select - @connection.execute(%q|insert into json_data_type (payload) VALUES ('{"k":"v"}')|) + @connection.execute(insert_statement_per_database('{"k":"v"}')) x = klass.first assert_equal({ "k" => "v" }, x.payload) end def test_select_multikey - @connection.execute(%q|insert into json_data_type (payload) VALUES ('{"k1":"v1", "k2":"v2", "k3":[1,2,3]}')|) + @connection.execute(insert_statement_per_database('{"k1":"v1", "k2":"v2", "k3":[1,2,3]}')) x = klass.first assert_equal({ "k1" => "v1", "k2" => "v2", "k3" => [1, 2, 3] }, x.payload) end def test_null_json - @connection.execute("insert into json_data_type (payload) VALUES(null)") + @connection.execute(insert_statement_per_database("null")) x = klass.first assert_nil(x.payload) end @@ -107,13 +107,13 @@ module JSONSharedTestCases end def test_select_array_json_value - @connection.execute(%q|insert into json_data_type (payload) VALUES ('["v0",{"k1":"v1"}]')|) + @connection.execute(insert_statement_per_database('["v0",{"k1":"v1"}]')) x = klass.first assert_equal(["v0", { "k1" => "v1" }], x.payload) end def test_rewrite_array_json_value - @connection.execute(%q|insert into json_data_type (payload) VALUES ('["v0",{"k1":"v1"}]')|) + @connection.execute(insert_statement_per_database('["v0",{"k1":"v1"}]')) x = klass.first x.payload = ["v1", { "k2" => "v2" }, "v3"] assert x.save! @@ -258,4 +258,12 @@ module JSONSharedTestCases native_type = ActiveRecord::Base.connection.native_database_types[type][:name] assert_match %r(\A#{native_type}\b), sql_type end + + def insert_statement_per_database(values) + if current_adapter?(:OracleAdapter) + "insert into json_data_type (id, payload) VALUES (json_data_type_seq.nextval, '#{values}')" + else + "insert into json_data_type (payload) VALUES ('#{values}')" + end + end end -- cgit v1.2.3 From 5ac4f4d2563e7f9c5ffaecce4be4b9e2c5b0c081 Mon Sep 17 00:00:00 2001 From: Ashley Ellis Pierce Date: Mon, 4 Dec 2017 16:44:33 -0500 Subject: Fix sqlite migrations with custom primary keys Previously, if a record was created with a custom primary key, that table could not be migrated using sqlite. While attempting to copy the table, the type of the primary key was ignored. Once that was corrected, copying the indexes would fail because custom primary keys are autoindexed by sqlite by default. To correct that, this skips copying the index if the index name begins with "sqlite_". This is a reserved word that indicates that the index is an internal schema object. SQLite prohibits applications from creating objects whose names begin with "sqlite_", so this string should be safe to use as a check. ref https://www.sqlite.org/fileformat2.html#intschema --- .../cases/adapters/sqlite3/sqlite3_adapter_test.rb | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'activerecord/test/cases') diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb index 1f057fe5c6..14f4997d5b 100644 --- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb @@ -360,6 +360,24 @@ module ActiveRecord end end + class Barcode < ActiveRecord::Base + end + + def test_existing_records_have_custom_primary_key + connection = Barcode.connection + connection.create_table(:barcodes, primary_key: "code", id: :string, limit: 42, force: true) do |t| + t.text :other_attr + end + code = "214fe0c2-dd47-46df-b53b-66090b3c1d40" + Barcode.create! code: code, other_attr: "xxx" + + connection.change_table "barcodes" do |t| + connection.remove_column("barcodes", "other_attr") + end + + assert_equal code, Barcode.first.id + end + def test_supports_extensions assert_not @conn.supports_extensions?, "does not support extensions" end -- cgit v1.2.3 From 6a8ce7416d6615a13ee5c4b9f6bcd91cc5adef4d Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Fri, 8 Dec 2017 02:37:02 +0900 Subject: Fix `scope_for_create` to do not lose polymorphic associations This regression was caused at 213796fb due to polymorphic predicates are combined by `Arel::Nodes::And`. But I'd like to keep that combined because it would help inverting polymorphic predicates correctly (e9ba12f7), and we can collect equality nodes regardless of combined by `Arel::Nodes::And` (`a AND (b AND c) AND d` == `a AND b AND c AND d`). This change fixes the regression to collect equality nodes in `Arel::Nodes::And` as well. Fixes #31338. --- activerecord/test/cases/relation_test.rb | 2 +- activerecord/test/cases/relations_test.rb | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'activerecord/test/cases') diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb index a71d8de521..b424ca91de 100644 --- a/activerecord/test/cases/relation_test.rb +++ b/activerecord/test/cases/relation_test.rb @@ -68,7 +68,7 @@ module ActiveRecord relation = Relation.new(Post, Post.arel_table, Post.predicate_builder) left = relation.table[:id].eq(10) right = relation.table[:id].eq(10) - combine = left.and right + combine = left.or(right) relation.where! combine assert_equal({}, relation.where_values_hash) end diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 50ad1d5b26..675aafabda 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -1189,6 +1189,15 @@ class RelationTest < ActiveRecord::TestCase assert_equal "hen", hen.name end + def test_create_with_polymorphic_association + author = authors(:david) + post = posts(:welcome) + comment = Comment.where(post: post, author: author).create!(body: "hello") + + assert_equal author, comment.author + assert_equal post, comment.post + end + def test_first_or_create parrot = Bird.where(color: "green").first_or_create(name: "parrot") assert_kind_of Bird, parrot -- cgit v1.2.3 From 131cc6eab64eeae6c7f508dca4176183144cf3a6 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Fri, 8 Dec 2017 10:49:25 +0900 Subject: SQLite: Fix `copy_table` with composite primary keys `connection.primary_key` also return composite primary keys, so `from_primary_key_column` may not be found even if `from_primary_key` is presented. ``` % ARCONN=sqlite3 be ruby -w -Itest test/cases/adapters/sqlite3/sqlite3_adapter_test.rb -n test_copy_table_with_composite_primary_keys Using sqlite3 Run options: -n test_copy_table_with_composite_primary_keys --seed 19041 # Running: E Error: ActiveRecord::ConnectionAdapters::SQLite3AdapterTest#test_copy_table_with_composite_primary_keys: NoMethodError: undefined method `type' for nil:NilClass /path/to/rails/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb:411:in `block in copy_table' ``` This change fixes `copy_table` to do not lose composite primary keys. --- .../cases/adapters/sqlite3/sqlite3_adapter_test.rb | 31 ++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) (limited to 'activerecord/test/cases') diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb index 14f4997d5b..1357719422 100644 --- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb @@ -361,21 +361,48 @@ module ActiveRecord end class Barcode < ActiveRecord::Base + self.primary_key = "code" end - def test_existing_records_have_custom_primary_key + def test_copy_table_with_existing_records_have_custom_primary_key connection = Barcode.connection connection.create_table(:barcodes, primary_key: "code", id: :string, limit: 42, force: true) do |t| t.text :other_attr end code = "214fe0c2-dd47-46df-b53b-66090b3c1d40" - Barcode.create! code: code, other_attr: "xxx" + Barcode.create!(code: code, other_attr: "xxx") connection.change_table "barcodes" do |t| connection.remove_column("barcodes", "other_attr") end assert_equal code, Barcode.first.id + ensure + Barcode.reset_column_information + end + + def test_copy_table_with_composite_primary_keys + connection = Barcode.connection + connection.create_table(:barcodes, primary_key: ["region", "code"], force: true) do |t| + t.string :region + t.string :code + t.text :other_attr + end + region = "US" + code = "214fe0c2-dd47-46df-b53b-66090b3c1d40" + Barcode.create!(region: region, code: code, other_attr: "xxx") + + connection.change_table "barcodes" do |t| + connection.remove_column("barcodes", "other_attr") + end + + assert_equal ["region", "code"], connection.primary_keys("barcodes") + + barcode = Barcode.first + assert_equal region, barcode.region + assert_equal code, barcode.code + ensure + Barcode.reset_column_information end def test_supports_extensions -- cgit v1.2.3