diff options
Diffstat (limited to 'activerecord/lib/active_record/connection_adapters/postgresql')
-rw-r--r-- | activerecord/lib/active_record/connection_adapters/postgresql/oid.rb | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb new file mode 100644 index 0000000000..006742688e --- /dev/null +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb @@ -0,0 +1,220 @@ +require 'active_record/connection_adapters/abstract_adapter' + +module ActiveRecord + module ConnectionAdapters + class PostgreSQLAdapter < AbstractAdapter + module OID + class Type + def type; end + + def type_cast_for_write(value) + value + end + end + + class Identity < Type + def type_cast(value) + value + end + end + + class Bytea < Type + def type_cast(value) + PGconn.unescape_bytea value if value + end + end + + class Money < Type + def type_cast(value) + return if value.nil? + + # 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 + + ConnectionAdapters::Column.value_to_decimal value + end + end + + class Vector < Type + attr_reader :delim, :subtype + + # +delim+ corresponds to the `typdelim` column in the pg_types + # table. +subtype+ is derived from the `typelem` column in the + # pg_types table. + def initialize(delim, subtype) + @delim = delim + @subtype = subtype + end + + # FIXME: this should probably split on +delim+ and use +subtype+ + # to cast the values. Unfortunately, the current Rails behavior + # is to just return the string. + def type_cast(value) + value + end + end + + class Integer < Type + def type_cast(value) + return if value.nil? + + value.to_i rescue value ? 1 : 0 + end + end + + class Boolean < Type + def type_cast(value) + return if value.nil? + + ConnectionAdapters::Column.value_to_boolean value + end + end + + class Timestamp < Type + def type; :timestamp; end + + def type_cast(value) + return if value.nil? + + # FIXME: probably we can improve this since we know it is PG + # specific + ConnectionAdapters::PostgreSQLColumn.string_to_time value + end + end + + class Date < Type + def type; :datetime; end + + def type_cast(value) + return if value.nil? + + # FIXME: probably we can improve this since we know it is PG + # specific + ConnectionAdapters::Column.value_to_date value + end + end + + class Time < Type + def type_cast(value) + return if value.nil? + + # FIXME: probably we can improve this since we know it is PG + # specific + ConnectionAdapters::Column.string_to_dummy_time value + end + end + + class Float < Type + def type_cast(value) + return if value.nil? + + value.to_f + end + end + + class Decimal < Type + def type_cast(value) + return if value.nil? + + ConnectionAdapters::Column.value_to_decimal value + end + end + + class Hstore < Type + def type_cast(value) + return if value.nil? + + ConnectionAdapters::PostgreSQLColumn.cast_hstore value + end + end + + class TypeMap + def initialize + @mapping = {} + end + + def []=(oid, type) + @mapping[oid] = type + end + + def [](oid) + @mapping[oid] + end + + def fetch(ftype, fmod) + # The type for the numeric depends on the width of the field, + # so we'll do something special here. + # + # When dealing with decimal columns: + # + # places after decimal = fmod - 4 & 0xffff + # places before decimal = (fmod - 4) >> 16 & 0xffff + if ftype == 1700 && (fmod - 4 & 0xffff).zero? + ftype = 23 + end + + @mapping.fetch(ftype) { |oid| yield oid, fmod } + end + end + + TYPE_MAP = TypeMap.new # :nodoc: + + TYPE_MAP[23] = OID::Integer.new # int4 + TYPE_MAP[20] = TYPE_MAP[23] # int8 + TYPE_MAP[21] = TYPE_MAP[23] # int2 + TYPE_MAP[26] = TYPE_MAP[23] # oid + + TYPE_MAP[1700] = OID::Decimal.new # decimal + + TYPE_MAP[25] = OID::Identity.new # text + TYPE_MAP[19] = TYPE_MAP[25] # name + TYPE_MAP[1043] = TYPE_MAP[25] # varchar + + # FIXME: why are we keeping these types as strings? + TYPE_MAP[3614] = TYPE_MAP[25] # tsvector + TYPE_MAP[1186] = TYPE_MAP[25] # interval + TYPE_MAP[650] = TYPE_MAP[25] # cidr + TYPE_MAP[869] = TYPE_MAP[25] # inet + TYPE_MAP[829] = TYPE_MAP[25] # macaddr + TYPE_MAP[1560] = TYPE_MAP[25] # bit + TYPE_MAP[1562] = TYPE_MAP[25] # varbit + + # FIXME: I don't think this is correct. We should probably be returning a parsed date, + # but the tests pass with a string returned. + TYPE_MAP[1184] = OID::Identity.new # timestamptz + + TYPE_MAP[790] = OID::Money.new # money + TYPE_MAP[17] = OID::Bytea.new # bytea + TYPE_MAP[16] = OID::Boolean.new # bool + + TYPE_MAP[700] = OID::Float.new # float4 + TYPE_MAP[701] = TYPE_MAP[700] # float8 + + TYPE_MAP[1114] = OID::Timestamp.new # timestamp + TYPE_MAP[1082] = OID::Date.new # date + TYPE_MAP[1083] = OID::Time.new # time + + TYPE_MAP[1009] = OID::Vector.new(',', TYPE_MAP[25]) # _text + TYPE_MAP[1007] = OID::Vector.new(',', TYPE_MAP[23]) # _int4 + TYPE_MAP[600] = OID::Vector.new(',', TYPE_MAP[701]) # point + TYPE_MAP[601] = OID::Vector.new(',', TYPE_MAP[600]) # lseg + TYPE_MAP[602] = OID::Identity.new # path + TYPE_MAP[603] = OID::Vector.new(';', TYPE_MAP[600]) # box + TYPE_MAP[604] = OID::Identity.new # polygon + TYPE_MAP[718] = OID::Identity.new # circle + + TYPE_MAP[1399854] = OID::Hstore.new # hstore + end + end + end +end + |