diff options
Diffstat (limited to 'activerecord/test/cases/adapters/mysql2')
17 files changed, 550 insertions, 116 deletions
diff --git a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb index cefc3e3c7e..31dc69a45b 100644 --- a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb +++ b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb @@ -1,7 +1,7 @@ require "cases/helper" require 'support/connection_helper' -class ActiveSchemaTest < ActiveRecord::TestCase +class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase include ConnectionHelper def setup @@ -59,21 +59,56 @@ class ActiveSchemaTest < ActiveRecord::TestCase assert_equal expected, add_index(:people, [:last_name, :first_name], :length => 15, :using => :btree) end - def test_drop_table - assert_equal "DROP TABLE `people`", drop_table(:people) + def test_index_in_create + def (ActiveRecord::Base.connection).table_exists?(*); false; end + + %w(SPATIAL FULLTEXT UNIQUE).each do |type| + expected = "CREATE TABLE `people` (#{type} INDEX `index_people_on_last_name` (`last_name`) ) ENGINE=InnoDB" + actual = ActiveRecord::Base.connection.create_table(:people, id: false) do |t| + t.index :last_name, type: type + end + assert_equal expected, actual + end + + expected = "CREATE TABLE `people` ( INDEX `index_people_on_last_name` USING btree (`last_name`(10)) ) ENGINE=InnoDB" + actual = ActiveRecord::Base.connection.create_table(:people, id: false) do |t| + t.index :last_name, length: 10, using: :btree + end + assert_equal expected, actual end - if current_adapter?(:Mysql2Adapter) - def test_create_mysql_database_with_encoding - assert_equal "CREATE DATABASE `matt` DEFAULT CHARACTER SET `utf8`", create_database(:matt) - assert_equal "CREATE DATABASE `aimonetti` DEFAULT CHARACTER SET `latin1`", create_database(:aimonetti, {:charset => 'latin1'}) - assert_equal "CREATE DATABASE `matt_aimonetti` DEFAULT CHARACTER SET `big5` COLLATE `big5_chinese_ci`", create_database(:matt_aimonetti, {:charset => :big5, :collation => :big5_chinese_ci}) + def test_index_in_bulk_change + def (ActiveRecord::Base.connection).table_exists?(*); true; end + def (ActiveRecord::Base.connection).index_name_exists?(*); false; end + + %w(SPATIAL FULLTEXT UNIQUE).each do |type| + expected = "ALTER TABLE `people` ADD #{type} INDEX `index_people_on_last_name` (`last_name`)" + actual = ActiveRecord::Base.connection.change_table(:people, bulk: true) do |t| + t.index :last_name, type: type + end + assert_equal expected, actual end - def test_recreate_mysql_database_with_encoding - create_database(:luca, {:charset => 'latin1'}) - assert_equal "CREATE DATABASE `luca` DEFAULT CHARACTER SET `latin1`", recreate_database(:luca, {:charset => 'latin1'}) + expected = "ALTER TABLE `peaple` ADD INDEX `index_peaple_on_last_name` USING btree (`last_name`(10)), ALGORITHM = COPY" + actual = ActiveRecord::Base.connection.change_table(:peaple, bulk: true) do |t| + t.index :last_name, length: 10, using: :btree, algorithm: :copy end + assert_equal expected, actual + end + + def test_drop_table + assert_equal "DROP TABLE `people`", drop_table(:people) + end + + def test_create_mysql_database_with_encoding + assert_equal "CREATE DATABASE `matt` DEFAULT CHARACTER SET `utf8`", create_database(:matt) + assert_equal "CREATE DATABASE `aimonetti` DEFAULT CHARACTER SET `latin1`", create_database(:aimonetti, {:charset => 'latin1'}) + assert_equal "CREATE DATABASE `matt_aimonetti` DEFAULT CHARACTER SET `big5` COLLATE `big5_chinese_ci`", create_database(:matt_aimonetti, {:charset => :big5, :collation => :big5_chinese_ci}) + end + + def test_recreate_mysql_database_with_encoding + create_database(:luca, {:charset => 'latin1'}) + assert_equal "CREATE DATABASE `luca` DEFAULT CHARACTER SET `latin1`", recreate_database(:luca, {:charset => 'latin1'}) end def test_add_column @@ -92,7 +127,7 @@ class ActiveSchemaTest < ActiveRecord::TestCase with_real_execute do begin ActiveRecord::Base.connection.create_table :delete_me - ActiveRecord::Base.connection.add_timestamps :delete_me + ActiveRecord::Base.connection.add_timestamps :delete_me, null: true assert column_present?('delete_me', 'updated_at', 'datetime') assert column_present?('delete_me', 'created_at', 'datetime') ensure @@ -105,9 +140,9 @@ class ActiveSchemaTest < ActiveRecord::TestCase with_real_execute do begin ActiveRecord::Base.connection.create_table :delete_me do |t| - t.timestamps + t.timestamps null: true end - ActiveRecord::Base.connection.remove_timestamps :delete_me + ActiveRecord::Base.connection.remove_timestamps :delete_me, { null: true } assert !column_present?('delete_me', 'updated_at', 'datetime') assert !column_present?('delete_me', 'created_at', 'datetime') ensure diff --git a/activerecord/test/cases/adapters/mysql2/bind_parameter_test.rb b/activerecord/test/cases/adapters/mysql2/bind_parameter_test.rb index 5e8065d80d..abdf3dbf5b 100644 --- a/activerecord/test/cases/adapters/mysql2/bind_parameter_test.rb +++ b/activerecord/test/cases/adapters/mysql2/bind_parameter_test.rb @@ -4,7 +4,7 @@ require 'models/topic' module ActiveRecord module ConnectionAdapters class Mysql2Adapter - class BindParameterTest < ActiveRecord::TestCase + class BindParameterTest < ActiveRecord::Mysql2TestCase fixtures :topics def test_update_question_marks diff --git a/activerecord/test/cases/adapters/mysql2/boolean_test.rb b/activerecord/test/cases/adapters/mysql2/boolean_test.rb index f3c711a64b..8575df9e43 100644 --- a/activerecord/test/cases/adapters/mysql2/boolean_test.rb +++ b/activerecord/test/cases/adapters/mysql2/boolean_test.rb @@ -1,7 +1,7 @@ require "cases/helper" -class Mysql2BooleanTest < ActiveRecord::TestCase - self.use_transactional_fixtures = false +class Mysql2BooleanTest < ActiveRecord::Mysql2TestCase + self.use_transactional_tests = false class BooleanType < ActiveRecord::Base self.table_name = "mysql_booleans" @@ -9,6 +9,7 @@ class Mysql2BooleanTest < ActiveRecord::TestCase setup do @connection = ActiveRecord::Base.connection + @connection.clear_cache! @connection.create_table("mysql_booleans") do |t| t.boolean "archived" t.string "published", limit: 1 @@ -46,8 +47,7 @@ class Mysql2BooleanTest < ActiveRecord::TestCase assert_equal 1, attributes["archived"] assert_equal "1", attributes["published"] - assert_equal 1, @connection.type_cast(true, boolean_column) - assert_equal "1", @connection.type_cast(true, string_column) + assert_equal 1, @connection.type_cast(true) end test "test type casting without emulated booleans" do @@ -59,8 +59,7 @@ class Mysql2BooleanTest < ActiveRecord::TestCase assert_equal 1, attributes["archived"] assert_equal "1", attributes["published"] - assert_equal 1, @connection.type_cast(true, boolean_column) - assert_equal "1", @connection.type_cast(true, string_column) + assert_equal 1, @connection.type_cast(true) end test "with booleans stored as 1 and 0" do diff --git a/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb b/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb index 09bebf3071..963116f08a 100644 --- a/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb +++ b/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb @@ -1,7 +1,6 @@ require "cases/helper" -require 'models/person' -class Mysql2CaseSensitivityTest < ActiveRecord::TestCase +class Mysql2CaseSensitivityTest < ActiveRecord::Mysql2TestCase class CollationTest < ActiveRecord::Base end diff --git a/activerecord/test/cases/adapters/mysql2/charset_collation_test.rb b/activerecord/test/cases/adapters/mysql2/charset_collation_test.rb new file mode 100644 index 0000000000..4fd34def15 --- /dev/null +++ b/activerecord/test/cases/adapters/mysql2/charset_collation_test.rb @@ -0,0 +1,54 @@ +require "cases/helper" +require 'support/schema_dumping_helper' + +class Mysql2CharsetCollationTest < ActiveRecord::Mysql2TestCase + include SchemaDumpingHelper + self.use_transactional_tests = false + + setup do + @connection = ActiveRecord::Base.connection + @connection.create_table :charset_collations, force: true do |t| + t.string :string_ascii_bin, charset: 'ascii', collation: 'ascii_bin' + t.text :text_ucs2_unicode_ci, charset: 'ucs2', collation: 'ucs2_unicode_ci' + end + end + + teardown do + @connection.drop_table :charset_collations, if_exists: true + end + + test "string column with charset and collation" do + column = @connection.columns(:charset_collations).find { |c| c.name == 'string_ascii_bin' } + assert_equal :string, column.type + assert_equal 'ascii_bin', column.collation + end + + test "text column with charset and collation" do + column = @connection.columns(:charset_collations).find { |c| c.name == 'text_ucs2_unicode_ci' } + assert_equal :text, column.type + assert_equal 'ucs2_unicode_ci', column.collation + end + + test "add column with charset and collation" do + @connection.add_column :charset_collations, :title, :string, charset: 'utf8', collation: 'utf8_bin' + + column = @connection.columns(:charset_collations).find { |c| c.name == 'title' } + assert_equal :string, column.type + assert_equal 'utf8_bin', column.collation + end + + test "change column with charset and collation" do + @connection.add_column :charset_collations, :description, :string, charset: 'utf8', collation: 'utf8_unicode_ci' + @connection.change_column :charset_collations, :description, :text, charset: 'utf8', collation: 'utf8_general_ci' + + column = @connection.columns(:charset_collations).find { |c| c.name == 'description' } + assert_equal :text, column.type + assert_equal 'utf8_general_ci', column.collation + end + + test "schema dump includes collation" do + output = dump_table_schema("charset_collations") + assert_match %r{t.string\s+"string_ascii_bin",\s+limit: 255,\s+collation: "ascii_bin"$}, output + assert_match %r{t.text\s+"text_ucs2_unicode_ci",\s+limit: 65535,\s+collation: "ucs2_unicode_ci"$}, output + end +end diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb index 3b35e69e0d..000bcadebe 100644 --- a/activerecord/test/cases/adapters/mysql2/connection_test.rb +++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb @@ -1,9 +1,11 @@ require "cases/helper" require 'support/connection_helper' -class MysqlConnectionTest < ActiveRecord::TestCase +class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase include ConnectionHelper + fixtures :comments + def setup super @subscriber = SQLSubscriber.new @@ -20,10 +22,21 @@ class MysqlConnectionTest < ActiveRecord::TestCase assert_raise ActiveRecord::NoDatabaseError do configuration = ActiveRecord::Base.configurations['arunit'].merge(database: 'inexistent_activerecord_unittest') connection = ActiveRecord::Base.mysql2_connection(configuration) - connection.exec_query('drop table if exists ex') + connection.drop_table 'ex', if_exists: true end end + def test_truncate + rows = ActiveRecord::Base.connection.exec_query("select count(*) from comments") + count = rows.first.values.first + assert_operator count, :>, 0 + + ActiveRecord::Base.connection.truncate("comments") + rows = ActiveRecord::Base.connection.exec_query("select count(*) from comments") + count = rows.first.values.first + assert_equal 0, count + end + def test_no_automatic_reconnection_after_timeout assert @connection.active? @connection.update('set @@wait_timeout=1') @@ -31,9 +44,7 @@ class MysqlConnectionTest < ActiveRecord::TestCase assert !@connection.active? # Repair all fixture connections so other tests won't break. - @fixture_connections.each do |c| - c.verify! - end + @fixture_connections.each(&:verify!) end def test_successful_reconnection_after_timeout_with_manual_reconnect @@ -52,6 +63,11 @@ class MysqlConnectionTest < ActiveRecord::TestCase assert @connection.active? end + def test_mysql_connection_collation_is_configured + assert_equal 'utf8_unicode_ci', @connection.show_variable('collation_connection') + assert_equal 'utf8_general_ci', ARUnit2Model.connection.show_variable('collation_connection') + end + # TODO: Below is a straight up copy/paste from mysql/connection_test.rb # I'm not sure what the correct way is to share these tests between # adapters in minitest. @@ -68,6 +84,15 @@ class MysqlConnectionTest < ActiveRecord::TestCase end end + def test_mysql_strict_mode_specified_default + run_without_connection do |orig_connection| + ActiveRecord::Base.establish_connection(orig_connection.merge({strict: :default})) + global_sql_mode = ActiveRecord::Base.connection.exec_query "SELECT @@GLOBAL.sql_mode" + session_sql_mode = ActiveRecord::Base.connection.exec_query "SELECT @@SESSION.sql_mode" + assert_equal global_sql_mode.rows, session_sql_mode.rows + end + end + def test_mysql_set_session_variable run_without_connection do |orig_connection| ActiveRecord::Base.establish_connection(orig_connection.deep_merge({:variables => {:default_week_format => 3}})) @@ -76,7 +101,7 @@ class MysqlConnectionTest < ActiveRecord::TestCase end end - def test_mysql_sql_mode_variable_overides_strict_mode + def test_mysql_sql_mode_variable_overrides_strict_mode run_without_connection do |orig_connection| ActiveRecord::Base.establish_connection(orig_connection.deep_merge(variables: { 'sql_mode' => 'ansi' })) result = ActiveRecord::Base.connection.exec_query 'SELECT @@SESSION.sql_mode' @@ -106,11 +131,4 @@ class MysqlConnectionTest < ActiveRecord::TestCase ensure @connection.execute "DROP TABLE `bar_baz`" end - - if mysql_56? - def test_quote_time_usec - assert_equal "'1970-01-01 00:00:00.000000'", @connection.quote(Time.at(0)) - assert_equal "'1970-01-01 00:00:00.000000'", @connection.quote(Time.at(0).to_datetime) - end - end end diff --git a/activerecord/test/cases/adapters/mysql2/enum_test.rb b/activerecord/test/cases/adapters/mysql2/enum_test.rb index 6dd9a5ec87..bd732b5eca 100644 --- a/activerecord/test/cases/adapters/mysql2/enum_test.rb +++ b/activerecord/test/cases/adapters/mysql2/enum_test.rb @@ -1,6 +1,6 @@ require "cases/helper" -class Mysql2EnumTest < ActiveRecord::TestCase +class Mysql2EnumTest < ActiveRecord::Mysql2TestCase class EnumTest < ActiveRecord::Base end diff --git a/activerecord/test/cases/adapters/mysql2/explain_test.rb b/activerecord/test/cases/adapters/mysql2/explain_test.rb index 675703caa1..4fc7414b18 100644 --- a/activerecord/test/cases/adapters/mysql2/explain_test.rb +++ b/activerecord/test/cases/adapters/mysql2/explain_test.rb @@ -1,10 +1,11 @@ require "cases/helper" require 'models/developer' +require 'models/computer' module ActiveRecord module ConnectionAdapters class Mysql2Adapter - class ExplainTest < ActiveRecord::TestCase + class ExplainTest < ActiveRecord::Mysql2TestCase fixtures :developers def test_explain_for_one_query @@ -17,7 +18,7 @@ module ActiveRecord explain = Developer.where(:id => 1).includes(:audit_logs).explain assert_match %(EXPLAIN for: SELECT `developers`.* FROM `developers` WHERE `developers`.`id` = 1), explain assert_match %r(developers |.* const), explain - assert_match %(EXPLAIN for: SELECT `audit_logs`.* FROM `audit_logs` WHERE `audit_logs`.`developer_id` IN (1)), explain + assert_match %(EXPLAIN for: SELECT `audit_logs`.* FROM `audit_logs` WHERE `audit_logs`.`developer_id` = 1), explain assert_match %r(audit_logs |.* ALL), explain end end diff --git a/activerecord/test/cases/adapters/mysql2/json_test.rb b/activerecord/test/cases/adapters/mysql2/json_test.rb new file mode 100644 index 0000000000..c8c933af5e --- /dev/null +++ b/activerecord/test/cases/adapters/mysql2/json_test.rb @@ -0,0 +1,172 @@ +require 'cases/helper' +require 'support/schema_dumping_helper' + +if ActiveRecord::Base.connection.supports_json? +class Mysql2JSONTest < ActiveRecord::Mysql2TestCase + include SchemaDumpingHelper + self.use_transactional_tests = false + + class JsonDataType < ActiveRecord::Base + self.table_name = 'json_data_type' + + store_accessor :settings, :resolution + end + + def setup + @connection = ActiveRecord::Base.connection + begin + @connection.create_table('json_data_type') do |t| + t.json 'payload' + t.json 'settings' + end + end + end + + def teardown + @connection.drop_table :json_data_type, if_exists: true + JsonDataType.reset_column_information + end + + def test_column + column = JsonDataType.columns_hash["payload"] + assert_equal :json, column.type + assert_equal 'json', column.sql_type + + type = JsonDataType.type_for_attribute("payload") + assert_not type.binary? + end + + def test_change_table_supports_json + @connection.change_table('json_data_type') do |t| + t.json 'users' + end + JsonDataType.reset_column_information + column = JsonDataType.columns_hash['users'] + assert_equal :json, column.type + end + + def test_schema_dumping + output = dump_table_schema("json_data_type") + assert_match(/t\.json\s+"settings"/, output) + end + + def test_cast_value_on_write + x = JsonDataType.new payload: {"string" => "foo", :symbol => :bar} + assert_equal({"string" => "foo", :symbol => :bar}, x.payload_before_type_cast) + assert_equal({"string" => "foo", "symbol" => "bar"}, x.payload) + x.save + assert_equal({"string" => "foo", "symbol" => "bar"}, x.reload.payload) + end + + def test_type_cast_json + type = JsonDataType.type_for_attribute("payload") + + data = "{\"a_key\":\"a_value\"}" + hash = type.deserialize(data) + assert_equal({'a_key' => 'a_value'}, hash) + assert_equal({'a_key' => 'a_value'}, type.deserialize(data)) + + assert_equal({}, type.deserialize("{}")) + assert_equal({'key'=>nil}, type.deserialize('{"key": null}')) + assert_equal({'c'=>'}','"a"'=>'b "a b'}, type.deserialize(%q({"c":"}", "\"a\"":"b \"a b"}))) + end + + def test_rewrite + @connection.execute "insert into json_data_type (payload) VALUES ('{\"k\":\"v\"}')" + x = JsonDataType.first + x.payload = { '"a\'' => 'b' } + assert x.save! + end + + def test_select + @connection.execute "insert into json_data_type (payload) VALUES ('{\"k\":\"v\"}')" + x = JsonDataType.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]}')| + x = JsonDataType.first + assert_equal({'k1' => 'v1', 'k2' => 'v2', 'k3' => [1,2,3]}, x.payload) + end + + def test_null_json + @connection.execute %q|insert into json_data_type (payload) VALUES(null)| + x = JsonDataType.first + assert_equal(nil, x.payload) + end + + def test_select_array_json_value + @connection.execute %q|insert into json_data_type (payload) VALUES ('["v0",{"k1":"v1"}]')| + x = JsonDataType.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"}]')| + x = JsonDataType.first + x.payload = ['v1', {'k2' => 'v2'}, 'v3'] + assert x.save! + end + + def test_with_store_accessors + x = JsonDataType.new(resolution: "320×480") + assert_equal "320×480", x.resolution + + x.save! + x = JsonDataType.first + assert_equal "320×480", x.resolution + + x.resolution = "640×1136" + x.save! + + x = JsonDataType.first + assert_equal "640×1136", x.resolution + end + + def test_duplication_with_store_accessors + x = JsonDataType.new(resolution: "320×480") + assert_equal "320×480", x.resolution + + y = x.dup + assert_equal "320×480", y.resolution + end + + def test_yaml_round_trip_with_store_accessors + x = JsonDataType.new(resolution: "320×480") + assert_equal "320×480", x.resolution + + y = YAML.load(YAML.dump(x)) + assert_equal "320×480", y.resolution + end + + def test_changes_in_place + json = JsonDataType.new + assert_not json.changed? + + json.payload = { 'one' => 'two' } + assert json.changed? + assert json.payload_changed? + + json.save! + assert_not json.changed? + + json.payload['three'] = 'four' + assert json.payload_changed? + + json.save! + json.reload + + assert_equal({ 'one' => 'two', 'three' => 'four' }, json.payload) + assert_not json.changed? + end + + def test_assigning_invalid_json + json = JsonDataType.new + + json.payload = 'foo' + + assert_nil json.payload + end +end +end diff --git a/activerecord/test/cases/adapters/mysql2/quoting_test.rb b/activerecord/test/cases/adapters/mysql2/quoting_test.rb new file mode 100644 index 0000000000..2de7e1b526 --- /dev/null +++ b/activerecord/test/cases/adapters/mysql2/quoting_test.rb @@ -0,0 +1,21 @@ +require "cases/helper" + +class Mysql2QuotingTest < ActiveRecord::Mysql2TestCase + setup do + @connection = ActiveRecord::Base.connection + end + + test 'quoted date precision for gte 5.6.4' do + @connection.stubs(:full_version).returns('5.6.4') + @connection.remove_instance_variable(:@version) if @connection.instance_variable_defined?(:@version) + t = Time.now.change(usec: 1) + assert_match(/\.000001\z/, @connection.quoted_date(t)) + end + + test 'quoted date precision for lt 5.6.4' do + @connection.stubs(:full_version).returns('5.6.3') + @connection.remove_instance_variable(:@version) if @connection.instance_variable_defined?(:@version) + t = Time.now.change(usec: 1) + assert_no_match(/\.000001\z/, @connection.quoted_date(t)) + end +end diff --git a/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb b/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb index 799d927ee4..ffb4e2c5cf 100644 --- a/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb +++ b/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb @@ -1,29 +1,29 @@ require "cases/helper" -class Group < ActiveRecord::Base - Group.table_name = 'group' - belongs_to :select - has_one :values -end +# a suite of tests to ensure the ConnectionAdapters#MysqlAdapter can handle tables with +# reserved word names (ie: group, order, values, etc...) +class Mysql2ReservedWordTest < ActiveRecord::Mysql2TestCase + class Group < ActiveRecord::Base + Group.table_name = 'group' + belongs_to :select + has_one :values + end -class Select < ActiveRecord::Base - Select.table_name = 'select' - has_many :groups -end + class Select < ActiveRecord::Base + Select.table_name = 'select' + has_many :groups + end -class Values < ActiveRecord::Base - Values.table_name = 'values' -end + class Values < ActiveRecord::Base + Values.table_name = 'values' + end -class Distinct < ActiveRecord::Base - Distinct.table_name = 'distinct' - has_and_belongs_to_many :selects - has_many :values, :through => :groups -end + class Distinct < ActiveRecord::Base + Distinct.table_name = 'distinct' + has_and_belongs_to_many :selects + has_many :values, :through => :groups + end -# a suite of tests to ensure the ConnectionAdapters#MysqlAdapter can handle tables with -# reserved word names (ie: group, order, values, etc...) -class MysqlReservedWordTest < ActiveRecord::TestCase def setup @connection = ActiveRecord::Base.connection @@ -71,7 +71,7 @@ class MysqlReservedWordTest < ActiveRecord::TestCase #fixtures self.use_instantiated_fixtures = true - self.use_transactional_fixtures = false + self.use_transactional_tests = false #activerecord model class with reserved-word table name def test_activerecord_model @@ -100,7 +100,7 @@ class MysqlReservedWordTest < ActiveRecord::TestCase gs = nil assert_nothing_raised { gs = Select.find(2).groups } assert_equal gs.length, 2 - assert(gs.collect{|x| x.id}.sort == [2, 3]) + assert(gs.collect(&:id).sort == [2, 3]) end # has_and_belongs_to_many with reserved-word table name @@ -109,7 +109,7 @@ class MysqlReservedWordTest < ActiveRecord::TestCase s = nil assert_nothing_raised { s = Distinct.find(1).selects } assert_equal s.length, 2 - assert(s.collect{|x|x.id}.sort == [1, 2]) + assert(s.collect(&:id).sort == [1, 2]) end # activerecord model introspection with reserved-word table and column names @@ -138,7 +138,7 @@ class MysqlReservedWordTest < ActiveRecord::TestCase # custom drop table, uses execute on connection to drop a table if it exists. note: escapes table_name def drop_tables_directly(table_names, connection = @connection) table_names.each do |name| - connection.execute("DROP TABLE IF EXISTS `#{name}`") + connection.drop_table name, if_exists: true end end diff --git a/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb b/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb index 9c49599d34..396f235e77 100644 --- a/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb +++ b/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb @@ -1,39 +1,42 @@ require "cases/helper" -module ActiveRecord - module ConnectionAdapters - class Mysql2Adapter - class SchemaMigrationsTest < ActiveRecord::TestCase - def test_renaming_index_on_foreign_key - connection.add_index "engines", "car_id" - connection.add_foreign_key :engines, :cars, name: "fk_engines_cars" - - connection.rename_index("engines", "index_engines_on_car_id", "idx_renamed") - assert_equal ["idx_renamed"], connection.indexes("engines").map(&:name) - ensure - connection.remove_foreign_key :engines, name: "fk_engines_cars" - end - - def test_initializes_schema_migrations_for_encoding_utf8mb4 - smtn = ActiveRecord::Migrator.schema_migrations_table_name - connection.drop_table(smtn) if connection.table_exists?(smtn) - - config = connection.instance_variable_get(:@config) - original_encoding = config[:encoding] - - config[:encoding] = 'utf8mb4' - connection.initialize_schema_migrations_table - - assert connection.column_exists?(smtn, :version, :string, limit: Mysql2Adapter::MAX_INDEX_LENGTH_FOR_UTF8MB4) - ensure - config[:encoding] = original_encoding - end - - private - def connection - @connection ||= ActiveRecord::Base.connection - end - end - end +class SchemaMigrationsTest < ActiveRecord::Mysql2TestCase + def test_renaming_index_on_foreign_key + connection.add_index "engines", "car_id" + connection.add_foreign_key :engines, :cars, name: "fk_engines_cars" + + connection.rename_index("engines", "index_engines_on_car_id", "idx_renamed") + assert_equal ["idx_renamed"], connection.indexes("engines").map(&:name) + ensure + connection.remove_foreign_key :engines, name: "fk_engines_cars" + end + + def test_initializes_schema_migrations_for_encoding_utf8mb4 + smtn = ActiveRecord::Migrator.schema_migrations_table_name + connection.drop_table smtn, if_exists: true + + database_name = connection.current_database + database_info = connection.select_one("SELECT * FROM information_schema.schemata WHERE schema_name = '#{database_name}'") + + original_charset = database_info["DEFAULT_CHARACTER_SET_NAME"] + original_collation = database_info["DEFAULT_COLLATION_NAME"] + + execute("ALTER DATABASE #{database_name} DEFAULT CHARACTER SET utf8mb4") + + connection.initialize_schema_migrations_table + + limit = ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::MAX_INDEX_LENGTH_FOR_CHARSETS_OF_4BYTES_MAXLEN + assert connection.column_exists?(smtn, :version, :string, limit: limit) + ensure + execute("ALTER DATABASE #{database_name} DEFAULT CHARACTER SET #{original_charset} COLLATE #{original_collation}") + end + + private + def connection + @connection ||= ActiveRecord::Base.connection + end + + def execute(sql) + connection.execute(sql) end end diff --git a/activerecord/test/cases/adapters/mysql2/schema_test.rb b/activerecord/test/cases/adapters/mysql2/schema_test.rb index 43c9116b5a..1ebdca661c 100644 --- a/activerecord/test/cases/adapters/mysql2/schema_test.rb +++ b/activerecord/test/cases/adapters/mysql2/schema_test.rb @@ -4,7 +4,7 @@ require 'models/comment' module ActiveRecord module ConnectionAdapters - class Mysql2SchemaTest < ActiveRecord::TestCase + class Mysql2SchemaTest < ActiveRecord::Mysql2TestCase fixtures :posts def setup @@ -14,6 +14,7 @@ module ActiveRecord @db_name = db @omgpost = Class.new(ActiveRecord::Base) do + self.inheritance_column = :disabled self.table_name = "#{db}.#{table}" def self.name; 'Post'; end end @@ -36,14 +37,6 @@ module ActiveRecord assert(!@connection.table_exists?("#{@db_name}.zomg"), "table should not exist") end - def test_tables_quoting - @connection.tables(nil, "foo-bar", nil) - flunk - rescue => e - # assertion for *quoted* database properly - assert_match(/database 'foo-bar'/, e.inspect) - end - def test_dump_indexes index_a_name = 'index_key_tests_on_snack' index_b_name = 'index_key_tests_on_pizza' @@ -51,7 +44,7 @@ module ActiveRecord table = 'key_tests' - indexes = @connection.indexes(table).sort_by {|i| i.name} + indexes = @connection.indexes(table).sort_by(&:name) assert_equal 3,indexes.size index_a = indexes.select{|i| i.name == index_a_name}[0] @@ -66,12 +59,14 @@ module ActiveRecord assert_equal :fulltext, index_c.type end - def test_drop_temporary_table - @connection.transaction do - @connection.create_table(:temp_table, temporary: true) - # if it doesn't properly say DROP TEMPORARY TABLE, the transaction commit - # will complain that no transaction is active - @connection.drop_table(:temp_table, temporary: true) + unless mysql_enforcing_gtid_consistency? + def test_drop_temporary_table + @connection.transaction do + @connection.create_table(:temp_table, temporary: true) + # if it doesn't properly say DROP TEMPORARY TABLE, the transaction commit + # will complain that no transaction is active + @connection.drop_table(:temp_table, temporary: true) + end end end end diff --git a/activerecord/test/cases/adapters/mysql2/sp_test.rb b/activerecord/test/cases/adapters/mysql2/sp_test.rb new file mode 100644 index 0000000000..cdaa2cca44 --- /dev/null +++ b/activerecord/test/cases/adapters/mysql2/sp_test.rb @@ -0,0 +1,30 @@ +require "cases/helper" +require 'models/topic' +require 'models/reply' + +class Mysql2StoredProcedureTest < ActiveRecord::Mysql2TestCase + fixtures :topics + + def setup + @connection = ActiveRecord::Base.connection + unless ActiveRecord::Base.connection.version >= '5.6.0' + skip("no stored procedure support") + end + end + + # Test that MySQL allows multiple results for stored procedures + # + # In MySQL 5.6, CLIENT_MULTI_RESULTS is enabled by default. + # http://dev.mysql.com/doc/refman/5.6/en/call.html + def test_multi_results + rows = @connection.select_rows('CALL ten();') + assert_equal 10, rows[0][0].to_i, "ten() did not return 10 as expected: #{rows.inspect}" + assert @connection.active?, "Bad connection use by 'Mysql2Adapter.select_rows'" + end + + def test_multi_results_from_find_by_sql + topics = Topic.find_by_sql 'CALL topics(3);' + assert_equal 3, topics.size + assert @connection.active?, "Bad connection use by 'Mysql2Adapter.select'" + end +end diff --git a/activerecord/test/cases/adapters/mysql2/sql_types_test.rb b/activerecord/test/cases/adapters/mysql2/sql_types_test.rb index 1ddb1b91c9..4926bc2267 100644 --- a/activerecord/test/cases/adapters/mysql2/sql_types_test.rb +++ b/activerecord/test/cases/adapters/mysql2/sql_types_test.rb @@ -1,10 +1,10 @@ require "cases/helper" -class SqlTypesTest < ActiveRecord::TestCase +class Mysql2SqlTypesTest < ActiveRecord::Mysql2TestCase def test_binary_types assert_equal 'varbinary(64)', type_to_sql(:binary, 64) assert_equal 'varbinary(4095)', type_to_sql(:binary, 4095) - assert_equal 'blob(4096)', type_to_sql(:binary, 4096) + assert_equal 'blob', type_to_sql(:binary, 4096) assert_equal 'blob', type_to_sql(:binary) end diff --git a/activerecord/test/cases/adapters/mysql2/table_options_test.rb b/activerecord/test/cases/adapters/mysql2/table_options_test.rb new file mode 100644 index 0000000000..af121ee7d9 --- /dev/null +++ b/activerecord/test/cases/adapters/mysql2/table_options_test.rb @@ -0,0 +1,42 @@ +require "cases/helper" +require 'support/schema_dumping_helper' + +class Mysql2TableOptionsTest < ActiveRecord::Mysql2TestCase + include SchemaDumpingHelper + + def setup + @connection = ActiveRecord::Base.connection + end + + def teardown + @connection.drop_table "mysql_table_options", if_exists: true + end + + test "table options with ENGINE" do + @connection.create_table "mysql_table_options", force: true, options: "ENGINE=MyISAM" + output = dump_table_schema("mysql_table_options") + options = %r{create_table "mysql_table_options", force: :cascade, options: "(?<options>.*)"}.match(output)[:options] + assert_match %r{ENGINE=MyISAM}, options + end + + test "table options with ROW_FORMAT" do + @connection.create_table "mysql_table_options", force: true, options: "ROW_FORMAT=REDUNDANT" + output = dump_table_schema("mysql_table_options") + options = %r{create_table "mysql_table_options", force: :cascade, options: "(?<options>.*)"}.match(output)[:options] + assert_match %r{ROW_FORMAT=REDUNDANT}, options + end + + test "table options with CHARSET" do + @connection.create_table "mysql_table_options", force: true, options: "CHARSET=utf8mb4" + output = dump_table_schema("mysql_table_options") + options = %r{create_table "mysql_table_options", force: :cascade, options: "(?<options>.*)"}.match(output)[:options] + assert_match %r{CHARSET=utf8mb4}, options + end + + test "table options with COLLATE" do + @connection.create_table "mysql_table_options", force: true, options: "COLLATE=utf8mb4_bin" + output = dump_table_schema("mysql_table_options") + options = %r{create_table "mysql_table_options", force: :cascade, options: "(?<options>.*)"}.match(output)[:options] + assert_match %r{COLLATE=utf8mb4_bin}, options + end +end diff --git a/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb b/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb new file mode 100644 index 0000000000..a6f6dd21bb --- /dev/null +++ b/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb @@ -0,0 +1,65 @@ +require "cases/helper" +require "support/schema_dumping_helper" + +class Mysql2UnsignedTypeTest < ActiveRecord::Mysql2TestCase + include SchemaDumpingHelper + self.use_transactional_tests = false + + class UnsignedType < ActiveRecord::Base + end + + setup do + @connection = ActiveRecord::Base.connection + @connection.create_table("unsigned_types", force: true) do |t| + t.integer :unsigned_integer, unsigned: true + t.bigint :unsigned_bigint, unsigned: true + t.float :unsigned_float, unsigned: true + t.decimal :unsigned_decimal, unsigned: true, precision: 10, scale: 2 + end + end + + teardown do + @connection.drop_table "unsigned_types", if_exists: true + end + + test "unsigned int max value is in range" do + assert expected = UnsignedType.create(unsigned_integer: 4294967295) + assert_equal expected, UnsignedType.find_by(unsigned_integer: 4294967295) + end + + test "minus value is out of range" do + assert_raise(RangeError) do + UnsignedType.create(unsigned_integer: -10) + end + assert_raise(RangeError) do + UnsignedType.create(unsigned_bigint: -10) + end + assert_raise(ActiveRecord::StatementInvalid) do + UnsignedType.create(unsigned_float: -10.0) + end + assert_raise(ActiveRecord::StatementInvalid) do + UnsignedType.create(unsigned_decimal: -10.0) + end + end + + test "schema definition can use unsigned as the type" do + @connection.change_table("unsigned_types") do |t| + t.unsigned_integer :unsigned_integer_t + t.unsigned_bigint :unsigned_bigint_t + t.unsigned_float :unsigned_float_t + t.unsigned_decimal :unsigned_decimal_t, precision: 10, scale: 2 + end + + @connection.columns("unsigned_types").select { |c| /^unsigned_/ === c.name }.each do |column| + assert column.unsigned? + end + end + + test "schema dump includes unsigned option" do + schema = dump_table_schema "unsigned_types" + assert_match %r{t.integer\s+"unsigned_integer",\s+limit: 4,\s+unsigned: true$}, schema + assert_match %r{t.integer\s+"unsigned_bigint",\s+limit: 8,\s+unsigned: true$}, schema + assert_match %r{t.float\s+"unsigned_float",\s+limit: 24,\s+unsigned: true$}, schema + assert_match %r{t.decimal\s+"unsigned_decimal",\s+precision: 10,\s+scale: 2,\s+unsigned: true$}, schema + end +end |