aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record/connection_adapters/postgresql/oid.rb')
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid.rb220
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
+