From 933063188870347b59b35d4f96df21864d0f8f0b Mon Sep 17 00:00:00 2001 From: Roderick van Domburg Date: Sun, 25 Aug 2013 11:22:36 +0200 Subject: Auto-generate stable fixture UUIDs on PostgreSQL. Fixes: #11524 --- activerecord/CHANGELOG.md | 10 +++++++ activerecord/lib/active_record/fixtures.rb | 33 ++++++++++++++++------ .../test/cases/adapters/postgresql/uuid_test.rb | 8 +----- activerecord/test/cases/fixtures_test.rb | 9 ++++++ activerecord/test/cases/helper.rb | 9 ++++++ activerecord/test/cases/schema_dumper_test.rb | 2 +- activerecord/test/fixtures/uuid_children.yml | 3 ++ activerecord/test/fixtures/uuid_parents.yml | 2 ++ activerecord/test/models/uuid_child.rb | 3 ++ activerecord/test/models/uuid_parent.rb | 3 ++ activerecord/test/schema/schema.rb | 9 ++++++ 11 files changed, 74 insertions(+), 17 deletions(-) create mode 100644 activerecord/test/fixtures/uuid_children.yml create mode 100644 activerecord/test/fixtures/uuid_parents.yml create mode 100644 activerecord/test/models/uuid_child.rb create mode 100644 activerecord/test/models/uuid_parent.rb (limited to 'activerecord') diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index add5334f39..9f2a4570cd 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,9 @@ +* Auto-generate stable fixture UUIDs on PostgreSQL. + + Fixes: #11524 + + *Roderick van Domburg* + * `change_table` now uses the current adapter's `update_table_definition` method to retrieve a specific table definition. This ensures that `change_table` and `create_table` will use @@ -752,6 +758,10 @@ *thedarkone* +* Test that PostgreSQL adapter includes `usec` when quoting `DateTime` objects + + *Ben Cherry* + * Re-use `order` argument pre-processing for `reorder`. *Paul Nikitochkin* diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index a7a54483bc..36e134ea17 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -2,6 +2,7 @@ require 'erb' require 'yaml' require 'zlib' require 'active_support/dependencies' +require 'active_support/core_ext/securerandom' require 'active_record/fixture_set/file' require 'active_record/errors' @@ -541,9 +542,13 @@ module ActiveRecord end # Returns a consistent, platform-independent identifier for +label+. - # Identifiers are positive integers less than 2^32. - def self.identify(label) - Zlib.crc32(label.to_s) % MAX_ID + # Integer identifiers are values less than 2^32. UUIDs are RFC 4122 version 5 SHA-1 hashes. + def self.identify(label, column_type = :integer) + if column_type == :uuid + SecureRandom.uuid_v5(SecureRandom::UUID_OID_NAMESPACE, label.to_s) + else + Zlib.crc32(label.to_s) % MAX_ID + end end # Superclass for the evaluation contexts used by ERB fixtures. @@ -634,7 +639,7 @@ module ActiveRecord # generate a primary key if necessary if has_primary_key_column? && !row.include?(primary_key_name) - row[primary_key_name] = ActiveRecord::FixtureSet.identify(label) + row[primary_key_name] = ActiveRecord::FixtureSet.identify(label, primary_key_type) end # If STI is used, find the correct subclass for association reflection @@ -657,7 +662,8 @@ module ActiveRecord row[association.foreign_type] = $1 end - row[fk_name] = ActiveRecord::FixtureSet.identify(value) + fk_type = association.send(:active_record).columns_hash[association.foreign_key].type + row[fk_name] = ActiveRecord::FixtureSet.identify(value, fk_type) end when :has_many if association.options[:through] @@ -684,6 +690,10 @@ module ActiveRecord def name @association.name end + + def primary_key_type + @association.klass.column_types[@association.klass.primary_key].type + end end class HasManyThroughProxy < ReflectionProxy # :nodoc: @@ -701,17 +711,22 @@ module ActiveRecord @primary_key_name ||= model_class && model_class.primary_key end + def primary_key_type + @primary_key_type ||= model_class && model_class.column_types[model_class.primary_key].type + end + def add_join_records(rows, row, association) # This is the case when the join table has no fixtures file if (targets = row.delete(association.name.to_s)) - table_name = association.join_table - lhs_key = association.lhs_key - rhs_key = association.rhs_key + table_name = association.join_table + column_type = association.primary_key_type + lhs_key = association.lhs_key + rhs_key = association.rhs_key targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/) rows[table_name].concat targets.map { |target| { lhs_key => row[primary_key_name], - rhs_key => ActiveRecord::FixtureSet.identify(target) } + rhs_key => ActiveRecord::FixtureSet.identify(target, column_type) } } end end diff --git a/activerecord/test/cases/adapters/postgresql/uuid_test.rb b/activerecord/test/cases/adapters/postgresql/uuid_test.rb index 3f5d981444..9157e5e1ac 100644 --- a/activerecord/test/cases/adapters/postgresql/uuid_test.rb +++ b/activerecord/test/cases/adapters/postgresql/uuid_test.rb @@ -11,13 +11,7 @@ class PostgresqlUUIDTest < ActiveRecord::TestCase def setup @connection = ActiveRecord::Base.connection - - unless @connection.extension_enabled?('uuid-ossp') - @connection.enable_extension 'uuid-ossp' - @connection.commit_db_transaction - end - - @connection.reconnect! + enable_uuid_ossp!(@connection) @connection.transaction do @connection.create_table('pg_uuids', id: :uuid, default: 'uuid_generate_v1()') do |t| diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb index f3a4887a85..1bf977cf8f 100644 --- a/activerecord/test/cases/fixtures_test.rb +++ b/activerecord/test/cases/fixtures_test.rb @@ -673,6 +673,12 @@ end class FoxyFixturesTest < ActiveRecord::TestCase fixtures :parrots, :parrots_pirates, :pirates, :treasures, :mateys, :ships, :computers, :developers, :"admin/accounts", :"admin/users" + if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL' + require 'models/uuid_parent' + require 'models/uuid_child' + fixtures :uuid_parents, :uuid_children + end + def test_identifies_strings assert_equal(ActiveRecord::FixtureSet.identify("foo"), ActiveRecord::FixtureSet.identify("foo")) assert_not_equal(ActiveRecord::FixtureSet.identify("foo"), ActiveRecord::FixtureSet.identify("FOO")) @@ -685,6 +691,9 @@ class FoxyFixturesTest < ActiveRecord::TestCase def test_identifies_consistently assert_equal 207281424, ActiveRecord::FixtureSet.identify(:ruby) assert_equal 1066363776, ActiveRecord::FixtureSet.identify(:sapphire_2) + + assert_equal 'f92b6bda-0d0d-5fe1-9124-502b18badded', ActiveRecord::FixtureSet.identify(:daddy, :uuid) + assert_equal 'b4b10018-ad47-595d-b42f-d8bdaa6d01bf', ActiveRecord::FixtureSet.identify(:sonny, :uuid) end TIMESTAMP_COLUMNS = %w(created_at created_on updated_at updated_on) diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb index 3758224b0c..7a7ed9740b 100644 --- a/activerecord/test/cases/helper.rb +++ b/activerecord/test/cases/helper.rb @@ -106,6 +106,15 @@ def verify_default_timezone_config end end +def enable_uuid_ossp!(connection) + return false unless connection.supports_extensions? + return true if connection.extension_enabled?('uuid-ossp') + + connection.enable_extension 'uuid-ossp' + connection.commit_db_transaction + connection.reconnect! +end + unless ENV['FIXTURE_DEBUG'] module ActiveRecord::TestFixtures::ClassMethods def try_to_load_dependency_with_silence(*args) diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index 741827446d..0adc18ced1 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -63,7 +63,7 @@ class SchemaDumperTest < ActiveRecord::TestCase next if column_set.empty? lengths = column_set.map do |column| - if match = column.match(/t\.(?:integer|decimal|float|datetime|timestamp|time|date|text|binary|string|boolean)\s+"/) + if match = column.match(/t\.(?:integer|decimal|float|datetime|timestamp|time|date|text|binary|string|boolean|uuid)\s+"/) match[0].length end end diff --git a/activerecord/test/fixtures/uuid_children.yml b/activerecord/test/fixtures/uuid_children.yml new file mode 100644 index 0000000000..a7b15016e2 --- /dev/null +++ b/activerecord/test/fixtures/uuid_children.yml @@ -0,0 +1,3 @@ +sonny: + uuid_parent: daddy + name: Sonny diff --git a/activerecord/test/fixtures/uuid_parents.yml b/activerecord/test/fixtures/uuid_parents.yml new file mode 100644 index 0000000000..0b40225c5c --- /dev/null +++ b/activerecord/test/fixtures/uuid_parents.yml @@ -0,0 +1,2 @@ +daddy: + name: Daddy diff --git a/activerecord/test/models/uuid_child.rb b/activerecord/test/models/uuid_child.rb new file mode 100644 index 0000000000..a3d0962ad6 --- /dev/null +++ b/activerecord/test/models/uuid_child.rb @@ -0,0 +1,3 @@ +class UuidChild < ActiveRecord::Base + belongs_to :uuid_parent +end diff --git a/activerecord/test/models/uuid_parent.rb b/activerecord/test/models/uuid_parent.rb new file mode 100644 index 0000000000..5634f22d0c --- /dev/null +++ b/activerecord/test/models/uuid_parent.rb @@ -0,0 +1,3 @@ +class UuidParent < ActiveRecord::Base + has_many :uuid_children +end diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index ddfc1ac0d6..0fb2c6269f 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -17,6 +17,15 @@ ActiveRecord::Schema.define do ActiveRecord::Base.connection.create_table(*args, &block) ActiveRecord::Base.connection.execute "SET GENERATOR #{args.first}_seq TO 10000" end + when "PostgreSQL" + enable_uuid_ossp!(ActiveRecord::Base.connection) + create_table :uuid_parents, id: :uuid, force: true do |t| + t.string :name + end + create_table :uuid_children, id: :uuid, force: true do |t| + t.string :name + t.uuid :uuid_parent_id + end end -- cgit v1.2.3