Skip to content

Commit

Permalink
PG column consults oid types when typecasting
Browse files Browse the repository at this point in the history
  • Loading branch information
tenderlove committed Feb 11, 2012
1 parent fa6cda5 commit c50cb4a
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 10 deletions.
6 changes: 6 additions & 0 deletions activerecord/lib/active_record/attribute_methods/write.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ def write_attribute(attr_name, value)
@attributes_cache.delete(attr_name)
column = column_for_attribute(attr_name)

# If we're dealing with a binary column, write the data to the cache
# so we don't attempt to typecast multiple times.
if column && column.binary?
@attributes_cache[attr_name] = value
end

if column || @attributes.has_key?(attr_name)
@attributes[attr_name] = type_cast_attribute_for_write(column, value)
else
Expand Down
5 changes: 4 additions & 1 deletion activerecord/lib/active_record/connection_adapters/column.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ def klass
end
end

def binary?
type == :binary
end

# Casts a Ruby value to something appropriate for writing to the database.
def type_cast_for_write(value)
return value unless number?
Expand Down Expand Up @@ -98,7 +102,6 @@ def type_cast(value)
when :date then klass.value_to_date(value)
when :binary then klass.binary_to_string(value)
when :boolean then klass.value_to_boolean(value)
when :hstore then klass.string_to_hstore(value)
else value
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def type_cast(value)

class Bytea < Type
def type_cast(value)
PGconn.unescape_bytea value if value
PGconn.unescape_bytea value
end
end

Expand Down Expand Up @@ -180,7 +180,6 @@ def fetch(ftype, fmod)
h[k] = OID::Identity.new
}


# Register an OID type named +name+ with a typcasting object in
# +type+. +name+ should correspond to the `typname` column in
# the `pg_type` table.
Expand All @@ -207,6 +206,8 @@ def self.registered_type?(name)
register_type 'text', OID::Identity.new
alias_type 'varchar', 'text'
alias_type 'char', 'text'
alias_type 'bpchar', 'text'
alias_type 'xml', 'text'

# FIXME: why are we keeping these types as strings?
alias_type 'tsvector', 'text'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ module ConnectionAdapters
# PostgreSQL-specific extensions to column definitions in a table.
class PostgreSQLColumn < Column #:nodoc:
# Instantiates a new PostgreSQL column definition in a table.
def initialize(name, default, sql_type = nil, null = true)
def initialize(name, default, oid_type, sql_type = nil, null = true)
@oid_type = oid_type
super(name, self.class.extract_value_from_default(default), sql_type, null)
end

Expand Down Expand Up @@ -154,6 +155,13 @@ def self.extract_value_from_default(default)
end
end

def type_cast(value)
return if value.nil?
return super if encoded?

@oid_type.type_cast value
end

private
def extract_limit(sql_type)
case sql_type
Expand Down Expand Up @@ -931,8 +939,11 @@ def indexes(table_name, name = nil)
# Returns the list of all column definitions for a table.
def columns(table_name)
# Limit, precision, and scale are all handled by the superclass.
column_definitions(table_name).collect do |column_name, type, default, notnull|
PostgreSQLColumn.new(column_name, default, type, notnull == 'f')
column_definitions(table_name).map do |column_name, type, default, notnull, oid, fmod|
oid = OID::TYPE_MAP.fetch(oid.to_i, fmod.to_i) {
OID::Identity.new
}
PostgreSQLColumn.new(column_name, default, oid, type, notnull == 'f')
end
end

Expand Down Expand Up @@ -1334,7 +1345,7 @@ def select_raw(sql, name = nil)
# - ::regclass is a function that gives the id for a table name
def column_definitions(table_name) #:nodoc:
exec_query(<<-end_sql, 'SCHEMA').rows
SELECT a.attname, format_type(a.atttypid, a.atttypmod), d.adsrc, a.attnotnull
SELECT a.attname, format_type(a.atttypid, a.atttypmod), d.adsrc, a.attnotnull, a.atttypid, a.atttypmod
FROM pg_attribute a LEFT JOIN pg_attrdef d
ON a.attrelid = d.adrelid AND a.attnum = d.adnum
WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
Expand Down
9 changes: 6 additions & 3 deletions activerecord/test/cases/column_definition_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -126,17 +126,20 @@ def test_has_default_should_return_false_for_blog_and_test_data_types

if current_adapter?(:PostgreSQLAdapter)
def test_bigint_column_should_map_to_integer
bigint_column = PostgreSQLColumn.new('number', nil, "bigint")
oid = PostgreSQLAdapter::OID::Identity.new
bigint_column = PostgreSQLColumn.new('number', nil, oid, "bigint")
assert_equal :integer, bigint_column.type
end

def test_smallint_column_should_map_to_integer
smallint_column = PostgreSQLColumn.new('number', nil, "smallint")
oid = PostgreSQLAdapter::OID::Identity.new
smallint_column = PostgreSQLColumn.new('number', nil, oid, "smallint")
assert_equal :integer, smallint_column.type
end

def test_uuid_column_should_map_to_string
uuid_column = PostgreSQLColumn.new('unique_id', nil, "uuid")
oid = PostgreSQLAdapter::OID::Identity.new
uuid_column = PostgreSQLColumn.new('unique_id', nil, oid, "uuid")
assert_equal :string, uuid_column.type
end
end
Expand Down

0 comments on commit c50cb4a

Please sign in to comment.