From 5b47e267328aaff6fac6da3202efae0439b3ec61 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Thu, 25 Jun 2020 20:36:47 +0900 Subject: [PATCH] Restore legacy YAML compatibility for MySQL with `active_record_yaml_version: 1` We had removed the dedicated `MysqlDateTime`, `MysqlJson`, and `OID::Json` classes in the past (f1a0fa9, #29666), so legacy YAML loading has no longer always perfectly compatiblity. Fortunately, v2 (Rails 5.1 style) YAML doesn't contain type information in almost all cases (unless serializing object using custom select), so usually removing dedicated type affects to legacy YAML older than v1 (Rails 5.0 style) YAML only. This restores legacy YAML compatibility for MySQL with v1 format by adding the class alias in YAML for `MysqlString` which is most recently reported about compatibility concern. It also affects to legacy Rails 4.2 style YAML, but 4.2 style YAML had already broken by removing `MysqlDateTime` over 4 years ago. --- activerecord/lib/active_record.rb | 1 + .../test/cases/yaml_serialization_test.rb | 18 ++ .../rails_v1_mysql.yml | 207 ++++++++++++++++++ .../yaml_compatibility_fixtures/rails_v2.yml | 55 +++++ 4 files changed, 281 insertions(+) create mode 100644 activerecord/test/support/yaml_compatibility_fixtures/rails_v1_mysql.yml create mode 100644 activerecord/test/support/yaml_compatibility_fixtures/rails_v2.yml diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index 6df96615023e2..e9a7623f4055e 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -186,3 +186,4 @@ def self.eager_load! YAML.load_tags["!ruby/object:ActiveRecord::AttributeSet"] = "ActiveModel::AttributeSet" YAML.load_tags["!ruby/object:ActiveRecord::Attribute::FromDatabase"] = "ActiveModel::Attribute::FromDatabase" YAML.load_tags["!ruby/object:ActiveRecord::LazyAttributeHash"] = "ActiveModel::LazyAttributeHash" +YAML.load_tags["!ruby/object:ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::MysqlString"] = "ActiveRecord::Type::String" diff --git a/activerecord/test/cases/yaml_serialization_test.rb b/activerecord/test/cases/yaml_serialization_test.rb index 9cdeb55e1303c..49890cd823009 100644 --- a/activerecord/test/cases/yaml_serialization_test.rb +++ b/activerecord/test/cases/yaml_serialization_test.rb @@ -93,6 +93,24 @@ def test_a_yaml_version_is_provided_for_future_backwards_compat assert coder["active_record_yaml_version"] end + def test_deserializing_rails_v2_yaml + topic = YAML.load(yaml_fixture("rails_v2")) + + assert_not_predicate topic, :new_record? + assert_equal 1, topic.id + assert_equal "The First Topic", topic.title + assert_equal "Have a nice day", topic.content + end + + def test_deserializing_rails_v1_mysql_yaml + topic = YAML.load(yaml_fixture("rails_v1_mysql")) + + assert_not_predicate topic, :new_record? + assert_equal 1, topic.id + assert_equal "The First Topic", topic.title + assert_equal "Have a nice day", topic.content + end + def test_deserializing_rails_41_yaml topic = assert_deprecated do YAML.load(yaml_fixture("rails_4_1")) diff --git a/activerecord/test/support/yaml_compatibility_fixtures/rails_v1_mysql.yml b/activerecord/test/support/yaml_compatibility_fixtures/rails_v1_mysql.yml new file mode 100644 index 0000000000000..b7b3e607b119d --- /dev/null +++ b/activerecord/test/support/yaml_compatibility_fixtures/rails_v1_mysql.yml @@ -0,0 +1,207 @@ +--- !ruby/object:Topic +raw_attributes: + id: 1 + title: The First Topic + author_name: David + author_email_address: david@loudthinking.com + written_on: &5 2003-07-16 14:28:11.223300000 Z + bonus_time: &6 2000-01-01 14:28:00.000000000 Z + last_read: 2004-04-15 + content: | + --- Have a nice day + ... + important: + approved: 0 + replies_count: 1 + unique_replies_count: 0 + parent_id: + parent_title: + type: + group: + created_at: &7 2020-06-25 10:25:48.335322000 Z + updated_at: &8 2020-06-25 10:25:48.335322000 Z +attributes: !ruby/object:ActiveRecord::AttributeSet + attributes: !ruby/object:ActiveRecord::LazyAttributeHash + types: + id: &9 !ruby/object:ActiveRecord::Type::Integer + precision: + scale: + limit: + range: !ruby/range + begin: -2147483648 + end: 2147483648 + excl: true + title: &10 !ruby/object:ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::MysqlString + precision: + scale: + limit: 250 + author_name: &1 !ruby/object:ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::MysqlString + precision: + scale: + limit: 255 + author_email_address: *1 + written_on: &4 !ruby/object:ActiveRecord::Type::DateTime + precision: 6 + scale: + limit: + bonus_time: &11 !ruby/object:ActiveRecord::Type::Time + precision: 0 + scale: + limit: + last_read: &12 !ruby/object:ActiveRecord::Type::Date + precision: + scale: + limit: + content: &13 !ruby/object:ActiveRecord::Type::Serialized + subtype: &2 !ruby/object:ActiveRecord::Type::Text + precision: + scale: + limit: 65535 + coder: !ruby/object:ActiveRecord::Coders::YAMLColumn + attr_name: :content + object_class: !ruby/class 'Object' + delegate_dc_obj: *2 + important: *2 + approved: &14 !ruby/object:ActiveRecord::Type::Boolean + precision: + scale: + limit: + replies_count: &3 !ruby/object:ActiveRecord::Type::Integer + precision: + scale: + limit: + range: !ruby/range + begin: -2147483648 + end: 2147483648 + excl: true + unique_replies_count: *3 + parent_id: *3 + parent_title: *1 + type: *1 + group: *1 + created_at: *4 + updated_at: *4 + values: + id: 1 + title: The First Topic + author_name: David + author_email_address: david@loudthinking.com + written_on: *5 + bonus_time: *6 + last_read: 2004-04-15 + content: | + --- Have a nice day + ... + important: + approved: 0 + replies_count: 1 + unique_replies_count: 0 + parent_id: + parent_title: + type: + group: + created_at: *7 + updated_at: *8 + additional_types: {} + materialized: true + delegate_hash: + id: !ruby/object:ActiveRecord::Attribute::FromDatabase + name: id + value_before_type_cast: 1 + type: *9 + original_attribute: + title: !ruby/object:ActiveRecord::Attribute::FromDatabase + name: title + value_before_type_cast: The First Topic + type: *10 + original_attribute: + author_name: !ruby/object:ActiveRecord::Attribute::FromDatabase + name: author_name + value_before_type_cast: David + type: *1 + original_attribute: + author_email_address: !ruby/object:ActiveRecord::Attribute::FromDatabase + name: author_email_address + value_before_type_cast: david@loudthinking.com + type: *1 + original_attribute: + written_on: !ruby/object:ActiveRecord::Attribute::FromDatabase + name: written_on + value_before_type_cast: *5 + type: *4 + original_attribute: + bonus_time: !ruby/object:ActiveRecord::Attribute::FromDatabase + name: bonus_time + value_before_type_cast: *6 + type: *11 + original_attribute: + last_read: !ruby/object:ActiveRecord::Attribute::FromDatabase + name: last_read + value_before_type_cast: 2004-04-15 + type: *12 + original_attribute: + content: !ruby/object:ActiveRecord::Attribute::FromDatabase + name: content + value_before_type_cast: | + --- Have a nice day + ... + type: *13 + original_attribute: + important: !ruby/object:ActiveRecord::Attribute::FromDatabase + name: important + value_before_type_cast: + type: *2 + original_attribute: + approved: !ruby/object:ActiveRecord::Attribute::FromDatabase + name: approved + value_before_type_cast: 0 + type: *14 + original_attribute: + replies_count: !ruby/object:ActiveRecord::Attribute::FromDatabase + name: replies_count + value_before_type_cast: 1 + type: *3 + original_attribute: + unique_replies_count: !ruby/object:ActiveRecord::Attribute::FromDatabase + name: unique_replies_count + value_before_type_cast: 0 + type: *3 + original_attribute: + parent_id: !ruby/object:ActiveRecord::Attribute::FromDatabase + name: parent_id + value_before_type_cast: + type: *3 + original_attribute: + parent_title: !ruby/object:ActiveRecord::Attribute::FromDatabase + name: parent_title + value_before_type_cast: + type: *1 + original_attribute: + type: !ruby/object:ActiveRecord::Attribute::FromDatabase + name: type + value_before_type_cast: + type: *1 + original_attribute: + group: !ruby/object:ActiveRecord::Attribute::FromDatabase + name: group + value_before_type_cast: + type: *1 + original_attribute: + created_at: !ruby/object:ActiveRecord::Attribute::FromDatabase + name: created_at + value_before_type_cast: *7 + type: *4 + original_attribute: + updated_at: !ruby/object:ActiveRecord::Attribute::FromDatabase + name: updated_at + value_before_type_cast: *8 + type: *4 + original_attribute: + default_attributes: + id: !ruby/object:ActiveRecord::Attribute::FromDatabase + name: id + value_before_type_cast: + type: *9 + original_attribute: +new_record: false +active_record_yaml_version: 1 diff --git a/activerecord/test/support/yaml_compatibility_fixtures/rails_v2.yml b/activerecord/test/support/yaml_compatibility_fixtures/rails_v2.yml new file mode 100644 index 0000000000000..efec6da4d0025 --- /dev/null +++ b/activerecord/test/support/yaml_compatibility_fixtures/rails_v2.yml @@ -0,0 +1,55 @@ +--- !ruby/object:Topic +concise_attributes: +- !ruby/object:ActiveModel::Attribute::FromDatabase + name: id + value_before_type_cast: 1 +- !ruby/object:ActiveModel::Attribute::FromDatabase + name: title + value_before_type_cast: The First Topic +- !ruby/object:ActiveModel::Attribute::FromDatabase + name: author_name + value_before_type_cast: David +- !ruby/object:ActiveModel::Attribute::FromDatabase + name: author_email_address + value_before_type_cast: david@loudthinking.com +- !ruby/object:ActiveModel::Attribute::FromDatabase + name: written_on + value_before_type_cast: '2003-07-16 14:28:11.223300' +- !ruby/object:ActiveModel::Attribute::FromDatabase + name: bonus_time + value_before_type_cast: '2000-01-01 14:28:00' +- !ruby/object:ActiveModel::Attribute::FromDatabase + name: last_read + value_before_type_cast: '2004-04-15' +- !ruby/object:ActiveModel::Attribute::FromDatabase + name: content + value_before_type_cast: | + --- Have a nice day + ... +- !ruby/object:ActiveModel::Attribute::FromDatabase + name: important +- !ruby/object:ActiveModel::Attribute::FromDatabase + name: approved + value_before_type_cast: 0 +- !ruby/object:ActiveModel::Attribute::FromDatabase + name: replies_count + value_before_type_cast: 1 +- !ruby/object:ActiveModel::Attribute::FromDatabase + name: unique_replies_count + value_before_type_cast: 0 +- !ruby/object:ActiveModel::Attribute::FromDatabase + name: parent_id +- !ruby/object:ActiveModel::Attribute::FromDatabase + name: parent_title +- !ruby/object:ActiveModel::Attribute::FromDatabase + name: type +- !ruby/object:ActiveModel::Attribute::FromDatabase + name: group +- !ruby/object:ActiveModel::Attribute::FromDatabase + name: created_at + value_before_type_cast: '2020-06-25 09:52:17.503713' +- !ruby/object:ActiveModel::Attribute::FromDatabase + name: updated_at + value_before_type_cast: '2020-06-25 09:52:17.503713' +new_record: false +active_record_yaml_version: 2