Permalink
Browse files

[postgres] make sure we type cast custom PG (column) types

  • Loading branch information...
1 parent 62f363b commit 9c0ab774f054d9c2cf3c79047e8ea15f17377c3c @kares kares committed Apr 2, 2013
Binary file not shown.
@@ -158,6 +158,40 @@ def default_value(default)
end
end
+ # Casts value (which is a String) to an appropriate instance.
+ def type_cast(value)
+ return if value.nil?
+ return super if encoded?
+
+ # NOTE: we do not use OID::Type
+ # @oid_type.type_cast value
+ case type
+ when :hstore then self.class.string_to_hstore value
+ when :json then self.class.string_to_json value
+ when :cidr, :inet, :macaddr then self.class.string_to_cidr value
+ when :money
+ # Because money output is formatted according to the locale, there are two
+ # cases to consider (note the decimal separators):
+ # (1) $12,345,678.12
+ # (2) $12.345.678,12
+ case value
+ when /^-?\D+[\d,]+\.\d{2}$/ # (1)
+ value.gsub!(/[^-\d.]/, '')
+ when /^-?\D+[\d.]+,\d{2}$/ # (2)
+ value.gsub!(/[^-\d,]/, '').sub!(/,/, '.')
+ end
+ self.class.value_to_decimal value
+ when /point/ # TODO
+ if String === value
+ self.class.string_to_point value
+ else
+ value
+ end
+ when :tsvector then value
+ else super
+ end
+ end
+
private
def extract_limit(sql_type)
@@ -144,7 +144,9 @@ protected IRubyObject objectToRuby(
if ( objectClass == PGInterval.class ) {
return runtime.newString( formatInterval(object) );
}
+
if ( object instanceof PGobject ) {
+ // PG 9.2 JSON type will be returned here as well
return runtime.newString( object.toString() );
}
@@ -0,0 +1,86 @@
+# encoding: utf-8
+require 'test_helper'
+require 'db/postgres'
+
+class PostgresqlJSONTest < Test::Unit::TestCase
+
+ class JsonDataType < ActiveRecord::Base
+ self.table_name = 'json_data_type'
+ end
+
+ def setup
+ @connection = ActiveRecord::Base.connection
+ begin
+ @connection.transaction do
+ @connection.create_table('json_data_type') do |t|
+ t.json 'payload', :default => {}
+ end
+ end
+ rescue ActiveRecord::StatementInvalid
+ return skip "do not test on PG without json"
+ end
+ end
+
+ def teardown
+ @connection.execute 'drop table if exists json_data_type'
+ end
+
+ def test_column
+ column = JsonDataType.columns.find { |c| c.name == 'payload' }
+ assert_equal :json, column.type
+ end
+
+ def test_change_table_supports_json
+ @connection.transaction do
+ @connection.change_table('json_data_type') do |t|
+ t.json 'users', :default => '{}'
+ end
+ JsonDataType.reset_column_information
+ column = JsonDataType.columns.find { |c| c.name == 'users' }
+ assert_equal :json, column.type
+
+ raise ActiveRecord::Rollback # reset the schema change
+ end
+ ensure
+ JsonDataType.reset_column_information
+ end
+
+ def test_type_cast_json
+ assert @column = JsonDataType.columns.find { |c| c.name == 'payload' }
+
+ data = "{\"a_key\":\"a_value\"}"
+ hash = @column.class.string_to_json data
+ assert_equal({'a_key' => 'a_value'}, hash)
+ assert_equal({'a_key' => 'a_value'}, @column.type_cast(data))
+
+ assert_equal({}, @column.type_cast("{}"))
+ assert_equal({'key'=>nil}, @column.type_cast('{"key": null}'))
+ assert_equal({'c'=>'}','"a"'=>'b "a b'}, @column.type_cast(%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
+
+end if Test::Unit::TestCase.ar_version('4.0')
@@ -122,7 +122,7 @@ def self.shutdown
BooleansMigration.down
end
- def test_should_handle_bool_conversion_with_boolean_relation
+ def test_conversion_with_boolean_relation
ActiveRecord::Base.connection.raw_connection.set_native_database_types
end

0 comments on commit 9c0ab77

Please sign in to comment.