diff options
Diffstat (limited to 'activerecord/test/cases/adapters/postgresql/composite_test.rb')
-rw-r--r-- | activerecord/test/cases/adapters/postgresql/composite_test.rb | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/activerecord/test/cases/adapters/postgresql/composite_test.rb b/activerecord/test/cases/adapters/postgresql/composite_test.rb new file mode 100644 index 0000000000..b0ce2694a3 --- /dev/null +++ b/activerecord/test/cases/adapters/postgresql/composite_test.rb @@ -0,0 +1,134 @@ +# frozen_string_literal: true + +require "cases/helper" +require "support/connection_helper" + +module PostgresqlCompositeBehavior + include ConnectionHelper + + class PostgresqlComposite < ActiveRecord::Base + self.table_name = "postgresql_composites" + end + + def setup + super + + @connection = ActiveRecord::Base.connection + @connection.transaction do + @connection.execute <<-SQL + CREATE TYPE full_address AS + ( + city VARCHAR(90), + street VARCHAR(90) + ); + SQL + @connection.create_table("postgresql_composites") do |t| + t.column :address, :full_address + end + end + end + + def teardown + super + + @connection.drop_table "postgresql_composites", if_exists: true + @connection.execute "DROP TYPE IF EXISTS full_address" + reset_connection + PostgresqlComposite.reset_column_information + end +end + +# Composites are mapped to `OID::Identity` by default. The user is informed by a warning like: +# "unknown OID 5653508: failed to recognize type of 'address'. It will be treated as String." +# To take full advantage of composite types, we suggest you register your own +OID::Type+. +# See PostgresqlCompositeWithCustomOIDTest +class PostgresqlCompositeTest < ActiveRecord::PostgreSQLTestCase + include PostgresqlCompositeBehavior + + def test_column + ensure_warning_is_issued + + column = PostgresqlComposite.columns_hash["address"] + assert_nil column.type + assert_equal "full_address", column.sql_type + assert_not_predicate column, :array? + + type = PostgresqlComposite.type_for_attribute("address") + assert_not_predicate type, :binary? + end + + def test_composite_mapping + ensure_warning_is_issued + + @connection.execute "INSERT INTO postgresql_composites VALUES (1, ROW('Paris', 'Champs-Élysées'));" + composite = PostgresqlComposite.first + assert_equal "(Paris,Champs-Élysées)", composite.address + + composite.address = "(Paris,Rue Basse)" + composite.save! + + assert_equal '(Paris,"Rue Basse")', composite.reload.address + end + + private + def ensure_warning_is_issued + warning = capture(:stderr) do + PostgresqlComposite.columns_hash + end + assert_match(/unknown OID \d+: failed to recognize type of 'address'\. It will be treated as String\./, warning) + end +end + +class PostgresqlCompositeWithCustomOIDTest < ActiveRecord::PostgreSQLTestCase + include PostgresqlCompositeBehavior + + class FullAddressType < ActiveRecord::Type::Value + def type; :full_address end + + def deserialize(value) + if value =~ /\("?([^",]*)"?,"?([^",]*)"?\)/ + FullAddress.new($1, $2) + end + end + + def cast(value) + value + end + + def serialize(value) + return if value.nil? + "(#{value.city},#{value.street})" + end + end + + FullAddress = Struct.new(:city, :street) + + def setup + super + + @connection.send(:type_map).register_type "full_address", FullAddressType.new + end + + def test_column + column = PostgresqlComposite.columns_hash["address"] + assert_equal :full_address, column.type + assert_equal "full_address", column.sql_type + assert_not_predicate column, :array? + + type = PostgresqlComposite.type_for_attribute("address") + assert_not_predicate type, :binary? + end + + def test_composite_mapping + @connection.execute "INSERT INTO postgresql_composites VALUES (1, ROW('Paris', 'Champs-Élysées'));" + composite = PostgresqlComposite.first + assert_equal "Paris", composite.address.city + assert_equal "Champs-Élysées", composite.address.street + + composite.address = FullAddress.new("Paris", "Rue Basse") + composite.save! + + assert_equal "Paris", composite.reload.address.city + assert_equal "Rue Basse", composite.reload.address.street + end +end |